├── .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 | 1229 | 1267 | 1268 | 1269 |

Enhanced Playermodel Selector ]]..Version..[[

1270 |

]]..intro..[[

1271 |

Compatible Addons

1272 |

Enhanced Playermodel Selector provides additional functionality with those addons installed: 1273 |

1277 |

More addons

1278 |

1287 |

Left click: Open in Steam Overlay.
Right click: Copy URL to clipboard for use in browser.

1288 | 1289 | 1290 | ]] ) 1291 | 1292 | 1293 | 1294 | 1295 | -- Helper functions 1296 | 1297 | function Menu.MakeNiceName( str ) 1298 | local newname = {} 1299 | 1300 | for _, s in pairs( string.Explode( "_", str ) ) do 1301 | if ( string.len( s ) == 1 ) then table.insert( newname, string.upper( s ) ) continue end 1302 | table.insert( newname, string.upper( string.Left( s, 1 ) ) .. string.Right( s, string.len( s ) - 1 ) ) -- Ugly way to capitalize first letters. 1303 | end 1304 | 1305 | return string.Implode( " ", newname ) 1306 | end 1307 | 1308 | function Menu.PlayPreviewAnimation( panel, playermodel ) 1309 | 1310 | if ( !panel or !IsValid( panel.Entity ) ) then return end 1311 | 1312 | local anims = list.Get( "PlayerOptionsAnimations" ) 1313 | 1314 | local anim = default_animations[ math.random( 1, #default_animations ) ] 1315 | if ( anims[ playermodel ] ) then 1316 | anims = anims[ playermodel ] 1317 | anim = anims[ math.random( 1, #anims ) ] 1318 | end 1319 | 1320 | local iSeq = panel.Entity:LookupSequence( anim ) 1321 | if ( iSeq > 0 ) then panel.Entity:ResetSequence( iSeq ) end 1322 | 1323 | end 1324 | 1325 | -- Updating 1326 | 1327 | function Menu.UpdateBodyGroups( pnl, val ) 1328 | if ( pnl.type == "bgroup" ) then 1329 | 1330 | mdl.Entity:SetBodygroup( pnl.typenum, math.Round( val ) ) 1331 | 1332 | local str = string.Explode( " ", GetConVar( "cl_playerbodygroups" ):GetString() ) 1333 | if ( #str < pnl.typenum + 1 ) then for i = 1, pnl.typenum + 1 do str[ i ] = str[ i ] or 0 end end 1334 | str[ pnl.typenum + 1 ] = math.Round( val ) 1335 | RunConsoleCommand( "cl_playerbodygroups", table.concat( str, " " ) ) 1336 | 1337 | elseif ( pnl.type == "flex" ) then 1338 | 1339 | mdl.Entity:SetFlexWeight( pnl.typenum, math.Round( val, 2 ) ) 1340 | 1341 | local str = string.Explode( " ", GetConVar( "cl_playerflexes" ):GetString() ) 1342 | if ( #str < pnl.typenum + 1 ) then for i = 1, pnl.typenum + 1 do str[ i ] = str[ i ] or 0 end end 1343 | str[ pnl.typenum + 1 ] = math.Round( val, 2 ) 1344 | RunConsoleCommand( "cl_playerflexes", table.concat( str, " " ) ) 1345 | 1346 | elseif ( pnl.type == "skin" ) then 1347 | 1348 | mdl.Entity:SetSkin( math.Round( val ) ) 1349 | RunConsoleCommand( "cl_playerskin", math.Round( val ) ) 1350 | 1351 | end 1352 | end 1353 | 1354 | function Menu.RebuildBodygroupTab() 1355 | bdcontrolspanel:Clear() 1356 | flexcontrolspanel:Clear() 1357 | 1358 | bgtab.Tab:SetVisible( false ) 1359 | flextab.Tab:SetVisible( false ) 1360 | 1361 | local nskins = mdl.Entity:SkinCount() - 1 1362 | if ( nskins > 0 ) then 1363 | local skins = vgui.Create( "DNumSlider" ) 1364 | skins:Dock( TOP ) 1365 | skins:SetText( "Skin" ) 1366 | skins:SetDark( true ) 1367 | skins:SetTall( 50 ) 1368 | skins:SetDecimals( 0 ) 1369 | skins:SetMax( nskins ) 1370 | skins:SetValue( GetConVar( "cl_playerskin" ):GetInt() ) 1371 | skins.type = "skin" 1372 | skins.OnValueChanged = Menu.UpdateBodyGroups 1373 | 1374 | bdcontrolspanel:AddItem( skins ) 1375 | 1376 | mdl.Entity:SetSkin( GetConVar( "cl_playerskin" ):GetInt() ) 1377 | 1378 | bgtab.Tab:SetVisible( true ) 1379 | end 1380 | 1381 | local groups = string.Explode( " ", GetConVar( "cl_playerbodygroups" ):GetString() ) 1382 | for k = 0, mdl.Entity:GetNumBodyGroups() - 1 do 1383 | if ( mdl.Entity:GetBodygroupCount( k ) <= 1 ) then continue end 1384 | 1385 | local bgroup = vgui.Create( "DNumSlider" ) 1386 | bgroup:Dock( TOP ) 1387 | bgroup:SetText( Menu.MakeNiceName( mdl.Entity:GetBodygroupName( k ) ) ) 1388 | bgroup:SetDark( true ) 1389 | bgroup:SetTall( 50 ) 1390 | bgroup:SetDecimals( 0 ) 1391 | bgroup.type = "bgroup" 1392 | bgroup.typenum = k 1393 | bgroup:SetMax( mdl.Entity:GetBodygroupCount( k ) - 1 ) 1394 | bgroup:SetValue( groups[ k + 1 ] or 0 ) 1395 | bgroup.OnValueChanged = Menu.UpdateBodyGroups 1396 | 1397 | bdcontrolspanel:AddItem( bgroup ) 1398 | 1399 | mdl.Entity:SetBodygroup( k, groups[ k + 1 ] or 0 ) 1400 | 1401 | bgtab.Tab:SetVisible( true ) 1402 | end 1403 | 1404 | if GetConVar( "sv_playermodel_selector_flexes" ):GetBool() and GetConVar( "cl_playermodel_selector_unlockflexes" ):GetBool() then 1405 | local t = vgui.Create( "DLabel" ) 1406 | t:Dock( TOP ) 1407 | t:SetTall( 70 ) 1408 | t:SetText( "Notes:\n-The model preview for flexes doesn't work correctly. However, they will be visible on your playermodel when you apply them.\n- The default values provided might not be correct and cause distorted faces.\n- There is no way to reset (or fix) flex manipulation besides disconnecting." ) 1409 | t:SetDark( true ) 1410 | t:SetWrap( true ) 1411 | flexcontrolspanel:AddItem( t ) 1412 | 1413 | local flexes = string.Explode( " ", GetConVar( "cl_playerflexes" ):GetString() ) 1414 | for k = 0, mdl.Entity:GetFlexNum() - 1 do 1415 | if ( mdl.Entity:GetFlexNum( k ) <= 1 ) then continue end 1416 | 1417 | local flex = vgui.Create( "DNumSlider" ) 1418 | local vmin, vmax = mdl.Entity:GetFlexBounds( k ) 1419 | local default = 0 1420 | if vmin == -1 and vmax == 1 then default = 0.5 end 1421 | flex:Dock( TOP ) 1422 | flex:SetText( Menu.MakeNiceName( mdl.Entity:GetFlexName( k ) ) ) 1423 | flex:SetDark( true ) 1424 | flex:SetTall( 30 ) 1425 | flex:SetDecimals( 2 ) 1426 | flex.type = "flex" 1427 | flex.typenum = k 1428 | flex:SetMin( vmin ) 1429 | flex:SetMax( vmax ) 1430 | flex:SetValue( flexes[ k + 1 ] or default ) 1431 | flex.OnValueChanged = Menu.UpdateBodyGroups 1432 | 1433 | flexcontrolspanel:AddItem( flex ) 1434 | 1435 | mdl.Entity:SetFlexWeight( k, flexes[ k + 1 ] or default ) 1436 | 1437 | flextab.Tab:SetVisible( true ) 1438 | end 1439 | end 1440 | 1441 | Menu.Right.tabScroller:InvalidateLayout( true ) 1442 | Menu.Right:InvalidateLayout( true ) 1443 | end 1444 | 1445 | function Menu.UpdateFromConvars() 1446 | 1447 | local model = LocalPlayer():GetInfo( "cl_playermodel" ) 1448 | local modelname = player_manager.TranslatePlayerModel( model ) 1449 | util.PrecacheModel( modelname ) 1450 | mdl:SetModel( modelname ) 1451 | mdl.Entity.GetPlayerColor = function() return Vector( GetConVar( "cl_playercolor" ):GetString() ) end 1452 | mdl.Entity:SetPos( Vector( -100, 0, -61 ) ) 1453 | 1454 | plycol:SetVector( Vector( GetConVar( "cl_playercolor" ):GetString() ) ) 1455 | wepcol:SetVector( Vector( GetConVar( "cl_weaponcolor" ):GetString() ) ) 1456 | 1457 | Menu.PlayPreviewAnimation( mdl, model ) 1458 | Menu.RebuildBodygroupTab() 1459 | 1460 | end 1461 | 1462 | function Menu.UpdateFromControls() 1463 | 1464 | RunConsoleCommand( "cl_playercolor", tostring( plycol:GetVector() ) ) 1465 | RunConsoleCommand( "cl_weaponcolor", tostring( wepcol:GetVector() ) ) 1466 | 1467 | end 1468 | 1469 | plycol.ValueChanged = Menu.UpdateFromControls 1470 | wepcol.ValueChanged = Menu.UpdateFromControls 1471 | 1472 | Menu.UpdateFromConvars() 1473 | 1474 | -- Hold to rotate 1475 | 1476 | function mdl:DragMousePress( button ) 1477 | self.PressX, self.PressY = gui.MousePos() 1478 | self.Pressed = button 1479 | end 1480 | 1481 | function mdl:OnMouseWheeled( delta ) 1482 | self.WheelD = delta * -10 1483 | self.Wheeled = true 1484 | end 1485 | 1486 | function mdl:DragMouseRelease() self.Pressed = false end 1487 | 1488 | function mdl:LayoutEntity( Entity ) 1489 | if ( self.bAnimated ) then self:RunAnimation() end 1490 | 1491 | if ( self.Pressed == MOUSE_LEFT ) then 1492 | local mx, my = gui.MousePos() 1493 | self.Angles = self.Angles - Angle( 0, ( self.PressX or mx ) - mx, 0 ) 1494 | 1495 | self.PressX, self.PressY = gui.MousePos() 1496 | end 1497 | 1498 | if ( self.Pressed == MOUSE_RIGHT ) then 1499 | local mx, my = gui.MousePos() 1500 | self.Angles = self.Angles - Angle( ( self.PressY*(0.5) or my*(0.5) ) - my*(0.5), 0, ( self.PressX*(-0.5) or mx*(-0.5) ) - mx*(-0.5) ) 1501 | 1502 | self.PressX, self.PressY = gui.MousePos() 1503 | end 1504 | 1505 | if ( self.Pressed == MOUSE_MIDDLE ) then 1506 | local mx, my = gui.MousePos() 1507 | self.Pos = self.Pos - Vector( 0, ( self.PressX*(0.5) or mx*(0.5) ) - mx*(0.5), ( self.PressY*(-0.5) or my*(-0.5) ) - my*(-0.5) ) 1508 | 1509 | self.PressX, self.PressY = gui.MousePos() 1510 | end 1511 | 1512 | if ( self.Wheeled ) then 1513 | self.Wheeled = false 1514 | self.Pos = self.Pos - Vector( self.WheelD, 0, 0 ) 1515 | end 1516 | 1517 | Entity:SetAngles( self.Angles ) 1518 | Entity:SetPos( self.Pos ) 1519 | end 1520 | 1521 | end 1522 | 1523 | function Menu.Toggle() 1524 | if LocalPlayer():IsAdmin() or GAMEMODE_NAME == "sandbox" or GetConVar( "sv_playermodel_selector_gamemodes" ):GetBool() 1525 | then 1526 | if IsValid( Frame ) then 1527 | Frame:ToggleVisible() 1528 | else 1529 | Menu.Setup() 1530 | end 1531 | else 1532 | if IsValid( Frame ) then Frame:Close() end 1533 | end 1534 | end 1535 | 1536 | concommand.Add( "playermodel_selector", Menu.Toggle ) 1537 | 1538 | hook.Add( "PostGamemodeLoaded", "lf_playermodel_desktop_hook", function() 1539 | if GAMEMODE_NAME == "sandbox" then 1540 | list.GetForEdit( "DesktopWindows" ).PlayerEditor.init = function( icon, window ) 1541 | window:Remove() 1542 | RunConsoleCommand( "playermodel_selector" ) 1543 | end 1544 | else 1545 | list.Set( "DesktopWindows", "PlayerEditor", { 1546 | title = "Player Model", 1547 | icon = "icon64/playermodel.png", 1548 | init = function( icon, window ) 1549 | window:Remove() 1550 | RunConsoleCommand( "playermodel_selector" ) 1551 | end 1552 | } ) 1553 | end 1554 | end ) 1555 | 1556 | 1557 | 1558 | list.Set( "PlayerOptionsAnimations", "gman", { "menu_gman" } ) 1559 | 1560 | list.Set( "PlayerOptionsAnimations", "hostage01", { "idle_all_scared" } ) 1561 | list.Set( "PlayerOptionsAnimations", "hostage02", { "idle_all_scared" } ) 1562 | list.Set( "PlayerOptionsAnimations", "hostage03", { "idle_all_scared" } ) 1563 | list.Set( "PlayerOptionsAnimations", "hostage04", { "idle_all_scared" } ) 1564 | 1565 | list.Set( "PlayerOptionsAnimations", "zombine", { "menu_zombie_01" } ) 1566 | list.Set( "PlayerOptionsAnimations", "corpse", { "menu_zombie_01" } ) 1567 | list.Set( "PlayerOptionsAnimations", "zombiefast", { "menu_zombie_01" } ) 1568 | list.Set( "PlayerOptionsAnimations", "zombie", { "menu_zombie_01" } ) 1569 | list.Set( "PlayerOptionsAnimations", "skeleton", { "menu_zombie_01" } ) 1570 | 1571 | list.Set( "PlayerOptionsAnimations", "combine", { "menu_combine" } ) 1572 | list.Set( "PlayerOptionsAnimations", "combineprison", { "menu_combine" } ) 1573 | list.Set( "PlayerOptionsAnimations", "combineelite", { "menu_combine" } ) 1574 | list.Set( "PlayerOptionsAnimations", "police", { "menu_combine" } ) 1575 | list.Set( "PlayerOptionsAnimations", "policefem", { "menu_combine" } ) 1576 | 1577 | list.Set( "PlayerOptionsAnimations", "css_arctic", { "pose_standing_02", "idle_fist" } ) 1578 | list.Set( "PlayerOptionsAnimations", "css_gasmask", { "pose_standing_02", "idle_fist" } ) 1579 | list.Set( "PlayerOptionsAnimations", "css_guerilla", { "pose_standing_02", "idle_fist" } ) 1580 | list.Set( "PlayerOptionsAnimations", "css_leet", { "pose_standing_02", "idle_fist" } ) 1581 | list.Set( "PlayerOptionsAnimations", "css_phoenix", { "pose_standing_02", "idle_fist" } ) 1582 | list.Set( "PlayerOptionsAnimations", "css_riot", { "pose_standing_02", "idle_fist" } ) 1583 | list.Set( "PlayerOptionsAnimations", "css_swat", { "pose_standing_02", "idle_fist" } ) 1584 | list.Set( "PlayerOptionsAnimations", "css_urban", { "pose_standing_02", "idle_fist" } ) 1585 | 1586 | local bonus = { "idle_all_01", "menu_walk", "pose_standing_02", "pose_standing_03", "idle_fist", "pose_standing_01", "pose_standing_04", "swim_idle_all", "idle_all_scared", "idle_magic" } 1587 | local fav = { -- ^_^ -- 1588 | "TFA-May-RS", "TFA-May-ORAS", "May", "Dawn", "Rosa", "Hilda", "Leaf", "Mami", 1589 | "Misaka Mikoto (Summer)", "Misaka Mikoto (Winter)", "Misaka Imoito (Summer)", "Misaka Imoito (Winter)", "Shirai Kuroko (Summer)", "Shirai Kuroko (Winter)", "Uiharu Kazari", "Saten Ruiko", 1590 | "Tda Hatsune Miku (v2)", "YYB Kagamine Rin (v3)", "Appearance Miku (Default)", "Appearance Miku (Stroll)", "Appearance Miku (Cupid)", "Appearance Miku (Colorful Drop)", "Kizuna AI" 1591 | } 1592 | for k, v in pairs( fav ) do 1593 | list.Set( "PlayerOptionsAnimations", v, bonus ) 1594 | end 1595 | 1596 | 1597 | end -------------------------------------------------------------------------------- /lua/tfa_vox/packs/zzz_lf_playermodel_selector.lua: -------------------------------------------------------------------------------- 1 | TFAVOX_Models = TFAVOX_Models or {} 2 | 3 | if SERVER then 4 | --timer.Simple( 0.2, function() 5 | local tbl 6 | if lf_playermodel_selector_get_voxlist and isfunction( lf_playermodel_selector_get_voxlist ) then tbl = lf_playermodel_selector_get_voxlist() end 7 | if istable( tbl ) then 8 | for k, v in pairs( tbl ) do 9 | TFAVOX_Models[k] = TFAVOX_Models[v] 10 | end 11 | end 12 | --end ) 13 | end 14 | --------------------------------------------------------------------------------