├── .gitattributes
├── .gitignore
├── addon.json
└── lua
├── autorun
└── lf_playermodel_selector.lua
└── tfa_vox
└── packs
└── zzz_lf_playermodel_selector.lua
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Lua sources
2 | luac.out
3 |
4 | # luarocks build files
5 | *.src.rock
6 | *.zip
7 | *.tar.gz
8 |
9 | # Object files
10 | *.o
11 | *.os
12 | *.ko
13 | *.obj
14 | *.elf
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 | *.def
26 | *.exp
27 |
28 | # Shared objects (inc. Windows DLLs)
29 | *.dll
30 | *.so
31 | *.so.*
32 | *.dylib
33 |
34 | # Executables
35 | *.exe
36 | *.out
37 | *.app
38 | *.i*86
39 | *.x86_64
40 | *.hex
41 |
42 |
43 | # =========================
44 | # Operating System Files
45 | # =========================
46 |
47 | # OSX
48 | # =========================
49 |
50 | .DS_Store
51 | .AppleDouble
52 | .LSOverride
53 |
54 | # Thumbnails
55 | ._*
56 |
57 | # Files that might appear on external disk
58 | .Spotlight-V100
59 | .Trashes
60 |
61 | # Directories potentially created on remote AFP share
62 | .AppleDB
63 | .AppleDesktop
64 | Network Trash Folder
65 | Temporary Items
66 | .apdisk
67 |
68 | # Windows
69 | # =========================
70 |
71 | # Windows image file caches
72 | Thumbs.db
73 | ehthumbs.db
74 |
75 | # Folder config file
76 | Desktop.ini
77 |
78 | # Recycle Bin used on file shares
79 | $RECYCLE.BIN/
80 |
81 | # Windows Installer files
82 | *.cab
83 | *.msi
84 | *.msm
85 | *.msp
86 |
87 | # Windows shortcuts
88 | *.lnk
89 |
--------------------------------------------------------------------------------
/addon.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "Description",
3 | "ignore": [
4 | "*.psd",
5 | "*.vcproj",
6 | "*.svn*",
7 | "*.git*"
8 | ],
9 | "tags": [
10 | "roleplay",
11 | "scenic"
12 | ],
13 | "title": "Enhanced PlayerModel Selector",
14 | "type": "tool",
15 | "workshopid": 504945881.0
16 | }
--------------------------------------------------------------------------------
/lua/autorun/lf_playermodel_selector.lua:
--------------------------------------------------------------------------------
1 | -- Enhanced PlayerModel Selector
2 | -- Upgraded code by LibertyForce http://steamcommunity.com/id/libertyforce
3 | -- Based on: https://github.com/garrynewman/garrysmod/blob/1a2c317eeeef691e923453018236cf9f66ee74b4/garrysmod/gamemodes/sandbox/gamemode/editor_player.lua
4 |
5 |
6 | local flag = { FCVAR_REPLICATED }
7 | if SERVER then flag = { FCVAR_ARCHIVE, FCVAR_REPLICATED } end
8 | local convars = { }
9 | convars["sv_playermodel_selector_force"] = 1
10 | convars["sv_playermodel_selector_gamemodes"] = 1
11 | convars["sv_playermodel_selector_instantly"] = 1
12 | convars["sv_playermodel_selector_flexes"] = 0
13 | convars["sv_playermodel_selector_limit"] = 1
14 | convars["sv_playermodel_selector_debug"] = 0
15 | for cvar, def in pairs( convars ) do
16 | CreateConVar( cvar, def, flag )
17 | end
18 | flag = nil
19 |
20 |
21 | if SERVER then
22 |
23 |
24 | AddCSLuaFile()
25 |
26 | --util.AddNetworkString("lf_playermodel_client_sync")
27 | util.AddNetworkString("lf_playermodel_cvar_change")
28 | util.AddNetworkString("lf_playermodel_blacklist")
29 | util.AddNetworkString("lf_playermodel_voxlist")
30 | util.AddNetworkString("lf_playermodel_update")
31 |
32 | local SetMDL = FindMetaTable("Entity").SetModel
33 |
34 | local addon_legs = false
35 |
36 | local debugmode = GetConVar( "sv_playermodel_selector_debug" ):GetBool() or false
37 | cvars.AddChangeCallback( "sv_playermodel_selector_debug", function() debugmode = GetConVar( "sv_playermodel_selector_debug" ):GetBool() end )
38 |
39 |
40 | --local function client_sync( ply )
41 | -- net.Start("lf_playermodel_client_sync")
42 | -- net.WriteBool( addon_vox )
43 | -- net.Send( ply )
44 | --end
45 | --hook.Add( "PlayerInitialSpawn", "lf_playermodel_client_sync_hook", client_sync )
46 | --net.Receive("lf_playermodel_client_sync", function( len, ply ) client_sync( ply ) end )
47 |
48 | net.Receive("lf_playermodel_cvar_change", function( len, ply )
49 | if ply:IsValid() and ply:IsPlayer() then
50 | local cvar = net.ReadString()
51 | if !convars[cvar] then ply:Kick("Illegal convar change") return end
52 | if !ply:IsAdmin() then return end
53 | RunConsoleCommand( cvar, net.ReadString() )
54 | end
55 | end )
56 |
57 |
58 | if !file.Exists( "lf_playermodel_selector", "DATA" ) then file.CreateDir( "lf_playermodel_selector" ) end
59 | if file.Exists( "playermodel_selector_blacklist.txt", "DATA" ) then -- Migrate from old version
60 | if !file.Exists( "lf_playermodel_selector/sv_blacklist.txt", "DATA" ) then
61 | local content = file.Read( "playermodel_selector_blacklist.txt", "DATA" )
62 | file.Write( "lf_playermodel_selector/sv_blacklist.txt", content )
63 | end
64 | file.Delete( "playermodel_selector_blacklist.txt" )
65 | end
66 |
67 | local Blacklist = { }
68 | if file.Exists( "lf_playermodel_selector/sv_blacklist.txt", "DATA" ) then
69 | local loaded = util.JSONToTable( file.Read( "lf_playermodel_selector/sv_blacklist.txt", "DATA" ) )
70 | if istable( loaded ) then
71 | for k, v in pairs( loaded ) do
72 | Blacklist[tostring(k)] = v
73 | end
74 | loaded = nil
75 | end
76 | end
77 |
78 | net.Receive("lf_playermodel_blacklist", function( len, ply )
79 | if ply:IsValid() and ply:IsPlayer() and ply:IsAdmin() then
80 | local mode = net.ReadInt( 3 )
81 | if mode == 1 then
82 | local gamemode = net.ReadString()
83 | if gamemode != "sandbox" then
84 | Blacklist[gamemode] = true
85 | file.Write( "lf_playermodel_selector/sv_blacklist.txt", util.TableToJSON( Blacklist, true ) )
86 | end
87 | elseif mode == 2 then
88 | local tbl = net.ReadTable()
89 | if istable( tbl ) then
90 | for k, v in pairs( tbl ) do
91 | local name = tostring( v )
92 | Blacklist[name] = nil
93 | end
94 | file.Write( "lf_playermodel_selector/sv_blacklist.txt", util.TableToJSON( Blacklist, true ) )
95 | end
96 | end
97 | net.Start("lf_playermodel_blacklist")
98 | net.WriteTable( Blacklist )
99 | net.Send( ply )
100 | end
101 | end )
102 |
103 |
104 |
105 | local VOXlist = { }
106 |
107 | function lf_playermodel_selector_get_voxlist() -- global
108 | return VOXlist
109 | end
110 |
111 | local function InitVOX()
112 | if file.Exists( "lf_playermodel_selector/sv_voxlist.txt", "DATA" ) then
113 | local loaded = util.JSONToTable( file.Read( "lf_playermodel_selector/sv_voxlist.txt", "DATA" ) )
114 | if istable( loaded ) then
115 | for k, v in pairs( loaded ) do
116 | VOXlist[tostring(k)] = tostring(v)
117 | end
118 | loaded = nil
119 | end
120 | end
121 | end
122 |
123 | net.Receive("lf_playermodel_voxlist", function( len, ply )
124 | if TFAVOX_Models and ply:IsValid() and ply:IsPlayer() and ply:IsAdmin() then
125 | local function tfa_reload()
126 | TFAVOX_Packs_Initialize()
127 | TFAVOX_PrecachePacks()
128 | for k,v in pairs( player.GetAll() ) do
129 | print("Resetting the VOX of " .. v:Nick() )
130 | if IsValid(v) then TFAVOX_Init(v,true,true) end
131 | end
132 | end
133 | local mode = net.ReadInt( 3 )
134 | if mode == 1 then
135 | local k = net.ReadString()
136 | local v = net.ReadString()
137 | VOXlist[k] = v
138 | file.Write( "lf_playermodel_selector/sv_voxlist.txt", util.TableToJSON( VOXlist, true ) )
139 | --TFAVOX_Models = { }
140 | tfa_reload()
141 | elseif mode == 2 then
142 | local tbl = net.ReadTable()
143 | if istable( tbl ) then
144 | for k, v in pairs( tbl ) do
145 | local name = tostring( v )
146 | VOXlist[name] = nil
147 | if istable( TFAVOX_Models ) then TFAVOX_Models[name] = nil end
148 | end
149 | file.Write( "lf_playermodel_selector/sv_voxlist.txt", util.TableToJSON( VOXlist, true ) )
150 | --TFAVOX_Models = { }
151 | tfa_reload()
152 | end
153 | end
154 | net.Start("lf_playermodel_voxlist")
155 | net.WriteTable( VOXlist )
156 | net.Send( ply )
157 | end
158 | end )
159 |
160 |
161 | local plymeta = FindMetaTable( "Player" )
162 | local CurrentPlySetModel
163 |
164 | local function Allowed( ply )
165 | if GAMEMODE_NAME == "sandbox" or ( !Blacklist[GAMEMODE_NAME] and ( ply:IsAdmin() or GetConVar( "sv_playermodel_selector_gamemodes"):GetBool() ) ) then
166 | return true else return false
167 | end
168 | end
169 |
170 |
171 | local function UpdatePlayerModel( ply )
172 | if Allowed( ply ) then
173 |
174 | ply.lf_playermodel_spawned = true
175 |
176 | if debugmode then print( "LF_PMS: Updating playermodel for: "..tostring( ply:GetName() ) ) end
177 |
178 | local mdlname = ply:GetInfo( "cl_playermodel" )
179 | local mdlpath = player_manager.TranslatePlayerModel( mdlname )
180 |
181 | SetMDL( ply, mdlpath )
182 | if debugmode then print( "LF_PMS: Set model to: "..tostring( mdlname ).." - "..tostring( mdlpath ) ) end
183 |
184 | local skin = ply:GetInfoNum( "cl_playerskin", 0 )
185 | ply:SetSkin( skin )
186 | if debugmode then print( "LF_PMS: Set model skin to no.: "..tostring( skin ) ) end
187 |
188 | local groups = ply:GetInfo( "cl_playerbodygroups" )
189 | if ( groups == nil ) then groups = "" end
190 | local groups = string.Explode( " ", groups )
191 | for k = 0, ply:GetNumBodyGroups() - 1 do
192 | local v = tonumber( groups[ k + 1 ] ) or 0
193 | ply:SetBodygroup( k, v )
194 | if debugmode then print( "LF_PMS: Set bodygroup no. "..tostring( k ).." to: "..tostring( v ) ) end
195 | end
196 |
197 | if GetConVar( "sv_playermodel_selector_flexes" ):GetBool() and tobool( ply:GetInfoNum( "cl_playermodel_selector_unlockflexes", 0 ) ) then
198 | local flexes = ply:GetInfo( "cl_playerflexes" )
199 | if ( flexes == nil ) or ( flexes == "0" ) then return end
200 | local flexes = string.Explode( " ", flexes )
201 | for k = 0, ply:GetFlexNum() - 1 do
202 | ply:SetFlexWeight( k, tonumber( flexes[ k + 1 ] ) or 0 )
203 | end
204 | end
205 |
206 | local pcol = ply:GetInfo( "cl_playercolor" )
207 | local wcol = ply:GetInfo( "cl_weaponcolor" )
208 | ply:SetPlayerColor( Vector( pcol ) )
209 | ply:SetWeaponColor( Vector( wcol ) )
210 |
211 | timer.Simple( 0.1, function() if ply.SetupHands and isfunction( ply.SetupHands ) then ply:SetupHands() end end )
212 | timer.Simple( 0.2, function()
213 | local mdlhands = player_manager.TranslatePlayerHands( mdlname )
214 | local hands_ent = ply:GetHands()
215 | if hands_ent and mdlhands and istable( mdlhands ) then
216 | if hands_ent:GetModel() != mdlhands.model then
217 | if debugmode then print( "LF_PMS: SetupHands failed. Gamemode doesn't implement this function correctly. Trying workaround..." ) end
218 | if ( IsValid( hands_ent ) ) then
219 | hands_ent:SetModel( mdlhands.model )
220 | hands_ent:SetSkin( mdlhands.skin )
221 | hands_ent:SetBodyGroups( mdlhands.body )
222 | if debugmode then
223 | timer.Simple( 0.2, function()
224 | if hands_ent:GetModel() != mdlhands.model then
225 | print( "LF_PMS: Workaround failed. Unable to setup viewmodel hands. Please check for incompatible addons." )
226 | else
227 | print( "LF_PMS: Workaround successful. Hands set to: "..mdlhands.model )
228 | end
229 | end )
230 | end
231 | end
232 | else
233 | if debugmode then print( "LF_PMS: SetupHands successful. Hands set to: "..tostring( mdlhands.model ) ) end
234 | end
235 | else
236 | if debugmode then print( "LF_PMS: ERROR - SetupHands failed. player_manager.TranslatePlayerHands didn't return valid data. Please check for incompatible addons." ) end
237 | end
238 | end )
239 |
240 | if addon_legs then
241 | hook.Run( "SetModel", ply, mdlpath )
242 | end
243 |
244 | end
245 | end
246 |
247 | net.Receive("lf_playermodel_update", function( len, ply )
248 | if ply:IsValid() and ply:IsPlayer() and ( ply:IsAdmin() or GetConVar( "sv_playermodel_selector_instantly"):GetBool() ) then
249 | if game.SinglePlayer() or ply:IsAdmin() then
250 | UpdatePlayerModel( ply )
251 | else
252 | local limit = math.Clamp( GetConVar( "sv_playermodel_selector_limit"):GetInt(), 0, 900 )
253 | local ct = CurTime()
254 | local diff1 = ct - ( ply.lf_playermodel_lastcall or limit*(-1) )
255 | local diff2 = ct - ( ply.lf_playermodel_lastsuccess or limit*(-1) )
256 | if diff1 < 0.1 then
257 | ply:Kick( "Too many requests. Please check your script for infinite loops" )
258 | if debugmode then print ( "LF_PMS: Kicked "..tostring( ply:GetName() )..". Multiple calls for playermodel change in less than: "..tostring( diff1 ).." seconds" ) end
259 | elseif diff2 >= limit then
260 | ply.lf_playermodel_lastcall = ct
261 | ply.lf_playermodel_lastsuccess = ct
262 | UpdatePlayerModel( ply )
263 | else
264 | ply.lf_playermodel_lastcall = ct
265 | ply:ChatPrint( "Enhanced PlayerModel Selector: Too many requests. Please wait another "..tostring( limit - math.floor( diff2 ) ).." seconds before trying again." )
266 | if debugmode then print ( "LF_PMS: Prevented "..tostring( ply:GetName() ).." from changing playermodel. Last try: "..tostring( math.floor( diff1 ) ).." seconds ago." ) end
267 | end
268 | end
269 | end
270 | end )
271 |
272 | hook.Add( "PlayerSpawn", "lf_playermodel_force_hook1", function( ply )
273 | if GetConVar( "sv_playermodel_selector_force" ):GetBool() and tobool( ply:GetInfoNum( "cl_playermodel_selector_force", 0 ) ) then
274 | --UpdatePlayerModel( ply )
275 | ply.lf_playermodel_spawned = nil
276 | end
277 | end )
278 |
279 | local function ForceSetModel( ply, mdl )
280 | if GetConVar( "sv_playermodel_selector_force" ):GetBool() and Allowed( ply ) and tobool( ply:GetInfoNum( "cl_playermodel_selector_force", 0 ) ) then
281 | if !ply.lf_playermodel_spawned then
282 | if debugmode then print( "LF_PMS: Detected initial call for SetModel on: "..tostring( ply:GetName() ) ) end
283 | UpdatePlayerModel( ply )
284 | else
285 | if debugmode then print( "LF_PMS: Enforcer prevented "..tostring( ply:GetName() ).."'s model from being changed to: "..tostring( mdl ) ) end
286 | end
287 | elseif mdl then
288 | CurrentPlySetModel( ply, mdl )
289 | if addon_legs then hook.Run( "SetModel" , ply, mdl ) end
290 | end
291 | end
292 |
293 | local function ToggleForce()
294 | if plymeta.SetModel and plymeta.SetModel ~= ForceSetModel then
295 | CurrentPlySetModel = plymeta.SetModel
296 | else
297 | CurrentPlySetModel = SetMDL
298 | end
299 |
300 | if GetConVar( "sv_playermodel_selector_force" ):GetBool() then
301 | plymeta.SetModel = ForceSetModel
302 | else
303 | plymeta.SetModel = CurrentPlySetModel
304 | end
305 | end
306 | cvars.AddChangeCallback( "sv_playermodel_selector_force", ToggleForce )
307 |
308 | hook.Add( "Initialize", "lf_playermodel_force_hook2", function( ply )
309 | if file.Exists( "autorun/sh_legs.lua", "LUA" ) then addon_legs = true end
310 | --if file.Exists( "autorun/tfa_vox_loader.lua", "LUA" ) then addon_vox = true end
311 | if TFAVOX_Models then InitVOX() end
312 |
313 | local try = 0
314 |
315 | ToggleForce()
316 |
317 | timer.Create( "lf_playermodel_force_timer", 5, 0, function()
318 | if plymeta.SetModel == ForceSetModel or not GetConVar( "sv_playermodel_selector_force" ):GetBool() then
319 | timer.Remove( "lf_playermodel_force_timer" )
320 | else
321 | ToggleForce()
322 | try = try + 1
323 | print( "LF_PMS: Addon conflict detected. Unable to initialize enforcer to protect playermodel. [Attempt: " .. tostring( try ) .. "/10]" )
324 | if try >= 10 then
325 | timer.Remove( "lf_playermodel_force_timer" )
326 | end
327 | end
328 | end )
329 | end )
330 |
331 |
332 | end
333 |
334 | -----------------------------------------------------------------------------------------------------------------------------------------------------
335 |
336 | if CLIENT then
337 |
338 |
339 | local Version = "3.3"
340 | local Menu = { }
341 | local Frame
342 | local default_animations = { "idle_all_01", "menu_walk", "pose_standing_02", "pose_standing_03", "idle_fist" }
343 | local Favorites = { }
344 | --local addon_vox = false
345 |
346 | if !file.Exists( "lf_playermodel_selector", "DATA" ) then file.CreateDir( "lf_playermodel_selector" ) end
347 | if file.Exists( "playermodel_selector_favorites.txt", "DATA" ) then -- Migrate from old version
348 | if !file.Exists( "lf_playermodel_selector/cl_favorites.txt", "DATA" ) then
349 | local content = file.Read( "playermodel_selector_favorites.txt", "DATA" )
350 | file.Write( "lf_playermodel_selector/cl_favorites.txt", content )
351 | end
352 | file.Delete( "playermodel_selector_favorites.txt" )
353 | end
354 |
355 | if file.Exists( "lf_playermodel_selector/cl_favorites.txt", "DATA" ) then
356 | local loaded = util.JSONToTable( file.Read( "lf_playermodel_selector/cl_favorites.txt", "DATA" ) )
357 | if istable( loaded ) then
358 | for k, v in pairs( loaded ) do
359 | Favorites[tostring(k)] = v
360 | end
361 | loaded = nil
362 | end
363 | end
364 |
365 |
366 | CreateClientConVar( "cl_playermodel_selector_force", "1", true, true )
367 | CreateClientConVar( "cl_playermodel_selector_unlockflexes", "0", false, true )
368 | CreateClientConVar( "cl_playermodel_selector_bgcolor_custom", "1", true, true )
369 | CreateClientConVar( "cl_playermodel_selector_bgcolor_trans", "1", true, true )
370 |
371 | --net.Start("lf_playermodel_client_sync")
372 | --net.SendToServer()
373 | --net.Receive("lf_playermodel_client_sync", function()
374 | -- addon_vox = net.ReadBool()
375 | --end )
376 |
377 | hook.Add( "PostGamemodeLoaded", "lf_playermodel_sboxcvars", function()
378 | if !ConVarExists( "cl_playercolor" ) then CreateConVar( "cl_playercolor", "0.24 0.34 0.41", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The value is a Vector - so between 0-1 - not between 0-255" ) end
379 | if !ConVarExists( "cl_weaponcolor" ) then CreateConVar( "cl_weaponcolor", "0.30 1.80 2.10", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The value is a Vector - so between 0-1 - not between 0-255" ) end
380 | if !ConVarExists( "cl_playerskin" ) then CreateConVar( "cl_playerskin", "0", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The skin to use, if the model has any" ) end
381 | if !ConVarExists( "cl_playerbodygroups" ) then CreateConVar( "cl_playerbodygroups", "0", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The bodygroups to use, if the model has any" ) end
382 | if !ConVarExists( "cl_playerflexes" ) then CreateConVar( "cl_playerflexes", "0", { FCVAR_ARCHIVE, FCVAR_USERINFO, FCVAR_DONTRECORD }, "The flexes to use, if the model has any" ) end
383 | end )
384 |
385 |
386 | local function KeyboardOn( pnl )
387 | if ( IsValid( Frame ) and IsValid( pnl ) and pnl:HasParent( Frame ) ) then
388 | Frame:SetKeyboardInputEnabled( true )
389 | end
390 | end
391 | hook.Add( "OnTextEntryGetFocus", "lf_playermodel_keyboard_on", KeyboardOn )
392 | local function KeyboardOff( pnl )
393 | if ( IsValid( Frame ) and IsValid( pnl ) and pnl:HasParent( Frame ) ) then
394 | Frame:SetKeyboardInputEnabled( false )
395 | end
396 | end
397 | hook.Add( "OnTextEntryLoseFocus", "lf_playermodel_keyboard_off", KeyboardOff )
398 |
399 |
400 | local function LoadPlayerModel()
401 | if LocalPlayer():IsAdmin() or GetConVar( "sv_playermodel_selector_instantly" ):GetBool() then
402 | net.Start("lf_playermodel_update")
403 | net.SendToServer()
404 | end
405 | end
406 | concommand.Add( "playermodel_apply", LoadPlayerModel )
407 |
408 | local function LoadFavorite( ply, cmd, args )
409 | local name = tostring( args[1] )
410 | if istable( Favorites[name] ) then
411 | RunConsoleCommand( "cl_playermodel", Favorites[name].model )
412 | RunConsoleCommand( "cl_playerbodygroups", Favorites[name].bodygroups )
413 | RunConsoleCommand( "cl_playerskin", Favorites[name].skin )
414 | timer.Simple( 0.1, LoadPlayerModel )
415 | else
416 | print( "Favorite not found. Remember: The name is case-sensitive and should be put in quotation marks." )
417 | end
418 | end
419 | concommand.Add( "playermodel_loadfav", LoadFavorite )
420 |
421 |
422 | function Menu.Setup()
423 |
424 | Frame = vgui.Create( "DFrame" )
425 | local fw, fh = math.min( ScrW() - 16, 960 ), math.min( ScrH() - 16, 700 )
426 | Frame:SetSize( fw, fh )
427 | Frame:SetTitle( "Enhanced PlayerModel Selector "..Version )
428 | Frame:SetVisible( true )
429 | Frame:SetDraggable( true )
430 | Frame:SetScreenLock( false )
431 | Frame:ShowCloseButton( true )
432 | Frame:Center()
433 | Frame:MakePopup()
434 | Frame:SetKeyboardInputEnabled( false )
435 | local r, g, b = 97, 100, 102
436 | if GetConVar( "cl_playermodel_selector_bgcolor_custom" ):GetBool() then
437 | local bgcolor = string.Explode( " ", GetConVar( "cl_playercolor" ):GetString() )
438 | bgcolor[1] = tonumber( bgcolor[1] )
439 | bgcolor[2] = tonumber( bgcolor[2] )
440 | bgcolor[3] = tonumber( bgcolor[3] )
441 | if isnumber( bgcolor[1] ) and isnumber( bgcolor[2] ) and isnumber( bgcolor[3] ) then
442 | r, g, b = math.Round( bgcolor[1] * 255 ), math.Round( bgcolor[2] * 255 ), math.Round( bgcolor[3] * 255 )
443 | else
444 | timer.Simple( 0.1, function() RunConsoleCommand( "cl_playercolor", "0.24 0.34 0.41" ) end )
445 | end
446 | end
447 | local a = GetConVar( "cl_playermodel_selector_bgcolor_trans" ):GetBool() == true and 127 or 255
448 | Frame.Paint = function( self, w, h )
449 | draw.RoundedBox( 10, 0, 0, w, h, Color( r, g, b, a ) ) return true
450 | end
451 |
452 | Frame.lblTitle:SetTextColor( Color( 0, 0, 0, 255 ) )
453 | Frame.lblTitle.Paint = function ( self, w, h )
454 | draw.SimpleTextOutlined( Frame.lblTitle:GetText(), "DermaDefaultBold", 1, 2, Color( 255, 255, 255, 255), 0, 0, 1, Color( 0, 0, 0, 255) ) return true
455 | end
456 |
457 | Frame.btnMinim:SetEnabled( true )
458 | Frame.btnMinim.DoClick = function()
459 | Frame:SetVisible( false )
460 | end
461 | --Frame.btnMaxim.Paint = function( panel, w, h ) derma.SkinHook( "Paint", "WindowMinimizeButton", panel, w, h ) end
462 | local maxi_allowed = false
463 | local maxi_mode = 0
464 | if ScrW() > fw and ScrH() > fh then maxi_allowed = true end
465 | Frame.btnMaxim:SetEnabled( maxi_allowed )
466 | Frame.btnMaxim.DoClick = function()
467 | if maxi_allowed and maxi_mode == 0 then
468 | Frame:SetSize( ScrW(), ScrH() )
469 | Frame:Center()
470 | Frame:SetDraggable( false )
471 | Menu.ApplyButton:SetPos( ScrW() - 560, 30 )
472 | Menu.ResetButton:SetPos( 5, ScrH() - 25 )
473 | Menu.AdvButton:SetPos( ScrW() - 200, 3 )
474 | maxi_mode = 1
475 | elseif maxi_allowed and maxi_mode == 1 then
476 | Menu.ApplyButton:SetVisible( false )
477 | Menu.ResetButton:SetVisible( false )
478 | Menu.AdvButton:SetVisible( false )
479 | Menu.Right:SetVisible( false )
480 | Frame:InvalidateLayout( false )
481 | maxi_mode = 2
482 | else
483 | Frame:SetSize( fw, fh )
484 | Frame:Center()
485 | Frame:SetDraggable( true )
486 | Menu.ApplyButton:SetPos( fw - 560, 30 )
487 | Menu.ApplyButton:SetVisible( true )
488 | Menu.ResetButton:SetPos( 5, fh - 25 )
489 | Menu.ResetButton:SetVisible( true )
490 | Menu.AdvButton:SetPos( fw - 200, 3 )
491 | Menu.AdvButton:SetVisible( true )
492 | Menu.Right:SetVisible( true )
493 | maxi_mode = 0
494 | end
495 | end
496 |
497 | local mdl = Frame:Add( "DModelPanel" )
498 | mdl:Dock( FILL )
499 | --mdl:SetSize( 520, 0 )
500 | mdl:SetFOV( 36 )
501 | mdl:SetCamPos( Vector( 0, 0, 0 ) )
502 | mdl:SetDirectionalLight( BOX_RIGHT, Color( 255, 160, 80, 255 ) )
503 | mdl:SetDirectionalLight( BOX_LEFT, Color( 80, 160, 255, 255 ) )
504 | mdl:SetAmbientLight( Vector( -64, -64, -64 ) )
505 | mdl:SetAnimated( true )
506 | mdl:SetLookAt( Vector( -100, 0, -22 ) )
507 | function mdl.DefaultPos()
508 | mdl.Angles = Angle( 0, 0, 0 )
509 | mdl.Pos = Vector( -100, 0, -61 )
510 | end
511 | mdl.DefaultPos()
512 |
513 | Menu.AdvButton = Frame:Add( "DButton" )
514 | Menu.AdvButton:SetSize( 100, 18 )
515 | Menu.AdvButton:SetPos( fw - 200, 3 )
516 | Menu.AdvButton:SetText( "Visit Addon Page" )
517 | Menu.AdvButton.DoClick = function()
518 | gui.OpenURL( "http://steamcommunity.com/sharedfiles/filedetails/?id=504945881" )
519 | SetClipboardText( "http://steamcommunity.com/sharedfiles/filedetails/?id=504945881" )
520 | end
521 |
522 | Menu.ApplyButton = Frame:Add( "DButton" )
523 | Menu.ApplyButton:SetSize( 120, 30 )
524 | Menu.ApplyButton:SetPos( fw - 560, 30 )
525 | Menu.ApplyButton:SetText( "Apply playermodel" )
526 | Menu.ApplyButton:SetEnabled( LocalPlayer():IsAdmin() or GetConVar( "sv_playermodel_selector_instantly" ):GetBool() )
527 | Menu.ApplyButton.DoClick = LoadPlayerModel
528 |
529 | Menu.ResetButton = Frame:Add( "DButton" )
530 | Menu.ResetButton:SetSize( 40, 20 )
531 | Menu.ResetButton:SetPos( 5, fh - 25 )
532 | Menu.ResetButton:SetText( "Reset" )
533 | Menu.ResetButton.DoClick = mdl.DefaultPos
534 |
535 |
536 | Menu.Right = Frame:Add( "DPropertySheet" )
537 | Menu.Right:Dock( RIGHT )
538 | Menu.Right:SetSize( 430, 0 )
539 |
540 |
541 | local modeltab = Menu.Right:Add( "DPropertySheet" )
542 | Menu.Right:AddSheet( "Model", modeltab, "icon16/user.png" )
543 |
544 | local t = modeltab:Add( "DLabel" )
545 | t:SetPos( 129, 1 )
546 | --t:SetSize( 100, 20 )
547 | t:SetText( "Search:" )
548 |
549 | Menu.ModelFilter = modeltab:Add( "DTextEntry" )
550 | Menu.ModelFilter:SetPos( 168, 1 )
551 | Menu.ModelFilter:SetSize( 246, 20 )
552 | Menu.ModelFilter:SetUpdateOnType( true )
553 | Menu.ModelFilter.OnValueChange = function() Menu.ModelPopulate() end
554 |
555 | local ModelScroll = modeltab:Add( "DScrollPanel" )
556 | modeltab:AddSheet( "Icons", ModelScroll, "icon16/application_view_tile.png" )
557 | ModelScroll:DockMargin( 2, 0, 2, 2 )
558 | ModelScroll:Dock( FILL )
559 |
560 | local ModelIconLayout = ModelScroll:Add( "DIconLayout" )
561 | ModelIconLayout:SetSpaceX( 2 )
562 | ModelIconLayout:SetSpaceY( 2 )
563 | ModelIconLayout:Dock( FILL )
564 |
565 | local modelicons = { }
566 |
567 |
568 | local ModelList = modeltab:Add( "DListView" )
569 | modeltab:AddSheet( "Table", ModelList, "icon16/application_view_list.png" )
570 | ModelList:DockMargin( 5, 0, 5, 5 )
571 | ModelList:Dock( FILL )
572 | ModelList:SetMultiSelect( false )
573 | ModelList:AddColumn( "Model" )
574 | ModelList:AddColumn( "Path" )
575 | ModelList.OnRowSelected = function()
576 | local sel = ModelList:GetSelected()
577 | if !sel[1] then return end
578 | local name = tostring( sel[1]:GetValue(1) )
579 | RunConsoleCommand( "cl_playermodel", name )
580 | RunConsoleCommand( "cl_playerbodygroups", "0" )
581 | RunConsoleCommand( "cl_playerskin", "0" )
582 | RunConsoleCommand( "cl_playerflexes", "0" )
583 | timer.Simple( 0.1, function() Menu.UpdateFromConvars() end )
584 | end
585 |
586 | local AllModels = player_manager.AllValidModels()
587 |
588 | function Menu.ModelPopulate()
589 |
590 | ModelIconLayout:Clear()
591 | ModelList:Clear()
592 |
593 | local ModelFilter = Menu.ModelFilter:GetValue() or nil
594 |
595 | local function IsInFilter( name )
596 | if not ModelFilter or ModelFilter == "" then
597 | return true
598 | else
599 | local tbl = string.Split( ModelFilter, " " )
600 | for _, substr in pairs( tbl ) do
601 | if not string.match( name:lower(), string.PatternSafe( substr:lower() ) ) then
602 | return false
603 | end
604 | end
605 | return true
606 | end
607 | end
608 |
609 | for name, model in SortedPairs( AllModels ) do
610 |
611 | if IsInFilter( name ) then
612 |
613 | local icon = ModelIconLayout:Add( "SpawnIcon" )
614 | icon:SetSize( 64, 64 )
615 | --icon:InvalidateLayout( true )
616 | icon:SetModel( model )
617 | icon:SetTooltip( name )
618 | table.insert( modelicons, icon )
619 | icon.DoClick = function()
620 | RunConsoleCommand( "cl_playermodel", name )
621 | RunConsoleCommand( "cl_playerbodygroups", "0" )
622 | RunConsoleCommand( "cl_playerskin", "0" )
623 | RunConsoleCommand( "cl_playerflexes", "0" )
624 | timer.Simple( 0.1, function() Menu.UpdateFromConvars() end )
625 | end
626 |
627 | ModelList:AddLine( name, model )
628 |
629 | end
630 |
631 | end
632 |
633 | end
634 |
635 | Menu.ModelPopulate()
636 |
637 |
638 | local favorites = Menu.Right:Add( "DPanel" )
639 | Menu.Right:AddSheet( "Favorites", favorites, "icon16/star.png" )
640 | favorites:DockPadding( 8, 8, 8, 8 )
641 |
642 | local FavList = favorites:Add( "DListView" )
643 | FavList:Dock( FILL )
644 | FavList:SetMultiSelect( true )
645 | FavList:AddColumn( "Favorites" )
646 | FavList:AddColumn( "Model" )
647 | FavList:AddColumn( "Skin" ):SetFixedWidth( 25 )
648 | FavList:AddColumn( "Bodygroups" )
649 | FavList.DoDoubleClick = function( id, sel )
650 | local name = tostring( FavList:GetLine( sel ):GetValue( 1 ) )
651 | if istable( Favorites[name] ) then
652 | RunConsoleCommand( "cl_playermodel", Favorites[name].model )
653 | RunConsoleCommand( "cl_playerbodygroups", Favorites[name].bodygroups )
654 | RunConsoleCommand( "cl_playerskin", Favorites[name].skin )
655 | timer.Simple( 0.1, function()
656 | Menu.UpdateFromConvars()
657 | end )
658 | end
659 | end
660 |
661 | function Menu.FavPopulate()
662 | FavList:Clear()
663 | for k, v in pairs( Favorites ) do
664 | FavList:AddLine( k, v.model, v.skin, v.bodygroups )
665 | end
666 | FavList:SortByColumn( 1 )
667 | end
668 | Menu.FavPopulate()
669 |
670 | local b = favorites:Add( "DButton" )
671 | b:Dock( TOP )
672 | b:SetHeight( 25 )
673 | b:DockMargin( 0, 0, 200, 10 )
674 | b:SetText( "Load selected Favorite" )
675 | b.DoClick = function()
676 | local sel = FavList:GetSelected()
677 | if !sel[1] then return end
678 | local name = tostring( sel[1]:GetValue(1) )
679 | if istable( Favorites[name] ) then
680 | RunConsoleCommand( "cl_playermodel", Favorites[name].model )
681 | RunConsoleCommand( "cl_playerbodygroups", Favorites[name].bodygroups )
682 | RunConsoleCommand( "cl_playerskin", Favorites[name].skin )
683 | timer.Simple( 0.1, function()
684 | Menu.UpdateFromConvars()
685 | end )
686 | end
687 | end
688 |
689 | local t = favorites:Add( "DLabel" )
690 | t:Dock( BOTTOM )
691 | t:SetAutoStretchVertical( true )
692 | t:SetText( "Here you can save your favorite playermodel combinations. To do this:\n1. Select a model and setup the skin and bodygroups as you wish.\n2. Enter a unique name into the textfield and click \"Add new favorite\".\n3. Load your favorite by selecting it in the list below and clicking \"Load selected\".\nYou can also apply existing favorites by console command:\nplayermodel_loadfav \"the favorite's name\"" )
693 | t:SetDark( true )
694 | t:SetWrap( true )
695 |
696 | local control = favorites:Add( "DPanel" )
697 | control:Dock( BOTTOM )
698 | control:DockMargin( 0, 10, 0, 0 )
699 | control:SetSize( 0, 60 )
700 | control:SetPaintBackground( false )
701 |
702 | function Menu.FavAdd( name )
703 | Favorites[name] = { }
704 | Favorites[name].model = LocalPlayer():GetInfo( "cl_playermodel" )
705 | Favorites[name].skin = LocalPlayer():GetInfoNum( "cl_playerskin", 0 )
706 | Favorites[name].bodygroups = LocalPlayer():GetInfo( "cl_playerbodygroups" )
707 | file.Write( "lf_playermodel_selector/cl_favorites.txt", util.TableToJSON( Favorites, true ) )
708 | Menu.FavPopulate()
709 | end
710 |
711 | local FavEntry = control:Add( "DTextEntry" )
712 | FavEntry:SetPos( 0, 0 )
713 | FavEntry:SetSize( 395, 20 )
714 |
715 | local b = control:Add( "DButton" )
716 | b:SetPos( 0, 30 )
717 | b:SetSize( 125, 20 )
718 | b:SetText( "Add new favorite" )
719 | b.DoClick = function()
720 | local name = FavEntry:GetValue()
721 | if name == "" then return end
722 | Menu.FavAdd( name )
723 | end
724 |
725 | local b = control:Add( "DButton" )
726 | b:SetPos( 135, 30 )
727 | b:SetSize( 125, 20 )
728 | b:SetText( "Replace selected" )
729 | b.DoClick = function()
730 | local sel = FavList:GetSelected()
731 | if sel[2] then return end
732 | if !sel[1] then return end
733 | local name = tostring( sel[1]:GetValue(1) )
734 | Menu.FavAdd( name )
735 | end
736 |
737 | local b = control:Add( "DButton" )
738 | b:SetPos( 270, 30 )
739 | b:SetSize( 125, 20 )
740 | b:SetText( "Delete all selected" )
741 | b.DoClick = function()
742 | local sel = FavList:GetSelected()
743 | for k, v in pairs( sel ) do
744 | local name = tostring( v:GetValue(1) )
745 | Favorites[name] = nil
746 | end
747 | file.Write( "lf_playermodel_selector/cl_favorites.txt", util.TableToJSON( Favorites, true ) )
748 | Menu.FavPopulate()
749 | end
750 |
751 |
752 | local bdcontrols = Menu.Right:Add( "DPanel" )
753 | local bgtab = Menu.Right:AddSheet( "Bodygroups", bdcontrols, "icon16/group.png" )
754 | bdcontrols:DockPadding( 8, 8, 8, 8 )
755 |
756 | local bdcontrolspanel = bdcontrols:Add( "DPanelList" )
757 | bdcontrolspanel:EnableVerticalScrollbar( true )
758 | bdcontrolspanel:Dock( FILL )
759 |
760 |
761 | local flexcontrols = Menu.Right:Add( "DPanel" )
762 | local flextab = Menu.Right:AddSheet( "Flexes", flexcontrols, "icon16/emoticon_wink.png" )
763 | flexcontrols:DockPadding( 8, 8, 8, 8 )
764 |
765 | local flexcontrolspanel = flexcontrols:Add( "DPanelList" )
766 | flexcontrolspanel:EnableVerticalScrollbar( true )
767 | flexcontrolspanel:Dock( FILL )
768 |
769 |
770 | local controls = Menu.Right:Add( "DPanel" )
771 | Menu.Right:AddSheet( "Colors", controls, "icon16/color_wheel.png" )
772 | controls:DockPadding( 8, 8, 8, 8 )
773 |
774 | local lbl = controls:Add( "DLabel" )
775 | lbl:SetText( "Player color" )
776 | lbl:SetTextColor( Color( 0, 0, 0, 255 ) )
777 | lbl:Dock( TOP )
778 |
779 | local plycol = controls:Add( "DColorMixer" )
780 | plycol:SetAlphaBar( false )
781 | plycol:SetPalette( false )
782 | plycol:Dock( TOP )
783 | plycol:SetSize( 200, ( fh - 160) / 2 )
784 |
785 | local lbl = controls:Add( "DLabel" )
786 | lbl:SetText( "Physgun color" )
787 | lbl:SetTextColor( Color( 0, 0, 0, 255 ) )
788 | lbl:DockMargin( 0, 8, 0, 0 )
789 | lbl:Dock( TOP )
790 |
791 | local wepcol = controls:Add( "DColorMixer" )
792 | wepcol:SetAlphaBar( false )
793 | wepcol:SetPalette( false )
794 | wepcol:Dock( TOP )
795 | wepcol:SetSize( 200, ( fh - 160) / 2 )
796 | wepcol:SetVector( Vector( GetConVar( "cl_weaponcolor" ):GetString() ) )
797 |
798 | local b = controls:Add( "DButton" )
799 | b:DockMargin( 0, 8, 0, 0 )
800 | b:Dock( TOP )
801 | b:SetSize( 150, 20 )
802 | b:SetText( "Reset to default values" )
803 | b.DoClick = function()
804 | plycol:SetVector( Vector( 0.24, 0.34, 0.41 ) )
805 | wepcol:SetVector( Vector( 0.30, 1.80, 2.10 ) )
806 | RunConsoleCommand( "cl_playercolor", "0.24 0.34 0.41" )
807 | RunConsoleCommand( "cl_weaponcolor", "0.30 1.80 2.10" )
808 | end
809 |
810 |
811 | local moretab = Menu.Right:Add( "DPropertySheet" )
812 | Menu.Right:AddSheet( "Settings", moretab, "icon16/key.png" )
813 |
814 |
815 | local panel = moretab:Add( "DPanel" )
816 | moretab:AddSheet( "Client", panel, "icon16/status_online.png" )
817 | panel:DockPadding( 10, 10, 10, 10 )
818 |
819 | local panel = panel:Add( "DScrollPanel" )
820 | panel:Dock( FILL )
821 |
822 | local c = panel:Add( "DCheckBoxLabel" )
823 | c.cvar = "cl_playermodel_selector_force"
824 | c:Dock( TOP )
825 | c:DockMargin( 0, 0, 0, 5 )
826 | c:SetValue( GetConVar(c.cvar):GetBool() )
827 | c:SetText( "Enforce your playermodel" )
828 | c:SetDark( true )
829 | c.OnChange = function( p, v )
830 | RunConsoleCommand( c.cvar, v == true and "1" or "0" )
831 | end
832 |
833 | local t = panel:Add( "DLabel" )
834 | t:Dock( TOP )
835 | t:DockMargin( 0, 0, 0, 20 )
836 | t:SetAutoStretchVertical( true )
837 | t:SetText( "If enabled, your selected playermodel will be protected. No other function will be able to change your playermodel anymore." )
838 | t:SetDark( true )
839 | t:SetWrap( true )
840 |
841 | local c = panel:Add( "DCheckBoxLabel" )
842 | c.cvar = "cl_playermodel_selector_bgcolor_custom"
843 | c:Dock( TOP )
844 | c:DockMargin( 0, 0, 0, 5 )
845 | c:SetValue( GetConVar(c.cvar):GetBool() )
846 | c:SetText( "Use Player color as background" )
847 | c:SetDark( true )
848 | c:SizeToContents()
849 | c.OnChange = function( p, v )
850 | RunConsoleCommand( c.cvar, v == true and "1" or "0" )
851 | end
852 |
853 | local t = panel:Add( "DLabel" )
854 | t:Dock( TOP )
855 | t:DockMargin( 0, 0, 0, 20 )
856 | t:SetAutoStretchVertical( true )
857 | t:SetText( "If enabled, your selected player color will be used as the menu background. If disabled, the background will be grey." )
858 | t:SetDark( true )
859 | t:SetWrap( true )
860 |
861 | local c = panel:Add( "DCheckBoxLabel" )
862 | c.cvar = "cl_playermodel_selector_bgcolor_trans"
863 | c:Dock( TOP )
864 | c:DockMargin( 0, 0, 0, 5 )
865 | c:SetValue( GetConVar(c.cvar):GetBool() )
866 | c:SetText( "Transparent background" )
867 | c:SetDark( true )
868 | c:SizeToContents()
869 | c.OnChange = function( p, v )
870 | RunConsoleCommand( c.cvar, v == true and "1" or "0" )
871 | end
872 |
873 | local t = panel:Add( "DLabel" )
874 | t:Dock( TOP )
875 | t:DockMargin( 0, 0, 0, 20 )
876 | t:SetAutoStretchVertical( true )
877 | t:SetText( "If enabled, the menu backgroup will be transparent. If disabled, the background will be opaque." )
878 | t:SetDark( true )
879 | t:SetWrap( true )
880 |
881 | local c = panel:Add( "DCheckBoxLabel" )
882 | c.cvar = "cl_playermodel_selector_unlockflexes"
883 | c:Dock( TOP )
884 | c:DockMargin( 0, 0, 0, 5 )
885 | c:SetValue( GetConVar(c.cvar):GetBool() )
886 | c:SetText( "Show flexes tab" )
887 | c:SetDark( true )
888 | c:SizeToContents()
889 | c.OnChange = function( p, v )
890 | RunConsoleCommand( c.cvar, v == true and "1" or "0" )
891 | timer.Simple( 0, function() Menu.RebuildBodygroupTab() end )
892 | end
893 |
894 | local t = panel:Add( "DLabel" )
895 | t:Dock( TOP )
896 | t:DockMargin( 0, 0, 0, 20 )
897 | t:SetAutoStretchVertical( true )
898 | t:SetText( "This allows you to manipulate flexes on your playermodel. However, flex manipulation is not really made for playermodels and will cause issues. This includes the following:\n- Eye blinking no longer working.\n- Faces might be distorted unless the flexes are corrected manually.\n- Might break the faces of incompatible playermodels completely.\n- Even if you put all flexes to default value, the engine still considers them as manipulated. Models with problems won't be fixed.\nYou must switch your model once, for the tab to appear!" )
899 | t:SetDark( true )
900 | t:SetWrap( true )
901 |
902 | local b = panel:Add( "DButton" )
903 | b:Dock( TOP )
904 | b:DockMargin( 0, 0, 270, 5 )
905 | b:SetHeight( 15 )
906 | b:SetText( "Rebuild spawn icons" )
907 | b.DoClick = function()
908 | for _, icon in pairs( modelicons ) do
909 | icon:RebuildSpawnIcon()
910 | end
911 | end
912 |
913 | local t = panel:Add( "DLabel" )
914 | t:Dock( TOP )
915 | t:DockMargin( 0, 0, 0, 20 )
916 | t:SetAutoStretchVertical( true )
917 | t:SetText( "Forces all playermodel icons to be re-rendered. Useful if the icons are outdated after custom models changed their appearance. This may take a while, depending on the number of models and your PC's speed." )
918 | t:SetDark( true )
919 | t:SetWrap( true )
920 |
921 |
922 | if LocalPlayer():IsAdmin() then
923 |
924 | local panel = moretab:Add( "DPanel" )
925 | moretab:AddSheet( "Server", panel, "icon16/world.png" )
926 | panel:DockPadding( 10, 10, 10, 10 )
927 |
928 | local panel = panel:Add( "DScrollPanel" )
929 | panel:Dock( FILL )
930 |
931 | local function ChangeCVar( p, v )
932 | net.Start("lf_playermodel_cvar_change")
933 | net.WriteString( p.cvar )
934 | net.WriteString( v == true and "1" or "0" )
935 | net.SendToServer()
936 | end
937 |
938 | local c = panel:Add( "DCheckBoxLabel" )
939 | c.cvar = "sv_playermodel_selector_force"
940 | c:Dock( TOP )
941 | c:DockMargin( 0, 0, 0, 5 )
942 | c:SetValue( GetConVar(c.cvar):GetBool() )
943 | c:SetText( "Enable playermodel enforcement" )
944 | c:SetDark( true )
945 | c.OnChange = ChangeCVar
946 |
947 | local t = panel:Add( "DLabel" )
948 | t:Dock( TOP )
949 | t:DockMargin( 0, 0, 0, 20 )
950 | t:SetAutoStretchVertical( true )
951 | t:SetText( "If enabled, selected playermodels will be enforced and protected. No gamemodes, maps or addons can overwrite them anymore. Players can toggle this function individually, using the checkbox on top of the menu.\nIf disabled, only the manual button works outside of Sandbox." )
952 | t:SetDark( true )
953 | t:SetWrap( true )
954 |
955 | local c = panel:Add( "DCheckBoxLabel" )
956 | c.cvar = "sv_playermodel_selector_instantly"
957 | c:Dock( TOP )
958 | c:DockMargin( 0, 0, 0, 5 )
959 | c:SetValue( GetConVar(c.cvar):GetBool() )
960 | c:SetText( "Allow instant changes" )
961 | c:SetDark( true )
962 | c.OnChange = ChangeCVar
963 |
964 | local t = panel:Add( "DLabel" )
965 | t:Dock( TOP )
966 | t:DockMargin( 0, 0, 0, 20 )
967 | t:SetAutoStretchVertical( true )
968 | t:SetText( "If enabled, players can apply their changes instantly instead of having to respawn." )
969 | t:SetDark( true )
970 | t:SetWrap( true )
971 |
972 | local c = panel:Add( "DCheckBoxLabel" )
973 | c.cvar = "sv_playermodel_selector_flexes"
974 | c:Dock( TOP )
975 | c:DockMargin( 0, 0, 0, 5 )
976 | c:SetValue( GetConVar(c.cvar):GetBool() )
977 | c:SetText( "Allow players to change flexes" )
978 | c:SetDark( true )
979 | c.OnChange = ChangeCVar
980 |
981 | local t = panel:Add( "DLabel" )
982 | t:Dock( TOP )
983 | t:DockMargin( 0, 0, 0, 20 )
984 | t:SetAutoStretchVertical( true )
985 | t:SetText( "If enabled, players can change the flexes for their playermodels. This will break player blinking and may cause other issues. Enable at own risk. Players can only reset their flexes by disconnecting." )
986 | t:SetDark( true )
987 | t:SetWrap( true )
988 |
989 | local c = panel:Add( "DCheckBoxLabel" )
990 | c.cvar = "sv_playermodel_selector_gamemodes"
991 | c:Dock( TOP )
992 | c:DockMargin( 0, 0, 0, 5 )
993 | c:SetValue( GetConVar(c.cvar):GetBool() )
994 | c:SetText( "Enable in all gamemodes" )
995 | c:SetDark( true )
996 | c.OnChange = ChangeCVar
997 |
998 | local t = panel:Add( "DLabel" )
999 | t:Dock( TOP )
1000 | t:DockMargin( 0, 0, 0, 20 )
1001 | t:SetAutoStretchVertical( true )
1002 | t:SetText( "If enabled, the PlayerModel Selector will be available for all players in every gamemode. If disabled, only Admins can use it outside of Sandbox." )
1003 | t:SetDark( true )
1004 | t:SetWrap( true )
1005 |
1006 | local s = panel:Add( "DNumSlider" )
1007 | s.cvar = "sv_playermodel_selector_limit"
1008 | s:Dock( TOP )
1009 | s:DockMargin( 0, 0, 0, 5 )
1010 | s:SetText( "Request limit" )
1011 | s:SetDark( true )
1012 | s:SetDecimals( 0 )
1013 | s:SetMax( 900 )
1014 | s:SetValue( GetConVar( "sv_playermodel_selector_limit" ):GetInt() )
1015 | s.OnValueChanged = function( val )
1016 | net.Start("lf_playermodel_cvar_change")
1017 | net.WriteString( s.cvar )
1018 | net.WriteString( tostring( math.floor( val:GetValue(1) ) ) )
1019 | net.SendToServer()
1020 | end
1021 |
1022 | local t = panel:Add( "DLabel" )
1023 | t:Dock( TOP )
1024 | t:DockMargin( 0, 0, 0, 20 )
1025 | t:SetAutoStretchVertical( true )
1026 | t:SetText( "Timelimit in seconds that players have to wait, before they can use the instant change function again. Set to 0 to disable." )
1027 | t:SetDark( true )
1028 | t:SetWrap( true )
1029 |
1030 |
1031 | local panel = moretab:Add( "DPanel" )
1032 | moretab:AddSheet( "GM Blacklist", panel, "icon16/delete.png" )
1033 | panel:DockPadding( 10, 10, 10, 10 )
1034 |
1035 | local Blacklist = panel:Add( "DListView" )
1036 | Blacklist:Dock( LEFT )
1037 | Blacklist:DockMargin( 0, 0, 20, 0 )
1038 | Blacklist:SetWidth( 150 )
1039 | Blacklist:SetMultiSelect( true )
1040 | Blacklist:AddColumn( "Blacklisted gamemodes" )
1041 |
1042 | net.Receive("lf_playermodel_blacklist", function()
1043 | local tbl = net.ReadTable()
1044 | Blacklist:Clear()
1045 | for k, v in pairs( tbl ) do
1046 | Blacklist:AddLine( k )
1047 | end
1048 | Blacklist:SortByColumn( 1 )
1049 | end )
1050 |
1051 | function Menu.BlacklistPopulate()
1052 | net.Start( "lf_playermodel_blacklist" )
1053 | net.WriteInt( 0, 3 )
1054 | net.SendToServer()
1055 | end
1056 | Menu.BlacklistPopulate()
1057 |
1058 | local t = panel:Add( "DLabel" )
1059 | t:Dock( TOP )
1060 | t:DockMargin( 0, 0, 0, 20 )
1061 | t:SetAutoStretchVertical( true )
1062 | t:SetText( "Here you can blacklist incompatible gamemodes.\n\nPlayers (including Admins) can't change their playermodels in those gamemodes, regardless of other settings." )
1063 | t:SetDark( true )
1064 | t:SetWrap( true )
1065 |
1066 | local b = panel:Add( "DButton" )
1067 | b:Dock( TOP )
1068 | b:DockMargin( 0, 0, 0, 20 )
1069 | b:SetHeight( 25 )
1070 | b:SetText( "Add current gamemode to Blacklist" )
1071 | b.DoClick = function()
1072 | if GAMEMODE_NAME == "sandbox" then return end
1073 | net.Start( "lf_playermodel_blacklist" )
1074 | net.WriteInt( 1, 3 )
1075 | net.WriteString( GAMEMODE_NAME )
1076 | net.SendToServer()
1077 | end
1078 |
1079 | local TextEntry = panel:Add( "DTextEntry" )
1080 | TextEntry:Dock( TOP )
1081 | TextEntry:DockMargin( 0, 0, 0, 10 )
1082 | TextEntry:SetHeight( 20 )
1083 |
1084 | local b = panel:Add( "DButton" )
1085 | b:Dock( TOP )
1086 | b:DockMargin( 0, 0, 0, 20 )
1087 | b:SetHeight( 20 )
1088 | b:SetText( "Manually add gamemode" )
1089 | b.DoClick = function()
1090 | local name = TextEntry:GetValue()
1091 | if name == "" or name == "sandbox" then return end
1092 | net.Start( "lf_playermodel_blacklist" )
1093 | net.WriteInt( 1, 3 )
1094 | net.WriteString( name )
1095 | net.SendToServer()
1096 | end
1097 |
1098 | local b = panel:Add( "DButton" )
1099 | b:Dock( TOP )
1100 | b:DockMargin( 0, 0, 0, 0 )
1101 | b:SetHeight( 25 )
1102 | b:SetText( "Remove selected gamemodes" )
1103 | b.DoClick = function()
1104 | local tbl = { }
1105 | local sel = Blacklist:GetSelected()
1106 | for k, v in pairs( sel ) do
1107 | local name = tostring( v:GetValue(1) )
1108 | table.insert( tbl, name )
1109 | end
1110 | net.Start( "lf_playermodel_blacklist" )
1111 | net.WriteInt( 2, 3 )
1112 | net.WriteTable( tbl )
1113 | net.SendToServer()
1114 | end
1115 |
1116 |
1117 | if TFAVOX_Models then
1118 |
1119 | local panel = moretab:Add( "DPanel" )
1120 | moretab:AddSheet( "VOX", panel, "icon16/sound.png" )
1121 | panel:DockPadding( 10, 10, 10, 10 )
1122 |
1123 | local VOXlist = panel:Add( "DListView" )
1124 | VOXlist:Dock( TOP )
1125 | VOXlist:DockMargin( 0, 0, 0, 10 )
1126 | VOXlist:SetHeight( ( fh - 126 - 44 ) / 2 ) -- 260
1127 | VOXlist:SetMultiSelect( true )
1128 | VOXlist:AddColumn( "PlayerModel" )
1129 | VOXlist:AddColumn( "assigned VOX pack" )
1130 |
1131 | net.Receive("lf_playermodel_voxlist", function()
1132 | local tbl = net.ReadTable()
1133 | VOXlist:Clear()
1134 | for k, v in pairs( tbl ) do
1135 | VOXlist:AddLine( string.StripExtension( string.gsub( k, "models/", "", 1 ) ), string.StripExtension( string.gsub( v, "models/", "", 1 ) ) )
1136 | end
1137 | VOXlist:SortByColumn( 1 )
1138 | end )
1139 |
1140 | function Menu.VOXlistPopulate()
1141 | net.Start( "lf_playermodel_voxlist" )
1142 | net.WriteInt( 0, 3 )
1143 | net.SendToServer()
1144 | end
1145 | Menu.VOXlistPopulate()
1146 |
1147 | local control = panel:Add( "DPanel" )
1148 | control:Dock( TOP )
1149 | control:DockMargin( 0, 0, 0, 0 )
1150 | --control:SetSize( 0, 60 )
1151 | control:SetPaintBackground( false )
1152 |
1153 | local VOXinstalled = panel:Add( "DListView" )
1154 | VOXinstalled:Dock( TOP )
1155 | VOXinstalled:DockMargin( 0, 10, 0, 0 )
1156 | VOXinstalled:SetHeight( ( fh - 126 - 44 ) / 2 )
1157 | VOXinstalled:SetMultiSelect( false )
1158 | VOXinstalled:AddColumn( "Available VOX packs" )
1159 |
1160 | if istable( TFAVOX_Models ) then
1161 | for k, v in pairs( TFAVOX_Models ) do
1162 | VOXinstalled:AddLine( string.StripExtension( string.gsub( k, "models/", "", 1 ) ) )
1163 | end
1164 | VOXinstalled:SortByColumn( 1 )
1165 | end
1166 |
1167 | local b = control:Add( "DButton" )
1168 | b:Dock( LEFT )
1169 | --b:DockPadding( 100, 0, 100, 0 )
1170 | b:SetWidth( 200 )
1171 | b:SetText( "Assign VOX pack to current PlayerModel" )
1172 | b.DoClick = function()
1173 | local sel = VOXinstalled:GetSelected()
1174 | if !sel[1] then return end
1175 | local v = "models/"..tostring( sel[1]:GetValue(1)..".mdl" )
1176 | local k = string.lower( player_manager.TranslatePlayerModel( LocalPlayer():GetInfo( "cl_playermodel" ) ) )
1177 | net.Start( "lf_playermodel_voxlist" )
1178 | net.WriteInt( 1, 3 )
1179 | net.WriteString( k )
1180 | net.WriteString( v )
1181 | net.SendToServer()
1182 | end
1183 |
1184 | local b = control:Add( "DButton" )
1185 | b:Dock( RIGHT )
1186 | --b:DockPadding( 100, 0, 100, 0 )
1187 | b:SetWidth( 170 )
1188 | b:SetText( "Remove selected assignment" )
1189 | b.DoClick = function()
1190 | local tbl = { }
1191 | local sel = VOXlist:GetSelected()
1192 | for k, v in pairs( sel ) do
1193 | local name = "models/"..tostring( v:GetValue(1)..".mdl" )
1194 | table.insert( tbl, name )
1195 | end
1196 | net.Start( "lf_playermodel_voxlist" )
1197 | net.WriteInt( 2, 3 )
1198 | net.WriteTable( tbl )
1199 | net.SendToServer()
1200 | end
1201 |
1202 | end
1203 |
1204 | end
1205 |
1206 |
1207 | local panel = moretab:Add( "DPanel" )
1208 | moretab:AddSheet( "Info", panel, "icon16/information.png" )
1209 | panel:DockPadding( 0, 0, 0, 0 )
1210 |
1211 | local t = panel:Add( "DHTML" )
1212 | t:Dock( FILL )
1213 | --t:DockMargin( 0, 0, 0, 15 )
1214 | --t:SetHeight( 260 )
1215 | t:SetAllowLua( true )
1216 | t:AddFunction( "url", "open", function( str ) gui.OpenURL( str ) end )
1217 | t:AddFunction( "url", "copy", function( str ) SetClipboardText( str ) end )
1218 |
1219 | local intro = [[Created by LibertyForce.
Thank you for installing this addon! Enjoying it?
1220 | Please leave a LIKE on the workshop page.]]
1221 | if !game.SinglePlayer() and !LocalPlayer():IsSuperAdmin() then
1222 | intro = [[This server is running Enhanced PlayerModel Selector by LibertyForce. Enjoying it?
1223 | Click here to download this addon for SinglePlayer.]]
1224 | end
1225 |
1226 | t:SetHTML( [[
1227 |
1228 |
]]..intro..[[
1271 |Enhanced Playermodel Selector provides additional functionality with those addons installed: 1273 |