├── LICENSE ├── LICENSE-MIT ├── README.md ├── biglobby_tweak.xml ├── hooks.xml ├── l10n ├── english.json ├── russian.json └── schinese.json ├── lua ├── _custom │ ├── biglobby_globals.lua │ ├── husl.lua │ └── menu.lua └── lib │ ├── managers │ ├── _achievementmanager.lua │ ├── _criminalsmanager.lua │ ├── _hudmanager.lua │ ├── _hudmanagerpd2.lua │ ├── _vehiclemanager.lua │ ├── group_ai_states │ │ └── _groupaistatebase.lua │ ├── hud │ │ ├── _hudlootscreen.lua │ │ ├── _hudmissionbriefing.lua │ │ └── _hudteammate.lua │ ├── menu │ │ ├── _contractboxgui.lua │ │ ├── _crewmanagementgui.lua │ │ ├── _crimespreemissionsmenucomponent.lua │ │ ├── _menucomponentmanager.lua │ │ ├── _menulobbyrenderer.lua │ │ ├── _menuscenemanager.lua │ │ └── _missionbriefinggui.lua │ └── mission │ │ ├── _elementareatrigger.lua │ │ └── _elementfilter.lua │ ├── network │ ├── base │ │ ├── _basenetworksession.lua │ │ ├── _clientnetworksession.lua │ │ ├── _hostnetworksession.lua │ │ ├── _networkmanager.lua │ │ ├── _networkpeer.lua │ │ ├── handlers │ │ │ └── _connectionnetworkhandler.lua │ │ └── session_states │ │ │ ├── _hoststateingame.lua │ │ │ └── _hoststateinlobby.lua │ ├── handlers │ │ └── _unitnetworkhandler.lua │ └── matchmaking │ │ ├── _networkmatchmakingepic.lua │ │ └── _networkmatchmakingsteam.lua │ └── tweak_data │ ├── _moneytweakdata.lua │ └── _tweakdata.lua ├── mod.txt ├── supermod.xml └── updates ├── biglobby3.zip └── meta_biglobby3.json /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Brennan Kinney 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 | # BigLobby3 2 | BigLobby: No PDMod Required! 3 | 4 | # Credits: 5 | - Polarathene: Original version of the mod located here: https://github.com/polarathene/biglobby 6 | - steam-test1: Contributor to current/previous project 7 | - ZNix: SuperBLT creator, XML injection API used to obsolete the pdmod file 8 | - RESTORATION Mod team: Additional R&D 9 | -------------------------------------------------------------------------------- /biglobby_tweak.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | -------------------------------------------------------------------------------- /hooks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /l10n/english.json: -------------------------------------------------------------------------------- 1 | { 2 | "bkin_bl__menu__title" : "BigLobby Options", 3 | "bkin_bl__menu__desc" : "Set the player size of your hosted lobbies.", 4 | "bkin_bl__set_size__title" : "Set max lobby size", 5 | "bkin_bl__set_size__desc" : "How many players do you want to allow in your lobby?", 6 | "bkin_bl__allow_more_bots__title" : "Allow more bots in lobby", 7 | "bkin_bl__allow_more_bots__desc" : "Allows additional bots up to the set lobby size.", 8 | "bkin_bl__set_num_bots__title" : "Set max bots", 9 | "bkin_bl__set_num_bots__desc" : "How many Team AI bots will you allow ingame?", 10 | "bkin_bl__stop_all_bots__title" : "Stop All Bots", 11 | "bkin_bl__stop_all_bots__desc" : "Allows you to stop bots following in stealth", 12 | "bkin_bl__release_all_bots__title" : "Releases All Bots", 13 | "bkin_bl__release_all_bots__desc" : "", 14 | "bkin_bl__auto_stop_all_bots__title" : "Automatically Stop All Bots in stealth", 15 | "bkin_bl__auto_stop_all_bots__desc" : "Allows you to automatically stop all bots in stealth" 16 | } -------------------------------------------------------------------------------- /l10n/russian.json: -------------------------------------------------------------------------------- 1 | { 2 | "bkin_bl__menu__title" : "BigLobby Настаройки", 3 | "bkin_bl__menu__desc" : "Задать количество игроков для лобби у своего хоста.", 4 | "bkin_bl__set_size__title" : "Задать максимальный размер лобби", 5 | "bkin_bl__set_size__desc" : "Скольким игрокам ты хочешь позволять в своем лобби? (*)", 6 | "bkin_bl__allow_more_bots__title" : "Позволить больше ботов в лобби", 7 | "bkin_bl__allow_more_bots__desc" : "Забить ботами пустые места в лобби.", 8 | "bkin_bl__set_num_bots__title" : "Максимум ботов", 9 | "bkin_bl__set_num_bots__desc" : "Сколько ботов добавить в игру? (*)", 10 | "bkin_bl__stop_all_bots__title" : "Остановить всех ботов", 11 | "bkin_bl__stop_all_bots__desc" : "Запретить ходить ботам в стелсе (*)", 12 | "bkin_bl__release_all_bots__title" : "Распустить ботов", 13 | "bkin_bl__release_all_bots__desc" : "", 14 | "bkin_bl__auto_stop_all_bots__title" : "Автоматическая остановка всех ботов в стелсе", 15 | "bkin_bl__auto_stop_all_bots__desc" : "Даёт возможность автоматической остановки всех ботов в стелсе" 16 | } -------------------------------------------------------------------------------- /l10n/schinese.json: -------------------------------------------------------------------------------- 1 | { 2 | "bkin_bl__menu__title" : "巨无霸大厅选项", 3 | "bkin_bl__menu__desc" : "设置您主持的大厅人数大小。", 4 | "bkin_bl__set_size__title" : "设置大厅大小", 5 | "bkin_bl__set_size__desc" : "您想允许多少玩家进入您的大厅?", 6 | "bkin_bl__allow_more_bots__title" : "允许更多人机", 7 | "bkin_bl__allow_more_bots__desc" : "允许加入更多的人机,数量为您设定的大厅大小的余位。", 8 | "bkin_bl__set_num_bots__title" : "最大人机数量", 9 | "bkin_bl__set_num_bots__desc" : "在游戏内您想允许多少人机出现?", 10 | "bkin_bl__stop_all_bots__title" : "暂停所有人机", 11 | "bkin_bl__stop_all_bots__desc" : "允许您在潜入时命令人机不要跟随您。", 12 | "bkin_bl__release_all_bots__title" : "释放所有人机", 13 | "bkin_bl__release_all_bots__desc" : "释放所有被暂停的人机。", 14 | "bkin_bl__auto_stop_all_bots__title" : "潜入时自动暂停所有人机", 15 | "bkin_bl__auto_stop_all_bots__desc" : "在潜入时自动允许人机不再跟随您。" 16 | } -------------------------------------------------------------------------------- /lua/_custom/biglobby_globals.lua: -------------------------------------------------------------------------------- 1 | if not Global.BigLobbyPersist then 2 | Global.BigLobbyPersist = { 3 | num_players = nil -- Set when joining lobbies, nil'd upon leaving 4 | } 5 | end 6 | 7 | 8 | if not _G.BigLobbyGlobals then 9 | _G.BigLobbyGlobals = {} 10 | 11 | -- Settings affected by BigLobby Mod Options 12 | BigLobbyGlobals.num_players_settings = nil 13 | BigLobbyGlobals.num_bots_settings = nil 14 | BigLobbyGlobals.allow_more_bots_settings = nil 15 | BigLobbyGlobals.auto_stop_all_bots_settings = nil 16 | 17 | -- Load custom lua files without specifying them in mod.txt -- 18 | BigLobbyGlobals.ModPath = ModPath 19 | BigLobbyGlobals.SavePath = SavePath 20 | 21 | BigLobbyGlobals.ClassPath = "lua/_custom/" 22 | 23 | BigLobbyGlobals.Classes = { 24 | "menu.lua", 25 | "husl.lua" 26 | } 27 | 28 | for _, class in pairs(BigLobbyGlobals.Classes) do 29 | dofile(BigLobbyGlobals.ModPath .. BigLobbyGlobals.ClassPath .. class) 30 | end 31 | -- End custom lua load -- 32 | 33 | 34 | -- Initializing menu will apply the default/saved settings 35 | BigLobbyGlobals.Menu = bkin_bl__menu:new() 36 | 37 | -- Set to the size of lobby you join, otherwise use your lobby size preferences for hosting 38 | BigLobbyGlobals.num_players = Global.BigLobbyPersist.num_players or BigLobbyGlobals.num_players_settings 39 | 40 | 41 | function BigLobbyGlobals:num_player_slots() 42 | return self.num_players 43 | end 44 | 45 | 46 | -- It's probably not going to cause any problems, but I'm capping the 47 | -- bot_slots to the lobby size just in case 48 | function BigLobbyGlobals:num_bot_slots() 49 | return math.min(self.num_bots_settings, self:num_player_slots()) 50 | end 51 | 52 | 53 | -- Regular lobby / Seamless switching support 54 | function BigLobbyGlobals:is_small_lobby() 55 | --TODO: Changing lobby slot size without reloading mods such as in 56 | -- Crime.Net won't properly update filters. Don't enable until working better 57 | return false --self.num_players<=4 58 | end 59 | 60 | 61 | -- Semantic versioning 62 | function BigLobbyGlobals:version() 63 | return "3.27.6" 64 | end 65 | 66 | 67 | -- GameVersion for matchmaking, integer is expected 68 | function BigLobbyGlobals:gameversion() 69 | return 3276 70 | end 71 | 72 | 73 | -- These tables show the network messages we've modified in the network settings pdmod 74 | -- We will use them for switching to biglobby prefixed messages when in big lobbies. 75 | local connection_network_handler_funcs = { 76 | 'kick_peer', 77 | 'remove_peer_confirmation', 78 | 'join_request_reply', 79 | 'peer_handshake', 80 | 'peer_exchange_info', 81 | 'connection_established', 82 | 'mutual_connection', 83 | 'set_member_ready', 84 | 'request_drop_in_pause', 85 | 'drop_in_pause_confirmation', 86 | 'set_peer_synched', 87 | 'dropin_progress', 88 | 'report_dead_connection', 89 | 'preplanning_reserved', 90 | 'draw_preplanning_event', 91 | 'sync_explode_bullet', 92 | 'sync_flame_bullet', 93 | 'sync_crime_spree_level', 94 | 'sync_player_installed_mod' 95 | } 96 | 97 | local unit_network_handler_funcs = { 98 | 'set_unit', 99 | 'remove_corpse_by_id', 100 | 'mission_ended', 101 | 'sync_trip_mine_setup', 102 | 'from_server_sentry_gun_place_result', 103 | 'place_sentry_gun', 104 | 'picked_up_sentry_gun', 105 | 'sync_equipment_setup', 106 | 'sync_ammo_bag_setup', 107 | 'on_sole_criminal_respawned', 108 | 'sync_grenades', 109 | 'sync_carry_data', 110 | 'sync_throw_projectile', 111 | 'sync_attach_projectile', 112 | 'sync_unlock_asset', 113 | 'sync_equipment_possession', 114 | 'sync_remove_equipment_possession', 115 | 'mark_minion', 116 | 'sync_statistics_result', 117 | 'suspicion', 118 | 'sync_enter_vehicle_host', 119 | 'sync_vehicle_player', 120 | 'sync_exit_vehicle', 121 | 'server_give_vehicle_loot_to_player', 122 | 'sync_give_vehicle_loot_to_player', 123 | 'sync_vehicle_interact_trunk', 124 | 'server_secure_loot', 125 | 'sync_secure_loot' 126 | } 127 | 128 | -- Builds a single table from our two string based keys for each handler above 129 | BigLobbyGlobals.network_handler_funcs = {} 130 | function add_handler_funcs(handler_funcs) 131 | for i = 1, #handler_funcs do 132 | BigLobbyGlobals.network_handler_funcs[handler_funcs[i]] = true 133 | end 134 | end 135 | 136 | add_handler_funcs(connection_network_handler_funcs) 137 | add_handler_funcs(unit_network_handler_funcs) 138 | 139 | 140 | -- Takes the network keys we defined above and prefixes any matches on the given handler 141 | function BigLobbyGlobals:rename_handler_funcs(NetworkHandler) 142 | for key, value in pairs(BigLobbyGlobals.network_handler_funcs) do 143 | if NetworkHandler[key] then 144 | NetworkHandler['biglobby__' .. key] = NetworkHandler[key] 145 | end 146 | end 147 | end 148 | 149 | 150 | -- Nothing calls this anymore for the time being. 151 | local log_data = true -- Can use to turn the logging on/off 152 | function BigLobbyGlobals:logger(content, use_chat) 153 | if log_data then 154 | if not content then return end 155 | 156 | if use_chat then 157 | managers.chat:_receive_message(ChatManager.GAME, "BigLobby", content, tweak_data.system_chat_color) 158 | end 159 | 160 | log(content) 161 | end 162 | end 163 | 164 | end 165 | -------------------------------------------------------------------------------- /lua/_custom/husl.lua: -------------------------------------------------------------------------------- 1 | -- HUSLP implementation from: https://github.com/husl-colors/husl-lua/blob/master/husl.lua 2 | -- I've modified it so all variables are tied to the `husl` table. PD2 also seems 3 | -- to return degrees and use degrees as input instead of radians for math functions. 4 | -- I've made adjustments to these methods commenting out the original to correct this. 5 | if not _G.HUSL then 6 | -- Version: 1.0.0 7 | -- public api 8 | 9 | local husl = {} 10 | 11 | husl._m = { 12 | { 3.240454162114103, -1.537138512797715, -0.49853140955601}, 13 | {-0.96926603050518, 1.876010845446694, 0.041556017530349}, 14 | { 0.055643430959114, -0.20402591351675, 1.057225188223179} 15 | } 16 | -- hard-coded d65 illuminant 17 | husl._refX = 0.95047 18 | husl._refY = 1.00000 19 | husl._refZ = 1.08883 20 | husl._refU = (4 * husl._refX) / (husl._refX + (15 * husl._refY) + (3 * husl._refZ)) 21 | husl._refV = (9 * husl._refY) / (husl._refX + (15 * husl._refY) + (3 * husl._refZ)) 22 | 23 | husl._kappa = 24389 / 27 24 | husl._epsilon = 216 / 24389 25 | 26 | function husl.husl_to_rgb(h, s, l) 27 | return husl.lch_to_rgb(husl.husl_to_lch(h, s, l)) 28 | end 29 | 30 | function husl.husl_to_hex(h, s, l) 31 | return husl.rgb_to_hex(husl.husl_to_rgb(h, s, l)) 32 | end 33 | 34 | function husl.rgb_to_husl(r, g, b) 35 | return husl.lch_to_husl(husl.rgb_to_lch(r, g, b)) 36 | end 37 | 38 | function husl.hex_to_husl(hex) 39 | return husl.rgb_to_husl(husl.hex_to_rgb(hex)) 40 | end 41 | 42 | function husl.huslp_to_rgb(h, s, l) 43 | return husl.lch_to_rgb(husl.huslp_to_lch(h, s, l)) 44 | end 45 | 46 | function husl.huslp_to_hex(h, s, l) 47 | return husl.rgb_to_hex(husl.huslp_to_rgb(h, s, l)) 48 | end 49 | 50 | function husl.rgb_to_huslp(r, g, b) 51 | return husl.lch_to_huslp(husl.rgb_to_lch(r, g, b)) 52 | end 53 | 54 | function husl.hex_to_huslp(hex) 55 | return husl.rgb_to_huslp(husl.hex_to_rgb(hex)) 56 | end 57 | 58 | function husl.lch_to_rgb(l, c, h) 59 | return husl.xyz_to_rgb(husl.luv_to_xyz(husl.lch_to_luv(l, c, h))) 60 | end 61 | 62 | function husl.rgb_to_lch(r, g, b) 63 | return husl.luv_to_lch(husl.xyz_to_luv(husl.rgb_to_xyz(r, g, b))) 64 | end 65 | 66 | function husl.max_chroma(L, H) 67 | -- local hrad = math.rad(H) 68 | local hrad = H 69 | local sinH = math.sin(hrad) 70 | local cosH = math.cos(hrad) 71 | local sub1 = math.pow(L + 16, 3) / 1560896 72 | local sub2 = (sub1 > husl._epsilon) and sub1 or (L / husl._kappa) 73 | 74 | local result = math.huge 75 | 76 | for _, row in ipairs(husl._m) do 77 | local m1, m2, m3 = unpack(row) 78 | 79 | local top = (12739311 * m3 + 11700000 * m2 + 11120499 * m1) * sub2 80 | local rbottom = 9608480 * m3 - 1921696 * m2 81 | local lbottom = 1441272 * m3 - 4323816 * m1 82 | 83 | local bottom = (rbottom * sinH + lbottom * cosH) * sub2 84 | 85 | local C0 = L * top / bottom 86 | local C1 = L * (top - 11700000) / (bottom + 1921696 * sinH) 87 | 88 | if C0 > 0 and C0 < result then 89 | result = C0 90 | end 91 | 92 | if C1 > 0 and C1 < result then 93 | result = C1 94 | end 95 | end 96 | 97 | return result 98 | end 99 | 100 | function husl.hrad_extremum(L) 101 | local lhs = (math.pow(L, 3) + 48 * math.pow(L, 2) + 768 * L + 4096) / 1560896 102 | local rhs = husl._epsilon 103 | local sub = (lhs > rhs) and lhs or L / husl._kappa 104 | 105 | local chroma = math.huge 106 | local result = nil 107 | 108 | for _, row in ipairs(husl._m) do 109 | local m1, m2, m3 = unpack(row) 110 | local bottom = (3 * m3 - 9 * m1) * sub 111 | 112 | for limit=0,1 do 113 | local top = (20 * m3 - 4 * m2) * sub + 4 * limit 114 | -- local hrad = math.atan2(top, bottom) 115 | local hrad = math.rad(math.atan2(top, bottom)) 116 | 117 | -- This is a math hack to deal with tan quadrants, I'm too lazy to figure 118 | -- out how to do this properly 119 | if limit == 1 then 120 | hrad = hrad + math.pi 121 | end 122 | 123 | local test = husl.max_chroma(L, math.deg(hrad)) 124 | 125 | if test < chroma then 126 | chroma = test 127 | result = hrad 128 | end 129 | end 130 | end 131 | 132 | return result 133 | end 134 | 135 | function husl.max_chroma_pastel(L) 136 | local H = math.deg(husl.hrad_extremum(L)) 137 | 138 | return husl.max_chroma(L, H) 139 | end 140 | 141 | function husl.dot_product(a, b) 142 | local sum = 0 143 | 144 | for i=1,#a do 145 | sum = sum + a[i] * b[i] 146 | end 147 | 148 | return sum 149 | end 150 | 151 | function husl.f(t) 152 | if t > husl._epsilon then 153 | return 116 * math.pow((t / husl._refY), 1 / 3) - 16 154 | else 155 | return t / husl._refY * husl._kappa 156 | end 157 | end 158 | 159 | function husl.f_inv(t) 160 | if t > 8 then 161 | return husl._refY * math.pow((t + 16) / 116, 3) 162 | else 163 | return husl._refY * t / husl._kappa 164 | end 165 | end 166 | 167 | function husl.from_linear(c) 168 | if c <= 0.0031308 then 169 | return 12.92 * c 170 | else 171 | return 1.055 * math.pow(c, 1 / 2.4) - 0.055 172 | end 173 | end 174 | 175 | function husl.to_linear(c) 176 | local a = 0.055 177 | 178 | if c > 0.04045 then 179 | return math.pow((c + a) / (1 + a), 2.4) 180 | else 181 | return c / 12.92 182 | end 183 | end 184 | 185 | function husl.round(number, digits) 186 | local f = math.pow(10, digits or 0) 187 | 188 | return math.floor(number * f + 0.5) / f 189 | end 190 | 191 | function husl.rgb_prepare(r, g, b) 192 | local prepared = {} 193 | 194 | for i, component in ipairs{r, g, b} do 195 | component = husl.round(component, 3) 196 | 197 | assert(component >= -0.0001 and component <= 1.0001, "illegal rgb value " .. component) 198 | 199 | component = math.min(1, math.max(component, 0)) 200 | 201 | prepared[i] = husl.round(component * 255) 202 | end 203 | 204 | return unpack(prepared) 205 | end 206 | 207 | function husl.hex_to_rgb(hex) 208 | hex = hex:gsub("#", "") 209 | 210 | local r = tonumber(hex:sub(1,2), 16) / 255 211 | local g = tonumber(hex:sub(3,4), 16) / 255 212 | local b = tonumber(hex:sub(5,6), 16) / 255 213 | 214 | return r, g, b 215 | end 216 | 217 | function husl.rgb_to_hex(r, g, b) 218 | return string.format("#%02x%02x%02x", husl.rgb_prepare(r, g, b)) 219 | end 220 | 221 | function husl.xyz_to_rgb(x, y, z) 222 | local rgb = {} 223 | 224 | for i, row in ipairs(husl._m) do 225 | rgb[i] = husl.from_linear(husl.dot_product(row, {x, y, z})) 226 | end 227 | 228 | return unpack(rgb) 229 | end 230 | 231 | function husl.rgb_to_xyz(r, g, b) 232 | local rgb = { 233 | husl.to_linear(r), 234 | husl.to_linear(g), 235 | husl.to_linear(b), 236 | } 237 | 238 | local xyz = {} 239 | local m_inv = { 240 | {0.41245643908969, 0.3575760776439, 0.18043748326639 }, 241 | {0.21267285140562, 0.71515215528781, 0.072174993306559}, 242 | {0.019333895582329, 0.1191920258813, 0.95030407853636 } 243 | } 244 | 245 | for i, row in ipairs(m_inv) do 246 | xyz[i] = husl.dot_product(row, rgb) 247 | end 248 | 249 | return unpack(xyz) 250 | end 251 | 252 | function husl.xyz_to_luv(X, Y, Z) 253 | if X == 0 and Y == 0 and Z == 0 then 254 | return 0, 0, 0 255 | end 256 | 257 | local varU = (4 * X) / (X + (15 * Y) + (3 * Z)) 258 | local varV = (9 * Y) / (X + (15 * Y) + (3 * Z)) 259 | local L = husl.f(Y) 260 | 261 | -- Black will create a divide-by-zero error 262 | if L == 0.0 then 263 | return 0, 0, 0 264 | end 265 | 266 | local U = 13 * L * (varU - husl._refU) 267 | local V = 13 * L * (varV - husl._refV) 268 | 269 | return L, U, V 270 | end 271 | 272 | function husl.luv_to_xyz(L, U, V) 273 | if L == 0 then 274 | return 0, 0, 0 275 | end 276 | 277 | local varY = husl.f_inv(L) 278 | local varU = U / (13 * L) + husl._refU 279 | local varV = V / (13 * L) + husl._refV 280 | local Y = varY * husl._refY 281 | local X = -(9 * Y * varU) / ((varU - 4) * varV - varU * varV) 282 | local Z = (9 * Y - (15 * varV * Y) - (varV * X)) / (3 * varV) 283 | 284 | return X, Y, Z 285 | end 286 | 287 | function husl.luv_to_lch(L, U, V) 288 | local C = (math.pow(math.pow(U, 2) + math.pow(V, 2), 0.5)) 289 | local hrad = (math.atan2(V, U)) 290 | 291 | -- local H = math.deg(hrad) 292 | local H = hrad 293 | 294 | if H < 0 then 295 | H = 360 + H 296 | end 297 | 298 | return L, C, H 299 | end 300 | 301 | function husl.lch_to_luv(L, C, H) 302 | -- local Hrad = math.rad(H) 303 | local Hrad = H 304 | local U = math.cos(Hrad) * C 305 | local V = math.sin(Hrad) * C 306 | 307 | return L, U, V 308 | end 309 | 310 | function husl.husl_to_lch(H, S, L) 311 | if L > 99.9999999 then 312 | return 100, 0, H 313 | elseif L < 0.00000001 then 314 | return 0, 0, H 315 | end 316 | 317 | local mx = husl.max_chroma(L, H) 318 | local C = mx / 100 * S 319 | 320 | return L, C, H 321 | end 322 | 323 | function husl.lch_to_husl(L, C, H) 324 | if L > 99.9999999 then 325 | return H, 0, 100 326 | elseif L < 0.00000001 then 327 | return H, 0, 0 328 | end 329 | 330 | local mx = husl.max_chroma(L, H) 331 | local S = C / mx * 100 332 | 333 | return H, S, L 334 | end 335 | 336 | function husl.huslp_to_lch(H, S, L) 337 | if L > 99.9999999 then 338 | return 100, 0, H 339 | elseif L < 0.00000001 then 340 | return 0, 0, H 341 | end 342 | 343 | local mx = husl.max_chroma_pastel(L) 344 | local C = mx / 100 * S 345 | 346 | return L, C, H 347 | end 348 | 349 | function husl.lch_to_huslp(L, C, H) 350 | if L > 99.9999999 then 351 | return H, 0, 100 352 | elseif L < 0.00000001 then 353 | return H, 0, 0 354 | end 355 | 356 | local mx = husl.max_chroma_pastel(L) 357 | local S = C / mx * 100 358 | 359 | return H, S, L 360 | end 361 | 362 | _G.HUSL = husl 363 | end 364 | -------------------------------------------------------------------------------- /lua/_custom/menu.lua: -------------------------------------------------------------------------------- 1 | bkin_bl__menu = bkin_bl__menu or class() 2 | bkin_bl__menu.menu_id = "bkin_bl__menu" 3 | bkin_bl__menu._data_path = BigLobbyGlobals.SavePath .. "big_lobby_new.json" 4 | bkin_bl__menu._data = bkin_bl__menu._data or {} 5 | 6 | 7 | function bkin_bl__menu:init() 8 | -- Load the user options 9 | self:Load() 10 | 11 | -- The new player limit is defined here, it should not be greater than 12 | -- the max values set in the network setting file. 13 | self._constants = {} 14 | self._constants.MAX_SLOTS = 128 15 | self._constants.UNIQUE_HEISTERS = 21 16 | self._data.lobby_size = self._data.lobby_size or 22 17 | self._data.allow_more_bots = self._data.allow_more_bots 18 | self._data.num_bots = self._data.num_bots or self._constants.UNIQUE_HEISTERS 19 | self._data.auto_stop_all_bots = self._data.auto_stop_all_bots or true 20 | 21 | -- Apply 'settings' values to BigLobbyGlobals 22 | BigLobbyGlobals.num_players_settings = self._data.lobby_size 23 | BigLobbyGlobals.allow_more_bots_settings = self._data.allow_more_bots 24 | BigLobbyGlobals.num_bots_settings = self._data.num_bots 25 | BigLobbyGlobals.auto_stop_all_bots_settings = self._data.auto_stop_all_bots 26 | 27 | -- Register the hooks for creating the option menu 28 | self:RegisterHooks() 29 | end 30 | 31 | 32 | function bkin_bl__menu:Save() 33 | local file = io.open( self._data_path, "w+" ) 34 | 35 | if file then 36 | local json_enc = json.encode( self._data ) 37 | 38 | -- Prevents BLT crash bug when trying to parse empty brackets [], empty braces {} are fine though 39 | file:write( json_enc ~= "[]" and json_enc or "{}" ) 40 | file:close() 41 | end 42 | end 43 | 44 | 45 | function bkin_bl__menu:Load() 46 | local file = io.open( self._data_path, "r" ) 47 | 48 | if file then 49 | self._data = json.decode( file:read("*all") ) 50 | file:close() 51 | end 52 | end 53 | 54 | 55 | function bkin_bl__menu:RegisterHooks() 56 | Hooks:Add("LocalizationManagerPostInit", "LocalizationManagerPostInit__bkin_bl", function(loc) 57 | if not language_filename then 58 | local selected_language = SystemInfo:language():key() 59 | local found_language = false 60 | 61 | for _, filename in pairs(file.GetFiles(BigLobbyGlobals.ModPath .. 'l10n/')) do 62 | local str = filename:match('^(.*).json$') 63 | if str and Idstring(str) and Idstring(str):key() == selected_language then 64 | language_filename = filename 65 | found_language = true 66 | break 67 | end 68 | end 69 | 70 | if not found_language then 71 | language_filename = "english.json" 72 | end 73 | end 74 | 75 | if language_filename then 76 | loc:load_localization_file(BigLobbyGlobals.ModPath .. 'l10n/' .. language_filename) 77 | end 78 | end) 79 | 80 | 81 | Hooks:Add("MenuManagerSetupCustomMenus", "MenuManagerSetupCustomMenus__bkin_bl", function( menu_manager, nodes ) 82 | MenuHelper:NewMenu( self.menu_id ) 83 | end) 84 | 85 | 86 | -- Menu Components and Callbacks 87 | Hooks:Add("MenuManagerPopulateCustomMenus", "MenuManagerPopulateCustomMenus__bkin_bl", function( menu_manager, nodes ) 88 | --Callbacks update data used by mod and for saving to file after user changes value 89 | MenuCallbackHandler.bkin_bl__set_size__clbk = function(menu_clbk, item) 90 | local num = math.floor( item:value() ) 91 | 92 | item:set_value(num) -- Update the slider display to avoid floating point numbers 93 | self._data.lobby_size = num -- Update so it can be saved 94 | BigLobbyGlobals.num_players_settings = num -- The variable that BigLobby references 95 | 96 | self:Save() 97 | end 98 | 99 | MenuCallbackHandler.bkin_bl__allow_more_bots__clbk = function(menu_clbk, item) 100 | local allow = ( item:value() == "on" and true or false ) 101 | 102 | item:set_value(allow) 103 | self._data.allow_more_bots = allow 104 | BigLobbyGlobals.allow_more_bots_settings = allow 105 | 106 | self:Save() 107 | end 108 | 109 | MenuCallbackHandler.bkin_bl__set_num_bots__clbk = function(menu_clbk, item) 110 | local num = math.floor( item:value() ) 111 | 112 | item:set_value(num) 113 | self._data.num_bots = num 114 | BigLobbyGlobals.num_bots_settings = num 115 | 116 | self:Save() 117 | end 118 | 119 | MenuCallbackHandler.bkin_bl__auto_stop_all_bots__clbk = function(menu_clbk, item) 120 | local allow = ( item:value() == "on" and true or false ) 121 | 122 | item:set_value(allow) 123 | self._data.auto_stop_all_bots = allow 124 | BigLobbyGlobals.auto_stop_all_bots_settings = allow 125 | 126 | self:Save() 127 | end 128 | 129 | 130 | MenuHelper:AddSlider({ 131 | id = "lobby_size", --ID only needs to be unique in the scope of the options node we are creating 132 | title = "bkin_bl__set_size__title", 133 | desc = "bkin_bl__set_size__desc", 134 | callback = "bkin_bl__set_size__clbk", 135 | value = self._data.lobby_size, 136 | min = 4, 137 | max = 22, 138 | step = 1, 139 | show_value = true, 140 | menu_id = self.menu_id, 141 | priority = 20 142 | }) 143 | 144 | MenuHelper:AddToggle({ 145 | id = "num_bots_toggle", 146 | title = "bkin_bl__allow_more_bots__title", 147 | desc = "bkin_bl__allow_more_bots__desc", 148 | callback = "bkin_bl__allow_more_bots__clbk", 149 | value = self._data.allow_more_bots, 150 | menu_id = self.menu_id, 151 | priority = 30 152 | }) 153 | 154 | MenuHelper:AddSlider({ 155 | id = "num_bots", 156 | title = "bkin_bl__set_num_bots__title", 157 | desc = "bkin_bl__set_num_bots__desc", 158 | callback = "bkin_bl__set_num_bots__clbk", 159 | value = self._data.num_bots, 160 | min = 3, 161 | max = 21, 162 | step = 1, 163 | show_value = true, 164 | menu_id = self.menu_id, 165 | priority = 10 166 | }) 167 | 168 | -- MenuHelper:AddToggle({ 169 | -- id = "auto_stop_all_bots_toggle", 170 | -- title = "bkin_bl__auto_stop_all_bots__title", 171 | -- desc = "bkin_bl__auto_stop_all_bots__desc", 172 | -- callback = "bkin_bl__auto_stop_all_bots__clbk", 173 | -- value = self._data.auto_stop_all_bots, 174 | -- menu_id = self.menu_id, 175 | -- priority = 6 176 | -- }) 177 | 178 | local mod = BLT.Mods.GetModOwnerOfFile and BLT.Mods:GetModOwnerOfFile(BigLobbyGlobals.ModPath) or BLT.Mods.GetMod and BLT.Mods:GetMod("BigLobby3-master") 179 | if not mod then 180 | return 181 | end 182 | 183 | BLT.Keybinds:register_keybind(mod, { id = "bkin_bl__stop_all_bots", allow_game = true, show_in_menu = false, callback = function() 184 | for _, ai in pairs(managers.groupai:state():all_AI_criminals()) do 185 | ai.unit:movement():set_should_stay(true) 186 | end 187 | end }) 188 | local bind = BLT.Keybinds:get_keybind("bkin_bl__stop_all_bots") 189 | local key = bind and bind:Key() or "" 190 | 191 | MenuHelper:AddKeybinding({ 192 | id = "bkin_bl__stop_all_bots", 193 | title = "bkin_bl__stop_all_bots__title", 194 | desc= "bkin_bl__stop_all_bots__desc", 195 | connection_name = "bkin_bl__stop_all_bots", 196 | binding = key, 197 | button = key, 198 | menu_id = self.menu_id, 199 | priority = 5 200 | }) 201 | 202 | BLT.Keybinds:register_keybind(mod, { id = "bkin_bl__release_all_bots", allow_game = true, show_in_menu = false, callback = function() 203 | for _, ai in pairs(managers.groupai:state():all_AI_criminals()) do 204 | ai.unit:movement():set_should_stay(false) 205 | end 206 | end }) 207 | local bind = BLT.Keybinds:get_keybind("bkin_bl__release_all_bots") 208 | local key = bind and bind:Key() or "" 209 | 210 | MenuHelper:AddKeybinding({ 211 | id = "bkin_bl__release_all_bots", 212 | title = "bkin_bl__release_all_bots__title", 213 | desc= "bkin_bl__release_all_bots__desc", 214 | connection_name = "bkin_bl__release_all_bots", 215 | binding = key, 216 | button = key, 217 | menu_id = self.menu_id, 218 | priority = 4 219 | }) 220 | end) 221 | 222 | 223 | Hooks:Add("MenuManagerBuildCustomMenus", "MenuManagerBuildCustomMenus__bkin_bl", function(menu_manager, nodes) 224 | nodes[self.menu_id] = MenuHelper:BuildMenu( self.menu_id ) 225 | MenuHelper:AddMenuItem( nodes["blt_options"], self.menu_id, "bkin_bl__menu__title", "bkin_bl__menu__desc", 1 ) 226 | end) 227 | end -------------------------------------------------------------------------------- /lua/lib/managers/_achievementmanager.lua: -------------------------------------------------------------------------------- 1 | -- Disable achievement progress if lobby size is greater than 4 players. 2 | local orig__AchievmentManager = { 3 | award = AchievmentManager.award, 4 | _give_reward = AchievmentManager._give_reward, 5 | award_progress = AchievmentManager.award_progress, 6 | award_steam = AchievmentManager.award_steam, 7 | steam_unlock_result = AchievmentManager.steam_unlock_result, 8 | award_epic = AchievmentManager.award_epic, 9 | epic_unlock_result = AchievmentManager.epic_unlock_result 10 | } 11 | 12 | 13 | function AchievmentManager:disable_achievements() 14 | -- `managers.network:session()` is here to prevent false positive, you should 15 | -- be able to unlock achievements while not in a game and have the mod enabled 16 | local m_session = managers.network:session() 17 | 18 | local isRegularAmount = m_session and (m_session:amount_of_players() > 4) 19 | local isRegularSize = m_session and BigLobbyGlobals:is_small_lobby() 20 | 21 | return isRegularAmount or isRegularSize 22 | end 23 | 24 | 25 | function AchievmentManager.award(self, ...) 26 | if not self:disable_achievements() then 27 | orig__AchievmentManager.award(self, ...) 28 | end 29 | end 30 | 31 | 32 | function AchievmentManager._give_reward(self, ...) 33 | if not self:disable_achievements() then 34 | orig__AchievmentManager._give_reward(self, ...) 35 | end 36 | end 37 | 38 | 39 | function AchievmentManager.award_progress(self, ...) 40 | if not self:disable_achievements() then 41 | orig__AchievmentManager.award_progress(self, ...) 42 | end 43 | end 44 | 45 | 46 | function AchievmentManager.award_steam(self, ...) 47 | if not self:disable_achievements() then 48 | orig__AchievmentManager.award_steam(self, ...) 49 | end 50 | end 51 | 52 | 53 | -- Original is defined in dot notation, presumably doesn't expect self to be 54 | -- passed in as first param? 55 | function AchievmentManager.steam_unlock_result(...) 56 | if not AchievmentManager:disable_achievements() then 57 | orig__AchievmentManager.steam_unlock_result(...) 58 | end 59 | end 60 | 61 | function AchievmentManager:award_epic(...) 62 | if not self:disable_achievements() then 63 | orig__AchievmentManager.award_epic(...) 64 | end 65 | end 66 | 67 | function AchievmentManager.epic_unlock_result(...) 68 | if not self:disable_achievements() then 69 | orig__AchievmentManager.epic_unlock_result(...) 70 | end 71 | end -------------------------------------------------------------------------------- /lua/lib/managers/_criminalsmanager.lua: -------------------------------------------------------------------------------- 1 | if Global.load_level == true and Global.game_settings.level_id == "vit" then return end 2 | if Global.load_level == true and Global.game_settings.level_id == "pex" then return end 3 | 4 | if BigLobbyGlobals.allow_more_bots_settings then 5 | CriminalsManager.MAX_NR_TEAM_AI = BigLobbyGlobals:num_bot_slots() 6 | end 7 | -- Not sure how useful this is, just updating it in case. 8 | CriminalsManager.MAX_NR_CRIMINALS = BigLobbyGlobals:num_player_slots() -------------------------------------------------------------------------------- /lua/lib/managers/_hudmanager.lua: -------------------------------------------------------------------------------- 1 | -- Modified to prevent UI bug, local player is no longer panel index 4, but a dynamic 2 | -- value set to `HUDManager.PLAYER_PANEL`. 3 | if BL2Options then return end 4 | function HUDManager:reset_player_hpbar() 5 | -- Only code changed was replacing two values hardcoded as 4 with the variable HUDManager.PLAYER_PANEL 6 | local crim_entry = managers.criminals:character_static_data_by_name(managers.criminals:local_character_name()) 7 | if not crim_entry then 8 | return 9 | end 10 | local color_id = managers.network:session():local_peer():id() 11 | self:set_teammate_callsign(HUDManager.PLAYER_PANEL, color_id) 12 | self:set_teammate_name(HUDManager.PLAYER_PANEL, managers.network:session():local_peer():name()) 13 | end 14 | -------------------------------------------------------------------------------- /lua/lib/managers/_hudmanagerpd2.lua: -------------------------------------------------------------------------------- 1 | -- This references 4 as in the 4th panel on the UI(one on the furtherest right) by default. 2 | -- Otherwise known as the local players UI panel. 3 | -- By updating this value, we keep that consistency that the player is used to. 4 | HUDManager.PLAYER_PANEL = BigLobbyGlobals:num_player_slots() 5 | if BL2Options then return end 6 | --Nothing seems to call this, I don't think it's even used.. Panels are created somewhere else 7 | function HUDManager:_create_teammates_panel(hud) 8 | hud = hud or managers.hud:script(PlayerBase.PLAYER_INFO_HUD_PD2) 9 | self._hud.teammate_panels_data = self._hud.teammate_panels_data or {} 10 | self._teammate_panels = {} 11 | if hud.panel:child("teammates_panel") then 12 | hud.panel:remove(hud.panel:child("teammates_panel")) 13 | end 14 | local h = self:teampanels_height() 15 | local teammates_panel = hud.panel:panel({ 16 | name = "teammates_panel", 17 | h = h, 18 | y = hud.panel:h() - h, 19 | halign = "grow", 20 | valign = "bottom" 21 | }) 22 | local teammate_w = 204 23 | local player_gap = 240 24 | local small_gap = (teammates_panel:w() - player_gap - teammate_w * HUDManager.PLAYER_PANEL) / (HUDManager.PLAYER_PANEL - 1) 25 | for i = 1, HUDManager.PLAYER_PANEL do 26 | local is_player = i == HUDManager.PLAYER_PANEL 27 | --do break end -- unhandled boolean indicator -- Decompile error here, hopefully not causing problems. 28 | 29 | self._hud.teammate_panels_data[i] = { 30 | taken = false, --this was true, but causes problem with add_teammate_panel() and data.taken, so maybe bad decompile bug from above? 31 | special_equipments = {} 32 | } 33 | local pw = teammate_w + (is_player and 0 or 64) 34 | local teammate = HUDTeammate:new(i, teammates_panel, is_player, pw) 35 | local x = math.floor((pw + small_gap) * (i - 1) + (i == HUDManager.PLAYER_PANEL and player_gap or 0)) 36 | teammate._panel:set_x(math.floor(x)) 37 | table.insert(self._teammate_panels, teammate) 38 | if is_player then 39 | teammate:add_panel() 40 | end 41 | end 42 | end 43 | 44 | 45 | -- TODO: nil check added, must have been causing a problem in past, not sure if still valid problem 46 | -- Possibly safe to delete 47 | local orig__HUDManager = {} 48 | orig__HUDManager.add_weapon = HUDManager.add_weapon 49 | function HUDManager:add_weapon(data) 50 | if not self._teammate_panels[HUDManager.PLAYER_PANEL] and not self._teammate_panels[HUDManager.PLAYER_PANEL]:panel() then 51 | log("[HUDManager :add_weapon] teammate_panels[HUDManager.PLAYER_PANEL] or teammate_panels[HUDManager.PLAYER_PANEL]:panel() is nil, HUDManager.PLAYER_PANEL = " .. tostring(HUDManager.PLAYER_PANEL)) 52 | return 53 | end 54 | 55 | 56 | orig__HUDManager.add_weapon(self, data) 57 | end 58 | 59 | 60 | -- `self:set_teammate_callsign(i, ai and 5 or peer_id)` 61 | -- Replaced hardcoded 5 with (HUDManager.PLAYER_PANEL + 1) 62 | -- TODO: Can probably wrap the function call and make the fix afterwards 63 | function HUDManager:add_teammate_panel(character_name, player_name, ai, peer_id) 64 | for i, data in ipairs(self._hud.teammate_panels_data) do 65 | if not data.taken then 66 | self._teammate_panels[i]:add_panel() 67 | self._teammate_panels[i]:set_peer_id(peer_id) 68 | self._teammate_panels[i]:set_ai(ai) 69 | self:set_teammate_callsign(i, ai and (HUDManager.PLAYER_PANEL + 1) or peer_id) -- TODO: this should cater for AI properly(change that 5 to dynamic variable) 70 | 71 | 72 | 73 | 74 | -- Original Code -- 75 | self:set_teammate_name(i, player_name) 76 | self:set_teammate_state(i, ai and "ai" or "player") 77 | if peer_id then 78 | local peer_equipment = managers.player:get_synced_equipment_possession(peer_id) or {} 79 | for equipment, amount in pairs(peer_equipment) do 80 | self:add_teammate_special_equipment(i, { 81 | id = equipment, 82 | icon = tweak_data.equipments.specials[equipment].icon, 83 | amount = amount 84 | }) 85 | end 86 | local peer_deployable_equipment = managers.player:get_synced_deployable_equipment(peer_id) 87 | if peer_deployable_equipment then 88 | local icon = tweak_data.equipments[peer_deployable_equipment.deployable].icon 89 | self:set_deployable_equipment(i, { 90 | icon = icon, 91 | amount = peer_deployable_equipment.amount 92 | }) 93 | end 94 | local peer_cable_ties = managers.player:get_synced_cable_ties(peer_id) 95 | if peer_cable_ties then 96 | local icon = tweak_data.equipments.specials.cable_tie.icon 97 | self:set_cable_tie(i, { 98 | icon = icon, 99 | amount = peer_cable_ties.amount 100 | }) 101 | end 102 | local peer_grenades = managers.player:get_synced_grenades(peer_id) 103 | if peer_grenades then 104 | local icon = tweak_data.blackmarket.projectiles[peer_grenades.grenade].icon 105 | self:set_teammate_grenades(i, { 106 | icon = icon, 107 | amount = Application:digest_value(peer_grenades.amount, false) 108 | }) 109 | end 110 | end 111 | local unit = managers.criminals:character_unit_by_name(character_name) 112 | if alive(unit) then 113 | local weapon = unit:inventory():equipped_unit() 114 | if alive(weapon) then 115 | local icon = weapon:base():weapon_tweak_data().hud_icon 116 | local equipped_selection = unit:inventory():equipped_selection() 117 | self:_set_teammate_weapon_selected(i, equipped_selection, icon) 118 | end 119 | end 120 | local peer_ammo_info = managers.player:get_synced_ammo_info(peer_id) 121 | if peer_ammo_info then 122 | for selection_index, ammo_info in pairs(peer_ammo_info) do 123 | self:set_teammate_ammo_amount(i, selection_index, unpack(ammo_info)) 124 | end 125 | end 126 | local peer_carry_data = managers.player:get_synced_carry(peer_id) 127 | if peer_carry_data then 128 | self:set_teammate_carry_info(i, peer_carry_data.carry_id, managers.loot:get_real_value(peer_carry_data.carry_id, peer_carry_data.multiplier)) 129 | end 130 | data.taken = true 131 | return i 132 | end 133 | end 134 | -- End Original Code -- 135 | end 136 | 137 | function HUDManager:set_teammate_health(i, data) 138 | if i and self._teammate_panels and self._teammate_panels[i] then 139 | self._teammate_panels[i]:set_health(data) 140 | end 141 | end -------------------------------------------------------------------------------- /lua/lib/managers/_vehiclemanager.lua: -------------------------------------------------------------------------------- 1 | -- Levels that trigger events that depend on all players inside become a problem with >4 players 2 | -- This caps the `total_players` variable to 4. The vehicle events are triggered by a listener 3 | -- fired by `VehicleManager:on_player_entered_vehicle(vehicle_unit, player)` 4 | -- Only change to this function is the 2nd line added to do the cap. 5 | function VehicleManager:all_players_in_vehicles() 6 | local total_players = managers.network:session():amount_of_alive_players() 7 | total_players = total_players > 4 and 4 or total_players 8 | 9 | local players_in_vehicles = 0 10 | for _, vehicle in pairs(self._vehicles) do 11 | players_in_vehicles = players_in_vehicles + vehicle:vehicle_driving():num_players_inside() 12 | end 13 | 14 | return total_players == players_in_vehicles 15 | end 16 | -------------------------------------------------------------------------------- /lua/lib/managers/group_ai_states/_groupaistatebase.lua: -------------------------------------------------------------------------------- 1 | -- Modified to support full team size instead of hardcoded 3, not hugely relevant as is a bot thing. 2 | function GroupAIStateBase:on_criminal_team_AI_enabled_state_changed() 3 | local num_player_slots = BigLobbyGlobals:num_player_slots() - 1 4 | 5 | -- Only code changed was replacing hardcoded 3 with variable num_player_slots 6 | if Network:is_client() then 7 | return 8 | end 9 | if managers.groupai:state():team_ai_enabled() then 10 | self:fill_criminal_team_with_AI() 11 | else 12 | for i = 1, num_player_slots do 13 | self:remove_one_teamAI() 14 | end 15 | end 16 | end 17 | 18 | -- Hooks:PostHook( GroupAIStateBase , "whisper_mode" , "GroupAIStateBasePostWhisperMode" , function( self ) 19 | -- for _, ai in pairs(self:all_AI_criminals()) do 20 | -- if self._whisper_mode == true and BigLobbyGlobals.auto_stop_all_bots_settings then 21 | -- ai.unit:movement():set_should_stay(true) 22 | -- elseif self._whisper_mode == false and BigLobbyGlobals.auto_stop_all_bots_settings then 23 | -- if ai.unit:movement() and ai.unit:movement()._should_stay then 24 | -- ai.unit:movement():set_should_stay(false) 25 | -- end 26 | -- end 27 | -- end 28 | -- end ) 29 | 30 | -- Hooks:PostHook( GroupAIStateBase , "whisper_mode" , "GroupAIStateBasePostWhisperMode" , function( self ) 31 | -- for _, ai in pairs(self:all_AI_criminals()) do 32 | -- if self._whisper_mode == true and Network:is_server() then 33 | -- ai.unit:movement():play_redirect("crouch") 34 | -- ai.unit:movement():action_request({ 35 | -- align_sync = true, 36 | -- body_part = 1, 37 | -- type = "idle" 38 | -- }) 39 | -- else 40 | -- return 41 | -- end 42 | -- end 43 | -- end ) -------------------------------------------------------------------------------- /lua/lib/managers/hud/_hudlootscreen.lua: -------------------------------------------------------------------------------- 1 | -- To avoid overriding `HUDLootScreen:init()` for a loop in the middle of it I'm 2 | -- hooking the first method afterwards to add the extra peers to the required table. 3 | -- It should be safe approach and allow for the rest of the init method to use the 4 | -- extra peers. 5 | HUDLootScreen._init_extra_peers = true 6 | 7 | function HUDLootScreen:set_num_visible(peers_num) 8 | local num_player_slots = BigLobbyGlobals:num_player_slots() 9 | 10 | if HUDLootScreen._init_extra_peers or (table.getn(self._peer_data) == 4 and num_player_slots > 4) then 11 | for i = 5, num_player_slots do 12 | self:create_peer(self._peers_panel, i) 13 | end 14 | 15 | -- This is only run once for `HUDLootScreen:init()` 16 | HUDLootScreen._init_extra_peers = false 17 | end 18 | 19 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 20 | self._num_visible = math.max(self._num_visible, peers_num) 21 | for i = 1, num_player_slots do 22 | self._peers_panel:child("peer" .. i):set_visible(i <= self._num_visible) 23 | end 24 | self._peers_panel:set_h(self._num_visible * 110) 25 | self._peers_panel:set_center_y(self._hud_panel:h() * 0.5) 26 | 27 | -- TODO: Is this console code useful for reworking the UI layout? 28 | if managers.menu:is_console() and self._num_visible >= 4 then 29 | self._peers_panel:move(0, 30) 30 | end 31 | end 32 | 33 | 34 | function HUDLootScreen:clear_other_peers(peer_id) 35 | local num_player_slots = BigLobbyGlobals:num_player_slots() 36 | 37 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 38 | peer_id = peer_id or self:get_local_peer_id() 39 | for i = 1, num_player_slots do 40 | if i ~= peer_id then 41 | self:remove_peer(i) 42 | end 43 | end 44 | end 45 | 46 | 47 | function HUDLootScreen:check_all_ready() 48 | local num_player_slots = BigLobbyGlobals:num_player_slots() 49 | 50 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 51 | local ready = true 52 | for i = 1, num_player_slots do 53 | if self._peer_data[i].active and ready then 54 | ready = self._peer_data[i].ready 55 | end 56 | end 57 | return ready 58 | end 59 | 60 | 61 | function HUDLootScreen:update(t, dt) 62 | local num_player_slots = BigLobbyGlobals:num_player_slots() 63 | 64 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 65 | for peer_id = 1, num_player_slots do 66 | if self._peer_data[peer_id].wait_t then 67 | self._peer_data[peer_id].wait_t = math.max(self._peer_data[peer_id].wait_t - dt, 0) 68 | local panel = self._peers_panel:child("peer" .. tostring(peer_id)) 69 | local card_info_panel = panel:child("card_info") 70 | local main_text = card_info_panel:child("main_text") 71 | main_text:set_text(managers.localization:to_upper_text("menu_l_choose_card_chosen", { 72 | time = math.ceil(self._peer_data[peer_id].wait_t) 73 | })) 74 | local _, _, _, hh = main_text:text_rect() 75 | main_text:set_h(hh + 2) 76 | if self._peer_data[peer_id].wait_t == 0 then 77 | main_text:set_text(managers.localization:to_upper_text("menu_l_choose_card_chosen_suspense")) 78 | local joker = self._peer_data[peer_id].joker 79 | local steam_drop = self._peer_data[peer_id].steam_drop 80 | local effects = self._peer_data[peer_id].effects 81 | panel:child("card" .. self._peer_data[peer_id].selected):animate(callback(self, self, "flipcard"), steam_drop and 5.5 or 2.5, callback(self, self, "show_item"), peer_id, effects) 82 | self._peer_data[peer_id].wait_t = false 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lua/lib/managers/hud/_hudmissionbriefing.lua: -------------------------------------------------------------------------------- 1 | -- Instead of overriding methods, I am now hooking them and continuiing the loop 2 | -- to handle >4 peers. 3 | local orig__HUDMissionBriefing = { 4 | init = HUDMissionBriefing.init 5 | } 6 | 7 | 8 | function HUDMissionBriefing:init(...) 9 | orig__HUDMissionBriefing.init(self, ...) 10 | 11 | local num_player_slots = BigLobbyGlobals:num_player_slots() 12 | 13 | local text_font = tweak_data.menu.pd2_small_font 14 | local text_font_size = tweak_data.menu.pd2_small_font_size 15 | 16 | -- Adjust height of panel to accomodate for the amount of player slots 17 | self._ready_slot_panel:set_h( text_font_size * num_player_slots + 20 ) 18 | 19 | -- Adds player slot panels for peers >4 20 | if not self._singleplayer then 21 | local voice_icon, voice_texture_rect = tweak_data.hud_icons:get_icon_data("mugshot_talk") 22 | local infamy_icon, infamy_rect = tweak_data.hud_icons:get_icon_data("infamy_icon") 23 | for i = 5, num_player_slots do 24 | 25 | 26 | 27 | 28 | -- Original Code -- 29 | local color_id = i 30 | local color = tweak_data.chat_colors[color_id] 31 | local slot_panel = self._ready_slot_panel:panel({ 32 | name = "slot_" .. tostring(i), 33 | h = text_font_size, 34 | y = (i - 1) * text_font_size + 10, 35 | x = 10, 36 | w = self._ready_slot_panel:w() - 20 37 | }) 38 | local criminal = slot_panel:text({ 39 | name = "criminal", 40 | font_size = text_font_size, 41 | font = text_font, 42 | color = color, 43 | text = tweak_data.gui.LONGEST_CHAR_NAME, 44 | blend_mode = "add", 45 | align = "left", 46 | vertical = "center" 47 | }) 48 | local voice = slot_panel:bitmap({ 49 | name = "voice", 50 | texture = voice_icon, 51 | visible = false, 52 | layer = 2, 53 | texture_rect = voice_texture_rect, 54 | w = voice_texture_rect[3], 55 | h = voice_texture_rect[4], 56 | color = color, 57 | x = 10 58 | }) 59 | local name = slot_panel:text({ 60 | name = "name", 61 | text = managers.localization:text("menu_lobby_player_slot_available") .. " ", 62 | font = text_font, 63 | font_size = text_font_size, 64 | color = color:with_alpha(0.5), 65 | align = "left", 66 | vertical = "center", 67 | w = 256, 68 | h = text_font_size, 69 | layer = 1, 70 | blend_mode = "add" 71 | }) 72 | local status = slot_panel:text({ 73 | name = "status", 74 | visible = false, 75 | text = " ", 76 | font = text_font, 77 | font_size = text_font_size, 78 | align = "right", 79 | vertical = "center", 80 | w = 256, 81 | h = text_font_size, 82 | layer = 1, 83 | blend_mode = "add", 84 | color = tweak_data.screen_colors.text:with_alpha(0.5) 85 | }) 86 | local infamy = slot_panel:bitmap({ 87 | name = "infamy", 88 | texture = infamy_icon, 89 | texture_rect = infamy_rect, 90 | visible = false, 91 | layer = 2, 92 | color = color, 93 | y = 1 94 | }) 95 | local detection = slot_panel:panel({ 96 | name = "detection", 97 | layer = 2, 98 | visible = false, 99 | w = slot_panel:h(), 100 | h = slot_panel:h() 101 | }) 102 | local detection_ring_left_bg = detection:bitmap({ 103 | name = "detection_left_bg", 104 | texture = "guis/textures/pd2/mission_briefing/inv_detection_meter", 105 | alpha = 0.2, 106 | blend_mode = "add", 107 | w = detection:w(), 108 | h = detection:h() 109 | }) 110 | local detection_ring_right_bg = detection:bitmap({ 111 | name = "detection_right_bg", 112 | texture = "guis/textures/pd2/mission_briefing/inv_detection_meter", 113 | alpha = 0.2, 114 | blend_mode = "add", 115 | w = detection:w(), 116 | h = detection:h() 117 | }) 118 | detection_ring_right_bg:set_texture_rect(detection_ring_right_bg:texture_width(), 0, -detection_ring_right_bg:texture_width(), detection_ring_right_bg:texture_height()) 119 | local detection_ring_left = detection:bitmap({ 120 | name = "detection_left", 121 | texture = "guis/textures/pd2/mission_briefing/inv_detection_meter", 122 | render_template = "VertexColorTexturedRadial", 123 | blend_mode = "add", 124 | layer = 1, 125 | w = detection:w(), 126 | h = detection:h() 127 | }) 128 | local detection_ring_right = detection:bitmap({ 129 | name = "detection_right", 130 | texture = "guis/textures/pd2/mission_briefing/inv_detection_meter", 131 | render_template = "VertexColorTexturedRadial", 132 | blend_mode = "add", 133 | layer = 1, 134 | w = detection:w(), 135 | h = detection:h() 136 | }) 137 | detection_ring_right:set_texture_rect(detection_ring_right:texture_width(), 0, -detection_ring_right:texture_width(), detection_ring_right:texture_height()) 138 | local detection_value = slot_panel:text({ 139 | name = "detection_value", 140 | font_size = text_font_size, 141 | font = text_font, 142 | color = color, 143 | text = " ", 144 | blend_mode = "add", 145 | align = "left", 146 | vertical = "center" 147 | }) 148 | detection:set_left(slot_panel:w() * 0.65) 149 | detection_value:set_left(detection:right() + 2) 150 | detection_value:set_visible(detection:visible()) 151 | local _, _, w, _ = criminal:text_rect() 152 | voice:set_left(w + 2) 153 | criminal:set_w(w) 154 | criminal:set_align("right") 155 | criminal:set_text("") 156 | name:set_left(voice:right() + 2) 157 | status:set_right(slot_panel:w()) 158 | infamy:set_left(name:x()) 159 | end 160 | BoxGuiObject:new(self._ready_slot_panel, { 161 | sides = { 162 | 1, 163 | 1, 164 | 1, 165 | 1 166 | } 167 | }) 168 | end 169 | -- End Original Code -- 170 | end 171 | -------------------------------------------------------------------------------- /lua/lib/managers/hud/_hudteammate.lua: -------------------------------------------------------------------------------- 1 | -- This function contains a variable `names` which is a table of dummy name strings. 2 | -- Unfortunately the function is massive and that data is hardcoded at the start of 3 | -- the function. Thankfully we might still be able to work around it without breaking 4 | -- other mods such as hud mods which may heavily modify this code. 5 | local orig__HUDTeammate = { 6 | init = HUDTeammate.init 7 | } 8 | 9 | if BL2Options then return end 10 | function HUDTeammate:init(i, teammates_panel, is_player, width) 11 | -- Main difference in this function is based on the `main_player` variable 12 | -- This refers to the local/client player, so as long as we can make that 13 | -- true when appropriate and false when not, we should be good. Just need to 14 | -- fix some settings after the original function finishes and hopefully 15 | -- everything works as it should. 16 | local fake_i = 1 17 | local real_player_panel = HUDManager.PLAYER_PANEL 18 | 19 | if (i == HUDManager.PLAYER_PANEL) then 20 | fake_i = 4 21 | HUDManager.PLAYER_PANEL = 4 22 | end 23 | 24 | orig__HUDTeammate.init(self, fake_i, teammates_panel, is_player, width) 25 | 26 | -- Fix some properties to align with the real i value. 27 | self._id = i 28 | self._panel:set_name("" .. i) 29 | self._panel:child("callsign"):set_color(tweak_data.chat_colors[i]:with_alpha(1)) 30 | 31 | HUDManager.PLAYER_PANEL = real_player_panel 32 | end 33 | -------------------------------------------------------------------------------- /lua/lib/managers/menu/_contractboxgui.lua: -------------------------------------------------------------------------------- 1 | -- Modified to support rendering UI text for additional peers in lobby screen. 2 | -- Instead of overriding methods, I am now hooking them and continuiing the loop 3 | -- to handle >4 peers. 4 | local orig__ContractBoxGui = { 5 | create_contract_box = ContractBoxGui.create_contract_box, 6 | update = ContractBoxGui.update 7 | } 8 | 9 | 10 | function ContractBoxGui:create_contract_box() 11 | orig__ContractBoxGui.create_contract_box(self) 12 | 13 | if not managers.network:session() then 14 | return 15 | end 16 | 17 | local num_player_slots = BigLobbyGlobals:num_player_slots() 18 | 19 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 20 | for i = 5, num_player_slots do 21 | local peer = managers.network:session():peer(i) 22 | if peer then 23 | local peer_pos = managers.menu_scene:character_screen_position(i) 24 | local peer_name = peer:name() 25 | 26 | if peer_pos then 27 | self:create_character_text(i, peer_pos.x, peer_pos.y, peer_name) 28 | end 29 | end 30 | end 31 | end 32 | 33 | 34 | -- Modified to support rendering UI text for additional peers. 35 | function ContractBoxGui:update(t, dt) 36 | local num_player_slots = BigLobbyGlobals:num_player_slots() 37 | 38 | for i = 5, num_player_slots do 39 | self:update_character(i) 40 | end 41 | 42 | orig__ContractBoxGui.update(self, t, dt) 43 | end -------------------------------------------------------------------------------- /lua/lib/managers/menu/_crewmanagementgui.lua: -------------------------------------------------------------------------------- 1 | 2 | -- Removes the hard coded 3 preferred characters 3 | function CrewManagementGui:select_characters(data, gui) 4 | local preferred = managers.blackmarket:preferred_henchmen() 5 | local num_player_slots = BigLobbyGlobals:num_player_slots() - 1 6 | if data.equipped_by then 7 | print("unselect") 8 | managers.blackmarket:set_preferred_henchmen(data.equipped_by, nil) 9 | else 10 | local index = math.min(#preferred + 1, num_player_slots) 11 | 12 | print(index, #preferred) 13 | managers.blackmarket:set_preferred_henchmen(index, data.name) 14 | end 15 | 16 | gui:reload() 17 | end -------------------------------------------------------------------------------- /lua/lib/managers/menu/_crimespreemissionsmenucomponent.lua: -------------------------------------------------------------------------------- 1 | function CrimeSpreeMissionButton:_get_mission_category(mission) 2 | if not mission.add then return end 3 | if mission.add <= 5 then 4 | return "short" 5 | elseif mission.add <= 7 then 6 | return "medium" 7 | else 8 | return "long" 9 | end 10 | end -------------------------------------------------------------------------------- /lua/lib/managers/menu/_menucomponentmanager.lua: -------------------------------------------------------------------------------- 1 | -- Instead of overriding methods, I am now hooking them and continuiing the loop 2 | -- to handle >4 peers. 3 | local orig__MenuComponentManager = { 4 | create_contract_gui = MenuComponentManager.create_contract_gui, 5 | show_contract_character = MenuComponentManager.show_contract_character 6 | } 7 | 8 | 9 | -- I believe this creates/updates label state based on what the player is doing? 10 | function MenuComponentManager:create_contract_gui() 11 | orig__MenuComponentManager.create_contract_gui(self) 12 | 13 | local num_player_slots = BigLobbyGlobals:num_player_slots() 14 | local peers_state = managers.menu:get_all_peers_state() or {} 15 | 16 | for i = 5, num_player_slots do 17 | self._contract_gui:update_character_menu_state(i, peers_state[i]) 18 | end 19 | end 20 | 21 | 22 | -- Shows the peer label over character with alpha of 1 if loaded, or 0.4 if still 23 | -- in a loading/joining state? 24 | function MenuComponentManager:show_contract_character(state) 25 | orig__MenuComponentManager.show_contract_character(self, state) 26 | 27 | local num_player_slots = BigLobbyGlobals:num_player_slots() 28 | 29 | if self._contract_gui then 30 | for i = 5, num_player_slots do 31 | self._contract_gui:set_character_panel_alpha(i, state and 1 or 0.4) 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lua/lib/managers/menu/_menulobbyrenderer.lua: -------------------------------------------------------------------------------- 1 | -- Modified to support additional player slots in the mission briefing screen. 2 | function MenuLobbyRenderer:open(...) 3 | local num_player_slots = BigLobbyGlobals:num_player_slots() 4 | 5 | -- Original Code -- 6 | MenuLobbyRenderer.super.open(self, ...) 7 | 8 | local safe_rect_pixels = managers.gui_data:scaled_size() 9 | local scaled_size = safe_rect_pixels 10 | 11 | MenuRenderer._create_framing(self) 12 | self._main_panel:hide() 13 | 14 | self._player_slots = {} 15 | self._menu_bg = self._fullscreen_panel:panel({}) 16 | 17 | if _G.IS_VR then 18 | self._menu_bg:rect({ 19 | halign = "scale", 20 | valign = "scale", 21 | visible = true, 22 | layer = -1000, 23 | color = Color.black 24 | }) 25 | end 26 | 27 | local is_server = Network:is_server() 28 | local server_peer = is_server and managers.network:session():local_peer() or managers.network:session():server_peer() 29 | local is_single_player = Global.game_settings.single_player 30 | local is_multiplayer = not is_single_player 31 | 32 | if not server_peer then 33 | return 34 | end 35 | -- End Original Code -- 36 | 37 | 38 | 39 | 40 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 41 | for i = 1, is_single_player and 1 or num_player_slots do 42 | local t = { 43 | player = {}, 44 | free = true, 45 | kit_slots = {}, 46 | params = {} 47 | } 48 | 49 | for slot = 1, PlayerManager.WEAPON_SLOTS + 3 do 50 | table.insert(t.kit_slots, slot) 51 | end 52 | 53 | table.insert(self._player_slots, t) 54 | end 55 | 56 | 57 | 58 | 59 | -- Original Code -- 60 | if is_server then 61 | local level = managers.experience:current_level() 62 | local rank = managers.experience:current_rank() 63 | local join_stinger_index = managers.infamy:selected_join_stinger_index() 64 | 65 | self:_set_player_slot(1, { 66 | character = "random", 67 | name = server_peer:name(), 68 | peer_id = server_peer:id(), 69 | level = level, 70 | rank = rank, 71 | join_stinger_index = join_stinger_index 72 | }) 73 | end 74 | 75 | self:_entered_menu() 76 | -- End Original Code -- 77 | end 78 | -------------------------------------------------------------------------------- /lua/lib/managers/menu/_menuscenemanager.lua: -------------------------------------------------------------------------------- 1 | function MenuSceneManager:_setup_lobby_characters() 2 | local num_player_slots = BigLobbyGlobals:num_player_slots() 3 | 4 | 5 | 6 | 7 | -- Original Code -- 8 | if self._lobby_characters then 9 | for _, unit in ipairs(self._lobby_characters) do 10 | self:_delete_character_mask(unit) 11 | World:delete_unit(unit) 12 | end 13 | end 14 | self._lobby_characters = {} 15 | self._characters_offset = Vector3(0, -200, -130) 16 | -- End Original Code -- 17 | 18 | 19 | 20 | 21 | -- Code changed was: 22 | -- Replacing hardcoded 4 with variable num_player_slots 23 | -- Replacing the local masks variable from hardcoded table to dynamically generated via loop 24 | -- Replacing self._characters_rotation from hardcoded table to dynamically generated via loop 25 | self._characters_rotation = {} 26 | 27 | -- Dynamically building a range of rotations, used below and by MenuSceneManager:set_lobby_character_out_fit 28 | local min = -130 29 | local max = -56 30 | local range = min - max 31 | local steps = range / (num_player_slots-1) 32 | -- The first half of rotations is for all peers except local peer: 33 | for i = 1, num_player_slots do 34 | self._characters_rotation[i] = (((i-1) * steps) + max) 35 | end 36 | 37 | -- The second half of rotations is what the local peer uses based on their peer id: 38 | for i = 1, num_player_slots do 39 | self._characters_rotation[i+num_player_slots] = (((i-1) * steps) + max) 40 | end 41 | 42 | -- Dummy string filled with "dallas" because that's what it was originally 43 | local masks = {} 44 | for i = 1, num_player_slots do 45 | masks[i] = "dallas" 46 | end 47 | 48 | 49 | -- This added logic should alter positioning of players to start at the center 50 | -- and expand outwards as the player count grows. 51 | local offset = 0 -- Starting offset 52 | local peer_rotations = #self._characters_rotation/2 53 | -- eg 4/2->2, 5/2->2.5->3, 6/2->3, 7/2->3.5->4 54 | local center_index = math.ceil(peer_rotations/2) 55 | local function get_new_index(index) 56 | local is_even = index%2==0 57 | local new_index = is_even and center_index + offset or center_index - offset 58 | -- After adding one to the left and one to the right, increase the offset 59 | -- Unless starting index is odd, then increase straight after(no left/right) 60 | if not is_even then offset = offset + 1 end 61 | 62 | return new_index 63 | end 64 | 65 | 66 | -- Only code changed here was replacing a hardcoded value of 4 with the variable 67 | -- num_player_slots and adding the local function above for set_yaw_pitch_roll 68 | local mvec = Vector3() 69 | local math_up = math.UP 70 | local pos = Vector3() 71 | local rot = Rotation() 72 | for i = 1, num_player_slots do 73 | mrotation.set_yaw_pitch_roll(rot, self._characters_rotation[get_new_index(i)], 0, 0) 74 | mvector3.set(pos, self._characters_offset) 75 | mvector3.rotate_with(pos, rot) 76 | mvector3.set(mvec, pos) 77 | mvector3.negate(mvec) 78 | mvector3.set_z(mvec, 0) 79 | mrotation.set_look_at(rot, mvec, math_up) 80 | local unit_name = tweak_data.blackmarket.characters.locked.menu_unit 81 | local unit = World:spawn_unit(Idstring(unit_name), pos, rot) 82 | self:_init_character(unit, i) 83 | self:set_character_mask(tweak_data.blackmarket.masks[ masks[i] ].unit, unit, nil, masks[i]) 84 | table.insert(self._lobby_characters, unit) 85 | self:set_lobby_character_visible(i, false, true) 86 | end 87 | end 88 | 89 | function MenuSceneManager:_select_lobby_character_pose(peer_id, unit, weapon_info) 90 | 91 | 92 | 93 | 94 | -- Original Code -- 95 | local state = unit:play_redirect(Idstring("idle_menu")) 96 | local weapon_id = managers.weapon_factory:get_weapon_id_by_factory_id(weapon_info.factory_id) 97 | local category = tweak_data.weapon[weapon_id].category 98 | local lobby_poses = self._lobby_poses[weapon_id] 99 | lobby_poses = lobby_poses or self._lobby_poses[category] 100 | lobby_poses = lobby_poses or self._lobby_poses.generic 101 | if type(lobby_poses[1]) == "string" then 102 | local pose = lobby_poses[math.random(#lobby_poses)] 103 | unit:anim_state_machine():set_parameter(state, pose, 1) 104 | else 105 | -- End Original Code -- 106 | 107 | 108 | 109 | 110 | -- Only modification is to use modulus to make sure our lobby peer is given a pose 111 | local pose = lobby_poses[(peer_id % #lobby_poses) + 1][math.random(#lobby_poses[(peer_id % #lobby_poses) + 1])] 112 | 113 | 114 | 115 | 116 | -- Original Code -- 117 | unit:anim_state_machine():set_parameter(state, pose, 1) 118 | end 119 | -- End Original Code -- 120 | end 121 | 122 | 123 | -- TODO: This shouldn't be relevant to gameplay, should be safe to delete. 124 | -- Not required for this mod to work, just updates the test function to work with more players 125 | -- Only for testing (literally no relevance to the game) 126 | -- function MenuSceneManager:test_show_all_lobby_characters(enable_card, pose) 127 | -- local num_player_slots = 15--BigLobbyGlobals:num_player_slots() 128 | -- pose = pose or "lobby_generic_idle1" 129 | -- 130 | -- local mvec = Vector3() 131 | -- local math_up = math.UP 132 | -- local pos = Vector3() 133 | -- local rot = Rotation() 134 | -- -- Forcing all `self._ti` to be static 1, this was for a predictable outcome 135 | -- -- since each call of this function was to test the feature with the client 136 | -- -- player in each of the different player slots? 137 | -- self._ti = 1--(self._ti or 0) + 1 138 | -- --self._ti = (self._ti - 1) % num_player_slots + 1 139 | -- for i = 1, num_player_slots do 140 | -- local is_me = i == self._ti 141 | -- log("TEST LOBBY, is_me = " .. tostring(is_me) .. ", i: " .. tostring(i)) 142 | -- local unit = self._lobby_characters[i] 143 | -- if unit and alive(unit) then 144 | -- if enable_card then 145 | -- self:set_character_card(i, math.random(25), unit) 146 | -- else 147 | -- -- Was forcing the pose for some reason instead of fix below? 148 | -- self:_set_character_unit_pose(pose, unit) 149 | -- -- local state = unit:play_redirect(Idstring("idle_menu")) 150 | -- -- Only 4 variations of lobby_generic_idle? 4%4=0 though, might want to + 1 if 0? 151 | -- -- unit:anim_state_machine():set_parameter(state, "lobby_generic_idle" .. i%4, 1) 152 | -- end 153 | -- -- Uses 0+i for rotation index unless it's your player unit then 4+i for some reason? 154 | -- -- I guess this explains the 8 values in `self._characters_rotation`, the last half are specific to the client player 155 | -- -- TODO: Therefore 4 should really be half of the table size? 156 | -- -- will break if not enough rotation indices use a modulus? (or you know, just make the table dynamically built) 157 | -- mrotation.set_yaw_pitch_roll(rot, self._characters_rotation[(is_me and 4 or 0) + i], 0, 0) 158 | -- mvector3.set(pos, self._characters_offset) 159 | -- if is_me then 160 | -- mvector3.set_y(pos, mvector3.y(pos) + 100) 161 | -- end 162 | -- mvector3.rotate_with(pos, rot) 163 | -- mvector3.set(mvec, pos) 164 | -- mvector3.negate(mvec) 165 | -- mvector3.set_z(mvec, 0) 166 | -- mrotation.set_look_at(rot, mvec, math_up) 167 | -- unit:set_position(pos) 168 | -- unit:set_rotation(rot) 169 | -- local character = managers.blackmarket:equipped_character() 170 | -- local mask_blueprint = managers.blackmarket:equipped_mask().blueprint 171 | -- self:change_lobby_character(i, character) 172 | -- unit = self._lobby_characters[i] 173 | -- self:set_character_mask_by_id(managers.blackmarket:equipped_mask().mask_id, mask_blueprint, unit, i) 174 | -- self:set_character_armor(managers.blackmarket:equipped_armor(), unit) 175 | -- self:set_lobby_character_visible(i, true) 176 | -- end 177 | -- end 178 | -- -- I seem to have added this, I guess it was for replacing all the dallas with my own unit? and thus weapons. 179 | -- managers.menu_scene:_set_character_equipment() 180 | -- end 181 | 182 | 183 | -- Modified to hide additional peers. 184 | function MenuSceneManager:hide_all_lobby_characters() 185 | local num_player_slots = BigLobbyGlobals:num_player_slots() 186 | 187 | for i = 1, num_player_slots do 188 | self:set_lobby_character_visible(i, false, true) 189 | end 190 | end 191 | 192 | 193 | -- Affects the x or y position by an offset for a player model in lobby? 194 | -- TODO: needs to support additional players? 195 | -- function MenuSceneManager:character_screen_position(peer_id) 196 | -- local unit = self._lobby_characters[peer_id] 197 | -- if unit and alive(unit) then 198 | -- local is_me = peer_id == managers.network:session():local_peer():id() 199 | -- local peer_3_x_offset = 0 200 | -- if peer_id == 3 then 201 | -- peer_3_x_offset = is_me and -20 or -40 202 | -- end 203 | -- local peer_y_offset = 0 204 | -- if peer_id == 2 then 205 | -- peer_y_offset = is_me and -3 or 0 206 | -- elseif peer_id == 3 then 207 | -- peer_y_offset = is_me and -7 or 0 208 | -- elseif peer_id == 4 then 209 | -- peer_y_offset = is_me and 5 or 0 210 | -- end 211 | -- local spine_pos = unit:get_object(Idstring("Spine")):position() + Vector3(peer_3_x_offset, 0, -5 + 15 * (peer_id % 4) + peer_y_offset) 212 | -- return self._workspace:world_to_screen(self._camera_object, spine_pos) 213 | -- end 214 | -- end 215 | 216 | -- TODO: Probably safe to delete? 217 | -- TODO: Instances of 4 might benefit from being increased to additional player value? I don't think they're related.. 218 | -- function MenuSceneManager:mouse_moved(o, x, y) 219 | -- if managers.menu_component:input_focus() == true or managers.menu_component:input_focus() == 1 then 220 | -- return false, "arrow" 221 | -- end 222 | -- if self._character_grabbed then 223 | -- self._character_yaw = self._character_yaw + (x - self._character_grabbed_current_x) / 4 224 | -- if self._use_character_pan and self._character_values and self._scene_templates and self._scene_templates[self._current_scene_template] then 225 | -- local new_z = mvector3.z(self._character_values.pos_target) - (y - self._character_grabbed_current_y) / 12 226 | -- local default_z = mvector3.z(self._scene_templates and self._scene_templates[self._current_scene_template].character_pos or self._character_values.pos_current) 227 | -- new_z = math.clamp(new_z, default_z - 20, default_z + 10) 228 | -- mvector3.set_z(self._character_values.pos_target, new_z) 229 | -- end 230 | -- self._character_unit:set_rotation(Rotation(self._character_yaw, self._character_pitch)) 231 | -- self._character_grabbed_current_x = x 232 | -- self._character_grabbed_current_y = y 233 | -- return true, "grab" 234 | -- end 235 | -- if self._item_grabbed then 236 | -- if self._item_unit and alive(self._item_unit.unit) then 237 | -- local diff = (y - self._item_grabbed_current_y) / 4 238 | -- self._item_yaw = (self._item_yaw + (x - self._item_grabbed_current_x) / 4) % 360 239 | -- local yaw_sin = math.sin(self._item_yaw) 240 | -- local yaw_cos = math.cos(self._item_yaw) 241 | -- local treshhold = math.sin(45) 242 | -- if yaw_cos > -treshhold and yaw_cos < treshhold then 243 | -- else 244 | -- self._item_pitch = math.clamp(self._item_pitch + diff * yaw_cos, -30, 30) 245 | -- end 246 | -- if yaw_sin > -treshhold and yaw_sin < treshhold then 247 | -- else 248 | -- self._item_roll = math.clamp(self._item_roll - diff * yaw_sin, -30, 30) 249 | -- end 250 | -- mrotation.set_yaw_pitch_roll(self._item_rot_temp, self._item_yaw, self._item_pitch, self._item_roll) 251 | -- mrotation.set_zero(self._item_rot) 252 | -- mrotation.multiply(self._item_rot, self._camera_object:rotation()) 253 | -- mrotation.multiply(self._item_rot, self._item_rot_temp) 254 | -- mrotation.multiply(self._item_rot, self._item_rot_mod) 255 | -- self._item_unit.unit:set_rotation(self._item_rot) 256 | -- local new_pos = self._item_rot_pos + self._item_offset:rotate_with(self._item_rot) 257 | -- self._item_unit.unit:set_position(new_pos) 258 | -- self._item_unit.unit:set_moving(2) 259 | -- end 260 | -- self._item_grabbed_current_x = x 261 | -- self._item_grabbed_current_y = y 262 | -- return true, "grab" 263 | -- elseif self._item_move_grabbed and self._item_unit and alive(self._item_unit.unit) then 264 | -- local diff_x = (x - self._item_move_grabbed_current_x) / 4 265 | -- local diff_y = (y - self._item_move_grabbed_current_y) / 4 266 | -- local move_v = Vector3(diff_x, 0, -diff_y):rotate_with(self._camera_object:rotation()) 267 | -- mvector3.add(self._item_rot_pos, move_v) 268 | -- local new_pos = self._item_rot_pos + self._item_offset:rotate_with(self._item_rot) 269 | -- self._item_unit.unit:set_position(new_pos) 270 | -- self._item_unit.unit:set_moving(2) 271 | -- self._item_move_grabbed_current_x = x 272 | -- self._item_move_grabbed_current_y = y 273 | -- return true, "grab" 274 | -- end 275 | -- if self._use_item_grab and self._item_grab:inside(x, y) then 276 | -- return true, "hand" 277 | -- end 278 | -- if self._use_character_grab and self._character_grab:inside(x, y) then 279 | -- return true, "hand" 280 | -- end 281 | -- if self._use_character_grab2 and self._character_grab2:inside(x, y) then 282 | -- return true, "hand" 283 | -- end 284 | -- end 285 | 286 | 287 | -- I run the original method, but then need to correct a hardcoded 4 which requires running a bunch 288 | -- of logic again. Should be safe. 289 | 290 | 291 | local orig__MenuSceneManager = {} 292 | orig__MenuSceneManager.set_lobby_character_out_fit = MenuSceneManager.set_lobby_character_out_fit 293 | function MenuSceneManager:set_lobby_character_out_fit(i, outfit_string, rank) 294 | local num_player_slots = BigLobbyGlobals:num_player_slots() 295 | 296 | orig__MenuSceneManager.set_lobby_character_out_fit(self, i, outfit_string, rank) 297 | 298 | 299 | 300 | 301 | -- Original Code -- as far as flow and relevance is concerned at least. 302 | local unit = self._lobby_characters[i] 303 | local is_me = i == managers.network:session():local_peer():id() 304 | local mvec = Vector3() 305 | local math_up = math.UP 306 | local pos = Vector3() 307 | local rot = Rotation() 308 | -- End Original Code -- 309 | 310 | 311 | 312 | 313 | -- Only the hardcoded 4 here is changed to a variable 314 | -- The 4 refers to halfway point of the rotation table, not player count. 315 | mrotation.set_yaw_pitch_roll(rot, self._characters_rotation[(is_me and num_player_slots or 0) + i], 0, 0) 316 | 317 | 318 | 319 | 320 | -- Original Code -- 321 | mvector3.set(pos, self._characters_offset) 322 | if is_me then 323 | mvector3.set_y(pos, mvector3.y(pos) + 100) 324 | end 325 | mvector3.rotate_with(pos, rot) 326 | mvector3.set(mvec, pos) 327 | mvector3.negate(mvec) 328 | mvector3.set_z(mvec, 0) 329 | mrotation.set_look_at(rot, mvec, math_up) 330 | unit:set_position(pos) 331 | unit:set_rotation(rot) 332 | self:set_lobby_character_visible(i, true) 333 | -- End Original Code -- 334 | end 335 | -------------------------------------------------------------------------------- /lua/lib/managers/menu/_missionbriefinggui.lua: -------------------------------------------------------------------------------- 1 | -- Modified to support displaying additional peers loadouts. 2 | -- TODO: This becomes undesirable the larger the player count and requires a reworked UI. 3 | function TeamLoadoutItem:init(panel, text, i) 4 | local num_player_slots = BigLobbyGlobals:num_player_slots() 5 | 6 | -- Only code changed was replacing two hardcoded values of 4 with the variable num_player_slots 7 | TeamLoadoutItem.super.init(self, panel, text, i) 8 | self._player_slots = {} 9 | local quarter_width = self._panel:w() / num_player_slots 10 | local slot_panel 11 | for i = 1, num_player_slots do 12 | local old_right = slot_panel and slot_panel:right() or 0 13 | slot_panel = self._panel:panel({ 14 | x = old_right, 15 | y = 0, 16 | w = quarter_width, 17 | h = self._panel:h(), 18 | valign = "grow" 19 | }) 20 | self._player_slots[i] = {} 21 | self._player_slots[i].panel = slot_panel 22 | self._player_slots[i].outfit = {} 23 | local kit_menu = managers.menu:get_menu("kit_menu") 24 | if kit_menu then 25 | local kit_slot = kit_menu.renderer:get_player_slot_by_peer_id(i) 26 | if kit_slot then 27 | local outfit = kit_slot.outfit 28 | local character = kit_slot.params and kit_slot.params.character 29 | if outfit and character then 30 | self:set_slot_outfit(i, character, outfit) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | 37 | 38 | -- Modified to support additional players. Seems to just reduce font size when needed? 39 | function TeamLoadoutItem:reduce_to_small_font(iteration) 40 | TeamLoadoutItem.super.reduce_to_small_font(self, iteration) 41 | 42 | local num_player_slots = BigLobbyGlobals:num_player_slots() 43 | 44 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 45 | TeamLoadoutItem.super.reduce_to_small_font(self) 46 | for i = 1, num_player_slots do 47 | if self._player_slots and self._player_slots[i].box then 48 | self._player_slots[i].box:create_sides(self._player_slots[i].panel, { 49 | sides = { 50 | 1, 51 | 1, 52 | 1, 53 | 1 54 | } 55 | }) 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lua/lib/managers/mission/_elementareatrigger.lua: -------------------------------------------------------------------------------- 1 | -- Store original functions from the ones we modify 2 | local orig_ElementAreaTrigger = { 3 | project_amount_all = ElementAreaTrigger.project_amount_all 4 | } 5 | 6 | 7 | -- Capping the value to 4 for `criminals`/`local_criminals` instigators to avoid 8 | -- some event comparisons that fail above expected max value of 4. 9 | -- Start of Framing Frame Day 2 where everyone needs to be in the train is said to be affected 10 | function ElementAreaTrigger:project_amount_all() 11 | local i = orig_ElementAreaTrigger.project_amount_all(self) 12 | 13 | -- Ensure i is no higher than 4 for these instigators (May also want to include `ai_teammates`?) 14 | -- This value for these instigators includes both Peers and AI criminals 15 | if self._values.instigator == "criminals" or self._values.instigator == "local_criminals" then 16 | i = i > 4 and 4 or i 17 | end 18 | 19 | return i 20 | end 21 | -------------------------------------------------------------------------------- /lua/lib/managers/mission/_elementfilter.lua: -------------------------------------------------------------------------------- 1 | -- Store the original _check_players function for use later 2 | local orig__ElementFilter = {} 3 | orig__ElementFilter._check_players = ElementFilter._check_players 4 | 5 | 6 | -- This function must be modified to allow for proper objective activation with greater than 4 players 7 | function ElementFilter:_check_players() 8 | 9 | 10 | 11 | 12 | -- Original Code -- 13 | local players = Global.running_simulation and managers.editor:mission_player() 14 | players = players or managers.network:session() and managers.network:session():amount_of_players() 15 | if not players then 16 | return false 17 | end 18 | -- End Original Code -- 19 | 20 | 21 | 22 | 23 | -- Check for >4 players for objective activation fixing 24 | if self._values.player_4 and players >= 4 then 25 | return true 26 | end 27 | 28 | -- Call the original function and return its value if the code above does not return anything 29 | return orig__ElementFilter._check_players(self) 30 | end 31 | -------------------------------------------------------------------------------- /lua/lib/network/base/_basenetworksession.lua: -------------------------------------------------------------------------------- 1 | local orig_BaseNetworkSession = { 2 | check_peer_preferred_character = BaseNetworkSession.check_peer_preferred_character 3 | } 4 | 5 | 6 | -- Modified to support additional peers. 7 | function BaseNetworkSession:on_network_stopped() 8 | local num_player_slots = BigLobbyGlobals:num_player_slots() 9 | 10 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 11 | for k = 1, num_player_slots do 12 | self:on_drop_in_pause_request_received(k, nil, false) 13 | local peer = self:peer(k) 14 | if peer then 15 | peer:unit_delete() 16 | end 17 | end 18 | if self._local_peer then 19 | self:on_drop_in_pause_request_received(self._local_peer:id(), nil, false) 20 | end 21 | 22 | -- Resets host lobby size preference when leaving their lobby 23 | Global.BigLobbyPersist.num_players = nil 24 | -- Update this variable in case the player left from lobby screen (doesn't reload the mod) 25 | BigLobbyGlobals.num_players = BigLobbyGlobals.num_players_settings--Global.BigLobbyPersist.num_players 26 | end 27 | 28 | 29 | -- Modified to support additional peers. 30 | function BaseNetworkSession:_get_peer_outfit_versions_str() 31 | local num_player_slots = BigLobbyGlobals:num_player_slots() 32 | 33 | -- Only code changed was replacing hardcoded 4 with variable num_player_slots 34 | local outfit_versions_str = "" 35 | for peer_id = 1, num_player_slots do 36 | local peer 37 | if peer_id == self._local_peer:id() then 38 | peer = self._local_peer 39 | else 40 | peer = self._peers[peer_id] 41 | end 42 | if peer and peer:waiting_for_player_ready() then 43 | outfit_versions_str = outfit_versions_str .. tostring(peer_id) .. "-" .. peer:outfit_version() .. "." 44 | end 45 | end 46 | return outfit_versions_str 47 | end 48 | 49 | 50 | -- Modified to provide all peers with a character, regardless of free characters. 51 | function BaseNetworkSession:check_peer_preferred_character(preferred_character) 52 | local all_characters = clone(CriminalsManager.character_names()) 53 | local character 54 | 55 | -- Only get a character through the normal method if one is availiable 56 | if #self._peers_all < #all_characters then 57 | -- Call Original Code 58 | character = orig_BaseNetworkSession.check_peer_preferred_character(self, preferred_character) 59 | end 60 | 61 | -- Get a new character if all have already been taken 62 | if character == nil then 63 | -- Allow them to use their preferred character first 64 | local preferreds = string.split(preferred_character, " ") 65 | for _, preferred in ipairs(preferreds) do 66 | if table.contains(all_characters, preferred) then 67 | return preferred 68 | end 69 | end 70 | 71 | -- Fallback to just getting a random character 72 | character = all_characters[math.random(#all_characters)] 73 | end 74 | 75 | return character 76 | end 77 | -------------------------------------------------------------------------------- /lua/lib/network/base/_clientnetworksession.lua: -------------------------------------------------------------------------------- 1 | local orig__ClientNetworkSession = {} 2 | orig__ClientNetworkSession.on_join_request_reply = ClientNetworkSession.on_join_request_reply 3 | 4 | 5 | function ClientNetworkSession:on_join_request_reply(...) 6 | -- Place params in table 7 | local params = {...} 8 | 9 | -- Get params we want based on if the func signature is correct 10 | local reply = params[1] 11 | local sender = #params==18 and params[18] -- last param should now be 19 12 | local num_players = sender and type(params[17]) == "number" and params[17] -- param 16 should now be 18 13 | 14 | -- If the response is `1`(ok), set BigLobby to use host preference or 4 if 15 | -- a regular lobby (num_players param is falsey). 16 | if reply == 1 then 17 | -- Persisting the value across BLT reloads is required, otherwise when you 18 | -- reach the mission briefing screen, it will use your prefs not hosts. 19 | Global.BigLobbyPersist.num_players = num_players or 4 20 | 21 | -- Updates state for current BLT instance 22 | BigLobbyGlobals.num_players = Global.BigLobbyPersist.num_players 23 | end 24 | 25 | -- Assign sender to original param 16 for the original func call to use 26 | if sender then params[17] = params[18] end 27 | 28 | -- Pass params on to the original call 29 | orig__ClientNetworkSession.on_join_request_reply(self, unpack(params)) 30 | end 31 | -------------------------------------------------------------------------------- /lua/lib/network/base/_hostnetworksession.lua: -------------------------------------------------------------------------------- 1 | -- Modified hardcoded value to allow returning available peer id up to the actual limit. 2 | -- Assigns a free Peer ID to new joining peer 3 | function HostNetworkSession:_get_free_client_id() 4 | -- The below loop stops when index(i) reaches this value 5 | local num_player_slots = (BigLobbyGlobals:num_player_slots() + 1) 6 | 7 | -- The player slot to start from(Host is 1 so defaults to 2) 8 | local i = 2 9 | repeat 10 | if not self._peers[i] then 11 | local is_dirty = false 12 | for peer_id, peer in pairs(self._peers) do 13 | if peer:handshakes()[i] then 14 | is_dirty = true 15 | end 16 | end 17 | if not is_dirty then 18 | return i 19 | end 20 | end 21 | i = i + 1 22 | until i == num_player_slots 23 | end 24 | 25 | 26 | -- Modified hardcoded value to prevent disabling the ability to join the server until actual limit is reached. 27 | function HostNetworkSession:chk_server_joinable_state() 28 | local num_player_slots = (BigLobbyGlobals:num_player_slots() - 1) 29 | 30 | 31 | 32 | 33 | -- Original Code -- 34 | for peer_id, peer in pairs(self._peers) do 35 | if peer:force_open_lobby_state() then 36 | print("force-opening lobby for peer", peer_id) 37 | managers.network.matchmake:set_server_joinable(true) 38 | return 39 | end 40 | end 41 | -- End Original Code -- 42 | 43 | 44 | 45 | 46 | -- num_player_slots variable instead of hardcoded 3, prevent disabling join early. 47 | if table.size(self._peers) >= num_player_slots then 48 | managers.network.matchmake:set_server_joinable(false) 49 | return 50 | end 51 | 52 | 53 | 54 | 55 | -- Original Code -- 56 | local game_state_name = game_state_machine:last_queued_state_name() 57 | if BaseNetworkHandler._gamestate_filter.any_end_game[game_state_name] then 58 | managers.network.matchmake:set_server_joinable(false) 59 | return 60 | end 61 | if not self:_get_free_client_id() then 62 | managers.network.matchmake:set_server_joinable(false) 63 | return 64 | end 65 | if not self._state:is_joinable(self._state_data) then 66 | managers.network.matchmake:set_server_joinable(false) 67 | return 68 | end 69 | if NetworkManager.DROPIN_ENABLED then 70 | if BaseNetworkHandler._gamestate_filter.lobby[game_state_name] then 71 | managers.network.matchmake:set_server_joinable(true) 72 | return 73 | elseif managers.groupai and not managers.groupai:state():chk_allow_drop_in() or not Global.game_settings.drop_in_allowed then 74 | managers.network.matchmake:set_server_joinable(false) 75 | return 76 | end 77 | elseif not BaseNetworkHandler._gamestate_filter.lobby[game_state_name] then 78 | managers.network.matchmake:set_server_joinable(false) 79 | return 80 | end 81 | managers.network.matchmake:set_server_joinable(true) 82 | -- End Original Code -- 83 | end 84 | -------------------------------------------------------------------------------- /lua/lib/network/base/_networkmanager.lua: -------------------------------------------------------------------------------- 1 | local orig__NetworkManager = { 2 | start_network = NetworkManager.start_network, 3 | on_peer_added = NetworkManager.on_peer_added 4 | } 5 | 6 | -- Modified to alter the display of player count in lobbies 7 | function NetworkManager:on_peer_added(peer, peer_id) 8 | orig__NetworkManager.on_peer_added(self, peer, peer_id) 9 | 10 | if Network:is_server() then 11 | -- Change the crime.net display to show the % of players relative to the lobby size set by host. 12 | local ratio = managers.network:session():amount_of_players() / BigLobbyGlobals:num_player_slots() 13 | local ratio_to_icon = math.clamp( math.ceil(4 * ratio), 1, 4 ) 14 | 15 | managers.network.matchmake:set_num_players( ratio_to_icon ) 16 | end 17 | end 18 | 19 | 20 | -- Adds two new handlers for network messages to handle the `biglobby__` prefix modifications. 21 | function NetworkManager:start_network() 22 | if not self._started then 23 | self:register_handler("biglobby__connection", BigLobby__ConnectionNetworkHandler) 24 | self:register_handler("biglobby__unit", BigLobby__UnitNetworkHandler) 25 | end 26 | 27 | orig__NetworkManager.start_network(self) 28 | end 29 | -------------------------------------------------------------------------------- /lua/lib/network/base/_networkpeer.lua: -------------------------------------------------------------------------------- 1 | -- Store original functions from the ones we modify 2 | local orig_NetworkPeer = { 3 | send = NetworkPeer.send 4 | } 5 | 6 | 7 | function NetworkPeer:send(func_name, ...) 8 | -- In biglobby mode if the func is matched, call the prefixed version instead 9 | if not BigLobbyGlobals:is_small_lobby() and BigLobbyGlobals.network_handler_funcs[func_name] then 10 | func_name = 'biglobby__' .. func_name 11 | end 12 | 13 | -- Call Original Code 14 | orig_NetworkPeer.send(self, func_name, ...) 15 | end 16 | -------------------------------------------------------------------------------- /lua/lib/network/base/handlers/_connectionnetworkhandler.lua: -------------------------------------------------------------------------------- 1 | -- Extends the ConnectionNetworkHandler class to add our own connection network calls 2 | -- For function modifications use the original function name it will be prefixed later 3 | BigLobby__ConnectionNetworkHandler = BigLobby__ConnectionNetworkHandler or class(ConnectionNetworkHandler) 4 | 5 | 6 | -- Additional `num_players` parameter(set in pdmod xml) for adjusting BigLobby to host lobby size preference 7 | -- params: [ reply_id, my_peer_id, my_character, level_index, difficulty_index, state, server_character, 8 | -- user_id, mission, job_id_index, job_stage, alternative_job_stage, interupt_job_stage_level_index, 9 | -- xuid, auth_ticket, num_players, sender ] 10 | -- The function is effectively the same, varargs(...) are used instead to support 11 | -- passing an extra param when in BigLobby mode. 12 | function BigLobby__ConnectionNetworkHandler:join_request_reply(...) 13 | if not self._verify_in_client_session() then 14 | return 15 | end 16 | 17 | managers.network:session():on_join_request_reply(...) 18 | end 19 | 20 | 21 | -- Will add a prefix of `biglobby__` to all functions our definitions use 22 | -- Required to maintain compatibility with normal lobbies. 23 | BigLobbyGlobals:rename_handler_funcs(BigLobby__ConnectionNetworkHandler) 24 | -------------------------------------------------------------------------------- /lua/lib/network/base/session_states/_hoststateingame.lua: -------------------------------------------------------------------------------- 1 | -- Unfortunately no clean way to modify this bit of code, so I have to include the 2 | -- original code with modified, could cause problems with other mods that would want to touch this function 3 | 4 | function HostStateInGame:on_join_request_received(data, peer_name, peer_account_type_str, peer_account_id, is_invite, client_preferred_character, dlcs, xuid, peer_level, peer_rank, peer_stinger_index, gameversion, join_attempt_identifier, auth_ticket, sender) 5 | 6 | -- Number of players allowed to join the game(excluding the host) 7 | local num_player_slots = BigLobbyGlobals:num_player_slots() - 1 8 | 9 | -- Original Code -- 10 | print("[HostStateInGame:on_join_request_received]", data, peer_name, peer_account_type_str, peer_account_id, client_preferred_character, dlcs, xuid, peer_level, gameversion, join_attempt_identifier, sender:ip_at_index(0)) 11 | 12 | local peer_id = sender:ip_at_index(0) 13 | local my_user_id = data.local_peer:user_id() or "" 14 | local drop_in_name = peer_name 15 | 16 | if peer_account_type_str == "STEAM" then 17 | local temp = peer_name 18 | 19 | if SystemInfo:distribution() == Idstring("STEAM") then 20 | peer_name = managers.network.account:username_by_id(peer_account_id) 21 | elseif SystemInfo:matchmaking() == Idstring("MM_STEAM") then 22 | peer_name = managers.network.matchmake:username_by_id(peer_account_id) 23 | end 24 | 25 | if peer_name == "" or peer_name == "[unknown]" then 26 | peer_name = temp 27 | end 28 | end 29 | 30 | if SocialHubFriends:is_blocked(peer_id) then 31 | self:_send_request_denied(sender, 11, my_user_id) 32 | 33 | return 34 | end 35 | 36 | if managers.network.matchmake:get_lobby_type() == "friend" then 37 | print("[HostStateInGame:on_join_request_received] lobby type friend only, check if friend") 38 | 39 | if SocialHubFriends:is_friend_global(peer_id, peer_account_type_str, peer_account_id) then 40 | print("[HostStateInGame:on_join_request_received] ok we are friend with ", peer_name) 41 | else 42 | print("[HostStateInGame:on_join_request_received] we are NOT friend with ", peer_name, " deny request") 43 | self:_send_request_denied(sender, 12, my_user_id) 44 | 45 | return 46 | end 47 | end 48 | 49 | if self:_has_peer_left_PSN(peer_name) then 50 | print("this CLIENT has left us from PSN, ignore his request", peer_name) 51 | 52 | return 53 | elseif not self:_is_in_server_state() then 54 | self:_send_request_denied(sender, 0, my_user_id) 55 | 56 | return 57 | elseif not NetworkManager.DROPIN_ENABLED or not Global.game_settings.drop_in_allowed then 58 | self:_send_request_denied(sender, 3, my_user_id) 59 | 60 | return 61 | elseif managers.groupai and not managers.groupai:state():chk_allow_drop_in() then 62 | self:_send_request_denied(sender, 0, my_user_id) 63 | 64 | return 65 | elseif self:_is_banned(peer_name, peer_account_id) then 66 | self:_send_request_denied(sender, 9, my_user_id) 67 | 68 | return 69 | elseif peer_level < Global.game_settings.reputation_permission then 70 | self:_send_request_denied(sender, 6, my_user_id) 71 | 72 | return 73 | elseif gameversion ~= -1 and gameversion ~= managers.network.matchmake.GAMEVERSION then 74 | self:_send_request_denied(sender, 7, my_user_id) 75 | 76 | return 77 | elseif data.wants_to_load_level then 78 | self:_send_request_denied(sender, 13, my_user_id) 79 | 80 | return 81 | elseif not managers.network:session():local_peer() then 82 | self:_send_request_denied(sender, 0, my_user_id) 83 | 84 | return 85 | elseif not MenuCallbackHandler:is_modded_client() and not Global.game_settings.allow_modded_players then 86 | local is_modded = false 87 | 88 | if SystemInfo:distribution() == Idstring("STEAM") and peer_account_type_str == "STEAM" then 89 | local user = Steam:user(sender:ip_at_index(0)) 90 | is_modded = user:rich_presence("is_modded") == "1" 91 | end 92 | 93 | if SystemInfo:distribution() == Idstring("EPIC") and peer_account_type_str == "EPIC" then 94 | -- Nothing 95 | end 96 | 97 | if is_modded then 98 | self:_send_request_denied(sender, 10, my_user_id) 99 | 100 | return 101 | end 102 | end 103 | 104 | local old_peer = data.session:chk_peer_already_in(sender) 105 | 106 | if old_peer then 107 | if join_attempt_identifier ~= old_peer:join_attempt_identifier() then 108 | self:_send_request_denied(sender, 14, my_user_id) 109 | data.session:remove_peer(old_peer, old_peer:id(), "lost") 110 | end 111 | 112 | return 113 | end 114 | -- End Original Code -- 115 | 116 | -- num_player_slots variable instead of hardcoded 3, removes enforced limit. 117 | if num_player_slots <= table.size(data.peers) then 118 | print("server is full") 119 | self:_send_request_denied(sender, 5, my_user_id) 120 | 121 | return 122 | end 123 | 124 | -- Original Code -- 125 | local character = managers.network:session():check_peer_preferred_character(client_preferred_character) 126 | local xnaddr = "" 127 | 128 | if SystemInfo:platform() == Idstring("X360") or SystemInfo:platform() == Idstring("XB1") then 129 | xnaddr = managers.network.matchmake:external_address(sender) 130 | end 131 | 132 | local new_peer_id, new_peer = nil 133 | new_peer_id, new_peer = data.session:add_peer(peer_name, nil, false, false, false, nil, character, sender:ip_at_index(0), peer_account_type_str, peer_account_id, xuid, xnaddr) 134 | 135 | if not new_peer_id then 136 | print("there was no clean peer_id") 137 | self:_send_request_denied(sender, 0, my_user_id) 138 | 139 | return 140 | end 141 | 142 | new_peer:set_dlcs(dlcs) 143 | new_peer:set_xuid(xuid) 144 | new_peer:set_name_drop_in(drop_in_name) 145 | new_peer:set_join_attempt_identifier(join_attempt_identifier) 146 | 147 | local new_peer_rpc = nil 148 | 149 | if sender:protocol_at_index(0) == "TCP_IP" then 150 | new_peer_rpc = managers.network:session():resolve_new_peer_rpc(new_peer, sender) 151 | else 152 | new_peer_rpc = sender 153 | end 154 | 155 | new_peer:set_rpc(new_peer_rpc) 156 | new_peer:set_ip_verified(true) 157 | Network:add_co_client(new_peer_rpc) 158 | 159 | if not new_peer:begin_ticket_session(auth_ticket) then 160 | self:_send_request_denied(sender, 8, my_user_id) 161 | data.session:remove_peer(new_peer, new_peer:id(), "auth_fail") 162 | 163 | return 164 | end 165 | 166 | local ticket = new_peer:create_ticket(data.local_peer:account_id()) 167 | local level_index = tweak_data.levels:get_index_from_level_id(Global.game_settings.level_id) 168 | local difficulty_index = tweak_data:difficulty_to_index(Global.game_settings.difficulty) 169 | local job_id_index = 0 170 | local job_stage = 0 171 | local alternative_job_stage = 0 172 | local interupt_job_stage_level_index = 0 173 | 174 | if managers.job:has_active_job() then 175 | job_id_index = tweak_data.narrative:get_index_from_job_id(managers.job:current_job_id()) 176 | job_stage = managers.job:current_stage() 177 | alternative_job_stage = managers.job:alternative_stage() or 0 178 | local interupt_stage_level = managers.job:interupt_stage() 179 | interupt_job_stage_level_index = interupt_stage_level and tweak_data.levels:get_index_from_level_id(interupt_stage_level) or 0 180 | end 181 | 182 | local server_xuid = (SystemInfo:platform() == Idstring("X360") or SystemInfo:platform() == Idstring("XB1")) and managers.network.account:player_id() or "" 183 | -- End Original Code -- 184 | 185 | -- Appears orginally, but is modified to include the num_player_slots parameter 186 | local params = { 187 | 1, 188 | new_peer_id, 189 | character, 190 | level_index, 191 | difficulty_index, 192 | Global.game_settings.one_down, 193 | self.STATE_INDEX, 194 | data.local_peer:character(), 195 | my_user_id, 196 | Global.game_settings.mission, 197 | job_id_index, 198 | job_stage, 199 | alternative_job_stage, 200 | interupt_job_stage_level_index, 201 | server_xuid, 202 | ticket, 203 | BigLobbyGlobals:num_player_slots() 204 | } 205 | 206 | new_peer:send("join_request_reply", unpack(params)) 207 | -- Original Code -- 208 | new_peer:send("set_loading_state", false, data.session:load_counter()) 209 | 210 | if SystemInfo:platform() == Idstring("X360") or SystemInfo:platform() == Idstring("XB1") then 211 | new_peer:send("request_player_name_reply", managers.network:session():local_peer():name()) 212 | end 213 | 214 | managers.vote:sync_server_kick_option(new_peer) 215 | data.session:send_ok_to_load_level() 216 | self:on_handshake_confirmation(data, new_peer, 1) 217 | new_peer:set_rank(peer_rank) 218 | new_peer:set_join_stinger_index(peer_stinger_index) 219 | 220 | self._new_peers[new_peer_id] = true 221 | -- End Original Code -- 222 | end -------------------------------------------------------------------------------- /lua/lib/network/base/session_states/_hoststateinlobby.lua: -------------------------------------------------------------------------------- 1 | -- Unfortunately no clean way to modify this bit of code, so I have to include the 2 | -- original code with modified, could cause problems with other mods that would want to touch this function 3 | function HostStateInLobby:on_join_request_received(data, peer_name, peer_account_type_str, peer_account_id, is_invite, client_preferred_character, dlcs, xuid, peer_level, peer_rank, peer_stinger_index, gameversion, join_attempt_identifier, auth_ticket, sender) 4 | print("[HostStateInLobby:on_join_request_received]", data, peer_name, peer_account_type_str, peer_account_id, is_invite, client_preferred_character, dlcs, xuid, peer_level, peer_rank, peer_stinger_index, gameversion, join_attempt_identifier, auth_ticket, sender:ip_at_index(0)) 5 | 6 | -- Number of players allowed to join the game(excluding the host) 7 | local num_player_slots = BigLobbyGlobals:num_player_slots() - 1 8 | 9 | -- Original Code -- 10 | local peer_id = sender:ip_at_index(0) 11 | local drop_in_name = peer_name 12 | 13 | if peer_account_type_str == "STEAM" then 14 | local temp = peer_name 15 | 16 | if SystemInfo:distribution() == Idstring("STEAM") then 17 | peer_name = managers.network.account:username_by_id(peer_account_id) 18 | elseif SystemInfo:matchmaking() == Idstring("MM_STEAM") then 19 | peer_name = managers.network.matchmake:username_by_id(peer_account_id) 20 | end 21 | 22 | if peer_name == "" or peer_name == "[unknown]" then 23 | peer_name = temp 24 | end 25 | end 26 | 27 | local my_user_id = data.local_peer:user_id() or "" 28 | 29 | if SocialHubFriends:is_blocked(peer_id) then 30 | self:_send_request_denied(sender, 11, my_user_id) 31 | 32 | return 33 | end 34 | 35 | if not is_invite and managers.network.matchmake:get_lobby_type() == "friend" then 36 | print("[HostStateInLobby:on_join_request_received] lobby type friend only, check if friend") 37 | 38 | if SocialHubFriends:is_friend_global(peer_id, peer_account_type_str, peer_account_id) then 39 | print("[HostStateInLobby:on_join_request_received] ok we are friend with ", peer_name) 40 | else 41 | print("[HostStateInLobby:on_join_request_received] we are NOT friend with ", peer_name, " deny request") 42 | self:_send_request_denied(sender, 12, my_user_id) 43 | 44 | return 45 | end 46 | end 47 | 48 | if self:_has_peer_left_PSN(peer_name) then 49 | print("this CLIENT has left us from PSN, ignore his request", peer_name) 50 | 51 | return 52 | end 53 | 54 | if self:_is_banned(peer_name, peer_account_id) then 55 | self:_send_request_denied(sender, 9, my_user_id) 56 | 57 | return 58 | end 59 | 60 | if not MenuCallbackHandler:is_modded_client() and not Global.game_settings.allow_modded_players then 61 | local is_modded = false 62 | 63 | if SystemInfo:distribution() == Idstring("STEAM") and peer_account_type_str == "STEAM" then 64 | local user = Steam:user(sender:ip_at_index(0)) 65 | is_modded = user:rich_presence("is_modded") == "1" 66 | end 67 | 68 | if SystemInfo:distribution() == Idstring("EPIC") and peer_account_type_str == "EPIC" then 69 | -- Nothing 70 | end 71 | 72 | if is_modded then 73 | self:_send_request_denied(sender, 10, my_user_id) 74 | 75 | return 76 | end 77 | end 78 | 79 | if peer_level < Global.game_settings.reputation_permission then 80 | self:_send_request_denied(sender, 6, my_user_id) 81 | 82 | return 83 | end 84 | 85 | if gameversion ~= -1 and gameversion ~= managers.network.matchmake.GAMEVERSION then 86 | self:_send_request_denied(sender, 7, my_user_id) 87 | 88 | return 89 | end 90 | 91 | if data.wants_to_load_level then 92 | self:_send_request_denied(sender, 13, my_user_id) 93 | 94 | return 95 | end 96 | 97 | if not managers.network:session():local_peer() then 98 | self:_send_request_denied(sender, 0, my_user_id) 99 | 100 | return 101 | end 102 | 103 | local old_peer = data.session:chk_peer_already_in(sender) 104 | 105 | if old_peer then 106 | if join_attempt_identifier ~= old_peer:join_attempt_identifier() then 107 | self:_send_request_denied(sender, 14, my_user_id) 108 | data.session:remove_peer(old_peer, old_peer:id(), "lost") 109 | end 110 | 111 | return 112 | end 113 | -- End Original Code -- 114 | 115 | -- num_player_slots variable instead of hardcoded 3, removes enforced limit. 116 | if table.size(data.peers) >= num_player_slots then 117 | print("server is full") 118 | self:_send_request_denied(sender, 5, my_user_id) 119 | return 120 | end 121 | 122 | -- Original Code -- 123 | print("[HostStateInLobby:on_join_request_received] new peer accepted", peer_name) 124 | 125 | local character = managers.network:session():check_peer_preferred_character(client_preferred_character) 126 | local xnaddr = "" 127 | 128 | if SystemInfo:platform() == Idstring("X360") or SystemInfo:platform() == Idstring("XB1") then 129 | xnaddr = managers.network.matchmake:external_address(sender) 130 | end 131 | 132 | local new_peer_id, new_peer = nil 133 | new_peer_id, new_peer = data.session:add_peer(peer_name, nil, true, false, false, nil, character, sender:ip_at_index(0), peer_account_type_str, peer_account_id, xuid, xnaddr) 134 | 135 | if not new_peer_id then 136 | print("there was no clean peer_id") 137 | self:_send_request_denied(sender, 0, my_user_id) 138 | 139 | return 140 | end 141 | 142 | new_peer:set_dlcs(dlcs) 143 | new_peer:set_xuid(xuid) 144 | new_peer:set_name_drop_in(drop_in_name) 145 | new_peer:set_join_attempt_identifier(join_attempt_identifier) 146 | 147 | local new_peer_rpc = nil 148 | 149 | if sender:protocol_at_index(0) == "TCP_IP" then 150 | new_peer_rpc = managers.network:session():resolve_new_peer_rpc(new_peer, sender) 151 | else 152 | new_peer_rpc = sender 153 | end 154 | 155 | new_peer:set_rpc(new_peer_rpc) 156 | new_peer:set_ip_verified(true) 157 | Network:add_client(new_peer:rpc()) 158 | 159 | if not new_peer:begin_ticket_session(auth_ticket) then 160 | self:_send_request_denied(sender, 8, my_user_id) 161 | data.session:remove_peer(new_peer, new_peer:id(), "auth_fail") 162 | 163 | return 164 | end 165 | 166 | local ticket = new_peer:create_ticket(data.local_peer:account_id()) 167 | 168 | new_peer:set_entering_lobby(true) 169 | 170 | local level_index = tweak_data.levels:get_index_from_level_id(Global.game_settings.level_id) 171 | local difficulty_index = tweak_data:difficulty_to_index(Global.game_settings.difficulty) 172 | local job_id_index = 0 173 | local job_stage = 0 174 | local alternative_job_stage = 0 175 | local interupt_job_stage_level_index = 0 176 | 177 | if managers.job:has_active_job() then 178 | job_id_index = tweak_data.narrative:get_index_from_job_id(managers.job:current_job_id()) 179 | job_stage = managers.job:current_stage() 180 | alternative_job_stage = managers.job:alternative_stage() or 0 181 | local interupt_stage_level = managers.job:interupt_stage() 182 | interupt_job_stage_level_index = interupt_stage_level and tweak_data.levels:get_index_from_level_id(interupt_stage_level) or 0 183 | end 184 | -- End Original Code -- 185 | 186 | local server_xuid = (SystemInfo:platform() == Idstring("X360") or SystemInfo:platform() == Idstring("XB1")) and managers.network.account:player_id() or "" 187 | 188 | -- Appears orginally, but is modified to include the num_player_slots parameter 189 | local params = { 190 | 1, 191 | new_peer_id, 192 | character, 193 | level_index, 194 | difficulty_index, 195 | Global.game_settings.one_down, 196 | 1, 197 | data.local_peer:character(), 198 | my_user_id, 199 | Global.game_settings.mission, 200 | job_id_index, 201 | job_stage, 202 | alternative_job_stage, 203 | interupt_job_stage_level_index, 204 | server_xuid, 205 | ticket, 206 | BigLobbyGlobals:num_player_slots() 207 | } 208 | 209 | -- Original Code -- 210 | new_peer:send("join_request_reply", unpack(params)) 211 | new_peer:send("set_loading_state", false, data.session:load_counter()) 212 | 213 | if SystemInfo:platform() == Idstring("X360") or SystemInfo:platform() == Idstring("XB1") then 214 | new_peer:send("request_player_name_reply", managers.network:session():local_peer():name()) 215 | end 216 | 217 | managers.vote:sync_server_kick_option(new_peer) 218 | self:_introduce_new_peer_to_old_peers(data, new_peer, false, peer_name, new_peer:character(), "remove", new_peer:xuid(), new_peer:xnaddr()) 219 | self:_introduce_old_peers_to_new_peer(data, new_peer) 220 | self:on_handshake_confirmation(data, new_peer, 1) 221 | managers.network:session():local_peer():sync_lobby_data(new_peer) 222 | managers.menu:play_join_stinger_by_index(peer_stinger_index) 223 | managers.network:session():send_to_peers_except(new_peer_id, "peer_joined_sound", peer_stinger_index) 224 | managers.crime_spree:on_peer_finished_loading(new_peer) 225 | -- End Original Code -- 226 | end -------------------------------------------------------------------------------- /lua/lib/network/handlers/_unitnetworkhandler.lua: -------------------------------------------------------------------------------- 1 | -- Extends the UnitNetworkHandler class to add our own unit network calls 2 | -- For function modifications use the original function name it will be prefixed later 3 | BigLobby__UnitNetworkHandler = BigLobby__UnitNetworkHandler or class(UnitNetworkHandler) 4 | 5 | -- Will add a prefix of `biglobby__` to all functions our definitions use 6 | -- Required to maintain compatibility with normal lobbies. 7 | BigLobbyGlobals:rename_handler_funcs(BigLobby__UnitNetworkHandler) 8 | -------------------------------------------------------------------------------- /lua/lib/network/matchmaking/_networkmatchmakingepic.lua: -------------------------------------------------------------------------------- 1 | -- Used in `NetworkMatchMakingEPIC:create_lobby(settings)` when calling `Steam:create_lobby(f, NetworkMatchMakingEPIC.OPEN_SLOTS, "invisible")` 2 | -- If not adjusted to new player limit will prevent Steam allowing a connection, failing it. 3 | NetworkMatchMakingEPIC.OPEN_SLOTS = BigLobbyGlobals:num_player_slots() 4 | 5 | -- Prevent non BigLobby players from finding/joining this game. 6 | if not BigLobbyGlobals:is_small_lobby() then 7 | -- Version is included in search key now, not sure of any benefit changing game version? 8 | -- Assign a gameversion, to prevent outdated clients from connecting 9 | --NetworkMatchMakingEPIC.GAMEVERSION = BigLobbyGlobals:gameversion() 10 | 11 | -- Use the existing search key and concatenate `:biglobby-{{version}}` to it 12 | -- so other mods can use this filter/isolation method. If search key has not been 13 | -- modified prior it's value is likely `nil`. 14 | local bl_key = ":biglobby-" .. BigLobbyGlobals:version() 15 | local current_key = NetworkMatchMakingEPIC._BUILD_SEARCH_INTEREST_KEY 16 | NetworkMatchMakingEPIC._BUILD_SEARCH_INTEREST_KEY = current_key and current_key .. bl_key or bl_key 17 | end 18 | -------------------------------------------------------------------------------- /lua/lib/network/matchmaking/_networkmatchmakingsteam.lua: -------------------------------------------------------------------------------- 1 | -- Used in `NetworkMatchMakingSTEAM:create_lobby(settings)` when calling `Steam:create_lobby(f, NetworkMatchMakingSTEAM.OPEN_SLOTS, "invisible")` 2 | -- If not adjusted to new player limit will prevent Steam allowing a connection, failing it. 3 | NetworkMatchMakingSTEAM.OPEN_SLOTS = BigLobbyGlobals:num_player_slots() 4 | 5 | -- Prevent non BigLobby players from finding/joining this game. 6 | if not BigLobbyGlobals:is_small_lobby() then 7 | -- Version is included in search key now, not sure of any benefit changing game version? 8 | -- Assign a gameversion, to prevent outdated clients from connecting 9 | --NetworkMatchMakingSTEAM.GAMEVERSION = BigLobbyGlobals:gameversion() 10 | 11 | -- Use the existing search key and concatenate `:biglobby-{{version}}` to it 12 | -- so other mods can use this filter/isolation method. If search key has not been 13 | -- modified prior it's value is likely `nil`. 14 | local bl_key = ":biglobby-" .. BigLobbyGlobals:version() 15 | local current_key = NetworkMatchMakingSTEAM._BUILD_SEARCH_INTEREST_KEY 16 | NetworkMatchMakingSTEAM._BUILD_SEARCH_INTEREST_KEY = current_key and current_key .. bl_key or bl_key 17 | end 18 | -------------------------------------------------------------------------------- /lua/lib/tweak_data/_moneytweakdata.lua: -------------------------------------------------------------------------------- 1 | -- moneytweakdata.lua - MoneyTweakData:init, fixes infinite money loss at the results/exp screen. 2 | -- Must be in this seperate hook as the main init function is called on the loading of the tweak_data lua file, so this hook will be added after the main call of it has already taken place 3 | -- This method prevents issues with other mods that may also touch this function 4 | -- Gets called after every call of the init function on the MoneyTweakData class table, init is called in tweak_data after it has already been initialized for a particular difficulty 5 | Hooks:PostHook(MoneyTweakData, "init", "BigLobbyModifyMoneyTweak", function(self, tweak_data) 6 | self.alive_humans_multiplier = self._create_value_table(1, self.alive_players_max, BigLobbyGlobals:num_player_slots(), false, 1) 7 | end) 8 | -------------------------------------------------------------------------------- /lua/lib/tweak_data/_tweakdata.lua: -------------------------------------------------------------------------------- 1 | -- Updates colours to support UI elements for additional peers while avoiding those 2 | -- that affect gameplay such as orange and yellow 3 | 4 | local num_player_slots = BigLobbyGlobals:num_player_slots() 5 | 6 | -- AI assigned colour: 7 | local team_ai = Vector3(0.2, 0.8, 1) 8 | 9 | -- Fixed colours 10 | tweak_data.peer_vector_colors = {} 11 | 12 | -- This doesn't appear to be referenced, not sure why it still exists in codebase 13 | tweak_data.peer_colors = {} 14 | 15 | -- Make sure we have enough colours to support the number of player slots 16 | local steps = 360 / num_player_slots 17 | for i = 0, num_player_slots - 1 do 18 | -- RGB channels 19 | local hue = i * steps 20 | local col = Vector3(_G.HUSL.huslp_to_rgb(hue, 100, 60)) 21 | 22 | table.insert(tweak_data.peer_vector_colors, col) 23 | table.insert(tweak_data.peer_colors, tostring("team_colour_") .. i) 24 | end 25 | 26 | math.randomseed(42) 27 | table.shuffle(tweak_data.peer_vector_colors) 28 | math.randomseed(os.time()) 29 | 30 | -- AI labels will use the last value so we add it at the end 31 | table.insert(tweak_data.peer_vector_colors, team_ai) 32 | table.insert(tweak_data.peer_colors, "mrai") 33 | 34 | -- Dynamically added now based on peer_vector_colors table 35 | tweak_data.chat_colors = {} 36 | for i = 1, #tweak_data.peer_vector_colors do 37 | tweak_data.chat_colors[i] = Color(tweak_data.peer_vector_colors[i]:unpack()) 38 | end 39 | 40 | -- Use the same colours created for chat for preplanning 41 | tweak_data.preplanning_peer_colors = {} 42 | for i = 1, #tweak_data.peer_vector_colors do 43 | tweak_data.preplanning_peer_colors[i] = Color(tweak_data.peer_vector_colors[i]:unpack()) 44 | end 45 | 46 | tweak_data.team_ai.stop_action.distance = 10000000000000000 47 | -------------------------------------------------------------------------------- /mod.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Biglobby3", 3 | "description": "Increase the player limit of a lobby from the original 4.", 4 | "author": "Polarathene, test1, Znix, Restoration Mod team", 5 | "contact": "no@support.com", 6 | "version": "3.27.6", 7 | "blt_version": 2, 8 | "priority" : 400, 9 | "updates": [ 10 | { 11 | "identifier": "biglobby3", 12 | "host": { 13 | "meta": "https://raw.githubusercontent.com/Crackdown-PD2/BigLobby3/master/updates/meta_biglobby3.json" 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /supermod.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | <:include src="hooks.xml" /> 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /updates/biglobby3.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Crackdown-PD2/BigLobby3/29ea99d6068a0610ce70bc3f402a4bd10774d118/updates/biglobby3.zip -------------------------------------------------------------------------------- /updates/meta_biglobby3.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ident": "biglobby3", 4 | "download_url": "https://github.com/Crackdown-PD2/BigLobby3/raw/master/updates/biglobby3.zip", 5 | "patchnotes_url": "https://github.com/Crackdown-PD2/BigLobby3/wiki/Big-Lobby-3-Changelog", 6 | "version": "3.27.6" 7 | } 8 | ] --------------------------------------------------------------------------------