├── .gitattributes ├── .github └── workflows │ └── glualint-check.yml ├── .templates ├── RollandinEmilie.ttf ├── plib_addon.psd └── plib_module.psd ├── LICENSE ├── README.md ├── glualint.json └── lua ├── autorun └── !!!sh_plib.lua └── plib ├── entity.lua ├── global.lua ├── player.lua └── utils.lua /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/glualint-check.yml: -------------------------------------------------------------------------------- 1 | name: GLuaFixer 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | gluaLint: 11 | uses: FPtje/GLuaFixer/.github/workflows/glualint.yml@master 12 | -------------------------------------------------------------------------------- /.templates/RollandinEmilie.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pika-Software/gmod_plib/ac6a4ccf61e7ea1039e925442cec1789c6adc89e/.templates/RollandinEmilie.ttf -------------------------------------------------------------------------------- /.templates/plib_addon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pika-Software/gmod_plib/ac6a4ccf61e7ea1039e925442cec1789c6adc89e/.templates/plib_addon.psd -------------------------------------------------------------------------------- /.templates/plib_module.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pika-Software/gmod_plib/ac6a4ccf61e7ea1039e925442cec1789c6adc89e/.templates/plib_module.psd -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 Pika Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PLib 2 | [![GLuaLint](https://github.com/Pika-Software/gmod_plib/actions/workflows/glualint-check.yml/badge.svg)](https://github.com/FPtje/GLuaFixer) 3 | 4 | 5 | - **What is it?** 6 | *This is a **Pika Library** - a auxiliary library for development in Garry's Mod. It can be used as a dependency for other addons or as an improvement that eliminates many problems in the game and the addons development, making everything easier and more stable.* 7 | - **Where is the documentation?** 8 | *We're still making, please wait...* 9 | - **Can I use it for my addons?** 10 | *Of course, this library is completely open for use by other developers, only if you are going to publish an addon, __do not include the library code in it!__ This will cause compatibility errors with new versions and also threatens errors in the future due to the lack of library code updates. Instead, use the "Necessary items" by specifying this addon.* 11 | - **What functionality does this library have?** 12 | *So far, the library is under early development and will be replenished, in the future we plan to develop the library as a package manager in which you and your addons will choose which functionality to download and use.* 13 | - **I want to (report a bug/suggest improvements)!** 14 | *You can write your problem/suggestion in __Issues__.* 15 | 16 | **And a lot of cool new functions can be used from PLib!** 17 | 18 | ____ 19 | ![](https://img.shields.io/github/downloads/Pika-Software/plib/total?style=for-the-badge) 20 | ![](https://img.shields.io/github/commit-activity/m/Pika-Software/plib?style=for-the-badge&logo=github) 21 | ![](https://img.shields.io/github/license/Pika-Software/plib?style=for-the-badge) 22 | ![](https://img.shields.io/steam/favorites/2628028051?label=Steam%20Favorites&style=for-the-badge&logo=steam) 23 | ![](https://img.shields.io/steam/subscriptions/2628028051?label=Steam%20Subscriptions&style=for-the-badge&logo=steam) 24 | 25 | [![](https://img.shields.io/badge/Workshop%20Page-Steam-%232a475e?style=for-the-badge&logo=steam)](https://steamcommunity.com/sharedfiles/filedetails/?id=2628028051) 26 | [![](https://img.shields.io/badge/Pika%20Software-Discord-%237289da?style=for-the-badge&logo=discord)](https://discord.pika-soft.ru/) 27 | [![](https://img.shields.io/badge/Pika%20Software-Hub-%231b242c?style=for-the-badge&logo=data:image/png;base64,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)](https://pika-soft.ru) 28 | -------------------------------------------------------------------------------- /glualint.json: -------------------------------------------------------------------------------- 1 | { 2 | "lint_maxScopeDepth": 0, 3 | "lint_syntaxErrors": true, 4 | "lint_syntaxInconsistencies": true, 5 | "lint_deprecated": true, 6 | "lint_trailingWhitespace": true, 7 | "lint_whitespaceStyle": true, 8 | "lint_beginnerMistakes": true, 9 | "lint_emptyBlocks": true, 10 | "lint_shadowing": true, 11 | "lint_gotos": true, 12 | "lint_goto_identifier": true, 13 | "lint_doubleNegations": true, 14 | "lint_redundantIfStatements": true, 15 | "lint_redundantParentheses": true, 16 | "lint_duplicateTableKeys": true, 17 | "lint_profanity": true, 18 | "lint_unusedVars": true, 19 | "lint_unusedParameters": false, 20 | "lint_unusedLoopVars": false, 21 | "lint_inconsistentVariableStyle": false, 22 | "lint_ignoreFiles": [], 23 | 24 | "prettyprint_spaceAfterParens": false, 25 | "prettyprint_spaceAfterBrackets": false, 26 | "prettyprint_spaceAfterBraces": false, 27 | "prettyprint_spaceEmptyParens": false, 28 | "prettyprint_spaceEmptyBraces": false, 29 | "prettyprint_spaceAfterLabel": false, 30 | "prettyprint_spaceBeforeComma": false, 31 | "prettyprint_spaceAfterComma": true, 32 | "prettyprint_semicolons": false, 33 | "prettyprint_cStyle": false, 34 | "prettyprint_rejectInvalidCode": false, 35 | "prettyprint_indentation": " ", 36 | 37 | "log_format": "auto" 38 | } 39 | -------------------------------------------------------------------------------- /lua/autorun/!!!sh_plib.lua: -------------------------------------------------------------------------------- 1 | local addonName = 'PLib' 2 | 3 | local AddCSLuaFile = AddCSLuaFile 4 | local file_Find = file.Find 5 | local ipairs = ipairs 6 | 7 | local SERVER = SERVER 8 | local CLIENT = CLIENT 9 | 10 | local lowerName = string.lower( addonName ) 11 | local pathToLib = lowerName .. '/' 12 | 13 | do 14 | local include = include 15 | for _, fl in ipairs( file_Find( pathToLib .. '*', 'LUA' ) ) do 16 | if (SERVER) then 17 | AddCSLuaFile( pathToLib .. fl ) 18 | end 19 | 20 | include( pathToLib .. fl ) 21 | end 22 | end 23 | 24 | local AddCSLuaFolder = AddCSLuaFolder 25 | local string_format = string.format 26 | local file_Exists = file.Exists 27 | local file_Path = file.Path 28 | local ArgAssert = ArgAssert 29 | local SysTime = SysTime 30 | 31 | local globalStopwatch = SysTime() 32 | 33 | module( lowerName, package.seeall ) 34 | 35 | -- Lib Version 36 | Version = 021200 37 | 38 | -- Developer Mode 39 | if (SERVER) then 40 | CreateConVar( lowerName .. '_developer_mode', '0', FCVAR_ARCHIVE, '', 0, 1 ) 41 | end 42 | 43 | DeveloperMode = cvars.Bool( lowerName .. '_developer_mode', false ) 44 | 45 | -- Colors 46 | do 47 | 48 | local colors = {} 49 | function GetColor( name ) 50 | ArgAssert( name, 1, 'string' ) 51 | return colors[ name ] 52 | end 53 | 54 | function SetColor( name, color ) 55 | ArgAssert( name, 1, 'string' ) 56 | local old = colors[ name ] 57 | if (old ~= nil) and (old.r ~= color.r and old.g ~= color.g and old.b ~= color.b and old.a ~= (color.a or 255)) then 58 | hook_Run( 'ColorUpdated', name, old, color ) 59 | end 60 | 61 | colors[ name ] = color 62 | end 63 | 64 | end 65 | 66 | -- Default Colors 67 | do 68 | 69 | local Color = Color 70 | 71 | -- Grey 72 | SetColor( 'light_grey', Color( 150, 150, 150, 255 ) ) 73 | SetColor( 'dark_grey', Color( 20, 20, 20, 255 ) ) 74 | SetColor( 'grey', Color( 50, 50, 50, 255 ) ) 75 | 76 | -- White 77 | SetColor( 'dark_white', Color( 200, 200, 200, 255 ) ) 78 | SetColor( 'white', Color( 255, 255, 255, 255 ) ) 79 | 80 | -- Black 81 | SetColor( 'black', Color( 0, 0, 0, 255 ) ) 82 | 83 | -- RGB 84 | SetColor( 'red', Color( 255, 0, 0 ) ) 85 | SetColor( 'green', Color( 0, 255, 0 ) ) 86 | SetColor( 'blue', Color( 0, 0, 255 ) ) 87 | 88 | -- Logs 89 | SetColor( 'info', Color( 71,134,255 ) ) 90 | SetColor( 'warn', Color( 255,131,89 ) ) 91 | SetColor( 'error', Color( 251,55,40 ) ) 92 | SetColor( 'debug', Color( 0,200,156 ) ) 93 | 94 | -- Server/Client 95 | SetColor( 'client', Color( 222, 170, 10 ) ) 96 | SetColor( 'server', Color( 5, 170, 250 ) ) 97 | 98 | -- Extra 99 | SetColor( 'plib', Color( 50, 200, 200 ) ) 100 | SetColor( 'yellow', Color( 200, 200, 50 ) ) 101 | 102 | end 103 | 104 | -- Log 105 | do 106 | 107 | local lightGrey, darkWhite = GetColor( 'light_grey' ), GetColor( 'dark_white' ) 108 | local sideColor = GetColor( SERVER and 'server' or 'client' ) 109 | local string_NetFormat = string.NetFormat 110 | local os_time = os.time 111 | local os_date = os.date 112 | 113 | function Log( levelColor, level, nameColor, name, str, ... ) 114 | ArgAssert( levelColor, 1, 'table' ); ArgAssert( level, 2, 'string' ); ArgAssert( nameColor, 3, 'table' ); ArgAssert( name, 4, 'string' ) 115 | MsgC( lightGrey, os_date( '%d/%m/%Y %H:%M:%S ', os_time() ), levelColor, level, lightGrey, ' --- ', sideColor, '[' .. (SERVER and 'SERVER' or 'CLIENT') .. '] ', nameColor, name, lightGrey, ' : ', darkWhite, string_NetFormat( str, ... ), '\n' ) 116 | end 117 | 118 | end 119 | 120 | -- Logger 121 | do 122 | 123 | local meta = {} 124 | meta.__index = meta 125 | 126 | -- Logs name 127 | function meta:GetName() 128 | return self.Name 129 | end 130 | 131 | function meta:SetName( str ) 132 | ArgAssert( str, 1, 'string' ) 133 | self.Name = str 134 | end 135 | 136 | -- Logs name color 137 | function meta:GetColor() 138 | return self.Color 139 | end 140 | 141 | function meta:SetColor( color ) 142 | ArgAssert( color, 1, 'table' ) 143 | self.Color = color 144 | end 145 | 146 | -- Logs basic text colot 147 | function meta:GetTextColor() 148 | return self.TextColor 149 | end 150 | 151 | function meta:SetTextColor( color ) 152 | ArgAssert( color, 1, 'table' ) 153 | self.TextColor = color 154 | end 155 | 156 | -- Debug options 157 | function meta:GetDebugFilter() 158 | return self.DebugFilter 159 | end 160 | 161 | function meta:SetDebugFilter( func ) 162 | ArgAssert( func, 1, 'function' ) 163 | self.DebugFilter = func 164 | end 165 | 166 | -- Log print functions 167 | do 168 | local color = GetColor( 'info' ) 169 | function meta:Info( str, ... ) 170 | Log( color, ' INFO', self:GetColor(), self:GetName(), str, ... ) 171 | end 172 | end 173 | 174 | do 175 | local color = GetColor( 'warn' ) 176 | function meta:Warn( str, ... ) 177 | Log( color, ' WARN', self:GetColor(), self:GetName(), str, ... ) 178 | end 179 | end 180 | 181 | do 182 | local color = GetColor( 'error' ) 183 | function meta:Error( str, ... ) 184 | Log( color, 'ERROR', self:GetColor(), self:GetName(), str, ... ) 185 | end 186 | end 187 | 188 | do 189 | local color = GetColor( 'debug' ) 190 | function meta:Debug( str, ... ) 191 | if DeveloperMode then 192 | Log( color, 'DEBUG', self:GetColor(), self:GetName(), str, ... ) 193 | end 194 | end 195 | end 196 | 197 | local plibColor = GetColor( 'plib' ) 198 | function Logger( name, color ) 199 | ArgAssert( name, 1, 'string' ) 200 | return setmetatable({ 201 | ['Color'] = color or plibColor, 202 | ['Name'] = name 203 | }, meta) 204 | end 205 | 206 | end 207 | 208 | -- Logs 209 | do 210 | 211 | local logger = Logger( addonName ) 212 | 213 | function Info( str, ... ) 214 | logger:Info( str, ... ) 215 | end 216 | 217 | function Error( str, ... ) 218 | logger:Error( str, ... ) 219 | end 220 | 221 | function Warn( str, ... ) 222 | logger:Warn( str, ... ) 223 | end 224 | 225 | function Debug( str, ... ) 226 | logger:Debug( str, ... ) 227 | end 228 | 229 | end 230 | 231 | -- Include 232 | do 233 | local CompileFile = CompileFile 234 | function Include( filePath ) 235 | ArgAssert( filePath, 1, 'string' ) 236 | if file_Exists( filePath, 'LUA' ) then 237 | if DeveloperMode then 238 | return true, include( filePath ) 239 | end 240 | 241 | local func = CompileFile( filePath ) 242 | if isfunction( func ) then 243 | return pcall( func ) 244 | else 245 | return false, 'File \'' .. filePath .. '\' assembly failed.' 246 | end 247 | else 248 | return false, 'File \'' .. filePath .. '\' does not exist.' 249 | end 250 | end 251 | end 252 | 253 | -- Modules 254 | do 255 | 256 | -- Folders :x 257 | local modulesFolder = pathToLib .. 'modules' 258 | local clientModulesFolder = file_Path( modulesFolder, 'client' ) 259 | local serverModulesFolder = file_Path( modulesFolder, 'server' ) 260 | 261 | -- Server side jobs 262 | if (SERVER) then 263 | 264 | local files, folders = file_Find( file_Path( modulesFolder, '*' ), 'LUA' ) 265 | for num, fl in ipairs( files ) do 266 | AddCSLuaFile( file_Path( modulesFolder, fl ) ) 267 | end 268 | 269 | for num, fol in ipairs( folders ) do 270 | if (fol == 'server') then continue end 271 | AddCSLuaFolder( file_Path( modulesFolder, fol ) ) 272 | end 273 | 274 | end 275 | 276 | local modules = {} 277 | function IsModuleInstalled( moduleName ) 278 | ArgAssert( moduleName, 1, 'string' ) 279 | if (modules[ moduleName ] == nil) then 280 | return false 281 | end 282 | 283 | return true 284 | end 285 | 286 | do 287 | 288 | local table_insert = table.insert 289 | local pairs = pairs 290 | 291 | function ModulesInstalled() 292 | local modulesList = {} 293 | for moduleName, state in pairs( modules ) do 294 | if (state) then 295 | table_insert( modulesList, moduleName ) 296 | end 297 | end 298 | 299 | return modulesList 300 | end 301 | 302 | end 303 | 304 | do 305 | 306 | local isfunction = isfunction 307 | local pcall = pcall 308 | 309 | -- Web require function 310 | do 311 | 312 | local string_GetFileFromFilename = string.GetFileFromFilename 313 | local CompileString = CompileString 314 | 315 | function WebRequire( url, callback ) 316 | ArgAssert( url, 1, 'string' ) 317 | 318 | local stopwatch = SysTime() 319 | local moduleName = string_GetFileFromFilename( url ) 320 | 321 | -- Re-installation lock 322 | if IsModuleInstalled( moduleName ) then return end 323 | 324 | -- Downloading & installing 325 | http.Fetch(url, function( luaCode, _, __, statusCode ) 326 | if (statusCode == 200) then 327 | local func = CompileString( luaCode, url ) 328 | if isfunction( func ) then 329 | local ok, result = pcall( func ) 330 | if (ok) then 331 | Info( string_format( 'Web module \'{0}\' successfully installed. (%.4f seconds)', SysTime() - stopwatch ), moduleName ) 332 | if isfunction( callback ) then 333 | callback( ok, result ) 334 | end 335 | else 336 | Error( 'Web module \'{0}\' could not be installed.', moduleName ) 337 | if isfunction( callback ) then 338 | callback( false ) 339 | end 340 | 341 | error( result ) 342 | end 343 | else 344 | Error( 'Web module \'{0}\' assembly failed.', moduleName ) 345 | if isfunction( callback ) then 346 | callback( false ) 347 | end 348 | end 349 | else 350 | Error( 'Web module \'{0}\' url is invalid.', moduleName ) 351 | if isfunction( callback ) then 352 | callback( false ) 353 | end 354 | end 355 | end, 356 | function( err ) 357 | Error( 'Web module \'{0}\' downloading error: {1}', moduleName, err ) 358 | if isfunction( callback ) then 359 | callback( false ) 360 | end 361 | end) 362 | end 363 | 364 | end 365 | 366 | -- Require function 367 | do 368 | 369 | local file_IsDir = file.IsDir 370 | local error = error 371 | 372 | local SERVER = SERVER 373 | local CLIENT = CLIENT 374 | 375 | function Require( moduleName, noErros, override ) 376 | ArgAssert( moduleName, 1, 'string' ) 377 | 378 | -- Re-installation lock 379 | if IsModuleInstalled( moduleName ) and not override then 380 | return modules[ moduleName ] 381 | end 382 | 383 | -- Stopwatch & empty path 384 | local stopwatch = SysTime() 385 | local filePath 386 | 387 | -- Client 388 | if (CLIENT) then 389 | local clientFolder = file_Path( clientModulesFolder, moduleName ) 390 | if file_IsDir( clientFolder, 'LUA' ) then 391 | filePath = file_Path( clientFolder, 'init.lua' ) 392 | end 393 | 394 | local clientFile = file_Path( clientModulesFolder, moduleName .. '.lua' ) 395 | if file_Exists( clientFile, 'LUA' ) then 396 | filePath = clientFile 397 | end 398 | end 399 | 400 | -- Server 401 | if (SERVER) then 402 | local serverFolder = file_Path( serverModulesFolder, moduleName ) 403 | if file_IsDir( serverFolder, 'LUA' ) then 404 | filePath = file_Path( serverFolder, 'init.lua' ) 405 | end 406 | 407 | local serverFile = file_Path( serverModulesFolder, moduleName .. '.lua' ) 408 | if file_Exists( serverFile, 'LUA' ) then 409 | filePath = serverFile 410 | end 411 | end 412 | 413 | -- Shared 414 | local sharedFolder = file_Path( modulesFolder, moduleName ) 415 | if file_IsDir( sharedFolder, 'LUA' ) then 416 | filePath = file_Path( sharedFolder, 'init.lua' ) 417 | end 418 | 419 | if (filePath == nil) then 420 | filePath = file_Path( modulesFolder, moduleName .. '.lua' ) 421 | end 422 | 423 | -- Including 424 | local ok, result = Include( filePath ) 425 | if (ok) then 426 | Info( string_format( 'Module \'' .. moduleName .. '\' successfully installed. (%.4f seconds)', SysTime() - stopwatch ) ) 427 | if (result == nil) then 428 | modules[ moduleName ] = true 429 | else 430 | modules[ moduleName ] = result 431 | end 432 | 433 | return result 434 | else 435 | if (noErros) then 436 | return false 437 | else 438 | Error( 'Module \'{0}\' could not be installed.', moduleName ) 439 | error( result ) 440 | end 441 | end 442 | end 443 | 444 | end 445 | 446 | end 447 | 448 | end 449 | 450 | -- Addons 451 | do 452 | 453 | -- Include Function 454 | function AddonInclude( filePath, name ) 455 | ArgAssert( filePath, 1, 'string' ) 456 | ArgAssert( name, 2, 'string' ) 457 | 458 | local stopwatch = SysTime() 459 | 460 | -- Include 461 | local ok, err = Include( filePath ) 462 | if (ok) then 463 | Info( string_format( 'Addon \'' .. name .. '\' successfully included. (%.4f seconds)', SysTime() - stopwatch ) ) 464 | else 465 | Error( 'Addon \'' .. name .. '\' include failed: ' .. err ) 466 | end 467 | end 468 | 469 | -- Folders x: 470 | local addonsFolder = pathToLib .. 'addons' 471 | local clientAddonsFolder = file_Path( addonsFolder, 'client' ) 472 | local serverAddonsFolder = file_Path( addonsFolder, 'server' ) 473 | 474 | -- Shared 475 | do 476 | 477 | local files, folders = file_Find( file_Path( addonsFolder, '*' ), 'LUA' ) 478 | for num, fl in ipairs( files ) do 479 | local filePath = file_Path( addonsFolder, fl ) 480 | if (SERVER) then 481 | AddCSLuaFile( filePath ) 482 | end 483 | 484 | AddonInclude( filePath, string.sub( fl, 1, #fl - 4 ) .. ' (FILE)' ) 485 | end 486 | 487 | for num, fol in ipairs( folders ) do 488 | if (fol ~= 'server') then 489 | AddCSLuaFolder( file_Path( addonsFolder, fol ) ) 490 | end 491 | 492 | local filePath = file_Path( addonsFolder, fol, 'init.lua' ) 493 | if file_Exists( filePath, 'LUA' ) then 494 | AddonInclude( filePath, string.sub( fol, 1, #fol - 4 ) .. ' (FOLDER)' ) 495 | end 496 | end 497 | 498 | end 499 | 500 | -- Client 501 | if (CLIENT) then 502 | 503 | local files, folders = file_Find( file_Path( clientAddonsFolder, '*' ), 'LUA' ) 504 | for num, fl in ipairs( files ) do 505 | AddonInclude( file_Path( clientAddonsFolder, fl ), string.sub( fl, 1, #fl - 4 ) .. ' (FILE)' ) 506 | end 507 | 508 | for num, fol in ipairs( folders ) do 509 | local filePath = file_Path( clientAddonsFolder, fol, 'init.lua' ) 510 | if file_Exists( filePath, 'LUA' ) then 511 | AddonInclude( filePath, string.sub( fol, 1, #fol - 4 ) .. ' (FOLDER)' ) 512 | end 513 | end 514 | 515 | end 516 | 517 | -- Server 518 | if (SERVER) then 519 | 520 | local files, folders = file_Find( file_Path( serverAddonsFolder, '*' ), 'LUA' ) 521 | for num, fl in ipairs( files ) do 522 | AddonInclude( file_Path( serverAddonsFolder, fl ), string.sub( fl, 1, #fl - 4 ) .. ' (FILE)' ) 523 | end 524 | 525 | for num, fol in ipairs( folders ) do 526 | local filePath = file_Path( serverAddonsFolder, fol, 'init.lua' ) 527 | if file_Exists( filePath, 'LUA' ) then 528 | AddonInclude( filePath, string.sub( fol, 1, #fol - 4 ) .. ' (FOLDER)' ) 529 | end 530 | end 531 | 532 | end 533 | 534 | end 535 | 536 | Info( string_format( '{0} v{1} is successfully initialized. (%.4f seconds)', SysTime() - globalStopwatch ), addonName, string.Version( Version ) ) -------------------------------------------------------------------------------- /lua/plib/entity.lua: -------------------------------------------------------------------------------- 1 | local list = list 2 | 3 | local ENTITY = FindMetaTable( 'Entity' ) 4 | 5 | -- ENTITY:IsProp() 6 | do 7 | 8 | list.Set( 'Prop Classes', 'prop_detail', true ) 9 | list.Set( 'Prop Classes', 'prop_static', true ) 10 | list.Set( 'Prop Classes', 'prop_physics', true ) 11 | list.Set( 'Prop Classes', 'prop_ragdoll', true ) 12 | list.Set( 'Prop Classes', 'prop_dynamic', true ) 13 | list.Set( 'Prop Classes', 'prop_physics_override', true ) 14 | list.Set( 'Prop Classes', 'prop_dynamic_override', true ) 15 | list.Set( 'Prop Classes', 'prop_physics_multiplayer', true ) 16 | 17 | function ENTITY:IsProp() 18 | if list.Get( 'Prop Classes' )[ self:GetClass() ] then 19 | return true 20 | end 21 | 22 | return false 23 | end 24 | 25 | end 26 | 27 | -- ENTITY:IsDoor() 28 | do 29 | 30 | 31 | list.Set( 'Door Classes', 'prop_testchamber_door', true ) 32 | list.Set( 'Door Classes', 'prop_door_rotating', true ) 33 | list.Set( 'Door Classes', 'func_door_rotating', true ) 34 | list.Set( 'Door Classes', 'func_door', true ) 35 | 36 | function ENTITY:IsDoor() 37 | if list.Get( 'Door Classes' )[ self:GetClass() ] then 38 | return true 39 | end 40 | 41 | return false 42 | end 43 | 44 | end 45 | 46 | -- ENTITY:IsButton() 47 | if (SERVER) then 48 | 49 | list.Set( 'Button Classes', 'momentary_rot_button', true ) 50 | list.Set( 'Button Classes', 'func_rot_button', true ) 51 | list.Set( 'Button Classes', 'func_button', true ) 52 | list.Set( 'Button Classes', 'gmod_button', true ) 53 | 54 | function ENTITY:IsButton() 55 | if list.Get( 'Button Classes' )[ self:GetClass() ] then 56 | return true 57 | end 58 | 59 | return false 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /lua/plib/global.lua: -------------------------------------------------------------------------------- 1 | local tonumber = tonumber 2 | local string = string 3 | local file = file 4 | local hook = hook 5 | 6 | -- Argument checker by Retr0 & PrikolMen:-b (From atmoshpere with love <3) 7 | do 8 | 9 | local debug_getinfo = debug.getinfo 10 | local error = error 11 | local type = type 12 | 13 | function ArgAssert( value, argNum, argType, errorlevel ) 14 | local valueType = string.lower( type( value ) ) 15 | if (valueType == argType) then return end 16 | 17 | local dinfo = debug_getinfo( 2, 'n' ) 18 | local fname = dinfo and dinfo.name or 'func' 19 | error( string.format( 'bad argument #%d to \'%s\' (%s expected, got %s)', argNum, fname, argType, valueType ), errorlevel or 3) 20 | end 21 | 22 | end 23 | 24 | -- Path Builder 25 | do 26 | local table_concat = table.concat 27 | function file.Path( ... ) 28 | return table_concat( {...}, '/' ) 29 | end 30 | end 31 | 32 | --- .NET like string formatting 33 | -- @see https://wiki.facepunch.com/gmod/Patterns 34 | do 35 | local string_gsub = string.gsub 36 | function string.NetFormat( fmt, ... ) 37 | local args = { ... } 38 | return string_gsub(fmt, '{(%d+)}', function( i ) 39 | return tostring( args[ tonumber(i) + 1 ] ) 40 | end) 41 | end 42 | end 43 | 44 | -- Version formatter 45 | function string.Version( number ) 46 | local version = string.format( '%06d', number ) 47 | return string.format( '%d.%d.%d', tonumber( string.sub( version, 0, 2 ) ), tonumber( string.sub( version, 3, 4 ) ), tonumber( string.sub( version, 5 ) ) ) 48 | end 49 | 50 | -- GM:ScreenResolutionChanged( w, h, oldW, oldH ) 51 | hook.Add('OnScreenSizeChanged', 'PLib - Global', function( oldWidth, oldHeight ) 52 | hook.Run( 'ScreenResolutionChanged', ScrW(), ScrH(), oldWidth, oldHeight ) 53 | end) 54 | 55 | if (CLIENT) then 56 | 57 | -- GM:SystemFocusChanged( isInFocus ) 58 | do 59 | 60 | local system_HasFocus = system.HasFocus 61 | local isInFocus = system_HasFocus() 62 | 63 | hook.Add('Think', 'PLib - System focus is changed', function() 64 | if (isInFocus == system_HasFocus()) then return end 65 | isInFocus = system_HasFocus() 66 | hook.Run( 'SystemFocusChanged', isInFocus ) 67 | end) 68 | 69 | end 70 | 71 | local vmin, vmax = 0, 0 72 | local vh, vw = 0, 0 73 | 74 | local function updateNumbers( w, h ) 75 | vh = h / 100 76 | vw = w / 100 77 | 78 | if (vh > vw) then 79 | vmin = vw 80 | vmax = vh 81 | else 82 | vmin = vh 83 | vmax = vw 84 | end 85 | end 86 | 87 | hook.Add('ScreenResolutionChanged', 'PLib - Global', updateNumbers) 88 | updateNumbers( ScrW(), ScrH() ) 89 | 90 | local function getPercent( number, percent ) 91 | if (percent) then 92 | return number * percent 93 | end 94 | 95 | return number 96 | end 97 | 98 | -- vh, vw, vmin, vmax like in CSS 99 | function ScreenPercentHeight( percent ) 100 | return getPercent( vh, percent ) 101 | end 102 | 103 | function ScreenPercentWidth( percent ) 104 | return getPercent( vw, percent ) 105 | end 106 | 107 | function ScreenPercentMin( percent ) 108 | return getPercent( vmin, percent ) 109 | end 110 | 111 | function ScreenPercentMax( percent ) 112 | return getPercent( vmax, percent ) 113 | end 114 | 115 | end 116 | 117 | do 118 | 119 | local ipairs = ipairs 120 | 121 | -- AddCSLuaFolder 122 | do 123 | 124 | local AddCSLuaFile = AddCSLuaFile 125 | 126 | if (SERVER) then 127 | function AddCSLuaFolder( folder ) 128 | local files, folders = file.Find( file.Path( folder, '*' ), 'LUA' ) 129 | for _, fl in ipairs( files ) do 130 | AddCSLuaFile( file.Path( folder, fl ) ) 131 | end 132 | 133 | for _, fol in ipairs( folders ) do 134 | AddCSLuaFolder( file.Path( folder, fol ) ) 135 | end 136 | end 137 | end 138 | 139 | if (CLIENT) then 140 | function AddCSLuaFolder() 141 | end 142 | end 143 | 144 | end 145 | 146 | -- includeFolder 147 | function includeFolder( filePath ) 148 | local files, folders = file.Find( file.Path( filePath, '*' ), 'LUA' ) 149 | for _, fl in ipairs( files ) do 150 | include( file.Path( filePath, fl ) ) 151 | end 152 | 153 | for _, fol in ipairs( folders ) do 154 | includeFolder( file.Path( filePath, fol ) ) 155 | end 156 | end 157 | 158 | end 159 | -------------------------------------------------------------------------------- /lua/plib/player.lua: -------------------------------------------------------------------------------- 1 | do 2 | 3 | local hook = hook 4 | 5 | if (CLIENT) then 6 | 7 | local LocalPlayer = LocalPlayer 8 | local IsValid = IsValid 9 | 10 | hook.Add('InitPostEntity', 'PLib - Player Initialization', function() 11 | hook.Remove('InitPostEntity', 'PLib - Player Initialization') 12 | 13 | hook.Add('RenderScene', 'PLib - Player Initialization', function() 14 | local ply = LocalPlayer() 15 | if IsValid( ply ) then 16 | hook.Remove('RenderScene', 'PLib - Player Initialization') 17 | hook.Run( 'PlayerInitialized', ply ) 18 | end 19 | end) 20 | end) 21 | 22 | hook.Add('ShutDown', 'PLib - Player Initialization', function() 23 | hook.Remove('ShutDown', 'PLib - Player Initialization') 24 | 25 | local ply = LocalPlayer() 26 | if IsValid( ply ) then 27 | hook.Run( 'PlayerDisconnected', ply ) 28 | end 29 | end) 30 | 31 | end 32 | 33 | if (SERVER) then 34 | 35 | hook.Add('PlayerInitialSpawn', 'PLib - Player Initialization', function( pl ) 36 | hook.Add('SetupMove', pl, function( self, ply, __, cmd ) 37 | if (pl == self) and not cmd:IsForced() then 38 | hook.Remove( 'SetupMove', self ) 39 | ply:SetNWBool( 'Fully Initialized', true ) 40 | hook.Run( 'PlayerInitialized', ply ) 41 | end 42 | end) 43 | end) 44 | 45 | end 46 | 47 | end 48 | 49 | local PLAYER = FindMetaTable( 'Player' ) 50 | 51 | function PLAYER:Initialized() 52 | return self:GetNWBool( 'Fully Initialized', false ) 53 | end 54 | -------------------------------------------------------------------------------- /lua/plib/utils.lua: -------------------------------------------------------------------------------- 1 | local util = util 2 | 3 | do 4 | 5 | local string = string 6 | local file = file 7 | 8 | function util.IsBSP( binnary ) 9 | return string.lower( string.sub( binnary, 1, 4 ) ) == 'vbsp' 10 | end 11 | 12 | function file.IsMap( fileName, gamePath ) 13 | local fileClass = file.Open( fileName, 'rb', gamePath ) 14 | if (fileClass) then 15 | local binnary = fileClass:Read( 4 ) 16 | fileClass:Close() 17 | return util.IsBSP( binnary ) 18 | end 19 | 20 | return false 21 | end 22 | 23 | function util.IsGMA( binnary ) 24 | return string.lower( string.sub( binnary, 1, 4 ) ) == 'gmad' 25 | end 26 | 27 | function file.IsGMA( fileName, gamePath ) 28 | local fileClass = file.Open( fileName, 'rb', gamePath ) 29 | if (fileClass) then 30 | local binnary = fileClass:Read( 4 ) 31 | fileClass:Close() 32 | return util.IsGMA( binnary ) 33 | end 34 | 35 | return false 36 | end 37 | 38 | function util.IsPNG( binnary ) 39 | return string.lower( string.sub( binnary, 2, 4 ) ) == 'png' 40 | end 41 | 42 | function file.IsPNG( fileName, gamePath ) 43 | local fileClass = file.Open( fileName, 'rb', gamePath ) 44 | if (fileClass) then 45 | local binnary = fileClass:Read( 4 ) 46 | fileClass:Close() 47 | return util.IsPNG( binnary ) 48 | end 49 | 50 | return false 51 | end 52 | 53 | function util.IsJPEG( binnary ) 54 | local str = string.lower( string.sub( binnary, 7, 10 ) ) 55 | return str == 'jfif' or str == 'exif' 56 | end 57 | 58 | function file.IsJPEG( fileName, gamePath ) 59 | local fileClass = file.Open( fileName, 'rb', gamePath ) 60 | if (fileClass) then 61 | local binnary = fileClass:Read( 10 ) 62 | fileClass:Close() 63 | return util.IsJPEG( binnary ) 64 | end 65 | 66 | return false 67 | end 68 | 69 | end 70 | 71 | do 72 | 73 | local isfunction = isfunction 74 | 75 | do 76 | 77 | local isnumber = isnumber 78 | 79 | do 80 | 81 | local isstring = isstring 82 | local isbool = isbool 83 | local NULL = NULL 84 | 85 | function util.IsValidObject( object ) 86 | if (object == nil) then return false end 87 | if (object == NULL) then return false end 88 | if isbool( object ) then return false end 89 | 90 | if isnumber( object ) then return false end 91 | if isstring( object ) then return false end 92 | if isfunction( object ) then return false end 93 | 94 | local isValid = object.IsValid 95 | if isfunction( isValid ) then 96 | return isValid( object ) 97 | end 98 | 99 | return false 100 | end 101 | 102 | end 103 | 104 | 105 | if (SERVER) then 106 | 107 | local util_BlastDamageInfo = util.BlastDamageInfo 108 | local util_Effect = util.Effect 109 | local EffectData = EffectData 110 | local DamageInfo = DamageInfo 111 | 112 | local up = Vector( 0, 0, 1 ) 113 | 114 | function util.Explosion( pos, radius, damage ) 115 | local dmg = DamageInfo() 116 | dmg:SetDamageType( DMG_BLAST ) 117 | 118 | if isnumber( damage ) then 119 | dmg:SetDamage( damage ) 120 | else 121 | dmg:SetDamage( 250 ) 122 | end 123 | 124 | local fx = EffectData() 125 | fx:SetRadius( radius ) 126 | fx:SetOrigin( pos ) 127 | fx:SetNormal( up ) 128 | 129 | util.NextTick(function() 130 | util_Effect( 'Explosion', fx ) 131 | util_Effect( 'HelicopterMegaBomb', fx ) 132 | util_BlastDamageInfo( dmg, pos, radius ) 133 | end) 134 | 135 | return dmg, fx 136 | end 137 | 138 | end 139 | 140 | 141 | end 142 | 143 | function util.NextTick( any, func, ... ) 144 | if util.IsValidObject( any ) then 145 | ArgAssert( func, 2, 'function' ) 146 | local args = {...} 147 | timer.Simple(0, function() 148 | if IsValid( any ) then 149 | func( any, unpack( args ) ) 150 | end 151 | end) 152 | 153 | return true 154 | elseif isfunction( any ) then 155 | local args = {...} 156 | timer.Simple(0, function() 157 | any( func, unpack( args ) ) 158 | end) 159 | 160 | return true 161 | end 162 | 163 | return false 164 | end 165 | 166 | end 167 | 168 | local suffix = ({'osx64','osx','linux64','linux','win64','win32'})[ 169 | ( system.IsWindows() and 4 or 0 ) 170 | + ( system.IsLinux() and 2 or 0 ) 171 | + ( jit.arch == 'x86' and 1 or 0 ) 172 | + 1 173 | ] 174 | 175 | local fmt = 'lua/bin/gm' .. ((CLIENT and !MENU_DLL) and 'cl' or 'sv') .. '_%s_%s.dll' 176 | function util.IsBinaryModuleInstalled( name ) 177 | ArgAssert( name, 1, 'string' ) 178 | 179 | if file.Exists( string.format( fmt, name, suffix ), 'GAME' ) then 180 | return true 181 | end 182 | 183 | -- Edge case - on Linux 32-bit x86-64 branch, linux32 is also supported as a suffix 184 | if (jit.versionnum ~= 20004) and (jit.arch == 'x86') and system.IsLinux() then 185 | return file.Exists( string.format( fmt, name, 'linux32' ), 'GAME' ) 186 | end 187 | 188 | return false 189 | end --------------------------------------------------------------------------------