diff --git a/mods/Acids Bot Placement System/BepInEx/plugins/acidphantasm-botplacementsystem/LICENSE b/mods/Acids Bot Placement System/BepInEx/plugins/acidphantasm-botplacementsystem/LICENSE new file mode 100644 index 0000000..9badf7a --- /dev/null +++ b/mods/Acids Bot Placement System/BepInEx/plugins/acidphantasm-botplacementsystem/LICENSE @@ -0,0 +1,402 @@ +Attribution-NonCommercial-NoDerivatives 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 +International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-NoDerivatives 4.0 International Public +License ("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + c. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + d. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + e. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + f. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + g. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + h. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part, for NonCommercial purposes only; and + + b. produce and reproduce, but not Share, Adapted Material + for NonCommercial purposes only. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material, You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + For the avoidance of doubt, You do not have permission under + this Public License to Share Adapted Material. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database for NonCommercial purposes + only and provided You do not Share Adapted Material; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/mods/Acids Bot Placement System/BepInEx/plugins/acidphantasm-botplacementsystem/acidphantasm-botplacementsystem.dll b/mods/Acids Bot Placement System/BepInEx/plugins/acidphantasm-botplacementsystem/acidphantasm-botplacementsystem.dll new file mode 100644 index 0000000..be1fcda Binary files /dev/null and b/mods/Acids Bot Placement System/BepInEx/plugins/acidphantasm-botplacementsystem/acidphantasm-botplacementsystem.dll differ diff --git a/mods/Waypoints_backup2/meta.ini b/mods/Acids Bot Placement System/meta.ini similarity index 72% rename from mods/Waypoints_backup2/meta.ini rename to mods/Acids Bot Placement System/meta.ini index 224fda0..a803d0f 100644 --- a/mods/Waypoints_backup2/meta.ini +++ b/mods/Acids Bot Placement System/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.17.0 +version=d2025.5.13.0 newestVersion= -category="1,2" +category="-1," nexusFileStatus=1 -installationFile=DrakiaXYZ-Waypoints-1.6.1.7z +installationFile=acidphantasm-botplacementsystem.zip repository=Nexus ignoredVersion= comments= @@ -15,7 +15,7 @@ url= hasCustomURL=false lastNexusQuery= lastNexusUpdate= -nexusLastModified=2024-12-16T06:41:17Z +nexusLastModified=2025-05-13T08:46:56Z nexusCategory=0 converted=false validated=false diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/ABPSConfig.exe b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/ABPSConfig.exe new file mode 100644 index 0000000..ee29aeb --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/ABPSConfig.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09429dea97413703b6e7196974d575d3329e35792bdd84c2af64793ba79aaa40 +size 16183933 diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/LICENSE b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/LICENSE new file mode 100644 index 0000000..9badf7a --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/LICENSE @@ -0,0 +1,402 @@ +Attribution-NonCommercial-NoDerivatives 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 +International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-NonCommercial-NoDerivatives 4.0 International Public +License ("Public License"). To the extent this Public License may be +interpreted as a contract, You are granted the Licensed Rights in +consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the +Licensor receives from making the Licensed Material available under +these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + c. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + d. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + e. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + f. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + g. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + h. NonCommercial means not primarily intended for or directed towards + commercial advantage or monetary compensation. For purposes of + this Public License, the exchange of the Licensed Material for + other material subject to Copyright and Similar Rights by digital + file-sharing or similar means is NonCommercial provided there is + no payment of monetary compensation in connection with the + exchange. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part, for NonCommercial purposes only; and + + b. produce and reproduce, but not Share, Adapted Material + for NonCommercial purposes only. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties, including when + the Licensed Material is used other than for NonCommercial + purposes. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material, You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + For the avoidance of doubt, You do not have permission under + this Public License to Share Adapted Material. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database for NonCommercial purposes + only and provided You do not Share Adapted Material; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/config/config.json b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/config/config.json new file mode 100644 index 0000000..239119c --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/config/config.json @@ -0,0 +1,640 @@ +{ + "pmcDifficulty": { + "easy": 10, + "normal": 50, + "hard": 30, + "impossible": 10 + }, + "pmcConfig": { + "startingPMCs": { + "enable": true, + "ignoreMaxBotCaps": true, + "groupChance": 25, + "maxGroupSize": 3, + "maxGroupCount": 4, + "mapLimits": { + "bigmap": { + "min": 8, + "max": 10 + }, + "factory4_day": { + "min": 5, + "max": 7 + }, + "factory4_night": { + "min": 5, + "max": 7 + }, + "interchange": { + "min": 9, + "max": 13 + }, + "laboratory": { + "min": 7, + "max": 9 + }, + "lighthouse": { + "min": 7, + "max": 10 + }, + "rezervbase": { + "min": 8, + "max": 10 + }, + "sandbox": { + "min": 8, + "max": 11 + }, + "sandbox_high": { + "min": 8, + "max": 11 + }, + "shoreline": { + "min": 9, + "max": 13 + }, + "tarkovstreets": { + "min": 7, + "max": 10 + }, + "woods": { + "min": 9, + "max": 13 + } + } + }, + "waves": { + "enable": false, + "ignoreMaxBotCaps": false, + "groupChance": 10, + "maxGroupSize": 2, + "maxGroupCount": 3, + "maxBotsPerWave": 5, + "delayBeforeFirstWave": 500, + "secondsBetweenWaves": 360, + "stopWavesBeforeEndOfRaidLimit": 300 + } + }, + "scavConfig": { + "startingScavs": { + "enable": true, + "maxBotSpawns": { + "bigmap": 5, + "factory4_day": 3, + "factory4_night": 3, + "interchange": 5, + "laboratory": 0, + "lighthouse": 5, + "rezervbase": 5, + "sandbox": 5, + "sandbox_high": 5, + "shoreline": 5, + "tarkovstreets": 5, + "woods": 5 + }, + "maxBotsPerZone": 1, + "startingMarksman": true + }, + "waves": { + "enable": true, + "enableCustomFactory": true, + "startSpawns": 60, + "stopSpawns": 600, + "activeTimeMin": 180, + "activeTimeMax": 240, + "quietTimeMin": 120, + "quietTimeMax": 180, + "checkToSpawnTimer": 15, + "pendingBotsToTrigger": 3 + } + }, + "bossDifficulty": { + "easy": 0, + "normal": 60, + "hard": 30, + "impossible": 10 + }, + "bossConfig": { + "bossKnight": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 30, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 30, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 30, + "tarkovstreets": 0, + "woods": 30 + }, + "bossZone": { + "bigmap": "ZoneScavBase", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "Zone_TreatmentContainers,Zone_Chalet", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "ZoneMeteoStation", + "tarkovstreets": "", + "woods": "ZoneScavBase2" + } + }, + "bossBully": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 30, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "ZoneDormitory,ZoneGasStation,ZoneScavBase", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "bossTagilla": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 30, + "factory4_night": 30, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "BotZone", + "factory4_night": "BotZone", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "bossKilla": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 30, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "ZoneCenterBot,ZoneCenter,ZoneOLI,ZoneIDEA,ZoneGoshan", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "bossZryachiy": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 100, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "Zone_Island", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "bossGluhar": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 30, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "ZoneRailStrorage,ZonePTOR2,ZoneBarrack,ZoneSubStorage", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "bossSanitar": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 30, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "ZoneGreenHouses,ZoneSanatorium1,ZoneSanatorium2,ZonePort", + "tarkovstreets": "", + "woods": "" + } + }, + "bossKolontay": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 30, + "shoreline": 0, + "tarkovstreets": 30, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "ZoneSandbox", + "shoreline": "", + "tarkovstreets": "ZoneClimova,ZoneMvd", + "woods": "" + } + }, + "bossBoar": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 30, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "ZoneCarShowroom", + "woods": "" + } + }, + "bossKojaniy": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 30 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "ZoneWoodCutter" + } + }, + "bossPartisan": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 30, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 30, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 30, + "tarkovstreets": 0, + "woods": 30 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "sectantPriest": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 15, + "factory4_day": 0, + "factory4_night": 20, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 44, + "shoreline": 15, + "tarkovstreets": 0, + "woods": 15 + }, + "bossZone": { + "bigmap": "ZoneScavBase", + "factory4_day": "", + "factory4_night": "BotZone", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "ZoneSandbox", + "shoreline": "ZoneSanatorium1,ZoneSanatorium2,ZoneForestSpawn", + "tarkovstreets": "", + "woods": "ZoneMiniHouse,ZoneBrokenVill" + } + }, + "arenaFighterEvent": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 5, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 5 + }, + "bossZone": { + "bigmap": "ZoneFactoryCenter,ZoneScavBase", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "ZoneMiniHouse,ZoneClearVill,ZoneRoad,ZoneBrokenVill,ZoneScavBase2" + } + }, + "pmcBot": { + "enable": true, + "addExtraSpawns": true, + "disableVanillaSpawns": false, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 75, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "BotZoneBasement,BotZoneFloor1,BotZoneFloor2", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "exUsec": { + "enable": true, + "addExtraSpawns": false, + "disableVanillaSpawns": false, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + }, + "gifter": { + "enable": true, + "time": -1, + "spawnChance": { + "bigmap": 0, + "factory4_day": 0, + "factory4_night": 0, + "interchange": 0, + "laboratory": 0, + "lighthouse": 0, + "rezervbase": 0, + "sandbox": 0, + "sandbox_high": 0, + "shoreline": 0, + "tarkovstreets": 0, + "woods": 0 + }, + "bossZone": { + "bigmap": "", + "factory4_day": "", + "factory4_night": "", + "interchange": "", + "laboratory": "", + "lighthouse": "", + "rezervbase": "", + "sandbox": "", + "sandbox_high": "", + "shoreline": "", + "tarkovstreets": "", + "woods": "" + } + } + }, + "configAppSettings": { + "showUndo": true, + "showDefault": false, + "disableAnimations": false + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/package.json b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/package.json new file mode 100644 index 0000000..82ede1e --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/package.json @@ -0,0 +1,33 @@ +{ + "name": "Acids Bot Placement System", + "version": "1.1.1", + "sptVersion": "~3.11", + "loadBefore": [ + "RaidOverhaul", + "WTT-RogueJustice" + ], + "loadAfter": [], + "incompatibilities": [], + "isBundleMod": false, + "main": "src/mod.js", + "scripts": { + "setup": "npm i", + "build": "node ./build.mjs", + "buildinfo": "node ./build.mjs --verbose" + }, + "devDependencies": { + "@types/node": "20.11", + "@typescript-eslint/eslint-plugin": "7.2", + "@typescript-eslint/parser": "7.2", + "archiver": "^6.0", + "eslint": "8.57", + "fs-extra": "11.2", + "ignore": "^5.2", + "tsyringe": "4.8.0", + "typescript": "5.8", + "winston": "3.12" + }, + "author": "acidphantasm", + "contributors": [], + "license": "BY-NC-ND 4.0" +} diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/BossSpawnControl.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/BossSpawnControl.ts new file mode 100644 index 0000000..833c3c3 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/BossSpawnControl.ts @@ -0,0 +1,187 @@ +import { injectable, inject } from "tsyringe"; +import { IBossLocationSpawn } from "@spt/models/eft/common/ILocationBase"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; +import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; +import { RandomUtil } from "@spt/utils/RandomUtil"; +import { ModConfig } from "../Globals/ModConfig"; + +// Default Boss Data +import { + arenaFighterEventData, + bossBoarData, + bossBullyData, + bossGluharData, + bossKillaData, + bossKnightData, + bossKojaniyData, + bossKolontayData, + bossPartisanData, + bossSanitarData, + bossTagillaData, + bossZryachiyData, + exUsecData, + gifterData, + pmcBotData, + pmcBotReserveData, + pmcBotLaboratoryData, + sectantPriestData +} from "../Defaults/Bosses" + +@injectable() +export class BossSpawnControl +{ + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("RandomUtil") protected randomUtil: RandomUtil, + @inject("PrimaryCloner") protected cloner: ICloner, + @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper + ) + {} + + public getCustomMapData(location: string, escapeTimeLimit: number): IBossLocationSpawn[] + { + return this.getConfigValueForLocation(location, escapeTimeLimit) + } + + private getConfigValueForLocation(location: string, escapeTimeLimit: number): IBossLocationSpawn[] + { + const bossesForMap: IBossLocationSpawn[] = []; + for (const boss in ModConfig.config.bossConfig) + { + const bossDefaultData = this.cloner.clone(this.getDefaultValuesForBoss(boss, location)); + const bossConfigData = ModConfig.config.bossConfig[boss]; + const difficultyWeights = ModConfig.config.bossDifficulty; + + if (!bossConfigData.enable) continue; + + if (boss == "exUsec" && !bossConfigData.disableVanillaSpawns && location == "lighthouse" || boss == "pmcBot" && !bossConfigData.disableVanillaSpawns && (location == "laboratory" || location == "rezervbase")) + { + for (const bossSpawn in bossDefaultData) + { + // Create the vanilla spawns + //bossDefaultData[bossSpawn].BossChance = bossConfigData.spawnChance[location]; + bossDefaultData[0].BossDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossesForMap.push(bossDefaultData[bossSpawn]); + } + if (!bossConfigData.addExtraSpawns) continue; + } + + if (!bossConfigData.spawnChance[location]) continue; + + if (location.includes("factory")) bossConfigData.bossZone[location] = "BotZone" + if ((boss == "pmcBot") && bossConfigData.addExtraSpawns) + { + bossesForMap.push(...this.generateBossWaves(location, escapeTimeLimit)); + continue; + } + + bossDefaultData[0].BossChance = bossConfigData.spawnChance[location]; + bossDefaultData[0].BossZone = bossConfigData.bossZone[location]; + bossDefaultData[0].BossDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossEscortDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].Time = bossConfigData.time; + bossesForMap.push(bossDefaultData[0]); + } + + return bossesForMap; + } + + private generateBossWaves(location: string, escapeTimeLimit: number): IBossLocationSpawn[] + { + const pmcWaveSpawnInfo: IBossLocationSpawn[] = []; + + const difficultyWeights = ModConfig.config.bossDifficulty; + const waveMaxPMCCount = location != "laboratory" ? 4 : 10; + const waveGroupLimit = 4; + const waveGroupSize = 2; + const waveGroupChance = 100; + const waveTimer = 450; + const endWavesAtRemainingTime = 300; + const waveCount = Math.floor((((escapeTimeLimit * 60) - endWavesAtRemainingTime)) / waveTimer); + let currentWaveTime = waveTimer; + const bossConfigData = ModConfig.config.bossConfig["pmcBot"]; + + //this.logger.warning(`[Boss Waves] Generating ${waveCount} waves for Raiders`) + for (let i = 1; i <= waveCount; i++) + { + if (i == 1) currentWaveTime = -1; + + let currentPMCCount = 0; + let groupCount = 0; + while (currentPMCCount < waveMaxPMCCount) + { + if (groupCount >= waveGroupLimit) break; + let groupSize = 0; + const remainingSpots = waveMaxPMCCount - currentPMCCount; + const isAGroup = remainingSpots > 1 ? this.randomUtil.getChance100(waveGroupChance) : false; + if (isAGroup) + { + groupSize = Math.min(remainingSpots - 1, this.randomUtil.getInt(1, waveGroupSize)); + } + const bossDefaultData = this.cloner.clone(this.getDefaultValuesForBoss("pmcBot", "")); + + bossDefaultData[0].BossChance = bossConfigData.spawnChance[location]; + bossDefaultData[0].BossZone = bossConfigData.bossZone[location]; + bossDefaultData[0].BossEscortAmount = groupSize.toString(); + bossDefaultData[0].BossDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossEscortDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].IgnoreMaxBots = false; + bossDefaultData[0].Time = currentWaveTime; + currentPMCCount += groupSize + 1; + groupCount++ + pmcWaveSpawnInfo.push(bossDefaultData[0]); + + //this.logger.warning(`[Boss Waves] Adding 1 spawn for Raiders to ${location} | GroupSize: ${groupSize + 1}`); + } + //this.logger.warning(`[Boss Waves] Wave: ${i} | Time: ${currentWaveTime} | Groups: ${groupCount} | TotalRaiderss: ${currentPMCCount}/${waveMaxPMCCount}`); + currentWaveTime += waveTimer; + } + + return pmcWaveSpawnInfo; + } + + private getDefaultValuesForBoss(boss: string, location: string): IBossLocationSpawn[] + { + switch (boss) + { + case "bossKnight": + return bossKnightData; + case "bossBully": + return bossBullyData; + case "bossTagilla": + return bossTagillaData; + case "bossKilla": + return bossKillaData; + case "bossZryachiy": + return bossZryachiyData; + case "bossGluhar": + return bossGluharData; + case "bossSanitar": + return bossSanitarData; + case "bossKolontay": + return bossKolontayData; + case "bossBoar": + return bossBoarData; + case "bossKojaniy": + return bossKojaniyData; + case "bossPartisan": + return bossPartisanData; + case "sectantPriest": + return sectantPriestData; + case "arenaFighterEvent": + return arenaFighterEventData; + case "pmcBot": // Requires Triggers + Has Multiple Zones + if (location == "rezervbase") return pmcBotReserveData; + if (location == "laboratory") return pmcBotLaboratoryData; + else return pmcBotData; + case "exUsec": // Has Multiple Zones + return exUsecData; + case "gifter": + return gifterData; + default: + this.logger.error(`[ABPS] Boss not found in config ${boss}`) + return undefined; + } + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/MapSpawnControl.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/MapSpawnControl.ts new file mode 100644 index 0000000..6e76831 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/MapSpawnControl.ts @@ -0,0 +1,221 @@ +import { injectable, inject } from "tsyringe"; +import { ILocationBase, IBossLocationSpawn, IWave } from "@spt/models/eft/common/ILocationBase"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; + +import { BossSpawnControl } from "./BossSpawnControl"; +import { DatabaseService } from "@spt/services/DatabaseService"; +import { ILocations } from "@spt/models/spt/server/ILocations"; +import { VanillaAdjustmentControl } from "./VanillaAdjustmentControl"; +import { PMCSpawnControl } from "./PMCSpawnControl"; +import { ScavSpawnControl } from "./ScavSpawnControl"; +import { IRaidChanges } from "@spt/models/spt/location/IRaidChanges"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; +import { ModConfig } from "../Globals/ModConfig"; + +@injectable() +export class MapSpawnControl +{ + public validMaps: string[] = [ + "bigmap", + "factory4_day", + "factory4_night", + "interchange", + "laboratory", + "lighthouse", + "rezervbase", + "sandbox", + "sandbox_high", + "shoreline", + "tarkovstreets", + "woods" + ]; + + public botMapCache: Record = {}; + public scavMapCache: Record = {}; + public locationData: ILocations = {}; + + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("DatabaseService") protected databaseService: DatabaseService, + @inject("BossSpawnControl") protected bossSpawnControl: BossSpawnControl, + @inject("ScavSpawnControl") protected scavSpawnControl: ScavSpawnControl, + @inject("PMCSpawnControl") protected pmcSpawnControl: PMCSpawnControl, + @inject("PrimaryCloner") protected cloner: ICloner, + @inject("VanillaAdjustmentControl") protected vanillaAdjustmentControl: VanillaAdjustmentControl + ) + {} + + public configureInitialData(): void + { + this.locationData = this.databaseService.getTables().locations; + for (const map in this.validMaps) + { + const mapName = this.validMaps[map]; + this.locationData[mapName].base.BossLocationSpawn = []; + this.botMapCache[mapName] = []; + this.scavMapCache[mapName] = []; + if (ModConfig.config.scavConfig.waves.enable) this.vanillaAdjustmentControl.enableAllSpawnSystem(this.locationData[mapName].base); + this.vanillaAdjustmentControl.removeExistingWaves(this.locationData[mapName].base); + this.vanillaAdjustmentControl.fixPMCHostility(this.locationData[mapName].base); + this.vanillaAdjustmentControl.adjustNewWaveSettings(this.locationData[mapName].base); + /* + This is how you make a spawn point properly + if (this.validMaps[map] == "bigmap") { + const test = { + "BotZoneName": "", + "Categories": [ + "Player" + ], + "ColliderParams": { + "_parent": "SpawnSphereParams", + "_props": { + "Center": { + "x": 0, + "y": 0, + "z": 0 + }, + "Radius": 75 + } + }, + "CorePointId": 0, + "DelayToCanSpawnSec": 4, + "Id": crypto.randomUUID(), + "Infiltration": "Boiler Tanks", + "Position": { + "x": 288.068, + "y": 1.718, + "z": -200.166 + }, + "Rotation": 17.73762, + "Sides": [ + "Pmc" + ] + } + this.locationData[mapName].base.SpawnPointParams.push(test); + } + */ + } + + this.vanillaAdjustmentControl.disableVanillaSettings(); + this.vanillaAdjustmentControl.removeCustomPMCWaves(); + this.buildInitialCache(); + } + public buildInitialCache(): void + { + this.buildBossWaves(); + this.buildPMCWaves(); + this.buildStartingScavs(); + this.replaceOriginalLocations(); + } + + private buildBossWaves(): void + { + for (const map in this.validMaps) + { + const mapName = this.validMaps[map]; + const mapData = this.bossSpawnControl.getCustomMapData(this.validMaps[map], this.locationData[mapName].base.EscapeTimeLimit); + if (mapData.length) mapData.forEach((index) => (this.botMapCache[mapName].push(index))); + } + } + + private buildPMCWaves(): void + { + for (const map in this.validMaps) + { + const mapName = this.validMaps[map]; + const mapData = this.pmcSpawnControl.getCustomMapData(this.validMaps[map], this.locationData[mapName].base.EscapeTimeLimit); + if (mapData.length) mapData.forEach((index) => (this.botMapCache[mapName].push(index))); + } + } + + private buildStartingScavs(): void + { + for (const map in this.validMaps) + { + const mapName = this.validMaps[map]; + if (mapName == "laboratory") continue; + const mapData = this.scavSpawnControl.getCustomMapData(this.validMaps[map]); + if (mapData.length) mapData.forEach((index) => (this.scavMapCache[mapName].push(index))); + } + } + + private replaceOriginalLocations(): void + { + for (const map in this.validMaps) + { + const mapName = this.validMaps[map]; + this.locationData[mapName].base.BossLocationSpawn = this.cloner.clone(this.botMapCache[mapName]); + this.locationData[mapName].base.waves = this.cloner.clone(this.scavMapCache[mapName]); + } + } + + public rebuildCache(location: string): void + { + location = location.toLowerCase(); + this.locationData = this.databaseService.getTables().locations; + this.botMapCache[location] = []; + this.scavMapCache[location] = []; + this.rebuildBossWave(location); + this.rebuildPMCWave(location); + this.rebuildStartingScavs(location) + this.rebuildLocation(location); + } + + private rebuildBossWave(location: string): void + { + const mapName = location.toLowerCase(); + this.logger.warning(`[ABPS] Recreating bosses for ${mapName}`); + + const mapData = this.bossSpawnControl.getCustomMapData(mapName, this.locationData[mapName].base.EscapeTimeLimit); + if (mapData.length) mapData.forEach((index) => (this.botMapCache[mapName].push(index))); + } + + private rebuildPMCWave(location: string): void + { + const mapName = location.toLowerCase(); + this.logger.warning(`[ABPS] Recreating PMCs for ${mapName}`); + + const mapData = this.pmcSpawnControl.getCustomMapData(mapName, this.locationData[mapName].base.EscapeTimeLimit); + if (mapData.length) mapData.forEach((index) => (this.botMapCache[mapName].push(index))); + } + + private rebuildStartingScavs(location: string): void + { + const mapName = location.toLowerCase(); + if (mapName == "laboratory") return; + this.logger.warning(`[ABPS] Recreating scavs for ${mapName}`); + + const mapData = this.scavSpawnControl.getCustomMapData(mapName); + if (mapData.length) mapData.forEach((index) => (this.scavMapCache[mapName].push(index))); + } + + private rebuildLocation(location: string): void + { + const mapName = location.toLowerCase(); + this.locationData[mapName].base.BossLocationSpawn = this.cloner.clone(this.botMapCache[mapName]); + this.locationData[mapName].base.waves = this.cloner.clone(this.scavMapCache[mapName]); + } + + public adjustWaves(mapBase: ILocationBase, raidAdjustments: IRaidChanges): void + { + const locationName = mapBase.Id.toLowerCase(); + if (raidAdjustments.simulatedRaidStartSeconds > 60) + { + const mapBosses = mapBase.BossLocationSpawn.filter((x) => x.Time == -1 && x.BossName != "pmcUSEC" && x.BossName != "pmcBEAR"); + mapBase.BossLocationSpawn = mapBase.BossLocationSpawn.filter((x) => x.Time > raidAdjustments.simulatedRaidStartSeconds && (x.BossName == "pmcUSEC" || x.BossName == "pmcBEAR")); + + for (const bossWave of mapBase.BossLocationSpawn) + { + bossWave.Time -= Math.max(raidAdjustments.simulatedRaidStartSeconds, 0); + } + + const totalRemainingTime = raidAdjustments.raidTimeMinutes * 60; + const newStartingPMCs = this.pmcSpawnControl.generateScavRaidRemainingPMCs(locationName, totalRemainingTime); + newStartingPMCs.forEach((index) => (mapBase.BossLocationSpawn.push(index))); + mapBosses.forEach((index) => (mapBase.BossLocationSpawn.push(index))); + + const newStartingScavs = this.scavSpawnControl.generateStartingScavs(locationName, "assault", true); + newStartingScavs.forEach((index) => (mapBase.waves.push(index))); + } + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/PMCSpawnControl.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/PMCSpawnControl.ts new file mode 100644 index 0000000..1eaf980 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/PMCSpawnControl.ts @@ -0,0 +1,212 @@ +import { injectable, inject } from "tsyringe"; +import { IBossLocationSpawn } from "@spt/models/eft/common/ILocationBase"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; +import { RandomUtil } from "@spt/utils/RandomUtil"; +import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; + + +// Default PMC Data +import { + pmcBEARData, + pmcUSECData +} from "../Defaults/PMCs"; + +import { ModConfig } from "../Globals/ModConfig"; +import { Labs_NonGateSpawnZones } from "../Defaults/MapSpawnZones"; + +@injectable() +export class PMCSpawnControl +{ + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("RandomUtil") protected randomUtil: RandomUtil, + @inject("PrimaryCloner") protected cloner: ICloner, + @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper + ) + {} + + public getCustomMapData(location: string, escapeTimeLimit: number): IBossLocationSpawn[] + { + return this.getConfigValueForLocation(location, escapeTimeLimit) + } + + private getConfigValueForLocation(location: string, escapeTimeLimit: number): IBossLocationSpawn[] + { + let pmcSpawnInfo: IBossLocationSpawn[] = []; + if (ModConfig.config.pmcConfig.startingPMCs.enable) + { + pmcSpawnInfo = pmcSpawnInfo.concat(this.generateStartingPMCWaves(location)); + } + if (ModConfig.config.pmcConfig.waves.enable) + { + pmcSpawnInfo = pmcSpawnInfo.concat(this.generatePMCWaves(location, escapeTimeLimit)); + } + return pmcSpawnInfo; + } + + private generateStartingPMCWaves(location: string): IBossLocationSpawn[] + { + const startingPMCWaveInfo: IBossLocationSpawn[] = []; + const ignoreMaxBotCaps = ModConfig.config.pmcConfig.startingPMCs.ignoreMaxBotCaps; + const minPMCCount = ModConfig.config.pmcConfig.startingPMCs.mapLimits[location].min; + const maxPMCCount = ModConfig.config.pmcConfig.startingPMCs.mapLimits[location].max; + const generatedPMCCount = this.randomUtil.getInt(minPMCCount, maxPMCCount); + const groupChance = ModConfig.config.pmcConfig.startingPMCs.groupChance; + const groupLimit = ModConfig.config.pmcConfig.startingPMCs.maxGroupCount; + const groupMaxSize = ModConfig.config.pmcConfig.startingPMCs.maxGroupSize; + const difficultyWeights = ModConfig.config.pmcDifficulty; + + let currentPMCCount = 0; + let groupCount = 0; + + while (currentPMCCount < generatedPMCCount) + { + const canBeAGroup = groupCount >= groupLimit ? false : true; + let groupSize = 0; + const remainingSpots = generatedPMCCount - currentPMCCount; + + const isAGroup = remainingSpots > 1 ? this.randomUtil.getChance100(groupChance) : false; + if (isAGroup && canBeAGroup) + { + groupSize = Math.min(remainingSpots - 1, this.randomUtil.getInt(1, groupMaxSize)); + groupCount++ + } + + const pmcType = this.randomUtil.getChance100(50) ? "pmcUSEC" : "pmcBEAR"; + const bossDefaultData = this.cloner.clone(this.getDefaultValuesForBoss(pmcType)); + + bossDefaultData[0].BossEscortAmount = groupSize.toString(); + bossDefaultData[0].BossDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossEscortDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossZone = ""; + bossDefaultData[0].IgnoreMaxBots = ignoreMaxBotCaps; + currentPMCCount += groupSize + 1; + startingPMCWaveInfo.push(bossDefaultData[0]); + + //this.logger.warning(`[Starting PMC] Adding 1 spawn for ${pmcType} to ${location} | GroupSize: ${groupSize + 1}`); + } + + //this.logger.warning(`[Starting PMCs] Map: ${location} (Time Limit: ${escapeTimeLimit}m) | Limits: ${minPMCCount}-${maxPMCCount} | Groups: ${groupCount} | TotalPMCs: ${currentPMCCount}/${generatedPMCCount}`); + return startingPMCWaveInfo; + } + + private generatePMCWaves(location: string, escapeTimeLimit: number): IBossLocationSpawn[] + { + const pmcWaveSpawnInfo: IBossLocationSpawn[] = []; + + const ignoreMaxBotCaps = ModConfig.config.pmcConfig.waves.ignoreMaxBotCaps; + const difficultyWeights = ModConfig.config.pmcDifficulty; + const waveMaxPMCCount = location.includes("factory") ? Math.min(2, ModConfig.config.pmcConfig.waves.maxBotsPerWave - 2) : ModConfig.config.pmcConfig.waves.maxBotsPerWave; + const waveGroupLimit = ModConfig.config.pmcConfig.waves.maxGroupCount; + const waveGroupSize = ModConfig.config.pmcConfig.waves.maxGroupSize; + const waveGroupChance = ModConfig.config.pmcConfig.waves.groupChance; + const firstWaveTimer = ModConfig.config.pmcConfig.waves.delayBeforeFirstWave; + const waveTimer = ModConfig.config.pmcConfig.waves.secondsBetweenWaves; + const endWavesAtRemainingTime = ModConfig.config.pmcConfig.waves.stopWavesBeforeEndOfRaidLimit; + const waveCount = Math.floor((((escapeTimeLimit * 60) - endWavesAtRemainingTime) - firstWaveTimer) / waveTimer); + let currentWaveTime = firstWaveTimer; + + //this.logger.warning(`[PMC Waves] Generating ${waveCount} waves for PMCs`) + for (let i = 1; i <= waveCount; i++) + { + let currentPMCCount = 0; + let groupCount = 0; + while (currentPMCCount < waveMaxPMCCount) + { + const canBeAGroup = groupCount >= waveGroupLimit ? false : true; + let groupSize = 0; + const remainingSpots = waveMaxPMCCount - currentPMCCount; + const isAGroup = remainingSpots > 1 ? this.randomUtil.getChance100(waveGroupChance) : false; + if (isAGroup && canBeAGroup) + { + groupSize = Math.min(remainingSpots - 1, this.randomUtil.getInt(1, waveGroupSize)); + groupCount++ + } + + const pmcType = this.randomUtil.getChance100(50) ? "pmcUSEC" : "pmcBEAR"; + const bossDefaultData = this.cloner.clone(this.getDefaultValuesForBoss(pmcType)); + + bossDefaultData[0].BossEscortAmount = groupSize.toString(); + bossDefaultData[0].Time = currentWaveTime; + bossDefaultData[0].BossDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossEscortDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossZone = ""; + bossDefaultData[0].IgnoreMaxBots = ignoreMaxBotCaps; + currentPMCCount += groupSize + 1; + pmcWaveSpawnInfo.push(bossDefaultData[0]); + + //this.logger.warning(`[PMC Waves] Adding 1 spawn for ${pmcType} to ${location} | GroupSize: ${groupSize + 1}`); + } + //this.logger.warning(`[PMC Waves] Wave: ${i} | Time: ${currentWaveTime} | Groups: ${groupCount} | TotalPMCs: ${currentPMCCount}/${waveMaxPMCCount}`); + currentWaveTime += waveTimer; + } + + return pmcWaveSpawnInfo; + } + + private getDefaultValuesForBoss(boss: string): IBossLocationSpawn[] + { + switch (boss) + { + case "pmcUSEC": + return pmcUSECData; + case "pmcBEAR": + return pmcBEARData; + default: + this.logger.error(`[ABPS] PMC not found in config ${boss}`) + return undefined; + } + } + + public generateScavRaidRemainingPMCs(location: string, remainingRaidTime: number): IBossLocationSpawn[] + { + const startingPMCWaveInfo: IBossLocationSpawn[] = []; + const ignoreMaxBotCaps = ModConfig.config.pmcConfig.startingPMCs.ignoreMaxBotCaps; + const minPMCCount = ModConfig.config.pmcConfig.startingPMCs.mapLimits[location].min; + const maxPMCCount = ModConfig.config.pmcConfig.startingPMCs.mapLimits[location].max; + let generatedPMCCount = this.randomUtil.getInt(minPMCCount, maxPMCCount); + const groupChance = ModConfig.config.pmcConfig.startingPMCs.groupChance; + const groupLimit = ModConfig.config.pmcConfig.startingPMCs.maxGroupCount; + const groupMaxSize = ModConfig.config.pmcConfig.startingPMCs.maxGroupSize; + const difficultyWeights = ModConfig.config.pmcDifficulty; + + let currentPMCCount = 0; + let groupCount = 0; + + if (remainingRaidTime < 600) generatedPMCCount = this.randomUtil.getInt(1, 3); + if (remainingRaidTime < 1200) generatedPMCCount = this.randomUtil.getInt(1, 6); + if (remainingRaidTime < 1800) generatedPMCCount = this.randomUtil.getInt(4, 9); + + if (location.includes("factory") && generatedPMCCount > 5) generatedPMCCount -= 2; + + while (currentPMCCount < generatedPMCCount) + { + const canBeAGroup = groupCount >= groupLimit ? false : true; + let groupSize = 0; + const remainingSpots = generatedPMCCount - currentPMCCount; + const isAGroup = remainingSpots > 1 ? this.randomUtil.getChance100(groupChance) : false; + if (isAGroup && canBeAGroup) + { + groupSize = Math.min(remainingSpots - 1, this.randomUtil.getInt(1, groupMaxSize)); + groupCount++ + } + + const pmcType = this.randomUtil.getChance100(50) ? "pmcUSEC" : "pmcBEAR"; + const bossDefaultData = this.cloner.clone(this.getDefaultValuesForBoss(pmcType)); + + bossDefaultData[0].BossEscortAmount = groupSize.toString(); + bossDefaultData[0].BossDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossEscortDifficult = this.weightedRandomHelper.getWeightedValue(difficultyWeights); + bossDefaultData[0].BossZone = ""; + bossDefaultData[0].IgnoreMaxBots = ignoreMaxBotCaps; + currentPMCCount += groupSize + 1; + startingPMCWaveInfo.push(bossDefaultData[0]); + + //this.logger.warning(`[Starting PMC] Adding 1 spawn for ${pmcType} to ${location} | GroupSize: ${groupSize + 1}`); + } + + //this.logger.warning(`[Starting PMCs] Map: ${location} (Time Limit: ${escapeTimeLimit}m) | Limits: ${minPMCCount}-${maxPMCCount} | Groups: ${groupCount} | TotalPMCs: ${currentPMCCount}/${generatedPMCCount}`); + return startingPMCWaveInfo; + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/ScavSpawnControl.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/ScavSpawnControl.ts new file mode 100644 index 0000000..f6d5678 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/ScavSpawnControl.ts @@ -0,0 +1,214 @@ +import { injectable, inject } from "tsyringe"; +import { IWave, WildSpawnType } from "@spt/models/eft/common/ILocationBase"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; +import { RandomUtil } from "@spt/utils/RandomUtil"; +import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; + + +// Default Scav Data + + +import { ModConfig } from "../Globals/ModConfig"; +import { scavData } from "../Defaults/Scavs"; +import { DatabaseService } from "@spt/services/DatabaseService"; +import { + Customs_SpawnZones, + Customs_SnipeSpawnZones, + Factory_SpawnZones, + GroundZero_SpawnZones, + Interchange_SpawnZones, + Labs_NonGateSpawnZones, + Lighthouse_NonWaterTreatmentSpawnZones, + Lighthouse_SnipeSpawnZones, + Reserve_SpawnZones, + Shoreline_SpawnZones, + Shoreline_SnipeSpawnZones, + Streets_SpawnZones, + Streets_SnipeSpawnZones, + Woods_SpawnZones, + Woods_SnipeSpawnZones, + GroundZero_SnipeSpawnZones +} from "../Defaults/MapSpawnZones"; +import { createExhaustableArray } from "../Utils/GlobalUtils"; + +@injectable() +export class ScavSpawnControl +{ + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("DatabaseService") protected databaseService: DatabaseService, + @inject("RandomUtil") protected randomUtil: RandomUtil, + @inject("PrimaryCloner") protected cloner: ICloner, + @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper + ) + {} + + public getCustomMapData(location: string): IWave[] + { + return this.getConfigValueForLocation(location) + } + + private getConfigValueForLocation(location: string): IWave[] + { + const scavSpawnInfo: IWave[] = []; + if (ModConfig.config.scavConfig.startingScavs.enable) scavSpawnInfo.push(...this.generateStartingScavs(location)); + if (ModConfig.config.scavConfig.startingScavs.startingMarksman) + { + const marksmanSpawn = this.generateStartingScavs(location, "marksman"); + if (marksmanSpawn.length) + { + scavSpawnInfo.push(...marksmanSpawn); + } + } + return scavSpawnInfo; + } + + public generateStartingScavs(location: string, botRole = "assault", lateStart = false): IWave[] + { + const scavWaveSpawnInfo: IWave[] = []; + + const waveLength = this.databaseService.getTables().locations[location].base.waves.length; + + const maxStartingSpawns: number = ModConfig.config.scavConfig.startingScavs.maxBotSpawns[location]; + const scavCap = lateStart ? maxStartingSpawns * 0.75 : maxStartingSpawns; + const playerScavChance = lateStart ? 60 : 10; + + const maxBotsPerZone = location.includes("factory") || location.includes("sandbox") ? maxStartingSpawns : ModConfig.config.scavConfig.startingScavs.maxBotsPerZone; + const availableSpawnZones = botRole == "assault" ? createExhaustableArray(this.getNonMarksmanSpawnZones(location), this.randomUtil, this.cloner) : createExhaustableArray(this.getMarksmanSpawnZones(location), this.randomUtil, this.cloner); + let spawnsAdded = botRole == "assault" ? 0 : waveLength; + let marksmanCount = 0; + + while (spawnsAdded < scavCap) + { + if (spawnsAdded >= maxStartingSpawns) break; + const scavDefaultData = this.cloner.clone(this.getDefaultValues()); + let selectedSpawnZone = location.includes("factory") || location.includes("sandbox") || !availableSpawnZones.hasValues() ? "" : availableSpawnZones.getRandomValue(); + if (botRole != "assault") + { + if (!availableSpawnZones.hasValues()) break; + if (selectedSpawnZone == undefined) break; + if (marksmanCount >= 2) break; + selectedSpawnZone = availableSpawnZones.getRandomValue(); + marksmanCount++; + } + const remainingSpots = maxStartingSpawns - spawnsAdded; + const groupSize = botRole == "assault" ? Math.min(remainingSpots - 1, this.randomUtil.getInt(1, maxBotsPerZone)) : 1; + + scavDefaultData.slots_min = groupSize > 1 ? 1 : 0; + scavDefaultData.slots_max = groupSize > 1 ? groupSize : 1; + scavDefaultData.time_min = 1; + scavDefaultData.time_max = 3; + scavDefaultData.number = spawnsAdded; + scavDefaultData.WildSpawnType = botRole == "assault" ? WildSpawnType.ASSAULT : WildSpawnType.MARKSMAN; + scavDefaultData.isPlayers = botRole == "assault" ? this.randomUtil.getChance100(playerScavChance) ? true : false : false; + scavDefaultData.SpawnPoints = selectedSpawnZone; + + spawnsAdded++; + scavWaveSpawnInfo.push(scavDefaultData); + //this.logger.warning(`[Scav Waves] ${scavDefaultData.number} - Adding 1 spawn for ${botRole} to ${location} | Zone: ${selectedSpawnZone} Min: ${scavDefaultData.slots_min} | Max: ${scavDefaultData.slots_max}`); + } + + return scavWaveSpawnInfo; + } + + private getDefaultValues(): IWave + { + return scavData; + } + + private getNonMarksmanSpawnZones(location: string): string[] + { + switch (location) + { + case "bigmap": + return Customs_SpawnZones; + case "factory4_day": + case "factory4_night": + return Factory_SpawnZones; + case "interchange": + return Interchange_SpawnZones; + case "laboratory": + return Labs_NonGateSpawnZones; + case "lighthouse": + return Lighthouse_NonWaterTreatmentSpawnZones; + case "rezervbase": + return Reserve_SpawnZones; + case "sandbox": + case "sandbox_high": + return GroundZero_SpawnZones; + case "shoreline": + return Shoreline_SpawnZones; + case "tarkovstreets": + return Streets_SpawnZones; + case "woods": + return Woods_SpawnZones; + } + } + + private getMarksmanSpawnZones(location: string): string[] + { + switch (location) + { + case "bigmap": + return Customs_SnipeSpawnZones; + case "factory4_day": + case "factory4_night": + return undefined; + case "interchange": + return undefined; + case "laboratory": + return undefined; + case "lighthouse": + return Lighthouse_SnipeSpawnZones; + case "rezervbase": + return undefined; + case "sandbox": + case "sandbox_high": + return GroundZero_SnipeSpawnZones; + case "shoreline": + return Shoreline_SnipeSpawnZones; + case "tarkovstreets": + return Streets_SnipeSpawnZones; + case "woods": + return Woods_SnipeSpawnZones; + } + } + + public generateInitialScavsForRemainingRaidTime(location: string, botRole: string): IWave[] + { + const scavWaveSpawnInfo: IWave[] = []; + + const waveLength = this.databaseService.getTables().locations[location].base.waves.length; + + const maxStartingSpawns: number = ModConfig.config.scavConfig.startingScavs.maxBotSpawns[location] * 0.75; + const maxBotsPerZone = location.includes("factory") || location.includes("sandbox") ? maxStartingSpawns : ModConfig.config.scavConfig.startingScavs.maxBotsPerZone; + const checkMarksman = ModConfig.config.scavConfig.startingScavs.startingMarksman; + + const availableSpawnZones = botRole == "assault" ? createExhaustableArray(this.getNonMarksmanSpawnZones(location), this.randomUtil, this.cloner) : createExhaustableArray(this.getMarksmanSpawnZones(location), this.randomUtil, this.cloner); + let spawnsAdded = botRole == "assault" ? 0 : waveLength; + + while (spawnsAdded < maxStartingSpawns) + { + if (spawnsAdded >= maxStartingSpawns) break; + const scavDefaultData = this.cloner.clone(this.getDefaultValues()); + const selectedSpawnZone = location.includes("factory") || location.includes("sandbox") || !availableSpawnZones.hasValues() ? "" : availableSpawnZones.getRandomValue(); + const remainingSpots = maxStartingSpawns - spawnsAdded; + const groupSize = Math.min(remainingSpots - 1, this.randomUtil.getInt(1, maxBotsPerZone)); + + scavDefaultData.slots_min = groupSize > 1 ? 1 : 1; + scavDefaultData.slots_max = groupSize > 1 ? groupSize : 1; + scavDefaultData.time_min = 1; + scavDefaultData.time_max = 3; + scavDefaultData.number = spawnsAdded; + scavDefaultData.isPlayers = botRole == "assault" ? this.randomUtil.getChance100(60) ? true : false : false; + scavDefaultData.SpawnPoints = selectedSpawnZone; + + spawnsAdded++; + scavWaveSpawnInfo.push(scavDefaultData); + //this.logger.warning(`[Scav Waves] ${scavDefaultData.number} - Adding 1 spawn for assault to ${location} | Zone: ${selectedSpawnZone} Min: ${scavDefaultData.slots_min} | Max: ${scavDefaultData.slots_max}`); + } + + return scavWaveSpawnInfo; + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/VanillaAdjustmentControl.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/VanillaAdjustmentControl.ts new file mode 100644 index 0000000..c56944a --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Controls/VanillaAdjustmentControl.ts @@ -0,0 +1,141 @@ +import { injectable, inject } from "tsyringe"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { DatabaseService } from "@spt/services/DatabaseService"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { ILocationConfig } from "@spt/models/spt/config/ILocationConfig"; +import { ILocationBase } from "@spt/models/eft/common/ILocationBase"; +import { newPMCHostilitySettings } from "../Defaults/Hostility"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; +import { IPmcConfig } from "@spt/models/spt/config/IPmcConfig"; +import { IBotConfig } from "@spt/models/spt/config/IBotConfig"; +import { ModConfig } from "../Globals/ModConfig"; + +@injectable() +export class VanillaAdjustmentControl +{ + public locationConfig: ILocationConfig; + public pmcConfig: IPmcConfig; + public botConfig: IBotConfig; + + constructor( + @inject("WinstonLogger") protected logger: ILogger, + @inject("DatabaseService") protected databaseService: DatabaseService, + @inject("ConfigServer") protected configServer: ConfigServer, + @inject("PrimaryCloner") protected cloner: ICloner + ) + { + this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION); + this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC); + this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); + } + + public disableVanillaSettings(): void + { + this.locationConfig.splitWaveIntoSingleSpawnsSettings.enabled = false; + this.locationConfig.rogueLighthouseSpawnTimeSettings.enabled = false; + this.locationConfig.addOpenZonesToAllMaps = false; + this.locationConfig.addCustomBotWavesToMaps = false; + this.locationConfig.enableBotTypeLimits = false; + } + + public disableNewSpawnSystem(base: any): void + { + if (base.Id == "laboratory") return; + + base.NewSpawn = false; + base.OfflineNewSpawn = false; + base.OldSpawn = true; + base.OfflineOldSpawn = true; + } + + public enableAllSpawnSystem(base: any): void + { + if (base.Id == "laboratory") return; + + base.NewSpawn = true; + base.OfflineNewSpawn = true; + base.OldSpawn = true; + base.OfflineOldSpawn = true; + } + + public adjustNewWaveSettings(base: any): void + { + if (base.Id == "laboratory") return; + + if (ModConfig.config.scavConfig.waves.enableCustomFactory && base.Id.includes("factory")) + { + // Start-Stop Time for spawns + base.BotStart = ModConfig.config.scavConfig.waves.startSpawns; + base.BotStop = (base.EscapeTimeLimit * 60) - ModConfig.config.scavConfig.waves.stopSpawns; + + // Start-Stop wave times for active spawning + base.BotSpawnTimeOnMin = 10; + base.BotSpawnTimeOnMax = 30; + + // Start-Stop wave wait times between active spawning + base.BotSpawnTimeOffMin = 240; + base.BotSpawnTimeOffMax = 300; + + // Probably how often it checks to spawn while active spawning + base.BotSpawnPeriodCheck = 15; + + // Bot count required to trigger a spawn + base.BotSpawnCountStep = 3; + + base.BotLocationModifier.NonWaveSpawnBotsLimitPerPlayer = 20; + base.BotLocationModifier.NonWaveSpawnBotsLimitPerPlayerPvE = 20; + } + else + { + // Start-Stop Time for spawns + base.BotStart = ModConfig.config.scavConfig.waves.startSpawns; + base.BotStop = (base.EscapeTimeLimit * 60) - ModConfig.config.scavConfig.waves.stopSpawns; + + // Start-Stop wave times for active spawning + base.BotSpawnTimeOnMin = ModConfig.config.scavConfig.waves.activeTimeMin; + base.BotSpawnTimeOnMax = ModConfig.config.scavConfig.waves.activeTimeMax; + + // Start-Stop wave wait times between active spawning + base.BotSpawnTimeOffMin = ModConfig.config.scavConfig.waves.quietTimeMin; + base.BotSpawnTimeOffMax = ModConfig.config.scavConfig.waves.quietTimeMax; + + // Probably how often it checks to spawn while active spawning + base.BotSpawnPeriodCheck = ModConfig.config.scavConfig.waves.checkToSpawnTimer; + + // Bot count required to trigger a spawn + base.BotSpawnCountStep = ModConfig.config.scavConfig.waves.pendingBotsToTrigger; + + base.BotLocationModifier.NonWaveSpawnBotsLimitPerPlayer = 20; + base.BotLocationModifier.NonWaveSpawnBotsLimitPerPlayerPvE = 20; + } + } + + public removeExistingWaves(base: any): void + { + base.waves = []; + } + + public fixPMCHostility(base: ILocationBase): void + { + const hostility = base.BotLocationModifier?.AdditionalHostilitySettings; + if (hostility) + { + for (const bot in hostility) + { + if (hostility[bot].BotRole == "pmcUSEC" || hostility[bot].BotRole == "pmcBEAR") + { + const newHostilitySettings = this.cloner.clone(newPMCHostilitySettings); + newHostilitySettings.BotRole = hostility[bot].BotRole; + hostility[bot] = newHostilitySettings; + } + } + } + } + + public removeCustomPMCWaves(): void + { + this.pmcConfig.removeExistingPmcWaves = false; + this.pmcConfig.customPmcWaves = {}; + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Bosses.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Bosses.ts new file mode 100644 index 0000000..2b956ef --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Bosses.ts @@ -0,0 +1,1113 @@ +export const bossKnightData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "bossKnight", + "BossPlayer": false, + "BossZone": "ZoneScavBase", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": true, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": [ + { + "BossEscortAmount": "1", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerBigPipe" + }, + { + "BossEscortAmount": "1", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerBirdEye" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossBullyData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "4", + "BossEscortDifficult": "normal", + "BossEscortType": "followerBully", + "BossName": "bossBully", + "BossPlayer": false, + "BossZone": "ZoneDormitory,ZoneGasStation,ZoneScavBase", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossTagillaData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "followerBully", + "BossName": "bossTagilla", + "BossPlayer": false, + "BossZone": "BotZone", + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossKillaData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "followerTagilla", + "BossName": "bossKilla", + "BossPlayer": false, + "BossZone": "ZoneCenterBot,ZoneCenter,ZoneOLI,ZoneIDEA,ZoneGoshan", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossZryachiyData = + [{ + "BossChance": 100, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "followerZryachiy", + "BossName": "bossZryachiy", + "BossPlayer": false, + "BossZone": "Zone_Island", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossGluharData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "followerGluharSecurity", + "BossName": "bossGluhar", + "BossPlayer": false, + "BossZone": "ZoneRailStrorage,ZonePTOR2,ZoneBarrack,ZoneSubStorage", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": [ + { + "BossEscortAmount": "2", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerGluharAssault" + }, + { + "BossEscortAmount": "2", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerGluharSecurity" + }, + { + "BossEscortAmount": "2", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerGluharScout" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossSanitarData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "3", + "BossEscortDifficult": "normal", + "BossEscortType": "followerSanitar", + "BossName": "bossSanitar", + "BossPlayer": false, + "BossZone": "ZoneGreenHouses,ZoneSanatorium1,ZoneGreenHouses,ZoneSanatorium2,ZonePort", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossKolontayData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "followerKolontaySecurity", + "BossName": "bossKolontay", + "BossPlayer": false, + "BossZone": "ZoneClimova,ZoneMvd", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": [ + { + "BossEscortAmount": "2", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerKolontayAssault" + }, + { + "BossEscortAmount": "2", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerKolontaySecurity" + }, + { + "BossEscortAmount": "0", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerGluharScout" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossBoarData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "6", + "BossEscortDifficult": "normal", + "BossEscortType": "followerBoar", + "BossName": "bossBoar", + "BossPlayer": false, + "BossZone": "ZoneCarShowroom", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": [ + { + "BossEscortAmount": "4", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerBoar" + }, + { + "BossEscortAmount": "1", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerBoarClose1" + }, + { + "BossEscortAmount": "1", + "BossEscortDifficult": [ + "normal" + ], + "BossEscortType": "followerBoarClose2" + } + ], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossKojaniyData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "2", + "BossEscortDifficult": "normal", + "BossEscortType": "followerKojaniy", + "BossName": "bossKojaniy", + "BossPlayer": false, + "BossZone": "ZoneWoodCutter", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossPartisanData = + [{ + "BossChance": 30, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "sectantWarrior", + "BossName": "bossPartisan", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "DependKarma": true, + "DependKarmaPVE": false, + "ForceSpawn": true, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "PARTISAN_TRIGGER", + "TriggerName": "botEvent" + }] + +export const sectantPriestData = + [{ + "BossChance": 15, + "BossDifficult": "normal", + "BossEscortAmount": "2,2,3,3,4", + "BossEscortDifficult": "normal", + "BossEscortType": "sectantWarrior", + "BossName": "sectantPriest", + "BossPlayer": false, + "BossZone": "ZoneMiniHouse,ZoneBrokenVill", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const arenaFighterEventData = + [{ + "BossChance": 5, + "BossDifficult": "normal", + "BossEscortAmount": "3,4", + "BossEscortDifficult": "normal", + "BossEscortType": "arenaFighterEvent", + "BossName": "arenaFighterEvent", + "BossPlayer": false, + "BossZone": "ZoneMiniHouse,ZoneClearVill,ZoneRoad,ZoneBrokenVill,ZoneScavBase2", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": [], + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const pmcBotData = + [ + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ] +export const pmcBotReserveData = + [ + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "ZoneRailStrorage", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": 1470, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "ZoneRailStrorage", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00632_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "ZoneSubCommand", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00000_D2_LEVER", + "TriggerName": "interactObject" + }, + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "2,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "ZoneSubCommand", + "Delay": 0, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve", + "regular" + ], + "Supports": null, + "Time": 3, + "TriggerId": "raider_simple_patroling", + "TriggerName": "interactObject" + } + ] + +export const pmcBotLaboratoryData = + [ + { + "BossChance": 60, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor1", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 900, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 60, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor2", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 300, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 45, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneBasement", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 450, + "TriggerId": "autoId_00008_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 45, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneBasement", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 800, + "TriggerId": "autoId_00010_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 40, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneBasement", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00007_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 45, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor2", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00007_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor1", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 600, + "TriggerId": "autoId_00632_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor2", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 600, + "TriggerId": "autoId_00632_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor1", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00012_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor2", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00012_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor1", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 1200, + "TriggerId": "autoId_00014_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,2,2,1,1,2,2,2,2,1,1,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor2", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": 1200, + "TriggerId": "autoId_00014_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,2,2,1,1,2,2,2,2,1,1,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor1", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00009_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneFloor2", + "Delay": 0, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00009_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 35, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneGate2", + "Delay": 8, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00014_EXFIL", + "TriggerName": "interactObject" + }, + { + "BossChance": 45, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,1,1,2,2,2,1,1,1,1,2,2,2,3", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBot", + "BossName": "pmcBot", + "BossPlayer": false, + "BossZone": "BotZoneGate1", + "Delay": 8, + "IgnoreMaxBots": false, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "autoId_00632_EXFIL", + "TriggerName": "interactObject" + } + ] + +export const exUsecData = + [ + { + "BossChance": 80, + "BossDifficult": "normal", + "BossEscortAmount": "1", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_Blockpost", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 80, + "BossDifficult": "normal", + "BossEscortAmount": "1,2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_RoofContainers", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 50, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_TreatmentRocks", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 50, + "BossDifficult": "normal", + "BossEscortAmount": "1,1,2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_TreatmentBeach", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 80, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_RoofRocks", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 80, + "BossDifficult": "normal", + "BossEscortAmount": "1", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_RoofBeach", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }, + { + "BossChance": 20, + "BossDifficult": "normal", + "BossEscortAmount": "1,2", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "exUsec", + "BossPlayer": false, + "BossZone": "Zone_Hellicopter", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ] + +export const gifterData = + [{ + "BossChance": 0, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "gifter", + "BossName": "gifter", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossPunisherData = + [{ + "BossChance": 0, + "BossDifficult": "impossible", + "BossEscortAmount": "4", + "BossEscortDifficult": "impossible", + "BossEscortType": "exUsec", + "BossName": "bossPunisher", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] + +export const bossLegionData = + [{ + "BossChance": 0, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "exUsec", + "BossName": "bossLegion", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "regular", + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + }] \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Hostility.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Hostility.ts new file mode 100644 index 0000000..92d7639 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Hostility.ts @@ -0,0 +1,64 @@ +export const newPMCHostilitySettings = +{ + AlwaysEnemies: [ + "arenaFighter", + "arenaFighterEvent", + "assault", + "assaultGroup", + "bossBoar", + "bossBoarSniper", + "bossBully", + "bossGluhar", + "bossKilla", + "bossKnight", + "bossKojaniy", + "bossKolontay", + "bossPartisan", + "bossSanitar", + "bossTagilla", + "crazyAssaultEvent", + "cursedAssault", + "exUsec", + "followerBigPipe", + "followerBirdEye", + "followerBoar", + "followerBoarClose1", + "followerBoarClose2", + "followerGluharAssault", + "followerGluharScout", + "followerGluharSecurity", + "followerGluharSnipe", + "followerKojaniy", + "followerKolontayAssault", + "followerKolontaySecurity", + "followerSanitar", + "followerTagilla", + "marksman", + "peacemaker", + "pmcBEAR", + "pmcBot", + "pmcUSEC", + "sectactPriestEvent", + "sectantPriest", + "sectantWarrior", + "skier", + "spiritSpring", + "spiritWinter" + ], + AlwaysFriends: [ + "bossZryachiy", + "followerZryachiy", + "gifter", + "peacefullZryachiyEvent", + "ravangeZryachiyEvent" + ], + BearEnemyChance: 100, + BearPlayerBehaviour: "AlwaysEnemies", + BotRole: "", + ChancedEnemies: [], + Neutral: [], + SavagePlayerBehaviour: "AlwaysEnemies", + UsecEnemyChance: 100, + UsecPlayerBehaviour: "AlwaysEnemies", + Warn: [] +}; \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/MapSpawnZones.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/MapSpawnZones.ts new file mode 100644 index 0000000..f102a8a --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/MapSpawnZones.ts @@ -0,0 +1,189 @@ +export const Customs_SpawnZones = + [ + "ZoneBrige", + "ZoneCrossRoad", + "ZoneDormitory", + "ZoneGasStation", + "ZoneFactoryCenter", + "ZoneFactorySide", + "ZoneOldAZS", + "ZoneBlockPost", + "ZoneTankSquare", + "ZoneWade", + "ZoneCustoms", + "ZoneScavBase" + ] + +export const Customs_SnipeSpawnZones = +[ + "ZoneSnipeBrige", + "ZoneSnipeTower", + "ZoneSnipeFactory", + "ZoneBlockPostSniper" +] +export const Factory_SpawnZones = + [ + "BotZone" + ] +export const Interchange_SpawnZones = + [ + "ZoneCenterBot", + "ZoneIDEA", + "ZoneCenter", + "ZoneIDEAPark", + "ZoneTrucks", + "ZoneRoad", + "ZoneOLI", + "ZoneGoshan", + "ZoneOLIPark", + "ZonePowerStation" + ] +export const Labs_GateSpawnZones = + [ + "BotZoneGate1", + "BotZoneGate2" + ] +export const Labs_NonGateSpawnZones = + [ + "BotZoneBasement", + "BotZoneFloor1", + "BotZoneFloor2" + ] +export const Lighthouse_NonWaterTreatmentSpawnZones = + [ + "Zone_Containers", + "Zone_Rocks", + "Zone_Chalet", + "Zone_Village", + "Zone_Bridge", + "Zone_OldHouse", + "Zone_LongRoad", + "Zone_RoofBeach", + "Zone_DestroyedHouse", + "Zone_RoofContainers", + "Zone_Blockpost", + "Zone_RoofRocks", + "Zone_TreatmentRocks", + "Zone_TreatmentContainers", + "Zone_TreatmentBeach", + "Zone_Hellicopter", + "Zone_SniperPeak", + "Zone_Island" + ] +export const Lighthouse_WaterTreatmentSpawnZones = + [ + "Zone_Containers", + "Zone_Rocks", + "Zone_Chalet", + "Zone_Village", + "Zone_Bridge", + "Zone_OldHouse", + "Zone_LongRoad", + "Zone_RoofBeach", + "Zone_DestroyedHouse", + "Zone_RoofContainers", + "Zone_Blockpost", + "Zone_RoofRocks", + "Zone_TreatmentRocks", + "Zone_TreatmentContainers", + "Zone_TreatmentBeach", + "Zone_Hellicopter", + "Zone_Island" + ] +export const Lighthouse_SnipeSpawnZones = + [ + "Zone_SniperPeak" + ] + +export const Reserve_SpawnZones = + [ + "ZoneRailStrorage", + "ZonePTOR1", + "ZonePTOR2", + "ZoneBarrack", + "ZoneBunkerStorage", + "ZoneSubStorage", + "ZoneSubCommand" + ] +export const GroundZero_SpawnZones = + [ + "ZoneSandbox" + ] +export const GroundZero_SnipeSpawnZones = + [ + "ZoneSandSnipeCenter", + "ZoneSandSnipeCenter2" + ] +export const Shoreline_SpawnZones = + [ + "ZoneGreenHouses", + "ZoneIsland", + "ZoneForestGasStation", + "ZoneGasStation", + "ZonePowerStation", + "ZoneBunker", + "ZoneBusStation", + "ZonePort", + "ZoneForestTruck", + "ZoneForestSpawn", + "ZoneSanatorium1", + "ZoneSanatorium2", + "ZoneStartVillage", + "ZoneMeteoStation", + "ZoneRailWays", + "ZoneSmuglers", + "ZonePassClose", + "ZoneTunnel" + ] +export const Shoreline_SnipeSpawnZones = + [ + "ZoneBunkeSniper", + "ZonePowerStationSniper" + ] +export const Streets_SpawnZones = + [ + "ZoneSW01", + "ZoneConstruction", + "ZoneCarShowroom", + "ZoneCinema", + "ZoneFactory", + "ZoneHotel_1", + "ZoneHotel_2", + "ZoneConcordia_1", + "ZoneConcordiaParking", + "ZoneColumn", + "ZoneSW00", + "ZoneStilo", + "ZoneCard1", + "ZoneMvd", + "ZoneClimova" + ] +export const Streets_SnipeSpawnZones = + [ + "ZoneSnipeCinema", + "ZoneSnipeBuilding", + "ZoneSnipeSW01", + "ZoneSnipeStilo", + "ZoneSnipeCard", + "ZoneSnipeCarShowroom" + ] +export const Woods_SpawnZones = + [ + "ZoneWoodCutter", + "ZoneHouse", + "ZoneBigRocks", + "ZoneRoad", + "ZoneHighRocks", + "ZoneMiniHouse", + "ZoneRedHouse", + "ZoneScavBase2", + "ZoneClearVill", + "ZoneBrokenVill", + "ZoneUsecBase", + "ZoneStoneBunker", + "ZoneDepo" + ] +export const Woods_SnipeSpawnZones = + [ + "ZoneHighRocks" + ] \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/PMCs.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/PMCs.ts new file mode 100644 index 0000000..6aa2626 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/PMCs.ts @@ -0,0 +1,53 @@ +export const pmcUSECData = + [ + { + "BossChance": 100, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcUSEC", + "BossName": "pmcUSEC", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ] + +export const pmcBEARData = + [ + { + "BossChance": 100, + "BossDifficult": "normal", + "BossEscortAmount": "0", + "BossEscortDifficult": "normal", + "BossEscortType": "pmcBEAR", + "BossName": "pmcBEAR", + "BossPlayer": false, + "BossZone": "", + "Delay": 0, + "DependKarma": false, + "DependKarmaPVE": false, + "ForceSpawn": false, + "IgnoreMaxBots": true, + "RandomTimeSpawn": false, + "SpawnMode": [ + "pve" + ], + "Supports": null, + "Time": -1, + "TriggerId": "", + "TriggerName": "" + } + ] \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Scavs.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Scavs.ts new file mode 100644 index 0000000..e1c32a7 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Defaults/Scavs.ts @@ -0,0 +1,18 @@ +import { WildSpawnType } from "@spt/models/eft/common/ILocationBase"; + +export const scavData = +{ + "BotPreset": "easy", + "BotSide": "Savage", + "SpawnMode": [ + "pve" + ], + "SpawnPoints": "ZoneBrige", + "WildSpawnType": WildSpawnType.ASSAULT, + "isPlayers": false, + "number": 0, + "slots_max": 0, + "slots_min": 0, + "time_max": -1, + "time_min": -1 +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Globals/ModConfig.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Globals/ModConfig.ts new file mode 100644 index 0000000..34ae3f9 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Globals/ModConfig.ts @@ -0,0 +1,153 @@ +import { inject, injectable } from "tsyringe"; +import type { FileSystemSync } from "@spt/utils/FileSystemSync"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import path from "node:path"; +import { MinMax } from "@spt/models/common/MinMax"; + +@injectable() +export class ModConfig +{ + public static config: Config; + private lol: string; + + constructor( + @inject("PrimaryLogger") private logger: ILogger, + @inject("FileSystemSync") private fileSystemSync: FileSystemSync + ) + { + ModConfig.config = this.fileSystemSync.readJson(path.resolve(__dirname, "../../config/config.json")); + } +} +export interface Config +{ + pmcDifficulty: Record, + pmcConfig: PMCConfig, + scavConfig: ScavConfig, + bossDifficulty: Record, + bossConfig: BossConfig, + configAppSettings: ConfigAppSettings, +} +export interface PMCConfig +{ + startingPMCs: PMCStartingConfig, + waves: WaveConfig, +} +export interface ScavConfig +{ + startingScavs: ScavStartingConfig, + waves: ScavWaveConfig, +} +export interface ScavStartingConfig +{ + enable: boolean, + maxBotSpawns: ValidLocations, + maxBotsPerZone: number, + startingMarksman: boolean, +} +export interface ScavWaveConfig +{ + enable: boolean, + enableCustomFactory: boolean, + startSpawns: number, + stopSpawns: number, + activeTimeMin: number, + activeTimeMax: number, + quietTimeMin: number, + quietTimeMax: number, + checkToSpawnTimer: number, + pendingBotsToTrigger: number, +} +export type DifficultyConfig = "easy" | "normal" | "hard" | "impossible"; +export interface PMCStartingConfig +{ + enable: boolean, + ignoreMaxBotCaps: boolean, + groupChance: number, + maxGroupSize: number, + maxGroupCount: number, + mapLimits: MinMaxLocations +} +export interface WaveConfig +{ + enable: boolean, + ignoreMaxBotCaps: boolean, + groupChance: number, + maxGroupSize: number, + maxGroupCount: number, + maxBotsPerWave: number, + delayBeforeFirstWave: number, + secondsBetweenWaves: number, + stopWavesBeforeEndOfRaidLimit: number +} +export interface BossConfig +{ + bossKnight: BossLocationInfo, + bossBully: BossLocationInfo, + bossTagilla: BossLocationInfo, + bossKilla: BossLocationInfo, + bossZryachiy: BossLocationInfo, + bossGluhar: BossLocationInfo, + bossSanitar: BossLocationInfo, + bossKolontay: BossLocationInfo, + bossBoar: BossLocationInfo, + bossKojaniy: BossLocationInfo, + bossPartisan: BossLocationInfo, + sectantPriest: BossLocationInfo, + arenaFighterEvent: BossLocationInfo, + pmcBot: SpecialLocationInfo, + exUsec: SpecialLocationInfo, + gifter: BossLocationInfo, +} +export interface BossLocationInfo +{ + enable: boolean, + time: number, + spawnChance: ValidLocations, + bossZone: ValidLocations; +} +export interface SpecialLocationInfo +{ + enable: boolean, + addExtraSpawns: boolean, + disableVanillaSpawns: boolean, + time: number, + spawnChance: ValidLocations, + bossZone: ValidLocations; +} +export interface ValidLocations +{ + bigmap: number | string, + factory4_day: number | string, + factory4_night: number | string, + interchange: number | string, + laboratory: number | string, + lighthouse: number | string, + rezervbase: number | string, + sandbox: number | string, + sandbox_high: number | string, + shoreline: number | string, + tarkovstreets: number | string, + woods: number | string, +} +export interface MinMaxLocations +{ + bigmap: MinMax, + factory4_day: MinMax, + factory4_night: MinMax, + interchange: MinMax, + laboratory: MinMax, + lighthouse: MinMax, + rezervbase: MinMax, + sandbox: MinMax, + sandbox_high: MinMax, + shoreline: MinMax, + tarkovstreets: MinMax, + woods: MinMax, +} + +export interface ConfigAppSettings +{ + showUndo: boolean, + showDefault: boolean, + disableAnimations: boolean +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/InstanceManager.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/InstanceManager.ts new file mode 100644 index 0000000..783df94 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/InstanceManager.ts @@ -0,0 +1,96 @@ +// SPT +import { DependencyContainer, Lifecycle } from "tsyringe"; +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; +import { DatabaseService } from "@spt/services/DatabaseService"; +import { FileSystemSync } from "@spt/utils/FileSystemSync"; + +// Custom +import { ModConfig } from "./Globals/ModConfig"; +import { MapSpawnControl } from "./Controls/MapSpawnControl"; +import { BossSpawnControl } from "./Controls/BossSpawnControl"; +import { ICloner } from "@spt/utils/cloners/ICloner"; +import { VanillaAdjustmentControl } from "./Controls/VanillaAdjustmentControl"; +import { PMCSpawnControl } from "./Controls/PMCSpawnControl"; +import { StaticRouterHooks } from "./Routers/StaticRouterHooks"; +import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; +import { ScavSpawnControl } from "./Controls/ScavSpawnControl"; + +export class InstanceManager +{ + //#region accessible in or after preSptLoad + public modName: string; + public container: DependencyContainer; + public preSptModLoader: PreSptModLoader; + public logger: ILogger; + public fileSystemSync: FileSystemSync; + public cloner: ICloner; + public staticRouterModService: StaticRouterModService; + + public staticRouterHooks: StaticRouterHooks; + public modConfig: ModConfig; + //#endregion + + //#region accessible in or after postDBLoad + public databaseService: DatabaseService; + public weightedRandomHelper: WeightedRandomHelper; + public vanillaAdjustmentControl: VanillaAdjustmentControl; + public bossSpawnControl: BossSpawnControl; + public pmcSpawnControl: PMCSpawnControl; + public scavSpawnControl: ScavSpawnControl; + public mapSpawnControl: MapSpawnControl; + //#endregion + + //#region accessible in or after PostSptLoad + + //#endregion + + // Call at the start of the mods postDBLoad method + public preSptLoad(container: DependencyContainer, mod: string): void + { + this.modName = mod; + this.container = container; + + // SPT Classes + this.preSptModLoader = container.resolve("PreSptModLoader"); + this.logger = container.resolve("WinstonLogger"); + this.fileSystemSync = container.resolve("FileSystemSync"); + this.cloner = container.resolve("PrimaryCloner"); + this.staticRouterModService = container.resolve("StaticRouterModService"); + + // Custom Classes + this.container.register("VanillaAdjustmentControl", VanillaAdjustmentControl, { lifecycle: Lifecycle.Singleton }) + this.container.register("BossSpawnControl", BossSpawnControl, { lifecycle: Lifecycle.Singleton }) + this.container.register("ScavSpawnControl", ScavSpawnControl, { lifecycle: Lifecycle.Singleton }) + this.container.register("PMCSpawnControl", PMCSpawnControl, { lifecycle: Lifecycle.Singleton }) + this.container.register("MapSpawnControl", MapSpawnControl, { lifecycle: Lifecycle.Singleton }) + + this.container.register("StaticRouterHooks", StaticRouterHooks, { lifecycle: Lifecycle.Singleton }) + this.staticRouterHooks = container.resolve("StaticRouterHooks"); + + // Custom Special + + // Resolve this last to set mod configs + this.container.register("ModConfig", ModConfig, { lifecycle: Lifecycle.Singleton }) + this.modConfig = container.resolve("ModConfig"); + } + + public postDBLoad(container: DependencyContainer): void + { + // SPT Classes + this.databaseService = container.resolve("DatabaseService"); + this.weightedRandomHelper = container.resolve("WeightedRandomHelper"); + this.vanillaAdjustmentControl = container.resolve("VanillaAdjustmentControl"); + this.bossSpawnControl = container.resolve("BossSpawnControl"); + this.scavSpawnControl = container.resolve("ScavSpawnControl"); + this.pmcSpawnControl = container.resolve("PMCSpawnControl"); + this.mapSpawnControl = container.resolve("MapSpawnControl"); + } + + public postSptLoad(container: DependencyContainer): void + { + // SPT Classes + this.databaseService = container.resolve("DatabaseService"); + } +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Routers/StaticRouterHooks.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Routers/StaticRouterHooks.ts new file mode 100644 index 0000000..26b1cf0 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Routers/StaticRouterHooks.ts @@ -0,0 +1,129 @@ +import { inject, injectable } from "tsyringe"; +import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; +import { MapSpawnControl } from "../Controls/MapSpawnControl"; +import path from "node:path"; +import { FileSystemSync } from "@spt/utils/FileSystemSync"; + +@injectable() +export class StaticRouterHooks +{ + public cacheRebuilt: boolean = false; + public mapToRebuild: string = ""; + public bossTrackingData: BossTrackingData = null; + + constructor( + @inject("StaticRouterModService") protected staticRouterService: StaticRouterModService, + @inject("MapSpawnControl") protected mapSpawnControl: MapSpawnControl, + @inject("FileSystemSync") protected fileSystem: FileSystemSync + ) + {} + + public registerRouterHooks(): void + { + + this.staticRouterService.registerStaticRouter( + "ABPS-StartMatchRouter", + [ + { + url: "/client/match/local/start", + action: async (url, info, sessionId, output) => + { + this.mapToRebuild = info.location; + if (this.cacheRebuilt) + { + this.cacheRebuilt = false; + } + return output; + } + } + ], + "ABPS" + ); + + this.staticRouterService.registerStaticRouter( + "ABPS-EndMatchRouter", + [ + { + url: "/client/match/local/end", + action: async (url, info, sessionId, output) => + { + if (!this.cacheRebuilt) + { + this.mapSpawnControl.rebuildCache(this.mapToRebuild); + this.cacheRebuilt = true; + } + return output; + } + } + ], + "ABPS" + ); + + this.staticRouterService.registerStaticRouter( + "ABPS-BossTrackingRoutes", + [ + { + url: "/abps/save", + action: async (url, info: BossTrackingData, sessionId, output) => this.saveBossTrackingData(info) + }, + { + url: "/abps/load", + action: async (url, info, sessionId, output) => JSON.stringify(this.bossTrackingData) + } + ], + "ABPS" + ); + + this.load(); + } + + private async saveBossTrackingData(payload: BossTrackingData): Promise + { + if (!payload) + { + return; + } + else + { + this.bossTrackingData = payload; + } + + await this.save(); + return JSON.stringify({ success: true }); + } + + private async save(): Promise + { + try + { + const filename = path.join(__dirname, "../../bossTrackingData.json"); + await this.fileSystem.writeJson(filename, this.bossTrackingData, 2); + } + catch (error) + { + console.error("[ABPS] Failed to save boss tracking data! " + error); + } + } + + public async load(): Promise + { + const filename = path.join(__dirname, "../../bossTrackingData.json"); + if (this.fileSystem.exists(filename)) + { + const jsonData = this.fileSystem.readJson(filename); + this.bossTrackingData = jsonData; + } + else + { + this.bossTrackingData = {}; + await this.save(); + } + } +} + +type BossTrackingInfo = { + spawnedLastRaid: boolean, + chance: number, +}; + +type BossTrackingData = Record>; \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Utils/GlobalUtils.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Utils/GlobalUtils.ts new file mode 100644 index 0000000..7a496a1 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/Utils/GlobalUtils.ts @@ -0,0 +1,8 @@ +import { ExhaustableArray } from "@spt/models/spt/server/ExhaustableArray"; +import { ICloner } from "@spt/utils/cloners/ICloner"; +import { RandomUtil } from "@spt/utils/RandomUtil"; + +export function createExhaustableArray(itemsToAddToArray: T[], randomUtil: RandomUtil, cloner: ICloner): ExhaustableArray +{ + return new ExhaustableArray(itemsToAddToArray, randomUtil, cloner); +} \ No newline at end of file diff --git a/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/mod.ts b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/mod.ts new file mode 100644 index 0000000..42bc884 --- /dev/null +++ b/mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/src/mod.ts @@ -0,0 +1,76 @@ +import { DependencyContainer } from "tsyringe"; +import { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; +import { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod"; +import { InstanceManager } from "./InstanceManager"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { FileSystemSync } from "@spt/utils/FileSystemSync"; + +import { minVersion, satisfies, SemVer } from "semver"; +import path from "node:path"; + +import { ILocationBase } from "@spt/models/eft/common/ILocationBase"; +import { IRaidChanges } from "@spt/models/spt/location/IRaidChanges"; + + +class ABPS implements IPreSptLoadMod, IPostDBLoadMod +{ + // Create InstanceManager - Thank you Cj as per usual + private instance: InstanceManager = new InstanceManager(); + + // PreSPTLoad + public preSptLoad(container: DependencyContainer): void + { + const logger = container.resolve("WinstonLogger"); + if (!this.validSptVersion(container)) + { + logger.error(`[ABPS] This version of ABPS was not made for your version of SPT. Disabling. Requires ${this.validMinimumSptVersion(container)} or higher.`); + return; + } + + this.instance.preSptLoad(container, "ABPS"); + this.instance.staticRouterHooks.registerRouterHooks(); + + container.afterResolution("RaidTimeAdjustmentService", (_t, result: any) => + { + result.adjustWaves = (mapBase: ILocationBase, raidAdjustments: IRaidChanges) => + { + this.instance.mapSpawnControl.adjustWaves(mapBase, raidAdjustments); + } + }, {frequency: "Always"}); + } + + // PostDBLoad + public postDBLoad(container: DependencyContainer): void + { + this.instance.postDBLoad(container); + this.instance.mapSpawnControl.configureInitialData(); + } + + // Version Validation + public validSptVersion(container: DependencyContainer): boolean + { + const fileSysem = container.resolve("FileSystemSync"); + const configServer = container.resolve("ConfigServer"); + const sptConfig = configServer.getConfig(ConfigTypes.CORE); + + const sptVersion = globalThis.G_SPTVERSION || sptConfig.sptVersion; + const packageJsonPath: string = path.join(__dirname, "../package.json"); + const modSptVersion = fileSysem.readJson(packageJsonPath).sptVersion; + + return satisfies(sptVersion, modSptVersion); + } + + public validMinimumSptVersion(container: DependencyContainer): SemVer + { + const fileSysem = container.resolve("FileSystemSync"); + const packageJsonPath: string = path.join(__dirname, "../package.json"); + const modSptVersion = fileSysem.readJson(packageJsonPath).sptVersion; + + return minVersion(modSptVersion) + } +} + +export const mod = new ABPS(); diff --git a/mods/Progressive Bot System/meta.ini b/mods/Acids Progressive Bot System/meta.ini similarity index 100% rename from mods/Progressive Bot System/meta.ini rename to mods/Acids Progressive Bot System/meta.ini diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/LICENSE b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/LICENSE similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/LICENSE rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/LICENSE diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/README.md b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/README.md similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/README.md rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/README.md diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/config/config.jsonc b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/config/config.jsonc similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/config/config.jsonc rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/config/config.jsonc diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/package.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/package.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/package.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/package.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier1_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier2_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier3_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier4_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier5_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier6_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/presets/example/Tier7_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Alterations/BotConfigs.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Alterations/BotConfigs.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Alterations/BotConfigs.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Alterations/BotConfigs.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotEquipmentModGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotEquipmentModGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotEquipmentModGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotEquipmentModGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGeneratorHelper.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGeneratorHelper.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGeneratorHelper.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotGeneratorHelper.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotInventoryGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotInventoryGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotInventoryGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotInventoryGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootCacheService.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootCacheService.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootCacheService.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootCacheService.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotLootGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotWeaponGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotWeaponGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotWeaponGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSBotWeaponGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSPlayerScavGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSPlayerScavGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSPlayerScavGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/ClassExtensions/APBSPlayerScavGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Enums/Logging.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Enums/Logging.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Enums/Logging.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Enums/Logging.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Generators/ABPSBotLevelGenerator.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Generators/ABPSBotLevelGenerator.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Generators/ABPSBotLevelGenerator.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Generators/ABPSBotLevelGenerator.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModConfig.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModConfig.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModConfig.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModConfig.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModInformation.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModInformation.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModInformation.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/ModInformation.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/RaidInformation.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/RaidInformation.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/RaidInformation.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/RaidInformation.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/TierInformation.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/TierInformation.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/TierInformation.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/TierInformation.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/VanillaItemLists.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/VanillaItemLists.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/VanillaItemLists.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Globals/VanillaItemLists.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/BlacklistHelper.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/BlacklistHelper.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/BlacklistHelper.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/BlacklistHelper.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/JSONHelper.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/JSONHelper.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/JSONHelper.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/JSONHelper.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/ModdedImportHelper.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/ModdedImportHelper.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/ModdedImportHelper.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/ModdedImportHelper.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/RealismHelper.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/RealismHelper.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/RealismHelper.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Helpers/RealismHelper.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InstanceManager.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InstanceManager.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InstanceManager.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InstanceManager.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Interface/APBSIBotBase.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Interface/APBSIBotBase.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Interface/APBSIBotBase.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Interface/APBSIBotBase.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSBarrelInventoryMagGen.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSBarrelInventoryMagGen.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSBarrelInventoryMagGen.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSBarrelInventoryMagGen.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSExternalInventoryMagGen.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSExternalInventoryMagGen.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSExternalInventoryMagGen.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSExternalInventoryMagGen.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSIInventoryMagGen.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSIInventoryMagGen.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSIInventoryMagGen.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSIInventoryMagGen.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInternalMagazineInventoryMagGen.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInternalMagazineInventoryMagGen.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInternalMagazineInventoryMagGen.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInternalMagazineInventoryMagGen.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInventoryMagGen.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInventoryMagGen.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInventoryMagGen.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSInventoryMagGen.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSMethodHolder.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSMethodHolder.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSMethodHolder.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSMethodHolder.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSUbglExternalMagGen.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSUbglExternalMagGen.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSUbglExternalMagGen.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/InventoryMagGen/APBSUbglExternalMagGen.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSDynamicRouterHooks.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSDynamicRouterHooks.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSDynamicRouterHooks.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSDynamicRouterHooks.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSStaticRouterHooks.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSStaticRouterHooks.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSStaticRouterHooks.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/RouterHooks/APBSStaticRouterHooks.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSAttachmentChecker.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSAttachmentChecker.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSAttachmentChecker.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSAttachmentChecker.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSEquipmentGetter.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSEquipmentGetter.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSEquipmentGetter.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSEquipmentGetter.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSLogger.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSLogger.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSLogger.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSLogger.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTester.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTester.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTester.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTester.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTierGetter.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTierGetter.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTierGetter.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/Utils/APBSTierGetter.ts diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier0_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier1_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier2_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier3_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier4_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier5_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier6_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_ammo.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_ammo.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_ammo.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_ammo.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_appearance.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_appearance.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_appearance.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_appearance.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_chances.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_chances.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_chances.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_chances.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_equipment.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_equipment.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_equipment.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_equipment.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_mods.json b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_mods.json similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_mods.json rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/db/Tier7_mods.json diff --git a/mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/mod.ts b/mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/mod.ts similarity index 100% rename from mods/Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/mod.ts rename to mods/Acids Progressive Bot System/user/mods/acidphantasm-progressivebotsystem/src/mod.ts diff --git a/mods/Auto Deposit/BepInEx/plugins/Tyfon.AutoDeposit.dll b/mods/Auto Deposit/BepInEx/plugins/Tyfon.AutoDeposit.dll index 8733194..1cc6000 100644 Binary files a/mods/Auto Deposit/BepInEx/plugins/Tyfon.AutoDeposit.dll and b/mods/Auto Deposit/BepInEx/plugins/Tyfon.AutoDeposit.dll differ diff --git a/mods/Auto Deposit/meta.ini b/mods/Auto Deposit/meta.ini index 69ff97f..f0efc22 100644 --- a/mods/Auto Deposit/meta.ini +++ b/mods/Auto Deposit/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.30.0 +version=d2025.5.13.0 newestVersion= category="2," nexusFileStatus=1 -installationFile=Tyfon-AutoDeposit-3.0.1.zip +installationFile=Tyfon-AutoDeposit-4.0.0.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Big Brain/BepInEx/plugins/DrakiaXYZ-BigBrain.dll b/mods/Big Brain/BepInEx/plugins/DrakiaXYZ-BigBrain.dll index 3a9aeb8..97479cf 100644 Binary files a/mods/Big Brain/BepInEx/plugins/DrakiaXYZ-BigBrain.dll and b/mods/Big Brain/BepInEx/plugins/DrakiaXYZ-BigBrain.dll differ diff --git a/mods/Big Brain/meta.ini b/mods/Big Brain/meta.ini index 0410295..02efb11 100644 --- a/mods/Big Brain/meta.ini +++ b/mods/Big Brain/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.16.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=DrakiaXYZ-BigBrain-1.2.0.7z +installationFile=DrakiaXYZ-BigBrain-1.3.2.7z repository=Nexus ignoredVersion= comments= diff --git a/mods/Config Files/BepInEx/config/com.mpstark.DynamicMaps.cfg b/mods/Config Files/BepInEx/config/com.mpstark.DynamicMaps.cfg index 76e2573..b3d36df 100644 --- a/mods/Config Files/BepInEx/config/com.mpstark.DynamicMaps.cfg +++ b/mods/Config Files/BepInEx/config/com.mpstark.DynamicMaps.cfg @@ -259,7 +259,7 @@ Centering On Player Zoom Level = 0.15 # Setting type: Single # Default value: 0 # Acceptable value range: From 0 to 15 -Main map zoom = 1.588901 +Main map zoom = 2.080739 ## The keyboard shortcut to peek at the map # Setting type: KeyboardShortcut diff --git a/mods/Custom Interactions/BepInEx/patchers/IcyClawz.CustomInteractions.Prepatch.dll b/mods/Custom Interactions/BepInEx/patchers/IcyClawz.CustomInteractions.Prepatch.dll index bb19b8c..fa8f2cb 100644 Binary files a/mods/Custom Interactions/BepInEx/patchers/IcyClawz.CustomInteractions.Prepatch.dll and b/mods/Custom Interactions/BepInEx/patchers/IcyClawz.CustomInteractions.Prepatch.dll differ diff --git a/mods/Custom Interactions/BepInEx/plugins/IcyClawz.CustomInteractions.dll b/mods/Custom Interactions/BepInEx/plugins/IcyClawz.CustomInteractions.dll index ce2797e..23bbf66 100644 Binary files a/mods/Custom Interactions/BepInEx/plugins/IcyClawz.CustomInteractions.dll and b/mods/Custom Interactions/BepInEx/plugins/IcyClawz.CustomInteractions.dll differ diff --git a/mods/Custom Interactions/meta.ini b/mods/Custom Interactions/meta.ini index 4fa7824..d34089f 100644 --- a/mods/Custom Interactions/meta.ini +++ b/mods/Custom Interactions/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.6.0 +version=d2025.5.13.0 newestVersion= category="2," nexusFileStatus=1 -installationFile=CustomInteractions-1.6.0.zip +installationFile=CustomInteractions-1.7.0.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/DynamicMaps.dll b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/DynamicMaps.dll index 70b746f..c64e37e 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/DynamicMaps.dll and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/DynamicMaps.dll differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Customs_TarkovDev.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Customs_TarkovDev.jsonc index 41f8dde..088f649 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Customs_TarkovDev.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Customs_TarkovDev.jsonc @@ -18,6 +18,14 @@ "Max": {"x": 698, "y": 235} }, "GameBounds": [ + { // next to warehouse 17 switch + "Min": {"x": 98, "y": -78, "z": -100}, + "Max": {"x": 127, "y": -30, "z": 0.5} + }, + { // smokestack building + "Min": {"x": 239, "y": -41, "z": -100}, + "Max": {"x": 270, "y": 4, "z": 0.5} + }, { // zb-1011 "Min": {"x": 620, "y": -137, "z": -100}, "Max": {"x": 635, "y": -125, "z": 0.5} @@ -62,6 +70,22 @@ "Max": {"x": 698, "y": 235} }, "GameBounds": [ + { // warehouse 3 switch + "Min": {"x": 406, "y": -98, "z": 2.7}, + "Max": {"x": 422, "y": -83, "z": 100} + }, + { // next to warehouse 17 switch + "Min": {"x": 80, "y": -82, "z": 2.7}, + "Max": {"x": 130, "y": -27, "z": 6.5} + }, + { // warehouse 17 switch + "Min": {"x": 17, "y": -100, "z": 2.7}, + "Max": {"x": 77, "y": -28, "z": 7.5} + }, + { // smokestack building + "Min": {"x": 240, "y": -85, "z": 2.7}, + "Max": {"x": 300, "y": 4, "z": 7.5} + }, { // dorms "Min": {"x": 165, "y": 125, "z": 2.7}, "Max": {"x": 243, "y": 190, "z": 5.0} @@ -124,11 +148,37 @@ "Max": {"x": 698, "y": 235} }, "GameBounds": [ + { // next to warehouse 17 switch + "Min": {"x": 80, "y": -82, "z": 6.5}, + "Max": {"x": 130, "y": -27, "z": 100} + }, + { // warehouse 17 switch + "Min": {"x": 17, "y": -100, "z": 7.5}, + "Max": {"x": 77, "y": -28, "z": 100} + }, + { // smokestack building + "Min": {"x": 240, "y": -85, "z": 7.5}, + "Max": {"x": 300, "y": 4, "z": 9.8} + }, { // dorms "Min": {"x": 165, "y": 125, "z": 5.0}, "Max": {"x": 234, "y": 190, "z": 100} } ] + }, + "4th Floor": { + "Level": 3, + "ImagePath": "Maps/Customs_TarkovDev/Layers/customs_level_3.png", + "ImageBounds": { + "Min": {"x": -372, "y": -306}, + "Max": {"x": 698, "y": 235} + }, + "GameBounds": [ + { // smokestack building + "Min": {"x": 237, "y": -85, "z": 9.8}, + "Max": {"x": 300, "y": 4, "z": 100} + } + ] } }, "Labels": [ // all of these labels are from Tarkov.dev map data under MIT license @@ -334,7 +384,7 @@ { "Text": "Warehouse 17", "ImagePath": "Markers/exit.png", - "Position": { "x": 45.9998779, "y": -78.43004, "z": 3.61000967 }, + "Position": { "x": 50.7402344, "y": -69.8183746, "z": 2.13601065 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -444,20 +494,68 @@ "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, { - "Text": "Smuggler's Boat", + "Text": "Railroad Passage (Flare)", "ImagePath": "Markers/exit.png", - "Position": { "x": -43.11017, "y": 119.40004, "z": -14.2250118 }, + "Position": { "x": 139.592712, "y": -324.7145, "z": 3.800012 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, { - "Text": "ZB-1012", + "Text": "Boiler Room Basement (Co-op)", "ImagePath": "Markers/exit.png", - "Position": { "x": 459.427826, "y": -111.042969, "z": -2.31699228 }, + "Position": { "x": 100.56, "y": -44.42, "z": -4.98 }, "Category": "Extract", "ShowInRaid": false, - "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } + "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 }, + }, + { + "Text": "Transit to Reserve", + "ImagePath": "Markers/transit.png", + "Position": { "x": 651.7, "y": 124.6, "z": 1.99 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + }, + { + "Text": "Transit to Factory", + "ImagePath": "Markers/transit.png", + "Position": { "x": 354.31, "y": -190.1, "z": 2.31 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + }, + { + "Text": "Transit to Interchange", + "ImagePath": "Markers/transit.png", + "Position": { "x": -335.24, "y": -205.68, "z": 2.11 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + }, + { + "Text": "Transit to Shoreline", + "ImagePath": "Markers/transit.png", + "Position": { "x": 23.67, "y": 139.53, "z": -1.3999995 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + }, + { + "Text": "Smugglers' Boat (Secret)", + "ImagePath": "Markers/exit.png", + "Position": { "x": -41.5100021, "y": 122.67, "z": -12.633 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, + { + "Text": "Smugglers' Bunker (Secret)", + "ImagePath": "Markers/exit.png", + "Position": { "x": 463.18, "y": -112.36, "z": -1.46 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 }, }, { "Text": "Tarcone Director's Office", @@ -722,6 +820,14 @@ "Category": "LockedDoor", "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, "AssociatedItemId": "5938144586f77473c2087145" + }, + { + "Text": "Company Director Room", + "ImagePath": "Markers/door_with_lock.png", + "Position": { "x": 96.02269, "y": -58.42183, "z": 8.23889 }, + "Category": "LockedDoor", + "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, + "AssociatedItemId": "6761a6f90575f25e020816a4" } ] -} +} \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Fourth_Floor.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Fourth_Floor.svg new file mode 100644 index 0000000..b140c66 --- /dev/null +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Fourth_Floor.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Ground_Level.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Ground_Level.svg index b0bd6f8..bc57b93 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Ground_Level.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Ground_Level.svg @@ -1,599 +1,723 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Second_Floor.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Second_Floor.svg index 784fcd5..2853c3d 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Second_Floor.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Second_Floor.svg @@ -1,136 +1,107 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Third_Floor.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Third_Floor.svg index a6fd9d3..bedd889 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Third_Floor.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Third_Floor.svg @@ -1,83 +1,48 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Underground_Level.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Underground_Level.svg index 03e6489..c30ebdc 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Underground_Level.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs-Underground_Level.svg @@ -1,82 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + + + - - - \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs.svg index 6259d15..01233ea 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/SVG/Customs.svg @@ -1,705 +1,877 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - + + + - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_-1.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_-1.png index 6a1c988..fdec791 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_-1.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_-1.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_0.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_0.png index 49465be..0ab544c 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_0.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_0.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_1.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_1.png index 5999127..a48d358 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_1.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_1.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_2.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_2.png index c67adb1..a5a7468 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_2.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_2.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_3.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_3.png new file mode 100644 index 0000000..b006cf2 Binary files /dev/null and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Customs_TarkovDev/Layers/customs_level_3.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Factory_TarkovDev/Factory_TarkovDev.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Factory_TarkovDev/Factory_TarkovDev.jsonc index faa6f0d..7aaa4f2 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Factory_TarkovDev/Factory_TarkovDev.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Factory_TarkovDev/Factory_TarkovDev.jsonc @@ -236,6 +236,38 @@ "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Woods", + "ImagePath": "Markers/transit.png", + "Position": { "x": 23.7, "y": 61.61, "z": 0.84 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Customs", + "ImagePath": "Markers/transit.png", + "Position": { "x": 18.51, "y": -48.33, "z": -0.423 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to The Lab", + "ImagePath": "Markers/transit.png", + "Position": { "x": -26.539, "y": -42.013, "z": -4.085 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Smugglers' Passage (Secret)", + "ImagePath": "Markers/exit.png", + "Position": { "x": 48.026, "y": -28.8580017, "z": -1.61300015 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, { "Text": "Locked Office", "ImagePath": "Markers/door_with_lock.png", diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/GroundZero_TarkovDev/GroundZero_TarkovDev.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/GroundZero_TarkovDev/GroundZero_TarkovDev.jsonc index 5a49d42..aa6a650 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/GroundZero_TarkovDev/GroundZero_TarkovDev.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/GroundZero_TarkovDev/GroundZero_TarkovDev.jsonc @@ -176,13 +176,29 @@ "Color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, { - "Text": "Mira Ave", + "Text": "Mira Ave (Flare)", "ImagePath": "Markers/exit.png", "Position": { "x": 218.225, "y": -38.5065842, "z": 17.9920559 }, "Category": "Extract", "ShowInRaid": false, "Color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Streets of Tarkov", + "ImagePath": "Markers/transit.png", + "Position": { "x": 222.829117, "y": 65.72, "z": 15.993 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": {"r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Tartowers Sales Office (Secret)", + "ImagePath": "Markers/exit.png", + "Position": { "x": 190.804, "y": 133.388992, "z": 17.6449986 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": {"r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, { "Text": "Underground Parking Utility Room", "ImagePath": "Markers/door_with_lock.png", @@ -198,6 +214,22 @@ "Category": "LockedDoor", "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0}, "AssociatedItemId": "658199aa38c79576a2569e13" + }, + { + "Text": "Unity Credit Bank Cash Register", + "ImagePath": "Markers/door_with_lock.png", + "Position": { "x": 58.2430344, "y": 172.438, "z": 24.5026741 }, + "Category": "LockedDoor", + "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0}, + "AssociatedItemId": "6581998038c79576a2569e11" + }, + { + "Text": "Horse Restaurant Toilet", + "ImagePath": "Markers/door_with_lock.png", + "Position": { "x": 120.074966, "y": 149.382675, "z": 25.4368019 }, + "Category": "LockedDoor", + "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0}, + "AssociatedItemId": "658199a0490414548c0fa83b" } ] } diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Interchange_TarkovDev.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Interchange_TarkovDev.jsonc index 1321dfc..655cdf5 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Interchange_TarkovDev.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Interchange_TarkovDev.jsonc @@ -480,7 +480,7 @@ { "Text": "Emercom Checkpoint", "ImagePath": "Markers/exit.png", - "Position": { "x": -317.21, "y": 267.94, "z": 21.94955 }, + "Position": { "x": -321.56, "y": 266.74, "z": 21.94955 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -488,7 +488,7 @@ { "Text": "Railway Exfil", "ImagePath": "Markers/exit.png", - "Position": { "x": 474.67, "y": -436.09, "z": 20.92 }, + "Position": { "x": 472.32, "y": -429.74, "z": 20.92 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -496,7 +496,7 @@ { "Text": "Power Station V-Ex", "ImagePath": "Markers/exit.png", - "Position": { "x": -251.93, "y": -366.99, "z": 21.7130013 }, + "Position": { "x": -251.92, "y": -367.13, "z": 21.72 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -504,7 +504,7 @@ { "Text": "Scav Camp (Co-Op)", "ImagePath": "Markers/exit.png", - "Position": { "x": 284.442, "y": -27.493, "z": 21.94955 }, + "Position": { "x": 278.69, "y": -30.97, "z": 21.94955 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -512,7 +512,7 @@ { "Text": "Hole in the Fence", "ImagePath": "Markers/exit.png", - "Position": { "x": -224.16, "y": -40.91, "z": 22.79 }, + "Position": { "x": -219.92, "y": -37.02, "z": 22.79 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -525,6 +525,22 @@ "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Customs", + "ImagePath": "Markers/transit.png", + "Position": { "x": 274.3, "y": 395.93, "z": 23.2799988 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Streets of Tarkov", + "ImagePath": "Markers/transit.png", + "Position": { "x": 263.1, "y": -444.4, "z": 24.1 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, { "Text": "Power Substation Utility Cabin", "ImagePath": "Markers/door_with_lock.png", diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-First_Floor.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-First_Floor.svg index 2f1fd74..c8394b1 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-First_Floor.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-First_Floor.svg @@ -1,128 +1,169 @@ - - - - - - - + + + + + + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + + - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Ground_Level.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Ground_Level.svg index 4601cdc..4d5e83e 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Ground_Level.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Ground_Level.svg @@ -1,258 +1,359 @@ - - - - - - - + + + + + + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + - - - - - - + + + + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Second_Floor.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Second_Floor.svg index e0d78a8..e8c2b3b 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Second_Floor.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange-Second_Floor.svg @@ -1,110 +1,76 @@ - - - - - - - + + + + + + + + - - - + + + - - - + + + - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + - - - - - - - - - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange.svg index 2621d43..441c108 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/SVG/Interchange.svg @@ -1,328 +1,572 @@ - - - - - - - + + + + + + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - + + + + + + + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + - - - - - - + + + + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - + + + - - + + + - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + + - - + + - - - - - - - - - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_0.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_0.png index 922ab9b..d28d644 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_0.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_0.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_1.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_1.png index 34ec181..01e1d6b 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_1.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_1.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_2.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_2.png index b0a3b7a..233f95b 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_2.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Interchange_TarkovDev/Layers/interchange_layer_2.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Labs_TarkovDev/Labs_TarkovDev.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Labs_TarkovDev/Labs_TarkovDev.jsonc index 19330d8..63b434b 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Labs_TarkovDev/Labs_TarkovDev.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Labs_TarkovDev/Labs_TarkovDev.jsonc @@ -91,7 +91,7 @@ { "Text": "Sewage Conduit", "ImagePath": "Markers/exit.png", - "Position": { "x": -123.711, "y": -255.3735, "z": -3.6609993 }, + "Position": { "x": -122.889992, "y": -258.3245, "z": -3.65600014 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -112,6 +112,14 @@ "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Streets of Tarkov", + "ImagePath": "Markers/transit.png", + "Position": { "x": -169.079956, "y": -420.878052, "z": 1.32 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, { "Text": "Medical Block Elevator Power", "ImagePath": "Markers/lever.png", @@ -295,6 +303,14 @@ "Category": "LockedDoor", "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, "AssociatedItemId": "5c1f79a086f7746ed066fb8f" + }, + { + "Text": "Residential Unit", + "ImagePath": "Markers/door_with_lock.png", + "Position": { "x": -117.3155, "y": -439.54068, "z": 1.12753034 }, + "Category": "LockedDoor", + "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, + "AssociatedItemId": "6711039f9e648049e50b3307" } ] } diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Lighthouse_TarkovData/Lighthouse_TarkovData.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Lighthouse_TarkovData/Lighthouse_TarkovData.jsonc index b81c3a7..7b1e829 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Lighthouse_TarkovData/Lighthouse_TarkovData.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Lighthouse_TarkovData/Lighthouse_TarkovData.jsonc @@ -226,6 +226,38 @@ "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Shoreline", + "ImagePath": "Markers/transit.png", + "Position": { "x": -338.6, "y": -168.9, "z": 17.5 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Reserve", + "ImagePath": "Markers/transit.png", + "Position": { "x": -294.6, "y": -780.1, "z": 15.2591143 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Woods", + "ImagePath": "Markers/transit.png", + "Position": { "x": 106.3, "y": -958.1, "z": 6.74 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Passage by the Lake (Secret)", + "ImagePath": "Markers/exit.png", + "Position": { "x": -360.0, "y": -564.87, "z": 27.19 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, // { // "Text": "OuterSwitcher", // "ImagePath": "Markers/lever.png", diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Reserve_TarkovData/Reserve_TarkovData.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Reserve_TarkovData/Reserve_TarkovData.jsonc index 0c07cc0..86def7e 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Reserve_TarkovData/Reserve_TarkovData.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Reserve_TarkovData/Reserve_TarkovData.jsonc @@ -226,7 +226,7 @@ { "Text": "Depot Hermetic Door", "ImagePath": "Markers/exit.png", - "Position": { "x": 123.59, "y": -121.12, "z": -10.27 }, + "Position": { "x": 120.78, "y": -120.352, "z": -10.27 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -234,7 +234,7 @@ { "Text": "Checkpoint Fence", "ImagePath": "Markers/exit.png", - "Position": { "x": 66.76, "y": 138.26, "z": -4.43 }, + "Position": { "x": 61.97, "y": 134.31, "z": -4.43 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -250,7 +250,7 @@ { "Text": "Bunker Hermetic Door", "ImagePath": "Markers/exit.png", - "Position": { "x": 62.3918457, "y": -194.269928, "z": -5.257 }, + "Position": { "x": 61.9206619, "y": -190.541931, "z": -5.257 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -279,6 +279,38 @@ "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Customs", + "ImagePath": "Markers/transit.png", + "Position": { "x": -196.729477, "y": -117.808853, "z": -4.544204 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Woods", + "ImagePath": "Markers/transit.png", + "Position": { "x": 216.860535, "y": -201.048859, "z": -6.344204 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Lighthouse", + "ImagePath": "Markers/transit.png", + "Position": { "x": 238.800537, "y": -128.048859, "z": -6.25420427 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Exit to Woods", + "ImagePath": "Markers/exit.png", + "Position": { "x": 36.53, "y": -221.56, "z": -5.97000027 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, { "Text": "Bunker Hermetic Door Power", "ImagePath": "Markers/lever.png", diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Shoreline_TarkovData/Shoreline_TarkovData.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Shoreline_TarkovData/Shoreline_TarkovData.jsonc index 34b020e..6607eff 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Shoreline_TarkovData/Shoreline_TarkovData.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Shoreline_TarkovData/Shoreline_TarkovData.jsonc @@ -185,15 +185,7 @@ { "Text": "Ruined Road", "ImagePath": "Markers/exit.png", - "Position": { "x": 368.559326, "y": 334.897644, "z": -61.12998 }, - "Category": "Extract", - "ShowInRaid": false, - "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } - }, - { - "Text": "Old Bunker", - "ImagePath": "Markers/exit.png", - "Position": { "x": -388.99, "y": -383.73, "z": -2.66 }, + "Position": { "x": 366.62, "y": 332.51, "z": -61.12998 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -201,7 +193,7 @@ { "Text": "East Wing Gym Entrance", "ImagePath": "Markers/exit.png", - "Position": { "x": -296.9457, "y": -101.476379, "z": -6.629977 }, + "Position": { "x": -296.9457, "y": -100.892, "z": -6.629977 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -209,7 +201,7 @@ { "Text": "Admin Basement", "ImagePath": "Markers/exit.png", - "Position": { "x": -258.3117, "y": -149.782379, "z": -7.70797729 }, + "Position": { "x": -256.87, "y": -149.782379, "z": -7.70797729 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -217,7 +209,7 @@ { "Text": "Smuggler's Path (Co-op)", "ImagePath": "Markers/exit.png", - "Position": { "x": -730.79, "y": -255.47, "z": -26.0 }, + "Position": { "x": -729.4, "y": -255.1, "z": -26.0 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } @@ -225,7 +217,7 @@ { "Text": "Road to North V-Ex", "ImagePath": "Markers/exit.png", - "Position": { "x": -542.9488, "y": -381.020721, "z": -16.9900017 }, + "Position": { "x": -543.264038, "y": -379.651367, "z": -16.8420162 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -233,7 +225,7 @@ { "Text": "Road to Customs", "ImagePath": "Markers/exit.png", - "Position": { "x": -861.65, "y": -0.36, "z": -41.7699776 }, + "Position": { "x": -859.05, "y": 0.49, "z": -42.07 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -241,7 +233,7 @@ { "Text": "Railway Bridge", "ImagePath": "Markers/exit.png", - "Position": { "x": -1026.47, "y": 299.85, "z": -60.79 }, + "Position": { "x": -1029.29, "y": 307.59, "z": -60.79 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -265,7 +257,7 @@ { "Text": "Pier Boat", "ImagePath": "Markers/exit.png", - "Position": { "x": -336.8407, "y": 568.4276, "z": -66.11997 }, + "Position": { "x": -332.5907, "y": 561.2576, "z": -66.11997 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -273,11 +265,27 @@ { "Text": "Climber's Trail", "ImagePath": "Markers/exit.png", - "Position": { "x": -204.5, "y": -359.71, "z": -10.74 }, + "Position": { "x": -214.3, "y": -361.8, "z": -1.0 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Lighthouse", + "ImagePath": "Markers/transit.png", + "Position": { "x": 417.2, "y": -217.5, "z": -56.1 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Mountain Bunker", + "ImagePath": "Markers/exit.png", + "Position": { "x": -390.956024,"y": -385.721, "z": -2.55000019 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, { "Text": "HEP Station Storage Room", "ImagePath": "Markers/door_with_lock.png", diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Streets_TarkovData/Streets_TarkovData.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Streets_TarkovData/Streets_TarkovData.jsonc index 1243ad7..b5ecea6 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Streets_TarkovData/Streets_TarkovData.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Streets_TarkovData/Streets_TarkovData.jsonc @@ -524,14 +524,6 @@ } ], "StaticMarkers": [ - { - "Text": "Basement Descent", - "ImagePath": "Markers/exit.png", - "Position": { "x": 74.769, "y": 49.22, "z": -0.701999664 }, - "Category": "Extract", - "ShowInRaid": false, - "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } - }, { "Text": "Entrance to Catacombs", "ImagePath": "Markers/exit.png", @@ -623,7 +615,7 @@ { "Text": "Sewer River", "ImagePath": "Markers/exit.png", - "Position": { "x": -264.267, "y": 223.92, "z": -1.535 }, + "Position": { "x": -267.04, "y": 219.48, "z": -1.535 }, "Category": "Extract", "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } @@ -653,7 +645,7 @@ "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, { - "Text": "Klimov Street", + "Text": "Klimov Street (Flare)", "ImagePath": "Markers/exit.png", "Position": { "x": -263.41, "y": 43.19, "z": 2.9 }, "Category": "Extract", @@ -668,6 +660,38 @@ "ShowInRaid": false, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } }, + { + "Text": "Transit to Ground Zero", + "ImagePath": "Markers/transit.png", + "Position": { "x": -260.05, "y": 103.645729, "z": 2.18500137 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to Interchange", + "ImagePath": "Markers/transit.png", + "Position": { "x": 286.670776, "y": 505.539978, "z": 3.39350128 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Transit to The Lab", + "ImagePath": "Markers/transit.png", + "Position": { "x": 206.955, "y": 82.194, "z": -8.382 }, + "Category": "Transit", + "ShowInRaid": false, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 } + }, + { + "Text": "Smugglers' Basement", + "ImagePath": "Markers/exit.png", + "Position": { "x": 75.99, "y": 50.0100021, "z": -0.6029999 }, + "Category": "Secret", + "ShowInRaid": false, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 } + }, { "Text": "MVD Academy Entrance Hall Guard Room", "ImagePath": "Markers/door_with_lock.png", diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Ground_Level.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Ground_Level.svg new file mode 100644 index 0000000..76662ec --- /dev/null +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Ground_Level.svg @@ -0,0 +1,789 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Second_Floor.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Second_Floor.svg new file mode 100644 index 0000000..b5ae5eb --- /dev/null +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Second_Floor.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Underground_Level.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Underground_Level.svg new file mode 100644 index 0000000..730373e --- /dev/null +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods-Underground_Level.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods.svg b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods.svg index e8f608c..0285f5a 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods.svg +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/SVG/Woods.svg @@ -1,499 +1,813 @@ - - - + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_-1.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_-1.png new file mode 100644 index 0000000..9d146fe Binary files /dev/null and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_-1.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_0.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_0.png index 081fb26..f97235f 100644 Binary files a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_0.png and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_0.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_1.png b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_1.png new file mode 100644 index 0000000..fe0167d Binary files /dev/null and b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Layers/woods_layer_1.png differ diff --git a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Woods_TarkovData.jsonc b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Woods_TarkovData.jsonc index 92fc942..7cf86b4 100644 --- a/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Woods_TarkovData.jsonc +++ b/mods/Dynamic Maps/BepInEx/plugins/DynamicMaps/Maps/Woods_TarkovData/Woods_TarkovData.jsonc @@ -5,24 +5,60 @@ "MapInternalNames": [ "Woods" ], "CoordinateRotation": 180, "Bounds": { - "Min": {"x": -692, "y": -915}, + "Min": {"x": -756, "y": -915}, "Max": {"x": 647, "y": 443} }, "DefaultLevel": 0, "Layers": { + "Underground": { + "Level": -1, + "ImagePath": "Maps/Woods_TarkovData/Layers/woods_layer_-1.png", + "ImageBounds": { + "Min": {"x": -756, "y": -915}, + "Max": {"x": 647, "y": 443} + }, + "GameBounds": [ + { // zb-14 + "Min": {"x": 435, "y": 55, "z": -100}, + "Max": {"x": 455, "y": 75, "z": -12.4} + }, + { // zb-16 + "Min": {"x": -399, "y": 9, "z": -100}, + "Max": {"x": -385, "y": 24, "z": 5.1} + }, + { // scav bunker + "Min": {"x": 211, "y": -715, "z": -100}, + "Max": {"x": 245, "y": -691, "z": 22.2} + } + ] + }, "Ground Level": { "Level": 0, "ImagePath": "Maps/Woods_TarkovData/Layers/woods_layer_0.png", "ImageBounds": { - "Min": {"x": -692, "y": -915}, + "Min": {"x": -756, "y": -915}, "Max": {"x": 647, "y": 443} }, "GameBounds": [ { - "Min": {"x": -692, "y": -915, "z": -100}, + "Min": {"x": -756, "y": -915, "z": -100}, "Max": {"x": 647, "y": 443, "z": 100} } ] + }, + "2nd Floor": { + "Level": 1, + "ImagePath": "Maps/Woods_TarkovData/Layers/woods_layer_1.png", + "ImageBounds": { + "Min": {"x": -756, "y": -915}, + "Max": {"x": 647, "y": 443} + }, + "GameBounds": [ + { // train depot + "Min": {"x": -671, "y": 108, "z": 9.6}, + "Max": {"x": -548, "y": 174, "z": 100} + } + ] } }, "Labels": [ @@ -105,21 +141,28 @@ "Text": "Convoy", "Position": {"x": 200, "y": -606, "z": 0}, "FontSize": 12 + }, + { + "Text": "ZB-016", + "Position": {"x": -397, "y": 18, "z": 6}, + "FontSize": 12 + }, + { + "Text": "ZB-014", + "Position": {"x": 438, "y": 65, "z": 0}, + "FontSize": 12 + }, + { + "Text": "Train Depot", + "Position": {"x": -657, "y": 138, "z": 0}, + "FontSize": 12 } ], "StaticMarkers": [ { - "Text": "Factory Gate (Co-Op)", + "Text": "Friendship Bridge (Co-Op)", "ImagePath": "Markers/exit.png", - "Position": { "x": -363.8, "y": 365.53, "z": 1.43 }, - "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 }, - "Category": "Extract", - "ShowInRaid": false - }, - { - "Text": "Scav Bridge", - "ImagePath": "Markers/exit.png", - "Position": { "x": 91.19565, "y": -855.499634, "z": 19.8512688 }, + "Position": { "x": 93.17, "y": -843.98, "z": 16.57 }, "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 }, "Category": "Extract", "ShowInRaid": false @@ -223,7 +266,7 @@ { "Text": "Outskirts", "ImagePath": "Markers/exit.png", - "Position": { "x": 347.315643, "y": 347.0304, "z": -9.148731 }, + "Position": { "x": 347.15567, "y": 360.0704, "z": -11.15 }, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 }, "Category": "Extract", "ShowInRaid": false @@ -231,11 +274,59 @@ { "Text": "Northern UN Roadblock", "ImagePath": "Markers/exit.png", - "Position": { "x": -561.1143, "y": -58.7096, "z": 9.321269 }, + "Position": { "x": -557.324341, "y": -67.0496, "z": 9.321269 }, "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 }, "Category": "Extract", "ShowInRaid": false }, + { + "Text": "Power Line Passage (Flare)", + "ImagePath": "Markers/exit.png", + "Position": { "x": 573.985657, "y": -85.2596, "z": -1.7487309 }, + "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 }, + "Category": "Extract", + "ShowInRaid": false + }, + { + "Text": "Transit to Factory", + "ImagePath": "Markers/transit.png", + "Position": { "x": -362.45, "y": 360.7704, "z": 0.821269155 }, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + "Category": "Transit", + "ShowInRaid": false + }, + { + "Text": "Transit to Reserve", + "ImagePath": "Markers/transit.png", + "Position": { "x": 246.06, "y": 369.9, "z": -10.22 }, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + "Category": "Transit", + "ShowInRaid": false + }, + { + "Text": "Transit to Lighthouse", + "ImagePath": "Markers/transit.png", + "Position": { "x": 494.34, "y": 345.37, "z": -16.77 }, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + "Category": "Transit", + "ShowInRaid": false + }, + { + "Text": "Transit to Customs", + "ImagePath": "Markers/transit.png", + "Position": { "x": -153.05, "y": 402.13, "z": 1.07 }, + "Color": { "r": 1.0, "g": 0.368627429, "b": 0.00627450971, "a": 1.0 }, + "Category": "Transit", + "ShowInRaid": false + }, + { + "Text": "Railway Bridge to Tarkov (Secret)", + "ImagePath": "Markers/exit.png", + "Position": { "x": -730.43, "y": 129.32, "z": 8.7 }, + "Color": { "r": 0.1, "g": 0.6, "b": 0.6, "a": 1.0 }, + "Category": "Secret", + "ShowInRaid": false + }, { "Text": "Yotota Car", "ImagePath": "Markers/car_door.png", @@ -259,14 +350,22 @@ "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, "Category": "LockedDoor", "AssociatedItemId": "664d3db6db5dea2bad286955" - } + }, // { - // "Text": "Shtrman's Stash", + // "Text": "Shturman's Stash", // "ImagePath": "Markers/door_with_lock.png", // "Position": { "x": 25.1087952, "y": -29.55574, "z": -1.47199988 }, // "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, // "Category": "LockedDoor", // "AssociatedItemId": "5d08d21286f774736e7c94c3" - // } + // }, + { + "Text": "Old House", + "ImagePath": "Markers/door_with_lock.png", + "Position": { "x": -655.1675, "y": 183.086014, "z": 8.68574 }, + "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0 }, + "Category": "LockedDoor", + "AssociatedItemId": "6761a6ccd9bbb27ad703c48a" + } ] } diff --git a/mods/Dynamic Maps/meta.ini b/mods/Dynamic Maps/meta.ini index 8f05b1c..bfea840 100644 --- a/mods/Dynamic Maps/meta.ini +++ b/mods/Dynamic Maps/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.16.0 +version=d2025.5.13.0 newestVersion= category="2," nexusFileStatus=1 -installationFile=DynamicMaps-0.5.2.zip +installationFile=DynamicMaps-0.5.7-ba475092.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Dynamic Maps/user/mods/SPTDynamicMaps/.buildignore b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/.buildignore new file mode 100644 index 0000000..2cbde65 --- /dev/null +++ b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/.buildignore @@ -0,0 +1,20 @@ +/.buildignore +/.DS_Store +/.editorconfig +/.eslintignore +/.eslintrc.json +/.git +/.github +/.gitignore +/.gitlab +/.nvmrc +/.prettierrc +/.vscode +/build.mjs +/dist +/images +/mod.code-workspace +/node_modules +/package-lock.json +/tsconfig.json +/types diff --git a/mods/Dynamic Maps/user/mods/SPTDynamicMaps/package.json b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/package.json new file mode 100644 index 0000000..a00d68c --- /dev/null +++ b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/package.json @@ -0,0 +1,37 @@ +{ + "name": "SPT-DynamicMaps", + "version": "0.5.7", + "sptVersion": "~3.11", + "loadBefore": [], + "loadAfter": [], + "incompatibilities": [], + "isBundleMod": false, + "main": "src/mod.js", + "scripts": { + "setup": "npm i", + "build": "node ./packageBuild.ts", + "buildinfo": "node ./packageBuild.ts --verbose" + }, + "devDependencies": { + "@types/node": "20.11", + "@typescript-eslint/eslint-plugin": "7.2", + "@typescript-eslint/parser": "7.2", + "archiver": "^6.0", + "eslint": "8.57", + "fs-extra": "11.2", + "ignore": "^5.2", + "tsyringe": "4.8.0", + "typescript": "5.4", + "winston": "3.12", + "bestzip": "^2.2.1" + }, + "author": "mpstark", + "contributors": [ + "dirtbikercj", + "AcidPhantasm" + ], + "license": "MIT", + "dependencies": { + + } +} diff --git a/mods/Dynamic Maps/user/mods/SPTDynamicMaps/src/InstanceManager.ts b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/src/InstanceManager.ts new file mode 100644 index 0000000..76be7be --- /dev/null +++ b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/src/InstanceManager.ts @@ -0,0 +1,82 @@ +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { DatabaseServer } from "@spt/servers/DatabaseServer"; +import type { IDatabaseTables } from "@spt/models/spt/server/IDatabaseTables"; +import type { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; +import type { DependencyContainer } from "tsyringe"; +import type { CustomItemService } from "@spt/services/mod/CustomItemService"; +import type { ImageRouter } from "@spt/routers/ImageRouter"; +import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import type { ConfigServer } from "@spt/servers/ConfigServer"; +import type { JsonUtil } from "@spt/utils/JsonUtil"; +import type { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import type { RagfairPriceService } from "@spt/services/RagfairPriceService"; +import type { ImporterUtil } from "@spt/utils/ImporterUtil"; +import type { SaveServer } from "@spt/servers/SaveServer"; +import type { ItemHelper } from "@spt/helpers/ItemHelper"; +import type { MailSendService } from "@spt/services/MailSendService"; +import type { HashUtil } from "@spt/utils/HashUtil"; +import type { TraderHelper } from "@spt/helpers/TraderHelper"; +import type { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; +import type { FileSystemSync } from "@spt/utils/FileSystemSync"; + +export class InstanceManager +{ + //#region Accessible in or after preAkiLoad + private alpha = false; + private version = ""; + + // Instances + public container: DependencyContainer; + public preSptModLoader: PreSptModLoader; + public configServer: ConfigServer; + public saveServer: SaveServer; + public itemHelper: ItemHelper; + public logger: ILogger; + public staticRouter: StaticRouterModService; + public fs: FileSystemSync; + public hashUtil: HashUtil; + public httpResponseUtil: HttpResponseUtil; + //#endregion + + //#region Acceessible in or after postDBLoad + public database: IDatabaseTables; + public customItem: CustomItemService; + public imageRouter: ImageRouter; + public jsonUtil: JsonUtil; + public profileHelper: ProfileHelper; + public ragfairPriceService: RagfairPriceService; + public importerUtil: ImporterUtil; + public customItemService: CustomItemService; + public mailSendService: MailSendService; + public traderHelper: TraderHelper; + //#endregion + + // Call at the start of the mods postDBLoad method + public preSptLoad(container: DependencyContainer): void + { + this.container = container; + this.preSptModLoader = container.resolve("PreSptModLoader"); + this.imageRouter = container.resolve("ImageRouter"); + this.configServer = container.resolve("ConfigServer"); + this.saveServer = container.resolve("SaveServer"); + this.itemHelper = container.resolve("ItemHelper"); + this.logger = container.resolve("WinstonLogger"); + this.staticRouter = container.resolve("StaticRouterModService"); + this.fs = container.resolve("FileSystemSync"); + this.hashUtil = container.resolve("HashUtil"); + this.httpResponseUtil = container.resolve("HttpResponseUtil"); + } + + public postDBLoad(container: DependencyContainer): void + { + this.database = container.resolve("DatabaseServer").getTables(); + this.customItem = container.resolve("CustomItemService"); + this.jsonUtil = container.resolve("JsonUtil"); + this.profileHelper = container.resolve("ProfileHelper"); + this.ragfairPriceService = container.resolve("RagfairPriceService"); + this.importerUtil = container.resolve("ImporterUtil"); + this.customItemService = container.resolve("CustomItemService"); + this.mailSendService = container.resolve("MailSendService"); + this.traderHelper = container.resolve("TraderHelper"); + } +} \ No newline at end of file diff --git a/mods/Dynamic Maps/user/mods/SPTDynamicMaps/src/mod.ts b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/src/mod.ts new file mode 100644 index 0000000..be0886a --- /dev/null +++ b/mods/Dynamic Maps/user/mods/SPTDynamicMaps/src/mod.ts @@ -0,0 +1,208 @@ +import type { DependencyContainer } from "tsyringe"; +import type { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod"; +import type { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; +import type { Item } from "@spt/models/eft/common/tables/IItem"; +import type { IBarterScheme } from "@spt/models/eft/common/tables/ITrader"; +import type { NewItemFromCloneDetails } from "@spt/models/spt/mod/NewItemDetails"; +import { Traders } from "@spt/models/enums/Traders"; +import { InstanceManager } from "./InstanceManager"; + +class DynamicMaps implements IPreSptLoadMod, IPostDBLoadMod +{ + // eslint-disable-next-line @typescript-eslint/naming-convention + protected InstanceManager: InstanceManager = new InstanceManager(); + + public preSptLoad(container: DependencyContainer): void + { + this.InstanceManager.preSptLoad(container); + } + + public postDBLoad(container: DependencyContainer): void + { + this.InstanceManager.postDBLoad(container); + + this.createNewMaps(); + } + + private createNewMaps(): void + { + this.createGroundZeroMap(); + this.createStreetsMap(); + this.createReserveMap(); + this.createLabsMap(); + this.createLighthouseMap(); + } + + private createGroundZeroMap(): void + { + const gzMap: NewItemFromCloneDetails = + { + itemTplToClone: "5900b89686f7744e704a8747", + parentId: "567849dd4bdc2d150f8b456e", + newId: "6738033eb7305d3bdafe9518", + fleaPriceRoubles: 25000, + handbookPriceRoubles: 32500, + handbookParentId: "5b47574386f77428ca22b343", + overrideProperties: { + + }, + locales: { + en: { + name: "Ground Zero plan map", + shortName: "Ground Zero", + description: "" + } + } + } + + const assortId = "6738076415fd9232e8dae982"; + + this.InstanceManager.customItemService.createItemFromClone(gzMap); + this.pushToTraderAssort(Traders.THERAPIST, gzMap.newId, gzMap.handbookPriceRoubles, assortId); + } + + private createStreetsMap(): void + { + const streetsMap: NewItemFromCloneDetails = + { + itemTplToClone: "5900b89686f7744e704a8747", + parentId: "567849dd4bdc2d150f8b456e", + newId: "673803448cb3819668d77b1b", + fleaPriceRoubles: 25000, + handbookPriceRoubles: 32500, + handbookParentId: "5b47574386f77428ca22b343", + overrideProperties: { + + }, + locales: { + en: { + name: "Streets of Tarkov plan", + shortName: "Streets", + description: "" + } + } + } + + const assortId = "67380769ebda082cf01c3fd7"; + + this.InstanceManager.customItemService.createItemFromClone(streetsMap); + this.pushToTraderAssort(Traders.THERAPIST, streetsMap.newId, streetsMap.handbookPriceRoubles, assortId); + } + + private createReserveMap(): void + { + const reserveMap: NewItemFromCloneDetails = + { + itemTplToClone: "5900b89686f7744e704a8747", + parentId: "567849dd4bdc2d150f8b456e", + newId: "6738034a9713b5f42b4a8b78", + fleaPriceRoubles: 25000, + handbookPriceRoubles: 32500, + handbookParentId: "5b47574386f77428ca22b343", + overrideProperties: { + + }, + locales: { + en: { + name: "Reserve plan", + shortName: "Reserve", + description: "" + } + } + } + + const assortId = "6738076e704fef20a1a580e6"; + + this.InstanceManager.customItemService.createItemFromClone(reserveMap); + this.pushToTraderAssort(Traders.THERAPIST, reserveMap.newId, reserveMap.handbookPriceRoubles, assortId); + } + + private createLabsMap(): void + { + const labsMap: NewItemFromCloneDetails = + { + itemTplToClone: "5900b89686f7744e704a8747", + parentId: "567849dd4bdc2d150f8b456e", + newId: "6738034e9d22459ad7cd1b81", + fleaPriceRoubles: 25000, + handbookPriceRoubles: 32500, + handbookParentId: "5b47574386f77428ca22b343", + overrideProperties: { + + }, + locales: { + en: { + name: "Labs plan", + shortName: "Labs", + description: "" + } + } + } + + const assortId = "673807742ef49729b9dd1b0a"; + + this.InstanceManager.customItemService.createItemFromClone(labsMap); + this.pushToTraderAssort(Traders.THERAPIST, labsMap.newId, labsMap.handbookPriceRoubles, assortId); + } + + private createLighthouseMap(): void + { + const lighthouseMap: NewItemFromCloneDetails = + { + itemTplToClone: "5900b89686f7744e704a8747", + parentId: "567849dd4bdc2d150f8b456e", + newId: "6738035350b24a4ae4a57997", + fleaPriceRoubles: 25000, + handbookPriceRoubles: 32500, + handbookParentId: "5b47574386f77428ca22b343", + overrideProperties: { + + }, + locales: { + en: { + name: "Lighthouse plan", + shortName: "Lighthouse", + description: "" + } + } + } + + const assortId = "6738077be5a03fda63c9917d"; + + this.InstanceManager.customItemService.createItemFromClone(lighthouseMap); + this.pushToTraderAssort(Traders.THERAPIST, lighthouseMap.newId, lighthouseMap.handbookPriceRoubles, assortId); + } + + private pushToTraderAssort(traderId: Traders, itemId: string, price: number, assortId: string): void + { + const assort = this.InstanceManager.database.traders[traderId].assort; + + const item: Item = { + _id: assortId, + _tpl: itemId, + parentId: "hideout", + slotId: "hideout", + upd: { + UnlimitedCount: false, + StackObjectsCount: 4, + BuyRestrictionMax: 10, + BuyRestrictionCurrent: 0 + } + } + + const scheme: IBarterScheme[][] = [ + [ + { + count: price, + _tpl: "5449016a4bdc2d6f028b456f" + } + ] + ]; + + assort.items.push(item); + assort.barter_scheme[assortId] = scheme; + assort.loyal_level_items[assortId] = 1; + } +} + +export const mod = new DynamicMaps(); diff --git a/mods/Fika Core/BepInEx/plugins/Fika.Core.dll b/mods/Fika Core/BepInEx/plugins/Fika.Core.dll index c327738..c55db85 100644 Binary files a/mods/Fika Core/BepInEx/plugins/Fika.Core.dll and b/mods/Fika Core/BepInEx/plugins/Fika.Core.dll differ diff --git a/mods/Fika Core/meta.ini b/mods/Fika Core/meta.ini index ff72077..7e41cec 100644 --- a/mods/Fika Core/meta.ini +++ b/mods/Fika Core/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.10.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=Fika.Release.1.1.5.0.zip +installationFile=Fika.Release.1.2.8.0.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Fika Dedicated/BepInEx/plugins/Fika.Dedicated.dll b/mods/Fika Dedicated/BepInEx/plugins/Fika.Dedicated.dll deleted file mode 100644 index 0938fe4..0000000 Binary files a/mods/Fika Dedicated/BepInEx/plugins/Fika.Dedicated.dll and /dev/null differ diff --git a/mods/Fika Dedicated/BepInEx/plugins/Fika.Headless.dll b/mods/Fika Dedicated/BepInEx/plugins/Fika.Headless.dll new file mode 100644 index 0000000..1699394 Binary files /dev/null and b/mods/Fika Dedicated/BepInEx/plugins/Fika.Headless.dll differ diff --git a/mods/Fika Dedicated/User/Mods/Fika Dedicated License.md b/mods/Fika Dedicated/User/Mods/Fika Headless License.md similarity index 100% rename from mods/Fika Dedicated/User/Mods/Fika Dedicated License.md rename to mods/Fika Dedicated/User/Mods/Fika Headless License.md diff --git a/mods/Fika Dedicated/meta.ini b/mods/Fika Dedicated/meta.ini index 1d77be4..10456f2 100644 --- a/mods/Fika Dedicated/meta.ini +++ b/mods/Fika Dedicated/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.12.0 +version=d2025.5.13.0 newestVersion= category="1," nexusFileStatus=1 -installationFile=Fika.Dedicated.1.2.4.zip +installationFile=Fika.Headless.1.3.7.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Fika Server/meta.ini b/mods/Fika Server/meta.ini index e2083b5..36662a0 100644 --- a/mods/Fika Server/meta.ini +++ b/mods/Fika Server/meta.ini @@ -1,12 +1,12 @@ [General] gameName=spt modid=0 -version=d2025.1.4.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=fika-server.zip -repository= +installationFile=fika-server-2.4.8.zip +repository=Nexus ignoredVersion= comments= notes= diff --git a/mods/Fika Server/user/mods/fika-server/README.md b/mods/Fika Server/user/mods/fika-server/README.md index 89e53dd..d1f09b4 100644 --- a/mods/Fika Server/user/mods/fika-server/README.md +++ b/mods/Fika Server/user/mods/fika-server/README.md @@ -11,7 +11,7 @@ Server-side changes to make multiplayer work. ## Requirements -- [Node 20.x](https://nodejs.org/en/download) +- [Node 22.x](https://nodejs.org/en/download) ## Build diff --git a/mods/Fika Server/user/mods/fika-server/assets/configs/fika.jsonc b/mods/Fika Server/user/mods/fika-server/assets/configs/fika.jsonc deleted file mode 100644 index 89d36a2..0000000 --- a/mods/Fika Server/user/mods/fika-server/assets/configs/fika.jsonc +++ /dev/null @@ -1,45 +0,0 @@ -{ - "client": { - "useBtr": true, - "friendlyFire": true, - "dynamicVExfils": false, - "allowFreeCam": false, - "allowSpectateFreeCam": false, - "allowItemSending": true, - "blacklistedItems": [], - "forceSaveOnDeath": false, - "mods": { - "required": [], - "optional": [] - }, - "useInertia": true, - "sharedQuestProgression": false, - "canEditRaidSettings": true, - "enableTransits": true - }, - "server": { - "giftedItemsLoseFIR": true, - "launcherListAllProfiles": false, - "sessionTimeout": 5, - "showDevProfile": false, - "showNonStandardProfile": false - }, - "natPunchServer": { - "enable": false, - "port": 6790, - "natIntroduceAmount": 1 - }, - "dedicated": { - "profiles": { - "amount": 0 - }, - "scripts": { - "generate": true, - "forceIp": "" - } - }, - "background": { - "enable": true, - "easteregg": false - } -} \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ch.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ch.json index 592a89f..f92008a 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ch.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ch.json @@ -43,9 +43,10 @@ "F_MMUI_StartButton": "开始", "F_MMUI_LoadingScreenHeader": "加载中", "F_MMUI_LoadingScreenDescription": "请稍候,正在初始化... 这可能需要一些时间。", - "F_MMUI_JoinAsSpectator": "观战", - "F_UI_StartRaid": "开始战局", - "F_UI_StartRaidDescription": "启动战局\n后续玩家将无法进入", + "F_MMUI_JoinAsSpectator": "旁观者模式", + "F_MMUI_JoinAsSpectator_D": "以旁观者模式加入游戏", + "F_UI_StartRaid": "启动战局", + "F_UI_StartRaidDescription": "开始战局\n点击此附加项后其他玩家不能加入", "F_UI_CannotJoinRaidOtherMap": "不可以加入另一个地图的行动\n行动地图: {0}", "F_UI_CannotJoinRaidOtherTime": "不可以加入另一个时间的行动.", "F_UI_CannotJoinRaidScavAsPMC": "你不能以PMC身份加入SCAV行动", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "错误绑定", "F_UI_ErrorBindIP": "{0} 不是一个有效的绑定IP地址!请检查“强制IP”设置", "F_UI_NoDedicatedClients": "现在没有可用的专用客户端。", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "开始战局\n其他玩家能够在加载战局状态时加入", + "F_UI_WaitForHostStartRaid": "正在等待主机开始战局...", "F_UI_WaitForHostFinishInit": "正在等待主机完成战局初始化", "F_UI_WaitForOtherPlayers": "正在等待其他玩家完成加载", "F_UI_RetrieveSpawnInfo": "正在从服务器获取生成信息...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "游戏模式", "F_BepInEx_OfficialVersion_T": "官方版本", "F_BepInEx_OfficialVersion_D": "显示官方版本而不是Fika版本。", + "F_BepInEx_UseHeadless_T": "自动使用Headless", + "F_BepInEx_UseHeadless_D": "如果可用请保持勾选来使用无Headless的客户端", "F_BepInEx_ShowFeed_T": "显示战局信息", "F_BepInEx_ShowFeed_D": "启用玩家死亡、撤离、击杀BOSS时的自定义通知", "F_BepInEx_AutoExtract_T": "自动撤离", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "最小姓名牌尺寸", "F_BepInEx_UseOcclusion_T": "使用遮挡功能", "F_BepInEx_UseOcclusion_D": "使用遮挡后,当玩家不在视野范围内时隐藏名称板", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "任务类型", "F_BepInEx_QuestTypes_D": "要接收和发送的任务类型。PlaceBeacon 包含了标记和物品任务", "F_BepInEx_QSNotifications_T": "显示通知", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "动态AI应该多久扫描一次所有玩家的范围", "F_BepInEx_DynamicAINoSnipers_T": "忽略狙击手", "F_BepInEx_DynamicAINoSnipers_D": "动态AI是否应该忽略狙击手Scav。", + "F_BepInEx_FikaGC_T": "使用 Fika GC", + "F_BepInEx_FikaGC_D": "如果使用自定义垃圾收集器来缓解内存使用问题。\n警告:这是一个实验性功能,可能对您的系统没有任何影响。", "F_BepInEx_EnforcedSpawnLimits_T": "强制生成上限", "F_BepInEx_EnforcedSpawnLimits_D": "在生成机器人时强制生成上限,确保不会超过原版的极限。 这主要会影响到使用生成模组或修改机器人限制的任何东西。不会阻止像boss这样的特殊机器人的生成。", "F_BepInEx_DespawnFurthest_T": "取消最远生成", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "连接超时", "F_BepInEx_ConnectionTimeout_D": "如果没有收到数据包,判定连接丢失所需的时间。", "F_BepInEx_SendRate_T": "发送速率", - "F_BepInEx_SendRate_D": "每秒发送移动数据包的频率(值越低=使用的带宽越少,但插值期间的延迟略高)。\n此设置仅影响主机,并会同步到所有客户端。\n单位为每秒发送次数:\n\n非常低 = 10\n低 = 15\n中 = 20\n高 = 30\n\n建议保持在不高于\"中\"的设置,因为更高的设置带来的性能提升微乎其微。", - "F_BepInEx_SmoothingRate_T": "平滑速率", - "F_BepInEx_SmoothingRate_D": "本地模拟延迟为(发送速率 * 平滑速率)。这样可以确保缓冲区中始终有足够的快照,以在插值期间减轻延迟和抖动的影响。\n\n低 = 1.5 \n中 = 2 \n高 = 2.5 \n\n如果运动不流畅,将此设置为“高”。无法在突袭过程中更改。", + "F_BepInEx_SendRate_D": "每秒发送移动数据包的频率(值越低=使用的带宽越少,但插值过程中延迟稍大)。\n此设置仅影响主机,并会同步到所有客户端。\n每秒发送次数:\n\n低 = 10\n中 = 20\n高 = 30\n\n建议保持使用不高于\"中\"的设置,因为更高的设置带来的性能提升几乎微乎其微。", + "F_BepInEx_NetMultiThread_T": "使用多线程技术", + "F_BepInEx_NetMultiThread_D": "是否启用多线程网络组件。这可能会缓解游戏延迟时的问题,使网络管理器在低FPS期间仍能处理和排队数据包。\n这是一个实验性功能,只能在高端PC上启用,并且只会在无头客户端上启用。", + "F_BepInEx_VOIP_T": "允许VOIP通话", + "F_BepInEx_VOIP_D": "是否在您主持游戏时启动VOIP通话。", "F_BepInEx_DisableBotMetabolism_T": "禁用AI新陈代谢", "F_BepInEx_DisableBotMetabolism_D": "禁用AI新陈代谢,防止其在长时间战局中因饥饿/脱水而死亡", "F_MMUI_OnlinePlayers": "在线玩家: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "在藏身处中", "F_MMUI_InFlea": "在跳蚤市场中", "F_MMUI_RaidDetails": "作为 {0} 在 {1} 上游玩\n时间:{2}", + "F_Mod_VerificationFailure": "Fika.Core.dll 文件中的\"Crc32\"检验操作失败,您无法与其他人一起联机游玩!\n请查看您的日志文件以获取更多信息。", "F_UI_LocaleError_H": "语言文件错误", - "F_UI_LocaleError_D": "加载Fika语言时发生错误。现在正在为失效的翻译条目切换为后备模式。 请将此报告给开发者!\n\n失效的文本键值: {0}" + "F_UI_LocaleError_D": "加载Fika语言时发生错误。现在正在为失效的翻译条目切换为后备模式。 请将此报告给开发者!\n\n失效的文本键值: {0}", + "F_UI_EFTVersionError": "你在尝试使用与运行战局的服务器主机版本不同的EFT\n客户端:{0}\n主机:{1}", + "F_UI_FikaVersionError": "你在尝试使用与运行战局的服务器主机版本不同的Fika\n客户端:{0}\n主机:{1}", + "F_UI_PlayerConnected": "{0} 已连接", + "F_UI_PlayerDisconnected": "{0} 已断开连接", + "F_UI_DisabledByServerHost": "服务器已禁用" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/cz.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/cz.json index 8b829c4..652857c 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/cz.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/cz.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "Založit RAID", "F_MMUI_JoinButton": "PŘIPOJIT", "F_MMUI_SelectAmountHeader": "VYBERTE POČET", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "NASTAVENÍ RELACE", "F_MMUI_SelectAmountDescription": "Kolik lidí (včetně vás) bude hrát?", "F_MMUI_UseDedicatedHost": "Použít Dedikovaného Hosta", "F_MMUI_StartButton": "SPUSTIT", "F_MMUI_LoadingScreenHeader": "NAČÍTÁNÍ", "F_MMUI_LoadingScreenDescription": "Počkejte prosím, než se raid spustí... může to chvíli trvat.", - "F_MMUI_JoinAsSpectator": "Join as spectator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Přihlížet", + "F_MMUI_JoinAsSpectator_D": "Připojit se do hry jako přihlížející", + "F_UI_StartRaid": "Začít Raid", + "F_UI_StartRaidDescription": "Začne Raid\nPo kliknutí nebude možné připojení dalších hráčů", "F_UI_CannotJoinRaidOtherMap": "Nelze se připojit k raidu na jiné mapě.\nMapa raidu: {0}", "F_UI_CannotJoinRaidOtherTime": "Nelze se připojit k raidu v jiném čase.", "F_UI_CannotJoinRaidScavAsPMC": "Ke scav raidu se jako PMC nemůžete připojit.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "CHYBA BINDOVÁNÍ", "F_UI_ErrorBindIP": "{0} není platná IP adresa pro připojení! Zkontrolujte nastavení 'Vynutit Bindování IP'.", "F_UI_NoDedicatedClients": "Dedikovaný klient není k dispozici.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Začít Raid\nDalší hráči se budou moci připojit během načítací obrazovky", + "F_UI_WaitForHostStartRaid": "Čeká se na hosta, než zapne hru...", "F_UI_WaitForHostFinishInit": "Čekáme na zakladatele hry aby dokončil raid inicializaci...", "F_UI_WaitForOtherPlayers": "Čekání na ostatní hráče co se načítají...", "F_UI_RetrieveSpawnInfo": "Obdržuji informace o spawnu ze serveru...", @@ -91,55 +93,57 @@ "F_UI_WaitForPlayers": "Čekání na {0} hráčů", "F_UI_ToSLong": "Vítejte u Fika!\n\nFika je kooperativní mod pro SPT, který vám umožňuje hrát s přáteli. Fika je a vždy bude zdarma, pokud jste za ni zaplatili, byli jste podvedeni. Také není dovoleno provozovat veřejné servery s monetizací nebo přijímáním darů.\n\n\"Počkejte, až tato zpráva zmizí, abyste přijali naše podmínky služby.\"\n\nMůžete se připojit na náš Discord zde: https://discord.gg/project-fika", "F_UI_ToSShort": "Vítejte u Fika!\n\nFika je kooperativní mod pro SPT, který vám umožňuje hrát s přáteli. Fika je a vždy bude zdarma, pokud jste za ni zaplatili, byli jste podvedeni. Také není dovoleno provozovat veřejné servery s monetizací nebo přijímáním darů.\n\nMůžete se připojit na náš Discord zde: https://discord.gg/project-fika", - "F_UI_RaidSettingsDescription": "In this mode, you can play online co-op with your friends without the risk of meeting others.\nAll game mechanics in the co-op mode (weather, bots, bosses, etc.) are identical to the online mechanics.\nOnly the hosts settings will take effect.", - "F_UI_CoopGameMode": "Fika Co-op Game Mode", - "F_UI_CoopRaidSettings": "Fika Co-op Raid Settings", - "F_UI_FikaAlwaysCoop": "Co-op is always enabled in Fika", - "F_UI_UpnpFailed": "UPnP mapping failed. Make sure the selected port is not already open!\nDisable UPnP if you are using a VPN.", - "F_UI_InitWeather": "Generating weather...", - "F_Notification_RaidStarted": "{0} started a raid on {1}", - "F_Notification_ItemReceived": "You received a {0} from {1}", - "F_Notification_RaidSettingsDisabled": "Editing of RaidSettings is disabled by the server host", - "F_UI_ExtractMessage": "Press {0} to extract", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", - "F_BepInEx_H_Advanced": "Advanced", + "F_UI_RaidSettingsDescription": "V tomto režimu můžete hrát online co-op se svými přáteli, bez rizika setkání se s ostatními.\nVšechny herní mechaniky v co-op režimu (počasí, boti, bosové, atd..) jsou identické k online mechanikám.\nBude použito pouze nastavení hostitele.", + "F_UI_CoopGameMode": "Herní mód Fika Co-op", + "F_UI_CoopRaidSettings": "Fika Co-op Raid Nastavení", + "F_UI_FikaAlwaysCoop": "Ve Fika je co-op vždy povolen", + "F_UI_UpnpFailed": "UPnP mapování se nezdařilo. Ujistěte se že jste vybral port, který ještě není otevřený! Vypněte UPnP pokud používáte VPN.", + "F_UI_InitWeather": "Generuju počasí...", + "F_Notification_RaidStarted": "{0} spustil raid na {1}", + "F_Notification_ItemReceived": "Dostal jsi {0} od {1}", + "F_Notification_RaidSettingsDisabled": "Hostitel serveru vypnul upravování nastavení Raidu", + "F_UI_ExtractMessage": "Zmáčkni {0} abys extrahoval", + "F_UI_DownloadProfile": "STÁHNOUT PROFIL", + "F_BepInEx_H_Advanced": "Pokročilé", "F_BepInEx_H_Coop": "Coop", - "F_BepInEx_H_Coop_NamePlates": "Coop | Name Plates", - "F_BepInEx_H_Coop_QuestSharing": "Coop | Quest Sharing", - "F_BepInEx_H_Coop_Pinging": "Coop | Pinging", - "F_BepInEx_H_Coop_Debug": "Coop | Debug", - "F_BepInEx_H_Performance": "Performance", - "F_BepInEx_H_PerformanceBots": "Performance | Max Bots", - "F_BepInEx_H_Network": "Network", - "F_BepInEx_H_Gameplay": "Gameplay", - "F_BepInEx_OfficialVersion_T": "Official Version", - "F_BepInEx_OfficialVersion_D": "Show official version instead of Fika version.", - "F_BepInEx_ShowFeed_T": "Show Feed", - "F_BepInEx_ShowFeed_D": "Enable custom notifications when a player dies, extracts, kills a boss, etc.", - "F_BepInEx_AutoExtract_T": "Auto Extract", - "F_BepInEx_AutoExtract_D": "Automatically extracts after the extraction countdown. As a host, this will only work if there are no clients connected.", - "F_BepInEx_ShowExtractMessage_T": "Show Extract Message", - "F_BepInEx_ShowExtractMessage_D": "Whether to show the extract message after dying/extracting.", - "F_BepInEx_ExtractKey_T": "Extract Key", - "F_BepInEx_ExtractKey_D": "The key used to extract from the raid.", - "F_BepInEx_EnableChat_T": "Enable Chat", - "F_BepInEx_EnableChat_D": "Toggle to enable chat in game. Cannot be change mid raid.", - "F_BepInEx_ChatKey_T": "Chat Key", - "F_BepInEx_ChatKey_D": "The key used to open the chat window.", - "F_BepInEx_EnableOnlinePlayers_T": "Enable Online Players", - "F_BepInEx_EnableOnlinePlayers_D": "If the online players menu should be shown in the menu.", - "F_BepInEx_OnlinePlayersScale_T": "Online Players Scale", - "F_BepInEx_OnlinePlayersScale_D": "The scale of the window that displays online players. Only change if it looks out of proportion.\n\nRequires a refresh of the main menu to take effect.", - "F_BepInEx_UseNamePlates_T": "Show Player Name Plates", - "F_BepInEx_UseNamePlates_D": "If name plates should be shown above teammates.", - "F_BepInEx_HideHealthBar_T": "Hide Health Bar", - "F_BepInEx_HideHealthBar_D": "Completely hides the health bar.", - "F_BepInEx_UsePercent_T": "Show HP% instead of bar", - "F_BepInEx_UsePercent_D": "Shows health in % amount instead of using the bar.", - "F_BepInEx_ShowEffects_T": "Show Effects", - "F_BepInEx_ShowEffects_D": "If status effects should be displayed below the health bar.", - "F_BepInEx_ShowFactionIcon_T": "Show Player Faction Icon", - "F_BepInEx_ShowFactionIcon_D": "Shows the player faction icon next to the HP bar.", + "F_BepInEx_H_Coop_NamePlates": "Coop | Štítky jmen", + "F_BepInEx_H_Coop_QuestSharing": "Coop | Sdílení úkolů", + "F_BepInEx_H_Coop_Pinging": "Coop | Vyznačení", + "F_BepInEx_H_Coop_Debug": "Coop | Debugování", + "F_BepInEx_H_Performance": "Výkon", + "F_BepInEx_H_PerformanceBots": "Výkon | Maximum Botů", + "F_BepInEx_H_Network": "Síť", + "F_BepInEx_H_Gameplay": "Hra", + "F_BepInEx_OfficialVersion_T": "Oficiální verze", + "F_BepInEx_OfficialVersion_D": "Zobrazit oficální verzi místo Fika verze.", + "F_BepInEx_UseHeadless_T": "Automaticky použij Headless", + "F_BepInEx_UseHeadless_D": "Pokud je to možné zaškrtni pole pro použití Headless klienta", + "F_BepInEx_ShowFeed_T": "Zobrazit Feed", + "F_BepInEx_ShowFeed_D": "Zapne oznámení když hráč umře, extrahuje, zabije bosse, atd.", + "F_BepInEx_AutoExtract_T": "Automaticky Extrahovat", + "F_BepInEx_AutoExtract_D": "Automaticky extrahovat poté co skončí odpočet. Toto bude pro hostitele fungovat pouze když nejsou připojeni žádní klienti.", + "F_BepInEx_ShowExtractMessage_T": "Zobrazit Zprávu Extrahování", + "F_BepInEx_ShowExtractMessage_D": "Zobrazení zprávy o extrahování po smrti/extrahování", + "F_BepInEx_ExtractKey_T": "Extrahovací Tlačítko", + "F_BepInEx_ExtractKey_D": "Tlačítko pro extrahování z raidu.", + "F_BepInEx_EnableChat_T": "Povolit Chat", + "F_BepInEx_EnableChat_D": "Přepínač k povolování chatu ve hře. Nelze změnit během raidu.", + "F_BepInEx_ChatKey_T": "Tlačítko Chatu", + "F_BepInEx_ChatKey_D": "Tlačítko používané k otevření chatovacího okna", + "F_BepInEx_EnableOnlinePlayers_T": "Povolit Online Hráče", + "F_BepInEx_EnableOnlinePlayers_D": "Zobrazení tabulky online hráčů v menu.", + "F_BepInEx_OnlinePlayersScale_T": "Měřítko okna online hráčů", + "F_BepInEx_OnlinePlayersScale_D": "Měřítko okna online hráčů v menu. Změňte pouze pokud okno vypadá divně.\n\nJe potřeba aktualizovat hlavní menu, aby se změna mohla projevit.", + "F_BepInEx_UseNamePlates_T": "Zobrazit Štítky Jmen Hráčů", + "F_BepInEx_UseNamePlates_D": "Zobrazení štítků se jmény nad spoluhráčema.", + "F_BepInEx_HideHealthBar_T": "Schovat lištu s životy.", + "F_BepInEx_HideHealthBar_D": "Zcela skrýt lištu s životy.", + "F_BepInEx_UsePercent_T": "Zobrazit % životů místo lišty", + "F_BepInEx_UsePercent_D": "Zobrazit životy v % místo používání lišty.", + "F_BepInEx_ShowEffects_T": "Zobrazit efekty", + "F_BepInEx_ShowEffects_D": "Zobrazit status efekty pod lištou se životy.", + "F_BepInEx_ShowFactionIcon_T": "Zobrazit Ikonku Frakce Hráče", + "F_BepInEx_ShowFactionIcon_D": "Zobrazit ikonku frakce vedle HP baru.", "F_BepInEx_HideInOptic_T": "Hide Name Plate in Optic", "F_BepInEx_HideInOptic_D": "Hides the name plate when viewing through PiP scopes.", "F_BepInEx_OpticUseZoom_T": "Name Plates Use Optic Zoom", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "The minimum scale of the name plates.", "F_BepInEx_UseOcclusion_T": "Use Occlusion", "F_BepInEx_UseOcclusion_D": "Use occlusion to hide the name plate when the player is out of sight.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Quest Types", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Show Notifications", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", "F_BepInEx_DynamicAINoSnipers_T": "Ignore Snipers", "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", @@ -217,7 +227,7 @@ "F_BepInEx_DespawnMinDistance_T": "Despawn Minimum Distance", "F_BepInEx_DespawnMinDistance_D": "The minimum distance to despawn a bot.", "F_BepInEx_MaxBots_T": "Max Bots {0}", - "F_BepInEx_MaxBots_D": "Max amount of bots that can be active at the same time on {0}. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", + "F_BepInEx_MaxBots_D": "Maximální počet botů kteří mohou být aktivní ve stejný moment na {0}. Vhodné když máte slabší PC. Zadejte 0 abyste použili vanilla limity. Nemůže být změněno během raidu.", "F_BepInEx_NativeSockets_T": "Native Sockets", "F_BepInEx_NativeSockets_D": "Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", "F_BepInEx_ForceIP_T": "Force IP", @@ -233,18 +243,26 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "How long it takes for a connection to be considered dropped if no packets are received.", "F_BepInEx_SendRate_T": "Send Rate", - "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", - "F_BepInEx_SmoothingRate_T": "Smoothing Rate", - "F_BepInEx_SmoothingRate_D": "Local simulation is behind by (Send Rate * Smoothing Rate). This guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter during interpolation.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\nSet this to 'High' if movement isn't smooth. Cannot be changed during a raid.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", - "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", + "F_BepInEx_DisableBotMetabolism_D": "Lokální simulace je pozadu o (Send Rate * Smoothing Rate). Toto garantuje že vždy budeme mít dostatečný počet snapshotů v bufferu, abychom předešli lagům během interpolace.\n\nNízké = 1.5\nStřední = 2\nVysoké = 2.5\n\nNastavte na Vysoké, pokud pohyb není plynulý. Nemůže být změněno během raidu.", "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", "F_MMUI_InMenu": "In Menu", - "F_MMUI_InRaid": "In Raid", + "F_MMUI_InRaid": "V Raidu", "F_MMUI_InStash": "In Stash", "F_MMUI_InHideout": "In Hideout", "F_MMUI_InFlea": "In Flea", - "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", + "F_MMUI_RaidDetails": "Hraje jako {0} na {1}\nČas v raidu: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "Pokoušíte se použít jinou verzi EFT než má hostitel raidu.\nVáš Client: {0}\nHostitel: {1}", + "F_UI_FikaVersionError": "Pokoušíte se použít jinou verzi Fika než má hostitel raidu.\nVaše verze: {0}\nHostitel: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/en.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/en.json index 48cf96a..ba66e9b 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/en.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/en.json @@ -43,9 +43,10 @@ "F_MMUI_StartButton": "START", "F_MMUI_LoadingScreenHeader": "LOADING", "F_MMUI_LoadingScreenDescription": "Please wait while the raid is initializing... this might take some time.", - "F_MMUI_JoinAsSpectator": "Join as spectator", + "F_MMUI_JoinAsSpectator": "Spectate", + "F_MMUI_JoinAsSpectator_D": "Join the game as a spectator", "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_UI_StartRaidDescription": "Starts the raid\nAfter clicking this additional players will not be able to join", "F_UI_CannotJoinRaidOtherMap": "Cannot join a raid that is on another map.\nRaid map: {0}", "F_UI_CannotJoinRaidOtherTime": "Cannot join a raid that is on another time.", "F_UI_CannotJoinRaidScavAsPMC": "You cannot join a scav raid as a PMC.", @@ -65,6 +66,7 @@ "F_UI_ErrorBindIPHeader": "ERROR BINDING", "F_UI_ErrorBindIP": "'{0}' is not a valid IP address to bind to! Check your 'Force Bind IP' setting.", "F_UI_NoDedicatedClients": "No dedicated clients are available.", + "F_UI_HostRaidTooltip": "Starts a raid\nOther players will be able to join you during the loading screen", "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", "F_UI_WaitForHostFinishInit": "Waiting for host to finish raid initialization...", "F_UI_WaitForOtherPlayers": "Waiting for other players to finish loading...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Gameplay", "F_BepInEx_OfficialVersion_T": "Official Version", "F_BepInEx_OfficialVersion_D": "Show official version instead of Fika version.", + "F_BepInEx_UseHeadless_T": "Auto Use Headless", + "F_BepInEx_UseHeadless_D": "Always tick the box to use the Headless Client if available", "F_BepInEx_ShowFeed_T": "Show Feed", "F_BepInEx_ShowFeed_D": "Enable custom notifications when a player dies, extracts, kills a boss, etc.", "F_BepInEx_AutoExtract_T": "Auto Extract", @@ -158,6 +162,12 @@ "F_BepInEx_MinPlateScale_D": "The minimum scale of the name plates.", "F_BepInEx_UseOcclusion_T": "Use Occlusion", "F_BepInEx_UseOcclusion_D": "Use occlusion to hide the name plate when the player is out of sight.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", + "F_BepInEx_NamePlateColor_T": "Name Plate Text Color", + "F_BepInEx_NamePlateColor_D": "The color of the text above the name plate.", "F_BepInEx_QuestTypes_T": "Quest Types", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Show Notifications", @@ -210,6 +220,8 @@ "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", "F_BepInEx_DynamicAINoSnipers_T": "Ignore Snipers", "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", @@ -233,9 +245,11 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "How long it takes for a connection to be considered dropped if no packets are received.", "F_BepInEx_SendRate_T": "Send Rate", - "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", - "F_BepInEx_SmoothingRate_T": "Smoothing Rate", - "F_BepInEx_SmoothingRate_D": "Local simulation is behind by (Send Rate * Smoothing Rate). This guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter during interpolation.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\nSet this to 'High' if movement isn't smooth. Cannot be changed during a raid.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", @@ -245,6 +259,12 @@ "F_MMUI_InHideout": "In Hideout", "F_MMUI_InFlea": "In Flea", "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es-mx.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es-mx.json index bcb918e..876da09 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es-mx.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es-mx.json @@ -43,9 +43,10 @@ "F_MMUI_StartButton": "COMENZAR", "F_MMUI_LoadingScreenHeader": "CARGANDO", "F_MMUI_LoadingScreenDescription": "Por favor, espere mientras la inclusión se está inicializando... esto puede tomar algún tiempo.", - "F_MMUI_JoinAsSpectator": "Unirse como espectador", + "F_MMUI_JoinAsSpectator": "Spectate", + "F_MMUI_JoinAsSpectator_D": "Join the game as a spectator", "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_UI_StartRaidDescription": "Starts the raid\nAfter clicking this additional players will not be able to join", "F_UI_CannotJoinRaidOtherMap": "No puedes unirte a una incursión que está en otro mapa.\nMapa de incursión: {0}", "F_UI_CannotJoinRaidOtherTime": "No puedes unirte a una incursión que esté en otro horario.", "F_UI_CannotJoinRaidScavAsPMC": "No puedes unirte a una incursión de SCAV como PMC.", @@ -65,6 +66,7 @@ "F_UI_ErrorBindIPHeader": "ERROR ENLAZANDO", "F_UI_ErrorBindIP": "'{0}' no es una dirección IP válida para enlazar! Compruebe su configuración de 'Forzar enlace IP'.", "F_UI_NoDedicatedClients": "No hay clientes dedicados disponibles.", + "F_UI_HostRaidTooltip": "Starts a raid\nOther players will be able to join you during the loading screen", "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", "F_UI_WaitForHostFinishInit": "Esperando por el anfitrión para inicializar de la incursión...", "F_UI_WaitForOtherPlayers": "Esperando por otros jugadores para terminar la carga...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Jugabilidad", "F_BepInEx_OfficialVersion_T": "Versión Oficial", "F_BepInEx_OfficialVersion_D": "Muestra la versión oficial en vez de la versión de Fika..", + "F_BepInEx_UseHeadless_T": "Auto Use Headless", + "F_BepInEx_UseHeadless_D": "Always tick the box to use the Headless Client if available", "F_BepInEx_ShowFeed_T": "Show Feed", "F_BepInEx_ShowFeed_D": "Habilitar notificaciones personalizadas cuando un jugador muere, extrae, mata a un jefe, etc.", "F_BepInEx_AutoExtract_T": "Auto Extracción", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "La escala mínima de las placas de nombre.", "F_BepInEx_UseOcclusion_T": "Usar oclusión", "F_BepInEx_UseOcclusion_D": "Usar la oclusión para ocultar las placas de nombre cuando el jugador está fuera de vista.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Quest Types", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Mostrar Notificaciones", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "Qué tan seguido la IA Dinámica debe buscar el rango de los todos los jugadores.", "F_BepInEx_DynamicAINoSnipers_T": "Ignorar Francotiradores", "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "Cuánto tiempo se tarda en que una conexión se considere abandonada si no se han recibido paquetes.", "F_BepInEx_SendRate_T": "Tasa de Envío", - "F_BepInEx_SendRate_D": "Que tan seguido se deben enviar los paquetes de movimiento por segundo (menor = menos ancho de banda utilizado, ligeramente más retraso durante la interpolación)\nEsto solo afecta al anfitrión y será sincronizado con todos los clientes.\nLa cantidad es por segundo:\n\nMuy Bajo = 10\nBajo = 15\nMedio = 20\nAlto = 30\n\nRecomendado no dejarlo más alto que Medio ya que las ganancias son insignificantes.", - "F_BepInEx_SmoothingRate_T": "Tasa de Suavizado", - "F_BepInEx_SmoothingRate_D": "La simulación local está retrasada por (Tasa de Envío * Tasa de Suavizado). Esto garantiza que siempre habrán suficientes tomas instantáneas en el buffer para mitigar retrasos e irregularidades durante la interpolación.\n\nBajo = 1.5\nMedio = 2\nAlto = 2.5\n\nEstablecer en \"Alto\" si el movimiento no es suave. No se puede cambiar durante una incursión.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", "F_MMUI_OnlinePlayers": "JUGADORES EN LÍNEA: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "In Hideout", "F_MMUI_InFlea": "In Flea", "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es.json index c003d0e..301e678 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/es.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "RAID HOST", "F_MMUI_JoinButton": "UNIRSE", "F_MMUI_SelectAmountHeader": "SELECCIONAR CANTIDAD", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "CONFIGURACIÓN DE SESIÓN", "F_MMUI_SelectAmountDescription": "¿Cuántas personas (incluyéndote a ti) jugarán?", "F_MMUI_UseDedicatedHost": "Utilice un host dedicado", "F_MMUI_StartButton": "INICIAR", "F_MMUI_LoadingScreenHeader": "CARGANDO", "F_MMUI_LoadingScreenDescription": "Espere mientras la raid se inicializa... esto podría llevar algún tiempo.", - "F_MMUI_JoinAsSpectator": "Unirse como espectador", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Modo espectador", + "F_MMUI_JoinAsSpectator_D": "Unirse como espectador", + "F_UI_StartRaid": "Comenzar Incursión", + "F_UI_StartRaidDescription": "Inicia la incursión\nDespués de hacer clic aquí no se podrán unir más jugadores", "F_UI_CannotJoinRaidOtherMap": "No puedes unirte a una partida en otro mapa. Mapa de la partida: {0}", "F_UI_CannotJoinRaidOtherTime": "No puedes unirte a una partida en horario distinto.", "F_UI_CannotJoinRaidScavAsPMC": "No puedes unirte a una partida scav como PMC.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "ERROR DE UNIÓN", "F_UI_ErrorBindIP": "'{0}' no es una IP válida a la que forzar la unión! Comprueba tus ajustes de 'Forzar Unión de IP'.", "F_UI_NoDedicatedClients": "No hay clientes dedicados disponibles.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Inicia una incursión\nOtros jugadores podrán unirse durante la pantalla de carga", + "F_UI_WaitForHostStartRaid": "Esperando a que el anfitrión inicie la incursión...", "F_UI_WaitForHostFinishInit": "Esperando a que el anfitrión inicialice la partida...", "F_UI_WaitForOtherPlayers": "Esperando a que otros jugadores terminen de cargar...", "F_UI_RetrieveSpawnInfo": "Recuperando información de reaparición del servidor...", @@ -101,7 +103,7 @@ "F_Notification_ItemReceived": "Recibiste un {0} de {1}", "F_Notification_RaidSettingsDisabled": "La edición de RaidSettings ha sido desactivada por el anfitrión del servidor", "F_UI_ExtractMessage": "Presiona {0} para extraer", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", + "F_UI_DownloadProfile": "DESCARGAR PERFIL", "F_BepInEx_H_Advanced": "Avanzado", "F_BepInEx_H_Coop": "Coop", "F_BepInEx_H_Coop_NamePlates": "Cocina | Placas de nombre", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Jugabilidad", "F_BepInEx_OfficialVersion_T": "Versión Oficial", "F_BepInEx_OfficialVersion_D": "Mostrar versión oficial en lugar de la versión de Fika.", + "F_BepInEx_UseHeadless_T": "Utilizar automáticamente Headless Client", + "F_BepInEx_UseHeadless_D": "Marque la casilla para utilizar el Headless Client siempre que esté disponible", "F_BepInEx_ShowFeed_T": "Mostrar fuente", "F_BepInEx_ShowFeed_D": "Habilitar notificaciones personalizadas cuando un jugador muere, extrae, mata a un jefe, etc.", "F_BepInEx_AutoExtract_T": "Auto Extracción", @@ -158,11 +162,15 @@ "F_BepInEx_MinPlateScale_D": "La escala mínima de las placas de nombre.", "F_BepInEx_UseOcclusion_T": "Usar oclusión", "F_BepInEx_UseOcclusion_D": "Usar la oclusión para ocultar las placas de nombre cuando el jugador está fuera de vista.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Tipos de Misión", "F_BepInEx_QuestTypes_D": "Los tipos de misiones a recibir y enviar. PlaceBeacon son tanto marcadores como objetos.", "F_BepInEx_QSNotifications_T": "Mostrar Notificaciones", - "F_BepInEx_QSNotifications_D": "If a notification should be shown when quest progress is shared with out.", - "F_BepInEx_EasyKillConditions_T": "Easy Kill Conditions", + "F_BepInEx_QSNotifications_D": "Mostrar una notificación cuando el progreso de una misión se comparta.", + "F_BepInEx_EasyKillConditions_T": "Condiciones de Baja sencillas", "F_BepInEx_EasyKillConditions_D": "Habilita las condiciones de baja fáciles. Cuando esto es utilizado, siempre que un jugador amistoso mate algo, cuenta como si lo hubieras matado tú para tus misiones siempre y cuando se cumplan las condiciones.\nEsto puede ser inconsistente y no siempre funciona.", "F_BepInEx_SharedKillXP_T": "Experiencia de Baja Compartida", "F_BepInEx_SharedKillXP_D": "De ser activado, recibirás la ½ de la experiencia cuando un jugador amistoso mate a un enemigo (no a un jefe).", @@ -176,43 +184,45 @@ "F_BepInEx_PingColor_D": "El color de tus marcas cuando se muestran a otros jugadores.", "F_BepInEx_PingSize_T": "Tamaño de la Marca", "F_BepInEx_PingSize_D": "The multiplier of the ping size.", - "F_BepInEx_PingTime_T": "Ping Time", - "F_BepInEx_PingTime_D": "How long pings should be displayed.", - "F_BepInEx_PingAnimation_T": "Play Ping Animation", - "F_BepInEx_PingAnimation_D": "Plays the pointing animation automatically when pinging. Can interfere with gameplay.", - "F_BepInEx_PingOptics_T": "Show Ping During Optics", - "F_BepInEx_PingOptics_D": "If pings should be displayed while aiming down an optics scope.", - "F_BepInEx_PingOpticZoom_T": "Ping Use Optic Zoom", - "F_BepInEx_PingOpticZoom_D": "If ping location should be displayed using the PiP optic camera.", - "F_BepInEx_PingScaleDistance_T": "Ping Scale With Distance", - "F_BepInEx_PingScaleDistance_D": "If ping size should scale with distance from player.", - "F_BepInEx_PingMinOpac_T": "Ping Minimum Opacity", - "F_BepInEx_PingMinOpac_D": "The minimum opacity of pings when looking straight at them.", - "F_BepInEx_PingRange_T": "Show Ping Range", - "F_BepInEx_PingRange_D": "Shows the range from your player to the ping if enabled.", - "F_BepInEx_PingSound_T": "Ping Sound", - "F_BepInEx_PingSound_D": "The audio that plays on ping", - "F_BepInEx_FreeCamButton_T": "Free Camera Button", - "F_BepInEx_FreeCamButton_D": "Button used to toggle free camera.", - "F_BepInEx_SpectateBots_T": "Allow Spectating Bots", - "F_BepInEx_SpectateBots_D": "If we should allow spectating bots if all players are dead/extracted.", - "F_BepInEx_AZERTYMode_T": "AZERTY Mode", - "F_BepInEx_AZERTYMode_D": "If free camera should use AZERTY keys for input.", - "F_BepInEx_DroneMode_T": "Drone Mode", - "F_BepInEx_DroneMode_D": "If the free camera should move only along the vertical axis like a drone.", - "F_BepInEx_KeybindOverlay_T": "Keybind Overlay", - "F_BepInEx_KeybindOverlay_D": "If an overlay with all free cam keybinds should show.", + "F_BepInEx_PingTime_T": "Tiempo de Ping", + "F_BepInEx_PingTime_D": "Durante cuanto deberían mostrarse los Pings.", + "F_BepInEx_PingAnimation_T": "Reproducir animación de Ping", + "F_BepInEx_PingAnimation_D": "Utilizar la animación de señalar automáticamente al hacer Ping. Puede interferir en el juego.", + "F_BepInEx_PingOptics_T": "Mostrar Ping cuando apuntas con ópticas", + "F_BepInEx_PingOptics_D": "Si los Ping debería mostrarse mientras apuntas con una óptica.", + "F_BepInEx_PingOpticZoom_T": "Mostrar Ping cuando apuntas con ópticas de magnificación", + "F_BepInEx_PingOpticZoom_D": "Si los Ping deberían mostrarse mientras apuntas con una mira de magnificación.", + "F_BepInEx_PingScaleDistance_T": "Escalar Ping a distancia", + "F_BepInEx_PingScaleDistance_D": "Si el tamaño de las Marcas debería escalar dependiendo de la distancia del jugador.", + "F_BepInEx_PingMinOpac_T": "Opacidad mínima del Ping", + "F_BepInEx_PingMinOpac_D": "La opacidad mínima de los Pings cuando los miras directamente.", + "F_BepInEx_PingRange_T": "Mostrar distancia del Ping", + "F_BepInEx_PingRange_D": "Muestra la distancia de tu personaje hasta el Ping si está activado.", + "F_BepInEx_PingSound_T": "Sonido del Ping", + "F_BepInEx_PingSound_D": "El audio que suena al hacer Ping", + "F_BepInEx_FreeCamButton_T": "Botón de Cámara Libre", + "F_BepInEx_FreeCamButton_D": "Botón usado para alternar a Cámara Libre.", + "F_BepInEx_SpectateBots_T": "Permitir espectar Bots", + "F_BepInEx_SpectateBots_D": "Permitir espectar a los bots si todos los jugadores han muerto/extraído.", + "F_BepInEx_AZERTYMode_T": "Modo teclado AZERTY", + "F_BepInEx_AZERTYMode_D": "Si la cámara debe usar teclas en formato AZERTY para moverse.", + "F_BepInEx_DroneMode_T": "Modo Dron", + "F_BepInEx_DroneMode_D": "Si la cámara libre debería moverse solo en el eje vertical como un dron.", + "F_BepInEx_KeybindOverlay_T": "Mostrar Leyenda", + "F_BepInEx_KeybindOverlay_D": "Si debería haber una leyenda con todas las teclas usadas para la cámara libre.", "F_BepInEx_DynamicAI_T": "IA Dinámica", "F_BepInEx_DynamicAI_D": "Utilizar el sistema de IA dinámico, desactivando la IA cuando están fuera de rango de todos los jugadores.", "F_BepInEx_DynamicAIRange_T": "Rango de la IA Dinámica", "F_BepInEx_DynamicAIRange_D": "El rango en el que la IA se desactivará automáticamente.", - "F_BepInEx_DynamicAIRate_T": "Dynamic AI Rate", - "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", + "F_BepInEx_DynamicAIRate_T": "Tasa de actualización de IA dinámica", + "F_BepInEx_DynamicAIRate_D": "Con que frecuencia debería la IA Dinámica escanear el rango de todos los jugadores.", "F_BepInEx_DynamicAINoSnipers_T": "Ignorar Francotiradores", - "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", - "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", - "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", - "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", + "F_BepInEx_DynamicAINoSnipers_D": "Debería la IA Dinámica ignorar los Scav francotiradores.", + "F_BepInEx_FikaGC_T": "Usar el GC de Fika", + "F_BepInEx_FikaGC_D": "Si debería usarse un Garbage Collector personalizado para aliviar problemas con el uso de memoria.\nCUIDADO: Experimental, puede que no tenga ningún efecto en tu sistema.", + "F_BepInEx_EnforcedSpawnLimits_T": "Límites forzados de Spawn", + "F_BepInEx_EnforcedSpawnLimits_D": "Fuerza un límite de reaparición para los bots, haciendo que no sobrepase los límites de Vanilla. Esta opción principalmente funciona con mods que ajustan la reaparición o el límite de bots. No bloquea el spawn de bots especiales como bosses.", + "F_BepInEx_DespawnFurthest_T": "Eliminar el más lejano", "F_BepInEx_DespawnFurthest_D": "When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", "F_BepInEx_DespawnMinDistance_T": "Despawn Minimum Distance", "F_BepInEx_DespawnMinDistance_D": "The minimum distance to despawn a bot.", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "Cuánto tiempo se tarda en que una conexión se considere abandonada si no se han recibido paquetes.", "F_BepInEx_SendRate_T": "Tasa de Envío", - "F_BepInEx_SendRate_D": "Que tan seguido se deben enviar los paquetes de movimiento por segundo (menor = menos ancho de banda utilizado, ligeramente más retraso durante la interpolación)\nEsto solo afecta al anfitrión y será sincronizado con todos los clientes.\nLa cantidad es por segundo:\n\nMuy Bajo = 10\nBajo = 15\nMedio = 20\nAlto = 30\n\nRecomendado no dejarlo más alto que Medio ya que las ganancias son insignificantes.", - "F_BepInEx_SmoothingRate_T": "Tasa de Suavizado", - "F_BepInEx_SmoothingRate_D": "La simulación local está retrasada por (Tasa de Envío * Tasa de Suavizado). Esto garantiza que siempre habrán suficientes tomas instantáneas en el buffer para mitigar retrasos e irregularidades durante la interpolación.\n\nBajo = 1.5\nMedio = 2\nAlto = 2.5\n\nEstablecer en \"Alto\" si el movimiento no es suave. No se puede cambiar durante una incursión.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", "F_MMUI_OnlinePlayers": "JUGADORES EN LÍNEA: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "En el Escondite", "F_MMUI_InFlea": "In Flea", "F_MMUI_RaidDetails": "Jugando como {0} en {1}\nTiempo: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/fr.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/fr.json index 0c77f42..ffa6f39 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/fr.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/fr.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "HÉBERGER UN RAID", "F_MMUI_JoinButton": "REJOINDRE", "F_MMUI_SelectAmountHeader": "SÉLECTIONNER LE MONTANT", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "PARAMÈTRES DE SESSION", "F_MMUI_SelectAmountDescription": "Combien de joueurs (vous inclus) vont participer ?", "F_MMUI_UseDedicatedHost": "Utiliser un hôte dédié", "F_MMUI_StartButton": "DÉMARRER", "F_MMUI_LoadingScreenHeader": "CHARGEMENT", "F_MMUI_LoadingScreenDescription": "Veuillez patienter pendant l'initialisation du raid... Cela peut prendre un certain temps.", - "F_MMUI_JoinAsSpectator": "Rejoindre comme spectateur", + "F_MMUI_JoinAsSpectator": "Spectateur", + "F_MMUI_JoinAsSpectator_D": "Rejoindre le jeu en tant que spectateur", "F_UI_StartRaid": "Lancer le raid", - "F_UI_StartRaidDescription": "Lance le raid\nLes nouveaux joueurs ne pourront plus rejoindre", + "F_UI_StartRaidDescription": "Lancer le raid\nAucun autre joueur ne pourra rejoindre après avoir cliqué", "F_UI_CannotJoinRaidOtherMap": "Impossible de rejoindre un raid dont la carte est différente.\nCarte du raid : {0}", "F_UI_CannotJoinRaidOtherTime": "Impossible de rejoindre un raid dont l'horaire est différent.", "F_UI_CannotJoinRaidScavAsPMC": "Vous ne pouvez pas rejoindre un raid SCAV en tant que SMP.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "ERREUR AFFECTATION IP", "F_UI_ErrorBindIP": "'{0}' n'est pas une adresse IP valide ! Vérifiez le paramètre 'Force Bind IP'.", "F_UI_NoDedicatedClients": "Aucun client dédié n'est disponible.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Lance un raid\nD'autres joueurs pourront vous rejoindre pendant l'écran de chargement", + "F_UI_WaitForHostStartRaid": "En attente du lancement du raid par l'hôte...", "F_UI_WaitForHostFinishInit": "En attente de la fin de l'initialisation du raid par l'hôte...", "F_UI_WaitForOtherPlayers": "En attente du chargement des autres joueurs...", "F_UI_RetrieveSpawnInfo": "Récupération des informations d'apparitions depuis le serveur...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Jouabilité", "F_BepInEx_OfficialVersion_T": "Version officielle", "F_BepInEx_OfficialVersion_D": "Afficher la version officielle au lieu de la version Fika.", + "F_BepInEx_UseHeadless_T": "Utiliser automatiquement le client sans interface graphique", + "F_BepInEx_UseHeadless_D": "Toujours cocher la case pour utiliser le client sans interface graphique si disponible", "F_BepInEx_ShowFeed_T": "Afficher le flux", "F_BepInEx_ShowFeed_D": "Activer les notifications personnalisées lorsqu'un joueur meurt, s'extrait, tue un boss, etc.", "F_BepInEx_AutoExtract_T": "Extraction Automatique", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "L'échelle minimale des plaques de nom.", "F_BepInEx_UseOcclusion_T": "Utiliser l'Occlusion", "F_BepInEx_UseOcclusion_D": "Utilisez l'occlusion pour masquer la plaque de nom lorsque le joueur est hors de vue.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Types de quêtes", "F_BepInEx_QuestTypes_D": "Quels types de quêtes à recevoir et à envoyer. PlaceBeacon est à la fois marqueurs et objets.", "F_BepInEx_QSNotifications_T": "Afficher les Notifications", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "À quelle fréquence DynamicAI devrait rechercher la portée de tous les joueurs.", "F_BepInEx_DynamicAINoSnipers_T": "Ignore les snipers", "F_BepInEx_DynamicAINoSnipers_D": "Si l'IA dynamique doit ignorer les scavs sniper.", + "F_BepInEx_FikaGC_T": "Utiliser Fika GC", + "F_BepInEx_FikaGC_D": "Si un récupérateur de mémoire personnalisé doit être utilisé pour résoudre les problèmes d'utilisation de la mémoire.\nAVERTISSEMENT : Expérimental, peut n'avoir aucun effet sur votre système.", "F_BepInEx_EnforcedSpawnLimits_T": "Limites dapparition imposées", "F_BepInEx_EnforcedSpawnLimits_D": "Impose les limites d'apparition lors de l'apparition des bots, en s'assurant de ne pas dépasser les limites vanilla. Cela agit principalement lorsque vous utilisez des mods d'apparition ou tout ce qui modifie les limites de bots. Ne bloquera pas les spawn de bots spéciaux comme les boss.", "F_BepInEx_DespawnFurthest_T": "Fait disparaitre le plus éloigné", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Délai de connexion dépassé", "F_BepInEx_ConnectionTimeout_D": "Combien de temps faut-il pour qu'une connexion soit considérée comme abandonnée si aucun paquet réseau n'est reçu.", "F_BepInEx_SendRate_T": "Taux denvoi", - "F_BepInEx_SendRate_D": "Combien de fois par seconde les paquets de mouvement doivent être envoyés (plus faible = moins de bande passante utilisée, un peu plus de latence lors de l'interpolation)\nCeci affecte uniquement l'hôte et sera synchronisé avec tous les clients.\nLe montant est par seconde :\n\nTrès Bas = 10\nBas = 15\nMoyen = 20\nHaut = 30\n\nRecommandé de laisser au plus haut sur Moyen car les gains sont insignifiants après.", - "F_BepInEx_SmoothingRate_T": "Taux de lissage", - "F_BepInEx_SmoothingRate_D": "La simulation locale est en retard par (Débit d'envoi* Taux de lissage). Cela garantit que nous ayons toujours assez de snapshots dans le tampon pour atténuer les retards et le jitter lors de l'interpolation.\n\nBas = 1.5\nMoyenne = 2\nHaut = 2.5\n\nRéglez ceci sur \"Haut\" si le mouvement n'est pas fluide. Ne peut pas être changé pendant un raid.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Utiliser le MultiThreading", + "F_BepInEx_NetMultiThread_D": "Si le composant réseau doit utiliser le multithreading, cela peut potentiellement atténuer les problèmes de latence du jeu, permettant au gestionnaire réseau de continuer à traiter et à mettre en file d'attente les paquets même avec un faible nombre d'images par seconde.\nCette fonctionnalité est expérimentale et ne doit être activée que sur les PC haut de gamme et sur un client sans interface graphique.", + "F_BepInEx_VOIP_T": "Autoriser le VOIP", + "F_BepInEx_VOIP_D": "Si le VOIP sera activé lorsque vous hébergez.", "F_BepInEx_DisableBotMetabolism_T": "Désactiver le métabolisme des bots", "F_BepInEx_DisableBotMetabolism_D": "Désactive le métabolisme des bots, les empêchant de mourir d'une perte d'énergie/hydratation lors de longs raids.", "F_MMUI_OnlinePlayers": "JOUEURS EN LIGNE : {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "Dans la Planque", "F_MMUI_InFlea": "Dans le commerce", "F_MMUI_RaidDetails": "Jouer en tant que {0} sur {1}\nTemps : {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", + "F_UI_LocaleError_H": "ERREUR LOCALE", + "F_UI_LocaleError_D": "Des erreurs se sont produites lors du chargement des paramètres régionaux Fika. La traduction utilise désormais des solutions de secours pour les en-têtes défaillants. Veuillez signaler ce problème aux développeurs !\n\nClés défaillantes : {0}", + "F_UI_EFTVersionError": "Vous essayez d'utiliser une version d'EFT différente de celle de l'hôte.\nClient : {0}\nHôte : {1}", + "F_UI_FikaVersionError": "Vous essayez d'utiliser une version de Fika différente de celle de l'hôte du raid.\nClient : {0}\nHôte : {1}", + "F_UI_PlayerConnected": "{0} s'est connecté", + "F_UI_PlayerDisconnected": "{0} s'est déconnecté", + "F_UI_DisabledByServerHost": "Désactivé par l'hôte du serveur" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ge.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ge.json index 73546ba..2c0c315 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ge.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ge.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "RAID HOSTEN", "F_MMUI_JoinButton": "BEITRETEN", "F_MMUI_SelectAmountHeader": "ANZAHL AUSWÄHLEN", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "SITZUNGS EINSTELLUNGEN", "F_MMUI_SelectAmountDescription": "Wie viele Spieler (inklusive dir) werden spielen?", "F_MMUI_UseDedicatedHost": "Dedizierten Host benutzen", "F_MMUI_StartButton": "START", "F_MMUI_LoadingScreenHeader": "LADEN", "F_MMUI_LoadingScreenDescription": "Bitte warten, während der Raid initialisiert wird... dies könnte eine Weile dauern.", - "F_MMUI_JoinAsSpectator": "Als Zuschauer beitreten", + "F_MMUI_JoinAsSpectator": "Zuschauen", + "F_MMUI_JoinAsSpectator_D": "Als Zuschauer beitreten", "F_UI_StartRaid": "Starte Raid", - "F_UI_StartRaidDescription": "Starte den Raid\nneue Spieler können nicht mehr joinen", + "F_UI_StartRaidDescription": "Raid starten\nNach dem Klicken können keine anderen Spieler mehr joinen", "F_UI_CannotJoinRaidOtherMap": "Du kannst keinem Raid beitreten, der sich auf einer anderen Karte befindet.\nDer Raid befindet sich auf Karte: {0}", "F_UI_CannotJoinRaidOtherTime": "Kann keinem Raid beitreten, der zu einer anderen Tageszeit stattfindet.", "F_UI_CannotJoinRaidScavAsPMC": "Du kannst als PMC keinem Scav Raid beitreten.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "Fehler beim IP Binding", "F_UI_ErrorBindIP": "'{0}' ist keine gültige IP-Adresse zum BINDING! Überprüfe die 'Force Bind IP'-Einstellung.", "F_UI_NoDedicatedClients": "Keine dedizierten Clients verfügbar.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Raid starten\nAndere Spieler können während des Ladebildschirms noch joinen", + "F_UI_WaitForHostStartRaid": "Warte darauf, dass der Host den Raid startet...", "F_UI_WaitForHostFinishInit": "Warte darauf, dass der Host die Initialisierung des Raids vollendet...", "F_UI_WaitForOtherPlayers": "Warte darauf, dass andere Spieler das Laden beendet haben...", "F_UI_RetrieveSpawnInfo": "Spawn-Info wird vom Server abgerufen...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Gameplay", "F_BepInEx_OfficialVersion_T": "Offizielle Version", "F_BepInEx_OfficialVersion_D": "Zeige die offizielle Version anstelle der Fika-Version.", + "F_BepInEx_UseHeadless_T": "Headless Client automatisch nutzen", + "F_BepInEx_UseHeadless_D": "Aktiviere diese Checkbox um immer den Headless Client zu nutzen, wenn verfügbar", "F_BepInEx_ShowFeed_T": "Feed anzeigen", "F_BepInEx_ShowFeed_D": "Aktiviere benutzerdefinierte Benachrichtigungen, wenn ein Spieler stirbt, extractet, einen Boss tötet, etc.", "F_BepInEx_AutoExtract_T": "Auto Extract", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "Die minimale Skalierung der Namensplaketten.", "F_BepInEx_UseOcclusion_T": "Occlusion verwenden", "F_BepInEx_UseOcclusion_D": "Verwende Verdeckung, um das Namensschild auszublenden, wenn der Spieler außerhalb des Sichtfelds ist.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Quest-Typen", "F_BepInEx_QuestTypes_D": "Welche AufgabenTypen sollen empfangen und gesendet werden. Platzierungen gelten für beides, Markierer und Gegenstände.", "F_BepInEx_QSNotifications_T": "Benachrichtigungen anzeigen", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "Frequenz, in der die DynamicAI nach der Reichweite aller Spieler suchen soll.", "F_BepInEx_DynamicAINoSnipers_T": "Ignoriere Scharfschützen", "F_BepInEx_DynamicAINoSnipers_D": "Ob Sniper Scavs von der dynamische AI ignoriert werden sollen.", + "F_BepInEx_FikaGC_T": "Benuzter Fika GC", + "F_BepInEx_FikaGC_D": "Ob ein benutzerdefinierter Garbagecollector verwendet werden sollt, um Probleme mit der Speicherauslastung zu lindern.\nWARNUNG: Experimentell, könnte überhaupt keine Wirkung auf dein System haben.", "F_BepInEx_EnforcedSpawnLimits_T": "Erzwinge Spawn Limitierungen", "F_BepInEx_EnforcedSpawnLimits_D": "Erzwingt Spawn Limitierungen, wenn Bots spawnen. Stelle sicher, dass du nicht oberhalb der Vanilla Limitierungen bist. Diese Einstellung wird hauptsächlich Effekte haben wenn du Spawn Modifikationen benutzt. Blockiert keine speziellen Bots wie Bosse.", "F_BepInEx_DespawnFurthest_T": "Despawn am weitesten entfernt", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Verbindungsauszeit", "F_BepInEx_ConnectionTimeout_D": "Wie lange es dauert, bis eine Verbindung als abgebrochen betrachtet wird, wenn keine Pakete empfangen werden.", "F_BepInEx_SendRate_T": "Senderate", - "F_BepInEx_SendRate_D": "Wie oft pro Sekunde Bewegungspakete gesendet werden sollen (niedriger = weniger Bandbreite, führt zu geringfügig mehr Verzögerung während der Interpolation)\nDies betrifft nur den Host und wird mit allen Clients synchronisiert.\nBetrag ist pro Sekunde:\n\nSehr niedrig = 10\nNiedrig = 15\nMittel = 20\nHigh = 30\n\nEmpfohlen nicht höher als Mittel zu lassen, da die Zuwächse danach unbedeutend sind.", - "F_BepInEx_SmoothingRate_T": "Glättungsrate", - "F_BepInEx_SmoothingRate_D": "Lokale Simulation liegt hinter der Formel (Send Rate * Smoothing Rate). Dies garantiert, dass wir immer genug Schnappschüsse im Puffer haben, um Verzögerungen und Jitter während der Interpolation zu lindern.\n\nNiedrig = 1,5\nMittel = 2\nHoch = 2,5\n\nSetze dies auf \"Hoch\", wenn die Bewegung nicht glatt ist. Kann während eines Raids nicht geändert werden.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "MultiThreading benutzen", + "F_BepInEx_NetMultiThread_D": "Ob die Netzwerkkomponente MultiThreading verwenden soll. Dies kann möglicherweise Probleme verringern, wenn das Spiel nicht funktioniert, was es dem Netzwerk-Manager erlaubt, Pakete während des niedrigen FPS immer noch zu bearbeiten und Warteschlangen durchzuführen.\nDies ist eine experimentelle Funktion und sollte nur auf High-End-PCs aktiviert werden und wird nur auf einem Headless-Client aktiviert.", + "F_BepInEx_VOIP_T": "VOIP aktivieren", + "F_BepInEx_VOIP_D": "VOIP aktivieren, wenn du einen Raid hostest.", "F_BepInEx_DisableBotMetabolism_T": "Bot Metabolismus deaktivieren", "F_BepInEx_DisableBotMetabolism_D": "Deaktiviert den Metabolismus an Bots. Verhindert das Bots aufgrund von Energie-/Wassermangel in längeren Raids sterben.", "F_MMUI_OnlinePlayers": "ONLINE-SPIELER: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "Im Versteck", "F_MMUI_InFlea": "Im Flohmarkt", "F_MMUI_RaidDetails": "Spielt als {0} auf {1}\nZeit: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "Fehler beim Laden der Fika-Locales. Die Übersetzung verwendet jetzt Fallbacks für die fehlgeschlagenen Kopfzeilen. Bitte melden Sie dies den Entwicklern!\n\nfehlgeschlagene Schlüssel: {0}", + "F_UI_EFTVersionError": "Du möchtest mit einer unterschiedlichen EFT Version als der Host beitreten. \nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "Du möchtest mit einer unterschiedlichen Fika Version als der Host beitreten. \nClient: {0}\nHost: {1}", + "F_UI_PlayerConnected": "{0} verbunden", + "F_UI_PlayerDisconnected": "{0} getrennt", + "F_UI_DisabledByServerHost": "Vom Serverhost deaktiviert" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/hu.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/hu.json index 8d643ce..3425791 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/hu.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/hu.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "RAID LÉTREHOZÁSA", "F_MMUI_JoinButton": "CSATLAKOZÁS", "F_MMUI_SelectAmountHeader": "ÖSSZEG KIVÁLASZTÁSA", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "SESSION BEÁLLÍTÁSOK", "F_MMUI_SelectAmountDescription": "Hány ember fog játszani (beleértve téged is)?", "F_MMUI_UseDedicatedHost": "Dedicated Host használata", "F_MMUI_StartButton": "INDITÁS", "F_MMUI_LoadingScreenHeader": "BETÖLTÉS", "F_MMUI_LoadingScreenDescription": "Kérjük, várjon, amíg a raid betölt... ez eltarthat egy ideig.", - "F_MMUI_JoinAsSpectator": "Csatlakozás nézőként", + "F_MMUI_JoinAsSpectator": "Nézd", + "F_MMUI_JoinAsSpectator_D": "Csatlakozz nézőként", "F_UI_StartRaid": "Raid kezdése", - "F_UI_StartRaidDescription": "Raid elkezdése, új játékosok nem fognak tudni csatlakozni", + "F_UI_StartRaidDescription": "Elindít egy raid-et\nA gombra kattintás után más játékosok nem tudnak majd csatlakozni", "F_UI_CannotJoinRaidOtherMap": "Nem lehet csatlakozni egy másik pályán lévő raidhez.\nRaid pálya: {0}", "F_UI_CannotJoinRaidOtherTime": "Nem lehet csatlakozni egy olyan raidhez, amely más időn van.", "F_UI_CannotJoinRaidScavAsPMC": "Nem csatlakozhatsz scav raid-hez PMC-ként.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "KÖTÉSI HIBA", "F_UI_ErrorBindIP": "A '{0}' nem érvényes IP-cím a kötéshez! Ellenőrizze a „Force Bind IP” beállítást.", "F_UI_NoDedicatedClients": "Nincsenek dedikált kliensek.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Elindít egy raid-et\nMás játékosok tudnak rád csatlakozni a töltőkép közben", + "F_UI_WaitForHostStartRaid": "Várakozás a host-ra, hogy indítsa a raid-et...", "F_UI_WaitForHostFinishInit": "Várakozás, hogy a host befejezze a raid inicializálását...", "F_UI_WaitForOtherPlayers": "Várakozás, amíg a többi játékos befejezi a betöltést...", "F_UI_RetrieveSpawnInfo": "Spawn információk lekérése a szerverről...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Játék", "F_BepInEx_OfficialVersion_T": "Eredeti Verzió", "F_BepInEx_OfficialVersion_D": "Eredeti Verzió mutatása a Fika verziója helyett.", + "F_BepInEx_UseHeadless_T": "Automatán használjon Headless-t", + "F_BepInEx_UseHeadless_D": "Mindig pipáld be a négyzetet, hogy használjon Headless klienst, ha elérhető", "F_BepInEx_ShowFeed_T": "Információ megjelenítése", "F_BepInEx_ShowFeed_D": "Egyedi értesítések engedélyezése, amikor egy játékos meghal, extractol, megöl egy bosst satöbbi.", "F_BepInEx_AutoExtract_T": "Meccs automatikus elhagyása", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "A névtáblák minimum áttetszősége.", "F_BepInEx_UseOcclusion_T": "Okklúzió használata", "F_BepInEx_UseOcclusion_D": "Okklúzió használata a névtábla elrejtéséhez, amikor a játékos nincs a látóteredben.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Küldetés formák", "F_BepInEx_QuestTypes_D": "Milyen típusú küldetéseket kell fogadni és elküldeni. A PlaceBeacon jelölőket és elemeket is tartalmaz.", "F_BepInEx_QSNotifications_T": "Értesítések mutatása", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "Milyen gyakran keressen játékost a DynamicAI a hatótávján belül.", "F_BepInEx_DynamicAINoSnipers_T": "Mesterlövészek kihagyása", "F_BepInEx_DynamicAINoSnipers_D": "A dinamikus AI figyelmen kívül hagyja-e a mesterlövész scav-eket.", + "F_BepInEx_FikaGC_T": "Használjon Fika GC-t", + "F_BepInEx_FikaGC_D": "Az egyedi szemétszedő használva legyen-e, hogy enyhítse a memória használati gondokat.\nFIGYELEM: Kísérleti funkció, lehet nem is változtat a rendszereden.", "F_BepInEx_EnforcedSpawnLimits_T": "Kényszerített spawn limitek", "F_BepInEx_EnforcedSpawnLimits_D": "Kikényszeríti a spawn korlátokat a botok spawnalásakór, ügyelve arra, hogy ne lépje túl a vanilla korlátokat. Ez főként spawn modok vagy bármi más, ami módosítja a botkorlátokat, használatakor érvényesül. Nem blokkolja a speciális botok, például a bossok megjelenését.", "F_BepInEx_DespawnFurthest_T": "A legtávolabbi eltüntetése", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Kapcsolati időtullépés", "F_BepInEx_ConnectionTimeout_D": "Mennyi időbe telik, amíg a kapcsolat megszakadtnak minősül, ha nem érkezik packet.", "F_BepInEx_SendRate_T": "Küldési arány", - "F_BepInEx_SendRate_D": "Milyen gyakran kell másodpercenként küldeni a mozgáscsomagokat (alacsonyabb = kevesebb sávszélességet használnak, kicsit több késleltetés az interpoláció során)\nEz csak a Hosztot érinti, és minden klienssel szinkronizálva lesz.\nAz összeg másodpercenként:\n\nNagyon alacsony = 10\nAlacsony = 15\nKözepes = 20\nMagas = 30\n\nJavasoljuk, hogy a közepesnél nem magasabb értéket hagyja, mivel a nyereség utána jelentéktelen.", - "F_BepInEx_SmoothingRate_T": "Simítási arány", - "F_BepInEx_SmoothingRate_D": "A helyi szimuláció mögött van (Küldési sebesség * Simítási sebesség). Ez garantálja, hogy mindig legyen elegendő pillanatképünk a pufferben az interpoláció közbeni késések és jitter csökkentéséhez.\n\nAlacsony = 1,5\nKözepes = 2\nMagas = 2,5\n\nÁllítsa ezt a 'Magas' értékre, ha a mozgás nem egyenletes. Raid közben nem változtatható.", + "F_BepInEx_SendRate_D": "Milyen gyakran legyenek a mozgási packetek elküldve másodpercenként (kisebb = alacsonyabb sávszélesség, kicsivel több késés interpoláció közben)\nEz csak a host-ot érinti és minden kliensen szinkronizálva lesz.\nEnnek száma másodpercenként:\nAlacsony = 10\nKözepes = 20\nMagas = 30\n\nKözepes felé nem ajánlott állítani, mert a különbség elhanyagolható.", + "F_BepInEx_NetMultiThread_T": "Használjon MultiThreading-et", + "F_BepInEx_NetMultiThread_D": "A hálózati komponens használja-e a multi-threading-et. Ez potenciálisan enyhítheti a lag problémákat, engedélyezi a hálózat manager-nek, hogy tovább dolgozhassa fel és sorba rendezhesse a packet-eket alacsony FPS-nél.", + "F_BepInEx_VOIP_T": "Engedélyezze a VOIP-t", + "F_BepInEx_VOIP_D": "A VOIP be legyen kapcsolva, ha host-olsz.", "F_BepInEx_DisableBotMetabolism_T": "Bot metabolizmus kikapcsolása", "F_BepInEx_DisableBotMetabolism_D": "Kikapcsolja a metabolizmust botoknak, megakadályozza őket abban, hogy meghaljanak az energiavesztéstől/dehidratáltságtól a hosszú játék során.", "F_MMUI_OnlinePlayers": "Online játékosok: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "Bunkerben", "F_MMUI_InFlea": "Aukciós házban", "F_MMUI_RaidDetails": "Játék {0} ként {1}-en\nIdő: {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_Mod_VerificationFailure": "A Crc32-es jóváhagyása a Fika.Core.dll fájlnak sikertelen, nem tudsz másokkal játszani!\nNézd meg a log fájlaidat további információkért.", + "F_UI_LocaleError_H": "FORDÍTÁSI HIBA", + "F_UI_LocaleError_D": "Problémák léptek fel a Fika helyi fordításainál. Most tartalékokat fog használni a hibás fejlécekhez. Kérlek, jelentsd a készítőknek!\n\nHibás kulcsok: {0}", + "F_UI_EFTVersionError": "Egy másik EFT verzióval próbálsz csatlakozni, mint amit a host futtat.\nKliens crc: {0}\nHost crc: {1}", + "F_UI_FikaVersionError": "Egy másik Fika verzióval próbálsz csatlakozni, mint amit a host futtat.\nKliens crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} csatlakozott", + "F_UI_PlayerDisconnected": "{0} elhagyta a játékot", + "F_UI_DisabledByServerHost": "Kikapcsolva a szerver host által" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/it.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/it.json index c012e58..34f32e2 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/it.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/it.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "HOSTA RAID", "F_MMUI_JoinButton": "UNISCITI", "F_MMUI_SelectAmountHeader": "SELEZIONA QUANTITÀ", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "IMPOSTAZIONI SESSIONE", "F_MMUI_SelectAmountDescription": "Quante persone (incluso te) giocheranno?", "F_MMUI_UseDedicatedHost": "Usa Host Dedicato", "F_MMUI_StartButton": "AVVIA", "F_MMUI_LoadingScreenHeader": "CARICAMENTO", "F_MMUI_LoadingScreenDescription": "Attendere mentre il raid sta partendo... questo potrebbe richiedere un po' di tempo.", - "F_MMUI_JoinAsSpectator": "Join as spectator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Modalità Spettatore", + "F_MMUI_JoinAsSpectator_D": "Unisciti alla partita come spettatore", + "F_UI_StartRaid": "Avvia Raid", + "F_UI_StartRaidDescription": "Inizia il raid\nDopo aver cliccato nessun giocatore aggiuntivo sarà in grado di unirsi", "F_UI_CannotJoinRaidOtherMap": "Impossibile entrare in un raid che è su un'altra mappa.\nMappa raid: {0}", "F_UI_CannotJoinRaidOtherTime": "Impossibile entrare in un raid che è in un altro orario.", "F_UI_CannotJoinRaidScavAsPMC": "Non puoi partecipare a un raid scav come PMC.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "ERRORE BINDING", "F_UI_ErrorBindIP": "'{0}' non è un indirizzo IP valido per il binding! Controlla l'impostazione 'Forza Bind IP'.", "F_UI_NoDedicatedClients": "Nessun client dedicato è disponibile.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Avvia un raid\nGli altri giocatori potranno unirsi a te durante la schermata di caricamento", + "F_UI_WaitForHostStartRaid": "In attesa che l'host avvii il raid...", "F_UI_WaitForHostFinishInit": "In attesa che l'host concluda l'inizializzazione del raid...", "F_UI_WaitForOtherPlayers": "In attesa che gli altri giocatori finiscano di caricare...", "F_UI_RetrieveSpawnInfo": "Recupero informazioni di generazione dal server...", @@ -101,7 +103,7 @@ "F_Notification_ItemReceived": "Hai ricevuto {0} da {1}", "F_Notification_RaidSettingsDisabled": "La modifica di RaidSettings è stata disabilitata dall' host del server", "F_UI_ExtractMessage": "Premi {0} per estrarre", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", + "F_UI_DownloadProfile": "SCARICA PROFILO", "F_BepInEx_H_Advanced": "Avanzato", "F_BepInEx_H_Coop": "Coop", "F_BepInEx_H_Coop_NamePlates": "Coop | Targhette Nome", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Gameplay", "F_BepInEx_OfficialVersion_T": "Versione Ufficiale", "F_BepInEx_OfficialVersion_D": "Mostra la versione ufficiale invece della versione Fika", + "F_BepInEx_UseHeadless_T": "Uso Automatico Headless", + "F_BepInEx_UseHeadless_D": "Spuntare la casella per usare sempre il client Headless se disponibile", "F_BepInEx_ShowFeed_T": "Mostra Feed", "F_BepInEx_ShowFeed_D": "Mostra notifiche personalizzate quando un giocate muore, estrae, uccide un boss, ecc.", "F_BepInEx_AutoExtract_T": "Estrazione automatica", @@ -128,7 +132,7 @@ "F_BepInEx_ChatKey_D": "Il tasto usato per aprire la chat", "F_BepInEx_EnableOnlinePlayers_T": "Abilita Giocatori Online", "F_BepInEx_EnableOnlinePlayers_D": ".", - "F_BepInEx_OnlinePlayersScale_T": "Online Players Scale", + "F_BepInEx_OnlinePlayersScale_T": "Dimensione Giocatori Online", "F_BepInEx_OnlinePlayersScale_D": "Grandezza della finestra che mostra i giocatori online. Modifica solo se sproporzionata.\n\nRichiede un riavvio per visualizzare le modifiche", "F_BepInEx_UseNamePlates_T": "Mostra le Targhette Nome dei Giocatori", "F_BepInEx_UseNamePlates_D": "Se le targhette dei nomi devono essere mostrare sopra i compagni di squadra.", @@ -137,27 +141,31 @@ "F_BepInEx_UsePercent_T": "Mostra HP% invece della barra", "F_BepInEx_UsePercent_D": "Mostra la salute in % invece che usare la barra.", "F_BepInEx_ShowEffects_T": "Mostra Effetti", - "F_BepInEx_ShowEffects_D": "If status effects should be displayed below the health bar.", + "F_BepInEx_ShowEffects_D": "Se gli effetti di stato devono essere visualizzati sotto la barra di salute.", "F_BepInEx_ShowFactionIcon_T": "Mostra Icona Fazione del Giocatore", "F_BepInEx_ShowFactionIcon_D": "Mostra l'icona fazione del giocatore di fianco alla balla della salute", "F_BepInEx_HideInOptic_T": "Nascondi Targhetta Nome mentre miri", "F_BepInEx_HideInOptic_D": "Nascondi targhetta nome durante la mira con mirini PiP", - "F_BepInEx_OpticUseZoom_T": "Name Plates Use Optic Zoom", - "F_BepInEx_OpticUseZoom_D": "If name plate location should be displayed using the PiP optic camera.", - "F_BepInEx_DecOpacPeri_T": "Decrease Opacity In Peripheral", + "F_BepInEx_OpticUseZoom_T": "Targhette Dei Nomi Usando Zoom Ottico", + "F_BepInEx_OpticUseZoom_D": "Se la posizione della targhetta deve essere visualizzata usando la visuale con ottica PiP.", + "F_BepInEx_DecOpacPeri_T": "Diminuire Opacità Nei Margini", "F_BepInEx_DecOpacPeri_D": "Diminuisce l'opacità delle targhette dei nomi quando non si guarda un giocatore", - "F_BepInEx_NamePlateScale_T": "Name Plate Scale", + "F_BepInEx_NamePlateScale_T": "Dimensione Targhette Dei Nomi", "F_BepInEx_NamePlateScale_D": "Grandezza delle targhette dei nomi.", "F_BepInEx_AdsOpac_T": "Opacità durante la mira", - "F_BepInEx_AdsOpac_D": "The opacity of the name plates when aiming down sights.", + "F_BepInEx_AdsOpac_D": "Opacità delle targhette mentre si è in puntamento.", "F_BepInEx_MaxDistance_T": "Distanza massima da mostrare", - "F_BepInEx_MaxDistance_D": "The maximum distance at which name plates will become invisible, starts to fade at half the input value.", + "F_BepInEx_MaxDistance_D": "La distanza massima alla quale le targhette diventeranno invisibili, inizieranno a sbiadire a metà del valore selezionato.", "F_BepInEx_MinOpac_T": "Opacità Minima", "F_BepInEx_MinOpac_D": "Opacità minima targhette dei nomi", - "F_BepInEx_MinPlateScale_T": "Minimum Name Plate Scale", - "F_BepInEx_MinPlateScale_D": "The minimum scale of the name plates.", + "F_BepInEx_MinPlateScale_T": "Dimensione Minima Targhette", + "F_BepInEx_MinPlateScale_D": "La dimensione minima delle targhette.", "F_BepInEx_UseOcclusion_T": "Abilità Occlusion", - "F_BepInEx_UseOcclusion_D": "Use occlusion to hide the name plate when the player is out of sight.", + "F_BepInEx_UseOcclusion_D": "Usa l'occlusione per nascondere la targhetta quando il giocatore non è visibile.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Tipi di Quest", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Mostra Notifiche", @@ -179,72 +187,82 @@ "F_BepInEx_PingTime_T": "Tempo Ping", "F_BepInEx_PingTime_D": "Per quanto un ping deve essere mostrato", "F_BepInEx_PingAnimation_T": "Riproduci Animazione Ping", - "F_BepInEx_PingAnimation_D": "Plays the pointing animation automatically when pinging. Can interfere with gameplay.", + "F_BepInEx_PingAnimation_D": "Riproduce automaticamente l'animazione di puntamento quando si pinga. Può interferire con il gameplay.", "F_BepInEx_PingOptics_T": "Mostra Ping mentre Miri", - "F_BepInEx_PingOptics_D": "If pings should be displayed while aiming down an optics scope.", - "F_BepInEx_PingOpticZoom_T": "Ping Use Optic Zoom", - "F_BepInEx_PingOpticZoom_D": "If ping location should be displayed using the PiP optic camera.", - "F_BepInEx_PingScaleDistance_T": "Ping Scale With Distance", - "F_BepInEx_PingScaleDistance_D": "If ping size should scale with distance from player.", + "F_BepInEx_PingOptics_D": "Se il ping deve essere mostrato quando si mira con un mirino ottico.", + "F_BepInEx_PingOpticZoom_T": "Pinga Usando Zoom Ottico", + "F_BepInEx_PingOpticZoom_D": "Se il ping deve essere visualizzato usando la visuale con ottica PiP.", + "F_BepInEx_PingScaleDistance_T": "Il Ping Si Ridimensiona Con La Distanza", + "F_BepInEx_PingScaleDistance_D": "Se la grandezza del ping deve ridimensionarsi con la distanza dal giocatore.", "F_BepInEx_PingMinOpac_T": "Opacità Minima Ping", "F_BepInEx_PingMinOpac_D": "Opacità minima di un ping quando vengono guardati", "F_BepInEx_PingRange_T": "Mostra range del ping", "F_BepInEx_PingRange_D": "Mostra la distanza dal ping se abilitato", "F_BepInEx_PingSound_T": "Suono Ping", "F_BepInEx_PingSound_D": "Audio riprodotto durante il ping", - "F_BepInEx_FreeCamButton_T": "Free Camera Button", - "F_BepInEx_FreeCamButton_D": "Button used to toggle free camera.", - "F_BepInEx_SpectateBots_T": "Allow Spectating Bots", - "F_BepInEx_SpectateBots_D": "If we should allow spectating bots if all players are dead/extracted.", - "F_BepInEx_AZERTYMode_T": "AZERTY Mode", - "F_BepInEx_AZERTYMode_D": "If free camera should use AZERTY keys for input.", - "F_BepInEx_DroneMode_T": "Drone Mode", - "F_BepInEx_DroneMode_D": "If the free camera should move only along the vertical axis like a drone.", - "F_BepInEx_KeybindOverlay_T": "Keybind Overlay", - "F_BepInEx_KeybindOverlay_D": "If an overlay with all free cam keybinds should show.", - "F_BepInEx_DynamicAI_T": "Dynamic AI", - "F_BepInEx_DynamicAI_D": "Use the dynamic AI system, disabling AI when they are outside of all player's range.", - "F_BepInEx_DynamicAIRange_T": "Dynamic AI Range", - "F_BepInEx_DynamicAIRange_D": "The range at which AI will be disabled dynamically.", - "F_BepInEx_DynamicAIRate_T": "Dynamic AI Rate", - "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", - "F_BepInEx_DynamicAINoSnipers_T": "Ignore Snipers", - "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", - "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", - "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", - "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", - "F_BepInEx_DespawnFurthest_D": "When enforcing spawn limits, should the furthest bot be de-spawned instead of blocking the spawn. This will make for a much more active raid on a lower Max Bots count. Helpful for weaker PCs. Will only despawn pmcs and scavs. If you don't run a dynamic spawn mod, this will however quickly exhaust the spawns on the map, making the raid very dead instead.", - "F_BepInEx_DespawnMinDistance_T": "Despawn Minimum Distance", - "F_BepInEx_DespawnMinDistance_D": "The minimum distance to despawn a bot.", - "F_BepInEx_MaxBots_T": "Max Bots {0}", - "F_BepInEx_MaxBots_D": "Max amount of bots that can be active at the same time on {0}. Useful if you have a weaker PC. Set to 0 to use vanilla limits. Cannot be changed during a raid.", - "F_BepInEx_NativeSockets_T": "Native Sockets", - "F_BepInEx_NativeSockets_D": "Use NativeSockets for gameplay traffic. This uses direct socket calls for send/receive to drastically increase speed and reduce GC pressure. Only for Windows/Linux and might not always work.", - "F_BepInEx_ForceIP_T": "Force IP", - "F_BepInEx_ForceIP_D": "Forces the server when hosting to use this IP when broadcasting to the backend instead of automatically trying to fetch it. Leave empty to disable.", - "F_BepInEx_ForceBindIP_T": "Force Bind IP", - "F_BepInEx_ForceBindIP_D": "Forces the server when hosting to use this local adapter when starting the server. Useful if you are hosting on a VPN.", - "F_BepInEx_UDPPort_T": "UDP Port", - "F_BepInEx_UDPPort_D": "Port to use for UDP gameplay packets.", - "F_BepInEx_UseUPnP_T": "Use UPnP", - "F_BepInEx_UseUPnP_D": "Attempt to open ports using UPnP. Useful if you cannot open ports yourself but the router supports UPnP.", + "F_BepInEx_FreeCamButton_T": "Pulsante Visuale Libera", + "F_BepInEx_FreeCamButton_D": "Pulsante usato per togliere la visuale libera.", + "F_BepInEx_SpectateBots_T": "Permetti Modalità Spettatore Sui Bot", + "F_BepInEx_SpectateBots_D": "Si permette di guardare i bot se tutti i giocatori sono morti/estratti.", + "F_BepInEx_AZERTYMode_T": "Modalità AZERTY", + "F_BepInEx_AZERTYMode_D": "Se la visuale libera deve usare i tasti AZERTY per l'input.", + "F_BepInEx_DroneMode_T": "Modalità Drone", + "F_BepInEx_DroneMode_D": "Se la visuale libera deve muoversi solo lungo l'asse verticale come un drone.", + "F_BepInEx_KeybindOverlay_T": "Overlay Scorciatoie", + "F_BepInEx_KeybindOverlay_D": "Se un overlay con tutte le scorciatoie per la visuale libera deve essere mostrato.", + "F_BepInEx_DynamicAI_T": "IA Dinamica", + "F_BepInEx_DynamicAI_D": "Utilizzare il sistema dinamico dell'IA, disabilitando le IA quando sono al di fuori della portata di tutti i giocatori.", + "F_BepInEx_DynamicAIRange_T": "Portata IA Dinamica", + "F_BepInEx_DynamicAIRange_D": "La portata in cui l'IA sarà disabilitata dinamicamente.", + "F_BepInEx_DynamicAIRate_T": "Frequenza IA Dinamica", + "F_BepInEx_DynamicAIRate_D": "Quante volte l'IA Dinamica dovrebbe scansionare la portata da tutti i giocatori.", + "F_BepInEx_DynamicAINoSnipers_T": "Ignora Cecchini", + "F_BepInEx_DynamicAINoSnipers_D": "Indica se l'IA dinamica debba ignorare gli scav cecchini.", + "F_BepInEx_FikaGC_T": "Utilizza GC Fika", + "F_BepInEx_FikaGC_D": "Se un garbage collector personalizzato deve essere utilizzato per alleviare i problemi con l'uso di memoria.\nATTENZIONE: Sperimentale, potrebbe non avere alcun effetto sul vostro sistema.", + "F_BepInEx_EnforcedSpawnLimits_T": "Limiti Spawn forzati", + "F_BepInEx_EnforcedSpawnLimits_D": "Forza i limiti di generazione quando si generano bot, assicurandosi di non superare i limiti vanilla. Questo ha effetto principalmente quando si usano le mod di spawn o qualsiasi cosa che modifichi i limiti del bot. Non bloccherà gli spawn di bot speciali come i boss.", + "F_BepInEx_DespawnFurthest_T": "Despawn Del Più Lontano", + "F_BepInEx_DespawnFurthest_D": "Quando si applicano i limiti di spawn, il bot più lontano dovrebbe essere rimosso invece di bloccare lo spawn. Questo renderà un raid molto più attivo su un numero di bot Max più basso. Utile per i PC più deboli. Rimuoverà solo pmc e scav. Se non si esegue una mod di generazione dinamica, tuttavia, questo svuoterà rapidamente gli spawn sulla mappa, rendendo il raid molto morto invece.", + "F_BepInEx_DespawnMinDistance_T": "Distanza Minima Di Despawn", + "F_BepInEx_DespawnMinDistance_D": "La distanza minima per rimuovere un bot.", + "F_BepInEx_MaxBots_T": "Bot Massimi {0}", + "F_BepInEx_MaxBots_D": "Quantità massima di bot che possono essere attivi contemporaneamente {0}. Utile se hai un PC più debole. Imposta a 0 per usare i limiti vanilla. Non può essere modificato durante un raid.", + "F_BepInEx_NativeSockets_T": "Socket Nativi", + "F_BepInEx_NativeSockets_D": "Usa Socket Nativi per il traffico di gameplay. Questo utilizza le chiamate di socket dirette per inviare/ricevere aumentando drasticamente la velocità e riducendo la pressione sul GC. Solo per Windows/Linux e potrebbe non sempre funzionare.", + "F_BepInEx_ForceIP_T": "Forza IP", + "F_BepInEx_ForceIP_D": "Forza il server mentre l'hosta ad usare questo IP durante la trasmissione al backend invece di tentaredi recuperarlo automaticamente. Lasciare vuoto per disabilitare.", + "F_BepInEx_ForceBindIP_T": "Forza L'IP Binding", + "F_BepInEx_ForceBindIP_D": "Forza il server durante l'hosting a utilizzare questo adattatore locale quando si avvia il server. Utile se si sta ospitando su una VPN.", + "F_BepInEx_UDPPort_T": "Porta UDP", + "F_BepInEx_UDPPort_D": "Porta da utilizzare per i pacchetti di gioco UDP.", + "F_BepInEx_UseUPnP_T": "Usa UPnP", + "F_BepInEx_UseUPnP_D": "Tenta di aprire le porte usando UPnP. Utile se non è possibile aprire le porte manualmente e il router supporta UPnP.", "F_BepInEx_UseNatPunch_T": "Use NAT Punching", "F_BepInEx_UseNatPunch_D": "Use NAT punching when hosting a raid. Only works with fullcone NAT type routers and requires NatPunchServer to be running on the SPT server. UPnP, Force IP and Force Bind IP are disabled with this mode.", - "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", - "F_BepInEx_ConnectionTimeout_D": "How long it takes for a connection to be considered dropped if no packets are received.", + "F_BepInEx_ConnectionTimeout_T": "Connessione Scaduta", + "F_BepInEx_ConnectionTimeout_D": "Quanto tempo ci vuole perché una connessione sia considerata scaduta se non vengono ricevuti pacchetti.", "F_BepInEx_SendRate_T": "Send Rate", - "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", - "F_BepInEx_SmoothingRate_T": "Smoothing Rate", - "F_BepInEx_SmoothingRate_D": "Local simulation is behind by (Send Rate * Smoothing Rate). This guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter during interpolation.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\nSet this to 'High' if movement isn't smooth. Cannot be changed during a raid.", - "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", - "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", - "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", - "F_MMUI_InMenu": "In Menu", - "F_MMUI_InRaid": "In Raid", - "F_MMUI_InStash": "In Stash", - "F_MMUI_InHideout": "In Hideout", - "F_MMUI_InFlea": "In Flea", - "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Usa MultiThreading", + "F_BepInEx_NetMultiThread_D": "Se il componente di rete dovrebbe usare multi-thread. Questo può potenzialmente alleviare i problemi quando il gioco lagga, consentendo al gestore di rete di elaborare e mettere in coda pacchetti durante FPS bassi.\nQuesta è una funzione sperimentale e dovrebbe essere abilitata solo sui PC di fascia alta e sarà abilitata solo su un client headless.", + "F_BepInEx_VOIP_T": "Consenti VOIP", + "F_BepInEx_VOIP_D": "Se VOIP sarà abilitato quando si è in hosting.", + "F_BepInEx_DisableBotMetabolism_T": "Disabilita Il Metabolismo Dei Bot", + "F_BepInEx_DisableBotMetabolism_D": "Disabilita il metabolismo sui bot, impedendo loro di morire dalla perdita di energia/idratazione durante lunghi raid.", + "F_MMUI_OnlinePlayers": "GIOCATORI ONLINE: {0}", + "F_MMUI_InMenu": "Nel Menu", + "F_MMUI_InRaid": "Nel Raid", + "F_MMUI_InStash": "Nello Stash", + "F_MMUI_InHideout": "Nell'Hideout", + "F_MMUI_InFlea": "Nel Flea", + "F_MMUI_RaidDetails": "Giocando come {0} su {1}\nOrario: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", + "F_UI_LocaleError_H": "ERRORE TRADUZIONE", + "F_UI_LocaleError_D": "Si sono verificati errori durante il caricamento delle traduzioni Fika. La traduzione ora sta usando ripieghi per le intestazioni fallite. Si prega di segnalare agli sviluppatori!\n\nChiavi fallite: {0}", + "F_UI_EFTVersionError": "Stai tentando di utilizzare una versione diversa di EFT rispetto a quella in esecuzione dall'host del raid.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "Stai tentando di utilizzare una versione diversa di Fika rispetto a quella in esecuzione dall'host del raid.\nClient: {0}\nHost: {1}", + "F_UI_PlayerConnected": "{0} si è connesso", + "F_UI_PlayerDisconnected": "{0} si è disconnesso", + "F_UI_DisabledByServerHost": "Disabilitato dall'host del server" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/jp.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/jp.json index 1d3c7cb..0471273 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/jp.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/jp.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "レイドをホスト", "F_MMUI_JoinButton": "参加", "F_MMUI_SelectAmountHeader": "数を選択", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "セッションの設定", "F_MMUI_SelectAmountDescription": "何人がプレイしますか? (あなたを含めて)", "F_MMUI_UseDedicatedHost": "専用ホストを使用", "F_MMUI_StartButton": "開始", "F_MMUI_LoadingScreenHeader": "読み込み中", "F_MMUI_LoadingScreenDescription": "レイドの初期化中ですので、しばらくお待ちください... 時間がかかる場合があります。", - "F_MMUI_JoinAsSpectator": "Join as spectator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "観戦", + "F_MMUI_JoinAsSpectator_D": "観戦に参加", + "F_UI_StartRaid": "集結開始", + "F_UI_StartRaidDescription": "Starts the raid\nAfter clicking this additional players will not be able to join", "F_UI_CannotJoinRaidOtherMap": "別のマップで行われているレイドには参加できません。\nレイドマップ: {0}", "F_UI_CannotJoinRaidOtherTime": "別の時間帯で行われているレイドには参加できません。", "F_UI_CannotJoinRaidScavAsPMC": "PMC として SCAV レイドには参加できません。", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "バインドエラー", "F_UI_ErrorBindIP": "'{0}' はバインドするための有効なIPアドレスではありません! 'Force Bind IP' 設定を確認してください。", "F_UI_NoDedicatedClients": "専用クライアントは利用できません。", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Starts a raid\nOther players will be able to join you during the loading screen", + "F_UI_WaitForHostStartRaid": "ホストの襲撃開始を待っています…", "F_UI_WaitForHostFinishInit": "ホストがレイドの初期化を終えるのを待っています…", "F_UI_WaitForOtherPlayers": "他のプレイヤーが読み込みを終えるのを待っています…", "F_UI_RetrieveSpawnInfo": "サーバーからスポーン情報を取得しています…", @@ -92,14 +94,14 @@ "F_UI_ToSLong": "Fika へようこそ!\n\nFika は SPT 用の協力プレイ Mod で、友達と一緒にプレイすることができます。Fika は無料であり、今後も無料で提供されます。もしお金を支払った場合、それは詐欺です。また、収益化や寄付を目的とした公開サーバーのホストは許可されていません。\n\n利用規約に同意するには、このメッセージが消えるまで待ってください。\n\nDiscord への参加はこちらから: https://discord.gg/project-fika", "F_UI_ToSShort": "Fika へようこそ!\n\nFika は SPT 用の協力プレイ Mod で、友達と一緒にプレイすることができます。Fika は無料であり、今後も無料で提供されます。もしお金を支払った場合、それは詐欺です。また、収益化や寄付を目的とした公開サーバーのホストは許可されていません。\n\nDiscord への参加はこちらから: https://discord.gg/project-fika", "F_UI_RaidSettingsDescription": "In this mode, you can play online co-op with your friends without the risk of meeting others.\nAll game mechanics in the co-op mode (weather, bots, bosses, etc.) are identical to the online mechanics.\nOnly the hosts settings will take effect.", - "F_UI_CoopGameMode": "Fika Co-op Game Mode", - "F_UI_CoopRaidSettings": "Fika Co-op Raid Settings", - "F_UI_FikaAlwaysCoop": "Co-op is always enabled in Fika", + "F_UI_CoopGameMode": "フィカ・コープ・ゲームモード", + "F_UI_CoopRaidSettings": "フィカ協力集結設定", + "F_UI_FikaAlwaysCoop": "Fika では常に協力が有効です", "F_UI_UpnpFailed": "UPnP mapping failed. Make sure the selected port is not already open!\nDisable UPnP if you are using a VPN.", - "F_UI_InitWeather": "Generating weather...", + "F_UI_InitWeather": "天気を生成中…", "F_Notification_RaidStarted": "{0} started a raid on {1}", "F_Notification_ItemReceived": "You received a {0} from {1}", - "F_Notification_RaidSettingsDisabled": "Editing of RaidSettings is disabled by the server host", + "F_Notification_RaidSettingsDisabled": "サーバのホストによりレイド設定の編集は無効になっています", "F_UI_ExtractMessage": "Press {0} to extract", "F_UI_DownloadProfile": "DOWNLOAD PROFILE", "F_BepInEx_H_Advanced": "Advanced", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Gameplay", "F_BepInEx_OfficialVersion_T": "Official Version", "F_BepInEx_OfficialVersion_D": "Show official version instead of Fika version.", + "F_BepInEx_UseHeadless_T": "Auto Use Headless", + "F_BepInEx_UseHeadless_D": "Always tick the box to use the Headless Client if available", "F_BepInEx_ShowFeed_T": "Show Feed", "F_BepInEx_ShowFeed_D": "Enable custom notifications when a player dies, extracts, kills a boss, etc.", "F_BepInEx_AutoExtract_T": "Auto Extract", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "The minimum scale of the name plates.", "F_BepInEx_UseOcclusion_T": "Use Occlusion", "F_BepInEx_UseOcclusion_D": "Use occlusion to hide the name plate when the player is out of sight.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Quest Types", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Show Notifications", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", "F_BepInEx_DynamicAINoSnipers_T": "Ignore Snipers", "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "How long it takes for a connection to be considered dropped if no packets are received.", "F_BepInEx_SendRate_T": "Send Rate", - "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", - "F_BepInEx_SmoothingRate_T": "Smoothing Rate", - "F_BepInEx_SmoothingRate_D": "Local simulation is behind by (Send Rate * Smoothing Rate). This guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter during interpolation.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\nSet this to 'High' if movement isn't smooth. Cannot be changed during a raid.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "In Hideout", "F_MMUI_InFlea": "In Flea", "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/kr.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/kr.json index 97a7ab1..7fc9d8a 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/kr.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/kr.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "레이드 호스트하기", "F_MMUI_JoinButton": "참가", "F_MMUI_SelectAmountHeader": "양을 선택하십시오.", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "세션 설정", "F_MMUI_SelectAmountDescription": "당신을 포함하여 몇 명의 플레이어가 참가합니까?", "F_MMUI_UseDedicatedHost": "전담 호스트를 사용하여 주십시오", "F_MMUI_StartButton": "시작", "F_MMUI_LoadingScreenHeader": "로딩 중", "F_MMUI_LoadingScreenDescription": "레이드를 동기화하는 중입니다... 잠시만 기다려주십시오.", - "F_MMUI_JoinAsSpectator": "관전모드로 참여하기", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Spectate", + "F_MMUI_JoinAsSpectator_D": "Join the game as a spectator", + "F_UI_StartRaid": "레이드 시작", + "F_UI_StartRaidDescription": "Starts the raid\nAfter clicking this additional players will not be able to join", "F_UI_CannotJoinRaidOtherMap": "다른 맵에서 진행중인 레이드에는 참가 할 수 없습니다.\n레이드 맵: {0}", "F_UI_CannotJoinRaidOtherTime": "다른 시간대의 레이드에는 참가할 수 없습니다.", "F_UI_CannotJoinRaidScavAsPMC": "PMC 캐릭터로는 Scav레이드에 참가 할 수없습니다.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "바인딩 에러", "F_UI_ErrorBindIP": "{0} 은 바인드하기 유효한 IP주소가 아닙니다. IP 강제 바인드 설정을 확인해주십시오.", "F_UI_NoDedicatedClients": "사용가능한 전담 클라이언트가 없습니다.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Starts a raid\nOther players will be able to join you during the loading screen", + "F_UI_WaitForHostStartRaid": "호스트가 레이드 시작하기 기다리는 중...", "F_UI_WaitForHostFinishInit": "호스트의 레이드 초기화 대기중", "F_UI_WaitForOtherPlayers": "다른 플레이어들의 로딩 대기중", "F_UI_RetrieveSpawnInfo": "서버에서 스폰 정보 받아오는중", @@ -101,7 +103,7 @@ "F_Notification_ItemReceived": "{1} 에게서 {0} 를 받았습니다", "F_Notification_RaidSettingsDisabled": "레이드 설정 편집이 서버 호스트에 의해서 비활성화 되어있습니다.", "F_UI_ExtractMessage": "{0} 을 눌러 탈출을 완료하세요", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", + "F_UI_DownloadProfile": "프로필 다운로드", "F_BepInEx_H_Advanced": "고급", "F_BepInEx_H_Coop": "코옵", "F_BepInEx_H_Coop_NamePlates": "코옵 | 네임 플레이트", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "게임플레이", "F_BepInEx_OfficialVersion_T": "공식 버전", "F_BepInEx_OfficialVersion_D": "피카 버전 대신 공식 버전 보이기", + "F_BepInEx_UseHeadless_T": "Auto Use Headless", + "F_BepInEx_UseHeadless_D": "Always tick the box to use the Headless Client if available", "F_BepInEx_ShowFeed_T": "피드 보이기", "F_BepInEx_ShowFeed_D": "플레이어가 죽거나 탈출하거나 보스 킬 등을 할 경우 뜨는 커스텀 메시지 알림 활성화", "F_BepInEx_AutoExtract_T": "자동 탈출완료", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "네임 플레이트들의 최소 비율", "F_BepInEx_UseOcclusion_T": "오클루전 사용", "F_BepInEx_UseOcclusion_D": "오크루전으로 플레이어가 시야에서 벗어나면 네임플레이트를 숨깁니다.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "퀘스트 타입", "F_BepInEx_QuestTypes_D": "보내거나 받을 퀘스트 타입. 비컨 설치하기는 마커랑 아이템 둘 다 해당됩니다.", "F_BepInEx_QSNotifications_T": "알림 보이기", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "다이나믹 Ai 적용 시 플레이어로부터 거리를 얼마나 자주 스캔할지", "F_BepInEx_DynamicAINoSnipers_T": "스나이퍼 무시", "F_BepInEx_DynamicAINoSnipers_D": "다이나믹 AI 적용 시 스나이퍼 스캐브는 건너뛸지 여부", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "스폰 제한 강제", "F_BepInEx_EnforcedSpawnLimits_D": "봇 스폰 시에 최대 스폰 수 제한 적용, 바닐라 제한 넘지 않도록 주의. 봇 스폰 관련 모드를 사용하게되면 이 제한에 영항을 받게 됩니다. 보스같은 특수 봇 타입은 영향을 받지 않습니다.", "F_BepInEx_DespawnFurthest_T": "최대거리 봇 디스폰", @@ -233,18 +243,26 @@ "F_BepInEx_ConnectionTimeout_T": "연결 타임아웃", "F_BepInEx_ConnectionTimeout_D": "해당 시간동안 패킷을 받지 못하면 연결을 드랍한걸로 간주함", "F_BepInEx_SendRate_T": "전송률", - "F_BepInEx_SendRate_D": "초당 얼마나 움직임 패킷을 보낼 지 설정.(적을수록 대역폭을 적게 사용하지만 보간과정에서 시간 더 소요)\n호스트만 영향을 받으며 모든 클라이언트와 동기화 됨.\n초당 값임\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nMedium 보다 높히지 않을것을 권장함. 그 이상은 마진이 미비함.", - "F_BepInEx_SmoothingRate_T": "평활화 레이트", - "F_BepInEx_SmoothingRate_D": "로컬 시뮬레이션은 전송률*평활화 레이트 이하로 따라감. 이를 통해서 버퍼에 스냅샷을 충분히 유지하여 보간 과정에서 랙과 끊김을 완화시킴.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\n움직임이 부드럽지 않으면 High로 설정하기를 권장. 레이드 도중에 변경할 수 없음.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "봇 신진대사 비활성화", "F_BepInEx_DisableBotMetabolism_D": "봇 신진대사 가능 비활성화, 물/밥 부족으로 봇들이 죽는것 방지.", - "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", - "F_MMUI_InMenu": "In Menu", - "F_MMUI_InRaid": "In Raid", - "F_MMUI_InStash": "In Stash", - "F_MMUI_InHideout": "In Hideout", - "F_MMUI_InFlea": "In Flea", - "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_MMUI_OnlinePlayers": "온라인 플레이어 수: {0}", + "F_MMUI_InMenu": "메뉴", + "F_MMUI_InRaid": "레이드 중", + "F_MMUI_InStash": "스태쉬", + "F_MMUI_InHideout": "하이드아웃", + "F_MMUI_InFlea": "플리마켓", + "F_MMUI_RaidDetails": "{1} 에서 {0}로 플레이\n시간: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", + "F_UI_LocaleError_H": "언어설정 오류", + "F_UI_LocaleError_D": "피카 언어 DB를 불러오던 중에 오류가 발생하였습니다. 불러오기 실패한 부분은 대체언어를 사용하겠습니다.\n실패한 키들: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/pl.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/pl.json index e79cff6..4cce25a 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/pl.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/pl.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "HOSTUJ RAID", "F_MMUI_JoinButton": "DOŁĄCZ", "F_MMUI_SelectAmountHeader": "WYBIERZ ILOŚĆ", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "USTAWIENIA SESJI", "F_MMUI_SelectAmountDescription": "Ile osób (łącznie z tobą) będzie grało?", "F_MMUI_UseDedicatedHost": "Użyj Dedykowanego Gospodarza", "F_MMUI_StartButton": "START", "F_MMUI_LoadingScreenHeader": "ŁADOWANIE", "F_MMUI_LoadingScreenDescription": "Proszę czekać na inicjalizację raidu... to może chwilę potrwać.", - "F_MMUI_JoinAsSpectator": "Dołącz jako obserwator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Obserwuj", + "F_MMUI_JoinAsSpectator_D": "Dołącz do gry jako widz", + "F_UI_StartRaid": "Rozpocznij Raid", + "F_UI_StartRaidDescription": "Rozpoczyna raid\nPo kliknięciu tego dodatkowi gracze nie będą mogli dołączać", "F_UI_CannotJoinRaidOtherMap": "Nie można dołączyć do raidu który odgrywa się na innej mapie.\nRaid odgrywa się na mapie: {0}", "F_UI_CannotJoinRaidOtherTime": "Nie możesz dołączyć do raidu który odgrywa się o innej porze.", "F_UI_CannotJoinRaidScavAsPMC": "Nie możesz dołączyć do raidu scavów jako PMC.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "BŁĄD POWIĄZANIA", "F_UI_ErrorBindIP": "'{0}' nie jest prawidłowym adresem IP do powiązania! Sprawdź ustawienie 'Wymuś powiązanie IP'.", "F_UI_NoDedicatedClients": "Brak dostępnych dedykowanych klientów.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Rozpoczyna raid\nInni gracze będą mogli dołączyć podczas ekranu ładowania", + "F_UI_WaitForHostStartRaid": "Oczekiwanie na rozpoczęcie raidu przez hosta...", "F_UI_WaitForHostFinishInit": "Oczekiwanie na zakończenie inicjalizacji raidu przez gospodarza...", "F_UI_WaitForOtherPlayers": "Oczekiwanie na zakończenie ładowania pozostałych graczy...", "F_UI_RetrieveSpawnInfo": "Pobieranie informacji o spawnie z serwera...", @@ -101,7 +103,7 @@ "F_Notification_ItemReceived": "Otrzymałeś {0} od {1}", "F_Notification_RaidSettingsDisabled": "Edytowanie ustawień raidu jest wyłączone przez hosta serwera", "F_UI_ExtractMessage": "Wciśnij {0} aby wyjść", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", + "F_UI_DownloadProfile": "POBIERZ PROFIL", "F_BepInEx_H_Advanced": "Zaawansowane", "F_BepInEx_H_Coop": "Kooperacja", "F_BepInEx_H_Coop_NamePlates": "Kooperacja | Nazwy Graczy", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Rozgrywka", "F_BepInEx_OfficialVersion_T": "Oficjalna Wersja", "F_BepInEx_OfficialVersion_D": "Pokazuj oficjalną wersję zamiast wersji Fika.", + "F_BepInEx_UseHeadless_T": "Automatycznie używaj trybu headless", + "F_BepInEx_UseHeadless_D": "Zawsze zaznacz pole, aby użyć trybu headless, jeśli jest dostępny", "F_BepInEx_ShowFeed_T": "Pokazuj Aktualności", "F_BepInEx_ShowFeed_D": "Włącz niestandardowe powiadomienia, gdy gracz umrze, wyjdzie z raidu, zabije bossa, itp.", "F_BepInEx_AutoExtract_T": "Automatyczne Wyjście", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "Minimalna skala nazw graczy.", "F_BepInEx_UseOcclusion_T": "Użyj Okluzji", "F_BepInEx_UseOcclusion_D": "Użyj okluzji, aby schować nazwy graczy, gdy gracz jest poza zasięgiem widzenia.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Typy zadań", "F_BepInEx_QuestTypes_D": "Które typy zadań do odebrania i wysyłania. PlaceBeacon to zarówno markery, jak i przedmioty.", "F_BepInEx_QSNotifications_T": "Pokazuj Powiadomienia", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "Jak często dynamiczne AI powinno sprawdzać odległość od graczy.", "F_BepInEx_DynamicAINoSnipers_T": "Ignoruj snajperów", "F_BepInEx_DynamicAINoSnipers_D": "Czy dynamiczne AI powinno ignorować scav snajperów", + "F_BepInEx_FikaGC_T": "Użyj Fika GC", + "F_BepInEx_FikaGC_D": "Jeśli niestandardowy garbage collector powinien być użyty w celu złagodzenia problemów związanych z pamięcią.\nOSTRZEŻENIE: Funkcja eksperymentalna — może nie mieć żadnego wpływu na Twój system.", "F_BepInEx_EnforcedSpawnLimits_T": "Wymuszone Limity Odradzania", "F_BepInEx_EnforcedSpawnLimits_D": "Wymusza limit odrodzeń podczas odradzania botów, upewniając się, by nie przekroczyć domyślnych limitów. Ma znaczenie głównie podczas korzystania z modyfikacji zmieniających zasady odradzania lub cokolwiek co zmienia limity botów. Nie ma zastosowania do botów specjalnych tj. bossów.", "F_BepInEx_DespawnFurthest_T": "Zniszcz Najdalsze", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Limit Czasu Połączenia", "F_BepInEx_ConnectionTimeout_D": "Jak długo trwa ustalenie połączenia za przerwane, jeśli żadne pakiety nie przychodzą.", "F_BepInEx_SendRate_T": "Tempo Wysyłania Pakietów", - "F_BepInEx_SendRate_D": "Jak często na sekundę, pakiety powiązane z ruchem powinny być przesyłane (mniej = mniejsze użycie sieci, lekko zwiększone opóźnienie podczas interpolacji)\nTa opcja ma efekt tylko dla hosta i jest synchronizowania pomiędzy klientami.\nIlość na sekundę:\n\nBardzo niska = 10\nNiska = 15\nŚrednia = 20\nWysoka = 30\n\nZalecane używanie wartości nie wyżej niż \"Średnie\" gdyż wyższe wartości nie powodują znacznej poprawy.", - "F_BepInEx_SmoothingRate_T": "Częstotliwość Wygładzania", - "F_BepInEx_SmoothingRate_D": "Lokalna symulacja jest opóźniona o (Częstotliwość wysyłania * częstotliwość wygładzania). To gwarantuje wystarczającą ilość próbek w buforze w celu uniknięcia lagów i przeskakiwania podczas interpolacji.\n\nNiski = 1.5\nŚredni = 2\nWysoki = 2.5\n\nUstaw tę opcję na \"Wysoki\" jeśli ruch jest poszarpany. Nie można zmienić w czasie raidu.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Używaj wielowątkowości", + "F_BepInEx_NetMultiThread_D": "Jeżeli komponent sieci powinien stosować wielowątkowanie. Może to potencjalnie złagodzić problemy związane z lagami, umożliwiając menedżerowi sieci przetwarzanie i kolejkowanie pakietów gdy FPSy są niskie.\nJest to funkcja eksperymentalna i powinna być włączona tylko na wysokiej klasy komputerach i będzie włączona tylko na kliencie headless.", + "F_BepInEx_VOIP_T": "Zezwalaj na VOIP", + "F_BepInEx_VOIP_D": "Czy VOIP ma zostać włączony podczas hostowania.", "F_BepInEx_DisableBotMetabolism_T": "Wyłącz Metabolizm Botów", "F_BepInEx_DisableBotMetabolism_D": "Wyłącza metabolizm botów, zapobiegając ich umieraniu z powodu utraty energi/nawodnienia podczas długich raidów.", "F_MMUI_OnlinePlayers": "GRACZE ONLINE: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "W Kryjówce", "F_MMUI_InFlea": "Przegląda Rynek", "F_MMUI_RaidDetails": "Gra jako {0} na obszarze {1}\nCzas: {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", + "F_UI_LocaleError_H": "BŁĄD TŁUMACZEŃ", + "F_UI_LocaleError_D": "Podczas wczytywania tłumaczeń Fika wystąpiły błędy. Tłumaczenie korzysta teraz z zapasowych plików dla nieudanych nagłówków. Proszę zgłosić to deweloperom!\n\nNieudane klucze: {0}", + "F_UI_EFTVersionError": "Próbujesz użyć innej wersji EFT niż ta, w której host uruchomił raid.\nKlient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "Próbujesz użyć innej wersji Fika niż ta, w której host uruchomił raid.\nKlient: {0}\nHost: {1}", + "F_UI_PlayerConnected": "{0} połączył się", + "F_UI_PlayerDisconnected": "{0} rozłączył się", + "F_UI_DisabledByServerHost": "Wyłączone przez hosta serwera" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/po.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/po.json index 5c906de..07a3da7 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/po.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/po.json @@ -43,9 +43,10 @@ "F_MMUI_StartButton": "INICIAR", "F_MMUI_LoadingScreenHeader": "Carregando", "F_MMUI_LoadingScreenDescription": "Aguarde enquanto a incursão é inicializada... Isso pode levar algum tempo.", - "F_MMUI_JoinAsSpectator": "Junte-se como espectador", + "F_MMUI_JoinAsSpectator": "Assistir", + "F_MMUI_JoinAsSpectator_D": "Entrar no jogo como espectador", "F_UI_StartRaid": "Começar Incursão", - "F_UI_StartRaidDescription": "Começa a Incursão\nNovos jogadores não poderão entrar", + "F_UI_StartRaidDescription": "Começa a incursão\nApós clicar nisso jogadores adicionais não serão capazes de entrar", "F_UI_CannotJoinRaidOtherMap": "Não é possível se juntar a uma incursão que está em outro mapa.\nMapa da Incursão: {0}", "F_UI_CannotJoinRaidOtherTime": "Não é possível participar de uma incursão que está em outra hora.", "F_UI_CannotJoinRaidScavAsPMC": "Você não pode entrar em um incursão de Scav como um PMC.", @@ -65,6 +66,7 @@ "F_UI_ErrorBindIPHeader": "ERRO DE VINCULAÇÃO", "F_UI_ErrorBindIP": "'{0}' não é um endereço IP válido para vincular! Verifique sua configuração 'Forçar vinculação de IP'.", "F_UI_NoDedicatedClients": "Não há clientes dedicados disponíveis.", + "F_UI_HostRaidTooltip": "Inicia uma incursão\nOutros jogadores poderão entrar enquanto está na tela de carregamento", "F_UI_WaitForHostStartRaid": "Esperando o anfitrião iniciar a incursão...", "F_UI_WaitForHostFinishInit": "Esperando o host terminar a inicialização da incursão...", "F_UI_WaitForOtherPlayers": "Esperando que outros jogadores terminem de carregar...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Jogabilidade", "F_BepInEx_OfficialVersion_T": "Versão Oficial", "F_BepInEx_OfficialVersion_D": "Mostrar versão oficial ao invés da versão Fika.", + "F_BepInEx_UseHeadless_T": "Usar Automaticamente Headless", + "F_BepInEx_UseHeadless_D": "Sempre marque a caixa para usar o Cliente Headless se disponível", "F_BepInEx_ShowFeed_T": "Exibir Feed", "F_BepInEx_ShowFeed_D": "Ativar notificações personalizadas quando um jogador morrer, extrair, matar um chefe, etc.", "F_BepInEx_AutoExtract_T": "Auto Extrair", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "A escala mínima das placas de nomes.", "F_BepInEx_UseOcclusion_T": "Usar Oclusão", "F_BepInEx_UseOcclusion_D": "Use oclusão para esconder a placa de nome quando o jogador estiver fora de vista.", + "F_BepInEx_HealthColorFull_T": "Cor de Saúde Cheia", + "F_BepInEx_HealthColorFull_D": "A cor mostrada quando em Saúde cheia. Ela irá gradualmente mover em direção à 'Cor de Saúde Baixa' quando HP diminuir.", + "F_BepInEx_HealthColorLow_T": "Cor de Saúde Baixa", + "F_BepInEx_HealthColorLow_D": "A cor mostrada quando em Saúde BAIXA. Ela irá gradualmente mover em direção à 'Cor de Saúde Cheia' quando HP aumentar.", "F_BepInEx_QuestTypes_T": "Tipos de Missão", "F_BepInEx_QuestTypes_D": "Quais tipos de missões receber e enviar. Colocar Itens são marcadores e itens.", "F_BepInEx_QSNotifications_T": "Mostrar notificações", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "Com que frequência a IA Dinâmica deve procurar o intervalo entre todos os jogadores.", "F_BepInEx_DynamicAINoSnipers_T": "Ignorar Franco-atiradores", "F_BepInEx_DynamicAINoSnipers_D": "Se AI Dinâmica deve ignorar os Franco-Atiradores.", + "F_BepInEx_FikaGC_T": "Usar GC do Fika", + "F_BepInEx_FikaGC_D": "Se um coletor de lixo (GC) customizado deve ser usado para aliviar problemas com uso de memória.\nAVISO: Experimental, talvez não tenha efeito no seu sistema.", "F_BepInEx_EnforcedSpawnLimits_T": "Limite de Spawn forçado", "F_BepInEx_EnforcedSpawnLimits_D": "Força limites de spawn ao gerar bots, certifique-se de não ultrapassar os limites padrões. Isso afeta principalmente ao usar mods de spawn ou qualquer coisa que modifique os limites do bot. Não bloqueará bots especiais, como chefes.", "F_BepInEx_DespawnFurthest_T": "Despawnar o mais longe", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Tempo de Limite de Conexão", "F_BepInEx_ConnectionTimeout_D": "Quanto tempo leva para uma conexão ser considerada perdida se nenhum pacote for recebido.", "F_BepInEx_SendRate_T": "Taxa de Envio", - "F_BepInEx_SendRate_D": "Com que frequência por segundo pacotes de movimento devem ser enviados (menor = menos largura de banda usada, um pouco mais de atraso durante a interpolação)\nIsto só afeta o anfitrião e será sincronizado com todos os clientes.\nValor é por segundo:\n\nMuito Baixo = 10\nBaixo = 15\nMédio = 20\nAlto = 30\n\nRecomendado deixar não mais que médio, já que os ganhos são insignificantes depois.", - "F_BepInEx_SmoothingRate_T": "Taxa de Suavização", - "F_BepInEx_SmoothingRate_D": "Simulação local está atrasada em (Taxa de envio * Taxa de Suavização). Isto garante que sempre temos snapshots suficientes no buffer para mitigar lags e jitter durante a interpolação.\n\nBaixa = 1,5\nMédio = 2\nAlto = 2,5\n\nDefina isto para 'Alto' se o movimento não for suave. Não pode ser alterado durante uma incursão.", + "F_BepInEx_SendRate_D": "Com que frequência os pacotes de movimento devem ser enviados por segundo (Baixo = menor largura de banda usada, leve aumento de atraso durante interpolação)\nIsso afeta apenas o host e irá ser sincronizado para todos os clientes.\nQuantidade é por secundo:\nBaixo = 10\nMédio = 20\nAlto = 30\n\nRecomendado a deixar em alto ao invés de médio como os ganhos serão insignificantes depois.", + "F_BepInEx_NetMultiThread_T": "Utilizar MultiThreading", + "F_BepInEx_NetMultiThread_D": "Se o componente de rede deveria usar multi-threading. Isso pode potencialmente aliviar problema quando o jogo da lag, permitindo o gerenciador de rede processar a fila de pacotes durante FPS baixo.\nIsso é um recurso experimental e deve ser somente ativado em PCs de alta performance e deverá ser ativado apenas em cliesntes headless.", + "F_BepInEx_VOIP_T": "Permitir VOIP", + "F_BepInEx_VOIP_D": "Se VOIP estará ativo quando você hospedar.", "F_BepInEx_DisableBotMetabolism_T": "Desativar o metabolismo do Bot", "F_BepInEx_DisableBotMetabolism_D": "Desabilita o metabolismo dos Bots, impedindo que eles morram por perda de energia/hidratação durante longas incursões.", "F_MMUI_OnlinePlayers": "JOGADORES ON-LINE: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "No esconderijo", "F_MMUI_InFlea": "Na pulga", "F_MMUI_RaidDetails": "Jogando como {0} em {1}\nHora: {2}", + "F_Mod_VerificationFailure": "A verificação Crc32 do Fika.Core.dll falhou, você não poderá jogar com outras pessoas!\nVerifique seu arquivo de log para mais informações.", "F_UI_LocaleError_H": "ERRO NAS LOCALIZAÇÕES", - "F_UI_LocaleError_D": "Houve erros ao carregar as localizações do Fika. A tradução agora está usando os originais para os cabeçalhos que falharam. Por favor, reporte isso para os desenvolvedores!\n\nChaves falhas: {0}" + "F_UI_LocaleError_D": "Houve erros ao carregar as localizações do Fika. A tradução agora está usando os originais para os cabeçalhos que falharam. Por favor, reporte isso para os desenvolvedores!\n\nChaves falhas: {0}", + "F_UI_EFTVersionError": "Você está usando uma versão diferente do EFT do qual o host da incursão atual está usando.\nCliente: {0}\nHost: {1}", + "F_UI_FikaVersionError": "Você está usando uma versão diferente do Fika do qual o host da incursão atual está usando.\nCliente: {0}\nHost: {1}", + "F_UI_PlayerConnected": "{0} se conectou", + "F_UI_PlayerDisconnected": "{0} se desconectou", + "F_UI_DisabledByServerHost": "Desabilitado pelo hospedeiro do servidor" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ro.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ro.json index 923e442..1045768 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ro.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ro.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "GĂZDUIEȘTE RAID", "F_MMUI_JoinButton": "ALĂTURĂ-TE", "F_MMUI_SelectAmountHeader": "SELECTAȚI SUMA", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "SETĂRI SESIUNE", "F_MMUI_SelectAmountDescription": "Câți oameni (inclusiv dumneavoastră) vor juca?", "F_MMUI_UseDedicatedHost": "Utilizați o gazdă dedicată", "F_MMUI_StartButton": "Începe", "F_MMUI_LoadingScreenHeader": "Se încarcă", "F_MMUI_LoadingScreenDescription": "Vă rugăm să așteptați până la inițializarea raidului... acest lucru poate dura un timp.", - "F_MMUI_JoinAsSpectator": "Join as spectator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Spectate", + "F_MMUI_JoinAsSpectator_D": "Alătură-te jocului ca spectator", + "F_UI_StartRaid": "Începe Raid", + "F_UI_StartRaidDescription": "Începe raid-ul\nDupă apăsare, alți jucători nu se vor mai putea alătura", "F_UI_CannotJoinRaidOtherMap": "Nu vă puteți alătura unui raid care se află pe altă hartă. Hartă raid: {0}", "F_UI_CannotJoinRaidOtherTime": "Nu vă puteți alătura unui raid care are loc pe alt interval de timp.", "F_UI_CannotJoinRaidScavAsPMC": "Nu vă puteți alătura unui raid scav ca PMC.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "EROAREDE DE LEGARE", "F_UI_ErrorBindIP": "„{0}” nu este o adresă IP validă la care să vă legați! Verificați setarea „Forțare legare IP”.", "F_UI_NoDedicatedClients": "Nu sunt disponibili clienți dedicați.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Începe un raid\nAlți jucători se vor putea alătura pe durata ecranului de încărcare", + "F_UI_WaitForHostStartRaid": "Se așteaptă ca gazda să înceapă raid-ul...", "F_UI_WaitForHostFinishInit": "Se așteaptă ca gazda să termine inițializarea raidului...", "F_UI_WaitForOtherPlayers": "Se așteaptă ca alți jucători să termine încărcarea...", "F_UI_RetrieveSpawnInfo": "Se preiau informații despre punctele de apariție de pe server...", @@ -101,19 +103,21 @@ "F_Notification_ItemReceived": "Ai primit un/o {0} de la {1}", "F_Notification_RaidSettingsDisabled": "Editing of RaidSettings is disabled by the server host", "F_UI_ExtractMessage": "Press {0} to extract", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", - "F_BepInEx_H_Advanced": "Advanced", - "F_BepInEx_H_Coop": "Coop", + "F_UI_DownloadProfile": "DESCARCĂ PROFIL", + "F_BepInEx_H_Advanced": "Avansat", + "F_BepInEx_H_Coop": "Cooperativ", "F_BepInEx_H_Coop_NamePlates": "Coop | Name Plates", "F_BepInEx_H_Coop_QuestSharing": "Coop | Quest Sharing", "F_BepInEx_H_Coop_Pinging": "Coop | Pinging", "F_BepInEx_H_Coop_Debug": "Coop | Debug", - "F_BepInEx_H_Performance": "Performance", + "F_BepInEx_H_Performance": "Performanță", "F_BepInEx_H_PerformanceBots": "Performance | Max Bots", - "F_BepInEx_H_Network": "Network", + "F_BepInEx_H_Network": "Rețea", "F_BepInEx_H_Gameplay": "Gameplay", "F_BepInEx_OfficialVersion_T": "Official Version", "F_BepInEx_OfficialVersion_D": "Show official version instead of Fika version.", + "F_BepInEx_UseHeadless_T": "Auto Use Headless", + "F_BepInEx_UseHeadless_D": "Always tick the box to use the Headless Client if available", "F_BepInEx_ShowFeed_T": "Show Feed", "F_BepInEx_ShowFeed_D": "Enable custom notifications when a player dies, extracts, kills a boss, etc.", "F_BepInEx_AutoExtract_T": "Auto Extract", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "The minimum scale of the name plates.", "F_BepInEx_UseOcclusion_T": "Use Occlusion", "F_BepInEx_UseOcclusion_D": "Use occlusion to hide the name plate when the player is out of sight.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Quest Types", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Show Notifications", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", "F_BepInEx_DynamicAINoSnipers_T": "Ignore Snipers", "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "How long it takes for a connection to be considered dropped if no packets are received.", "F_BepInEx_SendRate_T": "Send Rate", - "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", - "F_BepInEx_SmoothingRate_T": "Smoothing Rate", - "F_BepInEx_SmoothingRate_D": "Local simulation is behind by (Send Rate * Smoothing Rate). This guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter during interpolation.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\nSet this to 'High' if movement isn't smooth. Cannot be changed during a raid.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "In Hideout", "F_MMUI_InFlea": "In Flea", "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ru.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ru.json index 0f424ca..47e9e49 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ru.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/ru.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "СОЗДАТЬ РЕЙД", "F_MMUI_JoinButton": "ВОЙТИ", "F_MMUI_SelectAmountHeader": "ВЫБЕРИТЕ КОЛ-ВО", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "НАСТРОЙКИ СЕССИИ", "F_MMUI_SelectAmountDescription": "Сколько человек (включая вас) будет играть?", "F_MMUI_UseDedicatedHost": "Использовать выделенный хост", "F_MMUI_StartButton": "НАЧАТЬ", "F_MMUI_LoadingScreenHeader": "ЗАГРУЗКА", "F_MMUI_LoadingScreenDescription": "Пожалуйста, подождите, пока рейд инициализируется... это может занять некоторое время.", - "F_MMUI_JoinAsSpectator": "Присоединиться как зритель", + "F_MMUI_JoinAsSpectator": "Наблюдать", + "F_MMUI_JoinAsSpectator_D": "Присоединиться к игре как наблюдатель", "F_UI_StartRaid": "Начать Рейд", - "F_UI_StartRaidDescription": "Начать рейд\nНовые игроки не смогут присоединиться", + "F_UI_StartRaidDescription": "Начинает рейд\nПосле нажатия, другие игроки не смогут присоединиться", "F_UI_CannotJoinRaidOtherMap": "Невозможно присоединиться к рейду на другой карте.\nРейд на карте: {0}", "F_UI_CannotJoinRaidOtherTime": "Невозможно присоединиться к рейду в другом времени.", "F_UI_CannotJoinRaidScavAsPMC": "Вы не можете присоединиться к рейду дикого как ЧВК.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "ОШИБКА ЗАПИСИ", "F_UI_ErrorBindIP": "'{0}' не является допустимым IP-адресом для привязки! Проверьте параметр 'Принудительная привязка к IP'.", "F_UI_NoDedicatedClients": "Нет выделенных клиентов.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Начинает рейд\nДругие игроки смогут присоединиться к вам во время экрана загрузки", + "F_UI_WaitForHostStartRaid": "Ожидание начала рейда хостом...", "F_UI_WaitForHostFinishInit": "Ожидание завершения инициализации рейда...", "F_UI_WaitForOtherPlayers": "Ожидание загрузки других игроков...", "F_UI_RetrieveSpawnInfo": "Получение информации о спавне с сервера...", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Геймплей", "F_BepInEx_OfficialVersion_T": "Официальная версия", "F_BepInEx_OfficialVersion_D": "Показывать официальную версию вместо Fika.", + "F_BepInEx_UseHeadless_T": "Автоматически использовать Headless", + "F_BepInEx_UseHeadless_D": "Всегда отмечать галочку для использования Headless-клиента при его наличии", "F_BepInEx_ShowFeed_T": "Лента событий", "F_BepInEx_ShowFeed_D": "Включить пользовательские уведомления, когда игрок умирает, выходит с рейда, убивает босса и т. д.", "F_BepInEx_AutoExtract_T": "Автоматический выход", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "Устанавливает минимальный размер табличек с именами игроков.", "F_BepInEx_UseOcclusion_T": "Использовать окклюзию", "F_BepInEx_UseOcclusion_D": "Использовать окклюзию, чтобы скрыть табличку с именем, когда игрок вне поля зрения.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Типы заданий", "F_BepInEx_QuestTypes_D": "Определяет, какие типы квестов получать и отправлять. Тип квеста «Установить маячок» включает в себя как маркеры, так и предметы.", "F_BepInEx_QSNotifications_T": "Показывать уведомления", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "Определяет, как часто динамическая система ИИ должна проверять расстояние от всех игроков для определения необходимости отключения ботов.", "F_BepInEx_DynamicAINoSnipers_T": "Динамический ИИ: игнорировать снайперов", "F_BepInEx_DynamicAINoSnipers_D": "Определяет, должна ли динамическая система ИИ игнорировать снайперов при определении необходимости их отключения.", + "F_BepInEx_FikaGC_T": "Использовать Fika GC", + "F_BepInEx_FikaGC_D": "Если следует использовать пользовательский сборщик мусора для уменьшения проблем с использованием памяти.\nВНИМАНИЕ: Экспериментальная функция, возможно, не окажет никакого эффекта на вашей системе.", "F_BepInEx_EnforcedSpawnLimits_T": "Принудительные лимиты спавна", "F_BepInEx_EnforcedSpawnLimits_D": "Принудительно ограничивает количество одновременно появляющихся ботов, не допуская превышения лимитов, установленных в оригинальной игре. В основном влияет на игру при использовании модов, изменяющих количество ботов. Не блокирует появление особых ботов, таких как боссы.", "F_BepInEx_DespawnFurthest_T": "Удалять самого дальнего бота при достижении лимита", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Время ожидания соединения", "F_BepInEx_ConnectionTimeout_D": "Определяет, через какое время соединение будет считаться разорванным, если от клиента или сервера не поступает никаких пакетов данных.", "F_BepInEx_SendRate_T": "Скорость отправки", - "F_BepInEx_SendRate_D": "Как часто в секунду перемещать пакеты передвижения(меньше = меньше использованной пропускной способности, чуть больше задержек во время интерполяции)\nЭто влияет только на хост и будет синхронизировано со всеми клиентами.\nКоличество в секунду:\n\nОчень Низко = 10\nНизко = 15\nСредне = 20\nВысоко = 30\n\nРекомендуется оставить не выше среднего, так как приросты после незначительны.", - "F_BepInEx_SmoothingRate_T": "Уровень сглаживания", - "F_BepInEx_SmoothingRate_D": "Локальная симуляция отстает на (Скорость отправки * Скорость сглаживания). Это гарантирует, что у нас всегда будет достаточно кадров в буфере для уменьшения задержек и джиттера при интерполяции.\n\nНизкий = 1,5\nСредний = 2\nВысокий = 2,5\n\nУстановите значение «Высокий», если движение не является плавным. Не может быть изменено во время рейда.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Использовать многопоточность", + "F_BepInEx_NetMultiThread_D": "Если сетевой компонент должен использовать многопоточность. Это может потенциально устранить проблемы с лагами в игре, позволяя сетевому менеджеру продолжать обрабатывать и ставить пакеты в очередь даже при низком FPS.\nЭто экспериментальная функция, и её следует включать только на высокопроизводительных ПК. Кроме того, она будет активна только на headless-клиенте.", + "F_BepInEx_VOIP_T": "Разрешить VOIP", + "F_BepInEx_VOIP_D": "Включает VOIP, когда вы являетесь хостом.", "F_BepInEx_DisableBotMetabolism_T": "Отключить метаболизм у ботов", "F_BepInEx_DisableBotMetabolism_D": "Отключает метаболизм у ботов, не давая им умереть от потери энергии/воды во время длительных рейдов.", "F_MMUI_OnlinePlayers": "ОНЛАЙН ИГРОКОВ: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "В Убежище", "F_MMUI_InFlea": "На Барахолке", "F_MMUI_RaidDetails": "Играет за {0} на {1}\nВремя: {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", + "F_UI_LocaleError_H": "ОШИБКА ЛОКАЛИЗАЦИИ", + "F_UI_LocaleError_D": "Возникли ошибки при загрузке языковых настроек Fika. Перевод теперь использует резервные варианты для неудачных заголовков. Пожалуйста, сообщите об этом разработчикам!\nКлючи с ошибкой: {0}", + "F_UI_EFTVersionError": "Вы пытаетесь использовать другую версию EFT отличную от хоста.\nКлиент: {0}\nХост: {1}", + "F_UI_FikaVersionError": "Вы пытаетесь использовать другую версию Fika отличную от хоста.\nКлиент: {0}\nХост: {1}", + "F_UI_PlayerConnected": "{0} Подключился", + "F_UI_PlayerDisconnected": "{0} Отключился", + "F_UI_DisabledByServerHost": "Отключено хостом сервера" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/sk.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/sk.json index cfe567f..38935ca 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/sk.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/sk.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "VYTVORIŤ RAID", "F_MMUI_JoinButton": "PRIPOJIŤ SA", "F_MMUI_SelectAmountHeader": "ZVOLIŤ MNOŽSTVO", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "Nastavenia relácie", "F_MMUI_SelectAmountDescription": "Koľko ľudí (vrátane teba) bude hrať?", "F_MMUI_UseDedicatedHost": "Použiť dedikovaný server", "F_MMUI_StartButton": "ZAČAŤ", "F_MMUI_LoadingScreenHeader": "NAČÍTAVANIE", "F_MMUI_LoadingScreenDescription": "Prosím čakaj kým sa inicializuje raid... Môže to chvíľu trvať.", - "F_MMUI_JoinAsSpectator": "Join as spectator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "Sledovať", + "F_MMUI_JoinAsSpectator_D": "Pripojiť sa k hre ako sledovateľ", + "F_UI_StartRaid": "Zapnúť raid", + "F_UI_StartRaidDescription": "Zapnúť raid\nPo kliknutí sa nebudú môcť pripojiť ďalší hráči", "F_UI_CannotJoinRaidOtherMap": "Nemôžeš sa pripojiť k raidu ktorý je na inej mape.\nMapa: {0}", "F_UI_CannotJoinRaidOtherTime": "Nemôžeš sa pripojiť k raidu ktorý je v inom čase.", "F_UI_CannotJoinRaidScavAsPMC": "Nemôžeš sa pripojiť k scav raidu ako PMC.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "CHYBA PRIRADZOVANIA", "F_UI_ErrorBindIP": "'{0}' nie je validna IP adresa pre priradenie! Prosím skontroluj 'Force IP' nastavenie.", "F_UI_NoDedicatedClients": "Žiadny dedikovaný klienti nie sú dostupný.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Zapnúť raid\nOstatní hráči sa budú môcť pripojiť počas načítacej obrazovky", + "F_UI_WaitForHostStartRaid": "Čakám na spustenie raidu hostiteľom...", "F_UI_WaitForHostFinishInit": "Čakám na hosta kým inicializuje raid...", "F_UI_WaitForOtherPlayers": "Čakám na načítanie ostatných hráčov...", "F_UI_RetrieveSpawnInfo": "Získavam dáta o spawne zo serveru...", @@ -76,32 +78,32 @@ "F_UI_RetrieveInteractables": "Získavam interagovateľné objekty zo serveru...", "F_UI_InitCoopGame": "Inicializujem coop hru...", "F_UI_AllPlayersJoined": "Všetci hráči pripojený, spúšťam hru...", - "F_UI_WaitingForConnect": "Waiting for client to connect to server... If there is no notification it failed.", - "F_UI_ErrorConnectingToRaid": "Unable to connect to the raid server. Make sure ports are forwarded and/or UPnP is enabled and supported.", - "F_UI_FinishingRaidInit": "Finishing raid initialization...", - "F_UI_SyncThrowables": "Syncing throwables...", - "F_UI_SyncInteractables": "Syncing interactables...", + "F_UI_WaitingForConnect": "Čakám na pripojenie klienta k serveru... Ak nevyskočila notifikácia, operácia zlyhala.", + "F_UI_ErrorConnectingToRaid": "Nemožno pripojiť sa k raid serveru. Uistite sa že porty sú otvorené a/alebo UPnP je zapnuté a podporované.", + "F_UI_FinishingRaidInit": "Dokončovanie inicializácie raidu...", + "F_UI_SyncThrowables": "Synchronizácia vrhacích predmetov...", + "F_UI_SyncInteractables": "Synchronizácia interagovateľných predmetov...", "F_UI_SyncLampStates": "Syncing lamp states...", - "F_UI_SyncWindows": "Syncing windows...", + "F_UI_SyncWindows": "Synchronizácia okien...", "F_UI_ReceiveOwnPlayer": "Receiving own player...", - "F_UI_FinishReconnect": "Finishing reconnect...", - "F_Client_FreeCamInputDisabled": "Free cam input disabled", - "F_Client_FreeCamInputEnabled": "Free cam input enabled", - "F_UI_WaitForPlayer": "Waiting for {0} player", - "F_UI_WaitForPlayers": "Waiting for {0} players", - "F_UI_ToSLong": "Welcome to Fika!\r\n\r\nFika is a co-op mod for SPT, allowing you to play with your friends. Fika is and will always be free, if you paid for it you got scammed. You are also not allowed to host public servers with monetization or donations.\r\n\r\nWait for this message to fade to accept our Terms of Service.\r\n\r\nYou can join our Discord here: https://discord.gg/project-fika", - "F_UI_ToSShort": "Welcome to Fika!\r\n\r\nFika is a co-op mod for SPT, allowing you to play with your friends. Fika is and will always be free, if you paid for it you got scammed. You are also not allowed to host public servers with monetization or donations.\r\n\r\nYou can join our Discord here: https://discord.gg/project-fika", - "F_UI_RaidSettingsDescription": "In this mode, you can play online co-op with your friends without the risk of meeting others.\nAll game mechanics in the co-op mode (weather, bots, bosses, etc.) are identical to the online mechanics.\nOnly the hosts settings will take effect.", - "F_UI_CoopGameMode": "Fika Co-op Game Mode", - "F_UI_CoopRaidSettings": "Fika Co-op Raid Settings", - "F_UI_FikaAlwaysCoop": "Co-op is always enabled in Fika", - "F_UI_UpnpFailed": "UPnP mapping failed. Make sure the selected port is not already open!\nDisable UPnP if you are using a VPN.", - "F_UI_InitWeather": "Generating weather...", - "F_Notification_RaidStarted": "{0} started a raid on {1}", - "F_Notification_ItemReceived": "You received a {0} from {1}", - "F_Notification_RaidSettingsDisabled": "Editing of RaidSettings is disabled by the server host", + "F_UI_FinishReconnect": "Dokončovanie znovu-napojenia...", + "F_Client_FreeCamInputDisabled": "Vstup pre voľnú kameru vypnutý", + "F_Client_FreeCamInputEnabled": "Vstup pre voľnú kameru zapnutý", + "F_UI_WaitForPlayer": "Čakanie na {0} hráča", + "F_UI_WaitForPlayers": "Čakanie na {0} hráčov", + "F_UI_ToSLong": "Vitajte vo Fike!\n\nFika je co-op mód pre SPT, umožňuje vám hrať s vašimi priateľmi. Fika je a vždy bude ZDARMA, ak ste za ňu zaplatili, boli ste oklamaní. Tiež je zakázané hostovať verejné servery s monetizáciou (platením) alebo za donácie. \n\nPočkajte kým táto správa zmizne SAMA pre prijatie našich Podmienok používania. \n\nMôžete sa pridať na náš Discord tu: https://discord.gg/project-fika", + "F_UI_ToSShort": "Vitajte vo Fike!\n\nFika je co-op mód pre SPT, umožňuje vám hrať s vašimi priateľmi. Fika je a vždy bude ZDARMA, ak ste za ňu zaplatili, boli ste oklamaní. Tiež je zakázané hostovať verejné servery s monetizáciou (platením) alebo za donácie. \n\nMôžete sa pridať na náš Discord tu: https://discord.gg/project-fika", + "F_UI_RaidSettingsDescription": "V tomto hernom móde môžete hrať online co-op s vašimi priateľmi bez risku stretnutia cudzích alebo nežiadúcich ľudí a hráčov.\nVšetky herné mechaniky v co-op režime (počasie, boti, bossovia, atp.) sú identické s online mechanikami.\nJedine nastavenia hosťa ovplyvnia hru.", + "F_UI_CoopGameMode": "Fika Co-op herný režim", + "F_UI_CoopRaidSettings": "Fika Co-op nastavenia raidu", + "F_UI_FikaAlwaysCoop": "Co-op je vždy zapnutý vo Fike", + "F_UI_UpnpFailed": "UPnP mapovanie zlyhalo. Uistite sa že vybraný port nie je otvorený!\nVypnite UPnP ak používate VPN.", + "F_UI_InitWeather": "Generujem počasie...", + "F_Notification_RaidStarted": "{0} spustil raid na {1}", + "F_Notification_ItemReceived": "Dostal si {0} od {1}", + "F_Notification_RaidSettingsDisabled": "Úprava nastavení Raidu je vypnutá hosťom serveru", "F_UI_ExtractMessage": "Stlač {0} pre extrakt", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", + "F_UI_DownloadProfile": "STIAHNUŤ PROFIL", "F_BepInEx_H_Advanced": "Pokročilé", "F_BepInEx_H_Coop": "Coop", "F_BepInEx_H_Coop_NamePlates": "Coop | Menovky", @@ -113,30 +115,32 @@ "F_BepInEx_H_Network": "Sieť", "F_BepInEx_H_Gameplay": "Hrateľnosť", "F_BepInEx_OfficialVersion_T": "Oficiálna verzia", - "F_BepInEx_OfficialVersion_D": "Show official version instead of Fika version.", + "F_BepInEx_OfficialVersion_D": "Zobraziť oficiálnu verziu hry namiesto FIKA verzie.", + "F_BepInEx_UseHeadless_T": "Automaticky používať Headless", + "F_BepInEx_UseHeadless_D": "Vždy zakliknúť pole pre použitie Headless klienta pokiaľ je k dispozícií", "F_BepInEx_ShowFeed_T": "Zobraziť informačný kanál", - "F_BepInEx_ShowFeed_D": "Enable custom notifications when a player dies, extracts, kills a boss, etc.", + "F_BepInEx_ShowFeed_D": "Zapne oznámenia keď hráč zomrie, extrahuje, zabije bossa, atp.", "F_BepInEx_AutoExtract_T": "Automatický exfil", - "F_BepInEx_AutoExtract_D": "Automatically extracts after the extraction countdown. As a host, this will only work if there are no clients connected.", - "F_BepInEx_ShowExtractMessage_T": "Show Extract Message", - "F_BepInEx_ShowExtractMessage_D": "Whether to show the extract message after dying/extracting.", - "F_BepInEx_ExtractKey_T": "Extract Key", - "F_BepInEx_ExtractKey_D": "The key used to extract from the raid.", - "F_BepInEx_EnableChat_T": "Enable Chat", - "F_BepInEx_EnableChat_D": "Toggle to enable chat in game. Cannot be change mid raid.", - "F_BepInEx_ChatKey_T": "Chat Key", - "F_BepInEx_ChatKey_D": "The key used to open the chat window.", - "F_BepInEx_EnableOnlinePlayers_T": "Enable Online Players", - "F_BepInEx_EnableOnlinePlayers_D": "If the online players menu should be shown in the menu.", - "F_BepInEx_OnlinePlayersScale_T": "Online Players Scale", + "F_BepInEx_AutoExtract_D": "Automaticky extrahovať potom ako skončí odpočet. Toto bude pre hostiteľa fungovať iba ak nie sú pripojení žiadny klienti.", + "F_BepInEx_ShowExtractMessage_T": "Zobraziť správu o extrakcií", + "F_BepInEx_ShowExtractMessage_D": "Zobrazenie správy o extrahovaní po smrti/extrahovaní.", + "F_BepInEx_ExtractKey_T": "Tlačidlo pre extrakciu", + "F_BepInEx_ExtractKey_D": "Tlačidlo pre extrakciu z raidu.", + "F_BepInEx_EnableChat_T": "Zapnúť chat", + "F_BepInEx_EnableChat_D": "Prepínanie zobrazenia chatu v hre. Nemožno meniť hodnotu počas raidu.", + "F_BepInEx_ChatKey_T": "Tlačidlo pre chat", + "F_BepInEx_ChatKey_D": "Tlačítko pre otvorenie okna s chatom.", + "F_BepInEx_EnableOnlinePlayers_T": "Zapnúť online hráčov", + "F_BepInEx_EnableOnlinePlayers_D": "Zobrazenie tabuľky hráčov v hlavnom menu.", + "F_BepInEx_OnlinePlayersScale_T": "Škála online hráčov", "F_BepInEx_OnlinePlayersScale_D": "The scale of the window that displays online players. Only change if it looks out of proportion.\n\nRequires a refresh of the main menu to take effect.", - "F_BepInEx_UseNamePlates_T": "Show Player Name Plates", + "F_BepInEx_UseNamePlates_T": "Zobraziť menovky hráčov", "F_BepInEx_UseNamePlates_D": "If name plates should be shown above teammates.", - "F_BepInEx_HideHealthBar_T": "Hide Health Bar", - "F_BepInEx_HideHealthBar_D": "Completely hides the health bar.", + "F_BepInEx_HideHealthBar_T": "Skryť ukazovateľ zdravia", + "F_BepInEx_HideHealthBar_D": "Kompletne skryje ukazovateľ zdravia.", "F_BepInEx_UsePercent_T": "Show HP% instead of bar", "F_BepInEx_UsePercent_D": "Shows health in % amount instead of using the bar.", - "F_BepInEx_ShowEffects_T": "Show Effects", + "F_BepInEx_ShowEffects_T": "Zobraziť efekty", "F_BepInEx_ShowEffects_D": "If status effects should be displayed below the health bar.", "F_BepInEx_ShowFactionIcon_T": "Show Player Faction Icon", "F_BepInEx_ShowFactionIcon_D": "Shows the player faction icon next to the HP bar.", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "The minimum scale of the name plates.", "F_BepInEx_UseOcclusion_T": "Use Occlusion", "F_BepInEx_UseOcclusion_D": "Use occlusion to hide the name plate when the player is out of sight.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Quest Types", "F_BepInEx_QuestTypes_D": "Which quest types to receive and send. PlaceBeacon is both markers and items.", "F_BepInEx_QSNotifications_T": "Show Notifications", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "How often DynamicAI should scan for the range from all players.", "F_BepInEx_DynamicAINoSnipers_T": "Ignore Snipers", "F_BepInEx_DynamicAINoSnipers_D": "Whether Dynamic AI should ignore sniper scavs.", + "F_BepInEx_FikaGC_T": "Use Fika GC", + "F_BepInEx_FikaGC_D": "If a custom garbage collector should be used to alleviate issues with memory usage.\nWARNING: Experimental, might have no effect at all on your system.", "F_BepInEx_EnforcedSpawnLimits_T": "Enforced Spawn Limits", "F_BepInEx_EnforcedSpawnLimits_D": "Enforces spawn limits when spawning bots, making sure to not go over the vanilla limits. This mainly takes affect when using spawn mods or anything that modifies the bot limits. Will not block spawns of special bots like bosses.", "F_BepInEx_DespawnFurthest_T": "Despawn Furthest", @@ -233,9 +243,11 @@ "F_BepInEx_ConnectionTimeout_T": "Connection Timeout", "F_BepInEx_ConnectionTimeout_D": "How long it takes for a connection to be considered dropped if no packets are received.", "F_BepInEx_SendRate_T": "Send Rate", - "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\n\nVery Low = 10\nLow = 15\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", - "F_BepInEx_SmoothingRate_T": "Smoothing Rate", - "F_BepInEx_SmoothingRate_D": "Local simulation is behind by (Send Rate * Smoothing Rate). This guarantees that we always have enough snapshots in the buffer to mitigate lags & jitter during interpolation.\n\nLow = 1.5\nMedium = 2\nHigh = 2.5\n\nSet this to 'High' if movement isn't smooth. Cannot be changed during a raid.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "Use MultiThreading", + "F_BepInEx_NetMultiThread_D": "If the network component should use multi-threading. This can potentially alleviate problems when the game lags, allowing the network manager to still process and queue packets during low FPS.\nThis is an experimental feature and should only be enabled on high-end PCs and will only be enabled on a headless client.", + "F_BepInEx_VOIP_T": "Allow VOIP", + "F_BepInEx_VOIP_D": "If VOIP will be enabled when you are hosting.", "F_BepInEx_DisableBotMetabolism_T": "Disable Bot Metabolism", "F_BepInEx_DisableBotMetabolism_D": "Disables metabolism on bots, preventing them from dying from loss of energy/hydration during long raids.", "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", @@ -245,6 +257,12 @@ "F_MMUI_InHideout": "In Hideout", "F_MMUI_InFlea": "In Flea", "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}", + "F_UI_EFTVersionError": "You are attempting to use a different version of EFT than what the raid host is running.\nClient: {0}\nHost: {1}", + "F_UI_FikaVersionError": "You are attempting to use a different version of Fika than what the raid host is running.\nClient crc: {0}\nHost crc: {1}", + "F_UI_PlayerConnected": "{0} has connected", + "F_UI_PlayerDisconnected": "{0} has disconnected", + "F_UI_DisabledByServerHost": "Disabled by server host" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/tu.json b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/tu.json index 8c04df9..6724392 100644 --- a/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/tu.json +++ b/mods/Fika Server/user/mods/fika-server/assets/database/locales/global/tu.json @@ -37,15 +37,16 @@ "F_MMUI_HostRaidButton": "BASKIN OLUŞTUR", "F_MMUI_JoinButton": "KATIL", "F_MMUI_SelectAmountHeader": "MİKTAR SEÇİNİZ", - "F_MMUI_SessionSettingsHeader": "SESSION SETTINGS", + "F_MMUI_SessionSettingsHeader": "OTURUM AYARLARI", "F_MMUI_SelectAmountDescription": "Kaç kişi (siz dahil) oynayacak?", "F_MMUI_UseDedicatedHost": "Özel Host Kullanın", "F_MMUI_StartButton": "BAŞLAT", "F_MMUI_LoadingScreenHeader": "YÜKLENİYOR", "F_MMUI_LoadingScreenDescription": "Baskın başlatılırken lütfen bekleyin... bu biraz zaman alabilir.", - "F_MMUI_JoinAsSpectator": "Join as spectator", - "F_UI_StartRaid": "Start Raid", - "F_UI_StartRaidDescription": "Starts the raid\nNew players will not be able to join", + "F_MMUI_JoinAsSpectator": "İzleyin", + "F_MMUI_JoinAsSpectator_D": "Oyuna seyirci olarak katılın", + "F_UI_StartRaid": "Baskını başlat", + "F_UI_StartRaidDescription": "Baskını başlatır\nBuna tıkladıktan sonra ek oyuncular katılamayacaktır", "F_UI_CannotJoinRaidOtherMap": "Başka bir haritada olan bir baskına katılamazsınız.\nBaskın haritası: {0}", "F_UI_CannotJoinRaidOtherTime": "Başka bir zamanda olan bir baskına katılamazsınız.", "F_UI_CannotJoinRaidScavAsPMC": "PMC olarak bir SCAV baskınına katılamazsınız.", @@ -65,7 +66,8 @@ "F_UI_ErrorBindIPHeader": "HATA BAĞLANTISI", "F_UI_ErrorBindIP": "'{0}' bağlanmak için geçerli bir IP adresi değil! 'IP'yi Bağlamaya Zorla' ayarınızı kontrol edin.", "F_UI_NoDedicatedClients": "Özel istemci bulunmamaktadır.", - "F_UI_WaitForHostStartRaid": "Waiting for host to start the raid...", + "F_UI_HostRaidTooltip": "Baskını başlatır\nBuna tıkladıktan sonra başka oyuncular katılamayacaktır", + "F_UI_WaitForHostStartRaid": "Ana bilgisayarın baskını başlatması bekleniyor...", "F_UI_WaitForHostFinishInit": "Ana bilgisayarın raid başlatmayı bitirmesi bekleniyor...", "F_UI_WaitForOtherPlayers": "Diğer oyuncuların yüklemeyi bitirmesini bekleniliyor...", "F_UI_RetrieveSpawnInfo": "Sunucudan spawn bilgisi alınıyor...", @@ -101,7 +103,7 @@ "F_Notification_ItemReceived": "{1}'den bir {0} aldınız", "F_Notification_RaidSettingsDisabled": "Baskın Ayarlarının düzenlenmesi sunucu ana bilgisayarı tarafından devre dışı bırakılır", "F_UI_ExtractMessage": "Çıkarmak için {0} tuşuna basın", - "F_UI_DownloadProfile": "DOWNLOAD PROFILE", + "F_UI_DownloadProfile": "PROFİLİ İNDİR", "F_BepInEx_H_Advanced": "Gelişmiş", "F_BepInEx_H_Coop": "Coop", "F_BepInEx_H_Coop_NamePlates": "Coop | İsim Plakaları", @@ -114,6 +116,8 @@ "F_BepInEx_H_Gameplay": "Oynanış", "F_BepInEx_OfficialVersion_T": "Resmi Versiyon", "F_BepInEx_OfficialVersion_D": "Fika versiyonu yerine resmi versiyonu gösterin.", + "F_BepInEx_UseHeadless_T": "Otomatik Kullanım Başlıksız", + "F_BepInEx_UseHeadless_D": "Varsa, Başsız İstemciyi kullanmak için her zaman kutuyu işaretleyin", "F_BepInEx_ShowFeed_T": "Beslemeyi Göster", "F_BepInEx_ShowFeed_D": "Bir oyuncu öldüğünde, çıkarıldığında, bir boss öldürdüğünde vb. özel bildirimleri etkinleştirin.", "F_BepInEx_AutoExtract_T": "Otomatik Exfill", @@ -158,6 +162,10 @@ "F_BepInEx_MinPlateScale_D": "İsim plakalarının minimum ölçeği.", "F_BepInEx_UseOcclusion_T": "Oklüzyon Kullanın", "F_BepInEx_UseOcclusion_D": "Oyuncu görüş alanının dışındayken isim plakasını gizlemek için oklüzyon kullanın.", + "F_BepInEx_HealthColorFull_T": "Full Health Color", + "F_BepInEx_HealthColorFull_D": "The color displayed when at full health. This will gradually move towards 'Low Health Color' when HP decreases.", + "F_BepInEx_HealthColorLow_T": "Low Health Color", + "F_BepInEx_HealthColorLow_D": "The color displayed when at low health. This will gradually move towards 'Full Health Color' when HP increases.", "F_BepInEx_QuestTypes_T": "Görev Tipleri", "F_BepInEx_QuestTypes_D": "Hangi görev türlerinin alınacağı ve gönderileceği. PlaceBeacon hem işaretleyiciler hem de öğelerdir.", "F_BepInEx_QSNotifications_T": "Bildirimleri Göster", @@ -210,6 +218,8 @@ "F_BepInEx_DynamicAIRate_D": "DynamicAI'nin tüm oyuncuların menzilini ne sıklıkla taraması gerektiği.", "F_BepInEx_DynamicAINoSnipers_T": "Keskin Nişancıları Görmezden Gel", "F_BepInEx_DynamicAINoSnipers_D": "Dinamik Yapay Zekanın keskin nişancı görmezden gelip gelmeyeceği.", + "F_BepInEx_FikaGC_T": "Fika GC kullanın", + "F_BepInEx_FikaGC_D": "Bellek kullanımıyla ilgili sorunları hafifletmek için özel bir çöp toplayıcı kullanılması gerekiyorsa.\nUYARI: Deneyseldir, sisteminiz üzerinde hiçbir etkisi olmayabilir.", "F_BepInEx_EnforcedSpawnLimits_T": "Zorunlu Spawn Limiti", "F_BepInEx_EnforcedSpawnLimits_D": "Botlar ortaya çıkarken ortaya çıkma sınırlarını zorlar, vanilya sınırlarını aşmadığından emin olur. Bu, esas olarak spawn modları veya bot sınırlarını değiştiren herhangi bir şey kullanıldığında etkili olur. Bosslar gibi özel botların ortaya çıkmasını engellemez.", "F_BepInEx_DespawnFurthest_T": "En Uzakta Despawn", @@ -233,18 +243,26 @@ "F_BepInEx_ConnectionTimeout_T": "Bağlantı Zaman Aşımı", "F_BepInEx_ConnectionTimeout_D": "Hiçbir paket alınmadığında bir bağlantının düşmüş sayılması için ne kadar süre geçmesi gerektiği.", "F_BepInEx_SendRate_T": "Oran Gönder", - "F_BepInEx_SendRate_D": "Hareket paketlerinin saniye başına ne sıklıkta gönderilmesi gerektiği (daha düşük = daha az bant genişliği kullanılır, enterpolasyon sırasında biraz daha fazla gecikme)\nBu yalnızca ana bilgisayarı etkiler ve tüm istemcilerle senkronize edilir.\nMiktar saniye başına:\n\nÇok Düşük = 10\nDüşük = 15\nOrta = 20\nYüksek = 30\n\nSonrasında kazançlar önemsiz olduğu için Orta'dan daha yüksek bir seviyede bırakılmaması önerilir.", - "F_BepInEx_SmoothingRate_T": "Düzeltme Oranı", - "F_BepInEx_SmoothingRate_D": "Yerel simülasyon (Gönderme Oranı * Düzeltme Oranı) ile geride kalır. Bu, enterpolasyon sırasında gecikmeleri ve titreşimi azaltmak için tamponda her zaman yeterli anlık görüntüye sahip olduğumuzu garanti eder.\n\nDüşük = 1,5\nOrta = 2\nYüksek = 2,5\n\nHareket düzgün değilse bunu 'Yüksek' olarak ayarlayın. Baskın sırasında değiştirilemez.", + "F_BepInEx_SendRate_D": "How often per second movement packets should be sent (lower = less bandwidth used, slight more delay during interpolation)\nThis only affects the host and will be synchronized to all clients.\nAmount is per second:\nLow = 10\nMedium = 20\nHigh = 30\n\nRecommended to leave at no higher than Medium as the gains are insignificant after.", + "F_BepInEx_NetMultiThread_T": "MultiThreading Kullanın", + "F_BepInEx_NetMultiThread_D": "Ağ bileşeninin çoklu iş parçacığı kullanması gerekiyorsa. Bu, oyun geciktiğinde sorunları potansiyel olarak hafifletebilir ve ağ yöneticisinin düşük FPS sırasında paketleri işlemeye ve sıraya koymaya devam etmesine izin verir.\nBu deneysel bir özelliktir ve yalnızca üst düzey bilgisayarlarda etkinleştirilmelidir ve yalnızca başsız bir istemcide etkinleştirilecektir.", + "F_BepInEx_VOIP_T": "VOIP'e İzin Ver", + "F_BepInEx_VOIP_D": "Ev sahipliği yaparken VOIP etkinleştirilecekse.", "F_BepInEx_DisableBotMetabolism_T": "Bot Metabolizmasını Devre Dışı Bırak", "F_BepInEx_DisableBotMetabolism_D": "Botlarda metabolizmayı devre dışı bırakarak uzun akınlar sırasında enerji/hidrasyon kaybından ölmelerini önler.", - "F_MMUI_OnlinePlayers": "ONLINE PLAYERS: {0}", - "F_MMUI_InMenu": "In Menu", - "F_MMUI_InRaid": "In Raid", - "F_MMUI_InStash": "In Stash", - "F_MMUI_InHideout": "In Hideout", - "F_MMUI_InFlea": "In Flea", - "F_MMUI_RaidDetails": "Playing as a {0} on {1}\nTime: {2}", - "F_UI_LocaleError_H": "LOCALES ERROR", - "F_UI_LocaleError_D": "There were errors when loading the Fika locales. Translation is now using fallbacks for the failed headers. Please report this to the developers!\n\nFailed keys: {0}" + "F_MMUI_OnlinePlayers": "{0} ONLINE OYUNCU", + "F_MMUI_InMenu": "Menüde", + "F_MMUI_InRaid": "Baskında", + "F_MMUI_InStash": "Zulada", + "F_MMUI_InHideout": "Sığınakta", + "F_MMUI_InFlea": "Flea'da", + "F_MMUI_RaidDetails": "{1} üzerinde {0} olarak oynamak\nZaman: {2}", + "F_Mod_VerificationFailure": "Crc32 verification of the Fika.Core.dll failed, you will not be able to play with other people!\nCheck your log file for more information.", + "F_UI_LocaleError_H": "YEREL HATA", + "F_UI_LocaleError_D": "Fika yerel ayarları yüklenirken hatalar oluşuyordu. Çeviri artık başarısız başlıklar için geri dönüşler kullanıyor. Lütfen bunu geliştiricilere bildirin!\n\nBaşarısız anahtarlar: {0}", + "F_UI_EFTVersionError": "Raid host'un çalıştırdığından farklı bir EFT sürümü kullanmaya çalışıyorsunuz.\nİstemci: {0}\nHost: {1}", + "F_UI_FikaVersionError": "Raid ana bilgisayarının çalıştırdığından farklı bir Fika sürümü kullanmaya çalışıyorsunuz.\nİstemci crc: {0}\nhost crc: {1}", + "F_UI_PlayerConnected": "{0} bağlandı", + "F_UI_PlayerDisconnected": "{0} bağlantısı kesildi", + "F_UI_DisabledByServerHost": "Sunucu ana bilgisayarı tarafından devre dışı bırakıldı" } \ No newline at end of file diff --git a/mods/Fika Server/user/mods/fika-server/assets/scripts/_TEMPLATE.ps1 b/mods/Fika Server/user/mods/fika-server/assets/scripts/_TEMPLATE.ps1 new file mode 100644 index 0000000..a435a74 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/assets/scripts/_TEMPLATE.ps1 @@ -0,0 +1,70 @@ +$Version = "1.1" +$ProfileId = "${profileId}" +$BackendUrl = "${backendUrl}" +$WaitTime = 2 + +$Host.UI.RawUI.WindowTitle = "Fika Headless Watcher v$Version" + +Write-Host "Fika Headless Watcher v$Version" +Write-Host +Write-Host "Profile Id: $ProfileId" +Write-Host "Backend Url: $BackendUrl" +Write-Host + +if (!(Test-Path "$PSScriptRoot\EscapeFromTarkov.exe")) { + Write-Host "Could not find 'EscapeFromTarkov.exe'. Please move this launch script into the root directory of your headless client SPT install." -ForegroundColor Red + Write-Host "Press [ENTER] or close this window to exit..." + Read-Host + exit +} + +if (!(Test-Path "$PSScriptRoot\BepInEx\plugins\Fika.Headless.dll")) { + Write-Host "Could not find 'Fika.Headless.dll'. Please install the Headless plugin before launching this script." -ForegroundColor Red + Write-Host "Press [ENTER] or close this window to exit..." + Read-Host + exit +} + +if (!(Test-Path "$PSScriptRoot\BepInEx\plugins\Fika.Core.dll")) { + Write-Host "Could not find 'Fika.Core.dll'. If you have not made a copy of a working SPT+Fika install, please start over from the first step of the instructions on the wiki. If you have mistakenly deleted Fika.Core.dll, please reinstall it." -ForegroundColor Red + Write-Host "Press [ENTER] or close this window to exit..." + Read-Host + exit +} + +$graphicsMode = $false + +for ($i = 0; $i -lt $WaitTime; $i++) { + $countDown = $WaitTime - $i + + Write-Progress -Activity "Starting headless in $countDown... press G to start the headless in graphics mode." -PercentComplete ((100/$WaitTime)*($i+1)) + Start-Sleep -Seconds 1 + + if ([Console]::KeyAvailable) { + $key = [Console]::ReadKey($true).Key + + if ($key -eq "G") { + $graphicsMode = $true + break + } + } +} + +Write-Progress -Activity "Starting headless in..." -Completed + +if ($graphicsMode) { + Write-Host "Starting in graphics mode..." + Start-Process "$PSScriptRoot\EscapeFromTarkov.exe" -WorkingDirectory $PSScriptRoot -ArgumentList "-token=$ProfileId", "-config={'BackendUrl':'$BackendUrl','Version':'live'}", "--enable-console true" -Wait + exit +} + +Write-Host "[INFO]" -ForegroundColor Blue +Write-Host " - This script will restart the headless client console if it crashes or closes" +Write-Host " - To force the headless client to restart after X number of raids, change 'restartAfterAmountOfRaids' in the server mod configuration" +Write-Host +Write-Host "[$(Get-Date -Format 'HH:mm:ss')] Starting headless client" + +while ($true) { + Start-Process "$PSScriptRoot\EscapeFromTarkov.exe" -WorkingDirectory $PSScriptRoot -ArgumentList "-token=$ProfileId", "-config={'BackendUrl':'$BackendUrl','Version':'live'}", "-batchmode", "-nographics", "--enable-console true" -Wait + Write-Host "[$(Get-Date -Format 'HH:mm:ss')] Headless client closed, restarting" +} diff --git a/mods/Fika Server/user/mods/fika-server/package.json b/mods/Fika Server/user/mods/fika-server/package.json index 435a9d7..1772c4a 100644 --- a/mods/Fika Server/user/mods/fika-server/package.json +++ b/mods/Fika Server/user/mods/fika-server/package.json @@ -1,24 +1,27 @@ { "name": "server", - "version": "2.3.6", - "sptVersion": "~3.10", + "version": "2.4.8", + "sptVersion": "~3.11", "main": "src/mod.js", "scripts": { "setup": "npm i", "lint": "npx @biomejs/biome lint ./", + "check-types": "npx tsc -noEmit -p tsconfig.types.json", + "check-circular": "madge --circular --ts-config tsconfig.json --extensions ts ./src/", "build": "node ./build.mjs", "buildinfo": "node ./build.mjs --verbose" }, "devDependencies": { - "@biomejs/biome": "^1.7.1", - "@types/node": "~20.11", - "@types/ws": "^8.5.10", + "@biomejs/biome": "^1.9.4", + "@types/node": "22.10.5", + "@types/ws": "~8.5", "archiver": "^6.0", "fs-extra": "~11.2", "ignore": "^5.2", - "tsyringe": "4.8.0", - "typescript": "5.4", - "winston": "3.12" + "tsyringe": "~4.8", + "typescript": "5.7.3", + "winston": "~3.13", + "madge": "8.0.0" }, "author": "Fika", "license": "CC-BY-NC-SA-4.0" diff --git a/mods/Fika Server/user/mods/fika-server/src/Fika.ts b/mods/Fika Server/user/mods/fika-server/src/Fika.ts index 9b48f50..da62686 100644 --- a/mods/Fika Server/user/mods/fika-server/src/Fika.ts +++ b/mods/Fika Server/user/mods/fika-server/src/Fika.ts @@ -6,12 +6,21 @@ import { ImageRouter } from "@spt/routers/ImageRouter"; import { DatabaseServer } from "@spt/servers/DatabaseServer"; import { ImporterUtil } from "@spt/utils/ImporterUtil"; +import { watch } from "node:fs"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { FileSystem } from "@spt/utils/FileSystem"; +import { JsonUtil } from "@spt/utils/JsonUtil"; +import { IFikaConfig } from "./models/fika/config/IFikaConfig"; import { IFikaConfigBackground } from "./models/fika/config/IFikaConfigBackground"; -import { IFikaConfigDedicated } from "./models/fika/config/IFikaConfigDedicated"; +import { IFikaConfigHeadless } from "./models/fika/config/IFikaConfigHeadless"; import { IFikaConfigNatPunchServer } from "./models/fika/config/IFikaConfigNatPunchServer"; import { Overrider } from "./overrides/Overrider"; +import { FikaClientService } from "./services/FikaClientService"; import { FikaPlayerRelationsCacheService } from "./services/cache/FikaPlayerRelationsCacheService"; -import { FikaDedicatedProfileService } from "./services/dedicated/FikaDedicatedProfileService"; +import { FikaHeadlessProfileService } from "./services/headless/FikaHeadlessProfileService"; import { FikaConfig } from "./utils/FikaConfig"; import { FikaServerTools } from "./utils/FikaServerTools"; @@ -19,26 +28,35 @@ import { FikaServerTools } from "./utils/FikaServerTools"; export class Fika { protected modPath: string; protected natPunchServerConfig: IFikaConfigNatPunchServer; - protected dedicatedConfig: IFikaConfigDedicated; + protected headlessConfig: IFikaConfigHeadless; protected backgroundConfig: IFikaConfigBackground; constructor( + @inject("WinstonLogger") protected logger: ILogger, @inject("DatabaseServer") protected databaseServer: DatabaseServer, + @inject("ConfigServer") protected configServer: ConfigServer, @inject("Overrider") protected overrider: Overrider, @inject("FikaServerTools") protected fikaServerTools: FikaServerTools, @inject("FikaConfig") protected fikaConfig: FikaConfig, - @inject("FikaDedicatedProfileService") protected fikaDedicatedProfileService: FikaDedicatedProfileService, + @inject("FikaClientService") protected fikaClientService: FikaClientService, + @inject("FikaHeadlessProfileService") protected fikaHeadlessProfileService: FikaHeadlessProfileService, @inject("ImageRouter") protected imageRouter: ImageRouter, @inject("ImporterUtil") protected importerUtil: ImporterUtil, - @inject("FikaPlayerRelationsCacheService") protected fikaPlayerRelationCacheServce: FikaPlayerRelationsCacheService + @inject("JsonUtil") protected jsonUtil: JsonUtil, + @inject("FileSystem") protected fileSystem: FileSystem, + @inject("FikaPlayerRelationsCacheService") protected fikaPlayerRelationCacheServce: FikaPlayerRelationsCacheService, ) { this.modPath = fikaConfig.getModPath(); - this.natPunchServerConfig = fikaConfig.getConfig().natPunchServer; - this.dedicatedConfig = fikaConfig.getConfig().dedicated; - this.backgroundConfig = fikaConfig.getConfig().background; } public async preSptLoad(container: DependencyContainer): Promise { + await this.fikaConfig.preInit(); + + this.natPunchServerConfig = this.fikaConfig.getConfig().natPunchServer; + this.headlessConfig = this.fikaConfig.getConfig().headless; + this.backgroundConfig = this.fikaConfig.getConfig().background; + + await this.fikaClientService.preInit(); await this.overrider.override(container); } @@ -47,24 +65,27 @@ export class Fika { this.fikaServerTools.startService("NatPunchServer"); } - if (this.dedicatedConfig.profiles.amount > 0) { - this.fikaDedicatedProfileService.init(); + if (this.headlessConfig.profiles.amount > 0) { + await this.fikaHeadlessProfileService.init(); } - this.addFikaClientLocales(); + await this.addFikaClientLocales(); + this.blacklistSpecialProfiles(); this.fikaPlayerRelationCacheServce.postInit(); if (this.backgroundConfig.enable) { const image = this.backgroundConfig.easteregg ? "assets/images/launcher/bg-senko.png" : "assets/images/launcher/bg.png"; this.imageRouter.addRoute("/files/launcher/bg", path.join(this.modPath, image)); } + + this.watchFikaConfig(); } private async addFikaClientLocales() { const database = this.databaseServer.getTables(); - const databasePath = path.join(this.fikaConfig.getModPath(), "assets/database/"); + const databasePath = path.join(this.fikaConfig.getModPath(), "assets/database/").replace(/\\/g, "/"); - const locales = await this.importerUtil.loadAsync(path.join(databasePath, "locales/"), databasePath); + const locales = await this.importerUtil.loadAsync(`${databasePath}locales/`, databasePath); for (const folderName in locales) { if (folderName === "global") { @@ -75,4 +96,95 @@ export class Fika { } } } + + private async blacklistSpecialProfiles() { + const coreConfig: ICoreConfig = this.configServer.getConfig(ConfigTypes.CORE); + const profileBlacklist = coreConfig.features.createNewProfileTypesBlacklist; + + if (!this.fikaConfig.getConfig().server.showDevProfile) { + profileBlacklist.push("SPT Developer"); + } + + if (!this.fikaConfig.getConfig().server.showNonStandardProfile) { + for (const id of ["Tournament", "SPT Easy start", "SPT Zero to hero"]) { + profileBlacklist.push(id); + } + } + } + + private watchFikaConfig(): void { + const configPath = path.join(this.modPath, "assets/configs/fika.jsonc"); + let fileChangeTimeout = null; + + // At the moment this doesn't reload: + // Nat punch server (Requires additional setup) + // SPT Http configuration (Can't be changed once initialized) + // Backgrounds on the launcher (cached?) + // Any client options if a client is already in-game, client will have to restart his game. + watch(configPath, async (eventType, _filename) => { + if (eventType === "change") { + if (fileChangeTimeout) { + clearTimeout(fileChangeTimeout); + } + + // Timeout is required here, sometimes Windows will send three events if a file has changed. + fileChangeTimeout = setTimeout(async () => { + let config = this.jsonUtil.deserializeJsonC(await this.fileSystem.read(configPath)); + + if (!config) { + this.logger.error("[Fika Server] could not hot-reload configuration, is the syntax correct?"); + return; + } + + const oldHeadlessAmount = this.fikaConfig.getConfig().headless.profiles.amount; + + if (this.fikaConfig.updateFikaConfig(config)) { + // Re-initialize new headless profiles if the number changed. + if (this.fikaConfig.getConfig().headless.profiles.amount > oldHeadlessAmount) { + await this.fikaHeadlessProfileService.init(); + } + + // Re-initialize required & optional mods + await this.fikaClientService.preInit(); + + // Re-initialize background + if (this.backgroundConfig.enable) { + const image = this.backgroundConfig.easteregg ? "assets/images/launcher/bg-senko.png" : "assets/images/launcher/bg.png"; + this.imageRouter.addRoute("/files/launcher/bg", path.join(this.modPath, image)); + } + + // Re-initialize profile preset blacklist + const coreConfig: ICoreConfig = this.configServer.getConfig(ConfigTypes.CORE); + + // Re-initialize showing of dev profile + if (this.fikaConfig.getConfig().server.showDevProfile) { + // Remove the blacklisted developer template + coreConfig.features.createNewProfileTypesBlacklist = coreConfig.features.createNewProfileTypesBlacklist.filter((item) => item !== "SPT Developer"); + } else { + // Re-add the blacklisted developer template + if (!coreConfig.features.createNewProfileTypesBlacklist.includes("SPT Developer")) { + coreConfig.features.createNewProfileTypesBlacklist.push("SPT Developer"); + } + } + + if (this.fikaConfig.getConfig().server.showNonStandardProfile) { + for (const id of ["Tournament", "SPT Easy start", "SPT Zero to hero"]) { + // Remove each blacklisted template + coreConfig.features.createNewProfileTypesBlacklist = coreConfig.features.createNewProfileTypesBlacklist.filter((item) => item !== id); + } + } else { + for (const id of ["Tournament", "SPT Easy start", "SPT Zero to hero"]) { + // Re-add the blacklisted templates + if (!coreConfig.features.createNewProfileTypesBlacklist.includes(id)) { + coreConfig.features.createNewProfileTypesBlacklist.push(id); + } + } + } + + this.logger.info("[Fika Server] Configuration hot-reloaded successfully"); + } + }, 750); + } + }); + } } diff --git a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaClientCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaClientCallbacks.ts index 5dfa1f2..5d5523f 100644 --- a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaClientCallbacks.ts +++ b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaClientCallbacks.ts @@ -20,14 +20,14 @@ export class FikaClientCallbacks { return this.httpResponseUtil.noBody(this.fikaClientController.handleClientConfig()); } - /** Handle /fika/client/config */ + /** Handle /fika/natpunchserver/config */ public handleNatPunchServerConfig(_url: string, _info: IFikaRaidServerIdRequestData, _sessionID: string): string { return this.httpResponseUtil.noBody(this.fikaClientController.handleNatPunchServerConfig()); } /** Handle /fika/client/check/mods */ - public handleCheckMods(_url: string, info: IFikaCheckModRequestData, _sessionID: string): string { - return this.httpResponseUtil.noBody(this.fikaClientController.handleCheckMods(info)); + public handleCheckMods(_url: string, info: IFikaCheckModRequestData, sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaClientController.handleCheckMods(info, sessionID)); } /** Handle /fika/profile/download */ diff --git a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaHeadlessCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaHeadlessCallbacks.ts new file mode 100644 index 0000000..9eaef9c --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaHeadlessCallbacks.ts @@ -0,0 +1,38 @@ +import { inject, injectable } from "tsyringe"; + +import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; + +import { IQuest } from "@spt/models/eft/common/tables/IQuest"; +import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; +import { FikaHeadlessController } from "../controllers/FikaHeadlessController"; +import { IFikaRaidServerIdRequestData } from "../models/fika/routes/raid/IFikaRaidServerIdRequestData"; + +@injectable() +export class FikaHeadlessCallbacks { + constructor( + @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil, + @inject("FikaHeadlessController") protected fikaHeadlessController: FikaHeadlessController, + ) { + // empty + } + + /** Handle /fika/headless/get */ + public handleGetHeadlesses(_url: string, _info: IFikaRaidServerIdRequestData, _sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaHeadlessController.handleGetHeadlesses()); + } + + /** Handle /fika/headless/available */ + public handleAvailableHeadlesses(_url: string, _info: IFikaRaidServerIdRequestData, _sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaHeadlessController.handleGetAvailableHeadlesses()); + } + + /** Handle /fika/headless/restartafterraidamount */ + public handleRestartAfterRaidAmount(_url: string, _info: IFikaRaidServerIdRequestData, _sessionID: string): string { + return this.httpResponseUtil.noBody(this.fikaHeadlessController.handleRestartAfterRaidAmount()); + } + + /** Handle /fika/headless/questtemplates */ + public handleGetAllQuestTemplates(_url: string, _info: any, _sessionID: string): IGetBodyResponseData { + return this.httpResponseUtil.getBody(this.fikaHeadlessController.handleGetAllQuestTemplates()); + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaNotificationCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaNotificationCallbacks.ts index ac97d2f..6ceb2b4 100644 --- a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaNotificationCallbacks.ts +++ b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaNotificationCallbacks.ts @@ -2,11 +2,11 @@ import { inject, injectable } from "tsyringe"; import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; +import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData"; +import { EEFTNotificationIconType } from "../models/enums/EEFTNotificationIconType"; +import { EFikaNotifications } from "../models/enums/EFikaNotifications"; import { IPushNotification } from "../models/fika/websocket/notifications/IPushNotification"; import { FikaNotificationWebSocket } from "../websockets/FikaNotificationWebSocket"; -import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData"; -import { EFikaNotifications } from "../models/enums/EFikaNotifications"; -import { EEFTNotificationIconType } from "../models/enums/EEFTNotificationIconType"; @injectable() export class FikaNotificationCallbacks { @@ -18,7 +18,7 @@ export class FikaNotificationCallbacks { } /** Handle /fika/notification/push */ - public handlePushNotification(_url: string, info: IPushNotification, _sessionID: string): INullResponseData { + public async handlePushNotification(_url: string, info: IPushNotification, _sessionID: string): Promise { // Yes, technically this needs a controller to fit into this format. But I cant be bothered setting up a whole controller for a few checks. if (!info.notification) { return this.httpResponseUtil.nullResponse(); @@ -36,7 +36,7 @@ export class FikaNotificationCallbacks { info.notificationIcon = EEFTNotificationIconType.Default; } - this.fikaNotificationWebSocket.broadcast(info); + await this.fikaNotificationWebSocket.broadcast(info); return this.httpResponseUtil.nullResponse(); } diff --git a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaRaidCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaRaidCallbacks.ts index 3a30e6a..12f5aa6 100644 --- a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaRaidCallbacks.ts +++ b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaRaidCallbacks.ts @@ -7,8 +7,7 @@ import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { FikaRaidController } from "../controllers/FikaRaidController"; import { IFikaRaidServerIdRequestData } from "../models/fika/routes/raid/IFikaRaidServerIdRequestData"; import { IFikaRaidCreateRequestData } from "../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; -import { IStartDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; -import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; +import { IStartHeadlessRequest } from "../models/fika/routes/raid/headless/IStartHeadlessRequest"; import { IFikaRaidJoinRequestData } from "../models/fika/routes/raid/join/IFikaRaidJoinRequestData"; import { IFikaRaidLeaveRequestData } from "../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; @@ -48,22 +47,12 @@ export class FikaRaidCallbacks { return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidGetSettings(info)); } - /** Handle /fika/raid/dedicated/start */ - public handleRaidStartDedicated(_url: string, info: IStartDedicatedRequest, sessionID: string): string { - return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidStartDedicated(sessionID, info)); + /** Handle /fika/raid/headless/start */ + public async handleRaidStartHeadless(_url: string, info: IStartHeadlessRequest, sessionID: string): Promise { + return this.httpResponseUtil.noBody(await this.fikaRaidController.handleRaidStartHeadless(sessionID, info)); } - /** Handle /fika/raid/dedicated/status */ - public handleRaidStatusDedicated(_url: string, info: IStatusDedicatedRequest, sessionID: string): string { - return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidStatusDedicated(sessionID, info)); - } - - /** Handle /fika/raid/dedicated/getstatus */ - public handleRaidGetStatusDedicated(_url: string, _info: any, _sessionID: string): string { - return this.httpResponseUtil.noBody(this.fikaRaidController.handleRaidGetStatusDedicated()); - } - - /** Handle /fika/raid/dedicated/registerPlayer */ + /** Handle /fika/raid/registerPlayer */ public handleRaidRegisterPlayer(_url: string, info: IRegisterPlayerRequestData, sessionID: string): INullResponseData { this.fikaRaidController.handleRaidRegisterPlayer(sessionID, info); diff --git a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaSendItemCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaSendItemCallbacks.ts index 8a7c7b3..e4ecce1 100644 --- a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaSendItemCallbacks.ts +++ b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaSendItemCallbacks.ts @@ -17,8 +17,8 @@ export class FikaSendItemCallbacks { // empty } - public handleSendItem(pmcData: IPmcData, body: IFikaSendItemRequestData, sessionID: string): IItemEventRouterResponse { - return this.fikaSendItemController.sendItem(pmcData, body, sessionID); + public async handleSendItem(pmcData: IPmcData, body: IFikaSendItemRequestData, sessionID: string): Promise { + return await this.fikaSendItemController.sendItem(pmcData, body, sessionID); } /** Handle /fika/senditem/availablereceivers */ diff --git a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaUpdateCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaUpdateCallbacks.ts index 5938ff8..ff06d1f 100644 --- a/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaUpdateCallbacks.ts +++ b/mods/Fika Server/user/mods/fika-server/src/callbacks/FikaUpdateCallbacks.ts @@ -4,6 +4,7 @@ import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseDat import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { FikaUpdateController } from "../controllers/FikaUpdateController"; +import { EFikaMatchStatus } from "../models/enums/EFikaMatchStatus"; import { IFikaUpdateRaidAddPlayerData } from "../models/fika/routes/raid/join/IFikaRaidAddPlayerData"; import { IFikaUpdatePingRequestData } from "../models/fika/routes/update/IFikaUpdatePingRequestData"; import { IFikaUpdatePlayerspawnRequestData } from "../models/fika/routes/update/IFikaUpdatePlayerspawnRequestData"; @@ -41,8 +42,13 @@ export class FikaUpdateCallbacks { } /** Handle /fika/update/setstatus */ - public handleSetStatus(_url: string, info: IFikaUpdateSetStatusRequestData, _sessionID: string): INullResponseData { - this.fikaUpdateController.handleSetStatus(info); + public async handleSetStatus(_url: string, info: IFikaUpdateSetStatusRequestData, _sessionID: string): Promise { + // Handle conversion of Enum so it can be properly used. + if (Object.keys(EFikaMatchStatus).includes(info.status.toString())) { + info.status = EFikaMatchStatus[info.status.toString()]; + } + + await this.fikaUpdateController.handleSetStatus(info); return this.httpResponseUtil.nullResponse(); } diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaAchievementController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaAchievementController.ts index 046f394..0101f0c 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaAchievementController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaAchievementController.ts @@ -12,7 +12,7 @@ export class FikaAchievementController { ) { // empty } - getAchievementStatistics(_sessionID: string): ICompletedAchievementsResponse { + public getAchievementStatistics(_sessionID: string): ICompletedAchievementsResponse { const achievements = this.databaseService.getAchievements(); const stats: Record = {}; @@ -21,7 +21,7 @@ export class FikaAchievementController { for (const achievement of achievements) { let percentage = 0; for (const profile of profiles) { - if (profile.info?.password === "fika-dedicated") { + if (profile.info?.password === "fika-headless") { continue; } diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaClientController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaClientController.ts index c87d439..faa513f 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaClientController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaClientController.ts @@ -2,125 +2,63 @@ import { inject, injectable } from "tsyringe"; import { IFikaConfigClient } from "../models/fika/config/IFikaConfigClient"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { SaveServer } from "@spt/servers/SaveServer"; import { FikaClientModHashesHelper } from "../helpers/FikaClientModHashesHelper"; +import { IFikaConfigHeadless } from "../models/fika/config/IFikaConfigHeadless"; import { IFikaConfigNatPunchServer } from "../models/fika/config/IFikaConfigNatPunchServer"; import { IFikaCheckModRequestData } from "../models/fika/routes/client/check/IFikaCheckModRequestData"; import { IFikaCheckModResponse, IVersionCheckResponse } from "../models/fika/routes/client/check/IFikaCheckModResponse"; +import { FikaClientService } from "../services/FikaClientService"; import { FikaConfig } from "../utils/FikaConfig"; @injectable() export class FikaClientController { - protected requiredMods: Set; - protected allowedMods: Set; + protected requiredMods: string[] = []; + protected allowedMods: string[] = []; protected hasRequiredOrOptionalMods: boolean = true; - constructor( - @inject("FikaClientModHashesHelper") protected fikaClientModHashesHelper: FikaClientModHashesHelper, - @inject("FikaConfig") protected fikaConfig: FikaConfig, - @inject("SaveServer") protected saveServer: SaveServer, - @inject("WinstonLogger") protected logger: ILogger, - ) { - const config = this.fikaConfig.getConfig(); - - const sanitizedRequiredMods = this.filterEmptyMods(config.client.mods.required); - const sanitizedOptionalMods = this.filterEmptyMods(config.client.mods.optional); - - if (sanitizedRequiredMods.length === 0 && sanitizedOptionalMods.length === 0) { - this.hasRequiredOrOptionalMods = false; - } - - this.requiredMods = new Set([...sanitizedRequiredMods, "com.fika.core", "com.SPT.custom", "com.SPT.singleplayer", "com.SPT.core", "com.SPT.debugging"]); - this.allowedMods = new Set([...this.requiredMods, ...sanitizedOptionalMods, "com.bepis.bepinex.configurationmanager", "com.fika.dedicated"]); - } - - protected filterEmptyMods(array: string[]): string[] { - return array.filter((str) => str.trim() !== ""); - } - + constructor(@inject("FikaClientService") protected fikaClientService: FikaClientService) {} /** * Handle /fika/client/config */ public handleClientConfig(): IFikaConfigClient { - return this.fikaConfig.getConfig().client; + const clientConfig = this.fikaClientService.getClientConfig(); + + //Here be dragons, this is technically not in the client config, or well it was.. But it was decided it was better for this configuration + //To be together with 'sentItemsLoseFIR' so users could find both options easier. + //Keep this here as this is really only supposed to be a 'client' config and it's really only used on the client. + (clientConfig as any).allowItemSending = this.fikaClientService.getIsItemSendingAllowed(); + + return clientConfig; } /** * Handle /fika/natpunchserver/config */ public handleNatPunchServerConfig(): IFikaConfigNatPunchServer { - return this.fikaConfig.getConfig().natPunchServer; + return this.fikaClientService.getNatPunchServerConfig(); } /** * Handle /fika/client/check/mods */ - public handleCheckMods(request: IFikaCheckModRequestData): IFikaCheckModResponse { - const mismatchedMods: IFikaCheckModResponse = { - forbidden: [], - missingRequired: [], - hashMismatch: [], - }; - - // if no configuration was made, allow all mods - if (!this.hasRequiredOrOptionalMods) { - return mismatchedMods; - } - - //check for missing required mods first - for (const pluginId of this.requiredMods) { - if (!request[pluginId]) { - mismatchedMods.missingRequired.push(pluginId); - } - } - - // no need to check anything else since it's missing required mods - if (mismatchedMods.missingRequired.length > 0) { - return mismatchedMods; - } - - for (const [pluginId, hash] of Object.entries(request)) { - // check if the mod isn't allowed - if (!this.allowedMods.has(pluginId)) { - mismatchedMods.forbidden.push(pluginId); - continue; - } - - // first request made will fill in at the very least all the required mods hashes, following requests made by different clients will add any optional mod not added by the first request, otherwise will check against the first request data - if (!this.fikaClientModHashesHelper.exists(pluginId)) { - this.fikaClientModHashesHelper.addHash(pluginId, hash); - continue; - } - - if (this.fikaClientModHashesHelper.getHash(pluginId) !== hash) { - mismatchedMods.hashMismatch.push(pluginId); - } - } - - return mismatchedMods; + public handleCheckMods(request: IFikaCheckModRequestData, sessionID: string): IFikaCheckModResponse { + return this.fikaClientService.getCheckModsResponse(request, sessionID); } /** * Handle /fika/profile/download */ public handleProfileDownload(sessionID: string): any { - const profile = this.saveServer.getProfile(sessionID); - if (profile) { - this.logger.info(`${sessionID} has downloaded their profile`); - return profile; - } - - this.logger.error(`${sessionID} wants to download their profile but we don't have it`); - return null; + return this.fikaClientService.getProfileBySessionID(sessionID); } /** * Handle /fika/client/check/version */ public handleVersionCheck(): IVersionCheckResponse { - const version = this.fikaConfig.getVersion(); - return { version }; + return this.fikaClientService.getVersion(); } } diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaDialogueController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaDialogueController.ts index 6517dd1..1ed1a64 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaDialogueController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaDialogueController.ts @@ -1,17 +1,17 @@ -import { inject, injectAll, injectable } from "tsyringe"; +import { inject, injectable } from "tsyringe"; import { IDialogueChatBot } from "@spt/helpers/Dialogue/IDialogueChatBot"; +import { DialogueHelper } from "@spt/helpers/DialogueHelper"; import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { IFriendRequestSendResponse } from "@spt/models/eft/dialog/IFriendRequestSendResponse"; import { IGetFriendListDataResponse } from "@spt/models/eft/dialog/IGetFriendListDataResponse"; import { BackendErrorCodes } from "@spt/models/enums/BackendErrorCodes"; -import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; -import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { DialogueController } from "@spt/controllers/DialogueController"; import { ISendMessageRequest } from "@spt/models/eft/dialog/ISendMessageRequest"; -import { IMessage } from "@spt/models/eft/profile/ISptProfile"; +import { IMessage, IReplyTo } from "@spt/models/eft/profile/ISptProfile"; import { MessageType } from "@spt/models/enums/MessageType"; import { SaveServer } from "@spt/servers/SaveServer"; import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler"; @@ -25,22 +25,21 @@ import { IFriendRequestListResponse } from "../models/eft/dialog/IFriendRequestL export class FikaDialogueController { constructor( @inject("ProfileHelper") protected profileHelper: ProfileHelper, - @inject("ConfigServer") protected configServer: ConfigServer, + @inject("DialogueHelper") protected dialogueHelper: DialogueHelper, @inject("FikaFriendRequestsHelper") protected fikaFriendRequestsHelper: FikaFriendRequestsHelper, @inject("FikaPlayerRelationsHelper") protected fikaPlayerRelationsHelper: FikaPlayerRelationsHelper, - @inject("DialogueController") protected dialogController: DialogueController, + @inject("DialogueController") protected dialogueController: DialogueController, @inject("SaveServer") protected saveServer: SaveServer, @inject("HashUtil") protected hashUtil: HashUtil, @inject("TimeUtil") protected timeUtil: TimeUtil, @inject("SptWebSocketConnectionHandler") protected webSocketHandler: SptWebSocketConnectionHandler, + @inject("WinstonLogger") protected logger: ILogger, ) { // empty } public getFriendList(sessionID: string): IGetFriendListDataResponse { - // Cast to any to get rid of protected error - const dialogueChatBots: IDialogueChatBot[] = (this.dialogController as any).dialogueChatBots; - let botsAndFriends = dialogueChatBots.map((v) => v.getChatBot()); + let botsAndFriends = (this.dialogueController as any).getActiveChatBots(); const friendsIds = this.fikaPlayerRelationsHelper.getFriendsList(sessionID); @@ -76,7 +75,7 @@ export class FikaDialogueController { const profiles = this.saveServer.getProfiles(); if (!(sessionID in profiles) || !(request.dialogId in profiles)) { // if it's not to another player let SPT handle it - return DialogueController.prototype.sendMessage.call(this.dialogController, sessionID, request); + return DialogueController.prototype.sendMessage.call(this.dialogueController, sessionID, request); } const receiverProfile = profiles[request.dialogId]; @@ -175,10 +174,17 @@ export class FikaDialogueController { rewardCollected: false, }; + if (request.replyTo) { + const replyMessage = this.getMessageToReplyTo(request.dialogId, request.replyTo, sessionID); + if (replyMessage) { + message.replyTo = replyMessage; + } + } + senderDialog.messages.push(message); receiverDialog.messages.push(message); - this.webSocketHandler.sendMessage(receiverProfile.info.id, { + this.webSocketHandler.sendMessageAsync(receiverProfile.info.id, { type: "new_message", eventId: "new_message", EventId: "new_message", @@ -189,6 +195,37 @@ export class FikaDialogueController { return message._id; } + /** + * @param recipientId The id of the recipient + * @param replyToId The id of the message to reply to + * @param dialogueId The id of the dialogue (traderId or profileId) + * @returns A new instance with data from the found message, otherwise undefined + */ + private getMessageToReplyTo(recipientId: string, replyToId: string, dialogueId: string): IReplyTo | undefined { + let message: IReplyTo | undefined = undefined; + const currentDialogue = this.dialogueHelper.getDialogueFromProfile(recipientId, dialogueId); + + if (!currentDialogue) { + this.logger.warning(`Could not find dialogue ${dialogueId} from sender`); + return message; + } + + for (const dialogueMessage of currentDialogue.messages) { + if (dialogueMessage._id === replyToId) { + message = { + _id: dialogueMessage._id, + dt: dialogueMessage.dt, + type: dialogueMessage.type, + uid: dialogueMessage.uid, + text: dialogueMessage.text, + }; + break; + } + } + + return message; + } + public listOutbox(sessionID: string): IFriendRequestListResponse[] { const sentFriendRequests = this.fikaFriendRequestsHelper.getSentFriendRequests(sessionID) as IFriendRequestListResponse[]; diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaHeadlessController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaHeadlessController.ts new file mode 100644 index 0000000..8b408c7 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaHeadlessController.ts @@ -0,0 +1,70 @@ +import { inject, injectable } from "tsyringe"; + +import { QuestHelper } from "@spt/helpers/QuestHelper"; +import { IQuest } from "@spt/models/eft/common/tables/IQuest"; +import { QuestStatus } from "@spt/models/enums/QuestStatus"; +import { FikaHeadlessHelper } from "../helpers/FikaHeadlessHelper"; +import { IHeadlessAvailableClients } from "../models/fika/headless/IHeadlessAvailableClients"; +import { IHeadlessClients } from "../models/fika/headless/IHeadlessClients"; +import { IHeadlessRestartAfterAmountOfRaids } from "../models/fika/headless/IHeadlessRestartAfterAmountOfRaids"; +import { FikaConfig } from "../utils/FikaConfig"; + +@injectable() +export class FikaHeadlessController { + constructor( + @inject("FikaHeadlessHelper") protected fikaHeadlessHelper: FikaHeadlessHelper, + @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("QuestHelper") protected questHelper: QuestHelper, + ) {} + + /** + * Handle /fika/headless/get + */ + public handleGetHeadlesses(): IHeadlessClients { + const data: IHeadlessClients = { + headlesses: {}, + }; + + for (const [headlessSessionID, headless] of this.fikaHeadlessHelper.getHeadlessClients()) { + data.headlesses[headlessSessionID] = { + state: headless.state, + players: headless.players, + requesterSessionID: headless.requesterSessionID, + hasNotifiedRequester: headless.hasNotifiedRequester, + }; + } + + return data; + } + + /** + * Handle /fika/headless/available + */ + public handleGetAvailableHeadlesses(): IHeadlessAvailableClients[] { + return this.fikaHeadlessHelper.getAvailableHeadlessClients(); + } + + /** + * Handle /fika/headless/restartafterraidamount + */ + public handleRestartAfterRaidAmount(): IHeadlessRestartAfterAmountOfRaids { + const data: IHeadlessRestartAfterAmountOfRaids = { + amount: this.fikaConfig.getConfig().headless.restartAfterAmountOfRaids, + }; + + return data; + } + + /** + * Handle /fika/headless/questtemplates + */ + public handleGetAllQuestTemplates(): IQuest[] { + const quests = this.questHelper.getQuestsFromDb(); + + for (const quest of quests) { + quest.sptStatus = QuestStatus.AvailableForStart; + } + + return quests; + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaLocationController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaLocationController.ts index 5619672..05b102a 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaLocationController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaLocationController.ts @@ -2,12 +2,16 @@ import { inject, injectable } from "tsyringe"; import { IGetRaidConfigurationRequestData } from "@spt/models/eft/match/IGetRaidConfigurationRequestData"; +import { FikaHeadlessHelper } from "../helpers/FikaHeadlessHelper"; import { IFikaRaidsResponse } from "../models/fika/routes/location/IFikaRaidsResponse"; import { FikaMatchService } from "../services/FikaMatchService"; @injectable() export class FikaLocationController { - constructor(@inject("FikaMatchService") protected fikaMatchService: FikaMatchService) { + constructor( + @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, + @inject("FikaHeadlessHelper") protected fikaHeadlessHelper: FikaHeadlessHelper, + ) { // empty } @@ -25,15 +29,23 @@ export class FikaLocationController { players[profileId] = player.isDead; } + let hostUsername = match.hostUsername; + + if (match.isHeadless) { + hostUsername = this.fikaHeadlessHelper.getHeadlessNickname(matchId); + } + matches.push({ serverId: matchId, - hostUsername: match.hostUsername, + hostUsername: hostUsername, playerCount: match.players.size, status: match.status, location: match.raidConfig.location, side: match.side, time: match.time, players: players, + isHeadless: match.isHeadless, + headlessRequesterNickname: this.fikaHeadlessHelper.getRequesterUsername(matchId) || "", //Set this to an empty string if there is no requester. }); } diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaRaidController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaRaidController.ts index cfe220f..57689f3 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaRaidController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaRaidController.ts @@ -1,43 +1,36 @@ import { inject, injectable } from "tsyringe"; -import { WebSocket } from "ws"; import { InraidController } from "@spt/controllers/InraidController"; import { ProfileHelper } from "@spt/helpers/ProfileHelper"; -import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IRegisterPlayerRequestData } from "@spt/models/eft/inRaid/IRegisterPlayerRequestData"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { DatabaseService } from "@spt/services/DatabaseService"; -import { EDedicatedStatus } from "../models/enums/EDedicatedStatus"; +import { FikaHeadlessHelper } from "../helpers/FikaHeadlessHelper"; import { EFikaMatchEndSessionMessage } from "../models/enums/EFikaMatchEndSessionMessages"; import { EFikaNotifications } from "../models/enums/EFikaNotifications"; +import { IHeadlessAvailableClients } from "../models/fika/headless/IHeadlessAvailableClients"; import { IFikaRaidServerIdRequestData } from "../models/fika/routes/raid/IFikaRaidServerIdRequestData"; import { IFikaRaidCreateRequestData } from "../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; import { IFikaRaidCreateResponse } from "../models/fika/routes/raid/create/IFikaRaidCreateResponse"; -import { IGetStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse"; -import { IStartDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; -import { IStartDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStartDedicatedResponse"; -import { IStatusDedicatedRequest } from "../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; -import { IStatusDedicatedResponse } from "../models/fika/routes/raid/dedicated/IStatusDedicatedResponse"; import { IFikaRaidGethostResponse } from "../models/fika/routes/raid/gethost/IFikaRaidGethostResponse"; import { IFikaRaidSettingsResponse } from "../models/fika/routes/raid/getsettings/IFikaRaidSettingsResponse"; +import { IStartHeadlessRequest } from "../models/fika/routes/raid/headless/IStartHeadlessRequest"; +import { IStartHeadlessResponse } from "../models/fika/routes/raid/headless/IStartHeadlessResponse"; import { IFikaRaidJoinRequestData } from "../models/fika/routes/raid/join/IFikaRaidJoinRequestData"; import { IFikaRaidJoinResponse } from "../models/fika/routes/raid/join/IFikaRaidJoinResponse"; import { IFikaRaidLeaveRequestData } from "../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; import { IStartRaidNotification } from "../models/fika/websocket/notifications/IStartRaidNotification"; import { FikaMatchService } from "../services/FikaMatchService"; -import { FikaDedicatedRaidService } from "../services/dedicated/FikaDedicatedRaidService"; -import { FikaDedicatedRaidWebSocket } from "../websockets/FikaDedicatedRaidWebSocket"; +import { FikaHeadlessService } from "../services/headless/FikaHeadlessService"; import { FikaNotificationWebSocket } from "../websockets/FikaNotificationWebSocket"; @injectable() export class FikaRaidController { constructor( - @inject("DatabaseService") protected databaseService: DatabaseService, @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, - @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, - @inject("FikaDedicatedRaidWebSocket") protected fikaDedicatedRaidWebSocket: FikaDedicatedRaidWebSocket, - @inject("ProfileHelper") protected profileHelper: ProfileHelper, + @inject("FikaHeadlessHelper") protected fikaHeadlessHelper: FikaHeadlessHelper, + @inject("FikaHeadlessService") protected fikaHeadlessService: FikaHeadlessService, @inject("WinstonLogger") protected logger: ILogger, @inject("InraidController") protected inraidController: InraidController, @inject("FikaNotificationWebSocket") protected fikaNotificationWebSocket: FikaNotificationWebSocket, @@ -50,10 +43,19 @@ export class FikaRaidController { * @param request */ public handleRaidCreate(request: IFikaRaidCreateRequestData): IFikaRaidCreateResponse { + let hostUsername = request.hostUsername; + + if (this.fikaHeadlessHelper.isHeadlessClient(request.serverId)) { + hostUsername = this.fikaHeadlessHelper.getHeadlessNickname(request.serverId); + } + const notification: IStartRaidNotification = { type: EFikaNotifications.StartedRaid, - nickname: request.hostUsername, + nickname: hostUsername, location: request.settings.location, + isHeadlessRaid: this.fikaHeadlessHelper.isHeadlessClient(request.serverId), + headlessRequesterName: this.fikaHeadlessHelper.getRequesterUsername(request.serverId) || "", + raidTime: request.time }; this.fikaNotificationWebSocket.broadcast(notification); @@ -74,7 +76,7 @@ export class FikaRaidController { serverId: request.serverId, timestamp: match.timestamp, gameVersion: match.gameVersion, - fikaVersion: match.fikaVersion, + crc32: match.crc32, raidCode: match.raidCode, }; } @@ -106,7 +108,7 @@ export class FikaRaidController { ips: match.ips, port: match.port, natPunch: match.natPunch, - isDedicated: match.isDedicated, + isHeadless: match.isHeadless, }; } @@ -128,113 +130,34 @@ export class FikaRaidController { }; } - /** Handle /fika/raid/dedicated/start */ - handleRaidStartDedicated(sessionID: string, info: IStartDedicatedRequest): IStartDedicatedResponse { - if (!this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { + /** Handle /fika/raid/headless/start */ + public async handleRaidStartHeadless(sessionID: string, info: IStartHeadlessRequest): Promise { + if (!this.fikaHeadlessHelper.isHeadlessClientAvailable(info.headlessSessionID)) { return { matchId: null, - error: "No dedicated clients available.", + error: "This headless client is not available.", }; } - if (sessionID in this.fikaDedicatedRaidService.dedicatedClients) { + if (this.fikaHeadlessHelper.isHeadlessClient(sessionID)) { return { matchId: null, - error: "You are trying to connect to a dedicated client while having Fika.Dedicated installed. Please remove Fika.Dedicated from your client and try again.", + error: "You are trying to connect to a headless client while having Fika.Headless installed. Please remove Fika.Headless from your client and try again.", }; } - let dedicatedClient: string | undefined = undefined; - let dedicatedClientWs: WebSocket | undefined = undefined; + const headlessClientId = await this.fikaHeadlessService.startHeadlessRaid(info.headlessSessionID, sessionID, info); - for (const dedicatedSessionId in this.fikaDedicatedRaidService.dedicatedClients) { - const dedicatedClientInfo = this.fikaDedicatedRaidService.dedicatedClients[dedicatedSessionId]; - - if (dedicatedClientInfo.state != EDedicatedStatus.READY) { - continue; - } - - dedicatedClientWs = this.fikaDedicatedRaidWebSocket.clientWebSockets[dedicatedSessionId]; - - if (!dedicatedClientWs || dedicatedClientWs.readyState == WebSocket.CLOSED) { - continue; - } - - dedicatedClient = dedicatedSessionId; - break; - } - - if (!dedicatedClient) { - return { - matchId: null, - error: "No dedicated clients available at this time.", - }; - } - - const pmcDedicatedClientProfile: IPmcData = this.profileHelper.getPmcProfile(dedicatedClient); - const requesterProfile: IPmcData = this.profileHelper.getPmcProfile(sessionID); - - this.logger.debug(`Dedicated: ${pmcDedicatedClientProfile.Info.Nickname} ${pmcDedicatedClientProfile.Info.Level} - Requester: ${requesterProfile.Info.Nickname} ${requesterProfile.Info.Level}`); - - //Set level of the dedicated profile to the person that has requested the raid to be started. - pmcDedicatedClientProfile.Info.Level = requesterProfile.Info.Level; - pmcDedicatedClientProfile.Info.Experience = requesterProfile.Info.Experience; - - this.fikaDedicatedRaidService.requestedSessions[dedicatedClient] = sessionID; - - dedicatedClientWs.send( - JSON.stringify({ - type: "fikaDedicatedStartRaid", - ...info, - }), - ); - - this.logger.info(`Sent WS fikaDedicatedStartRaid to ${dedicatedClient}`); + this.logger.info(`Sent WS fikaHeadlessStartRaid to ${headlessClientId}`); return { // This really isn't required, I just want to make sure on the client - matchId: dedicatedClient, + matchId: headlessClientId, error: null, }; } - /** Handle /fika/raid/dedicated/status */ - public handleRaidStatusDedicated(sessionId: string, info: IStatusDedicatedRequest): IStatusDedicatedResponse { - // Temp fix because the enum gets deserialized as a string instead of an integer - switch (info.status.toString()) { - case "READY": - info.status = EDedicatedStatus.READY; - break; - case "IN_RAID": - info.status = EDedicatedStatus.IN_RAID; - break; - } - - if (info.status == EDedicatedStatus.READY && !this.fikaDedicatedRaidService.isDedicatedClientAvailable()) { - if (this.fikaDedicatedRaidService.onDedicatedClientAvailable) { - this.fikaDedicatedRaidService.onDedicatedClientAvailable(); - } - } - - this.fikaDedicatedRaidService.dedicatedClients[sessionId] = { - state: info.status, - lastPing: Date.now(), - }; - - return { - sessionId: info.sessionId, - status: info.status, - }; - } - - /** Handle /fika/raid/dedicated/getstatus */ - public handleRaidGetStatusDedicated(): IGetStatusDedicatedResponse { - return { - available: this.fikaDedicatedRaidService.isDedicatedClientAvailable(), - }; - } - - /** Handle /fika/raid/dedicated/registerPlayer */ + /** Handle /fika/raid/registerPlayer */ public handleRaidRegisterPlayer(sessionId: string, info: IRegisterPlayerRequestData): void { this.inraidController.addPlayer(sessionId, info); } diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaSendItemController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaSendItemController.ts index 974d509..0db0803 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaSendItemController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaSendItemController.ts @@ -6,7 +6,7 @@ import { IPmcData } from "@spt/models/eft/common/IPmcData"; import { IItem } from "@spt/models/eft/common/tables/IItem"; import { IItemEventRouterResponse } from "@spt/models/eft/itemEvent/IItemEventRouterResponse"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { EventOutputHolder } from "@spt/routers/EventOutputHolder"; import { SaveServer } from "@spt/servers/SaveServer"; import { DatabaseService } from "@spt/services/DatabaseService"; @@ -25,7 +25,6 @@ export class FikaSendItemController { constructor( @inject("WinstonLogger") protected logger: ILogger, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, - @inject("DatabaseService") protected databaseService: DatabaseService, @inject("MailSendService") protected mailSendService: MailSendService, @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, @inject("SaveServer") protected saveServer: SaveServer, @@ -37,7 +36,7 @@ export class FikaSendItemController { // empty } - public sendItem(_pmcData: IPmcData, body: IFikaSendItemRequestData, sessionID: string): IItemEventRouterResponse { + public async sendItem(_pmcData: IPmcData, body: IFikaSendItemRequestData, sessionID: string): Promise { const fikaConfig = this.fikaConfig.getConfig(); const output = this.eventOutputHolder.getOutput(sessionID); @@ -72,7 +71,7 @@ export class FikaSendItemController { return this.httpResponse.appendErrorToOutput(output, "Item not found in inventory"); } - if (fikaConfig.server.giftedItemsLoseFIR) { + if (fikaConfig.server.sentItemsLoseFIR) { for (const item of itemsToSend) { item.upd ??= {}; @@ -80,12 +79,7 @@ export class FikaSendItemController { } } - this.mailSendService.sendSystemMessageToPlayer( - body.target, - `You have received a gift from ${senderProfile?.characters?.pmc?.Info?.Nickname ?? "unknown"}`, - itemsToSend, - 604800 - ); + this.mailSendService.sendSystemMessageToPlayer(body.target, `You have received a gift from ${senderProfile?.characters?.pmc?.Info?.Nickname ?? "unknown"}`, itemsToSend, 604800); this.inventoryHelper.removeItem(senderProfile.characters.pmc, body.id, sessionID, output); @@ -96,7 +90,7 @@ export class FikaSendItemController { itemName: `${itemsToSend[0]._tpl} ShortName`, }; - this.fikaNotificationWebSocket.send(body.target, notification); + await this.fikaNotificationWebSocket.sendAsync(body.target, notification); return output; } @@ -119,7 +113,7 @@ export class FikaSendItemController { //Uninitialized profiles can cause this to error out, skip these. if (!profile.characters?.pmc?.Info) continue; - if (profile.info.password === "fika-dedicated") continue; + if (profile.info.password === "fika-headless") continue; const nickname = profile.characters.pmc.Info.Nickname; if (!(nickname in result) && nickname !== sender.characters.pmc.Info.Nickname) { diff --git a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaUpdateController.ts b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaUpdateController.ts index a9db341..0af833c 100644 --- a/mods/Fika Server/user/mods/fika-server/src/controllers/FikaUpdateController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/controllers/FikaUpdateController.ts @@ -34,15 +34,15 @@ export class FikaUpdateController { * @param request */ public handleSethost(request: IFikaUpdateSethostRequestData): void { - this.fikaMatchService.setMatchHost(request.serverId, request.ips, request.port, request.natPunch, request.isDedicated); + this.fikaMatchService.setMatchHost(request.serverId, request.ips, request.port, request.natPunch, request.isHeadless); } /** * Handle /fika/update/setstatus * @param request */ - public handleSetStatus(request: IFikaUpdateSetStatusRequestData): void { - this.fikaMatchService.setMatchStatus(request.serverId, request.status); + public async handleSetStatus(request: IFikaUpdateSetStatusRequestData): Promise { + await this.fikaMatchService.setMatchStatus(request.serverId, request.status); } /** diff --git a/mods/Fika Server/user/mods/fika-server/src/di/Container.ts b/mods/Fika Server/user/mods/fika-server/src/di/Container.ts index ecda774..4bfc965 100644 --- a/mods/Fika Server/user/mods/fika-server/src/di/Container.ts +++ b/mods/Fika Server/user/mods/fika-server/src/di/Container.ts @@ -4,21 +4,18 @@ import { FikaConfig } from "../utils/FikaConfig"; import { Overrider } from "../overrides/Overrider"; import { DialogueCallbacksOverride } from "../overrides/callbacks/DialogueCallbacks"; -import { LocationLifecycleServiceOverride } from "../overrides/services/LocationLifecycleService"; import { AchievementControllerOverride } from "../overrides/controllers/AchievementController"; import { DialogueControllerOverride } from "../overrides/controllers/DialogueController"; import { ProfileControllerOverride } from "../overrides/controllers/ProfileController"; // import { LocalesOverride } from "../overrides/other/Locales"; import { HttpRouterOverride } from "../overrides/routers/HttpRouter"; -import { LauncherControllerOverride } from "../overrides/controllers/LauncherController"; -import { MatchControllerOverride } from "../overrides/controllers/MatchController"; +import { LocationLifecycleServiceOverride } from "../overrides/services/LocationLifecycleService"; +import { FikaInsuranceService } from "../services/FikaInsuranceService"; import { FikaMatchService } from "../services/FikaMatchService"; +import { FikaPresenceService } from "../services/FikaPresenceService"; import { FikaFriendRequestsCacheService } from "../services/cache/FikaFriendRequestsCacheService"; import { FikaPlayerRelationsCacheService } from "../services/cache/FikaPlayerRelationsCacheService"; -import { FikaDedicatedRaidService } from "../services/dedicated/FikaDedicatedRaidService"; -import { FikaInsuranceService } from "../services/FikaInsuranceService"; -import { FikaPresenceService } from "../services/FikaPresenceService"; import { FikaClientModHashesHelper } from "../helpers/FikaClientModHashesHelper"; import { FikaFriendRequestsHelper } from "../helpers/FikaFriendRequestsHelper"; @@ -34,27 +31,36 @@ import { FikaUpdateController } from "../controllers/FikaUpdateController"; import { FikaClientCallbacks } from "../callbacks/FikaClientCallbacks"; import { FikaLocationCallbacks } from "../callbacks/FikaLocationCallbacks"; +import { FikaPresenceCallbacks } from "../callbacks/FikaPresenceCallbacks"; import { FikaRaidCallbacks } from "../callbacks/FikaRaidCallbacks"; import { FikaSendItemCallbacks } from "../callbacks/FikaSendItemCallbacks"; import { FikaUpdateCallbacks } from "../callbacks/FikaUpdateCallbacks"; -import { FikaPresenceCallbacks } from "../callbacks/FikaPresenceCallbacks"; import { FikaItemEventRouter } from "../routers/item_events/FikaItemEventRouter"; import { FikaClientStaticRouter } from "../routers/static/FikaClientStaticRouter"; import { FikaLocationStaticRouter } from "../routers/static/FikaLocationStaticRouter"; +import { FikaPresenceStaticRouter } from "../routers/static/FikaPresenceStaticRouter"; import { FikaRaidStaticRouter } from "../routers/static/FikaRaidStaticRouter"; import { FikaSendItemStaticRouter } from "../routers/static/FikaSendItemStaticRouter"; import { FikaUpdateStaticRouter } from "../routers/static/FikaUpdateStaticRouter"; -import { FikaPresenceStaticRouter } from "../routers/static/FikaPresenceStaticRouter"; -import { FikaDedicatedRaidWebSocket } from "../websockets/FikaDedicatedRaidWebSocket"; import { FikaNotificationWebSocket } from "../websockets/FikaNotificationWebSocket"; +import { ClientLogCallbacks } from "@spt/callbacks/ClientLogCallbacks"; import { Fika } from "../Fika"; -import { FikaDedicatedProfileService } from "../services/dedicated/FikaDedicatedProfileService"; -import { FikaServerTools } from "../utils/FikaServerTools"; -import { FikaNotificationStaticRouter } from "../routers/static/FikaNotificationStaticRouter"; +import { FikaHeadlessCallbacks } from "../callbacks/FikaHeadlessCallbacks"; import { FikaNotificationCallbacks } from "../callbacks/FikaNotificationCallbacks"; +import { FikaHeadlessController } from "../controllers/FikaHeadlessController"; +import { FikaHeadlessHelper } from "../helpers/FikaHeadlessHelper"; +import { ClientLogCallbacksOverride } from "../overrides/callbacks/ClientLogCallbacks"; +import { FikaHeadlessStaticRouter } from "../routers/static/FikaHeadlessStaticRouter"; +import { FikaNotificationStaticRouter } from "../routers/static/FikaNotificationStaticRouter"; +import { FikaClientService } from "../services/FikaClientService"; +import { FikaHeadlessProfileService } from "../services/headless/FikaHeadlessProfileService"; +import { FikaHeadlessService } from "../services/headless/FikaHeadlessService"; +import { FikaServerTools } from "../utils/FikaServerTools"; +import { FikaHeadlessClientWebSocket } from "../websockets/FikaHeadlessClientWebSocket"; +import { FikaHeadlessRequesterWebSocket } from "../websockets/FikaHeadlessRequesterWebSocket"; export class Container { public static register(container: DependencyContainer): void { @@ -86,11 +92,10 @@ export class Container { container.registerType("Overrides", "HttpRouterOverride"); // container.registerType("Overrides", "LocalesOverride"); container.registerType("Overrides", "AchievementControllerOverride"); - container.registerType("Overrides", "LauncherControllerOverride"); container.registerType("Overrides", "LocationLifecycleServiceOverride"); - container.registerType("Overrides", "MatchControllerOverride"); container.registerType("StaticRoutes", "FikaClientStaticRouter"); + container.registerType("StaticRoutes", "FikaHeadlessStaticRouter"); container.registerType("StaticRoutes", "FikaLocationStaticRouter"); container.registerType("StaticRoutes", "FikaRaidStaticRouter"); container.registerType("StaticRoutes", "FikaSendItemStaticRouter"); @@ -100,8 +105,9 @@ export class Container { container.registerType("IERouters", "FikaItemEventRouter"); - container.registerType("WebSocketConnectionHandler", "FikaDedicatedRaidWebSocket"); container.registerType("WebSocketConnectionHandler", "FikaNotificationWebSocket"); + container.registerType("WebSocketConnectionHandler", "FikaHeadlessClientWebSocket"); + container.registerType("WebSocketConnectionHandler", "FikaHeadlessRequesterWebSocket"); } private static registerUtils(container: DependencyContainer): void { @@ -111,35 +117,37 @@ export class Container { private static registerOverrides(container: DependencyContainer): void { container.register("DialogueCallbacksOverride", DialogueCallbacksOverride, { lifecycle: Lifecycle.Singleton }); + container.register("ClientLogCallbacksOverride", ClientLogCallbacksOverride, { lifecycle: Lifecycle.Singleton }); container.register("DialogueControllerOverride", DialogueControllerOverride, { lifecycle: Lifecycle.Singleton }); container.register("ProfileControllerOverride", ProfileControllerOverride, { lifecycle: Lifecycle.Singleton }); container.register("HttpRouterOverride", HttpRouterOverride, { lifecycle: Lifecycle.Singleton }); // container.register("LocalesOverride", LocalesOverride, { lifecycle: Lifecycle.Singleton }); container.register("Overrider", Overrider, { lifecycle: Lifecycle.Singleton }); container.register("AchievementControllerOverride", AchievementControllerOverride, { lifecycle: Lifecycle.Singleton }); - container.register("LauncherControllerOverride", LauncherControllerOverride, { lifecycle: Lifecycle.Singleton }); container.register("LocationLifecycleServiceOverride", LocationLifecycleServiceOverride, { lifecycle: Lifecycle.Singleton }); - container.register("MatchControllerOverride", MatchControllerOverride, { lifecycle: Lifecycle.Singleton }); } private static registerServices(container: DependencyContainer): void { + container.register("FikaClientService", FikaClientService, { lifecycle: Lifecycle.Singleton }); container.register("FikaMatchService", FikaMatchService, { lifecycle: Lifecycle.Singleton }); container.register("FikaFriendRequestsCacheService", FikaFriendRequestsCacheService, { lifecycle: Lifecycle.Singleton }); container.register("FikaPlayerRelationsCacheService", FikaPlayerRelationsCacheService, { lifecycle: Lifecycle.Singleton }); - container.register("FikaDedicatedRaidService", FikaDedicatedRaidService, { lifecycle: Lifecycle.Singleton }); - container.register("FikaDedicatedProfileService", FikaDedicatedProfileService, { lifecycle: Lifecycle.Singleton }); + container.register("FikaHeadlessProfileService", FikaHeadlessProfileService, { lifecycle: Lifecycle.Singleton }); container.register("FikaInsuranceService", FikaInsuranceService, { lifecycle: Lifecycle.Singleton }); container.register("FikaPresenceService", FikaPresenceService, { lifecycle: Lifecycle.Singleton }); + container.register("FikaHeadlessService", FikaHeadlessService, { lifecycle: Lifecycle.Singleton }); } private static registerHelpers(container: DependencyContainer): void { container.register("FikaClientModHashesHelper", FikaClientModHashesHelper, { lifecycle: Lifecycle.Singleton }); - container.register("FikaFriendRequestsHelper", FikaFriendRequestsHelper, { lifecycle: Lifecycle.Singleton }); - container.register("FikaPlayerRelationsHelper", FikaPlayerRelationsHelper, { lifecycle: Lifecycle.Singleton }); + container.register("FikaFriendRequestsHelper", { useClass: FikaFriendRequestsHelper }); + container.register("FikaPlayerRelationsHelper", { useClass: FikaPlayerRelationsHelper }); + container.register("FikaHeadlessHelper", { useClass: FikaHeadlessHelper }); } private static registerControllers(container: DependencyContainer): void { container.register("FikaClientController", { useClass: FikaClientController }); + container.register("FikaHeadlessController", { useClass: FikaHeadlessController }); container.register("FikaDialogueController", { useClass: FikaDialogueController }); container.register("FikaLocationController", { useClass: FikaLocationController }); container.register("FikaRaidController", { useClass: FikaRaidController }); @@ -150,6 +158,7 @@ export class Container { private static registerCallbacks(container: DependencyContainer): void { container.register("FikaClientCallbacks", { useClass: FikaClientCallbacks }); + container.register("FikaHeadlessCallbacks", { useClass: FikaHeadlessCallbacks }); container.register("FikaLocationCallbacks", { useClass: FikaLocationCallbacks }); container.register("FikaRaidCallbacks", { useClass: FikaRaidCallbacks }); container.register("FikaSendItemCallbacks", { useClass: FikaSendItemCallbacks }); @@ -160,6 +169,7 @@ export class Container { private static registerRouters(container: DependencyContainer): void { container.register("FikaClientStaticRouter", { useClass: FikaClientStaticRouter }); + container.register("FikaHeadlessStaticRouter", { useClass: FikaHeadlessStaticRouter }); container.register("FikaLocationStaticRouter", { useClass: FikaLocationStaticRouter }); container.register("FikaRaidStaticRouter", { useClass: FikaRaidStaticRouter }); container.register("FikaSendItemStaticRouter", { useClass: FikaSendItemStaticRouter }); @@ -170,7 +180,8 @@ export class Container { } private static registerWebSockets(container: DependencyContainer): void { - container.register("FikaDedicatedRaidWebSocket", FikaDedicatedRaidWebSocket, { lifecycle: Lifecycle.Singleton }); container.register("FikaNotificationWebSocket", FikaNotificationWebSocket, { lifecycle: Lifecycle.Singleton }); + container.register("FikaHeadlessClientWebSocket", FikaHeadlessClientWebSocket, { lifecycle: Lifecycle.Singleton }); + container.register("FikaHeadlessRequesterWebSocket", FikaHeadlessRequesterWebSocket, { lifecycle: Lifecycle.Singleton }); } } diff --git a/mods/Fika Server/user/mods/fika-server/src/helpers/FikaFriendRequestsHelper.ts b/mods/Fika Server/user/mods/fika-server/src/helpers/FikaFriendRequestsHelper.ts index 13f06b8..e83ee13 100644 --- a/mods/Fika Server/user/mods/fika-server/src/helpers/FikaFriendRequestsHelper.ts +++ b/mods/Fika Server/user/mods/fika-server/src/helpers/FikaFriendRequestsHelper.ts @@ -2,13 +2,13 @@ import { inject, injectable } from "tsyringe"; import { HashUtil } from "@spt/utils/HashUtil"; -import { IFikaFriendRequests } from "../models/fika/IFikaFriendRequests"; -import { FikaFriendRequestsCacheService } from "../services/cache/FikaFriendRequestsCacheService"; import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; +import { LogTextColor } from "@spt/models/spt/logging/LogTextColor"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { SaveServer } from "@spt/servers/SaveServer"; import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { LogTextColor } from "@spt/models/spt/logging/LogTextColor"; +import { IFikaFriendRequests } from "../models/fika/IFikaFriendRequests"; +import { FikaFriendRequestsCacheService } from "../services/cache/FikaFriendRequestsCacheService"; @injectable() export class FikaFriendRequestsHelper { @@ -69,7 +69,7 @@ export class FikaFriendRequestsHelper { if (profile) { this.logger.logWithColor(`Sending WebSocket message to ${toProfileId}`, LogTextColor.GREEN); - this.webSocketHandler.sendMessage(toProfileId, { + this.webSocketHandler.sendMessageAsync(toProfileId, { type: "friendListNewRequest", eventId: "friendListNewRequest", _id: fromProfileId, @@ -109,7 +109,7 @@ export class FikaFriendRequestsHelper { switch (reason) { case "accept": { const profile = this.saveServer.getProfile(toProfileId); - this.webSocketHandler.sendMessage(fromProfileId, { + this.webSocketHandler.sendMessageAsync(fromProfileId, { type: "friendListRequestAccept", eventId: "friendListRequestAccept", profile: { @@ -131,7 +131,7 @@ export class FikaFriendRequestsHelper { } case "cancel": { const profile = this.saveServer.getProfile(fromProfileId); - this.webSocketHandler.sendMessage(toProfileId, { + this.webSocketHandler.sendMessageAsync(toProfileId, { type: "friendListRequestCancel", eventId: "friendListRequestCancel", profile: { @@ -153,7 +153,7 @@ export class FikaFriendRequestsHelper { } case "decline": { const profile = this.saveServer.getProfile(toProfileId); - this.webSocketHandler.sendMessage(fromProfileId, { + this.webSocketHandler.sendMessageAsync(fromProfileId, { type: "friendListRequestDecline", eventId: "friendListRequestDecline", profile: { diff --git a/mods/Fika Server/user/mods/fika-server/src/helpers/FikaHeadlessHelper.ts b/mods/Fika Server/user/mods/fika-server/src/helpers/FikaHeadlessHelper.ts new file mode 100644 index 0000000..b135ff6 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/helpers/FikaHeadlessHelper.ts @@ -0,0 +1,115 @@ +import { inject, injectable } from "tsyringe"; + +import { SaveServer } from "@spt/servers/SaveServer"; +import { EHeadlessStatus } from "../models/enums/EHeadlessStatus"; +import { IHeadlessAvailableClients } from "../models/fika/headless/IHeadlessAvailableClients"; +import { IHeadlessClientInfo } from "../models/fika/headless/IHeadlessClientInfo"; +import { FikaHeadlessProfileService } from "../services/headless/FikaHeadlessProfileService"; +import { FikaHeadlessService } from "../services/headless/FikaHeadlessService"; +import { FikaConfig } from "../utils/FikaConfig"; + +@injectable() +export class FikaHeadlessHelper { + constructor( + @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("SaveServer") protected saveServer: SaveServer, + @inject("FikaHeadlessService") protected FikaHeadlessService: FikaHeadlessService, + @inject("FikaHeadlessProfileService") protected fikaHeadlessProfileService: FikaHeadlessProfileService, + ) { + // empty + } + + /** + * Gets all currently logged in headlesses + * + * @returns A map where the key is the sessionID and the value is an IHeadlessClientInfo object + */ + public getHeadlessClients(): Map { + return this.FikaHeadlessService.getHeadlessClients(); + } + + /** + * Allows for checking if a SessionID is a headless client + * + * @param sessionId The sessionID to check + * @returns Returns true if the passed sessionID is a headless, returns false if not. + */ + public isHeadlessClient(sessionId: string): boolean { + return this.fikaHeadlessProfileService.getHeadlessProfiles().some((profile) => profile.info.id === sessionId); + } + + /** + * Allows for checking if the given headless client is available + * + * @returns Returns true if it's available, returns false if it isn't available. + */ + public isHeadlessClientAvailable(headlessSessionID: string): boolean { + const headless = this.FikaHeadlessService.getHeadlessClients().get(headlessSessionID); + + if (!headless) { + return false; + } + + if (headless.state === EHeadlessStatus.READY) { + return true; + } + + return false; + } + + /** + * Gets the requester's username for a headless client if there is any. + * + * @returns The nickname if the headless has been requested by a user, returns null if not. + */ + public getRequesterUsername(headlessSessionID: string): string | null { + const headlessClient = this.FikaHeadlessService.getHeadlessClients().get(headlessSessionID); + + if (!headlessClient) { + return null; + } + + if (!headlessClient.requesterSessionID) { + return null; + } + + return this.saveServer.getProfile(headlessClient.requesterSessionID).characters.pmc.Info.Nickname; + } + + /*** + * Gets the alias (If it has been given one) or nickname of the headless client + * + * @returns the alias, or nickname or the headless client. + */ + public getHeadlessNickname(headlessSessionID: string): string { + const AliasName = this.fikaConfig.getConfig().headless.profiles.aliases[headlessSessionID]; + + if (!AliasName) { + return this.saveServer.getProfile(headlessSessionID).characters.pmc.Info.Nickname; + } + + return AliasName; + } + + /** + * Gets all available headless clients + * + * @returns Returns an array of available headless clients + */ + public getAvailableHeadlessClients(): IHeadlessAvailableClients[] { + const headlessClients: IHeadlessAvailableClients[] = []; + + for (const [headlessSessionID, headless] of this.getHeadlessClients()) { + if (headless.state === EHeadlessStatus.READY) { + const availableHeadlessClient: IHeadlessAvailableClients = { + headlessSessionID: headlessSessionID, + alias: this.getHeadlessNickname(headlessSessionID), + }; + + headlessClients.push(availableHeadlessClient); + } + } + + return headlessClients; + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/helpers/FikaPlayerRelationsHelper.ts b/mods/Fika Server/user/mods/fika-server/src/helpers/FikaPlayerRelationsHelper.ts index bc6c66f..c96b1f6 100644 --- a/mods/Fika Server/user/mods/fika-server/src/helpers/FikaPlayerRelationsHelper.ts +++ b/mods/Fika Server/user/mods/fika-server/src/helpers/FikaPlayerRelationsHelper.ts @@ -1,10 +1,10 @@ import { inject, injectable } from "tsyringe"; -import { FikaPlayerRelationsCacheService } from "../services/cache/FikaPlayerRelationsCacheService"; +import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { SaveServer } from "@spt/servers/SaveServer"; import { SptWebSocketConnectionHandler } from "@spt/servers/ws/SptWebSocketConnectionHandler"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; +import { FikaPlayerRelationsCacheService } from "../services/cache/FikaPlayerRelationsCacheService"; @injectable() export class FikaPlayerRelationsHelper { @@ -90,7 +90,7 @@ export class FikaPlayerRelationsHelper { this.logger.info(`removeFriend: ${fromProfileId}->${toProfileId}`); const profile = this.saveServer.getProfile(fromProfileId); - this.webSocketHandler.sendMessage(toProfileId, { + this.webSocketHandler.sendMessageAsync(toProfileId, { type: "youAreRemovedFromFriendList", eventId: "youAreRemovedFromFriendList", profile: { @@ -125,7 +125,7 @@ export class FikaPlayerRelationsHelper { this.fikaPlayerRelationsCacheService.storeValue(fromProfileId, playerRelations); let profile: ISptProfile = this.saveServer.getProfile(fromProfileId); - this.webSocketHandler.sendMessage(toProfileId, { + this.webSocketHandler.sendMessageAsync(toProfileId, { type: "youAreAddToIgnoreList", eventId: "youAreAddToIgnoreList", _id: fromProfileId, @@ -161,7 +161,7 @@ export class FikaPlayerRelationsHelper { this.fikaPlayerRelationsCacheService.storeValue(fromProfileId, playerRelations); let profile: ISptProfile = this.saveServer.getProfile(fromProfileId); - this.webSocketHandler.sendMessage(toProfileId, { + this.webSocketHandler.sendMessageAsync(toProfileId, { type: "youAreRemoveFromIgnoreList", eventId: "youAreRemoveFromIgnoreList", _id: fromProfileId, diff --git a/mods/Fika Server/user/mods/fika-server/src/mod.ts b/mods/Fika Server/user/mods/fika-server/src/mod.ts index 21c781c..3d2b713 100644 --- a/mods/Fika Server/user/mods/fika-server/src/mod.ts +++ b/mods/Fika Server/user/mods/fika-server/src/mod.ts @@ -1,7 +1,7 @@ import { DependencyContainer } from "tsyringe"; -import { IPreSptLoadModAsync } from "@spt/models/external/IPreSptLoadModAsync"; import { IPostSptLoadModAsync } from "@spt/models/external/IPostSptLoadModAsync"; +import { IPreSptLoadModAsync } from "@spt/models/external/IPreSptLoadModAsync"; import { Fika } from "./Fika"; import { Container } from "./di/Container"; diff --git a/mods/Fika Server/user/mods/fika-server/src/models/eft/dialog/IWsProfile.ts b/mods/Fika Server/user/mods/fika-server/src/models/eft/dialog/IWsProfile.ts deleted file mode 100644 index 2286f57..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/eft/dialog/IWsProfile.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { MemberCategory } from "@spt/models/enums/MemberCategory"; - -export interface IWsProfile { - type: string; - eventId: string; - _id?: string; - profile: { - _id: string; - aid: number; - Info: { - Nickname: string; - Side: string; - Level: number; - MemberCategory: MemberCategory; - }; - }; -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/enums/EEFTNotificationIconType.ts b/mods/Fika Server/user/mods/fika-server/src/models/enums/EEFTNotificationIconType.ts index e8a0877..196896b 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/enums/EEFTNotificationIconType.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/enums/EEFTNotificationIconType.ts @@ -13,5 +13,5 @@ export enum EEFTNotificationIconType { WishlistHideout = 11, WishlistTrading = 12, WishlistEquipment = 13, - WishlistOther = 14 + WishlistOther = 14, } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaHeadlessWSMessageTypes.ts b/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaHeadlessWSMessageTypes.ts new file mode 100644 index 0000000..296fd4d --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaHeadlessWSMessageTypes.ts @@ -0,0 +1,5 @@ +export enum EFikaHeadlessWSMessageTypes { + KeepAlive = 0, + HeadlessStartRaid = 1, + RequesterJoinMatch = 2, +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaNotifications.ts b/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaNotifications.ts index 74ed80f..691fdd9 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaNotifications.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/enums/EFikaNotifications.ts @@ -2,5 +2,5 @@ export enum EFikaNotifications { KeepAlive = 0, StartedRaid = 1, SentItem = 2, - PushNotification = 3 + PushNotification = 3, } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/enums/EDedicatedStatus.ts b/mods/Fika Server/user/mods/fika-server/src/models/enums/EHeadlessStatus.ts similarity index 52% rename from mods/Fika Server/user/mods/fika-server/src/models/enums/EDedicatedStatus.ts rename to mods/Fika Server/user/mods/fika-server/src/models/enums/EHeadlessStatus.ts index cf94e59..ddb63e7 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/enums/EDedicatedStatus.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/enums/EHeadlessStatus.ts @@ -1,4 +1,4 @@ -export enum EDedicatedStatus { +export enum EHeadlessStatus { READY = 1, IN_RAID = 2, } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/IFikaMatch.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/IFikaMatch.ts index ea9ed3b..d7c4959 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/IFikaMatch.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/IFikaMatch.ts @@ -11,7 +11,7 @@ export interface IFikaMatch { port: number; hostUsername: string; timestamp: string; - fikaVersion: string; + crc32: number; gameVersion: string; raidConfig: IGetRaidConfigurationRequestData; locationData: ILocationBase; @@ -22,6 +22,6 @@ export interface IFikaMatch { time: EFikaTime; raidCode: string; natPunch: boolean; - isDedicated: boolean; + isHeadless: boolean; raids: number; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfig.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfig.ts index 2ac8d31..9796a24 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfig.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfig.ts @@ -1,13 +1,13 @@ -import { IFikaConfigClient } from "./IFikaConfigClient"; -import { IFikaConfigServer } from "./IFikaConfigServer"; -import { IFikaConfigNatPunchServer } from "./IFikaConfigNatPunchServer"; -import { IFikaConfigDedicated } from "./IFikaConfigDedicated"; import { IFikaConfigBackground } from "./IFikaConfigBackground"; +import { IFikaConfigClient } from "./IFikaConfigClient"; +import { IFikaConfigHeadless } from "./IFikaConfigHeadless"; +import { IFikaConfigNatPunchServer } from "./IFikaConfigNatPunchServer"; +import { IFikaConfigServer } from "./IFikaConfigServer"; export interface IFikaConfig { client: IFikaConfigClient; server: IFikaConfigServer; natPunchServer: IFikaConfigNatPunchServer; - dedicated: IFikaConfigDedicated; - background: IFikaConfigBackground + headless: IFikaConfigHeadless; + background: IFikaConfigBackground; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigClient.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigClient.ts index 6cbf858..a1682c8 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigClient.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigClient.ts @@ -4,8 +4,7 @@ export interface IFikaConfigClient { dynamicVExfils: boolean; allowFreeCam: boolean; allowSpectateFreeCam: boolean; - allowItemSending: boolean; - blacklistedItems: string[], + blacklistedItems: string[]; forceSaveOnDeath: boolean; mods: { required: string[]; @@ -15,4 +14,5 @@ export interface IFikaConfigClient { sharedQuestProgression: boolean; canEditRaidSettings: boolean; enableTransits: boolean; + anyoneCanStartRaid: boolean; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigDedicated.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigDedicated.ts deleted file mode 100644 index 9784282..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigDedicated.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface IFikaConfigDedicated { - profiles: { - amount: number; - } - scripts: { - generate: boolean; - forceIp: string; - } -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigHeadless.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigHeadless.ts new file mode 100644 index 0000000..b7b385f --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigHeadless.ts @@ -0,0 +1,13 @@ +export interface IFikaConfigHeadless { + profiles: { + amount: number; + aliases: Record; + }; + scripts: { + generate: boolean; + forceIp: string; + }; + /** If this is true, sets the headless's average level to that of the entire lobby, if set to false it will take the level of the requester. */ + setLevelToAverageOfLobby: boolean; + restartAfterAmountOfRaids: number; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigServer.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigServer.ts index 7a78d2a..efde041 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigServer.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/config/IFikaConfigServer.ts @@ -1,7 +1,22 @@ export interface IFikaConfigServer { - giftedItemsLoseFIR: boolean; + SPT: IFikaSPTServerConfig; + allowItemSending: boolean; + sentItemsLoseFIR: boolean; launcherListAllProfiles: boolean; sessionTimeout: number; showDevProfile: boolean; showNonStandardProfile: boolean; + logClientModsInConsole: boolean; +} + +export interface IFikaSPTServerConfig { + http: IFikaSPTHttpServerConfig; + disableSPTChatBots: boolean; +} + +export interface IFikaSPTHttpServerConfig { + ip: string; + port: number; + backendIp: string; + backendPort: number; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/dedicated/IDedicatedClientInfo.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/dedicated/IDedicatedClientInfo.ts deleted file mode 100644 index 0577069..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/dedicated/IDedicatedClientInfo.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { EDedicatedStatus } from "../../enums/EDedicatedStatus"; - -export interface IDedicatedClientInfo { - state: EDedicatedStatus; - lastPing: number; -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessAvailableClients.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessAvailableClients.ts new file mode 100644 index 0000000..c29138b --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessAvailableClients.ts @@ -0,0 +1,6 @@ +export interface IHeadlessAvailableClients { + /** SessionID of the headless client */ + headlessSessionID: string; + /** The alias of the headless client, if it has any assigned. If it doesn't have any assigned uses the nickname */ + alias: string; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessClientInfo.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessClientInfo.ts new file mode 100644 index 0000000..341f6c9 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessClientInfo.ts @@ -0,0 +1,15 @@ +import { SPTWebSocket } from "@spt/servers/ws/SPTWebsocket"; +import { EHeadlessStatus } from "../../enums/EHeadlessStatus"; + +export interface IHeadlessClientInfo { + /** Websocket of the headless client */ + webSocket: SPTWebSocket; + /** State of the headless client */ + state: EHeadlessStatus; + /** The players that are playing on this headless client, only set if the state is IN_RAID */ + players?: string[]; + /** SessionID of the person who has requested the headless client, it will only ever be set if the status is IN_RAID */ + requesterSessionID?: string; + /** Allows for checking if the requester has been notified the match has started through the requester WebSocket so he can auto-join */ + hasNotifiedRequester?: boolean; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessClients.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessClients.ts new file mode 100644 index 0000000..ddb8509 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessClients.ts @@ -0,0 +1,17 @@ +import { EHeadlessStatus } from "../../enums/EHeadlessStatus"; + +/** A copy of IHeadlessClientInfo but for /fika/headless/get */ +export interface IHeadlessClient { + /** State of the headless client */ + state: EHeadlessStatus; + /** The players that are playing on this headless client, only set if the state is IN_RAID */ + players?: string[]; + /** SessionID of the person who has requested the headless client, it will only ever be set if the status is IN_RAID */ + requesterSessionID?: string; + /** Allows for checking if the requester has been notified the match has started through the requester WebSocket so he can auto-join */ + hasNotifiedRequester?: boolean; +} + +export interface IHeadlessClients { + headlesses: Record; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessRestartAfterAmountOfRaids.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessRestartAfterAmountOfRaids.ts new file mode 100644 index 0000000..2f568f0 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/headless/IHeadlessRestartAfterAmountOfRaids.ts @@ -0,0 +1,3 @@ +export interface IHeadlessRestartAfterAmountOfRaids { + amount: number; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaPlayerPresence.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaPlayerPresence.ts index 887b17f..62b160c 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaPlayerPresence.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaPlayerPresence.ts @@ -7,4 +7,4 @@ export interface IFikaPlayerPresence { activity: EFikaPlayerPresences; activityStartedTimestamp: number; raidInformation?: IFikaRaidPresence; -} \ No newline at end of file +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaRaidPresence.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaRaidPresence.ts index 7d9bd3c..450af0b 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaRaidPresence.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaRaidPresence.ts @@ -5,4 +5,4 @@ export interface IFikaRaidPresence { location: string; side: EFikaSide; time: EFikaTime; -} \ No newline at end of file +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaSetPresence.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaSetPresence.ts index 27629fe..9a70688 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaSetPresence.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/presence/IFikaSetPresence.ts @@ -1,7 +1,7 @@ import { EFikaPlayerPresences } from "../../enums/EFikaPlayerPresences"; import { IFikaRaidPresence } from "./IFikaRaidPresence"; -export interface IFikaSetPresence{ +export interface IFikaSetPresence { activity: EFikaPlayerPresences; raidInformation?: IFikaRaidPresence; -} \ No newline at end of file +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/location/IFikaRaidsResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/location/IFikaRaidsResponse.ts index d520af7..57b7a50 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/location/IFikaRaidsResponse.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/location/IFikaRaidsResponse.ts @@ -11,6 +11,8 @@ export interface IFikaRaidResponse { side: EFikaSide; time: EFikaTime; players: Record; + isHeadless: boolean; + headlessRequesterNickname: string; } export type IFikaRaidsResponse = IFikaRaidResponse[]; diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/create/IFikaRaidCreateRequestData.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/create/IFikaRaidCreateRequestData.ts index 1aa9906..8a62269 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/create/IFikaRaidCreateRequestData.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/create/IFikaRaidCreateRequestData.ts @@ -9,7 +9,7 @@ export interface IFikaRaidCreateRequestData { timestamp: string; settings: IGetRaidConfigurationRequestData; gameVersion: string; - fikaVersion: string; + crc32: number; side: EFikaSide; time: EFikaTime; isSpectator: boolean; diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts deleted file mode 100644 index c037bd3..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IGetStatusDedicatedResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IGetStatusDedicatedResponse { - available: boolean; -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts deleted file mode 100644 index 3645ead..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStartDedicatedResponse.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface IStartDedicatedResponse { - matchId: string; - error: string; -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts deleted file mode 100644 index ea18455..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStatusDedicatedRequest.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { EDedicatedStatus } from "../../../../enums/EDedicatedStatus"; - -export interface IStatusDedicatedRequest { - sessionId: string; - status: EDedicatedStatus; -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts deleted file mode 100644 index aa4a09d..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStatusDedicatedResponse.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { EDedicatedStatus } from "../../../../enums/EDedicatedStatus"; - -export interface IStatusDedicatedResponse { - sessionId: string; - status: EDedicatedStatus; -} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts index 3841dff..49bf103 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/gethost/IFikaRaidGethostResponse.ts @@ -2,5 +2,5 @@ export interface IFikaRaidGethostResponse { ips: string[]; port: number; natPunch: boolean; - isDedicated: boolean; + isHeadless: boolean; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/getsettings/IFikaRaidSettingsResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/getsettings/IFikaRaidSettingsResponse.ts index dd53303..54058e9 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/getsettings/IFikaRaidSettingsResponse.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/getsettings/IFikaRaidSettingsResponse.ts @@ -5,5 +5,5 @@ export interface IFikaRaidSettingsResponse { metabolismDisabled: boolean; playersSpawnPlace: PlayersSpawnPlace; hourOfDay: number; - timeFlowType: TimeFlowType + timeFlowType: TimeFlowType; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/headless/IStartHeadlessRequest.ts similarity index 88% rename from mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts rename to mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/headless/IStartHeadlessRequest.ts index a46ca05..508b454 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/dedicated/IStartDedicatedRequest.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/headless/IStartHeadlessRequest.ts @@ -3,7 +3,8 @@ import { DateTime } from "@spt/models/enums/DateTime"; import { PlayersSpawnPlace } from "@spt/models/enums/PlayersSpawnPlace"; import { SideType } from "@spt/models/enums/SideType"; -export interface IStartDedicatedRequest { +export interface IStartHeadlessRequest { + headlessSessionID: string; time: DateTime; locationId: string; spawnPlace: PlayersSpawnPlace; diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/headless/IStartHeadlessResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/headless/IStartHeadlessResponse.ts new file mode 100644 index 0000000..d9386b5 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/headless/IStartHeadlessResponse.ts @@ -0,0 +1,4 @@ +export interface IStartHeadlessResponse { + matchId: string; + error: string; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/join/IFikaRaidJoinResponse.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/join/IFikaRaidJoinResponse.ts index b093f8b..9da594d 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/join/IFikaRaidJoinResponse.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/raid/join/IFikaRaidJoinResponse.ts @@ -2,6 +2,6 @@ export interface IFikaRaidJoinResponse { serverId: string; timestamp: string; gameVersion: string; - fikaVersion: string; + crc32: number; raidCode: string; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts index 2ab47f9..9ed357d 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/routes/update/IFikaUpdateSethostRequestData.ts @@ -3,5 +3,5 @@ export interface IFikaUpdateSethostRequestData { ips: string[]; port: number; natPunch: boolean; - isDedicated: boolean; + isHeadless: boolean; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/IFikaHeadlessBase.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/IFikaHeadlessBase.ts new file mode 100644 index 0000000..f509864 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/IFikaHeadlessBase.ts @@ -0,0 +1,5 @@ +import { EFikaHeadlessWSMessageTypes } from "../../enums/EFikaHeadlessWSMessageTypes"; + +export interface IFikaHeadlessBase { + type: EFikaHeadlessWSMessageTypes; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/headless/IHeadlessRequesterJoinRaid.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/headless/IHeadlessRequesterJoinRaid.ts new file mode 100644 index 0000000..1cce2bb --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/headless/IHeadlessRequesterJoinRaid.ts @@ -0,0 +1,5 @@ +import { IFikaHeadlessBase } from "../IFikaHeadlessBase"; + +export interface IHeadlessRequesterJoinRaid extends IFikaHeadlessBase { + matchId: string; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/headless/IHeadlessStartRaid.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/headless/IHeadlessStartRaid.ts new file mode 100644 index 0000000..e436cec --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/headless/IHeadlessStartRaid.ts @@ -0,0 +1,6 @@ +import { IStartHeadlessRequest } from "../../routes/raid/headless/IStartHeadlessRequest"; +import { IFikaHeadlessBase } from "../IFikaHeadlessBase"; + +export interface IStartHeadlessRaid extends IFikaHeadlessBase { + startRequest: IStartHeadlessRequest; +} diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IPushNotification.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IPushNotification.ts index b470206..d28d23d 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IPushNotification.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IPushNotification.ts @@ -1,7 +1,7 @@ -import { IFikaNotificationBase } from "../IFikaNotificationBase"; import { EEFTNotificationIconType } from "../../../enums/EEFTNotificationIconType"; +import { IFikaNotificationBase } from "../IFikaNotificationBase"; export interface IPushNotification extends IFikaNotificationBase { - notificationIcon : EEFTNotificationIconType; + notificationIcon: EEFTNotificationIconType; notification: string; } diff --git a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IStartRaidNotification.ts b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IStartRaidNotification.ts index ef39b5d..b508968 100644 --- a/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IStartRaidNotification.ts +++ b/mods/Fika Server/user/mods/fika-server/src/models/fika/websocket/notifications/IStartRaidNotification.ts @@ -3,4 +3,7 @@ import { IFikaNotificationBase } from "../IFikaNotificationBase"; export interface IStartRaidNotification extends IFikaNotificationBase { nickname: string; location: string; + isHeadlessRaid: boolean; + headlessRequesterName: string; + raidTime: number; } diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/callbacks/ClientLogCallbacks.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/callbacks/ClientLogCallbacks.ts new file mode 100644 index 0000000..1c8f04e --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/overrides/callbacks/ClientLogCallbacks.ts @@ -0,0 +1,37 @@ +import { ClientLogCallbacks } from "@spt/callbacks/ClientLogCallbacks"; +import { ClientLogController } from "@spt/controllers/ClientLogController"; +import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData"; +import { IClientLogRequest } from "@spt/models/spt/logging/IClientLogRequest"; +import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; +import { DependencyContainer, inject, injectable } from "tsyringe"; +import { Override } from "../../di/Override"; +import { FikaHeadlessHelper } from "../../helpers/FikaHeadlessHelper"; + +@injectable() +export class ClientLogCallbacksOverride extends Override { + constructor( + @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil, + @inject("FikaHeadlessHelper") protected fikaHeadlessHelper: FikaHeadlessHelper, + @inject("ClientLogController") protected clientLogController: ClientLogController, + ) { + super(); + } + + public execute(container: DependencyContainer): void { + container.afterResolution( + "ClientLogCallbacks", + (_t, result: ClientLogCallbacks) => { + result.clientLog = (url: string, info: IClientLogRequest, sessionID: string): INullResponseData => { + if (this.fikaHeadlessHelper.isHeadlessClient(sessionID)) { + this.clientLogController.clientLog(info); + + return this.httpResponseUtil.nullResponse(); + } + + return ClientLogCallbacks.prototype.clientLog.call(result, url, info, sessionID); + }; + }, + { frequency: "Always" }, + ); + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/DialogueController.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/DialogueController.ts index 33a7f31..cf2cda9 100644 --- a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/DialogueController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/DialogueController.ts @@ -3,9 +3,9 @@ import { DependencyContainer, inject, injectable } from "tsyringe"; import { DialogueController } from "@spt/controllers/DialogueController"; import { IGetFriendListDataResponse } from "@spt/models/eft/dialog/IGetFriendListDataResponse"; +import { ISendMessageRequest } from "@spt/models/eft/dialog/ISendMessageRequest"; import { FikaDialogueController } from "../../controllers/FikaDialogueController"; import { Override } from "../../di/Override"; -import { ISendMessageRequest } from "@spt/models/eft/dialog/ISendMessageRequest"; @injectable() export class DialogueControllerOverride extends Override { diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/LauncherController.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/LauncherController.ts deleted file mode 100644 index 230c949..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/LauncherController.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { DependencyContainer, inject, injectable } from "tsyringe"; - -import { LauncherController } from "@spt/controllers/LauncherController"; -import { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; -import { DatabaseService } from "@spt/services/DatabaseService"; -import { ConfigServer } from "@spt/servers/ConfigServer"; -import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; -import { IConnectResponse } from "@spt/models/eft/profile/IConnectResponse"; - -import { FikaConfig } from "../../utils/FikaConfig"; -import { Override } from "../../di/Override"; -import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; - -@injectable() -export class LauncherControllerOverride extends Override { - protected coreConfig: ICoreConfig; - - constructor( - @inject("LauncherController") protected launcherController: LauncherController, - @inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper, - @inject("DatabaseService") protected databaseService: DatabaseService, - @inject("ConfigServer") protected configServer: ConfigServer, - @inject("FikaConfig") protected fikaConfig: FikaConfig, - ) { - super(); - - this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); - } - - public execute(container: DependencyContainer): void { - container.afterResolution( - "LauncherController", - (_t, result: LauncherController) => { - result.connect = (): IConnectResponse => { - let editions = this.databaseService.getProfiles(); - - if (!this.fikaConfig.getConfig().server.showDevProfile) { - // biome-ignore lint/performance/noDelete: Only ran once. - delete editions["SPT Developer"]; - } - - if (!this.fikaConfig.getConfig().server.showNonStandardProfile) { - for (const id of ["Tournament", "SPT Easy start", "SPT Zero to hero"]) { - delete editions[id]; - } - } - - // Stop TS from throwing a tantrum over protected methods - const launchController = this.launcherController as any; - - return { - backendUrl: this.httpServerHelper.getBackendUrl(), - name: this.coreConfig.serverName, - editions: Object.keys(editions), - profileDescriptions: launchController.getProfileDescriptions(), - }; - }; - }, - { frequency: "Always" }, - ); - } -} diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/MatchController.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/MatchController.ts deleted file mode 100644 index 3f685c8..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/MatchController.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { DependencyContainer, inject, injectable } from "tsyringe"; -import { MatchController } from "@spt/controllers/MatchController"; -import { Override } from "../../di/Override"; -import { FikaInsuranceService } from "../../services/FikaInsuranceService"; -import { FikaMatchService } from "../../services/FikaMatchService"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { IEndLocalRaidRequestData } from "@spt/models/eft/match/IEndLocalRaidRequestData"; - -@injectable() -export class MatchControllerOverride extends Override { - constructor( - @inject("FikaInsuranceService") protected fikaInsuranceService: FikaInsuranceService, - @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, - @inject("WinstonLogger") protected logger: ILogger, - ) { - super(); - } - - public execute(container: DependencyContainer): void { - container.afterResolution( - "MatchController", - (_t, result: MatchController) => { - result.endLocalRaid = (sessionId: string, request: IEndLocalRaidRequestData) => { - this.fikaInsuranceService.onEndLocalRaidRequest(sessionId, this.fikaInsuranceService.getMatchId(sessionId), request); - }; - }, - { frequency: "Always" }, - ); - } -} diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/ProfileController.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/ProfileController.ts index deb9353..8199755 100644 --- a/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/ProfileController.ts +++ b/mods/Fika Server/user/mods/fika-server/src/overrides/controllers/ProfileController.ts @@ -3,8 +3,6 @@ import { DependencyContainer, inject, injectable } from "tsyringe"; import { ProfileController } from "@spt/controllers/ProfileController"; import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { IMiniProfile } from "@spt/models/eft/launcher/IMiniProfile"; -import { IGetOtherProfileRequest } from "@spt/models/eft/profile/IGetOtherProfileRequest"; -import { IGetOtherProfileResponse } from "@spt/models/eft/profile/IGetOtherProfileResponse"; import { ISearchFriendRequestData } from "@spt/models/eft/profile/ISearchFriendRequestData"; import { ISearchFriendResponse } from "@spt/models/eft/profile/ISearchFriendResponse"; @@ -40,7 +38,7 @@ export class ProfileControllerOverride extends Override { const matches: ISearchFriendResponse[] = []; for (const profile of Object.values(profiles)) { - if (profile.info?.password === "fika-dedicated") continue; + if (profile.info?.password === "fika-headless") continue; if (profile.characters?.pmc?.Info) { if (profile.characters.pmc.Info.Nickname.toLowerCase().startsWith(searchNicknameLowerCase)) { @@ -52,6 +50,7 @@ export class ProfileControllerOverride extends Override { Side: profile.characters.pmc.Info.Side, Level: profile.characters.pmc.Info.Level, MemberCategory: profile.characters.pmc.Info.MemberCategory, + SelectedMemberCategory: profile.characters.pmc.Info.SelectedMemberCategory, }, }); } @@ -60,63 +59,6 @@ export class ProfileControllerOverride extends Override { return matches; }; - - result.getOtherProfile = (sessionId: string, request: IGetOtherProfileRequest): IGetOtherProfileResponse => { - const profiles = this.profileHelper.getProfiles(); - - // default to player profile - let profileId = sessionId; - - for (const profile of Object.values(profiles)) { - if (profile.characters.pmc.aid === Number(request.accountId)) { - profileId = profile.characters.pmc._id; - break; - } - } - - const player = this.profileHelper.getFullProfile(profileId); - const playerPmc = player.characters.pmc; - const playerScav = player.characters.scav; - - return { - id: playerPmc._id, - aid: playerPmc.aid, - info: { - nickname: playerPmc.Info.Nickname, - side: playerPmc.Info.Side, - experience: playerPmc.Info.Experience, - memberCategory: playerPmc.Info.MemberCategory, - bannedState: playerPmc.Info.BannedState, - bannedUntil: playerPmc.Info.BannedUntil, - registrationDate: playerPmc.Info.RegistrationDate, - }, - customization: { - head: playerPmc.Customization.Head, - body: playerPmc.Customization.Body, - feet: playerPmc.Customization.Feet, - hands: playerPmc.Customization.Hands, - }, - skills: playerPmc.Skills, - equipment: { - Id: playerPmc.Inventory.equipment, - Items: playerPmc.Inventory.items, - }, - achievements: playerPmc.Achievements, - favoriteItems: playerPmc.Inventory.favoriteItems ?? [], - pmcStats: { - eft: { - totalInGameTime: playerPmc.Stats.Eft.TotalInGameTime, - overAllCounters: playerPmc.Stats.Eft.OverallCounters, - }, - }, - scavStats: { - eft: { - totalInGameTime: playerScav.Stats.Eft.TotalInGameTime, - overAllCounters: playerScav.Stats.Eft.OverallCounters, - }, - }, - }; - }; }, { frequency: "Always" }, ); diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/routers/HttpRouter.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/routers/HttpRouter.ts index fc7932a..0fd1f67 100644 --- a/mods/Fika Server/user/mods/fika-server/src/overrides/routers/HttpRouter.ts +++ b/mods/Fika Server/user/mods/fika-server/src/overrides/routers/HttpRouter.ts @@ -6,8 +6,10 @@ import { HttpRouter } from "@spt/routers/HttpRouter"; import { Override } from "../../di/Override"; -// Thanks to DrakiaXYZ for this implementation - +/** + * This override is essential for setups where the backendIp or NAT port mapping differs from the SPT server’s backend port. + * Without it, SPT constructs an incorrect backend URL, causing connection issues (e.g., using 0.0.0.0 instead of the correct address). + */ @injectable() export class HttpRouterOverride extends Override { constructor(@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper) { diff --git a/mods/Fika Server/user/mods/fika-server/src/overrides/services/LocationLifecycleService.ts b/mods/Fika Server/user/mods/fika-server/src/overrides/services/LocationLifecycleService.ts index 0d1b39e..12a4d16 100644 --- a/mods/Fika Server/user/mods/fika-server/src/overrides/services/LocationLifecycleService.ts +++ b/mods/Fika Server/user/mods/fika-server/src/overrides/services/LocationLifecycleService.ts @@ -2,37 +2,32 @@ import { DependencyContainer, inject, injectable } from "tsyringe"; import { ApplicationContext } from "@spt/context/ApplicationContext"; import { ContextVariableType } from "@spt/context/ContextVariableType"; -import { LocationController } from "@spt/controllers/LocationController"; import { ProfileHelper } from "@spt/helpers/ProfileHelper"; import { ILocationBase } from "@spt/models/eft/common/ILocationBase"; import { IEndLocalRaidRequestData, ILocationTransit } from "@spt/models/eft/match/IEndLocalRaidRequestData"; import { IStartLocalRaidRequestData } from "@spt/models/eft/match/IStartLocalRaidRequestData"; import { IStartLocalRaidResponseData } from "@spt/models/eft/match/IStartLocalRaidResponseData"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { BotGenerationCacheService } from "@spt/services/BotGenerationCacheService"; -import { BotLootCacheService } from "@spt/services/BotLootCacheService"; import { DatabaseService } from "@spt/services/DatabaseService"; import { LocationLifecycleService } from "@spt/services/LocationLifecycleService"; -import { HttpResponseUtil } from "@spt/utils/HttpResponseUtil"; import { TimeUtil } from "@spt/utils/TimeUtil"; +import { TransitionType } from "@spt/models/enums/TransitionType"; import { Override } from "../../di/Override"; +import { FikaInsuranceService } from "../../services/FikaInsuranceService"; import { FikaMatchService } from "../../services/FikaMatchService"; @injectable() export class LocationLifecycleServiceOverride extends Override { constructor( + @inject("FikaInsuranceService") protected fikaInsuranceService: FikaInsuranceService, @inject("DatabaseService") protected databaseService: DatabaseService, @inject("ProfileHelper") protected profileHelper: ProfileHelper, - @inject("LocationController") protected locationController: LocationController, - @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil, @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, @inject("LocationLifecycleService") protected locationLifecycleService: LocationLifecycleService, - @inject("BotGenerationCacheService") protected botGenerationCacheService: BotGenerationCacheService, @inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("TimeUtil") protected timeUtil: TimeUtil, - @inject("BotLootCacheService") protected botLootCacheService: BotLootCacheService, - @inject("PrimaryLogger") protected logger: ILogger, ) { super(); } @@ -70,12 +65,12 @@ export class LocationLifecycleServiceOverride extends Override { profile: { insuredItems: playerProfile.InsuredItems }, locationLoot: locationLoot, transition: { - isLocationTransition: false, + transitionType: TransitionType.Common, transitionRaidId: "66f5750951530ca5ae09876d", transitionCount: 0, visitedLocations: [], }, - }; + } as IStartLocalRaidResponseData; // Only has value when transitioning into map from previous one if (request.transition) { @@ -83,11 +78,8 @@ export class LocationLifecycleServiceOverride extends Override { } // Get data stored at end of previous raid (if any) - const transitionData = this.applicationContext - .getLatestValue(ContextVariableType.TRANSIT_INFO) - ?.getValue(); + const transitionData = this.applicationContext.getLatestValue(ContextVariableType.TRANSIT_INFO)?.getValue(); if (transitionData) { - result.transition.isLocationTransition = true; result.transition.transitionRaidId = transitionData.transitionRaidId; result.transition.transitionCount += 1; result.transition.visitedLocations.push(transitionData.sptLastVisitedLocation); @@ -115,11 +107,6 @@ export class LocationLifecycleServiceOverride extends Override { // Get match id from player session id const matchId = this.fikaMatchService.getMatchIdByPlayer(sessionId); - if (sessionId == matchId) { - // Clear bot loot cache only if host ended raid - this.botLootCacheService.clearCache(); - } - // Find player that exited the raid const player = this.fikaMatchService.getPlayerInMatch(matchId, sessionId); @@ -129,8 +116,11 @@ export class LocationLifecycleServiceOverride extends Override { } } + this.fikaInsuranceService.onEndLocalRaidRequest(sessionId, this.fikaInsuranceService.getMatchId(sessionId), request); + // Execute the original method if not a spectator if (!isSpectator) { + //Prototype callback because apparently setting a original callback before overriding doesn't allow some stuff to work. LocationLifecycleService.prototype.endLocalRaid.call(result, sessionId, request); } }; diff --git a/mods/Fika Server/user/mods/fika-server/src/routers/item_events/FikaItemEventRouter.ts b/mods/Fika Server/user/mods/fika-server/src/routers/item_events/FikaItemEventRouter.ts index 2a1eac1..269632f 100644 --- a/mods/Fika Server/user/mods/fika-server/src/routers/item_events/FikaItemEventRouter.ts +++ b/mods/Fika Server/user/mods/fika-server/src/routers/item_events/FikaItemEventRouter.ts @@ -19,7 +19,7 @@ export class FikaItemEventRouter extends ItemEventRouterDefinition { public override async handleItemEvent(url: string, pmcData: IPmcData, body: any, sessionID: string): Promise { switch (url) { case "SendToPlayer": - return this.fikaSendItemCallbacks.handleSendItem(pmcData, body, sessionID); + return await this.fikaSendItemCallbacks.handleSendItem(pmcData, body, sessionID); } } } diff --git a/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaHeadlessStaticRouter.ts b/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaHeadlessStaticRouter.ts new file mode 100644 index 0000000..67fabda --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaHeadlessStaticRouter.ts @@ -0,0 +1,27 @@ +import { inject, injectable } from "tsyringe"; + +import { RouteAction, StaticRouter } from "@spt/di/Router"; + +import { IQuest } from "@spt/models/eft/common/tables/IQuest"; +import { IGetBodyResponseData } from "@spt/models/eft/httpResponse/IGetBodyResponseData"; +import { FikaHeadlessCallbacks } from "../../callbacks/FikaHeadlessCallbacks"; + +@injectable() +export class FikaHeadlessStaticRouter extends StaticRouter { + constructor(@inject("FikaHeadlessCallbacks") protected fikaHeadlessCallbacks: FikaHeadlessCallbacks) { + super([ + new RouteAction("/fika/headless/get", async (url: string, info: any, sessionID: string, _output: string): Promise => { + return this.fikaHeadlessCallbacks.handleGetHeadlesses(url, info, sessionID); + }), + new RouteAction("/fika/headless/available", async (url: string, info: any, sessionID: string, _output: string): Promise => { + return this.fikaHeadlessCallbacks.handleAvailableHeadlesses(url, info, sessionID); + }), + new RouteAction("/fika/headless/restartafterraidamount", async (url: string, info: any, sessionID: string, _output: string): Promise => { + return this.fikaHeadlessCallbacks.handleRestartAfterRaidAmount(url, info, sessionID); + }), + new RouteAction("/fika/headless/questtemplates", async (url: string, info: any, sessionID: string, _output: string): Promise> => { + return this.fikaHeadlessCallbacks.handleGetAllQuestTemplates(url, info, sessionID); + }), + ]); + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaNotificationStaticRouter.ts b/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaNotificationStaticRouter.ts index 5af2fee..ca1b7d3 100644 --- a/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaNotificationStaticRouter.ts +++ b/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaNotificationStaticRouter.ts @@ -10,7 +10,7 @@ export class FikaNotificationStaticRouter extends StaticRouter { constructor(@inject("FikaNotificationCallbacks") protected fikaNotificationCallbacks: FikaNotificationCallbacks) { super([ new RouteAction("/fika/notification/push", async (url: string, info: IPushNotification, sessionID: string, _output: string): Promise => { - return this.fikaNotificationCallbacks.handlePushNotification(url, info, sessionID); + return await this.fikaNotificationCallbacks.handlePushNotification(url, info, sessionID); }), ]); } diff --git a/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaRaidStaticRouter.ts b/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaRaidStaticRouter.ts index ab29e50..62082e3 100644 --- a/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaRaidStaticRouter.ts +++ b/mods/Fika Server/user/mods/fika-server/src/routers/static/FikaRaidStaticRouter.ts @@ -3,14 +3,13 @@ import { inject, injectable } from "tsyringe"; import { RouteAction, StaticRouter } from "@spt/di/Router"; import { INullResponseData } from "@spt/models/eft/httpResponse/INullResponseData"; +import { IRegisterPlayerRequestData } from "@spt/models/eft/inRaid/IRegisterPlayerRequestData"; import { FikaRaidCallbacks } from "../../callbacks/FikaRaidCallbacks"; import { IFikaRaidServerIdRequestData } from "../../models/fika/routes/raid/IFikaRaidServerIdRequestData"; import { IFikaRaidCreateRequestData } from "../../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; +import { IStartHeadlessRequest } from "../../models/fika/routes/raid/headless/IStartHeadlessRequest"; import { IFikaRaidJoinRequestData } from "../../models/fika/routes/raid/join/IFikaRaidJoinRequestData"; import { IFikaRaidLeaveRequestData } from "../../models/fika/routes/raid/leave/IFikaRaidLeaveRequestData"; -import { IStartDedicatedRequest } from "../../models/fika/routes/raid/dedicated/IStartDedicatedRequest"; -import { IStatusDedicatedRequest } from "../../models/fika/routes/raid/dedicated/IStatusDedicatedRequest"; -import { IRegisterPlayerRequestData } from "@spt/models/eft/inRaid/IRegisterPlayerRequestData"; @injectable() export class FikaRaidStaticRouter extends StaticRouter { @@ -31,14 +30,8 @@ export class FikaRaidStaticRouter extends StaticRouter { new RouteAction("/fika/raid/getsettings", async (url: string, info: IFikaRaidServerIdRequestData, sessionID: string, _output: string): Promise => { return this.fikaRaidCallbacks.handleRaidGetSettings(url, info, sessionID); }), - new RouteAction("/fika/raid/dedicated/start", async (url: string, info: IStartDedicatedRequest, sessionID: string, _output: string): Promise => { - return this.fikaRaidCallbacks.handleRaidStartDedicated(url, info, sessionID); - }), - new RouteAction("/fika/raid/dedicated/status", async (url: string, info: IStatusDedicatedRequest, sessionID: string, _output: string): Promise => { - return this.fikaRaidCallbacks.handleRaidStatusDedicated(url, info, sessionID); - }), - new RouteAction("/fika/raid/dedicated/getstatus", async (url: string, info: any, sessionID: string, _output: string): Promise => { - return this.fikaRaidCallbacks.handleRaidGetStatusDedicated(url, info, sessionID); + new RouteAction("/fika/raid/headless/start", async (url: string, info: IStartHeadlessRequest, sessionID: string, _output: string): Promise => { + return this.fikaRaidCallbacks.handleRaidStartHeadless(url, info, sessionID); }), new RouteAction("/fika/raid/registerPlayer", async (url: string, info: IRegisterPlayerRequestData, sessionID: string, _output: string): Promise => { return this.fikaRaidCallbacks.handleRaidRegisterPlayer(url, info, sessionID); diff --git a/mods/Fika Server/user/mods/fika-server/src/services/FikaClientService.ts b/mods/Fika Server/user/mods/fika-server/src/services/FikaClientService.ts new file mode 100644 index 0000000..53e8cb8 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/services/FikaClientService.ts @@ -0,0 +1,129 @@ +import { inject, injectable } from "tsyringe"; + +import { IFikaConfigClient } from "../models/fika/config/IFikaConfigClient"; + +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { SaveServer } from "@spt/servers/SaveServer"; + +import { FikaClientModHashesHelper } from "../helpers/FikaClientModHashesHelper"; +import { IFikaConfigHeadless } from "../models/fika/config/IFikaConfigHeadless"; +import { IFikaConfigNatPunchServer } from "../models/fika/config/IFikaConfigNatPunchServer"; +import { IFikaCheckModRequestData } from "../models/fika/routes/client/check/IFikaCheckModRequestData"; +import { IFikaCheckModResponse, IVersionCheckResponse } from "../models/fika/routes/client/check/IFikaCheckModResponse"; +import { FikaConfig } from "../utils/FikaConfig"; + +@injectable() +export class FikaClientService { + protected requiredMods: string[] = []; + protected allowedMods: string[] = []; + protected hasRequiredOrOptionalMods: boolean = true; + + constructor( + @inject("FikaClientModHashesHelper") protected fikaClientModHashesHelper: FikaClientModHashesHelper, + @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("SaveServer") protected saveServer: SaveServer, + @inject("WinstonLogger") protected logger: ILogger, + ) {} + + public async preInit(): Promise { + const config = this.fikaConfig.getConfig(); + + const sanitizedRequiredMods = this.filterEmptyMods(config.client.mods.required); + const sanitizedOptionalMods = this.filterEmptyMods(config.client.mods.optional); + + if (sanitizedRequiredMods.length === 0 && sanitizedOptionalMods.length === 0) { + this.hasRequiredOrOptionalMods = false; + } + + this.requiredMods = [...sanitizedRequiredMods, "com.fika.core", "com.SPT.custom", "com.SPT.singleplayer", "com.SPT.core", "com.SPT.debugging"]; + this.allowedMods = [...this.requiredMods, ...sanitizedOptionalMods, "com.bepis.bepinex.configurationmanager", "com.fika.headless"]; + } + + protected filterEmptyMods(array: string[]): string[] { + return array.filter((str) => str.trim() !== ""); + } + + public getClientConfig(): IFikaConfigClient { + return this.fikaConfig.getConfig().client; + } + + public getIsItemSendingAllowed(): boolean { + return this.fikaConfig.getConfig().server.allowItemSending; + } + + public getNatPunchServerConfig(): IFikaConfigNatPunchServer { + return this.fikaConfig.getConfig().natPunchServer; + } + + public getHeadlessConfig(): IFikaConfigHeadless { + return this.fikaConfig.getConfig().headless; + } + + public getVersion(): IVersionCheckResponse { + const version = this.fikaConfig.getVersion(); + return { version }; + } + + public getCheckModsResponse(request: IFikaCheckModRequestData, sessionID: string): IFikaCheckModResponse { + const mismatchedMods: IFikaCheckModResponse = { + forbidden: [], + missingRequired: [], + hashMismatch: [], + }; + + if (this.fikaConfig.getConfig().server.logClientModsInConsole) { + const username = this.saveServer.getProfile(sessionID).info.username; + const mods = Object.keys(request); + + this.logger.info(`${username} (${sessionID}) connected with ${mods.length} client mods: ${mods.join(", ")}`); + } + + // if no configuration was made, allow all mods + if (!this.hasRequiredOrOptionalMods) { + return mismatchedMods; + } + + //check for missing required mods first + for (const pluginId of this.requiredMods) { + if (!request[pluginId]) { + mismatchedMods.missingRequired.push(pluginId); + } + } + + // no need to check anything else since it's missing required mods + if (mismatchedMods.missingRequired.length > 0) { + return mismatchedMods; + } + + for (const [pluginId, hash] of Object.entries(request)) { + // check if the mod isn't allowed + if (!this.allowedMods.includes(pluginId)) { + mismatchedMods.forbidden.push(pluginId); + continue; + } + + // first request made will fill in at the very least all the required mods hashes, following requests made by different clients will add any optional mod not added by the first request, otherwise will check against the first request data + if (!this.fikaClientModHashesHelper.exists(pluginId)) { + this.fikaClientModHashesHelper.addHash(pluginId, hash); + continue; + } + + if (this.fikaClientModHashesHelper.getHash(pluginId) !== hash) { + mismatchedMods.hashMismatch.push(pluginId); + } + } + + return mismatchedMods; + } + + public getProfileBySessionID(sessionID: string): any { + const profile = this.saveServer.getProfile(sessionID); + if (profile) { + this.logger.info(`${sessionID} has downloaded their profile`); + return profile; + } + + this.logger.error(`${sessionID} wants to download their profile but we don't have it`); + return null; + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/services/FikaInsuranceService.ts b/mods/Fika Server/user/mods/fika-server/src/services/FikaInsuranceService.ts index 0b7eba8..9913311 100644 --- a/mods/Fika Server/user/mods/fika-server/src/services/FikaInsuranceService.ts +++ b/mods/Fika Server/user/mods/fika-server/src/services/FikaInsuranceService.ts @@ -1,7 +1,6 @@ +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { inject, injectable } from "tsyringe"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { MatchController } from "@spt/controllers/MatchController"; import { ItemHelper } from "@spt/helpers/ItemHelper"; import { IEndLocalRaidRequestData } from "@spt/models/eft/match/IEndLocalRaidRequestData"; import { BaseClasses } from "@spt/models/enums/BaseClasses"; @@ -16,7 +15,6 @@ export class FikaInsuranceService { constructor( @inject("SaveServer") protected saveServer: SaveServer, @inject("ItemHelper") protected itemHelper: ItemHelper, - @inject("MatchController") protected matchController: MatchController, @inject("WinstonLogger") protected logger: ILogger, ) { this.matchInsuranceInfo = {}; @@ -42,7 +40,7 @@ export class FikaInsuranceService { endedRaid: false, lostItems: [], foundItems: [], - inventory: [], + inventory: [], }; this.matchInsuranceInfo[matchId].push(player); @@ -51,9 +49,6 @@ export class FikaInsuranceService { public onEndLocalRaidRequest(sessionID: string, matchId: string, endLocalRaidRequest: IEndLocalRaidRequestData): void { if (!(matchId in this.matchInsuranceInfo)) { this.logger.error("[Fika Insurance] onEndLocalRaidRequest: matchId not found!"); - - // Pass back to SPT so that the player can save. - MatchController.prototype.endLocalRaid.call(this.matchController, sessionID, endLocalRaidRequest); return; } @@ -69,9 +64,6 @@ export class FikaInsuranceService { player.inventory = endLocalRaidRequest.results.profile.Inventory.items.map((i) => i._id); player.endedRaid = true; } - - // Pass back to SPT so that the player can save. - MatchController.prototype.endLocalRaid.call(this.matchController, sessionID, endLocalRaidRequest); } public onMatchEnd(matchId: string): void { diff --git a/mods/Fika Server/user/mods/fika-server/src/services/FikaMatchService.ts b/mods/Fika Server/user/mods/fika-server/src/services/FikaMatchService.ts index 1af1b2d..35dbcea 100644 --- a/mods/Fika Server/user/mods/fika-server/src/services/FikaMatchService.ts +++ b/mods/Fika Server/user/mods/fika-server/src/services/FikaMatchService.ts @@ -1,6 +1,6 @@ import { inject, injectable } from "tsyringe"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { SaveServer } from "@spt/servers/SaveServer"; import { LocationLifecycleService } from "@spt/services/LocationLifecycleService"; @@ -11,10 +11,11 @@ import { IFikaMatch } from "../models/fika/IFikaMatch"; import { IFikaPlayer } from "../models/fika/IFikaPlayer"; import { IFikaRaidCreateRequestData } from "../models/fika/routes/raid/create/IFikaRaidCreateRequestData"; +import { FikaHeadlessHelper } from "../helpers/FikaHeadlessHelper"; import { FikaConfig } from "../utils/FikaConfig"; import { FikaInsuranceService } from "./FikaInsuranceService"; import { FikaPresenceService } from "./FikaPresenceService"; -import { FikaDedicatedRaidService } from "./dedicated/FikaDedicatedRaidService"; +import { FikaHeadlessService } from "./headless/FikaHeadlessService"; @injectable() export class FikaMatchService { @@ -26,7 +27,8 @@ export class FikaMatchService { @inject("LocationLifecycleService") protected locationLifecycleService: LocationLifecycleService, @inject("SaveServer") protected saveServer: SaveServer, @inject("FikaConfig") protected fikaConfig: FikaConfig, - @inject("FikaDedicatedRaidService") protected fikaDedicatedRaidService: FikaDedicatedRaidService, + @inject("FikaHeadlessHelper") protected fikaHeadlessHelper: FikaHeadlessHelper, + @inject("FikaHeadlessService") protected fikaHeadlessService: FikaHeadlessService, @inject("FikaInsuranceService") protected fikaInsuranceService: FikaInsuranceService, @inject("FikaPresenceService") protected fikaPresenceService: FikaPresenceService, ) { @@ -97,15 +99,7 @@ export class FikaMatchService { } /** - * Returns all match ids - * @returns - */ - public getAllMatchIds(): string[] { - return Array.from(this.matches.keys()); - } - - /** - * Returns the player with the given id in the given match, undefined if either match or player does not exist + * Returns the player with the given id in the given match, returns undefined if either match or player does not exist * @param matchId * @param playerId * @returns @@ -123,28 +117,12 @@ export class FikaMatchService { } /** - * Returns an array with all playerIds in the given match, undefined if match does not exist - * - * Note: - * - host player is the one where playerId is equal to matchId - * @param matchId - * @returns - */ - public getPlayersIdsByMatch(matchId: string): string[] { - if (!this.matches.has(matchId)) { - return; - } - - return Array.from(this.matches.get(matchId).players.keys()); - } - - /** - * Returns the match id that has a player with the given player id, undefined if the player isn't in a match + * Returns the match id that has a player with the given player id, returns undefined if the player isn't in a match * * @param playerId * @returns */ - public getMatchIdByPlayer(playerId: string): string { + public getMatchIdByPlayer(playerId: string): string | undefined { for (const [key, value] of this.matches.entries()) { if (value.players.has(playerId)) { return key; @@ -155,14 +133,14 @@ export class FikaMatchService { } /** - * Returns the match id that has a player with the given session id, undefined if the player isn't in a match + * Returns the match id that has a player with the given session id, returns undefined if the player isn't in a match * * Note: * - First tries to find pmc, then scav * @param sessionId * @returns */ - public getMatchIdByProfile(sessionId: string): string { + public getMatchIdByProfile(sessionId: string): string | undefined { const profile = this.saveServer.getProfile(sessionId); // check if pmc is in match @@ -200,20 +178,24 @@ export class FikaMatchService { locationData: locationData, status: EFikaMatchStatus.LOADING, timeout: 0, - players: new Map(), + players: new Map(), gameVersion: data.gameVersion, - fikaVersion: data.fikaVersion, + crc32: data.crc32, side: data.side, time: data.time, raidCode: data.raidCode, natPunch: false, - isDedicated: false, + isHeadless: false, raids: 0, }); this.addTimeoutInterval(data.serverId); - this.addPlayerToMatch(data.serverId, data.serverId, { groupId: null, isDead: false, isSpectator: data.isSpectator }); + this.addPlayerToMatch(data.serverId, data.serverId, { + groupId: null, + isDead: false, + isSpectator: data.isSpectator, + }); return this.matches.has(data.serverId) && this.timeoutIntervals.has(data.serverId); } @@ -240,8 +222,8 @@ export class FikaMatchService { public endMatch(matchId: string, reason: EFikaMatchEndSessionMessage): void { this.logger.info(`Coop session ${matchId} has ended: ${reason}`); - if (this.fikaDedicatedRaidService.requestedSessions.hasOwnProperty(matchId)) { - delete this.fikaDedicatedRaidService.requestedSessions[matchId]; + if (this.fikaHeadlessHelper.isHeadlessClient(matchId)) { + this.fikaHeadlessService.endHeadlessRaid(matchId); } this.fikaInsuranceService.onMatchEnd(matchId); @@ -253,15 +235,15 @@ export class FikaMatchService { * @param matchId * @param status */ - public setMatchStatus(matchId: string, status: EFikaMatchStatus): void { + public async setMatchStatus(matchId: string, status: EFikaMatchStatus): Promise { if (!this.matches.has(matchId)) { return; } this.matches.get(matchId).status = status; - if (status.toString() == "COMPLETE") { - this.fikaDedicatedRaidService.handleRequestedSessions(matchId); + if (status === EFikaMatchStatus.COMPLETE) { + await this.fikaHeadlessService.sendJoinMessageToRequester(matchId); } } @@ -271,7 +253,7 @@ export class FikaMatchService { * @param ips * @param port */ - public setMatchHost(matchId: string, ips: string[], port: number, natPunch: boolean, isDedicated: boolean): void { + public setMatchHost(matchId: string, ips: string[], port: number, natPunch: boolean, isHeadless: boolean): void { if (!this.matches.has(matchId)) { return; } @@ -281,7 +263,7 @@ export class FikaMatchService { match.ips = ips; match.port = port; match.natPunch = natPunch; - match.isDedicated = isDedicated; + match.isHeadless = isHeadless; } /** @@ -312,9 +294,11 @@ export class FikaMatchService { this.fikaInsuranceService.addPlayerToMatchId(matchId, playerId); - this.fikaPresenceService.updatePlayerPresence(playerId, this.fikaPresenceService.generateSetPresence( - EFikaPlayerPresences.IN_RAID, - this.fikaPresenceService.generateRaidPresence(match.locationData.Id, match.side, match.time))); + if (this.fikaHeadlessHelper.isHeadlessClient(matchId)) { + this.fikaHeadlessService.addPlayerToHeadlessMatch(matchId, playerId); + } + + this.fikaPresenceService.updatePlayerPresence(playerId, this.fikaPresenceService.generateSetPresence(EFikaPlayerPresences.IN_RAID, this.fikaPresenceService.generateRaidPresence(match.locationData.Id, match.side, match.time))); } /** diff --git a/mods/Fika Server/user/mods/fika-server/src/services/FikaPresenceService.ts b/mods/Fika Server/user/mods/fika-server/src/services/FikaPresenceService.ts index dcef92b..88d9bde 100644 --- a/mods/Fika Server/user/mods/fika-server/src/services/FikaPresenceService.ts +++ b/mods/Fika Server/user/mods/fika-server/src/services/FikaPresenceService.ts @@ -1,15 +1,15 @@ import { inject, injectable } from "tsyringe"; -import { TimeUtil } from "@spt/utils/TimeUtil"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { SaveServer } from "@spt/servers/SaveServer"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { TimeUtil } from "@spt/utils/TimeUtil"; import { EFikaPlayerPresences } from "../models/enums/EFikaPlayerPresences"; +import { EFikaSide } from "../models/enums/EFikaSide"; +import { EFikaTime } from "../models/enums/EFikaTime"; import { IFikaPlayerPresence } from "../models/fika/presence/IFikaPlayerPresence"; import { IFikaRaidPresence } from "../models/fika/presence/IFikaRaidPresence"; import { IFikaSetPresence } from "../models/fika/presence/IFikaSetPresence"; -import { EFikaSide } from "../models/enums/EFikaSide"; -import { EFikaTime } from "../models/enums/EFikaTime"; @injectable() export class FikaPresenceService { diff --git a/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaFriendRequestsCacheService.ts b/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaFriendRequestsCacheService.ts index 0927bd9..932f34f 100644 --- a/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaFriendRequestsCacheService.ts +++ b/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaFriendRequestsCacheService.ts @@ -1,7 +1,7 @@ import { inject, injectable } from "tsyringe"; +import { FileSystemSync } from "@spt/utils/FileSystemSync"; import { JsonUtil } from "@spt/utils/JsonUtil"; -import { VFS } from "@spt/utils/VFS"; import { IFikaFriendRequests } from "../../models/fika/IFikaFriendRequests"; import { FikaConfig } from "../../utils/FikaConfig"; @@ -14,16 +14,16 @@ export class FikaFriendRequestsCacheService { constructor( @inject("JsonUtil") protected jsonUtil: JsonUtil, - @inject("VFS") protected vfs: VFS, + @inject("FileSystemSync") protected fileSystemSync: FileSystemSync, @inject("FikaConfig") protected fikaConfig: FikaConfig, ) { this.friendRequestsFullPath = `./${this.fikaConfig.getModPath()}${this.friendRequestsPath}`; - if (!this.vfs.exists(this.friendRequestsFullPath)) { - this.vfs.writeFile(this.friendRequestsFullPath, "[]"); + if (!this.fileSystemSync.exists(this.friendRequestsFullPath)) { + this.fileSystemSync.write(this.friendRequestsFullPath, "[]"); } - this.friendRequests = this.jsonUtil.deserialize(this.vfs.readFile(this.friendRequestsFullPath), this.friendRequestsFullPath); + this.friendRequests = this.jsonUtil.deserialize(this.fileSystemSync.read(this.friendRequestsFullPath), this.friendRequestsFullPath); } public getAllFriendRequests(): IFikaFriendRequests[] { @@ -50,12 +50,12 @@ export class FikaFriendRequestsCacheService { this.friendRequests.splice(index, 1); - this.vfs.writeFile(this.friendRequestsFullPath, this.jsonUtil.serialize(this.friendRequests)); + this.fileSystemSync.write(this.friendRequestsFullPath, this.jsonUtil.serialize(this.friendRequests)); } public storeFriendRequest(value: IFikaFriendRequests): void { this.friendRequests.push(value); - this.vfs.writeFile(this.friendRequestsFullPath, this.jsonUtil.serialize(this.friendRequests)); + this.fileSystemSync.write(this.friendRequestsFullPath, this.jsonUtil.serialize(this.friendRequests)); } } diff --git a/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaPlayerRelationsCacheService.ts b/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaPlayerRelationsCacheService.ts index cd4699e..c7eea5f 100644 --- a/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaPlayerRelationsCacheService.ts +++ b/mods/Fika Server/user/mods/fika-server/src/services/cache/FikaPlayerRelationsCacheService.ts @@ -1,9 +1,9 @@ import { inject, injectable } from "tsyringe"; import { ProfileHelper } from "@spt/helpers/ProfileHelper"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { FileSystemSync } from "@spt/utils/FileSystemSync"; import { JsonUtil } from "@spt/utils/JsonUtil"; -import { VFS } from "@spt/utils/VFS"; import { IFikaPlayerRelations } from "../../models/fika/IFikaPlayerRelations"; import { FikaConfig } from "../../utils/FikaConfig"; @@ -18,16 +18,16 @@ export class FikaPlayerRelationsCacheService { @inject("WinstonLogger") protected logger: ILogger, @inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("JsonUtil") protected jsonUtil: JsonUtil, - @inject("VFS") protected vfs: VFS, + @inject("FileSystemSync") protected fileSystemSync: FileSystemSync, @inject("FikaConfig") protected fikaConfig: FikaConfig, ) { this.playerRelationsFullPath = `./${this.fikaConfig.getModPath()}${this.playerRelationsPath}`; - if (!this.vfs.exists(this.playerRelationsFullPath)) { - this.vfs.writeFile(this.playerRelationsFullPath, "{}"); + if (!this.fileSystemSync.exists(this.playerRelationsFullPath)) { + this.fileSystemSync.write(this.playerRelationsFullPath, "{}"); } - this.playerRelations = this.jsonUtil.deserialize(this.vfs.readFile(this.playerRelationsFullPath), this.playerRelationsFullPath); + this.playerRelations = this.jsonUtil.deserialize(this.fileSystemSync.read(this.playerRelationsFullPath), this.playerRelationsFullPath); } public postInit() { @@ -73,7 +73,7 @@ export class FikaPlayerRelationsCacheService { } if (shouldSave) { - this.vfs.writeFile(this.playerRelationsFullPath, this.jsonUtil.serialize(this.playerRelations)); + this.fileSystemSync.write(this.playerRelationsFullPath, this.jsonUtil.serialize(this.playerRelations)); } } @@ -95,6 +95,6 @@ export class FikaPlayerRelationsCacheService { public storeValue(key: string, value: IFikaPlayerRelations): void { this.playerRelations[key] = value; - this.vfs.writeFile(this.playerRelationsFullPath, this.jsonUtil.serialize(this.playerRelations)); + this.fileSystemSync.write(this.playerRelationsFullPath, this.jsonUtil.serialize(this.playerRelations)); } } diff --git a/mods/Fika Server/user/mods/fika-server/src/services/dedicated/FikaDedicatedProfileService.ts b/mods/Fika Server/user/mods/fika-server/src/services/dedicated/FikaDedicatedProfileService.ts deleted file mode 100644 index 65f6ae1..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/services/dedicated/FikaDedicatedProfileService.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { inject, injectable } from "tsyringe"; -import { LauncherController } from "@spt/controllers/LauncherController"; -import { SaveServer } from "@spt/servers/SaveServer"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { TimeUtil } from "@spt/utils/TimeUtil"; -import { RandomUtil } from "@spt/utils/RandomUtil"; -import { HashUtil } from "@spt/utils/HashUtil"; -import { Info, ISptProfile } from "@spt/models/eft/profile/ISptProfile"; -import { ProfileController } from "@spt/controllers/ProfileController"; -import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData"; -import { IFikaConfigDedicated } from "../../models/fika/config/IFikaConfigDedicated"; -import { FikaConfig } from "../../utils/FikaConfig"; -import path from "path"; -import fs from "fs"; -import { ConfigServer } from "@spt/servers/ConfigServer"; -import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; -import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; - -@injectable() -export class FikaDedicatedProfileService { - readonly scriptsPath = path.join(__dirname, "../../../assets/scripts"); - readonly HEAD_USEC_4 = "5fdb4139e4ed5b5ea251e4ed"; // _parent: 5cc085e214c02e000c6bea67 - readonly VOICE_USEC_4 = "6284d6a28e4092597733b7a6"; // _parent: 5fc100cf95572123ae738483 - - protected httpConfig: IHttpConfig; - protected dedicatedConfig: IFikaConfigDedicated; - public dedicatedProfiles: ISptProfile[] = []; - - constructor( - @inject("LauncherController") protected launcherController: LauncherController, - @inject("SaveServer") protected saveServer: SaveServer, - @inject("WinstonLogger") protected logger: ILogger, - @inject("TimeUtil") protected timeUtil: TimeUtil, - @inject("RandomUtil") protected randomUtil: RandomUtil, - @inject("HashUtil") protected hashUtil: HashUtil, - @inject("ProfileController") protected profileController: ProfileController, - @inject("FikaConfig") protected fikaConfig: FikaConfig, - @inject("ConfigServer") protected configServer: ConfigServer, - ) { - this.dedicatedConfig = fikaConfig.getConfig().dedicated; - this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); - } - - public init() { - this.dedicatedProfiles = this.loadDedicatedProfiles(); - - this.logger.info(`Found ${this.dedicatedProfiles.length} dedicated client profiles.`); - - const profileAmount = this.dedicatedConfig.profiles.amount; - - if (this.dedicatedProfiles.length < profileAmount) { - const createdProfiles = this.createDedicatedProfiles(profileAmount); - - this.logger.success(`Created ${createdProfiles.length} dedicated client profiles!`); - - if (this.dedicatedConfig.scripts.generate) { - let ip = this.httpConfig.ip; - const port = this.httpConfig.port; - - const forceIp = this.dedicatedConfig.scripts.forceIp; - - if (forceIp != "") { - ip = forceIp; - } - - if (ip == "0.0.0.0") { - ip = "127.0.0.1"; - } - - const backendUrl = `http://${ip}:${port}`; - - for (const profile of createdProfiles) { - this.generateLaunchScript(profile, backendUrl, this.scriptsPath); - } - } - } - } - - public loadDedicatedProfiles(): ISptProfile[] { - let profiles: ISptProfile[] = []; - - for (const profileId in this.saveServer.getProfiles()) { - const profile = this.saveServer.getProfile(profileId); - - if (profile.info.password == "fika-dedicated") { - profiles.push(profile); - } - } - - return profiles; - } - - public createDedicatedProfiles(profileAmount: number): ISptProfile[] { - let profileCount = this.dedicatedProfiles.length; - let profileAmountToCreate = profileAmount - profileCount; - let createdProfiles: ISptProfile[] = []; - - for (let i = 0; i < profileAmountToCreate; i++) { - const profile = this.createDedicatedProfile(); - createdProfiles.push(profile); - } - - return createdProfiles; - } - - public createDedicatedProfile(): ISptProfile { - // Generate a unique username - const username = `dedicated_${this.generateUniqueId()}`; - // Using a password allows us to know which profiles are dedicated client profiles. - const password = "fika-dedicated"; - // Random edition. Doesn't matter - const edition = "Edge Of Darkness"; - - // Create mini profile - const profileId = this.createMiniProfile(username, password, edition); - - // Random character configs. Doesn't matter. - const newProfileData: IProfileCreateRequestData = { - side: "usec", - nickname: username, // Use the username as the nickname to ensure it is unique. - headId: this.HEAD_USEC_4, - voiceId: this.VOICE_USEC_4, - }; - - const profile = this.createFullProfile(newProfileData, profileId); - - return profile; - } - - public createMiniProfile(username: string, password: string, edition: string): string { - const profileId = this.generateUniqueId(); - const scavId = this.generateUniqueId(); - - const newProfileDetails: Info = { - id: profileId, - scavId: scavId, - aid: this.hashUtil.generateAccountId(), - username: username, - password: password, - wipe: true, - edition: edition, - }; - - this.saveServer.createProfile(newProfileDetails); - - this.saveServer.loadProfile(profileId); - this.saveServer.saveProfile(profileId); - - return profileId; - } - - public createFullProfile(profileData: IProfileCreateRequestData, profileId: string) { - this.profileController.createProfile(profileData, profileId); - - const profile = this.saveServer.getProfile(profileId); - - return profile; - } - - public generateLaunchScript(profile: ISptProfile, backendUrl: string, targetFolderPath: string) { - const scriptName = `Start_${profile.info.username}.bat`; - const scriptPath = path.join(targetFolderPath, scriptName); - const scriptContent = `@echo off -if NOT EXIST ".\\BepInEx\\plugins\\Fika.Dedicated.dll" ( - echo Could not find 'Fika.Dedicated.dll', please install the dedicated plugin before starting the client. - pause -) else ( - start "" EscapeFromTarkov.exe -token=${profile.info.id} -config={"BackendUrl":"${backendUrl}","Version":"live"} -batchmode -nographics --enable-console true & exit -)`; - - try { - if (!fs.existsSync(targetFolderPath)) { - fs.mkdirSync(targetFolderPath); - } - - fs.writeFileSync(scriptPath, scriptContent); - - this.logger.success(`Generated launch script: /fika-server/assets/scripts/${scriptName}`); - } catch (error) { - this.logger.error(`Failed to generate launch script: ${error}`); - } - } - - // generateProfileId - protected generateUniqueId(): string { - const timestamp = this.timeUtil.getTimestamp(); - - return this.formatID(timestamp, timestamp * this.randomUtil.getInt(1, 1000000)); - } - - protected formatID(timeStamp: number, counter: number): string { - const timeStampStr = timeStamp.toString(16).padStart(8, "0"); - const counterStr = counter.toString(16).padStart(16, "0"); - - return timeStampStr.toLowerCase() + counterStr.toLowerCase(); - } -} diff --git a/mods/Fika Server/user/mods/fika-server/src/services/dedicated/FikaDedicatedRaidService.ts b/mods/Fika Server/user/mods/fika-server/src/services/dedicated/FikaDedicatedRaidService.ts deleted file mode 100644 index 6b4f28d..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/services/dedicated/FikaDedicatedRaidService.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { inject, injectable } from "tsyringe"; - -import { ILogger } from "@spt/models/spt/utils/ILogger"; - -import { IDedicatedClientInfo } from "../../models/fika/dedicated/IDedicatedClientInfo"; -import { FikaDedicatedRaidWebSocket } from "../../websockets/FikaDedicatedRaidWebSocket"; - -@injectable() -export class FikaDedicatedRaidService { - public dedicatedClients: Record; - public requestedSessions: Record; - public onNoDedicatedClientAvailable?: () => void; - public onDedicatedClientAvailable?: () => void; - public onDedicatedClientResponse?: (sessionID: string) => void; - - constructor( - @inject("FikaDedicatedRaidWebSocket") protected fikaDedicatedRaidWebSocket: FikaDedicatedRaidWebSocket, - @inject("WinstonLogger") protected logger: ILogger, - ) { - this.dedicatedClients = {}; - this.requestedSessions = {}; - - // TODO: find a more elegant solution to keep track of dedicated clients being available. - setInterval(() => { - const currentTime = Date.now(); - - for (const dedicatedClientSessionId in this.dedicatedClients) { - const dedicatedClientLastPing = this.dedicatedClients[dedicatedClientSessionId].lastPing; - - if (currentTime - dedicatedClientLastPing > 16000) { - delete this.dedicatedClients[dedicatedClientSessionId]; - logger.info(`Dedicated client removed: ${dedicatedClientSessionId}`); - } - - if (!this.isDedicatedClientAvailable()) { - if (this.onNoDedicatedClientAvailable) { - this.onNoDedicatedClientAvailable(); - } - } - } - }, 5000); - } - - public handleRequestedSessions(matchId: string): void { - if (matchId in this.requestedSessions) { - const userToJoin = this.requestedSessions[matchId]; - - this.fikaDedicatedRaidWebSocket.clientWebSockets[userToJoin].send( - JSON.stringify({ - type: "fikaDedicatedJoinMatch", - matchId: matchId, - }), - ); - - this.logger.info(`Told ${userToJoin} to join raid ${matchId}`); - } - } - - public isDedicatedClientAvailable(): boolean { - return Object.keys(this.dedicatedClients).length > 0; - } -} diff --git a/mods/Fika Server/user/mods/fika-server/src/services/headless/FikaHeadlessProfileService.ts b/mods/Fika Server/user/mods/fika-server/src/services/headless/FikaHeadlessProfileService.ts new file mode 100644 index 0000000..fe2746f --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/services/headless/FikaHeadlessProfileService.ts @@ -0,0 +1,205 @@ +import path from "path"; + +import { ProfileController } from "@spt/controllers/ProfileController"; +import { IProfileCreateRequestData } from "@spt/models/eft/profile/IProfileCreateRequestData"; +import { ISptProfile, Info } from "@spt/models/eft/profile/ISptProfile"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { SaveServer } from "@spt/servers/SaveServer"; +import { FileSystem } from "@spt/utils/FileSystem"; +import { HashUtil } from "@spt/utils/HashUtil"; + +import { InventoryHelper } from "@spt/helpers/InventoryHelper"; +import { IPmcData } from "@spt/models/eft/common/IPmcData"; +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { inject, injectable } from "tsyringe"; +import { FikaConfig } from "../../utils/FikaConfig"; + +@injectable() +export class FikaHeadlessProfileService { + readonly scriptsPath = path.join(__dirname, "../../../assets/scripts"); + readonly HEAD_USEC_4 = "5fdb4139e4ed5b5ea251e4ed"; // _parent: 5cc085e214c02e000c6bea67 + readonly VOICE_USEC_4 = "6284d6a28e4092597733b7a6"; // _parent: 5fc100cf95572123ae738483 + + protected httpConfig: IHttpConfig; + private headlessProfiles: ISptProfile[] = []; + + constructor( + @inject("SaveServer") protected saveServer: SaveServer, + @inject("WinstonLogger") protected logger: ILogger, + @inject("HashUtil") protected hashUtil: HashUtil, + @inject("ProfileController") protected profileController: ProfileController, + @inject("FikaConfig") protected fikaConfig: FikaConfig, + @inject("ConfigServer") protected configServer: ConfigServer, + @inject("InventoryHelper") protected inventoryHelper: InventoryHelper, + @inject("FileSystem") protected fileSystem: FileSystem, + ) { + this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); + } + + public getHeadlessProfiles(): ISptProfile[] { + return this.headlessProfiles; + } + + public async init(): Promise { + const headlessConfig = this.fikaConfig.getConfig().headless; + + this.headlessProfiles = this.loadHeadlessProfiles(); + + this.logger.info(`Found ${this.headlessProfiles.length} headless client profiles.`); + + const profileAmount = headlessConfig.profiles.amount; + + if (this.headlessProfiles.length < profileAmount) { + const createdProfiles = await this.createHeadlessProfiles(profileAmount); + + this.logger.success(`Created ${createdProfiles.length} headless client profiles!`); + + if (headlessConfig.scripts.generate) { + let ip = this.httpConfig.ip; + const port = this.httpConfig.port; + + const forceIp = headlessConfig.scripts.forceIp; + + if (forceIp != "") { + ip = forceIp; + } + + if (ip == "0.0.0.0") { + ip = "127.0.0.1"; + } + + const backendUrl = `https://${ip}:${port}`; + + for (const profile of createdProfiles) { + await this.generateLaunchScript(profile.info.id, backendUrl, this.scriptsPath); + } + } + } + } + + private loadHeadlessProfiles(): ISptProfile[] { + let profiles: ISptProfile[] = []; + + for (const profileId in this.saveServer.getProfiles()) { + const profile = this.saveServer.getProfile(profileId); + + if (profile.info.password === "fika-headless") { + profiles.push(profile); + } + } + + return profiles; + } + + public async createHeadlessProfiles(profileAmount: number): Promise { + let profileCount = this.headlessProfiles.length; + let profileAmountToCreate = profileAmount - profileCount; + let createdProfiles: ISptProfile[] = []; + + for (let i = 0; i < profileAmountToCreate; i++) { + const profile = await this.createHeadlessProfile(); + createdProfiles.push(profile); + this.headlessProfiles.push(profile); + } + + return createdProfiles; + } + + private async createHeadlessProfile(): Promise { + // Generate a unique username + const username = `headless_${this.hashUtil.generate()}`; + // Using a password allows us to know which profiles are headless client profiles. + const password = "fika-headless"; + // Random edition. Doesn't matter + const edition = "Standard"; + + // Create mini profile + const profileId = await this.createMiniProfile(username, password, edition); + + // Random character configs. Doesn't matter. + const newProfileData: IProfileCreateRequestData = { + side: "usec", + nickname: username, // Use the username as the nickname to ensure it is unique. + headId: this.HEAD_USEC_4, + voiceId: this.VOICE_USEC_4, + }; + + const profile = await this.createFullProfile(newProfileData, profileId); + + return profile; + } + + private async createMiniProfile(username: string, password: string, edition: string): Promise { + const profileId = this.hashUtil.generate(); + const scavId = this.hashUtil.generate(); + + const newProfileDetails: Info = { + id: profileId, + scavId: scavId, + aid: this.hashUtil.generateAccountId(), + username: username, + password: password, + wipe: true, + edition: edition, + }; + + this.saveServer.createProfile(newProfileDetails); + + await this.saveServer.loadProfile(profileId); + await this.saveServer.saveProfile(profileId); + + return profileId; + } + + private async createFullProfile(profileData: IProfileCreateRequestData, profileId: string): Promise { + await this.profileController.createProfile(profileData, profileId); + + const profile = this.saveServer.getProfile(profileId); + + this.clearUnecessaryHeadlessItems(profile.characters.pmc, profileId); + + return profile; + } + + private async generateLaunchScript(profileId: string, backendUrl: string, scriptsFolderPath: string): Promise { + try { + if (!(await this.fileSystem.exists(scriptsFolderPath))) { + await this.fileSystem.ensureDir(scriptsFolderPath); + } + + const templatePath = path.join(scriptsFolderPath, "_TEMPLATE.ps1"); + const templateContent = await this.fileSystem.read(templatePath); + + const scriptName = `Start_headless_${profileId}.ps1`; + const scriptPath = path.join(scriptsFolderPath, scriptName); + const scriptContent = templateContent.replace("${profileId}", profileId).replace("${backendUrl}", backendUrl); + + await this.fileSystem.write(scriptPath, scriptContent); + + this.logger.success(`Generated launch script: /fika-server/assets/scripts/${scriptName}`); + } catch (error) { + this.logger.error(`Failed to generate launch script: ${error}`); + } + } + + private clearUnecessaryHeadlessItems(pmcProfile: IPmcData, sessionId: string): void { + const itemsToDelete = this.getAllHeadlessItems(pmcProfile).map((item) => item._id); + + for (const itemIdToDelete of itemsToDelete) { + this.inventoryHelper.removeItem(pmcProfile, itemIdToDelete, sessionId); + } + + pmcProfile.Inventory.fastPanel = {}; + } + + private getAllHeadlessItems(pmcProfile: IPmcData): IItem[] { + const inventoryItems = pmcProfile.Inventory.items ?? []; + const equipmentRootId = pmcProfile?.Inventory?.equipment; + const stashRootId = pmcProfile?.Inventory.stash; + + return inventoryItems.filter((item) => item.parentId == equipmentRootId || item.parentId == stashRootId); + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/services/headless/FikaHeadlessService.ts b/mods/Fika Server/user/mods/fika-server/src/services/headless/FikaHeadlessService.ts new file mode 100644 index 0000000..487a978 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/services/headless/FikaHeadlessService.ts @@ -0,0 +1,157 @@ +import { inject, injectable } from "tsyringe"; + +import type { ILogger } from "@spt/models/spt/utils/ILogger"; + +import { SaveServer } from "@spt/servers/SaveServer"; +import { SPTWebSocket } from "@spt/servers/ws/SPTWebsocket"; +import { EFikaHeadlessWSMessageTypes } from "../../models/enums/EFikaHeadlessWSMessageTypes"; +import { EHeadlessStatus } from "../../models/enums/EHeadlessStatus"; +import { IHeadlessClientInfo } from "../../models/fika/headless/IHeadlessClientInfo"; +import { IStartHeadlessRequest } from "../../models/fika/routes/raid/headless/IStartHeadlessRequest"; +import { IHeadlessRequesterJoinRaid } from "../../models/fika/websocket/headless/IHeadlessRequesterJoinRaid"; +import { IStartHeadlessRaid } from "../../models/fika/websocket/headless/IHeadlessStartRaid"; +import { FikaConfig } from "../../utils/FikaConfig"; +import { FikaHeadlessRequesterWebSocket } from "../../websockets/FikaHeadlessRequesterWebSocket"; + +@injectable() +export class FikaHeadlessService { + private headlessClients: Map = new Map(); + + constructor( + @inject("FikaHeadlessRequesterWebSocket") protected fikaHeadlessRequesterWebSocket: FikaHeadlessRequesterWebSocket, + @inject("SaveServer") protected saveServer: SaveServer, + @inject("WinstonLogger") protected logger: ILogger, + @inject("FikaConfig") protected fikaConfig: FikaConfig, + ) {} + + public getHeadlessClients(): Map { + return this.headlessClients; + } + + public addHeadlessClient(sessionID: string, webSocket: SPTWebSocket): void { + this.headlessClients.set(sessionID, { webSocket: webSocket, state: EHeadlessStatus.READY }); + } + + public removeHeadlessClient(sessionID: string): void { + this.headlessClients.delete(sessionID); + } + + /** Begin setting up a raid for a headless client + * + * @returns returns the SessionID of the headless client that is starting this raid, returns null if no client could be found or there was an error. + */ + public async startHeadlessRaid(headlessSessionID: string, requesterSessionID: string, info: IStartHeadlessRequest): Promise { + const headlessClient = this.headlessClients.get(headlessSessionID); + + if (!headlessClient || headlessClient?.state != EHeadlessStatus.READY) { + return null; + } + + headlessClient.state = EHeadlessStatus.IN_RAID; + headlessClient.players = []; + headlessClient.requesterSessionID = requesterSessionID; + headlessClient.hasNotifiedRequester = false; + + const headlessClientWS = headlessClient.webSocket; + + if (!headlessClientWS) { + return null; + } + + if (headlessClientWS.readyState === WebSocket.CLOSED) { + return null; + } + + const startRequest: IStartHeadlessRaid = { + type: EFikaHeadlessWSMessageTypes.HeadlessStartRaid, + startRequest: info, + }; + + await headlessClientWS.sendAsync(JSON.stringify(startRequest)); + + return headlessSessionID; + } + + /** Sends a join message to the requester of a headless client */ + public async sendJoinMessageToRequester(headlessClientId: string): Promise { + const headlessClient = this.headlessClients.get(headlessClientId); + + if (!headlessClient || headlessClient?.state === EHeadlessStatus.READY) { + return null; + } + + const message: IHeadlessRequesterJoinRaid = { + type: EFikaHeadlessWSMessageTypes.RequesterJoinMatch, + matchId: headlessClientId, + }; + + await this.fikaHeadlessRequesterWebSocket.sendAsync(headlessClient.requesterSessionID, message); + headlessClient.hasNotifiedRequester = true; + } + + public addPlayerToHeadlessMatch(headlessClientId: string, sessionID: string): void { + const headlessClient = this.headlessClients.get(headlessClientId); + + if (!headlessClient || headlessClient?.state != EHeadlessStatus.IN_RAID) { + return; + } + + if (headlessClientId === sessionID) { + return; + } + + headlessClient.players.push(sessionID); + + if (!this.fikaConfig.getConfig().headless.setLevelToAverageOfLobby) { + // Doing this everytime is unecessary if we're not setting the average level so only set it once the original requester of the headless joins. + if (headlessClient.requesterSessionID === sessionID) { + this.setHeadlessLevel(headlessClientId); + } + } else { + this.setHeadlessLevel(headlessClientId); + } + } + + private setHeadlessLevel(headlessClientId: string): void { + const headlessClient = this.headlessClients.get(headlessClientId); + + if (!headlessClient || headlessClient?.state != EHeadlessStatus.IN_RAID) { + return; + } + + const headlessProfile = this.saveServer.getProfile(headlessClientId); + + // Set level of headless to that of the requester. + if (!this.fikaConfig.getConfig().headless.setLevelToAverageOfLobby) { + headlessProfile.characters.pmc.Info.Level = this.saveServer.getProfile(headlessClient.requesterSessionID).characters.pmc.Info.Level; + return; + } + + let baseHeadlessLevel = 0; + let players = headlessClient.players.length; + + for (const sessionID of headlessClient.players) { + baseHeadlessLevel += this.saveServer.getProfile(sessionID).characters.pmc.Info.Level; + } + + baseHeadlessLevel = Math.round(baseHeadlessLevel / players); + + this.logger.debug(`[${headlessClientId}] Setting headless level to: ${baseHeadlessLevel} | Players: ${players}`); + + headlessProfile.characters.pmc.Info.Level = baseHeadlessLevel; + } + + /** End the raid for the specified headless client, sets the state back to READY so that he can be requested to host again. */ + public endHeadlessRaid(headlessClientId: string): void { + const headlessClient = this.headlessClients.get(headlessClientId); + + if (!headlessClient) { + return; + } + + headlessClient.state = EHeadlessStatus.READY; + headlessClient.players = null; + headlessClient.requesterSessionID = null; + headlessClient.hasNotifiedRequester = null; + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/utils/FikaConfig.ts b/mods/Fika Server/user/mods/fika-server/src/utils/FikaConfig.ts index f7890b3..59bf541 100644 --- a/mods/Fika Server/user/mods/fika-server/src/utils/FikaConfig.ts +++ b/mods/Fika Server/user/mods/fika-server/src/utils/FikaConfig.ts @@ -3,11 +3,17 @@ import { inject, injectable } from "tsyringe"; import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import { JsonUtil } from "@spt/utils/JsonUtil"; -import { VFS } from "@spt/utils/VFS"; import { IFikaConfig } from "../models/fika/config/IFikaConfig"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { ICoreConfig } from "@spt/models/spt/config/ICoreConfig"; +import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { FileSystem } from "@spt/utils/FileSystem"; import packageJson from "../../package.json"; +import { IFikaSPTServerConfig } from "../models/fika/config/IFikaConfigServer"; @injectable() export class FikaConfig { @@ -15,17 +21,176 @@ export class FikaConfig { protected modName: string; protected modPath: string; protected fikaConfig: IFikaConfig; + protected defaultFikaConfig: IFikaConfig = { + client: { + useBtr: true, + friendlyFire: true, + dynamicVExfils: false, + allowFreeCam: false, + allowSpectateFreeCam: false, + blacklistedItems: [], + forceSaveOnDeath: false, + mods: { + required: [], + optional: [], + }, + useInertia: true, + sharedQuestProgression: false, + canEditRaidSettings: true, + enableTransits: true, + anyoneCanStartRaid: false, + }, + server: { + SPT: { + http: { + ip: "0.0.0.0", + port: 6969, + backendIp: "0.0.0.0", + backendPort: 6969, + }, + disableSPTChatBots: true, + }, + allowItemSending: true, + sentItemsLoseFIR: true, + launcherListAllProfiles: false, + sessionTimeout: 5, + showDevProfile: false, + showNonStandardProfile: false, + logClientModsInConsole: false, + }, + natPunchServer: { + enable: false, + port: 6790, + natIntroduceAmount: 1, + }, + headless: { + profiles: { + amount: 0, + aliases: {}, + }, + scripts: { + generate: true, + forceIp: "", + }, + setLevelToAverageOfLobby: true, + restartAfterAmountOfRaids: 0, + }, + background: { + enable: true, + easteregg: false, + }, + }; constructor( @inject("PreSptModLoader") protected preSptModLoader: PreSptModLoader, - @inject("VFS") protected vfs: VFS, + @inject("FileSystem") protected fileSystem: FileSystem, @inject("JsonUtil") protected jsonUtil: JsonUtil, + @inject("ConfigServer") protected configServer: ConfigServer, + @inject("WinstonLogger") protected logger: ILogger, ) { this.modAuthor = packageJson.author.replace(/\W/g, "").toLowerCase(); this.modName = packageJson.name.replace(/\W/g, "").toLowerCase(); this.modPath = this.preSptModLoader.getModPath(this.getModFolderName()); + } - this.fikaConfig = this.jsonUtil.deserializeJsonC(this.vfs.readFile(path.join(this.modPath, "assets/configs/fika.jsonc"))); + public async preInit(): Promise { + const configPath = path.join(this.modPath, "assets/configs/fika.jsonc"); + + if (!(await this.fileSystem.exists(path.join(configPath)))) { + await this.fileSystem.writeJson(configPath, this.defaultFikaConfig, "\t"); + } + + this.fikaConfig = this.jsonUtil.deserializeJsonC(await this.fileSystem.read(configPath)); + + if (await this.checkAndAddMissingConfigProperties(this.defaultFikaConfig, this.fikaConfig)) { + this.fikaConfig = this.sortProperties(this.defaultFikaConfig, this.fikaConfig); + await this.fileSystem.writeJson(configPath, this.fikaConfig, "\t"); + } + + await this.applySPTConfig(this.fikaConfig.server.SPT); + } + + protected async checkAndAddMissingConfigProperties(source: IFikaConfig, target: IFikaConfig): Promise { + let modified = false; + for (const key in source) { + if (source.hasOwnProperty(key)) { + if (typeof source[key] === "object" && !Array.isArray(source[key])) { + if (!target.hasOwnProperty(key)) { + target[key] = {}; + modified = true; + } + const nestedModified = await this.checkAndAddMissingConfigProperties(source[key], target[key]); + if (nestedModified) { + modified = true; + } + } else { + if (!target.hasOwnProperty(key)) { + target[key] = source[key]; + modified = true; + } + } + } + } + return modified; + } + + protected sortProperties(source: IFikaConfig, target: IFikaConfig): IFikaConfig { + // Cast as IFikaConfig as this is empty before sorting. + const sortedTarget: IFikaConfig = {} as IFikaConfig; + for (const key in source) { + if (source.hasOwnProperty(key)) { + if (typeof source[key] === "object" && !Array.isArray(source[key])) { + sortedTarget[key] = this.sortProperties(source[key], target[key] || {}); + } else { + sortedTarget[key] = target[key]; + } + } + } + + return sortedTarget; + } + + protected async applySPTConfig(config: IFikaSPTServerConfig): Promise { + this.logger.info("[Fika Server] Overriding SPT configuration"); + + const coreConfig = this.configServer.getConfig(ConfigTypes.CORE) as ICoreConfig; + const httpConfig = this.configServer.getConfig(ConfigTypes.HTTP) as IHttpConfig; + + if (config.disableSPTChatBots) { + const commando = coreConfig.features.chatbotFeatures.ids.commando; + const sptFriend = coreConfig.features.chatbotFeatures.ids.spt; + + coreConfig.features.chatbotFeatures.enabledBots[commando] = false; + coreConfig.features.chatbotFeatures.enabledBots[sptFriend] = false; + } + + httpConfig.ip = config.http.ip; + httpConfig.port = config.http.port; + httpConfig.backendIp = config.http.backendIp; + httpConfig.backendPort = config.http.backendPort; + } + + public updateFikaConfig(config: IFikaConfig): boolean { + if (config != this.fikaConfig) { + this.fikaConfig = config; + + const coreConfig = this.configServer.getConfig(ConfigTypes.CORE) as ICoreConfig; + const commando = coreConfig.features.chatbotFeatures.ids.commando; + const sptFriend = coreConfig.features.chatbotFeatures.ids.spt; + + // Re-handle chatbot settings + if (config.server.SPT.disableSPTChatBots) { + coreConfig.features.chatbotFeatures.enabledBots[commando] = false; + coreConfig.features.chatbotFeatures.enabledBots[sptFriend] = false; + } else { + coreConfig.features.chatbotFeatures.enabledBots[commando] = true; + coreConfig.features.chatbotFeatures.enabledBots[sptFriend] = true; + } + + return true; + } + + return false; } public getConfig(): IFikaConfig { diff --git a/mods/Fika Server/user/mods/fika-server/src/utils/FikaServerTools.ts b/mods/Fika Server/user/mods/fika-server/src/utils/FikaServerTools.ts index ed815ce..0b797e5 100644 --- a/mods/Fika Server/user/mods/fika-server/src/utils/FikaServerTools.ts +++ b/mods/Fika Server/user/mods/fika-server/src/utils/FikaServerTools.ts @@ -1,20 +1,19 @@ -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { inject, injectable } from "tsyringe"; -import { FikaConfig } from "./FikaConfig"; -import { IFikaConfigNatPunchServer } from "../models/fika/config/IFikaConfigNatPunchServer"; -import { ConfigServer } from "@spt/servers/ConfigServer"; +import { ChildProcessWithoutNullStreams, spawn } from "child_process"; +import fs from "fs"; +import path from "node:path"; +import os from "os"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig"; -import { ChildProcessWithoutNullStreams, spawn } from "child_process"; -import path from "node:path"; -import fs from "fs"; -import os from "os"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { inject, injectable } from "tsyringe"; +import { IFikaConfigNatPunchServer } from "../models/fika/config/IFikaConfigNatPunchServer"; +import { FikaConfig } from "./FikaConfig"; @injectable() export class FikaServerTools { protected readonly name: string = "FikaServerTools"; protected exePath: string; - protected natPunchServerConfig: IFikaConfigNatPunchServer; protected httpConfig: IHttpConfig; protected processes: Record = {}; @@ -34,18 +33,19 @@ export class FikaServerTools { break; } } - this.natPunchServerConfig = fikaConfig.getConfig().natPunchServer; this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); } public startService(serviceName: string): void { var exeArgs: string[]; + const natPunchServerConfig = this.fikaConfig.getConfig().natPunchServer; + switch (serviceName) { case "NatPunchServer": const ip = this.httpConfig.ip; - const port = this.natPunchServerConfig.port; - const natIntroduceAmount = this.natPunchServerConfig.natIntroduceAmount; + const port = natPunchServerConfig.port; + const natIntroduceAmount = natPunchServerConfig.natIntroduceAmount; exeArgs = `-NatPunchServer -IP ${ip} -Port ${port} -NatIntroduceAmount ${natIntroduceAmount}`.split(" "); break; default: diff --git a/mods/Fika Server/user/mods/fika-server/src/websockets/FikaDedicatedRaidWebSocket.ts b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaDedicatedRaidWebSocket.ts deleted file mode 100644 index a4c9a7a..0000000 --- a/mods/Fika Server/user/mods/fika-server/src/websockets/FikaDedicatedRaidWebSocket.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { inject, injectable } from "tsyringe"; - -import { ILogger } from "@spt/models/spt/utils/ILogger"; -import { SaveServer } from "@spt/servers/SaveServer"; -import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; -import { IncomingMessage } from "http"; -import { WebSocket } from "ws"; - -@injectable() -export class FikaDedicatedRaidWebSocket implements IWebSocketConnectionHandler { - public clientWebSockets: Record; - - constructor( - @inject("SaveServer") protected saveServer: SaveServer, - @inject("WinstonLogger") protected logger: ILogger, - ) { - this.clientWebSockets = {}; - - // Keep websocket connections alive - setInterval(() => { - this.keepWebSocketAlive(); - }, 30000); - } - - public getSocketId(): string { - return "Fika Dedicated Raid Service"; - } - - public getHookUrl(): string { - return "/fika/dedicatedraidservice/"; - } - - public onConnection(ws: WebSocket, req: IncomingMessage): void { - if (req.headers.authorization === undefined) { - ws.close(); - return; - } - - const Authorization = Buffer.from(req.headers.authorization.split(" ")[1], "base64").toString().split(":"); - const UserSessionID = Authorization[0]; - - this.logger.debug(`[${this.getSocketId()}] User is ${UserSessionID}`); - - if (!this.saveServer.getProfile(UserSessionID)) { - this.logger.warning(`[${this.getSocketId()}] Invalid user ${UserSessionID} tried to authenticate!`); - return; - } - - this.clientWebSockets[UserSessionID] = ws; - - ws.on("message", (msg) => this.onMessage(UserSessionID, msg.toString())); - ws.on("close", (code, reason) => this.onClose(ws, UserSessionID, code, reason)); - } - - // biome-ignore lint/correctness/noUnusedVariables: Currently unused, but might be implemented in the future. - public onMessage(sessionID: string, msg: string): void { - // Do nothing - } - - // biome-ignore lint/correctness/noUnusedVariables: Currently unused, but might be implemented in the future. - public onClose(ws: WebSocket, sessionID: string, code: number, reason: Buffer): void { - const clientWebSocket = this.clientWebSockets[sessionID]; - - if (clientWebSocket === ws) { - this.logger.debug(`[${this.getSocketId()}] Deleting client ${sessionID}`); - - delete this.clientWebSockets[sessionID]; - } - } - - private keepWebSocketAlive(): void { - for (const sessionId in this.clientWebSockets) { - const clientWebSocket = this.clientWebSockets[sessionId]; - - if (clientWebSocket.readyState == WebSocket.CLOSED) { - delete this.clientWebSockets[sessionId]; - return; - } - - // Send a keep alive message to the dedicated client - clientWebSocket.send( - JSON.stringify({ - type: "fikaDedicatedKeepAlive", - }), - ); - } - } -} diff --git a/mods/Fika Server/user/mods/fika-server/src/websockets/FikaHeadlessClientWebSocket.ts b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaHeadlessClientWebSocket.ts new file mode 100644 index 0000000..75d227f --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaHeadlessClientWebSocket.ts @@ -0,0 +1,101 @@ +import { inject, injectable } from "tsyringe"; + +import { IncomingMessage } from "http"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; +import { SPTWebSocket } from "@spt/servers/ws/SPTWebsocket"; +import { FikaHeadlessHelper } from "../helpers/FikaHeadlessHelper"; +import { EFikaHeadlessWSMessageTypes } from "../models/enums/EFikaHeadlessWSMessageTypes"; +import { IFikaHeadlessBase } from "../models/fika/websocket/IFikaHeadlessBase"; +import { FikaMatchService } from "../services/FikaMatchService"; +import { FikaHeadlessService } from "../services/headless/FikaHeadlessService"; + +@injectable() +export class FikaHeadlessClientWebSocket implements IWebSocketConnectionHandler { + private headlessWebSockets: Record = {}; + + constructor( + @inject("FikaHeadlessHelper") protected fikaHeadlessHelper: FikaHeadlessHelper, + @inject("FikaHeadlessService") protected fikaHeadlessService: FikaHeadlessService, + @inject("FikaMatchService") protected fikaMatchService: FikaMatchService, + @inject("WinstonLogger") protected logger: ILogger, + ) { + // Keep websocket connections alive + setInterval(async () => { + await this.keepWebSocketAlive(); + }, 30000); + } + + public getSocketId(): string { + return "Fika Headless Client"; + } + + public getHookUrl(): string { + return "/fika/headless/client"; + } + + public async onConnection(ws: SPTWebSocket, req: IncomingMessage): Promise { + if (req.headers.authorization === undefined) { + ws.close(); + return; + } + + const Authorization = Buffer.from(req.headers.authorization.split(" ")[1], "base64").toString().split(":"); + const UserSessionID = Authorization[0]; + + this.logger.debug(`[${this.getSocketId()}] User is ${UserSessionID}`); + + if (!this.fikaHeadlessHelper.isHeadlessClient(UserSessionID)) { + this.logger.warning(`[${this.getSocketId()}] Invalid headless client ${UserSessionID} tried to authenticate!`); + return; + } + + this.headlessWebSockets[UserSessionID] = ws; + + ws.on("message", (msg) => this.onMessage(UserSessionID, msg.toString())); + ws.on("close", (code, reason) => this.onClose(ws, UserSessionID, code, reason)); + + // Cleanup match if headless has crashed before re-adding it as an available client + if (this.fikaMatchService.getMatchIdByProfile(UserSessionID)) { + this.fikaMatchService.deleteMatch(UserSessionID); + } + + this.fikaHeadlessService.addHeadlessClient(UserSessionID, ws); + } + + // biome-ignore lint/correctness/noUnusedVariables: Currently unused, but might be implemented in the future. + public onMessage(sessionID: string, msg: string): void { + // Do nothing + } + + // biome-ignore lint/correctness/noUnusedVariables: Currently unused, but might be implemented in the future. + public onClose(ws: SPTWebSocket, sessionID: string, code: number, reason: Buffer): void { + const clientWebSocket = this.headlessWebSockets[sessionID]; + + if (clientWebSocket === ws) { + this.logger.debug(`[${this.getSocketId()}] Deleting client ${sessionID}`); + + delete this.headlessWebSockets[sessionID]; + this.fikaHeadlessService.removeHeadlessClient(sessionID); + } + } + + private async keepWebSocketAlive(): Promise { + for (const sessionId in this.headlessWebSockets) { + const clientWebSocket = this.headlessWebSockets[sessionId]; + + if (clientWebSocket.readyState === WebSocket.CLOSED) { + delete this.headlessWebSockets[sessionId]; + this.fikaHeadlessService.removeHeadlessClient(sessionId); + return; + } + + let message: IFikaHeadlessBase = { + type: EFikaHeadlessWSMessageTypes.KeepAlive, + }; + + // Send a keep alive message to the headless client + await clientWebSocket.sendAsync(JSON.stringify(message)); + } + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/websockets/FikaHeadlessRequesterWebSocket.ts b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaHeadlessRequesterWebSocket.ts new file mode 100644 index 0000000..dee1eb7 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaHeadlessRequesterWebSocket.ts @@ -0,0 +1,99 @@ +import { inject, injectable } from "tsyringe"; + +import { IncomingMessage } from "http"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import { SaveServer } from "@spt/servers/SaveServer"; +import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; +import { SPTWebSocket } from "@spt/servers/ws/SPTWebsocket"; +import { EFikaHeadlessWSMessageTypes } from "../models/enums/EFikaHeadlessWSMessageTypes"; +import { IFikaHeadlessBase } from "../models/fika/websocket/IFikaHeadlessBase"; + +@injectable() +export class FikaHeadlessRequesterWebSocket implements IWebSocketConnectionHandler { + private requesterWebSockets: Record = {}; + + constructor( + @inject("SaveServer") protected saveServer: SaveServer, + @inject("WinstonLogger") protected logger: ILogger, + ) { + // Keep websocket connections alive + setInterval(async () => { + await this.keepWebSocketAlive(); + }, 30000); + } + + public getSocketId(): string { + return "Fika Headless Requester"; + } + + public getHookUrl(): string { + return "/fika/headless/requester"; + } + + public async onConnection(ws: SPTWebSocket, req: IncomingMessage): Promise { + if (req.headers.authorization === undefined) { + ws.close(); + return; + } + + const Authorization = Buffer.from(req.headers.authorization.split(" ")[1], "base64").toString().split(":"); + const UserSessionID = Authorization[0]; + + this.logger.debug(`[${this.getSocketId()}] User is ${UserSessionID}`); + + if (!this.saveServer.getProfile(UserSessionID)) { + this.logger.warning(`[${this.getSocketId()}] Invalid user ${UserSessionID} tried to authenticate!`); + return; + } + + this.requesterWebSockets[UserSessionID] = ws; + ws.on("close", (code, reason) => this.onClose(ws, UserSessionID, code, reason)); + } + + // biome-ignore lint/correctness/noUnusedVariables: Currently unused, but might be implemented in the future. + public onClose(ws: SPTWebSocket, sessionID: string, code: number, reason: Buffer): void { + const clientWebSocket = this.requesterWebSockets[sessionID]; + + if (clientWebSocket === ws) { + this.logger.debug(`[${this.getSocketId()}] Deleting requester ${sessionID}`); + + delete this.requesterWebSockets[sessionID]; + } + } + + public async sendAsync(sessionID: string, message: IFikaHeadlessBase): Promise { + const client = this.requesterWebSockets[sessionID]; + + // Client is not online or not currently connected to the websocket. + if (!client) { + this.logger.warning(`[${this.getSocketId()}] Requester (${sessionID}) is not connected yet?`); + return; + } + + // Client was formerly connected to the websocket, but may have connection issues as it didn't run onClose + if (client.readyState === WebSocket.CLOSED) { + this.logger.warning(`[${this.getSocketId()}] Requester (${sessionID})'s websocket is closed?`); + return; + } + + await client.sendAsync(JSON.stringify(message)); + } + + private async keepWebSocketAlive(): Promise { + for (const sessionId in this.requesterWebSockets) { + const clientWebSocket = this.requesterWebSockets[sessionId]; + + if (clientWebSocket.readyState === WebSocket.CLOSED) { + delete this.requesterWebSockets[sessionId]; + return; + } + + let message: IFikaHeadlessBase = { + type: EFikaHeadlessWSMessageTypes.KeepAlive, + }; + + // Send a keep alive message to the headless client + await clientWebSocket.sendAsync(JSON.stringify(message)); + } + } +} diff --git a/mods/Fika Server/user/mods/fika-server/src/websockets/FikaNotificationWebSocket.ts b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaNotificationWebSocket.ts index d06ed8c..fa9dc45 100644 --- a/mods/Fika Server/user/mods/fika-server/src/websockets/FikaNotificationWebSocket.ts +++ b/mods/Fika Server/user/mods/fika-server/src/websockets/FikaNotificationWebSocket.ts @@ -1,18 +1,18 @@ import { IncomingMessage } from "http"; import { inject, injectable } from "tsyringe"; -import { WebSocket } from "ws"; -import { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { SaveServer } from "@spt/servers/SaveServer"; import { IWebSocketConnectionHandler } from "@spt/servers/ws/IWebSocketConnectionHandler"; +import { SPTWebSocket } from "@spt/servers/ws/SPTWebsocket"; import { EFikaNotifications } from "../models/enums/EFikaNotifications"; import { IFikaNotificationBase } from "../models/fika/websocket/IFikaNotificationBase"; import { FikaPresenceService } from "../services/FikaPresenceService"; @injectable() export class FikaNotificationWebSocket implements IWebSocketConnectionHandler { - private clientWebSockets: Record; + private clientWebSockets: Record; constructor( @inject("SaveServer") protected saveServer: SaveServer, @@ -22,8 +22,8 @@ export class FikaNotificationWebSocket implements IWebSocketConnectionHandler { this.clientWebSockets = {}; // Keep websocket connections alive - setInterval(() => { - this.keepWebSocketAlive(); + setInterval(async () => { + await this.keepWebSocketAlive(); }, 30000); } @@ -35,7 +35,7 @@ export class FikaNotificationWebSocket implements IWebSocketConnectionHandler { return "/fika/notification/"; } - public onConnection(ws: WebSocket, req: IncomingMessage): void { + public async onConnection(ws: SPTWebSocket, req: IncomingMessage): Promise { if (req.headers.authorization === undefined) { ws.close(); return; @@ -65,7 +65,7 @@ export class FikaNotificationWebSocket implements IWebSocketConnectionHandler { } // biome-ignore lint/correctness/noUnusedVariables: Currently unused, but might be implemented in the future. - public onClose(ws: WebSocket, sessionID: string, code: number, reason: Buffer): void { + public onClose(ws: SPTWebSocket, sessionID: string, code: number, reason: Buffer): void { const clientWebSocket = this.clientWebSockets[sessionID]; if (clientWebSocket === ws) { @@ -78,7 +78,7 @@ export class FikaNotificationWebSocket implements IWebSocketConnectionHandler { } // Send functionality for sending to a single client. - public send(sessionID: string, message: IFikaNotificationBase): void { + public async sendAsync(sessionID: string, message: IFikaNotificationBase): Promise { const client = this.clientWebSockets[sessionID]; // Client is not online or not currently connected to the websocket. @@ -91,22 +91,22 @@ export class FikaNotificationWebSocket implements IWebSocketConnectionHandler { return; } - client.send(JSON.stringify(message)); + await client.sendAsync(JSON.stringify(message)); } - public broadcast(message: IFikaNotificationBase): void { + public async broadcast(message: IFikaNotificationBase): Promise { for (const sessionID in this.clientWebSockets) { - this.send(sessionID, message); + await this.sendAsync(sessionID, message); } } - private keepWebSocketAlive(): void { + private async keepWebSocketAlive(): Promise { for (const sessionID in this.clientWebSockets) { let message: IFikaNotificationBase = { type: EFikaNotifications.KeepAlive, }; - this.send(sessionID, message); + await this.sendAsync(sessionID, message); } } } diff --git a/mods/Fika Server/user/mods/fika-server/tsconfig.types.json b/mods/Fika Server/user/mods/fika-server/tsconfig.types.json new file mode 100644 index 0000000..8b2c3a0 --- /dev/null +++ b/mods/Fika Server/user/mods/fika-server/tsconfig.types.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "skipLibCheck": true + } +} diff --git a/mods/Gekos Better Progression/BepInEx/plugins/gekos_api/Assets/skillsbutton.bundle b/mods/Gekos Better Progression/BepInEx/plugins/gekos_api/Assets/skillsbutton.bundle new file mode 100644 index 0000000..266c340 --- /dev/null +++ b/mods/Gekos Better Progression/BepInEx/plugins/gekos_api/Assets/skillsbutton.bundle @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ba94d5f096917207344426956f0cf889196cb8be847780ca319c4dc4f643d8e +size 386750 diff --git a/mods/Gekos Better Progression/BepInEx/plugins/gekos_api/gekos_api.dll b/mods/Gekos Better Progression/BepInEx/plugins/gekos_api/gekos_api.dll new file mode 100644 index 0000000..6044be8 Binary files /dev/null and b/mods/Gekos Better Progression/BepInEx/plugins/gekos_api/gekos_api.dll differ diff --git a/mods/Gekos Better Progression/meta.ini b/mods/Gekos Better Progression/meta.ini new file mode 100644 index 0000000..d6704b9 --- /dev/null +++ b/mods/Gekos Better Progression/meta.ini @@ -0,0 +1,28 @@ +[General] +gameName=spt +modid=0 +version=d2025.5.13.0 +newestVersion= +category="-1," +nexusFileStatus=1 +installationFile=drunkgeko-gekos_betterprogression.zip +repository=Nexus +ignoredVersion= +comments= +notes= +nexusDescription= +url= +hasCustomURL=false +lastNexusQuery= +lastNexusUpdate= +nexusLastModified=2025-05-13T08:40:24Z +nexusCategory=0 +converted=false +validated=false +color=@Variant(\0\0\0\x43\0\xff\xff\0\0\0\0\0\0\0\0) +tracked=0 + +[installedFiles] +1\modid=0 +1\fileid=0 +size=1 diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/.qodo/history.sqlite b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/.qodo/history.sqlite new file mode 100644 index 0000000..61be47a Binary files /dev/null and b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/.qodo/history.sqlite differ diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/README.md b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/README.md new file mode 100644 index 0000000..1985e7d --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/README.md @@ -0,0 +1 @@ +Not much to see here \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/additionalQuestRewards.json5 b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/additionalQuestRewards.json5 new file mode 100644 index 0000000..8cc8fc9 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/additionalQuestRewards.json5 @@ -0,0 +1,27 @@ +{ + "Started": { + + }, + "Success": { + //Stirrup rewards an Ammunition Case + "596b455186f77457cb50eccb": { + "availableInGameEditions": [], + "findInRaid": false, + "id": "67dfe1d6c86d01e21c962c36", + "index": 0, + "items": [ + { + "_id": "67dfe1d2e5b3ffda5e44b529", + "_tpl": "5aafbde786f774389d0cbc0f", //Ammo case + "upd": { + "StackObjectsCount": 1 + } + } + ], + "target": "67dfe1d2e5b3ffda5e44b529", + "type": "Item", + "unknown": false, + "value": 1 + } + }, +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/customItems.json5 b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/customItems.json5 new file mode 100644 index 0000000..7133aad --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/customItems.json5 @@ -0,0 +1,234 @@ +{ + //New buffs then referenced by custom items + "customBuffs": + { + "BuffsSuperSoldier": [ + { + "AbsoluteValue": true, + "BuffType": "SkillRate", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "Endurance", + "Value": 30 + }, + { + "AbsoluteValue": true, + "BuffType": "SkillRate", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "Strength", + "Value": 30 + }, + { + "AbsoluteValue": true, + "BuffType": "SkillRate", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "StressResistance", + "Value": 20 + }, + { + "AbsoluteValue": true, + "BuffType": "HealthRate", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "", + "Value": 3 + }, + { + "AbsoluteValue": true, + "BuffType": "QuantumTunnelling", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "", + "Value": 0 + }, + { + "AbsoluteValue": false, + "BuffType": "WeightLimit", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "", + "Value": 1 + }, + { + "AbsoluteValue": false, + "BuffType": "DamageModifier", + "Chance": 1, + "Delay": 1, + "Duration": 1800, + "SkillName": "", + "Value": -0.5 + }, + { + "AbsoluteValue": true, + "BuffType": "StaminaRate", + "Chance": 1, + "Delay": 1, + "Duration": 3000, + "Value": 3, + "SkillName": "" + }, + { + "AbsoluteValue": true, + "BuffType": "BodyTemperature", + "Chance": 1, + "Delay": 10, + "Duration": 1800, + "SkillName": "", + "Value": 4 + }, + { + "AbsoluteValue": true, + "BuffType": "HydrationRate", + "Chance": 0.7, + "Delay": 60, + "Duration": 1800, + "SkillName": "", + "Value": -0.7 + }, + { + "AbsoluteValue": true, + "BuffType": "EnergyRate", + "Chance": 0.7, + "Delay": 60, + "Duration": 1800, + "SkillName": "", + "Value": -0.6 + }, + { + "AbsoluteValue": true, + "BuffType": "HealthRate", + "Chance": 0.7, + "Delay": 1800, + "Duration": 300, + "SkillName": "", + "Value": -3 + }, + ], + }, + + //New item templates to be added + "customItems": + { + "67c9a2f88fcabd70c9b35c08": { + "_id": "67c9a2f88fcabd70c9b35c08", + "_name": "SUP_Compound", + "_parent": "5448f3a64bdc2d60728b456a", + "_props": { + "AnimationVariantsNumber": 0, + "BackgroundColor": "orange", + "CanRequireOnRagfair": false, + "CanSellOnRagfair": false, + "ConflictingItems": [], + "Description": "", + "DiscardLimit": 2, + "DiscardingBlock": false, + "DropSoundType": "None", + "ExamineExperience": 100, + "ExamineTime": 1, + "ExaminedByDefault": false, + "ExtraSizeDown": 0, + "ExtraSizeForceAdd": false, + "ExtraSizeLeft": 0, + "ExtraSizeRight": 0, + "ExtraSizeUp": 0, + "Height": 1, + "HideEntrails": false, + "InsuranceDisabled": false, + "IsAlwaysAvailableForInsurance": false, + "IsLockedafterEquip": false, + "IsSpecialSlotOnly": false, + "IsUnbuyable": false, + "IsUndiscardable": false, + "IsUngivable": false, + "IsUnremovable": false, + "IsUnsaleable": false, + "ItemSound": "med_stimulator", + "LootExperience": 10, + "MaxHpResource": 0, + "MergesWithChildren": false, + "Name": "S.U.P. Compound", + "NotShownInSlot": false, + "Prefab": { + "path": "assets/content/weapons/usable_items/item_syringe/item_stimulator_sj9_tglabs_loot.bundle", + "rcid": "" + }, + "QuestItem": false, + "QuestStashMaxCount": 0, + "RagFairCommissionModifier": 1, + "RarityPvE": "Superrare", + "RepairCost": 0, + "RepairSpeed": 0, + "ShortName": "S.U.P.", + "StackMaxSize": 1, + "StackObjectsCount": 1, + "StimulatorBuffs": "BuffsSuperSoldier", + "Unlootable": false, + "UnlootableFromSide": [], + "UnlootableFromSlot": "FirstPrimaryWeapon", + "UsePrefab": { + "path": "assets/content/weapons/usable_items/item_syringe/item_stimulator_sj9_tglabs_container.bundle", + "rcid": "" + }, + "Weight": 0.05, + "Width": 1, + "effects_damage": [], + "effects_health": [], + "hpResourceRate": 0, + "medEffectType": "duringUse", + "medUseTime": 2 + }, + "_proto": "544fb3f34bdc2d03748b456a", + "_type": "Item" + }, + }, + + "customLocales": { + "67c9a2f88fcabd70c9b35c08": { + "Name": "S.U.P. Compound Injector", + "ShortName": "S.U.P.", + "Description": "A cutting-edge military-grade stimulant, the Strategic Ultra-Performance (S.U.P.) Compound is designed for elite operators requiring peak combat efficiency. Injected directly into the bloodstream, this powerful serum enhances speed, strength, reflexes, and pain resistance for a short but critical duration. Originally an untested experimental drug, the compound was intended for special forces but was never officially approved due to unpredictable side effects. While it grants immense battlefield superiority, users risk severe metabolic crashes, uncontrolled aggression, and potential long-term damage. Use wisely; supremacy on the battlefield comes at a price." + } + }, + + //Add custom trades + "customTrades": { + //Therapist + "54cb57776803fa99248b456e": { + "items": [ + { + "_id": "67c99cb6d072a85372ecd09e", + "_tpl": "67c9a2f88fcabd70c9b35c08", //SUP stim + "parentId": "hideout", + "slotId": "hideout", + "upd": { + "UnlimitedCount": true, + "StackObjectsCount": 9999999, + "BuyRestrictionMax": 3, + "BuyRestrictionCurrent": 0 + } + }, + ], + "barter_scheme": { + "67c99cb6d072a85372ecd09e": [ //SUP stim + [ + { + "count": 1320000, + "_tpl": "5449016a4bdc2d6f028b456f" + } + ] + ], + }, + "loyal_level_items": { + "67c99cb6d072a85372ecd09e": 4, //SUP stim + } + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/customTrades.json5 b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/customTrades.json5 new file mode 100644 index 0000000..a723cdd --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/customTrades.json5 @@ -0,0 +1,83 @@ +{ + //Prapor + "54cb50c76803fa8b248b4571": { + + "items": { + "67d43f2a227bcdfcdd58ecf3": { + "_id": "67d43f2a227bcdfcdd58ecf3", + "_tpl": "617fd91e5539a84ec44ce155", //RGN impact nade + "parentId": "hideout", + "slotId": "hideout", + "upd": { + "UnlimitedCount": true, + "StackObjectsCount": 9999999, + "BuyRestrictionMax": 3, + "BuyRestrictionCurrent": 0 + } + }, + }, + + "barter_scheme": { + "67d43f2a227bcdfcdd58ecf3": [ //RGN impact nade + [ + { + "count": 25000, + "_tpl": "5449016a4bdc2d6f028b456f" + } + ] + ], + + }, + + "loyal_level_items": { + "67d43f2a227bcdfcdd58ecf3": 4, //RGN impact nade + + }, + + "questlocks": { + "67d43f2a227bcdfcdd58ecf3": { //RGN impact nade + "quest": "639670029113f06a7c3b2377", //Following the Bread Crumbs (Lightkeeper) + "rewardId": "67d44502588ce6c75823d67c", + "targetId": "67d4451e5d4569a3b28ae368", + }, + } + }, + + //Ref + "6617beeaa9cfa777ca915b7c": { + + "items": { + "67dfe267d9d392d28b7e63d4": { + "_id": "67dfe267d9d392d28b7e63d4", + "_tpl": "66bc98a01a47be227a5e956e", //Streamer Item Case + "parentId": "hideout", + "slotId": "hideout", + "upd": { + "UnlimitedCount": true, + "StackObjectsCount": 9999999, + "BuyRestrictionMax": 3, + "BuyRestrictionCurrent": 0 + } + }, + }, + + "barter_scheme": { + "67dfe267d9d392d28b7e63d4": [ //Streamer Item Case + [ + { + "count": 50, + "_tpl": "5d235b4d86f7742e017bc88a" + } + ] + ], + }, + + "loyal_level_items": { + "67dfe267d9d392d28b7e63d4": 1, //Streamer Item Case + }, + + "questlocks": { + + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/secureChanges.json5 b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/secureChanges.json5 new file mode 100644 index 0000000..b2fa14d --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/advanced/secureChanges.json5 @@ -0,0 +1,71 @@ +{ + "AdditionalQuestRewards": { + "Started": { + //Network Provider - Part 1 gives Gamma container (as initial equipment) + "625d6ff5ddc94657c21a1625": + { + "availableInGameEditions": [], + "findInRaid": false, + "id": "67c7102265c41f16d88520dc", + "index": 0, + "items": [ + { + "_id": "67c7101e8891bcc8c33b3ba5", + "_tpl": "5857a8bc2459772bad15db29", //Gamma container + "upd": { + "StackObjectsCount": 1 + } + } + ], + "target": "67c7101e8891bcc8c33b3ba5", + "type": "Item", + "unknown": false, + "value": 1 + } + }, + "Success": { + //Delivery from the Past gives Alpha container + "59674eb386f774539f14813a": + { + "availableInGameEditions": [], + "findInRaid": false, + "id": "67c70fbb4a41e8e5d609745a", + "index": 0, + "items": [ + { + "_id": "67c70fb55bfaa2c4455f7dff", + "_tpl": "544a11ac4bdc2d470e8b456a", //Alpha container + "upd": { + "StackObjectsCount": 1 + } + } + ], + "target": "67c70fb55bfaa2c4455f7dff", + "type": "Item", + "unknown": false, + "value": 1 + }, + //Setup gives Beta container + "5c1234c286f77406fa13baeb" : + { + "availableInGameEditions": [], + "findInRaid": false, + "id": "67c7064b2261718aee538635", + "index": 0, + "items": [ + { + "_id": "67c70634853d4fee9264efe1", + "_tpl": "5857a8b324597729ab0a0e7d", //Beta container + "upd": { + "StackObjectsCount": 1 + } + } + ], + "target": "67c70634853d4fee9264efe1", + "type": "Item", + "unknown": false, + "value": 1 + }, + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/config.json5 b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/config.json5 new file mode 100644 index 0000000..4e016f8 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/config/config.json5 @@ -0,0 +1,865 @@ +//// GEKO'S BETTER PROGRESSION CONFIGURATION FILE //// +// +// Welcome to the config file. Nearly everything this mod does can be disabled or tweaked to your liking. +// All text after "//" in a given line is a "comment" and the mod won't read it at all, such as these lines over here +// You can find IDs for items and other things on https://moxopixel.com/tarkynator/. Failing that you can explore the SPT_Data/Server/database folder in your SPT installation to find the ID you need +// A few times "Deltas" are mentioned in this config file. Delta is a fancy word to mean "change", a negative delta will reduce a value and a positive one will increase it +// All ranges are considered to be inclusive of the lower bound but exclusive of the upper one. For example a [10, 40] range will be all values from 10 to 40 with 10 included but 40 excluded +// If you encounter any bugs please do let me know either on the GitHub page or as a comment on the SPT Hub page for the mod +// + +{ + //Apply changes to secure containers and the ways to obtain them + //Most safe containers have been made slightly bigger (2 to 4 extra slots on each) + //Alpha is obtainable from Delivery from the Past + //Beta is obtainable from Setup + //Gamma is obtainable from accepting Network Provider - Part 1 + "secureContainerProgression": { + "enable": true, + + //Set the container you start a new profile with. Doesn't work if mod gets added after the profile has already been created, please start fresh + "starterContainer": "5732ee6a24597719ae0c0281", //Waist Pouch + + //Set the grid sizes of each container. The format for each is [ [*grid 1 width*, *grid 1 height*], [*grid 2 width*, *grid 2 height*] ] and so on for any amount of grids. + //Grids will be laid out horizontally in the game no matter what so don't go too crazy + "sizeChanges": { + "544a11ac4bdc2d470e8b456a": [[2,2], [1,2]], //Alpha + "5857a8b324597729ab0a0e7d": [[1,2], [2,2], [1,2]], //Beta + "59db794186f77448bc595262": [[1,2], [3,2], [1,2]], //Epsilon + "5857a8bc2459772bad15db29": [[3,3], [1,3]], //Gamma + "5c093ca986f7740a1867ab12": [[1,2], [3,4], [1,2]], //Kappa + }, + + //More options can be found in the advanced config folder in the secureChanges.json5 file + }, + + //This mod is intended to (and balanced around) playing without the flea market. Change this setting at your own peril! + "fleaMarketChanges": { + "disableFleaMarket": true, + + //If true, keys that are tradable on the flea market by default will still be tradable even if disableFleaMarket is true + "stillAllowKeys": true, + + //Even if the flea market is disabled the following items will still be sold (and sellable) on it + "fleaWhitelist": [ + + ] + }, + + //Reduce the amounts of items required by each hideout upgrade. All items required beyond a certain threshold will be multiplied by the given factor. Only the amount that goes past the threshold is mutliplied + //For example if the threshold is 3 and the factor is 0.5 if a build would require 15 (= 3 + 12) nuts it will instead require 3 + (12 * 0.5) = 9 nuts + "hideoutBuildsChanges": { + "enable": true, + + "threshold": 3, + "factor": 0.5, + "roundDown": true, + }, + + //Stash changes + "stashProgression" : { + "enable": true, + + //What stash level should a new profile start with? + "startingStashLevel": 1, + + //Amount of lines in each stash level + "stashSizes": [40, 55, 70, 85, 100], + + //Multiply ruble cost of each stage of stash upgrade by this much + "stashUpgradeCostFactor": 0.4, + + //Change loyalty requirements for stash upgrades by this much + "stashUpgradeLoyaltyDelta": -1, + }, + + //Make skill xp gain faster and buff some skill bonuses + //Also optionally adds a system that provides skill points that can be freely allocated as you level up your PMC + "skillChanges": { + "enable": true, + + //Multiply earned skill XP by this much if it's fresh XP (XP for a skill you haven't leveled recently this raid) + "SkillFreshEffectiveness": 2, + //How many full skill points are considered to be fresh before going back to normal XP earn rates + "SkillFreshPoints": 2, + //After this many points have been earned at a normal XP rate (so after the fresh points) skill fatigue kicks in + "SkillPointsBeforeFatigue": 1, + //Skill fatigue gradually reduces XP earned down to a minimum multiplier that is specified here + "SkillMinEffectiveness": 0.4, + + //Multiply the amount of XP gained for each skill and/or the effectiveness of the various skill buffs + "CustomMultipliers": { + //Flat out multiply the amount of xp earned for skills + "GlobalXPMultiplier": 1, + //Specific multipliers per-skill. If unspecified it's considered to be 1 + //Both global and specific multipliers apply, this does NOT override the global + "SkillXPMultipliers": { + "Metabolism" : 0.125, + "Charisma": 0.7, + }, + + //Multiply the buff effect of specific skill buffs + "SkillBuffMultipliers": { + "StrengthBuffLiftWeightInc": 2.5, + "MagDrillsLoadSpeed": 3, + "MagDrillsUnloadSpeed": 3, + "MagDrillsInventoryCheckSpeed": 2, + "CharismaDailyQuestsRerollDiscount": 10, + "CharismaHealingDiscount": 10, + "CharismaInsuranceDiscount": 10, + "CharismaExfiltrationDiscount": 10, + "SurgeryReducePenalty": 2, + "SurgerySpeed": 2, + "SearchSpeed": 1.5, + + //Complete list of all buffs that can be modified: + + //EnduranceBuffEnduranceInc + //EnduranceBuffJumpCostRed + //EnduranceBuffBreathTimeInc + //EnduranceBuffRestorationTimeRed + //EnduranceBreathElite + //EnduranceHands + //StrengthBuffLiftWeightInc + //StrengthBuffSprintSpeedInc + //StrengthBuffJumpHeightInc + //StrengthBuffThrowDistanceInc + //StrengthBuffMeleePowerInc + //StrengthBuffElite + //StrengthBuffMeleeCrits + //StrengthBuffAim + //VitalityBuffBleedChanceRed + //VitalityBuffSurviobilityInc + //VitalityBuffRegeneration + //VitalityBuffBleedStop + //HealthBreakChanceRed + //HealthOfflineRegenerationInc + //HealthEnergy + //HealthHydration + //HealthEliteAbsorbDamage + //HealthElitePosion + //StressPainChance + //StressTremor + //StressBerserk + //MetabolismRatioPlus + //MetabolismEnergyExpenses + //MetabolismPhysicsForget + //MetabolismPhysicsForget2 + //MetabolismEliteBuffNoDyhydration + //MetabolismEliteNoForget + //PerceptionHearing + //PerceptionFov + //PerceptionLootDot + //PerceptionmEliteNoIdea + //IntellectLearningSpeed + //IntellectWeaponMaintance + //IntellectEliteNaturalLearner + //IntellectEliteAmmoCounter + //IntellectEliteContainerScope + //IntellectRepairPointsCostReduction + //AttentionLootSpeed + //AttentionRareLoot + //AttentionEliteLuckySearch + //AttentionEliteExtraLootExp + //MagDrillsLoadSpeed + //MagDrillsUnloadSpeed + //MagDrillsInventoryCheckSpeed + //MagDrillsInventoryCheckAccuracy + //MagDrillsInstantCheck + //MagDrillsLoadProgression + //CharismaBuff1 + //CharismaBuff2 + //CharismaEliteBuff1 + //CharismaEliteBuff2 + //CharismaDailyQuestsRerollDiscount + //CharismaHealingDiscount + //CharismaInsuranceDiscount + //CharismaExfiltrationDiscount + //CharismaScavCaseDiscount + //CharismaFenceRepPenaltyReduction + //CharismaAdditionalDailyQuests + //WeaponReloadBuff + //WeaponRecoilBuff + //WeaponSwapBuff + //DrawSpeed + //DrawElite + //DrawTremor + //DrawSound + //AimMasterElite + //AimMasterWiggle + //AimMasterSpeed + //RecoilControlImprove + //RecoilControlElite + //CovertMovementSoundVolume + //ProneMovementSpeed + //ProneMovementVolume + //ProneMovementElite + //TroubleFixing + //TroubleFixingAmmoElite + //TroubleFixingExamineMalfElite + //WeaponErgonomicsBuff + //WeaponDoubleMastering + //WeaponDoubleActionRecoilReduceBuff + //WeaponStiffHands + //ThrowingStrengthBuff + //ThrowingEnergyExpenses + //ThrowingWeaponsBuffElite + //CovertMovementSpeed + //CovertMovementElite + //CovertMovementLoud + //CovertMovementEquipment + //SearchSpeed + //SearchDouble + //SurgeryReducePenalty + //SurgerySpeed + //HideoutResourceConsumption + //HideoutZoneBonusBoost + //HideoutExtraSlots + //CraftingElite + //CraftingSingleTimeReduce + //CraftingContinueTimeReduce + //MetabolismPoisonTime + //MetabolismMiscDebuffTime + //ImmunityMiscEffects + //ImmunityPoisonBuff + //ImmunityPainKiller + //ImmunityPoisonChance + //ImmunityMiscEffectsChance + //WeaponDurabilityLossOnShotReduce + //WearAmountRepairGunsReducePerLevel + }, + }, + + //Earn a customizable amount of freely allocatable skill points with each PMC level + "SkillPointsSystem": { + "enable": true, + + //Decimal values are fine. An extra point will be awarded when the decimal part ticks over 1. For example 1.5 points per level means that every other level you'll get 2 points instead of 1 + "skillPointsPerLevel": 1, + + //The game keeps track of skill levels behind the scenes even when you got past level 51. If this is true any point allocated into a skill that is level 51 will be refunded as the skill naturally keeps leveling up. + //This keeps going until the skill reaches level 51 "naturally" without the help of the skill points + //For example say that you have a skill at level 51 out of which 40 are naturally earned levels and 11 have been allocated with this system. + //Then as the skill continues to overlevel past 51 (which is the cap) you will gradually get your 11 points back if this option is set to true + "automaticallyRefundOverflows": true, + + //If true, players can freely deallocate invested points. I suggest keeping this off as it otherwise encourages moving skill points around constantly + "enableDeallocation": false, + } + }, + + //A series of changes that make Ref work much better in SPT by tying him to PMC kills + //The mods "Bosses Have Lega Medals" and "Ref - SPT Friendly Quests" are STRONGLY reccomended, this section is ment to complement those two + //Bosses Have Lega Medals: https://hub.sp-tarkov.com/files/file/2109-bosses-have-lega-medals/ + //Ref - SPT Friendly Quests: https://hub.sp-tarkov.com/files/file/2108-ref-spt-friendly-quests/ + "refChanges": { + "enable": true, + + //If true, Ref will buy items for GP coins instead of RUB + //I strongly reccomend keeping refOnlyBuysDogtags to true if you intend on keeping this option enabled + "refBuysInGPCoins": true, + + //Ref won't buy anything except dogtags (and Lega Medals if enabled) + "refOnlyBuysDogtags": true, + "refAlsoBuysLegaMedals": true, //Works regardless of the value of the previous option + + //Gain standing/rep for Ref every time you get a PMC kill + "refStandingOnKill": { + "enable": true, + + //Amount of standing/rep to be gained depending on the level of the player killed + "repByKillLevel": [ + { + "levelRange": [0, 10], + "rep": 0.003 + }, + { + "levelRange": [10, 30], + "rep": 0.004 + }, + { + "levelRange": [30, 50], + "rep": 0.006 + }, + { + "levelRange": [50, 9999], + "rep": 0.01 + } + ] + }, + }, + + //A series of buffs to the SICC container + "SICCBuffs": { + "enable": true, + + //SICC will be able to hold everything that the Docs case can (on top of what the SICC already can carry) + "canHoldWhatDocsCan": true, + + //Additional items that can be put in a SICC case + "additionalWhitelistedItems": [ + "59fafd4b86f7745ca07e1232", //Key tool + ] + }, + + //A variety of changes to bitcoin farming. The defualt configuration is aimed at making bitcoin farming less powerful to start with but more powerful at high GPU counts + //Keep in mind that with the rest of the mod with default settings GPUs will be much harder to obtain than normal, which is intended and accounted for in the balancing of this part + "bitcoinChanges": { + "enable": true, + + //Remove all non-barter trades for GPUs + "cannotBuyGPU": true, + + //Should the value of a BitCoin be overridden to be a set value? + "overrideValue": true, + "value": 648000, //This is the actual item value. Traders will buy them at a lower amount since they never pay full price for items + + //Desmos graph to visualize the following two values: https://www.desmos.com/calculator/osralvxw4g + //Flatly multiply the rate of btc produced by this much (variable m in Desmos) + "btcFarmSpeedMult": 0.68, + //How effective is each GPU in boosting the production rate (variable E in Desmos) + "gpuBoostRate": 0.164, + + //How many bitcoins fit in the bitcoin farm + "btcCapacity": 5, + }, + + //Change initial trader reputation + "overrideInitialStanding": { + "enable": true, + + //Default starting reputation + "defaultOverride": 0.05, + + //Change the default stating on a per-trader basis + "indivudalOverrides": { + "579dc571d53a0658a154fbec": 1, //Fence + } + }, + + //Miscellaneous changes + "misc": { + + //Remove Found in Raid requirements from various places + "removeFirFromQuests": true, + "removeFirFromHideout": true, + "removeFirFromFlea": true, + + //Multiply amounts of crafted products and crafting times in hideout + "craftProductMultiplier": 2, + "craftTimeMultiplier": 0.5, + + //Enable the additional quest rewards (separate from secure containers quest rewards) + //More settings can be found in the advanced/additionalQuestRewards.json5 config file + "enableExtraQuestRewards": true, + + //Enable or disable the custom trades defined in the advanced config customTrades.json5 + "addCustomTrades": true, + + //Change maximum stack size for the given items + "stackSizeOverride": { + //Special items used in quests + //Unfortunately BSG's spaghetti code makes it so that placing one of these consumes the entire stack instead of just one unit so i disabled the changes. Feel free to re-enable them if you still want them + //To do so remove the "//" at the start of the desired lines + //"5991b51486f77447b112d44f": 3, //MS2000 Marker + //"5ac78a9b86f7741cca0bbd8d": 3, //Signal Jammer + //"5b4391a586f7745321235ab2": 3, //Wi-fi Camera + //Others + //"6656560053eaaa7a23349c86": 5, //Lega Medals + }, + + //Change sizes of containers + "ContanerSizeChanges": { + //Vanilla items + "5aafbde786f774389d0cbc0f": [8, 8], //Ammunition case buff from 7x7 to 8x8 + "5c127c4486f7745625356c13": [8, 8], //Magazine case buff from 7x7 to 8x8 + "5aafbcd986f7745e590fff23": [8, 8], //Medicine case buff from 7x7 to 8x8 + + //WTT-Pack 'n' Strap items (does nothing if mod is not installed) + "12403f74773f49be6a2d84b7": [2, 3], //Small Medical Pouch nerf down to 2x3 + "ae9e418fd5d4c4eec4a0e6ea": [2, 2], //Small Ammunition Pouch nerf down to 2x2 + + //Tactical Gear Component items (does nothing if mod is not installed) + "672e2e7526ba61dbb88be7ff": [4, 2], //TGC First Aid container nerf down to 4x2 + "672e2e758808bacbb9d5abc4": [2, 3], //TGC Ammo Pouch nerf down to 2x3 + }, + + //Change the price of items that are sold for rubles + "priceChanges": { + //WTT-Pack 'n' Strap items (does nothing if mod is not installed) + "12403f74773f49be6a2d84b7": 1442000, //Small Medical Pouch + "ae9e418fd5d4c4eec4a0e6ea": 620920, //Small Ammunition Pouch + + //Tactical Gear Component items (does nothing if mod is not installed) + "672e2e7526ba61dbb88be7ff": 6080000, //TGC First Aid container + "672e2e758808bacbb9d5abc4": 3880000, //TGC Ammo Pouch + }, + + }, + + //Module responsible for algorithmically rebalancing most military items in the game + //This module calculates a loyalty score for each item that can also be decimal. The actual in-game loyalty level of the item will be the floor of the loyalty score. So for example a loyalty score of 3.8 will correspond to loyalty level 3 + //The exact way in which this works will become more apparent by looking at the specific rules for each class of items (for example the ammoRules) + "algorithmicalRebalancing": { + "enable": true, + + //If false, items that end up having a loyalty score greater or equal than 5 will be hidden (level >4 traders do not exist) + //If it's true items with loyalty over 4 will be brought back to 4 + "clampToMaxLevel": true, + //If true, all trades that are locked behind quests will be treated as if "clampToMaxLevel" is true rgardless of its actual value + //If this is false then some quest-locked items could potentially be unobtainable even when unlocked since they would end up at loyalty level 5+ + "forceClampingOfQuestlockedItems": true, + + //Change the loyalty score of all items this mod affects (for example barter items are unaffected) by this much if the item trade is a barter or if it's quest locked + "barterDelta": -0.7, + "questLockDelta": -1, + "logBartersAndLocks": false, //For debugging + + //Traders that will not be affected by the rebalancing module + "excludeTraders": [ + "579dc571d53a0658a154fbec", //Fence + ], + + //Modify the loyalty score of all offers on the given trader. Only affects items that are already being algorithmically rebalanced (ammo, wepaons) + "deltaByTrader": { + "6617beeaa9cfa777ca915b7c": -0.7, //Ref + }, + + //Ammo rebalancing module + "ammoRules": { + "enable": true, + "logChanges": false, //For debugging + + //Change every ammunition loyalty level by this much + "globalDelta": 0, + + //If an ammunition falls between the ammunition ranges specified here then it will get the corresponding base loyalty score + //Loyalty score is then further modified by the other rules + "defaultBaseLoyaltyByPen": 1, + "ammoBaseLoyaltyByPen": [ + { + "penInterval": [0, 14], + "baseLoyalty": 0.5 + }, + { + "penInterval": [14, 17], + "baseLoyalty": 1 + }, + { + "penInterval": [17, 21], + "baseLoyalty": 1.5 + }, + { + "penInterval": [21, 27], + "baseLoyalty": 2 + }, + { + "penInterval": [27, 31], + "baseLoyalty": 2.6 + }, + { + "penInterval": [31, 37], + "baseLoyalty": 3 + }, + { + "penInterval": [37, 41], + "baseLoyalty": 3.7 + }, + { + "penInterval": [41, 47], + "baseLoyalty": 4 + }, + { + "penInterval": [47, 99999], + "baseLoyalty": 5 + } + ], + + //Modify the loyalty score of an ammunition depending on its caliber + "caliberRules": [ + { + "caliber": "Caliber12g", + "loyaltyDelta": -2 + }, + { + "caliber": "Caliber762x54R", + "loyaltyDelta": -2 + }, + { + "caliber": "Caliber762x39", + "loyaltyDelta": 0 + }, + { + "caliber": "Caliber762x51", + "loyaltyDelta": -1 + }, + { + "caliber": "Caliber20g", + "loyaltyDelta": -3 + }, + { + "caliber": "Caliber366TKM", + "loyaltyDelta": -0.6 + }, + { + "caliber": "Caliber9x19PARA", + "loyaltyDelta": 1.4 + }, + { + "caliber": "Caliber9x18PM", + "loyaltyDelta": 1 + }, + { + "caliber": "Caliber127x55", + "loyaltyDelta": -1 + }, + { + "caliber": "Caliber9x21", + "loyaltyDelta": 0.5 + }, + { + "caliber": "Caliber752x25TT", + "loyaltyDelta": 0.5 + }, + { + "caliber": "Caliber86x70", + "loyaltyDelta": -2 + }, + { + "caliber": "Caliber556x45NATO", + "loyaltyDelta": 0.4 + }, + { + "caliber": "Caliber545x39", + "loyaltyDelta": 0.3 + }, + { + "caliber": "Caliber127x33", + "loyaltyDelta": -0.5 + } + ], + + //If the damage of an ammunition falls within one (or more) of the intervals then its loyalty score will be modified by the corresponding delta + "damageRules": [ + { + "damageInterval": [85, 90], + "loyaltyDelta": 0.5 + }, + { + "damageInterval": [90, 100], + "loyaltyDelta": 1 + }, + { + "damageInterval": [100, 120], + "loyaltyDelta": 1.5 + }, + { + "damageInterval": [120, 255], + "loyaltyDelta": 2 + }, + { + "damageInterval": [255, 360], + "loyaltyDelta": 3 + }, + { + "damageInterval": [360, 9999], + "loyaltyDelta": 4 + } + ], + + //Disable the ammo balancing module on the following calibers + "ignoreCalibers": [ + "Caliber26x75", //Flare gun + ], + + //Settings specific to hideout crafts of ammo + "craftSettings": { + //If disabled ammo crafts will not be changed by this mod + "enable": true, + + //If loyalty score falls between the given ranges then the ammunition craft will end up in the corresponding workbench level + "loyaltyToLevelRanges": [ + { + "range": [-9999, 2.5], + "level": 1 + }, + { + "range": [2.5, 3.5], + "level": 2 + }, + { + "range": [3.5, 9999], + "level": 3 + } + ] + }, + + }, + + //Weapons rebalancing module + "weaponRules": { + "enable": true, + "logChanges": false, //For debugging + + //Change every weapon loyalty level by this much + "globalDelta": 0, + + //All weapons will bring all of their default parts to their same loyalty level. If multiple weapons share the same default parts the lowest level is picked + //For example if this is true the default m4 gas block will always be found on the same loyalty level as the m4 + "attachmentsFollowDefaultBuild": true, + //Add this delta if the weapon contains any attachments that cannot be purchased at the trader level the weapon would otherwise be placed at + //Won't affect attachments that are part of the default build of the weapon + //If attachmentsFollowDefaultBuild is set to false i reccomend setting this to 0 + //If this is different than 0 the mod will take a few seconds longer to load when starting the server (still no performance impact when playing, no worries) + "advancedAttachmentsDelta": 1, + + //Base loyalty score of each weapon is decided by its caliber. If the caliber of the weapon is not listed below then the default value will be picked + //Loyalty score is then further modified by the other rules + "defaultBaseLoyalty": 2, + "weaponBaseLoyaltyByCaliber": [ + { + "caliber": "Caliber762x51", + "baseLoyalty": 3.8 + }, + { + "caliber": "Caliber556x45NATO", + "baseLoyalty": 3 + }, + { + "caliber": "Caliber545x39", + "baseLoyalty": 3 + }, + { + "caliber": "Caliber762x39", + "baseLoyalty": 3 + }, + { + "caliber": "Caliber57x28", + "baseLoyalty": 2.5 + }, + { + "caliber": "Caliber46x30", + "baseLoyalty": 2.5 + }, + { + "caliber": "Caliber9x19PARA", + "baseLoyalty": 1 + }, + { + "caliber": "Caliber366TKM", + "baseLoyalty": 2 + }, + { + "caliber": "Caliber9x39", + "baseLoyalty": 2.5 + }, + { + "caliber": "Caliber127x55", + "baseLoyalty": 3.5 + }, + { + "caliber": "Caliber23x75", + "baseLoyalty": 5 + }, + { + "caliber": "Caliber9x18PM", + "baseLoyalty": 1 + }, + { + "caliber": "Caliber9x18PMM", + "baseLoyalty": 1 + }, + { + "caliber": "Caliber762x25TT", + "baseLoyalty": 1 + }, + { + "caliber": "Caliber86x70", + "baseLoyalty": 6 + }, + { + "caliber": "Caliber1143x23ACP", + "baseLoyalty": 2 + }, + { + "caliber": "Caliber762x54R", + "baseLoyalty": 3.5 + }, + { + "caliber": "Caliber68x51", + "baseLoyalty": 4.9 + }, + { + "caliber": "Caliber127x33", + "baseLoyalty": 4 + }, + { + "caliber": "Caliber12g", + "baseLoyalty": 3.7 + }, + { + "caliber": "Caliber762x39", + "baseLoyalty": 3 + }, + + ], + + //Modify the loyalty score depending on the BEST fire mode available to the weapon + //Manual includes bolt-action, pump-action and single-shot + //Semiauto includes semi automatic, double-action and double barrel + //Fullauto and burst are self-explanatory + "fireModeRules": [ + { + "mode" : "manual", + "delta" : -2 + }, + { + "mode" : "semiauto", + "delta" : -1 + }, + { + "mode" : "burst", + "delta" : -0.5 + }, + { + "mode" : "fullauto", + "delta" : 0 + } + ], + + //If the fire rate of a weapon falls within one (or more) of the intervals then its loyalty score will be modified by the corresponding delta + //THIS ONLY AFFECTS FULL AUTO AND BURST WEAPONS + "fireRateRules": [ + { + "rateInterval": [0, 620], + "delta": -0.7 + }, + { + "rateInterval": [620, 760], + "delta": -0.5 + }, + { + "rateInterval": [900, 99999], + "delta": 1 + } + ], + + //If multiple weapons share the same niche then the better ones will be upshifted to higher trader levels (how a niche is defined is configurable below) + //For example if both the UMP and the Vector.45 happen to both fall into loyalty level 3 then the Vector will be upshifted to level 4 (if the power level rules below define the Vector as a better weapon than the UMP and with default niche definition) + //Loyalty level for shifting is checked after all other rules have already been applied. + //The shifting applies recursively. This means that if, for example, three weapons of three different power levels all share the same niche and loyalty level then one will stay at that level, one will be shifted once and the other will be shifted twice + "upshiftRules": { + "enable": true, + "shiftAmount": 1, + + //If true, the worse weapon will be shifted down instead of the better one being shifted up + "shiftDownInstead": false, + + //How is a wapon niche defined for shifting? + "devideNicheByFiremode": true, //If true, weapons of different fire modes will be considered as parts of different niches. Only cares about the best available fire mode and only considers "manual", "semi auto", "burst" and "fullauto" + "devideNicheByCaliber": true, //If true, weapons of different calibers will be considered as parts of different niches + "devideNicheByBarterType": true, //If true, items sold for currency and items recieved from barters will be considered of two different niches + "devideNicheByQuestLock": true, //If true, items locked behind quests and items that aren't so will be considered of two different niches + + //Power levels of weapons. This is used to determine which weapons are better than which others. Has only meaning in a relative sense (to define an order between weapons of same niche), the absolute value is meaningless. + //For example if we want to define the Vepr Hunter as a weaker weapon compared to the RSASS we only need to set their power levels so that the VPO's is lower, the actual numbers don't matter + //Once again, the power level is compared for a possible shift only between weapons that share the same caliber AND fire mode AND loyalty level (with default niche settings) + //For example the power level of the Vepr Hunter will never compared to that of the SCAR-H nor that of the STM-9 + //If two weapons in the same niche and loyalty level have the same power level then neither will be shifted + "powerLevels": { + //// SEMI-AUTO 7.62x51 //// + "5c501a4d2e221602b412b540" : 1, //Vepr Hunter + "5f2a9575926fd9352339381f" : 2, //RFB + "5aafa857e5b5b00018480968" : 2, //M1A + "6176aca650224f204c1da3fb" : 3, //G28 + "5a367e5dc4a282000e49738f" : 3, //RSASS + "5df8ce05b11454561e39243b" : 3, //SR-25 + //// FULL-AUTO SHOTGUNS //// + "66ffa9b66e19cc902401c5e8": 1, //AA-12 Gen 1 + "67124dcfa3541f2a1f0e788b": 1, //AA-12 Gen 2 + "674fe9a75e51f1c47c04ec23": 2, //Auto Saiga-12k + //// FULL-AUTO 3x39 //// + "644674a13d52156624001fbc": 1, //9A-91 + "645e0c6b3b381ede770e1cc9": 2, //VSK-94 + "57838ad32459774a17445cd2": 3, //VSS + "57c44b372459772d2b39b8ce": 4, //AS-VAL + "651450ce0e00edc794068371": 4, //SR-3M + //// BOLT-ACTION 7.62×51 //// + "5bfea6e90db834001b7347f3": 1, //M700 + "5df24cf80dee1b22f862e9bc": 2, //T-5000M + "588892092459774ac91d4b11": 3, //DVL + //// FULL-AUTO 9x19 //// + "5926bb2186f7744b1c6c6e60": 1, //MP5 + "66992b349950f5f4cd06029f": 1, //Uzi + "58948c8e86f77409493f7266": 2, //MPX + "6680304edadb7aa61d00cef0": 2, //Uzi-pro + "668e71a8dadf42204c032ce1": 2, //Uzi-pro SMG + //// BOLT-ACTION 7.62x54R //// + "5ae08f0a5acfc408fb1398a1": 1, //Mosin + "5bfd297f0db834001a669119": 1, //Mosin infantry + "55801eed4bdc2d89578b4588": 2, //SV-98 + //// FULL-AUTO 7.62x51 //// + "5b0bbe4e5acfc40dc528a72d": 1, //SA58 + "5dcbd56fdbd3d91b3e5468d5": 2, //Black MDR + "6165ac306ef05c2ce828ef74": 2, //Scar-H (FDE) + "6183afd850224f204c1da514": 2, //Scar-H + "676176d362e0497044079f4c": 3, //Scar-H X-17 + //// .45 PISTOLS //// + "5e81c3cbac2bb513793cdc75": 1, //m1911 + "6193a720f8ee7e52e42109ed": 2, //USP .45 + } + + } + }, + + //Modify loyalty score of the given items by the given amount + //This only affects items that are already affected by at least one other algorithmical rebalancing module (so items such as ammo if ammo rebalancing is enabled) + "explicitLoyaltyDelta": { + //ID of the specific trade, NOT the id of the item being sold + "trades": { + + }, + //ID of the item being sold + "items": { + "57dc2fa62459775949412633": -0.7, //AKS-74U + "583990e32459771419544dd2": -0.7, //AKS-74UN + "5839a40f24597726f856b511": -0.7, //AKS-74UB + "62e7c4fba689e8c9c50dfc38": -1, //AUG A1 + "61f7c9e189e6fb1a5e3ea78d": -2, //MP-18 + "606587252535c57a13424cfd": +1, //Mutant + "5bb2475ed4351e00853264e3": +1, //HK 416A5 + "6513ef33e06849f06c0957ca": +1, //RPD + "65268d8ecb944ff1e90ea385": +2, //RPDN + "60db29ce99594040e04c4a27": -1, //Revolving shotgun + "5580223e4bdc2d1c128b457f": -2, //MP-43 + "5bfd297f0db834001a669119": -1, //Mosin infantry + "6499849fc93611967b034949": +0.7, //Ak-12 + "628a60ae6b1d481ff772e9c8": +0.7, //RD-704 + "576165642459773c7a400233": +1, //Saiga-12k + "674fe9a75e51f1c47c04ec23": +1, //Saiga-12k automatic + } + }, + + //Ignore all previous calculations and set the given items or trades to the given levels + //Items and trades specified here will also be ignored by all other systems (for example they won't count for the shifting system) + "explicitLoyaltyOverride": { + //ID of the specific trade, NOT the id of the item being sold + "trades": { + + }, + //ID of the item being sold + "items": { + "627e14b21713922ded6f2c15": 4, //AXMC + } + } + }, + + //Development options + "dev": { + //Hide info logging of each change being applied when mod is loading + "muteProgressOnServerLoad": false, + + //When an error occurs show the whole error message + "showFullError": true, + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/package.json b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/package.json new file mode 100644 index 0000000..95887e6 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/package.json @@ -0,0 +1,35 @@ +{ + "name": "gekos_betterprogression", + "version": "1.1.3", + "sptVersion": "~3.11.1", + "loadBefore": [ + "DewardianDev-AlgorithmicLevelProgression" + ], + "loadAfter": [ + "MoxoPixel-Painter - Trader" + ], + "incompatibilities": [], + "isBundleMod": false, + "main": "src/mod.js", + "scripts": { + "setup": "npm i", + "build": "node ./build.mjs", + "buildinfo": "node ./build.mjs --verbose" + }, + "devDependencies": { + "@types/node": "20.11", + "@typescript-eslint/eslint-plugin": "7.2", + "@typescript-eslint/parser": "7.2", + "archiver": "^6.0", + "eslint": "8.57", + "fs-extra": "11.2", + "ignore": "^5.2", + "json5": "^2.2.3", + "tsyringe": "4.8.0", + "typescript": "5.4", + "winston": "3.12" + }, + "author": "DrunkGeko", + "contributors": [], + "license": "MIT" +} diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.js new file mode 100644 index 0000000..4ad5369 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.calculateAmmoLoyalty = calculateAmmoLoyalty; +exports.scoreAmmo = scoreAmmo; +exports.rebalanceAmmoCrafts = rebalanceAmmoCrafts; +const BaseClasses_1 = require("C:/snapshot/project/obj/models/enums/BaseClasses"); +const utils_1 = require("../utils"); +function calculateAmmoLoyalty(item, context) { + const itemTemplate = context.tables.templates.items[item._tpl]; + return scoreAmmo(itemTemplate, context); +} +function scoreAmmo(itemTemplate, context) { + const config = context.config.algorithmicalRebalancing.ammoRules; + let loyalty = config.defaultBaseLoyaltyByPen; + //Base level from penetration + for (const rule of config.ammoBaseLoyaltyByPen) { + if (itemTemplate._props.PenetrationPower >= rule.penInterval[0] && itemTemplate._props.PenetrationPower < rule.penInterval[1]) { + loyalty = rule.baseLoyalty; + } + } + //Modify by caliber + for (const rule of config.caliberRules) { + if (itemTemplate._props.Caliber == rule.caliber) { + loyalty += rule.loyaltyDelta; + } + } + //Modify by damage (accounting for projectile count) + for (const rule of config.damageRules) { + const totalDamage = itemTemplate._props.Damage * itemTemplate._props.ProjectileCount; + if (totalDamage >= rule.damageInterval[0] && totalDamage < rule.damageInterval[1]) { + loyalty += rule.loyaltyDelta; + } + } + loyalty += config.globalDelta; + return loyalty; +} +function rebalanceAmmoCrafts(context) { + const config = context.config.algorithmicalRebalancing.ammoRules; + const crafts = context.tables.hideout.production.recipes; + for (const craft of crafts) { + const ammoId = craft.endProduct; + if (!context.itemHelper.isOfBaseclass(ammoId, BaseClasses_1.BaseClasses.AMMO)) + continue; //Exclude non-ammo + let score = scoreAmmo(context.tables.templates.items[ammoId], context); + if ((0, utils_1.isQuestLockedCraft)(craft)) + score += context.config.algorithmicalRebalancing.questLockDelta; + for (const map of config.craftSettings.loyaltyToLevelRanges) { + if (score >= map.range[0] && score < map.range[1]) + (0, utils_1.setAreaLevelRequirement)(craft, map.level); + } + } +} +//# sourceMappingURL=ammo.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.js.map new file mode 100644 index 0000000..23b1ba4 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "ammo.js", + "sourceRoot": "", + "sources": [ + "ammo.ts" + ], + "names": [], + "mappings": ";;AAMA,oDAKC;AAED,8BAsCC;AAED,kDAoBC;AAtED,kFAA+E;AAC/E,oCAAuE;AAEvE,SAAgB,oBAAoB,CAAC,IAAW,EAAE,OAAgB;IAE9D,MAAM,YAAY,GAAkB,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE9E,OAAO,SAAS,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,SAAgB,SAAS,CAAC,YAA2B,EAAE,OAAgB;IAGnE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,SAAS,CAAC;IAEjE,IAAI,OAAO,GAAW,MAAM,CAAC,uBAAuB,CAAC;IAErD,6BAA6B;IAC7B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,oBAAoB,EAC9C,CAAC;QACG,IAAI,YAAY,CAAC,MAAM,CAAC,gBAAgB,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAC7H,CAAC;YACG,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC;QAC/B,CAAC;IACL,CAAC;IAED,mBAAmB;IACnB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,YAAY,EACtC,CAAC;QACG,IAAI,YAAY,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAC/C,CAAC;YACG,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC;QACjC,CAAC;IACL,CAAC;IAED,oDAAoD;IACpD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW,EACrC,CAAC;QACG,MAAM,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC;QACrF,IAAI,WAAW,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EACjF,CAAC;YACG,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC;QACjC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,MAAM,CAAC,WAAW,CAAC;IAE9B,OAAO,OAAO,CAAA;AAClB,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAAgB;IAEhD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,SAAS,CAAC;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;IAEzD,KAAK,MAAM,KAAK,IAAI,MAAM,EAC1B,CAAC;QACG,MAAM,MAAM,GAAW,KAAK,CAAC,UAAU,CAAC;QACxC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,MAAM,EAAE,yBAAW,CAAC,IAAI,CAAC;YAAE,SAAS,CAAC,kBAAkB;QAE7F,IAAI,KAAK,GAAW,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAE/E,IAAI,IAAA,0BAAkB,EAAC,KAAK,CAAC;YAAE,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,cAAc,CAAC;QAE/F,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,oBAAoB,EAC3D,CAAC;YACG,IAAI,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;gBAAE,IAAA,+BAAuB,EAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QACjG,CAAC;IACL,CAAC;AAEL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.ts new file mode 100644 index 0000000..ab728ef --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/ammo.ts @@ -0,0 +1,74 @@ +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem"; +import { Context } from "../contex"; +import { BaseClasses } from "@spt/models/enums/BaseClasses"; +import { isQuestLockedCraft, setAreaLevelRequirement } from "../utils"; + +export function calculateAmmoLoyalty(item: IItem, context: Context): number +{ + const itemTemplate: ITemplateItem = context.tables.templates.items[item._tpl]; + + return scoreAmmo(itemTemplate, context); +} + +export function scoreAmmo(itemTemplate: ITemplateItem, context: Context): number +{ + + const config = context.config.algorithmicalRebalancing.ammoRules; + + let loyalty: number = config.defaultBaseLoyaltyByPen; + + //Base level from penetration + for (const rule of config.ammoBaseLoyaltyByPen) + { + if (itemTemplate._props.PenetrationPower >= rule.penInterval[0] && itemTemplate._props.PenetrationPower < rule.penInterval[1]) + { + loyalty = rule.baseLoyalty; + } + } + + //Modify by caliber + for (const rule of config.caliberRules) + { + if (itemTemplate._props.Caliber == rule.caliber) + { + loyalty += rule.loyaltyDelta; + } + } + + //Modify by damage (accounting for projectile count) + for (const rule of config.damageRules) + { + const totalDamage = itemTemplate._props.Damage * itemTemplate._props.ProjectileCount; + if (totalDamage >= rule.damageInterval[0] && totalDamage < rule.damageInterval[1]) + { + loyalty += rule.loyaltyDelta; + } + } + + loyalty += config.globalDelta; + + return loyalty +} + +export function rebalanceAmmoCrafts(context: Context): void +{ + const config = context.config.algorithmicalRebalancing.ammoRules; + const crafts = context.tables.hideout.production.recipes; + + for (const craft of crafts) + { + const ammoId: string = craft.endProduct; + if (!context.itemHelper.isOfBaseclass(ammoId, BaseClasses.AMMO)) continue; //Exclude non-ammo + + let score: number = scoreAmmo(context.tables.templates.items[ammoId], context); + + if (isQuestLockedCraft(craft)) score += context.config.algorithmicalRebalancing.questLockDelta; + + for (const map of config.craftSettings.loyaltyToLevelRanges) + { + if (score >= map.range[0] && score < map.range[1]) setAreaLevelRequirement(craft, map.level); + } + } + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.js new file mode 100644 index 0000000..64aef76 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.js @@ -0,0 +1,135 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.algorithmicallyRebalance = algorithmicallyRebalance; +const ammo_1 = require("./ammo"); +const weapon_1 = require("./weapon"); +const utils_1 = require("../utils"); +const BaseClasses_1 = require("C:/snapshot/project/obj/models/enums/BaseClasses"); +const types_1 = require("./types"); +function algorithmicallyRebalance(context) { + //Alias + const config = context.config.algorithmicalRebalancing; + const itemHelper = context.itemHelper; + const traders = Object.values(context.tables.traders); + //Organize changed items by loyalty level so that they can be easily accessed by the shifting system + //[trade, loyaltyScore, trader, logChanges][index][loyaltyLevel] + const changedItems = {}; + //Loop over each trader + for (const trader of traders) { + const loyaltyLevels = trader?.assort?.loyal_level_items; + if (loyaltyLevels == null) + continue; + const itemsForSale = trader?.assort?.items; + if (itemsForSale == null) + continue; + if (config.excludeTraders.includes(trader.base._id)) + continue; + for (const item of itemsForSale) { + if (Object.keys(config.explicitLoyaltyOverride.trades).includes(item._id)) + continue; + if (Object.keys(config.explicitLoyaltyOverride.items).includes(item._tpl)) + continue; + let thisItem; + //// AMMO //// + if (config.ammoRules.enable) { + let ammoOrBox = false; + let ammo; + let loyaltyScore; + if (itemHelper.isOfBaseclass(item._tpl, BaseClasses_1.BaseClasses.AMMO)) { + loyaltyScore = (0, ammo_1.calculateAmmoLoyalty)(item, context); + ammo = item._tpl; + ammoOrBox = true; + } + else if (itemHelper.isOfBaseclass(item._tpl, BaseClasses_1.BaseClasses.AMMO_BOX)) { + ammo = context.tables.templates.items[item._tpl]._props.StackSlots[0]._props.filters[0].Filter[0]; + loyaltyScore = (0, ammo_1.scoreAmmo)(context.tables.templates.items[ammo], context); + ammoOrBox = true; + } + if (ammoOrBox && !config.ammoRules.ignoreCalibers.includes(context.tables.templates.items[ammo]._props.Caliber)) { + thisItem = new types_1.ChangedItem(item, loyaltyScore, trader, config.ammoRules.logChanges, false); + } + } + //// WEAPONS //// + if (config.weaponRules.enable + && itemHelper.isOfBaseclass(item._tpl, BaseClasses_1.BaseClasses.WEAPON) + && !itemHelper.isOfBaseclass(item._tpl, BaseClasses_1.BaseClasses.SPECIAL_WEAPON)) //ToDo: double check melee and grenades (and possibly other exceptions) + { + const loyaltyScore = (0, weapon_1.calculateWeaponLoyalty)(item, itemsForSale, context); + thisItem = new types_1.ChangedItem(item, loyaltyScore, trader, config.weaponRules.logChanges, true); + } + if (thisItem != null) //If item is being affected by the mod + { + //Final modifications + if ((0, utils_1.isQuestLocked)(item, trader)) { + thisItem.score += config.questLockDelta; + if (config.logBartersAndLocks) + context.logger.info(context.tables.templates.items[item._tpl]._name + " is a quest-locked item\t(Trade ID: " + item._id + ")"); + } + if ((0, utils_1.isBarterTrade)(item, trader)) { + thisItem.score += config.barterDelta; + if (config.logBartersAndLocks) + context.logger.info(context.tables.templates.items[item._tpl]._name + " is a bartered item\t(Trade ID: " + item._id + ")"); + } + //By-trader delta + if (trader.base._id in config.deltaByTrader) + thisItem.score += config.deltaByTrader[trader.base._id]; + //Explicit deltas + const tradeD = config.explicitLoyaltyDelta.trades[thisItem.trade._id]; + const itemD = config.explicitLoyaltyDelta.items[thisItem.trade._tpl]; + if (!isNaN(tradeD)) + thisItem.score += tradeD; + if (!isNaN(itemD)) + thisItem.score += itemD; + const level = (0, utils_1.loyaltyFromScore)(thisItem.score, config.clampToMaxLevel); + if (changedItems[level] == null) { + changedItems[level] = [thisItem]; + } + else { + changedItems[level].push(thisItem); + } + } + } + } + if (config.weaponRules.upshiftRules.enable) + (0, weapon_1.weaponShifting)(changedItems, context); + if (config.weaponRules.attachmentsFollowDefaultBuild) + (0, weapon_1.followDefaultBuild)(changedItems, context); + if (config.weaponRules.advancedAttachmentsDelta != 0) + (0, weapon_1.penalizeAdvancedAttachments)(changedItems, context); + //Apply changes + for (const changesInLevel of Object.values(changedItems)) { + if (changesInLevel == null || changesInLevel.length == 0) + continue; + for (const changedItem of changesInLevel) { + let doClamp = config.clampToMaxLevel; + if (config.forceClampingOfQuestlockedItems && (0, utils_1.isQuestLocked)(changedItem.trade, changedItem.trader)) + doClamp = true; + if (changedItem.logChange) + context.logger.info(`Setting ${context.tables.templates.items[changedItem.trade._tpl]._name} at loyalty level ${(0, utils_1.loyaltyFromScore)(changedItem.score, doClamp)} (${changedItem.score})`); + (0, utils_1.setLoyalty)(changedItem.trade._id, changedItem.score, changedItem.trader, doClamp); + } + } + //Overrides + for (const trader of traders) { + const loyaltyLevels = trader?.assort?.loyal_level_items; + if (loyaltyLevels == null) + continue; + const itemsForSale = trader?.assort?.items; + if (itemsForSale == null) + continue; + if (config.excludeTraders.includes(trader.base._id)) + continue; + for (const item of trader.assort.items) { + let override = null; + override = config.explicitLoyaltyOverride.trades[item._id]; + if (override == null) + override = config.explicitLoyaltyOverride.items[item._tpl]; + if (override == null) + continue; + (0, utils_1.setLoyalty)(item._id, override, trader, config.clampToMaxLevel); + } + } + if (config.ammoRules.craftSettings.enable) + (0, ammo_1.rebalanceAmmoCrafts)(context); +} +//# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.js.map new file mode 100644 index 0000000..bebb0b2 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "core.js", + "sourceRoot": "", + "sources": [ + "core.ts" + ], + "names": [], + "mappings": ";;AAQA,4DA6JC;AAnKD,iCAA8E;AAC9E,qCAAmI;AACnI,oCAAsF;AACtF,kFAA+E;AAC/E,mCAAsC;AAEtC,SAAgB,wBAAwB,CAAC,OAAgB;IAErD,OAAO;IACP,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC;IAEvD,MAAM,UAAU,GAAe,OAAO,CAAC,UAAU,CAAC;IAClD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEtD,oGAAoG;IACpG,gEAAgE;IAChE,MAAM,YAAY,GAAwC,EAAE,CAAC;IAE7D,uBAAuB;IACvB,KAAK,MAAM,MAAM,IAAI,OAAO,EAC5B,CAAC;QACG,MAAM,aAAa,GAAG,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC;QACxD,IAAI,aAAa,IAAI,IAAI;YAAE,SAAS;QAEpC,MAAM,YAAY,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC;QAC3C,IAAI,YAAY,IAAI,IAAI;YAAE,SAAS;QAEnC,IAAI,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAE9D,KAAK,MAAM,IAAI,IAAI,YAAY,EAC/B,CAAC;YACG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,SAAS;YACpF,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YACpF,IAAI,QAAqB,CAAC;YAE1B,cAAc;YACd,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,EAC3B,CAAC;gBACG,IAAI,SAAS,GAAG,KAAK,CAAC;gBACtB,IAAI,IAAI,CAAC;gBACT,IAAI,YAAY,CAAC;gBACjB,IAAI,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,yBAAW,CAAC,IAAI,CAAC,EACzD,CAAC;oBACG,YAAY,GAAG,IAAA,2BAAoB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;oBACnD,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;oBACjB,SAAS,GAAG,IAAI,CAAC;gBACrB,CAAC;qBACI,IAAI,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,yBAAW,CAAC,QAAQ,CAAC,EAClE,CAAC;oBACG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBAClG,YAAY,GAAG,IAAA,gBAAS,EAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;oBACvE,SAAS,GAAG,IAAI,CAAA;gBACpB,CAAC;gBACD,IAAI,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAC/G,CAAC;oBACG,QAAQ,GAAG,IAAI,mBAAW,CACtB,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,MAAM,CAAC,SAAS,CAAC,UAAU,EAC3B,KAAK,CACR,CAAC;gBACN,CAAC;YACL,CAAC;YAED,iBAAiB;YACjB,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM;mBACtB,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,yBAAW,CAAC,MAAM,CAAC;mBACvD,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,yBAAW,CAAC,cAAc,CAAC,EAAE,uEAAuE;aAChJ,CAAC;gBACG,MAAM,YAAY,GAAG,IAAA,+BAAsB,EAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;gBAEzE,QAAQ,GAAG,IAAI,mBAAW,CACtB,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,MAAM,CAAC,WAAW,CAAC,UAAU,EAC7B,IAAI,CACP,CAAC;YACN,CAAC;YAED,IAAI,QAAQ,IAAI,IAAI,EAAE,sCAAsC;aAC5D,CAAC;gBACG,qBAAqB;gBACrB,IAAI,IAAA,qBAAa,EAAC,IAAI,EAAE,MAAM,CAAC,EAC/B,CAAC;oBACG,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,cAAc,CAAC;oBACxC,IAAI,MAAM,CAAC,kBAAkB;wBAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,sCAAsC,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;gBAClK,CAAC;gBACD,IAAI,IAAA,qBAAa,EAAC,IAAI,EAAE,MAAM,CAAC,EAC/B,CAAC;oBACG,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,CAAC;oBACrC,IAAI,MAAM,CAAC,kBAAkB;wBAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,kCAAkC,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;gBAC9J,CAAC;gBAED,iBAAiB;gBACjB,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,MAAM,CAAC,aAAa;oBAAE,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBAEpG,iBAAiB;gBACjB,MAAM,MAAM,GAAG,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACtE,MAAM,KAAK,GAAG,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACrE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;oBAAE,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC;gBAC7C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;oBAAE,QAAQ,CAAC,KAAK,IAAI,KAAK,CAAC;gBAE3C,MAAM,KAAK,GAAG,IAAA,wBAAgB,EAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;gBACvE,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,IAAI,EAC/B,CAAC;oBACG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBACrC,CAAC;qBAED,CAAC;oBACG,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACvC,CAAC;YAEL,CAAC;QAEL,CAAC;IAEL,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM;QAAE,IAAA,uBAAY,EAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAEhF,IAAI,MAAM,CAAC,WAAW,CAAC,6BAA6B;QAAE,IAAA,2BAAkB,EAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAEhG,IAAI,MAAM,CAAC,WAAW,CAAC,wBAAwB,IAAI,CAAC;QAAE,IAAA,oCAA2B,EAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAEzG,eAAe;IACf,KAAK,MAAM,cAAc,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EACxD,CAAC;QACG,IAAI,cAAc,IAAI,IAAI,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC;YAAE,SAAS;QACnE,KAAK,MAAM,WAAW,IAAI,cAAc,EACxC,CAAC;YACG,IAAI,OAAO,GAAY,MAAM,CAAC,eAAe,CAAC;YAC9C,IAAI,MAAM,CAAC,+BAA+B,IAAI,IAAA,qBAAa,EAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC;gBAAE,OAAO,GAAG,IAAI,CAAC;YACnH,IAAI,WAAW,CAAC,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,qBAAqB,IAAA,wBAAgB,EAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,KAAK,GAAG,CAAC,CAAC;YAClN,IAAA,kBAAU,EAAC,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtF,CAAC;IACL,CAAC;IAED,WAAW;IACX,KAAK,MAAM,MAAM,IAAI,OAAO,EAC5B,CAAC;QACG,MAAM,aAAa,GAAG,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC;QACxD,IAAI,aAAa,IAAI,IAAI;YAAE,SAAS;QAEpC,MAAM,YAAY,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC;QAC3C,IAAI,YAAY,IAAI,IAAI;YAAE,SAAS;QAEnC,IAAI,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAE9D,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EACtC,CAAC;YACG,IAAI,QAAQ,GAAkB,IAAI,CAAC;YACnC,QAAQ,GAAG,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC3D,IAAI,QAAQ,IAAI,IAAI;gBAAE,QAAQ,GAAG,MAAM,CAAC,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjF,IAAI,QAAQ,IAAI,IAAI;gBAAE,SAAS;YAE/B,IAAA,kBAAU,EAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QACnE,CAAC;IACL,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,MAAM;QAAE,IAAA,0BAAmB,EAAC,OAAO,CAAC,CAAC;AAE5E,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.ts new file mode 100644 index 0000000..10c9c06 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/core.ts @@ -0,0 +1,166 @@ +import { ItemHelper } from "@spt/helpers/ItemHelper"; +import { Context } from "../contex"; +import { calculateAmmoLoyalty, rebalanceAmmoCrafts, scoreAmmo } from "./ammo"; +import { calculateWeaponLoyalty, followDefaultBuild, penalizeAdvancedAttachments, weaponShifting as shiftWeapons } from "./weapon"; +import { isBarterTrade, isQuestLocked, loyaltyFromScore, setLoyalty } from "../utils"; +import { BaseClasses } from "@spt/models/enums/BaseClasses"; +import { ChangedItem } from "./types"; + +export function algorithmicallyRebalance(context: Context): void +{ + //Alias + const config = context.config.algorithmicalRebalancing; + + const itemHelper: ItemHelper = context.itemHelper; + const traders = Object.values(context.tables.traders); + + //Organize changed items by loyalty level so that they can be easily accessed by the shifting system + //[trade, loyaltyScore, trader, logChanges][index][loyaltyLevel] + const changedItems: { [level: number] : ChangedItem[] } = {}; + + //Loop over each trader + for (const trader of traders) + { + const loyaltyLevels = trader?.assort?.loyal_level_items; + if (loyaltyLevels == null) continue; + + const itemsForSale = trader?.assort?.items; + if (itemsForSale == null) continue; + + if (config.excludeTraders.includes(trader.base._id)) continue; + + for (const item of itemsForSale) + { + if (Object.keys(config.explicitLoyaltyOverride.trades).includes(item._id)) continue; + if (Object.keys(config.explicitLoyaltyOverride.items).includes(item._tpl)) continue; + let thisItem: ChangedItem; + + //// AMMO //// + if (config.ammoRules.enable) + { + let ammoOrBox = false; + let ammo; + let loyaltyScore; + if (itemHelper.isOfBaseclass(item._tpl, BaseClasses.AMMO)) + { + loyaltyScore = calculateAmmoLoyalty(item, context); + ammo = item._tpl; + ammoOrBox = true; + } + else if (itemHelper.isOfBaseclass(item._tpl, BaseClasses.AMMO_BOX)) + { + ammo = context.tables.templates.items[item._tpl]._props.StackSlots[0]._props.filters[0].Filter[0]; + loyaltyScore = scoreAmmo(context.tables.templates.items[ammo], context) + ammoOrBox = true + } + if (ammoOrBox && !config.ammoRules.ignoreCalibers.includes(context.tables.templates.items[ammo]._props.Caliber)) + { + thisItem = new ChangedItem( + item, + loyaltyScore, + trader, + config.ammoRules.logChanges, + false + ); + } + } + + //// WEAPONS //// + if (config.weaponRules.enable + && itemHelper.isOfBaseclass(item._tpl, BaseClasses.WEAPON) + && !itemHelper.isOfBaseclass(item._tpl, BaseClasses.SPECIAL_WEAPON)) //ToDo: double check melee and grenades (and possibly other exceptions) + { + const loyaltyScore = calculateWeaponLoyalty(item, itemsForSale, context); + + thisItem = new ChangedItem( + item, + loyaltyScore, + trader, + config.weaponRules.logChanges, + true + ); + } + + if (thisItem != null) //If item is being affected by the mod + { + //Final modifications + if (isQuestLocked(item, trader)) + { + thisItem.score += config.questLockDelta; + if (config.logBartersAndLocks) context.logger.info(context.tables.templates.items[item._tpl]._name + " is a quest-locked item\t(Trade ID: " + item._id + ")"); + } + if (isBarterTrade(item, trader)) + { + thisItem.score += config.barterDelta; + if (config.logBartersAndLocks) context.logger.info(context.tables.templates.items[item._tpl]._name + " is a bartered item\t(Trade ID: " + item._id + ")"); + } + + //By-trader delta + if (trader.base._id in config.deltaByTrader) thisItem.score += config.deltaByTrader[trader.base._id] + + //Explicit deltas + const tradeD = config.explicitLoyaltyDelta.trades[thisItem.trade._id]; + const itemD = config.explicitLoyaltyDelta.items[thisItem.trade._tpl]; + if (!isNaN(tradeD)) thisItem.score += tradeD; + if (!isNaN(itemD)) thisItem.score += itemD; + + const level = loyaltyFromScore(thisItem.score, config.clampToMaxLevel); + if (changedItems[level] == null) + { + changedItems[level] = [thisItem]; + } + else + { + changedItems[level].push(thisItem); + } + + } + + } + + } + + if (config.weaponRules.upshiftRules.enable) shiftWeapons(changedItems, context); + + if (config.weaponRules.attachmentsFollowDefaultBuild) followDefaultBuild(changedItems, context); + + if (config.weaponRules.advancedAttachmentsDelta != 0) penalizeAdvancedAttachments(changedItems, context); + + //Apply changes + for (const changesInLevel of Object.values(changedItems)) + { + if (changesInLevel == null || changesInLevel.length == 0) continue; + for (const changedItem of changesInLevel) + { + let doClamp: boolean = config.clampToMaxLevel; + if (config.forceClampingOfQuestlockedItems && isQuestLocked(changedItem.trade, changedItem.trader)) doClamp = true; + if (changedItem.logChange) context.logger.info(`Setting ${context.tables.templates.items[changedItem.trade._tpl]._name} at loyalty level ${loyaltyFromScore(changedItem.score, doClamp)} (${changedItem.score})`); + setLoyalty(changedItem.trade._id, changedItem.score, changedItem.trader, doClamp); + } + } + + //Overrides + for (const trader of traders) + { + const loyaltyLevels = trader?.assort?.loyal_level_items; + if (loyaltyLevels == null) continue; + + const itemsForSale = trader?.assort?.items; + if (itemsForSale == null) continue; + + if (config.excludeTraders.includes(trader.base._id)) continue; + + for (const item of trader.assort.items) + { + let override: number | null = null; + override = config.explicitLoyaltyOverride.trades[item._id]; + if (override == null) override = config.explicitLoyaltyOverride.items[item._tpl]; + if (override == null) continue; + + setLoyalty(item._id, override, trader, config.clampToMaxLevel); + } + } + + if (config.ammoRules.craftSettings.enable) rebalanceAmmoCrafts(context); + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.js new file mode 100644 index 0000000..36eb834 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ChangedItem = void 0; +class ChangedItem { + trade; + score; + trader; + logChange; + isWeapon; + constructor(trade, score, trader, logChange, isWeapon) { + this.trade = trade; + this.score = score; + this.trader = trader; + this.logChange = logChange; + this.isWeapon = isWeapon; + } +} +exports.ChangedItem = ChangedItem; +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.js.map new file mode 100644 index 0000000..b826d5c --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "types.js", + "sourceRoot": "", + "sources": [ + "types.ts" + ], + "names": [], + "mappings": ";;;AAGA,MAAa,WAAW;IAEpB,KAAK,CAAQ;IAAC,KAAK,CAAS;IAAC,MAAM,CAAU;IAAC,SAAS,CAAU;IAAC,QAAQ,CAAU;IAEpF,YAAY,KAAY,EAAE,KAAa,EAAE,MAAe,EAAE,SAAkB,EAAE,QAAiB;QAE3F,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AAZD,kCAYC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.ts new file mode 100644 index 0000000..470e9d5 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/types.ts @@ -0,0 +1,16 @@ +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ITrader } from "@spt/models/eft/common/tables/ITrader"; + +export class ChangedItem +{ + trade: IItem; score: number; trader: ITrader; logChange: boolean; isWeapon: boolean; + + constructor(trade: IItem, score: number, trader: ITrader, logChange: boolean, isWeapon: boolean) + { + this.trade = trade; + this.score = score; + this.trader = trader; + this.logChange = logChange; + this.isWeapon = isWeapon; + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.js new file mode 100644 index 0000000..aea5855 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.js @@ -0,0 +1,141 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.calculateWeaponLoyalty = calculateWeaponLoyalty; +exports.followDefaultBuild = followDefaultBuild; +exports.penalizeAdvancedAttachments = penalizeAdvancedAttachments; +exports.weaponShifting = weaponShifting; +const utils_1 = require("../utils"); +function calculateWeaponLoyalty(item, assort, context) { + const config = context.config.algorithmicalRebalancing.weaponRules; + const tables = context.tables; + const itemTemplate = tables.templates.items[item._tpl]; + const fireMode = (0, utils_1.bestFiremode)(itemTemplate); + const fireRate = itemTemplate._props.bFirerate; + let loyalty = config.defaultBaseLoyalty; + //Base loyalty level based on caliber + for (const byCaliber of config.weaponBaseLoyaltyByCaliber) { + if (byCaliber.caliber == itemTemplate._props.ammoCaliber) + loyalty = byCaliber.baseLoyalty; + } + //Account for best available fire mode + for (const byMode of config.fireModeRules) { + if (byMode.mode == fireMode) + loyalty += byMode.delta; + } + //Account for fire rate if applicable + if (fireMode == "fullauto" || fireMode == "burst") + for (const byRate of config.fireRateRules) { + if (fireRate >= byRate.rateInterval[0] && fireRate < byRate.rateInterval[1]) + loyalty += byRate.delta; + } + return loyalty + config.globalDelta; +} +//Move attachments that are part of some default build to the same tier of the weapon their are part of the build for +function followDefaultBuild(changedItems, context) { + const changesById = (0, utils_1.indexById)(changedItems); + for (const weaponChange of Object.values(changesById)) { + if (!weaponChange.isWeapon) + continue; + const parts = (0, utils_1.getDefaultAttachments)(weaponChange.trade._tpl, context); + for (const part of parts) { + const level = (0, utils_1.loyaltyFromScore)(weaponChange.score, context.config.algorithmicalRebalancing.clampToMaxLevel); + const partTrades = (0, utils_1.findTrades)(part, context); + for (const partTrade of partTrades) { + const oldPartChange = changesById[partTrade.trade._id]; + if (oldPartChange != null) { + const oldLevel = (0, utils_1.loyaltyFromScore)(oldPartChange.score, context.config.algorithmicalRebalancing.clampToMaxLevel); + if (oldLevel <= level) { + continue; + } + else { + changedItems[oldLevel] = changedItems[oldLevel].filter((item) => item.trade._id != partTrade.trade._id); + } + } + changedItems[level].push({ + trade: partTrade.trade, + score: weaponChange.score, + trader: partTrade.trader, + logChange: false, + isWeapon: false + }); + } + } + } +} +function penalizeAdvancedAttachments(changedItems, context) { + const config = context.config.algorithmicalRebalancing; + const toPenalize = []; + for (const [tier, changes] of Object.entries(changedItems)) + for (const change of changes) { + if (!change.isWeapon) + continue; + if (!(0, utils_1.canAllAttachmentsBePurchased)(change.trade, change.trader.assort.items, true, true, Number(tier), (0, utils_1.getDefaultAttachments)(change.trade._tpl, context), (0, utils_1.indexById)(changedItems), context)) { + toPenalize.push([change, Number(tier)]); + //context.logger.info(`Penalizing ${context.tables.templates.items[change.trade._tpl]._name}`); + } + } + for (const [change, tier] of toPenalize) { + changedItems[tier] = changedItems[tier].filter((item) => item.trade._id != change.trade._id); + change.score += config.weaponRules.advancedAttachmentsDelta; + const newLevel = (0, utils_1.loyaltyFromScore)(change.score, config.clampToMaxLevel); + if (changedItems[newLevel] == null) { + changedItems[newLevel] = [change]; + } + else { + changedItems[newLevel].push(change); + } + } +} +function weaponShifting(changedItems, context) { + const config = context.config.algorithmicalRebalancing; + const reverse = config.weaponRules.upshiftRules.shiftDownInstead; + //Shifting system + //WARNING!!! HORRIBLE CODE AHEAD!!! + const keys = Object.keys(changedItems).sort(); + for (let i = 0; i < keys.length; i++) { + const index = reverse ? keys.length - i - 1 : i; //Reverse order + const changesInLevel = changedItems[keys[index]]; + if (changesInLevel == null || changesInLevel.length == 0) + continue; + const toShift = new Set(); + for (let i = 0; i < changesInLevel.length; i++) { + if (toShift.has(i)) + continue; + if (!changesInLevel[i].isWeapon) + continue; + for (let j = i + 1; j < changesInLevel.length; j++) { + if (toShift.has(j)) + continue; + if (!changesInLevel[j].isWeapon) + continue; + const a = changesInLevel[i]; + const b = changesInLevel[j]; + if ((0, utils_1.shareSameNiche)(a.trade, b.trade, a.trader, b.trader, context)) { + const aPowerLevel = config.weaponRules.upshiftRules.powerLevels[a.trade._tpl]; + const bPowerLevel = config.weaponRules.upshiftRules.powerLevels[b.trade._tpl]; + if (aPowerLevel == null || bPowerLevel == null || aPowerLevel == bPowerLevel) + continue; + if (aPowerLevel < bPowerLevel) { + toShift.add(reverse ? i : j); + } + else { + toShift.add(reverse ? j : i); + } + } + } + } + for (const shiftIndex of toShift) { + const change = changesInLevel[shiftIndex]; + change.score += config.weaponRules.upshiftRules.shiftAmount * (reverse ? -1 : 1); + const newLevel = (0, utils_1.loyaltyFromScore)(change.score, config.clampToMaxLevel); + changedItems[keys[index]] = changedItems[keys[index]].filter(item => item.trade._id != change.trade._id); + if (changedItems[newLevel] == null) { + changedItems[newLevel] = [change]; + } + else { + changedItems[newLevel].push(change); + } + } + } +} +//# sourceMappingURL=weapon.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.js.map new file mode 100644 index 0000000..2429eb3 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "weapon.js", + "sourceRoot": "", + "sources": [ + "weapon.ts" + ], + "names": [], + "mappings": ";;AAMA,wDA6BC;AAGD,gDAyCC;AAED,kEAiCC;AAED,wCAkEC;AAnLD,oCAAsJ;AAGtJ,SAAgB,sBAAsB,CAAC,IAAW,EAAE,MAAe,EAAE,OAAgB;IAEjF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,WAAW,CAAC;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE9B,MAAM,YAAY,GAAkB,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,MAAM,QAAQ,GAAG,IAAA,oBAAY,EAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC;IAC/C,IAAI,OAAO,GAAW,MAAM,CAAC,kBAAkB,CAAC;IAEhD,qCAAqC;IACrC,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,0BAA0B,EACzD,CAAC;QACG,IAAI,SAAS,CAAC,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,WAAW;YAAE,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC;IAC9F,CAAC;IAED,sCAAsC;IACtC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,aAAa,EACzC,CAAC;QACG,IAAI,MAAM,CAAC,IAAI,IAAI,QAAQ;YAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC;IACzD,CAAC;IAED,qCAAqC;IACrC,IAAI,QAAQ,IAAI,UAAU,IAAI,QAAQ,IAAI,OAAO;QAAE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,aAAa,EAC5F,CAAC;YACG,IAAI,QAAQ,IAAI,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;gBAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC;QACzG,CAAC;IAED,OAAO,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC;AACxC,CAAC;AAED,qHAAqH;AACrH,SAAgB,kBAAkB,CAAC,YAA2C,EAAE,OAAgB;IAG5F,MAAM,WAAW,GAAG,IAAA,iBAAS,EAAC,YAAY,CAAC,CAAC;IAE5C,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EACrD,CAAC;QACG,IAAI,CAAC,YAAY,CAAC,QAAQ;YAAE,SAAS;QAErC,MAAM,KAAK,GAAG,IAAA,6BAAqB,EAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEtE,KAAK,MAAM,IAAI,IAAI,KAAK,EACxB,CAAC;YACG,MAAM,KAAK,GAAG,IAAA,wBAAgB,EAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,eAAe,CAAC,CAAC;YAC5G,MAAM,UAAU,GAAG,IAAA,kBAAU,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAE7C,KAAK,MAAM,SAAS,IAAI,UAAU,EAClC,CAAC;gBACG,MAAM,aAAa,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACvD,IAAI,aAAa,IAAI,IAAI,EACzB,CAAC;oBACG,MAAM,QAAQ,GAAG,IAAA,wBAAgB,EAAC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,eAAe,CAAC,CAAC;oBAChH,IAAI,QAAQ,IAAI,KAAK,EACrB,CAAC;wBACG,SAAS;oBACb,CAAC;yBAED,CAAC;wBACG,YAAY,CAAC,QAAQ,CAAC,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAC5G,CAAC;gBACL,CAAC;gBACD,YAAY,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;oBACrB,KAAK,EAAE,SAAS,CAAC,KAAK;oBACtB,KAAK,EAAE,YAAY,CAAC,KAAK;oBACzB,MAAM,EAAE,SAAS,CAAC,MAAM;oBACxB,SAAS,EAAE,KAAK;oBAChB,QAAQ,EAAE,KAAK;iBAClB,CAAC,CAAA;YACN,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC;AAED,SAAgB,2BAA2B,CAAC,YAA2C,EAAE,OAAgB;IAErG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC;IACvD,MAAM,UAAU,GAA4B,EAAE,CAAC;IAE/C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC;QAAE,KAAK,MAAM,MAAM,IAAI,OAAO,EACxF,CAAC;YACG,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAAE,SAAS;YAE/B,IAAI,CAAC,IAAA,oCAA4B,EAC7B,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAClE,IAAA,6BAAqB,EAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAA,iBAAS,EAAC,YAAY,CAAC,EAC1E,OAAO,CAAC,EACZ,CAAC;gBACG,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACxC,+FAA+F;YACnG,CAAC;QACL,CAAC;IAED,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,UAAU,EACvC,CAAC;QACG,YAAY,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7F,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,CAAC,wBAAwB,CAAC;QAC5D,MAAM,QAAQ,GAAG,IAAA,wBAAgB,EAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QACxE,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,IAAI,EAClC,CAAC;YACG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;aAED,CAAC;YACG,YAAY,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;IACL,CAAC;AACL,CAAC;AAED,SAAgB,cAAc,CAAC,YAAiD,EAAE,OAAgB;IAG9F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC;IACvD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,gBAAgB,CAAC;IAEjE,iBAAiB;IACjB,mCAAmC;IACnC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EACpC,CAAC;QACG,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe;QAChE,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAEjD,IAAI,cAAc,IAAI,IAAI,IAAI,cAAc,CAAC,MAAM,IAAI,CAAC;YAAE,SAAS;QAEnE,MAAM,OAAO,GAAgB,IAAI,GAAG,EAAU,CAAC;QAE/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAC9C,CAAC;YACG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,SAAS;YAC7B,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,QAAQ;gBAAE,SAAS;YAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAClD,CAAC;gBACG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,SAAS;gBAC7B,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,QAAQ;oBAAE,SAAS;gBAE1C,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;gBAAC,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;gBAEzD,IAAI,IAAA,sBAAc,EAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,CAAC;oBACG,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC9E,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAE9E,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,WAAW;wBAAE,SAAS;oBAEvF,IAAI,WAAW,GAAG,WAAW,EAC7B,CAAC;wBACG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACjC,CAAC;yBAED,CAAC;wBACG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBACjC,CAAC;gBACL,CAAC;YAEL,CAAC;QACL,CAAC;QAED,KAAK,MAAM,UAAU,IAAI,OAAO,EAChC,CAAC;YACG,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAC1C,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,WAAW,CAAC,YAAY,CAAC,WAAW,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjF,MAAM,QAAQ,GAAG,IAAA,wBAAgB,EAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;YACxE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACzG,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,IAAI,EAClC,CAAC;gBACG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,CAAC;iBAED,CAAC;gBACG,YAAY,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxC,CAAC;QACL,CAAC;IAEL,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.ts new file mode 100644 index 0000000..40c22cf --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/algoRebalancing/weapon.ts @@ -0,0 +1,183 @@ +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem"; +import { Context } from "../contex"; +import { bestFiremode, canAllAttachmentsBePurchased, findTrades, getDefaultAttachments, indexById, loyaltyFromScore, shareSameNiche } from "../utils"; +import { ChangedItem } from "./types"; + +export function calculateWeaponLoyalty(item: IItem, assort: IItem[], context: Context): number +{ + const config = context.config.algorithmicalRebalancing.weaponRules; + const tables = context.tables; + + const itemTemplate: ITemplateItem = tables.templates.items[item._tpl]; + const fireMode = bestFiremode(itemTemplate); + const fireRate = itemTemplate._props.bFirerate; + let loyalty: number = config.defaultBaseLoyalty; + + //Base loyalty level based on caliber + for (const byCaliber of config.weaponBaseLoyaltyByCaliber) + { + if (byCaliber.caliber == itemTemplate._props.ammoCaliber) loyalty = byCaliber.baseLoyalty; + } + + //Account for best available fire mode + for (const byMode of config.fireModeRules) + { + if (byMode.mode == fireMode) loyalty += byMode.delta; + } + + //Account for fire rate if applicable + if (fireMode == "fullauto" || fireMode == "burst") for (const byRate of config.fireRateRules) + { + if (fireRate >= byRate.rateInterval[0] && fireRate < byRate.rateInterval[1]) loyalty += byRate.delta; + } + + return loyalty + config.globalDelta; +} + +//Move attachments that are part of some default build to the same tier of the weapon their are part of the build for +export function followDefaultBuild(changedItems: Record, context: Context): void +{ + + const changesById = indexById(changedItems); + + for (const weaponChange of Object.values(changesById)) + { + if (!weaponChange.isWeapon) continue; + + const parts = getDefaultAttachments(weaponChange.trade._tpl, context); + + for (const part of parts) + { + const level = loyaltyFromScore(weaponChange.score, context.config.algorithmicalRebalancing.clampToMaxLevel); + const partTrades = findTrades(part, context); + + for (const partTrade of partTrades) + { + const oldPartChange = changesById[partTrade.trade._id]; + if (oldPartChange != null) + { + const oldLevel = loyaltyFromScore(oldPartChange.score, context.config.algorithmicalRebalancing.clampToMaxLevel); + if (oldLevel <= level) + { + continue; + } + else + { + changedItems[oldLevel] = changedItems[oldLevel].filter((item) => item.trade._id != partTrade.trade._id); + } + } + changedItems[level].push({ + trade: partTrade.trade, + score: weaponChange.score, + trader: partTrade.trader, + logChange: false, + isWeapon: false + }) + } + } + } +} + +export function penalizeAdvancedAttachments(changedItems: Record, context: Context): void +{ + const config = context.config.algorithmicalRebalancing; + const toPenalize: [ChangedItem, number][] = []; + + for (const [tier, changes] of Object.entries(changedItems)) for (const change of changes) + { + if (!change.isWeapon) continue; + + if (!canAllAttachmentsBePurchased( + change.trade, change.trader.assort.items, true, true, Number(tier), + getDefaultAttachments(change.trade._tpl, context), indexById(changedItems), + context)) + { + toPenalize.push([change, Number(tier)]); + //context.logger.info(`Penalizing ${context.tables.templates.items[change.trade._tpl]._name}`); + } + } + + for (const [change, tier] of toPenalize) + { + changedItems[tier] = changedItems[tier].filter((item) => item.trade._id != change.trade._id); + change.score += config.weaponRules.advancedAttachmentsDelta; + const newLevel = loyaltyFromScore(change.score, config.clampToMaxLevel); + if (changedItems[newLevel] == null) + { + changedItems[newLevel] = [change]; + } + else + { + changedItems[newLevel].push(change); + } + } +} + +export function weaponShifting(changedItems: { [level: number] : ChangedItem[] }, context: Context): void +{ + + const config = context.config.algorithmicalRebalancing; + const reverse = config.weaponRules.upshiftRules.shiftDownInstead; + + //Shifting system + //WARNING!!! HORRIBLE CODE AHEAD!!! + const keys = Object.keys(changedItems).sort(); + for (let i = 0; i < keys.length; i++) + { + const index = reverse ? keys.length - i - 1 : i; //Reverse order + const changesInLevel = changedItems[keys[index]]; + + if (changesInLevel == null || changesInLevel.length == 0) continue; + + const toShift: Set = new Set(); + + for (let i = 0; i < changesInLevel.length; i++) + { + if (toShift.has(i)) continue; + if (!changesInLevel[i].isWeapon) continue; + for (let j = i + 1; j < changesInLevel.length; j++) + { + if (toShift.has(j)) continue; + if (!changesInLevel[j].isWeapon) continue; + + const a = changesInLevel[i]; const b = changesInLevel[j]; + + if (shareSameNiche(a.trade, b.trade, a.trader, b.trader, context)) + { + const aPowerLevel = config.weaponRules.upshiftRules.powerLevels[a.trade._tpl]; + const bPowerLevel = config.weaponRules.upshiftRules.powerLevels[b.trade._tpl]; + + if (aPowerLevel == null || bPowerLevel == null || aPowerLevel == bPowerLevel) continue; + + if (aPowerLevel < bPowerLevel) + { + toShift.add(reverse ? i : j); + } + else + { + toShift.add(reverse ? j : i); + } + } + + } + } + + for (const shiftIndex of toShift) + { + const change = changesInLevel[shiftIndex]; + change.score += config.weaponRules.upshiftRules.shiftAmount * (reverse ? -1 : 1); + const newLevel = loyaltyFromScore(change.score, config.clampToMaxLevel); + changedItems[keys[index]] = changedItems[keys[index]].filter(item => item.trade._id != change.trade._id); + if (changedItems[newLevel] == null) + { + changedItems[newLevel] = [change]; + } + else + { + changedItems[newLevel].push(change); + } + } + + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.js new file mode 100644 index 0000000..6299831 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Context = void 0; +class Context { + database; + tables; + logger; + itemHelper; + presetHelper; + sptConfig; + hashUtil; + config; + constructor(_config, /*Not sure how to type this, dynamically type checked languages are weird*/ _database, _tables, _logger, _itemHelper, _preetHelper, _sptConfig, _hashUtil) { + this.database = _database; + this.tables = _tables; + this.logger = _logger; + this.config = _config; + this.itemHelper = _itemHelper; + this.presetHelper = _preetHelper; + this.sptConfig = _sptConfig; + this.hashUtil = _hashUtil; + } + logObject(object) { + this.logger?.info(JSON.stringify(object, null, 2)); + } +} +exports.Context = Context; +//# sourceMappingURL=contex.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.js.map new file mode 100644 index 0000000..57d13a8 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "contex.js", + "sourceRoot": "", + "sources": [ + "contex.ts" + ], + "names": [], + "mappings": ";;;AAQA,MAAa,OAAO;IAGT,QAAQ,CAAiB;IACzB,MAAM,CAAkB;IACxB,MAAM,CAAgB;IACtB,UAAU,CAAa;IACvB,YAAY,CAAe;IAC3B,SAAS,CAAe;IACxB,QAAQ,CAAW;IACnB,MAAM,CAAC;IAEd,YACI,OAAa,EAAE,2EAA2E,CAC1F,SAA0B,EAAE,OAAyB,EAAE,OAAuB,EAAE,WAAwB,EAAE,YAA2B,EAAE,UAAyB,EAAE,SAAoB;QAGtL,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QACtB,IAAI,CAAC,UAAU,GAAG,WAAW,CAAC;QAC9B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC9B,CAAC;IAEM,SAAS,CAAC,MAAW;QAExB,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;CAEJ;AAhCD,0BAgCC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.ts new file mode 100644 index 0000000..56a9b4b --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/contex.ts @@ -0,0 +1,41 @@ +import { ItemHelper } from "@spt/helpers/ItemHelper"; +import { IDatabaseTables } from "@spt/models/spt/server/IDatabaseTables"; +import { DatabaseServer } from "@spt/servers/DatabaseServer"; +import { LoggerWrapper } from "./loggerWrapper"; +import { PresetHelper } from "@spt/helpers/PresetHelper"; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { HashUtil } from "@spt/utils/HashUtil"; + +export class Context +{ + + public database: DatabaseServer; + public tables: IDatabaseTables; + public logger: LoggerWrapper; + public itemHelper: ItemHelper; + public presetHelper: PresetHelper; + public sptConfig: ConfigServer; + public hashUtil: HashUtil; + public config; + + constructor( + _config?: any, /*Not sure how to type this, dynamically type checked languages are weird*/ + _database?: DatabaseServer, _tables?: IDatabaseTables, _logger?: LoggerWrapper, _itemHelper?: ItemHelper, _preetHelper?: PresetHelper, _sptConfig?: ConfigServer, _hashUtil?: HashUtil + ) + { + this.database = _database; + this.tables = _tables; + this.logger = _logger; + this.config = _config; + this.itemHelper = _itemHelper; + this.presetHelper = _preetHelper; + this.sptConfig = _sptConfig; + this.hashUtil = _hashUtil; + } + + public logObject(object: any): void + { + this.logger?.info(JSON.stringify(object, null, 2)); + } + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.js new file mode 100644 index 0000000..858211a --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.js @@ -0,0 +1,44 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addNewItems = addNewItems; +const fs_1 = __importDefault(require("fs")); +// eslint-disable-next-line @typescript-eslint/naming-convention +const json5_1 = __importDefault(require("C:/snapshot/project/node_modules/json5")); +const utils_1 = require("../utils"); +function addNewItems(context) { + //Read custom items config + const fileContent = fs_1.default.readFileSync((0, utils_1.getModFolder)() + "/config/advanced/customItems.json5", "utf-8"); + const itemsConfig = json5_1.default.parse(fileContent); + const buffs = context.tables.globals.config.Health.Effects.Stimulator.Buffs; + const items = context.tables.templates.items; + const locales = context.tables.locales.global; + //Add the buffs + for (const [buff, effects] of Object.entries(itemsConfig.customBuffs)) { + buffs[buff] = effects; + } + //Add the items + for (const [id, item] of Object.entries(itemsConfig.customItems)) { + items[id] = item; + } + //Add the locales + for (const [item, locale] of Object.entries(itemsConfig.customLocales)) { + (0, utils_1.addToLocale)(locales, item, locale.Name, locale.ShortName, locale.Description); + } + //Add the trades + for (const [traderID, additions] of Object.entries(itemsConfig.customTrades)) { + const trader = context.tables.traders[traderID]; + for (const trade of additions.items) { + trader.assort.items.push(trade); + } + for (const [trade, scheme] of Object.entries(additions.barter_scheme)) { + trader.assort.barter_scheme[trade] = scheme; + } + for (const [trade, loyalty] of Object.entries(additions.loyal_level_items)) { + trader.assort.loyal_level_items[trade] = loyalty; + } + } +} +//# sourceMappingURL=items.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.js.map new file mode 100644 index 0000000..6fb5c6d --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "items.js", + "sourceRoot": "", + "sources": [ + "items.ts" + ], + "names": [], + "mappings": ";;;;;AASA,kCAsDC;AA/DD,4CAAoB;AACpB,gEAAgE;AAChE,mFAA2D;AAE3D,oCAAqD;AAKrD,SAAgB,WAAW,CAAC,OAAgB;IAExC,0BAA0B;IAC1B,MAAM,WAAW,GAAG,YAAE,CAAC,YAAY,CAAC,IAAA,oBAAY,GAAE,GAAG,oCAAoC,EAAE,OAAO,CAAC,CAAC;IACpG,MAAM,WAAW,GAAG,eAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;IAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAE9C,eAAe;IACf,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,EACrE,CAAC;QACG,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,eAAe;IACf,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,WAA4C,CAAC,EACjG,CAAC;QACG,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,iBAAiB;IACjB,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,aAAuF,CAAC,EAChJ,CAAC;QACG,IAAA,mBAAW,EAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;IACjF,CAAC;IAED,gBAAgB;IAChB,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAC5B,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,YAItB,CACJ,EAEL,CAAC;QACG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAChD,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,KAAK,EACnC,CAAC;YACG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC,EACrE,CAAC;YACG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAChD,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAC1E,CAAC;YACG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;QACrD,CAAC;IACL,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.ts new file mode 100644 index 0000000..0338ee9 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/customItems/items.ts @@ -0,0 +1,64 @@ +import fs from "fs"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import JSON5 from "json5"; +import { Context } from "../contex"; +import { addToLocale, getModFolder } from "../utils"; +import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem"; +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { IBarterScheme } from "@spt/models/eft/common/tables/ITrader"; + +export function addNewItems(context: Context): void +{ + //Read custom items config + const fileContent = fs.readFileSync(getModFolder() + "/config/advanced/customItems.json5", "utf-8"); + const itemsConfig = JSON5.parse(fileContent); + + const buffs = context.tables.globals.config.Health.Effects.Stimulator.Buffs; + const items = context.tables.templates.items; + const locales = context.tables.locales.global; + + //Add the buffs + for (const [buff, effects] of Object.entries(itemsConfig.customBuffs)) + { + buffs[buff] = effects; + } + + //Add the items + for (const [id, item] of Object.entries(itemsConfig.customItems as Record)) + { + items[id] = item; + } + + //Add the locales + for (const [item, locale] of Object.entries(itemsConfig.customLocales as Record)) + { + addToLocale(locales, item, locale.Name, locale.ShortName, locale.Description) + } + + //Add the trades + for (const [traderID, additions] of + Object.entries(itemsConfig.customTrades as + Record< + // eslint-disable-next-line @typescript-eslint/naming-convention + string, {items: IItem[], barter_scheme: Record, loyal_level_items: Record} + > + ) + ) + { + const trader = context.tables.traders[traderID]; + for (const trade of additions.items) + { + trader.assort.items.push(trade); + } + + for (const [trade, scheme] of Object.entries(additions.barter_scheme)) + { + trader.assort.barter_scheme[trade] = scheme; + } + + for (const [trade, loyalty] of Object.entries(additions.loyal_level_items)) + { + trader.assort.loyal_level_items[trade] = loyalty; + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.js new file mode 100644 index 0000000..4f34df5 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LoggerWrapper = void 0; +class LoggerWrapper { + prefix; + logger; + modName; + modVersion; + constructor(_logger, preLoader) { + this.logger = _logger; + try { + let modDetails = preLoader.getImportedModDetails()["Gekos_BetterProgression"]; + if (modDetails == null) { + modDetails = preLoader.getImportedModDetails()["drunkgeko-gekos_betterprogression"]; + } + this.modName = modDetails.name; + this.modVersion = modDetails.version; + this.prefix = `[${this.modName}-${this.modVersion}] `; + } + catch (error) { + this.modName = "gekos_betterprogression"; + this.modVersion = "??"; + this.prefix = `[${this.modName}-${this.modVersion}] `; + this.warning("Could not retrieve mod info!"); + this.warning("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + info(message) { + this.logger.info(this.prefix + message); + } + writeToLogFile(data) { + if (typeof data === "string") { + data += this.prefix; + } + this.logger.writeToLogFile(data); + } + log(data, color, backgroundColor) { + if (typeof data === "string") { + data += this.prefix; + } + this.logger.log(data, backgroundColor); + } + logWithColor(data, textColor, backgroundColor) { + if (typeof data === "string") { + data += this.prefix; + } + this.logger.logWithColor(data, textColor, backgroundColor); + } + error(data) { + this.logger.error(`${this.prefix}${data}`); + } + warning(data) { + this.logger.warning(this.prefix + data); + } + success(data) { + this.logger.success(this.prefix + data); + } + debug(data, onlyShowInConsole) { + if (typeof data === "string") { + data += this.prefix; + } + this.logger.debug(data, onlyShowInConsole); + } +} +exports.LoggerWrapper = LoggerWrapper; +//# sourceMappingURL=loggerWrapper.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.js.map new file mode 100644 index 0000000..ee98dfd --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "loggerWrapper.js", + "sourceRoot": "", + "sources": [ + "loggerWrapper.ts" + ], + "names": [], + "mappings": ";;;AAMA,MAAa,aAAa;IAGb,MAAM,CAAS;IACf,MAAM,CAAU;IAChB,OAAO,CAAS;IAChB,UAAU,CAAS;IAE5B,YAAY,OAAgB,EAAE,SAA0B;QAEpD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QAEtB,IACA,CAAC;YACG,IAAI,UAAU,GAAG,SAAS,CAAC,qBAAqB,EAAE,CAAC,yBAAyB,CAAC,CAAC;YAC9E,IAAI,UAAU,IAAI,IAAI,EACtB,CAAC;gBACG,UAAU,GAAG,SAAS,CAAC,qBAAqB,EAAE,CAAC,mCAAmC,CAAC,CAAC;YACxF,CAAC;YAED,IAAI,CAAC,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC;YACrC,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC;QAC1D,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,GAAG,yBAAyB,CAAC;YACzC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;YACvB,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACnG,CAAC;IACL,CAAC;IAEM,IAAI,CAAC,OAAe;QAEvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,cAAc,CAAC,IAAoB;QAE/B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAC5B,CAAC;YACG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,GAAG,CAAC,IAA8C,EAAE,KAAa,EAAE,eAAwB;QAEvF,IAAI,OAAO,IAAI,KAAK,QAAQ,EAC5B,CAAC;YACG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IAC3C,CAAC;IAED,YAAY,CAAC,IAAsC,EAAE,SAAuB,EAAE,eAAoC;QAE9G,IAAI,OAAO,IAAI,KAAK,QAAQ,EAC5B,CAAC;YACG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,IAAY;QAEd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,CAAC,IAAY;QAEhB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,CAAC,IAAY;QAEhB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,IAAsC,EAAE,iBAA2B;QAErE,IAAI,OAAO,IAAI,KAAK,QAAQ,EAC5B,CAAC;YACG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;IAC/C,CAAC;CAEJ;AA1FD,sCA0FC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.ts new file mode 100644 index 0000000..cf3c109 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/loggerWrapper.ts @@ -0,0 +1,97 @@ +import { IDaum } from "@spt/models/eft/itemEvent/IItemEventRouterRequest"; +import { LogBackgroundColor } from "@spt/models/spt/logging/LogBackgroundColor"; +import { LogTextColor } from "@spt/models/spt/logging/LogTextColor"; +import { ILogger } from "@spt/models/spt/utils/ILogger" +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; + +export class LoggerWrapper +{ + + readonly prefix: string; + readonly logger: ILogger; + readonly modName: string; + readonly modVersion: string; + + constructor(_logger: ILogger, preLoader: PreSptModLoader) + { + this.logger = _logger; + + try + { + let modDetails = preLoader.getImportedModDetails()["Gekos_BetterProgression"]; + if (modDetails == null) + { + modDetails = preLoader.getImportedModDetails()["drunkgeko-gekos_betterprogression"]; + } + + this.modName = modDetails.name; + this.modVersion = modDetails.version; + this.prefix = `[${this.modName}-${this.modVersion}] `; + } + catch (error) + { + this.modName = "gekos_betterprogression"; + this.modVersion = "??"; + this.prefix = `[${this.modName}-${this.modVersion}] `; + this.warning("Could not retrieve mod info!"); + this.warning("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + + public info(message: string): void + { + this.logger.info(this.prefix + message); + } + + writeToLogFile(data: string | IDaum): void + { + if (typeof data === "string") + { + data += this.prefix + } + this.logger.writeToLogFile(data); + } + + log(data: string | Record | Error, color: string, backgroundColor?: string): void + { + if (typeof data === "string") + { + data += this.prefix + } + this.logger.log(data, backgroundColor); + } + + logWithColor(data: string | Record, textColor: LogTextColor, backgroundColor?: LogBackgroundColor): void + { + if (typeof data === "string") + { + data += this.prefix + } + this.logger.logWithColor(data, textColor, backgroundColor); + } + + error(data: string): void + { + this.logger.error(`${this.prefix}${data}`); + } + + warning(data: string): void + { + this.logger.warning(this.prefix + data); + } + + success(data: string): void + { + this.logger.success(this.prefix + data); + } + + debug(data: string | Record, onlyShowInConsole?: boolean): void + { + if (typeof data === "string") + { + data += this.prefix + } + this.logger.debug(data, onlyShowInConsole); + } + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/addGPtoPMC.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/addGPtoPMC.js new file mode 100644 index 0000000..177b2ef --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/addGPtoPMC.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addGPtoPMC = addGPtoPMC; +function addGPtoPMC(context) { + //Thanks to AcidPhantasm and his Bosses Have Lega Medals mod for giving me an example on how to implement this feature + //The mod can be found here: https://hub.sp-tarkov.com/files/file/2109-bosses-have-lega-medals/ + const config = context.config.misc.pmcsHaveGPCoins; + const factor = config.GPCoinWeightProportional; + for (const botType in context.tables.bots.types) { + if (!botType.includes("usec") && !botType.includes("bear") && !botType.includes("pmc")) + continue; + if (botType == "exusec" || botType == "infectedpmc") + continue; + const pockets = context.tables.bots.types[botType].inventory.items.Pockets; + let totalWeight = Object.values(pockets).reduce((a, b) => a + b, 0); + pockets["5d235b4d86f7742e017bc88a"] = Math.max(1, totalWeight * factor); + const backpack = context.tables.bots.types[botType].inventory.items.Backpack; + totalWeight = Object.values(backpack).reduce((a, b) => a + b, 0); + backpack["5d235b4d86f7742e017bc88a"] = Math.max(1, totalWeight * factor); + context.logger.info(`weight for ${botType} is ${pockets["5d235b4d86f7742e017bc88a"]}`); + } +} +//# sourceMappingURL=addGPtoPMC.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/addGPtoPMC.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/addGPtoPMC.js.map new file mode 100644 index 0000000..75d576d --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/addGPtoPMC.js.map @@ -0,0 +1 @@ +{"version":3,"file":"addGPtoPMC.js","sourceRoot":"","sources":["addGPtoPMC.ts"],"names":[],"mappings":";;AAEA,gCAyBC;AAzBD,SAAgB,UAAU,CAAC,OAAgB;IAEvC,sHAAsH;IACtH,+FAA+F;IAE/F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,CAAC,wBAAwB,CAAC;IAE/C,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAC/C,CAAC;QACG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACjG,IAAI,OAAO,IAAI,QAAQ,IAAI,OAAO,IAAI,aAAa;YAAE,SAAS;QAE9D,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC;QAC3E,IAAI,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC;QAExE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC7E,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACjE,QAAQ,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,CAAC;QAGzE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,OAAO,OAAO,OAAO,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;AAEL,CAAC"} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.js new file mode 100644 index 0000000..513187d --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeBitcoinFarming = changeBitcoinFarming; +const ItemTpl_1 = require("C:/snapshot/project/obj/models/enums/ItemTpl"); +const utils_1 = require("../utils"); +function changeBitcoinFarming(context) { + const config = context.config.bitcoinChanges; + //Set bitcoin value + if (config.overrideValue) { + context.tables.templates.handbook.Items.find((item) => item.Id === ItemTpl_1.ItemTpl.BARTER_PHYSICAL_BITCOIN).Price = config.value; + } + //Multiply btc production rate + const btcProds = context.tables.hideout.production.recipes.filter((production) => production.endProduct === ItemTpl_1.ItemTpl.BARTER_PHYSICAL_BITCOIN); + for (const p of btcProds) { + p.productionTime = Math.round(p.productionTime / config.btcFarmSpeedMult); + p.productionLimitCount = config.btcCapacity; + } + //Apply boost rate + context.tables.hideout.settings.gpuBoostRate = config.gpuBoostRate; + //Remove all non-barters for GPUs + if (config.cannotBuyGPU) + for (const trader of Object.values(context.tables.traders)) { + if (trader.assort == null) + continue; + trader.assort.items = trader.assort.items.filter(assort => assort._tpl != "57347ca924597744596b4e71" || (0, utils_1.isBarterTrade)(assort, trader)); + } +} +//# sourceMappingURL=bitcoinChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.js.map new file mode 100644 index 0000000..0c78154 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "bitcoinChanges.js", + "sourceRoot": "", + "sources": [ + "bitcoinChanges.ts" + ], + "names": [], + "mappings": ";;AAIA,oDA2BC;AA/BD,0EAAuE;AAEvE,oCAAyC;AAEzC,SAAgB,oBAAoB,CAAC,OAAgB;IAEjD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;IAE7C,mBAAmB;IACnB,IAAI,MAAM,CAAC,aAAa,EACxB,CAAC;QACG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,iBAAO,CAAC,uBAAuB,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC7H,CAAC;IAED,8BAA8B;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,KAAK,iBAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7I,KAAK,MAAM,CAAC,IAAI,QAAQ,EACxB,CAAC;QACG,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;QAC1E,CAAC,CAAC,oBAAoB,GAAG,MAAM,CAAC,WAAW,CAAC;IAChD,CAAC;IAED,kBAAkB;IAClB,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IAEnE,iCAAiC;IACjC,IAAI,MAAM,CAAC,YAAY;QAAE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EACnF,CAAC;YACG,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI;gBAAE,SAAS;YACpC,MAAM,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,0BAA0B,IAAI,IAAA,qBAAa,EAAC,MAAM,EAAE,MAAM,CAAC,CAAE,CAAC;QAC5I,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.ts new file mode 100644 index 0000000..1a9f182 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/bitcoinChanges.ts @@ -0,0 +1,32 @@ +import { ItemTpl } from "@spt/models/enums/ItemTpl"; +import { Context } from "../contex"; +import { isBarterTrade } from "../utils"; + +export function changeBitcoinFarming(context: Context): void +{ + const config = context.config.bitcoinChanges; + + //Set bitcoin value + if (config.overrideValue) + { + context.tables.templates.handbook.Items.find((item) => item.Id === ItemTpl.BARTER_PHYSICAL_BITCOIN).Price = config.value; + } + + //Multiply btc production rate + const btcProds = context.tables.hideout.production.recipes.filter((production) => production.endProduct === ItemTpl.BARTER_PHYSICAL_BITCOIN); + for (const p of btcProds) + { + p.productionTime = Math.round(p.productionTime / config.btcFarmSpeedMult); + p.productionLimitCount = config.btcCapacity; + } + + //Apply boost rate + context.tables.hideout.settings.gpuBoostRate = config.gpuBoostRate; + + //Remove all non-barters for GPUs + if (config.cannotBuyGPU) for (const trader of Object.values(context.tables.traders)) + { + if (trader.assort == null) continue; + trader.assort.items = trader.assort.items.filter(assort => assort._tpl != "57347ca924597744596b4e71" || isBarterTrade(assort, trader) ); + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.js new file mode 100644 index 0000000..1ec93f3 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.js @@ -0,0 +1,19 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.buffSICCCase = buffSICCCase; +const ItemTpl_1 = require("C:/snapshot/project/obj/models/enums/ItemTpl"); +function buffSICCCase(context) { + //Thanks to ODT and his Softcore mod for this part of the code, most of this function is taken from there with appropriate modifications + //Link to the mod: https://hub.sp-tarkov.com/files/file/998-softcore-proper-singleplayer-experience-for-spt/ + const config = context.config.SICCBuffs; + const docsFilter = config.canHoldWhatDocsCan ? context.tables.templates.items?.[ItemTpl_1.ItemTpl.CONTAINER_DOCUMENTS_CASE]._props.Grids?.[0]._props.filters[0].Filter : []; + const siccFilter = context.tables.templates.items?.[ItemTpl_1.ItemTpl.CONTAINER_SICC]._props.Grids?.[0]._props.filters[0].Filter; + const additionalItems = config.additionalWhitelistedItems; + if (!docsFilter || !siccFilter) { + context.logger.warning("HideoutContainers: doSiccCaseBuff: docsFilter or siccFilter not found"); + return; + } + const mergeFilters = [...new Set([...docsFilter, ...siccFilter, ...additionalItems])]; + context.tables.templates.items[ItemTpl_1.ItemTpl.CONTAINER_SICC]._props.Grids[0]._props.filters[0].Filter = mergeFilters; +} +//# sourceMappingURL=buffSICCCase.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.js.map new file mode 100644 index 0000000..30d1052 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "buffSICCCase.js", + "sourceRoot": "", + "sources": [ + "buffSICCCase.ts" + ], + "names": [], + "mappings": ";;AAGA,oCAmBC;AAtBD,0EAAuE;AAGvE,SAAgB,YAAY,CAAC,OAAgB;IAEzC,wIAAwI;IACxI,4GAA4G;IAE5G,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;IAExC,MAAM,UAAU,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,iBAAO,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAClK,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,iBAAO,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACvH,MAAM,eAAe,GAAG,MAAM,CAAC,0BAA0B,CAAC;IAE1D,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,EAAE,CAAC;QAC7B,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,uEAAuE,CAAC,CAAA;QAC/F,OAAM;IACV,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,UAAU,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAAA;IACrF,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,iBAAO,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,YAAY,CAAA;AAElH,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.ts new file mode 100644 index 0000000..b52d9ce --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buffSICCCase.ts @@ -0,0 +1,23 @@ +import { ItemTpl } from "@spt/models/enums/ItemTpl"; +import { Context } from "../contex"; + +export function buffSICCCase(context: Context): void +{ + //Thanks to ODT and his Softcore mod for this part of the code, most of this function is taken from there with appropriate modifications + //Link to the mod: https://hub.sp-tarkov.com/files/file/998-softcore-proper-singleplayer-experience-for-spt/ + + const config = context.config.SICCBuffs; + + const docsFilter = config.canHoldWhatDocsCan ? context.tables.templates.items?.[ItemTpl.CONTAINER_DOCUMENTS_CASE]._props.Grids?.[0]._props.filters[0].Filter : []; + const siccFilter = context.tables.templates.items?.[ItemTpl.CONTAINER_SICC]._props.Grids?.[0]._props.filters[0].Filter; + const additionalItems = config.additionalWhitelistedItems; + + if (!docsFilter || !siccFilter) { + context.logger.warning("HideoutContainers: doSiccCaseBuff: docsFilter or siccFilter not found") + return + } + + const mergeFilters = [...new Set([...docsFilter, ...siccFilter, ...additionalItems])] + context.tables.templates.items[ItemTpl.CONTAINER_SICC]._props.Grids[0]._props.filters[0].Filter = mergeFilters + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.js new file mode 100644 index 0000000..4d3e555 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeHidehoutBuildCosts = changeHidehoutBuildCosts; +const utils_1 = require("../utils"); +function changeHidehoutBuildCosts(context) { + const config = context.config.hideoutBuildsChanges; + const hideoutAreas = context.tables.hideout.areas; + for (const area of hideoutAreas) + for (const stage of Object.values(area.stages)) { + const nonCurrencyReq = stage.requirements.filter((req) => !utils_1.currencies.includes(req.templateId)); + for (const req of nonCurrencyReq) { + if (req.count) { + req.count -= config.threshold; + if (req.count > 0) + req.count *= config.factor; + req.count += config.threshold; + if (config.roundDown) { + req.count = Math.floor(req.count); + } + else { + req.count = Math.ceil(req.count); + } + } + } + } +} +//# sourceMappingURL=buildChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.js.map new file mode 100644 index 0000000..4f787a4 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "buildChanges.js", + "sourceRoot": "", + "sources": [ + "buildChanges.ts" + ], + "names": [], + "mappings": ";;AAGA,4DA0BC;AA5BD,oCAAsC;AAEtC,SAAgB,wBAAwB,CAAC,OAAgB;IAErD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC;IACnD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;IAElD,KAAK,MAAM,IAAI,IAAI,YAAY;QAAE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAC/E,CAAC;YACG,MAAM,cAAc,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,kBAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAA;YAC/F,KAAK,MAAM,GAAG,IAAI,cAAc,EAChC,CAAC;gBACG,IAAI,GAAG,CAAC,KAAK,EACb,CAAC;oBACG,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC;oBAC9B,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;wBAAE,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;oBAC9C,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,SAAS,CAAC;oBAC9B,IAAI,MAAM,CAAC,SAAS,EACpB,CAAC;wBACG,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBACtC,CAAC;yBAED,CAAC;wBACG,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBACrC,CAAC;gBACL,CAAC;YACL,CAAC;QACL,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.ts new file mode 100644 index 0000000..c5f10b8 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/buildChanges.ts @@ -0,0 +1,30 @@ +import { Context } from "../contex"; +import { currencies } from "../utils"; + +export function changeHidehoutBuildCosts(context: Context): void +{ + const config = context.config.hideoutBuildsChanges; + const hideoutAreas = context.tables.hideout.areas; + + for (const area of hideoutAreas) for (const stage of Object.values(area.stages)) + { + const nonCurrencyReq = stage.requirements.filter((req) => !currencies.includes(req.templateId)) + for (const req of nonCurrencyReq) + { + if (req.count) + { + req.count -= config.threshold; + if (req.count > 0) req.count *= config.factor; + req.count += config.threshold; + if (config.roundDown) + { + req.count = Math.floor(req.count); + } + else + { + req.count = Math.ceil(req.count); + } + } + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.js new file mode 100644 index 0000000..1aea6c0 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeContainer = changeContainer; +function changeContainer(context) { + const changes = context.config.misc.ContanerSizeChanges; + for (const [key, size] of Object.entries(changes)) { + if (context.tables.templates.items[key] == null) + continue; + context.tables.templates.items[key]._props.Grids[0]._props.cellsH = size[0]; + context.tables.templates.items[key]._props.Grids[0]._props.cellsV = size[1]; + } +} +//# sourceMappingURL=changeContainerSizes.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.js.map new file mode 100644 index 0000000..c377cd5 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "changeContainerSizes.js", + "sourceRoot": "", + "sources": [ + "changeContainerSizes.ts" + ], + "names": [], + "mappings": ";;AAEA,0CAUC;AAVD,SAAgB,eAAe,CAAC,OAAgB;IAE5C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC;IAExD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EACjD,CAAC;QACG,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI;YAAE,SAAS;QAC1D,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5E,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAChF,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.ts new file mode 100644 index 0000000..c826159 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/changeContainerSizes.ts @@ -0,0 +1,13 @@ +import { Context } from "../contex"; + +export function changeContainer(context: Context): void +{ + const changes = context.config.misc.ContanerSizeChanges; + + for (const [key, size] of Object.entries(changes)) + { + if (context.tables.templates.items[key] == null) continue; + context.tables.templates.items[key]._props.Grids[0]._props.cellsH = size[0]; + context.tables.templates.items[key]._props.Grids[0]._props.cellsV = size[1]; + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.js new file mode 100644 index 0000000..27211cc --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeCrafts = changeCrafts; +const ItemTpl_1 = require("C:/snapshot/project/obj/models/enums/ItemTpl"); +function changeCrafts(context) { + //Do not affect crafts that output these + const forbiddenCrafts = [ + ItemTpl_1.ItemTpl.BARTER_PHYSICAL_BITCOIN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_GEARCRATE_BLUE_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_GEARCRATE_GREEN_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_GEARCRATE_VIOLET_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_JEWELRYCRATE_BLUE_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_JEWELRYCRATE_GREEN_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_JEWELRYCRATE_VIOLET_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_JUNKCRATE_BLUE_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_JUNKCRATE_GREEN_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_JUNKCRATE_VIOLET_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_BLUE_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_GREEN_OPEN, + ItemTpl_1.ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_VIOLET_OPEN, + ItemTpl_1.ItemTpl.DRINK_CANISTER_WITH_PURIFIED_WATER, + ItemTpl_1.ItemTpl.DRINK_BOTTLE_OF_FIERCE_HATCHLING_MOONSHINE + ]; + const config = context.config.misc; + const crafts = context.tables.hideout.production.recipes; + const nonBtc = crafts.filter((production) => !forbiddenCrafts.includes(production.endProduct)); + for (const craft of nonBtc) { + craft.count *= config.craftProductMultiplier; + craft.productionTime *= config.craftTimeMultiplier; + } +} +//# sourceMappingURL=craftChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.js.map new file mode 100644 index 0000000..5ed7ab7 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "craftChanges.js", + "sourceRoot": "", + "sources": [ + "craftChanges.ts" + ], + "names": [], + "mappings": ";;AAGA,oCA+BC;AAlCD,0EAAuE;AAGvE,SAAgB,YAAY,CAAC,OAAgB;IAGzC,wCAAwC;IACxC,MAAM,eAAe,GAAG;QACpB,iBAAO,CAAC,uBAAuB;QAC/B,iBAAO,CAAC,6CAA6C;QACrD,iBAAO,CAAC,8CAA8C;QACtD,iBAAO,CAAC,+CAA+C;QACvD,iBAAO,CAAC,gDAAgD;QACxD,iBAAO,CAAC,iDAAiD;QACzD,iBAAO,CAAC,kDAAkD;QAC1D,iBAAO,CAAC,6CAA6C;QACrD,iBAAO,CAAC,8CAA8C;QACtD,iBAAO,CAAC,+CAA+C;QACvD,iBAAO,CAAC,+CAA+C;QACvD,iBAAO,CAAC,gDAAgD;QACxD,iBAAO,CAAC,iDAAiD;QACzD,iBAAO,CAAC,kCAAkC;QAC1C,iBAAO,CAAC,0CAA0C;KACzC,CAAC;IAEd,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;IACzD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;IAE/F,KAAK,MAAM,KAAK,IAAI,MAAM,EAC1B,CAAC;QACG,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,sBAAsB,CAAC;QAC7C,KAAK,CAAC,cAAc,IAAI,MAAM,CAAC,mBAAmB,CAAC;IACvD,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.ts new file mode 100644 index 0000000..5c88ab3 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/craftChanges.ts @@ -0,0 +1,35 @@ +import { ItemTpl } from "@spt/models/enums/ItemTpl"; +import { Context } from "../contex"; + +export function changeCrafts(context: Context): void +{ + + //Do not affect crafts that output these + const forbiddenCrafts = [ + ItemTpl.BARTER_PHYSICAL_BITCOIN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_GEARCRATE_BLUE_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_GEARCRATE_GREEN_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_GEARCRATE_VIOLET_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_JEWELRYCRATE_BLUE_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_JEWELRYCRATE_GREEN_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_JEWELRYCRATE_VIOLET_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_JUNKCRATE_BLUE_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_JUNKCRATE_GREEN_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_JUNKCRATE_VIOLET_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_BLUE_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_GREEN_OPEN, + ItemTpl.RANDOMLOOTCONTAINER_ARENA_WEAPONCRATE_VIOLET_OPEN, + ItemTpl.DRINK_CANISTER_WITH_PURIFIED_WATER, + ItemTpl.DRINK_BOTTLE_OF_FIERCE_HATCHLING_MOONSHINE + ] as string[]; + + const config = context.config.misc; + const crafts = context.tables.hideout.production.recipes; + const nonBtc = crafts.filter((production) => !forbiddenCrafts.includes(production.endProduct)); + + for (const craft of nonBtc) + { + craft.count *= config.craftProductMultiplier; + craft.productionTime *= config.craftTimeMultiplier; + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.js new file mode 100644 index 0000000..c71aa32 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.js @@ -0,0 +1,31 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addCustomTrades = addCustomTrades; +const fs_1 = __importDefault(require("fs")); +// eslint-disable-next-line @typescript-eslint/naming-convention +const json5_1 = __importDefault(require("C:/snapshot/project/node_modules/json5")); +const utils_1 = require("../utils"); +function addCustomTrades(context) { + //Read custom trades config + const fileContent = fs_1.default.readFileSync((0, utils_1.getModFolder)() + "/config/advanced/customTrades.json5", "utf-8"); + const tradesConfig = json5_1.default.parse(fileContent); + for (const [traderID, additions] of Object.entries(tradesConfig)) { + const trader = context.tables.traders[traderID]; + for (const trade of Object.values(additions.items)) { + trader.assort.items.push(trade); + } + for (const [trade, scheme] of Object.entries(additions.barter_scheme)) { + trader.assort.barter_scheme[trade] = scheme; + } + for (const [trade, loyalty] of Object.entries(additions.loyal_level_items)) { + trader.assort.loyal_level_items[trade] = loyalty; + } + for (const [trade, lockProps] of Object.entries(additions.questlocks)) { + (0, utils_1.lockBehindQuest)(context, traderID, trade, lockProps.quest, additions.items[trade]._tpl, lockProps.rewardId, lockProps.targetId); + } + } +} +//# sourceMappingURL=customTrades.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.js.map new file mode 100644 index 0000000..e2ca989 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "customTrades.js", + "sourceRoot": "", + "sources": [ + "customTrades.ts" + ], + "names": [], + "mappings": ";;;;;AAQA,0CA0CC;AAlDD,4CAAoB;AACpB,gEAAgE;AAChE,mFAA2D;AAE3D,oCAAyD;AAIzD,SAAgB,eAAe,CAAC,OAAgB;IAE5C,2BAA2B;IAC3B,MAAM,WAAW,GAAG,YAAE,CAAC,YAAY,CAAC,IAAA,oBAAY,GAAE,GAAG,qCAAqC,EAAE,OAAO,CAAC,CAAC;IACrG,MAAM,YAAY,GAAG,eAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9C,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAC5B,MAAM,CAAC,OAAO,CAAC,YAUV,CACJ,EAEL,CAAC;QACG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAChD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAClD,CAAC;YACG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,aAAa,CAAC,EACrE,CAAC;YACG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAChD,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAC1E,CAAC;YACG,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;QACrD,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,EACrE,CAAC;YACG,IAAA,uBAAe,EAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;QACpI,CAAC;IACL,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.ts new file mode 100644 index 0000000..0c86519 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/customTrades.ts @@ -0,0 +1,51 @@ +import fs from "fs"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import JSON5 from "json5"; +import { Context } from "../contex"; +import { getModFolder, lockBehindQuest } from "../utils"; +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { IBarterScheme } from "@spt/models/eft/common/tables/ITrader"; + +export function addCustomTrades(context: Context): void +{ + //Read custom trades config + const fileContent = fs.readFileSync(getModFolder() + "/config/advanced/customTrades.json5", "utf-8"); + const tradesConfig = JSON5.parse(fileContent); + + for (const [traderID, additions] of + Object.entries(tradesConfig as + Record< + string, { + items: Record, + // eslint-disable-next-line @typescript-eslint/naming-convention + barter_scheme: Record, + // eslint-disable-next-line @typescript-eslint/naming-convention + loyal_level_items: Record, + questlocks: Record + } + > + ) + ) + { + const trader = context.tables.traders[traderID]; + for (const trade of Object.values(additions.items)) + { + trader.assort.items.push(trade); + } + + for (const [trade, scheme] of Object.entries(additions.barter_scheme)) + { + trader.assort.barter_scheme[trade] = scheme; + } + + for (const [trade, loyalty] of Object.entries(additions.loyal_level_items)) + { + trader.assort.loyal_level_items[trade] = loyalty; + } + + for (const [trade, lockProps] of Object.entries(additions.questlocks)) + { + lockBehindQuest(context, traderID, trade, lockProps.quest, additions.items[trade]._tpl, lockProps.rewardId, lockProps.targetId); + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.js new file mode 100644 index 0000000..1be1e15 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.js @@ -0,0 +1,52 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.removeFirFromQuests = removeFirFromQuests; +exports.removeFirFromFlea = removeFirFromFlea; +exports.removeFirFromHideout = removeFirFromHideout; +exports.removeFirFromRepeatables = removeFirFromRepeatables; +const ConfigTypes_1 = require("C:/snapshot/project/obj/models/enums/ConfigTypes"); +function removeFirFromQuests(context) { + const quests = context.tables.templates.quests; + const locales = context.tables.locales.global; + const foundInRaidRegex = new RegExp("Find.*in raid", "i"); + const inRaidRegex = new RegExp("in raid", "i"); + for (const quest of Object.values(quests)) { + const sets = [quest.conditions.AvailableForFinish, quest.conditions.AvailableForStart, quest.conditions.Fail, quest.conditions.Started, quest.conditions.Success]; + for (const set of sets) { + if (set == null) + continue; + //Allow handing over of non-FiR items + for (const cond of set) { + if (cond.conditionType == "HandoverItem" || cond.conditionType === "FindItem") { + cond.onlyFoundInRaid = false; + } + } + } + } + //Change the locales to remove "in raid" + for (const [lang, locale] of Object.entries(locales)) + for (const [key, text] of Object.entries(locale)) { + if (foundInRaidRegex.test(text)) { + locales[lang][key] = text.replace(inRaidRegex, ""); + } + } +} +function removeFirFromFlea(context) { + context.tables.globals.config.RagFair.isOnlyFoundInRaidAllowed = false; +} +function removeFirFromHideout(context) { + const hideoutAreas = context.tables.hideout.areas; + for (const area of hideoutAreas) + for (const stage of Object.values(area.stages)) { + const itemReq = stage.requirements.filter((req) => req.type == "Item"); + for (const req of itemReq) { + req.isSpawnedInSession = false; + } + } +} +function removeFirFromRepeatables(context) { + for (const repType of context.sptConfig.getConfig(ConfigTypes_1.ConfigTypes.QUEST).repeatableQuests) { + repType.questConfig.Completion.requiredItemsAreFiR = false; + } +} +//# sourceMappingURL=firChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.js.map new file mode 100644 index 0000000..4f63bb2 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "firChanges.js", + "sourceRoot": "", + "sources": [ + "firChanges.ts" + ], + "names": [], + "mappings": ";;AAQA,kDAkCC;AAED,8CAGC;AAED,oDAeC;AAED,4DAMC;AAnED,kFAA+E;AAG/E,SAAgB,mBAAmB,CAAC,OAAgB;IAEhD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAC9C,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAE/C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EACzC,CAAC;QACG,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,iBAAiB,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;QAEjK,KAAK,MAAM,GAAG,IAAI,IAAI,EACtB,CAAC;YACG,IAAI,GAAG,IAAI,IAAI;gBAAE,SAAS;YAE1B,qCAAqC;YACrC,KAAK,MAAM,IAAI,IAAI,GAAG,EACtB,CAAC;gBACG,IAAI,IAAI,CAAC,aAAa,IAAI,cAAc,IAAI,IAAI,CAAC,aAAa,KAAK,UAAU,EAC7E,CAAC;oBACG,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;gBACjC,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;IAED,wCAAwC;IACxC,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EACtG,CAAC;YACG,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAC/B,CAAC;gBACG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACvD,CAAC;QACL,CAAC;AACL,CAAC;AAED,SAAgB,iBAAiB,CAAC,OAAgB;IAE9C,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,wBAAwB,GAAG,KAAK,CAAC;AAC3E,CAAC;AAED,SAAgB,oBAAoB,CAAC,OAAgB;IAGjD,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;IAElD,KAAK,MAAM,IAAI,IAAI,YAAY;QAAE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAC/E,CAAC;YACG,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;YAEvE,KAAK,MAAM,GAAG,IAAI,OAA8B,EAChD,CAAC;gBACG,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAC;YACnC,CAAC;QACL,CAAC;AAEL,CAAC;AAED,SAAgB,wBAAwB,CAAC,OAAgB;IAErD,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,SAAS,CAAC,SAAS,CAAe,yBAAW,CAAC,KAAK,CAAC,CAAC,gBAAgB,EACnG,CAAC;QACG,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,mBAAmB,GAAG,KAAK,CAAC;IAC/D,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.ts new file mode 100644 index 0000000..ca63553 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/firChanges.ts @@ -0,0 +1,73 @@ +import { IStageRequirement } from "@spt/models/eft/hideout/IHideoutArea"; +import { Context } from "../contex"; +import { DependencyContainer } from "tsyringe"; +import { RepeatableQuestGenerator } from "@spt/generators/RepeatableQuestGenerator"; +import { IQuestCondition } from "@spt/models/eft/common/tables/IQuest"; +import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; +import { IQuestConfig } from "@spt/models/spt/config/IQuestConfig"; + +export function removeFirFromQuests(context: Context): void +{ + const quests = context.tables.templates.quests; + const locales = context.tables.locales.global; + const foundInRaidRegex = new RegExp("Find.*in raid", "i"); + const inRaidRegex = new RegExp("in raid", "i"); + + for (const quest of Object.values(quests)) + { + const sets = [quest.conditions.AvailableForFinish, quest.conditions.AvailableForStart, quest.conditions.Fail, quest.conditions.Started, quest.conditions.Success] + + for (const set of sets) + { + if (set == null) continue; + + //Allow handing over of non-FiR items + for (const cond of set) + { + if (cond.conditionType == "HandoverItem" || cond.conditionType === "FindItem") + { + cond.onlyFoundInRaid = false; + } + } + } + } + + //Change the locales to remove "in raid" + for (const [lang, locale] of Object.entries(locales)) for (const [key, text] of Object.entries(locale)) + { + if (foundInRaidRegex.test(text)) + { + locales[lang][key] = text.replace(inRaidRegex, ""); + } + } +} + +export function removeFirFromFlea(context: Context): void +{ + context.tables.globals.config.RagFair.isOnlyFoundInRaidAllowed = false; +} + +export function removeFirFromHideout(context: Context): void +{ + + const hideoutAreas = context.tables.hideout.areas; + + for (const area of hideoutAreas) for (const stage of Object.values(area.stages)) + { + const itemReq = stage.requirements.filter((req) => req.type == "Item"); + + for (const req of itemReq as IStageRequirement[]) + { + req.isSpawnedInSession = false; + } + } + +} + +export function removeFirFromRepeatables(context: Context): void +{ + for (const repType of context.sptConfig.getConfig(ConfigTypes.QUEST).repeatableQuests) + { + repType.questConfig.Completion.requiredItemsAreFiR = false; + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.js new file mode 100644 index 0000000..9822d08 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.js @@ -0,0 +1,16 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.disableFleaMarket = disableFleaMarket; +const BaseClasses_1 = require("C:/snapshot/project/obj/models/enums/BaseClasses"); +function disableFleaMarket(context) { + const allItems = context.tables.templates.items; + const config = context.config.fleaMarketChanges; + for (const [id, item] of Object.entries(allItems)) { + let allowed = config.fleaWhitelist.includes(id); + if (config.stillAllowKeys && context.itemHelper.isOfBaseclass(id, BaseClasses_1.BaseClasses.KEY) && item._props.CanSellOnRagfair) + allowed = true; + item._props.CanRequireOnRagfair = allowed && item._props.CanRequireOnRagfair; + item._props.CanSellOnRagfair = allowed; + } +} +//# sourceMappingURL=fleaChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.js.map new file mode 100644 index 0000000..1637baf --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "fleaChanges.js", + "sourceRoot": "", + "sources": [ + "fleaChanges.ts" + ], + "names": [], + "mappings": ";;AAGA,8CAYC;AAfD,kFAA+E;AAG/E,SAAgB,iBAAiB,CAAC,OAAgB;IAE9C,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAEhD,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EACjD,CAAC;QACG,IAAI,OAAO,GAAY,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACzD,IAAI,MAAM,CAAC,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,EAAE,yBAAW,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,gBAAgB;YAAE,OAAO,GAAG,IAAI,CAAC;QACnI,IAAI,CAAC,MAAM,CAAC,mBAAmB,GAAG,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,mBAAmB,CAAC;QAC7E,IAAI,CAAC,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC;IAC3C,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.ts new file mode 100644 index 0000000..0dbef60 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/fleaChanges.ts @@ -0,0 +1,16 @@ +import { BaseClasses } from "@spt/models/enums/BaseClasses"; +import { Context } from "../contex"; + +export function disableFleaMarket(context: Context): void +{ + const allItems = context.tables.templates.items; + const config = context.config.fleaMarketChanges; + + for (const [id, item] of Object.entries(allItems)) + { + let allowed: boolean = config.fleaWhitelist.includes(id); + if (config.stillAllowKeys && context.itemHelper.isOfBaseclass(id, BaseClasses.KEY) && item._props.CanSellOnRagfair) allowed = true; + item._props.CanRequireOnRagfair = allowed && item._props.CanRequireOnRagfair; + item._props.CanSellOnRagfair = allowed; + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.js new file mode 100644 index 0000000..8581db8 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changePrices = changePrices; +function changePrices(context) { + const config = context.config.misc.priceChanges; + for (const [itemId, change] of Object.entries(config)) { + //Set handbook price + const handbookItem = context.tables.templates.handbook.Items.find((item) => item.Id === itemId); + if (handbookItem == null) + continue; + handbookItem.Price = change; + //Change trader assorts + for (const trader of Object.values(context.tables.traders)) { + if (trader.assort == null) + continue; + const assorts = trader.assort.items.filter((item) => item._tpl == itemId); + for (const assort of assorts) { + const scheme = trader.assort.barter_scheme[assort._id][0][0]; + if (scheme == null) + continue; + if (scheme._tpl == "5449016a4bdc2d6f028b456f") { + scheme.count = change; + } + } + } + } +} +//# sourceMappingURL=priceChanging.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.js.map new file mode 100644 index 0000000..11cade0 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "priceChanging.js", + "sourceRoot": "", + "sources": [ + "priceChanging.ts" + ], + "names": [], + "mappings": ";;AAGA,oCA8BC;AA9BD,SAAgB,YAAY,CAAC,OAAgB;IAGzC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;IAEhD,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAgC,CAAC,EAC/E,CAAC;QACG,oBAAoB;QACpB,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QAChG,IAAI,YAAY,IAAI,IAAI;YAAE,SAAS;QACnC,YAAY,CAAC,KAAK,GAAG,MAAM,CAAC;QAE5B,uBAAuB;QACvB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAC1D,CAAC;YACG,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI;gBAAE,SAAS;YACpC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;YAE1E,KAAK,MAAM,MAAM,IAAI,OAAO,EAC5B,CAAC;gBACG,MAAM,MAAM,GAAkB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5E,IAAI,MAAM,IAAI,IAAI;oBAAE,SAAS;gBAC7B,IAAI,MAAM,CAAC,IAAI,IAAI,0BAA0B,EAC7C,CAAC;oBACG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC;gBAC1B,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;AAEL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.ts new file mode 100644 index 0000000..c686585 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/priceChanging.ts @@ -0,0 +1,34 @@ +import { IBarterScheme } from "@spt/models/eft/common/tables/ITrader"; +import { Context } from "../contex"; + +export function changePrices(context: Context): void +{ + + const config = context.config.misc.priceChanges; + + for (const [itemId, change] of Object.entries(config as Record)) + { + //Set handbook price + const handbookItem = context.tables.templates.handbook.Items.find((item) => item.Id === itemId); + if (handbookItem == null) continue; + handbookItem.Price = change; + + //Change trader assorts + for (const trader of Object.values(context.tables.traders)) + { + if (trader.assort == null) continue; + const assorts = trader.assort.items.filter((item) => item._tpl == itemId); + + for (const assort of assorts) + { + const scheme: IBarterScheme = trader.assort.barter_scheme[assort._id][0][0]; + if (scheme == null) continue; + if (scheme._tpl == "5449016a4bdc2d6f028b456f") + { + scheme.count = change; + } + } + } + } + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..js new file mode 100644 index 0000000..c46d1f1 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..js @@ -0,0 +1,24 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addAdditionalQuestRewards = addAdditionalQuestRewards; +const fs_1 = __importDefault(require("fs")); +// eslint-disable-next-line @typescript-eslint/naming-convention +const json5_1 = __importDefault(require("C:/snapshot/project/node_modules/json5")); +const utils_1 = require("../utils"); +function addAdditionalQuestRewards(context) { + //Read rewards config + const fileContent = fs_1.default.readFileSync((0, utils_1.getModFolder)() + "/config/advanced/additionalQuestRewards.json5", "utf-8"); + const secureConfig = json5_1.default.parse(fileContent); + for (const [condition, questsWithRewards] of Object.entries(secureConfig)) { + for (const [quest, extraReward] of Object.entries(questsWithRewards)) { + if (condition == "Success") + context.tables.templates.quests[quest].rewards.Success.push(extraReward); + if (condition == "Started") + context.tables.templates.quests[quest].rewards.Started.push(extraReward); + } + } +} +//# sourceMappingURL=questRewards..js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..js.map new file mode 100644 index 0000000..96caa64 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "questRewards..js", + "sourceRoot": "", + "sources": [ + "questRewards..ts" + ], + "names": [], + "mappings": ";;;;;AAOA,8DAgBC;AArBD,4CAAoB;AACpB,gEAAgE;AAChE,mFAA2D;AAC3D,oCAAwC;AAExC,SAAgB,yBAAyB,CAAC,OAAgB;IAGtD,qBAAqB;IACrB,MAAM,WAAW,GAAG,YAAE,CAAC,YAAY,CAAC,IAAA,oBAAY,GAAE,GAAG,+CAA+C,EAAE,OAAO,CAAC,CAAC;IAC/G,MAAM,YAAY,GAAG,eAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9C,KAAK,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EACzE,CAAC;QACG,KAAK,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,EACpE,CAAC;YACG,IAAI,SAAS,IAAI,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAsB,CAAC,CAAC;YAChH,IAAI,SAAS,IAAI,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAsB,CAAC,CAAC;QACpH,CAAC;IACL,CAAC;AAEL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..ts new file mode 100644 index 0000000..ea4d746 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/questRewards..ts @@ -0,0 +1,24 @@ +import { IReward } from "@spt/models/eft/common/tables/IReward"; +import { Context } from "../contex"; +import fs from "fs"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import JSON5 from "json5"; +import { getModFolder } from "../utils"; + +export function addAdditionalQuestRewards(context: Context): void +{ + + //Read rewards config + const fileContent = fs.readFileSync(getModFolder() + "/config/advanced/additionalQuestRewards.json5", "utf-8"); + const secureConfig = JSON5.parse(fileContent); + + for (const [condition, questsWithRewards] of Object.entries(secureConfig)) + { + for (const [quest, extraReward] of Object.entries(questsWithRewards)) + { + if (condition == "Success") context.tables.templates.quests[quest].rewards.Success.push(extraReward as IReward); + if (condition == "Started") context.tables.templates.quests[quest].rewards.Started.push(extraReward as IReward); + } + } + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.js new file mode 100644 index 0000000..f0ef1d8 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.addSupportForGPTraders = addSupportForGPTraders; +exports.changeRefPurchasingOptions = changeRefPurchasingOptions; +exports.gainRefRepOnKill = gainRefRepOnKill; +const utils_1 = require("../utils"); +const ItemTpl_1 = require("C:/snapshot/project/obj/models/enums/ItemTpl"); +function addSupportForGPTraders(context, container) { + container.afterResolution("TraderController", (_t, result) => { + // We want to replace the original method logic with something different + const old = result.getItemPrices.bind(result); + result.getItemPrices = (sessionId, traderId) => { + const oldOut = old(sessionId, traderId); //Add old logic back in + oldOut.currencyCourses["5d235b4d86f7742e017bc88a"] = 7500; //ToDo: configurable? + return oldOut; + }; + }, { frequency: "Always" }); +} +function changeRefPurchasingOptions(context) { + const ref = context.tables.traders["6617beeaa9cfa777ca915b7c"]; + const config = context.config.refChanges; + if (config.refBuysInGPCoins) + ref.base.currency = "GP"; + if (config.refOnlyBuysDogtags) { + ref.base.items_buy.category = []; + ref.base.items_buy.id_list = (0, utils_1.getDogtagsList)(context); + } + if (config.refAlsoBuysLegaMedals) + ref.base.items_buy.id_list.push(ItemTpl_1.ItemTpl.BARTER_LEGA_MEDAL); +} +function gainRefRepOnKill(context, container) { + container.afterResolution("LocationLifecycleService", (_t, result) => { + // We want to replace the original method logic with something different + const old = result.handlePostRaidPmc.bind(result); + result.handlePostRaidPmc = (sessionId, fullProfile, scavProfile, isDead, isSurvived, isTransfer, request, locationName) => { + old(sessionId, fullProfile, scavProfile, isDead, isSurvived, isTransfer, request, locationName); //Add old logic back in + let pmcKills = []; + try { + //Filter PMC kills + pmcKills = request.results.profile.Stats.Eft.Victims.filter((victim) => ["pmcbear", "pmcusec"].includes(victim.Role.toLowerCase())); + } + catch (error) { + this.context.logger.error("Error Details:" + "Something went wrong when trying to tally up PMC kills!"); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + try { + fullProfile.characters.pmc.TradersInfo["6617beeaa9cfa777ca915b7c"].standing += repByKills(pmcKills, context); + } + catch (error) { + this.context.logger.error("Error Details:" + "Something went wrong when trying add Ref rep for PMC kills!"); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + }; + // The modifier Always makes sure this replacement method is ALWAYS replaced + }, { frequency: "Always" }); +} +function repByKills(pmcKills, context) { + let totalRep = 0; + for (const kill of pmcKills) + for (const repRange of context.config.refChanges.refStandingOnKill.repByKillLevel) { + if (kill.Level >= repRange.levelRange[0] && kill.Level < repRange.levelRange[1]) + totalRep += repRange.rep; + } + return totalRep; +} +//# sourceMappingURL=refRepRework.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.js.map new file mode 100644 index 0000000..d17d63f --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "refRepRework.js", + "sourceRoot": "", + "sources": [ + "refRepRework.ts" + ], + "names": [], + "mappings": ";;AAWA,wDAeC;AAED,gEAcC;AAED,4CAuCC;AA3ED,oCAA0C;AAC1C,0EAAuE;AAEvE,SAAgB,sBAAsB,CAAC,OAAgB,EAAE,SAA8B;IAEnF,SAAS,CAAC,eAAe,CAAC,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAwB,EAAE,EAAE;QAE3E,wEAAwE;QACxE,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,CAAC,aAAa,GAAG,CAAC,SAAiB,EAAE,QAAgB,EAAE,EAAE;YAE3D,MAAM,MAAM,GAAG,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,uBAAuB;YAEhE,MAAM,CAAC,eAAe,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAAC,CAAC,qBAAqB;YAEhF,OAAO,MAAM,CAAC;QAClB,CAAC,CAAC;IACN,CAAC,EAAE,EAAC,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAgB,0BAA0B,CAAC,OAAgB;IAEvD,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;IAEzC,IAAI,MAAM,CAAC,gBAAgB;QAAE,GAAG,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IAEtD,IAAI,MAAM,CAAC,kBAAkB,EAC7B,CAAC;QACG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,GAAG,EAAE,CAAC;QACjC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,IAAA,sBAAc,EAAC,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,MAAM,CAAC,qBAAqB;QAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAO,CAAC,iBAAiB,CAAC,CAAC;AACjG,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAgB,EAAE,SAA8B;IAG7E,SAAS,CAAC,eAAe,CAAC,0BAA0B,EAAE,CAAC,EAAE,EAAE,MAAgC,EAAE,EAAE;QAE3F,wEAAwE;QACxE,MAAM,GAAG,GAAI,MAAc,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1D,MAAc,CAAC,iBAAiB,GAAG,CAAC,SAAiB,EAAE,WAAwB,EAAE,WAAqB,EAAE,MAAe,EAAE,UAAmB,EAAE,UAAmB,EAAE,OAAiC,EAAE,YAAoB,EAAE,EAAE;YAE3N,GAAG,CAAC,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,uBAAuB;YAExH,IAAI,QAAQ,GAAc,EAAE,CAAC;YAE7B,IACA,CAAC;gBACG,kBAAkB;gBAClB,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CACvD,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;YAChF,CAAC;YACD,OAAO,KAAK,EACZ,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,yDAAyD,CAAC,CAAC;gBACxG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;YAED,IACA,CAAC;gBACG,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjH,CAAC;YACD,OAAO,KAAK,EACZ,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,6DAA6D,CAAC,CAAC;gBAC5G,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;QAEL,CAAC,CAAC;QACF,4EAA4E;IAChF,CAAC,EAAE,EAAC,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAC;AAE9B,CAAC;AAED,SAAS,UAAU,CAAC,QAAmB,EAAE,OAAgB;IAErD,IAAI,QAAQ,GAAW,CAAC,CAAC;IAEzB,KAAK,MAAM,IAAI,IAAI,QAAQ;QAAE,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,cAAc,EAC9G,CAAC;YACG,IAAI,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;gBAAE,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC;QAC9G,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.ts new file mode 100644 index 0000000..4b02f66 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/refRepRework.ts @@ -0,0 +1,96 @@ +import { DependencyContainer } from "tsyringe"; +import { Context } from "../contex"; +import { LocationLifecycleService } from "@spt/services/LocationLifecycleService"; +import { IPmcData } from "@spt/models/eft/common/IPmcData"; +import { IEndLocalRaidRequestData } from "@spt/models/eft/match/IEndLocalRaidRequestData"; +import { IVictim } from "@spt/models/eft/common/tables/IBotBase"; +import { ISptProfile } from "@spt/models/eft/profile/ISptProfile"; +import { TraderController } from "@spt/controllers/TraderController"; +import { getDogtagsList } from "../utils"; +import { ItemTpl } from "@spt/models/enums/ItemTpl"; + +export function addSupportForGPTraders(context: Context, container: DependencyContainer): void +{ + container.afterResolution("TraderController", (_t, result: TraderController) => + { + // We want to replace the original method logic with something different + const old = result.getItemPrices.bind(result); + result.getItemPrices = (sessionId: string, traderId: string) => + { + const oldOut = old(sessionId, traderId); //Add old logic back in + + oldOut.currencyCourses["5d235b4d86f7742e017bc88a"] = 7500; //ToDo: configurable? + + return oldOut; + }; + }, {frequency: "Always"}); +} + +export function changeRefPurchasingOptions(context: Context): void +{ + const ref = context.tables.traders["6617beeaa9cfa777ca915b7c"]; + const config = context.config.refChanges; + + if (config.refBuysInGPCoins) ref.base.currency = "GP"; + + if (config.refOnlyBuysDogtags) + { + ref.base.items_buy.category = []; + ref.base.items_buy.id_list = getDogtagsList(context); + } + + if (config.refAlsoBuysLegaMedals) ref.base.items_buy.id_list.push(ItemTpl.BARTER_LEGA_MEDAL); +} + +export function gainRefRepOnKill(context: Context, container: DependencyContainer): void +{ + + container.afterResolution("LocationLifecycleService", (_t, result: LocationLifecycleService) => + { + // We want to replace the original method logic with something different + const old = (result as any).handlePostRaidPmc.bind(result); + (result as any).handlePostRaidPmc = (sessionId: string, fullProfile: ISptProfile, scavProfile: IPmcData, isDead: boolean, isSurvived: boolean, isTransfer: boolean, request: IEndLocalRaidRequestData, locationName: string) => + { + old(sessionId, fullProfile, scavProfile, isDead, isSurvived, isTransfer, request, locationName); //Add old logic back in + + let pmcKills: IVictim[] = []; + + try + { + //Filter PMC kills + pmcKills = request.results.profile.Stats.Eft.Victims.filter( + (victim) => ["pmcbear", "pmcusec"].includes(victim.Role.toLowerCase())); + } + catch (error) + { + this.context.logger.error("Error Details:" + "Something went wrong when trying to tally up PMC kills!"); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + + try + { + fullProfile.characters.pmc.TradersInfo["6617beeaa9cfa777ca915b7c"].standing += repByKills(pmcKills, context); + } + catch (error) + { + this.context.logger.error("Error Details:" + "Something went wrong when trying add Ref rep for PMC kills!"); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + + }; + // The modifier Always makes sure this replacement method is ALWAYS replaced + }, {frequency: "Always"}); + +} + +function repByKills(pmcKills: IVictim[], context: Context): number +{ + let totalRep: number = 0; + + for (const kill of pmcKills) for (const repRange of context.config.refChanges.refStandingOnKill.repByKillLevel) + { + if (kill.Level >= repRange.levelRange[0] && kill.Level < repRange.levelRange[1]) totalRep += repRange.rep; + } + + return totalRep; +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.js new file mode 100644 index 0000000..3f2c2dd --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.js @@ -0,0 +1,64 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.applySecureContainerChanges = applySecureContainerChanges; +const fs_1 = __importDefault(require("fs")); +// eslint-disable-next-line @typescript-eslint/naming-convention +const json5_1 = __importDefault(require("C:/snapshot/project/node_modules/json5")); +const utils_1 = require("../utils"); +function applySecureContainerChanges(context) { + //Read secure container config + const fileContent = fs_1.default.readFileSync((0, utils_1.getModFolder)() + "/config/advanced/secureChanges.json5", "utf-8"); + const secureConfig = json5_1.default.parse(fileContent); + const config = context.config.secureContainerProgression; + //Change container grids + for (const container of Object.keys(config.sizeChanges)) { + const containerItem = context.tables.templates.items[container]; + const gridTemplate = containerItem._props.Grids[0]; + const gridSizes = config.sizeChanges[container]; + const newGrids = []; + for (let i = 0; i < gridSizes.length; i++) { + const size = gridSizes[i]; + newGrids.push({ + _name: (i + 1).toString(), + _id: context.hashUtil.generate(), + _parent: "664a55d84a90fc2c8a6305c9", + _props: { + filters: gridTemplate._props.filters, + cellsH: size[0], + cellsV: size[1], + minCount: 0, + maxCount: 0, + maxWeight: 0, + isSortingTable: false + }, + _proto: "55d329c24bdc2d892f8b4567" + }); + } + containerItem._props.Grids = newGrids; + } + //Add quest rewards + for (const [condition, questsWithRewards] of Object.entries(secureConfig.AdditionalQuestRewards)) { + for (const [quest, extraReward] of Object.entries(questsWithRewards)) { + if (condition == "Success") + context.tables.templates.quests[quest].rewards.Success.push(extraReward); + if (condition == "Started") + context.tables.templates.quests[quest].rewards.Started.push(extraReward); + } + } + //Start with Waist Pouch + const profileTemplates = context.tables.templates.profiles; + for (const profile of Object.values(profileTemplates)) { + const bearContainer = profile.bear.character.Inventory.items.find((x) => x.slotId === "SecuredContainer"); + if (bearContainer) { + bearContainer._tpl = context.config.secureContainerProgression.starterContainer; + } + const usecContainer = profile.usec.character.Inventory.items.find((x) => x.slotId === "SecuredContainer"); + if (usecContainer) { + usecContainer._tpl = context.config.secureContainerProgression.starterContainer; + } + } +} +//# sourceMappingURL=secureContainer.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.js.map new file mode 100644 index 0000000..b0f8521 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "secureContainer.js", + "sourceRoot": "", + "sources": [ + "secureContainer.ts" + ], + "names": [], + "mappings": ";;;;;AAOA,kEAoEC;AA1ED,4CAAoB;AACpB,gEAAgE;AAChE,mFAA2D;AAC3D,oCAAwC;AAGxC,SAAgB,2BAA2B,CAAC,OAAgB;IAGxD,8BAA8B;IAC9B,MAAM,WAAW,GAAG,YAAE,CAAC,YAAY,CAAC,IAAA,oBAAY,GAAE,GAAG,sCAAsC,EAAE,OAAO,CAAC,CAAC;IACtG,MAAM,YAAY,GAAG,eAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC;IAEzD,wBAAwB;IACxB,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EACvD,CAAC;QACG,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChE,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,EAAE,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EACzC,CAAC;YACG,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;YAC1B,QAAQ,CAAC,IAAI,CACT;gBACI,KAAK,EAAE,CAAC,CAAC,GAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;gBACvB,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE;gBAChC,OAAO,EAAE,0BAA0B;gBACnC,MAAM,EAAE;oBACJ,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,OAAO;oBACpC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;oBACf,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;oBACf,QAAQ,EAAE,CAAC;oBACX,QAAQ,EAAE,CAAC;oBACX,SAAS,EAAE,CAAC;oBACZ,cAAc,EAAE,KAAK;iBACxB;gBACD,MAAM,EAAE,0BAA0B;aACrC,CACJ,CAAC;QACN,CAAC;QAED,aAAa,CAAC,MAAM,CAAC,KAAK,GAAG,QAAQ,CAAC;IAC1C,CAAC;IAED,mBAAmB;IACnB,KAAK,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,sBAAsB,CAAC,EAChG,CAAC;QACG,KAAK,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,EACpE,CAAC;YACG,IAAI,SAAS,IAAI,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAsB,CAAC,CAAC;YAChH,IAAI,SAAS,IAAI,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAsB,CAAC,CAAC;QACpH,CAAC;IACL,CAAC;IAED,wBAAwB;IACxB,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAA;IAC1D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,EACrD,CAAC;QACG,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,kBAAkB,CAAC,CAAA;QACzG,IAAI,aAAa,EACjB,CAAC;YACG,aAAa,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,gBAAgB,CAAC;QACpF,CAAC;QACD,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,kBAAkB,CAAC,CAAA;QACzG,IAAI,aAAa,EACjB,CAAC;YACG,aAAa,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,gBAAgB,CAAC;QACpF,CAAC;IACL,CAAC;AAEL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.ts new file mode 100644 index 0000000..a45d3be --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/secureContainer.ts @@ -0,0 +1,76 @@ +import { Context } from "../contex"; +import fs from "fs"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import JSON5 from "json5"; +import { getModFolder } from "../utils"; +import { IReward } from "@spt/models/eft/common/tables/IReward"; + +export function applySecureContainerChanges(context: Context): void +{ + + //Read secure container config + const fileContent = fs.readFileSync(getModFolder() + "/config/advanced/secureChanges.json5", "utf-8"); + const secureConfig = JSON5.parse(fileContent); + + const config = context.config.secureContainerProgression; + + //Change container grids + for (const container of Object.keys(config.sizeChanges)) + { + const containerItem = context.tables.templates.items[container]; + const gridTemplate = containerItem._props.Grids[0]; + const gridSizes = config.sizeChanges[container]; + const newGrids = []; + + for (let i = 0; i < gridSizes.length; i++) + { + const size = gridSizes[i]; + newGrids.push( + { + _name: (i+1).toString(), + _id: context.hashUtil.generate(), + _parent: "664a55d84a90fc2c8a6305c9", + _props: { + filters: gridTemplate._props.filters, + cellsH: size[0], + cellsV: size[1], + minCount: 0, + maxCount: 0, + maxWeight: 0, + isSortingTable: false + }, + _proto: "55d329c24bdc2d892f8b4567" + } + ); + } + + containerItem._props.Grids = newGrids; + } + + //Add quest rewards + for (const [condition, questsWithRewards] of Object.entries(secureConfig.AdditionalQuestRewards)) + { + for (const [quest, extraReward] of Object.entries(questsWithRewards)) + { + if (condition == "Success") context.tables.templates.quests[quest].rewards.Success.push(extraReward as IReward); + if (condition == "Started") context.tables.templates.quests[quest].rewards.Started.push(extraReward as IReward); + } + } + + //Start with Waist Pouch + const profileTemplates = context.tables.templates.profiles + for (const profile of Object.values(profileTemplates)) + { + const bearContainer = profile.bear.character.Inventory.items.find((x) => x.slotId === "SecuredContainer") + if (bearContainer) + { + bearContainer._tpl = context.config.secureContainerProgression.starterContainer; + } + const usecContainer = profile.usec.character.Inventory.items.find((x) => x.slotId === "SecuredContainer") + if (usecContainer) + { + usecContainer._tpl = context.config.secureContainerProgression.starterContainer; + } + } + +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.js new file mode 100644 index 0000000..22526cc --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeSkills = changeSkills; +function changeSkills(context) { + context.tables.globals.config.SkillFreshEffectiveness = context.config.skillChanges.SkillFreshEffectiveness; + context.tables.globals.config.SkillFreshPoints = context.config.skillChanges.SkillFreshPoints; + context.tables.globals.config.SkillMinEffectiveness = context.config.skillChanges.SkillMinEffectiveness; + context.tables.globals.config.SkillPointsBeforeFatigue = context.config.skillChanges.SkillPointsBeforeFatigue; +} +//# sourceMappingURL=skills.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.js.map new file mode 100644 index 0000000..32b6e4b --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "skills.js", + "sourceRoot": "", + "sources": [ + "skills.ts" + ], + "names": [], + "mappings": ";;AAEA,oCAMC;AAND,SAAgB,YAAY,CAAC,OAAgB;IAEzC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,uBAAuB,CAAC;IAC5G,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC;IAC9F,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,qBAAqB,CAAC;IACxG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,wBAAwB,CAAC;AAClH,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.ts new file mode 100644 index 0000000..7556e28 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/skills.ts @@ -0,0 +1,9 @@ +import { Context } from "../contex"; + +export function changeSkills(context: Context): void +{ + context.tables.globals.config.SkillFreshEffectiveness = context.config.skillChanges.SkillFreshEffectiveness; + context.tables.globals.config.SkillFreshPoints = context.config.skillChanges.SkillFreshPoints; + context.tables.globals.config.SkillMinEffectiveness = context.config.skillChanges.SkillMinEffectiveness; + context.tables.globals.config.SkillPointsBeforeFatigue = context.config.skillChanges.SkillPointsBeforeFatigue; +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.js new file mode 100644 index 0000000..e540a5c --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeStackSizes = changeStackSizes; +function changeStackSizes(context) { + //Change stack sizes + for (const [item, stackSize] of Object.entries(context.config.misc.stackSizeOverride)) { + context.tables.templates.items[item]._props.StackMaxSize = stackSize; + } +} +//# sourceMappingURL=stackSizeChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.js.map new file mode 100644 index 0000000..2aca0ff --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "stackSizeChanges.js", + "sourceRoot": "", + "sources": [ + "stackSizeChanges.ts" + ], + "names": [], + "mappings": ";;AAEA,4CAOC;AAPD,SAAgB,gBAAgB,CAAC,OAAgB;IAE7C,oBAAoB;IACpB,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,iBAA2C,CAAC,EAC/G,CAAC;QACG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,SAAS,CAAC;IACzE,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.ts new file mode 100644 index 0000000..64a13b1 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stackSizeChanges.ts @@ -0,0 +1,10 @@ +import { Context } from "../contex"; + +export function changeStackSizes(context: Context): void +{ + //Change stack sizes + for (const [item, stackSize] of Object.entries(context.config.misc.stackSizeOverride as Record)) + { + context.tables.templates.items[item]._props.StackMaxSize = stackSize; + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.js new file mode 100644 index 0000000..060d76f --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setStartingReputation = setStartingReputation; +function setStartingReputation(context) { + const config = context.config.overrideInitialStanding; + const profileTemplates = context.tables.templates.profiles; + for (const profile of Object.values(profileTemplates)) + for (const template of [profile.bear, profile.usec]) { + template.trader.initialStanding["default"] = config.defaultOverride; + for (const trader of Object.keys(context.tables.traders)) { + template.trader.initialStanding[trader] = config.defaultOverride; + } + for (const [trader, standing] of Object.entries(config.indivudalOverrides)) { + template.trader.initialStanding[trader] = standing; + } + } +} +//# sourceMappingURL=startingRep.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.js.map new file mode 100644 index 0000000..5b1b979 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "startingRep.js", + "sourceRoot": "", + "sources": [ + "startingRep.ts" + ], + "names": [], + "mappings": ";;AAGA,sDAoBC;AApBD,SAAgB,qBAAqB,CAAC,OAAgB;IAElD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC;IACtD,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;IAE3D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAAE,KAAK,MAAM,QAAQ,IAAI,CAAE,OAAyB,CAAC,IAAI,EAAG,OAAyB,CAAC,IAAI,CAAC,EAChJ,CAAC;YACG,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC;YAEpE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EACxD,CAAC;gBACG,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC;YACrE,CAAC;YAED,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,kBAA4C,CAAC,EACpG,CAAC;gBACG,QAAQ,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;YACvD,CAAC;QAEL,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.ts new file mode 100644 index 0000000..9e99580 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/startingRep.ts @@ -0,0 +1,24 @@ +import { IProfileSides } from "@spt/models/eft/common/tables/IProfileTemplate"; +import { Context } from "../contex"; + +export function setStartingReputation(context: Context): void +{ + const config = context.config.overrideInitialStanding; + const profileTemplates = context.tables.templates.profiles; + + for (const profile of Object.values(profileTemplates)) for (const template of [(profile as IProfileSides).bear, (profile as IProfileSides).usec]) + { + template.trader.initialStanding["default"] = config.defaultOverride; + + for (const trader of Object.keys(context.tables.traders)) + { + template.trader.initialStanding[trader] = config.defaultOverride; + } + + for (const [trader, standing] of Object.entries(config.indivudalOverrides as Record)) + { + template.trader.initialStanding[trader] = standing; + } + + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.js new file mode 100644 index 0000000..773447f --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.js @@ -0,0 +1,79 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.changeStashProgression = changeStashProgression; +const HideoutAreas_1 = require("C:/snapshot/project/obj/models/enums/HideoutAreas"); +const ItemTpl_1 = require("C:/snapshot/project/obj/models/enums/ItemTpl"); +const utils_1 = require("../utils"); +function changeStashProgression(context) { + //Thanks to ODT and his Softcore mod for this part of the code, most of this function is taken from there with appropriate modifications + //Link to the mod: https://hub.sp-tarkov.com/files/file/998-softcore-proper-singleplayer-experience-for-spt/ + const config = context.config.stashProgression; + const profs = context.tables.templates.profiles; + const basicStashBonuses = { + id: "64f5b9e5fa34f11b380756c0", + templateId: ItemTpl_1.ItemTpl.STASH_STANDARD_STASH_10X30, + type: "StashSize" + }; + const startingStashes = [ + ItemTpl_1.ItemTpl.STASH_STANDARD_STASH_10X30, + ItemTpl_1.ItemTpl.STASH_LEFT_BEHIND_STASH_10X40, + ItemTpl_1.ItemTpl.STASH_PREPARE_FOR_ESCAPE_STASH_10X50, + ItemTpl_1.ItemTpl.STASH_EDGE_OF_DARKNESS_STASH_10X68, + ItemTpl_1.ItemTpl.STASH_THE_UNHEARD_EDITION_STASH_10X72 + ]; + for (const [profile, _] of Object.entries(profs)) { + for (const sidekey of Object.keys(_)) { + if (sidekey === "descriptionLocaleKey") { + continue; + } + const side = profs[profile][sidekey]; + const hideoutArea = side.character.Hideout?.Areas.find((area) => area.type === HideoutAreas_1.HideoutAreas.STASH); + if (!hideoutArea) { + context.logger.warning(`HideoutOptionsChanger: doProgressiveStash: hideoutArea for profile ${_} not found`); + continue; + } + hideoutArea.level = 1; + const startingStashItems = side.character.Inventory.items.filter((item) => startingStashes.includes(item._tpl)); + for (const item of startingStashItems) { + item._tpl = ItemTpl_1.ItemTpl.STASH_STANDARD_STASH_10X30; + } + // Fix for Unheard profiles + side.character.Bonuses = side.character.Bonuses.filter((x) => x.type !== "StashSize"); + side.character.Bonuses.push(basicStashBonuses); + } + } + //Change stash sizes + const stashUpdates = { + [ItemTpl_1.ItemTpl.STASH_STANDARD_STASH_10X30]: config.stashSizes[0], + [ItemTpl_1.ItemTpl.STASH_LEFT_BEHIND_STASH_10X40]: config.stashSizes[1], + [ItemTpl_1.ItemTpl.STASH_PREPARE_FOR_ESCAPE_STASH_10X50]: config.stashSizes[2], + [ItemTpl_1.ItemTpl.STASH_EDGE_OF_DARKNESS_STASH_10X68]: config.stashSizes[3], + [ItemTpl_1.ItemTpl.STASH_THE_UNHEARD_EDITION_STASH_10X72]: config.stashSizes[4] + }; + for (const [itemTpl, stashSize] of Object.entries(stashUpdates)) { + const stashItem = context.tables.templates.items[itemTpl]; + if (stashItem?._props?.Grids?.[0]?._props) { + stashItem._props.Grids[0]._props.cellsV = stashSize; + } + else { + context.logger.warning(`HideoutOptions: doBiggerStash: Failed to modify stash with Tpl ${itemTpl}, skipping`); + } + } + //Change construction price + const hideoutStashStages = context.tables.hideout.areas.find((area) => area.type === HideoutAreas_1.HideoutAreas.STASH).stages; + for (const stage of Object.values(hideoutStashStages)) { + const currencyRequirements = stage.requirements.filter((req) => utils_1.currencies.includes(req.templateId)); + for (const currencyRequirement of currencyRequirements) { + if (currencyRequirement.count) { + currencyRequirement.count *= config.stashUpgradeCostFactor; + } + } + const loyaltylevels = stage.requirements.filter((req) => req.loyaltyLevel !== undefined); + for (const loyaltyLevel of loyaltylevels) { + if (loyaltyLevel.loyaltyLevel !== undefined) { + loyaltyLevel.loyaltyLevel += config.stashUpgradeLoyaltyDelta; + } + } + } +} +//# sourceMappingURL=stashChanges.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.js.map new file mode 100644 index 0000000..b99dd88 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "stashChanges.js", + "sourceRoot": "", + "sources": [ + "stashChanges.ts" + ], + "names": [], + "mappings": ";;AAOA,wDAgGC;AAvGD,oFAAiF;AAGjF,0EAAuE;AAEvE,oCAAsC;AAEtC,SAAgB,sBAAsB,CAAC,OAAgB;IAEnD,wIAAwI;IACxI,4GAA4G;IAE5G,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC;IAEhD,MAAM,iBAAiB,GAAG;QACtB,EAAE,EAAE,0BAA0B;QAC9B,UAAU,EAAE,iBAAO,CAAC,0BAA0B;QAC9C,IAAI,EAAE,WAAW;KACpB,CAAA;IAED,MAAM,eAAe,GAAG;QACpB,iBAAO,CAAC,0BAA0B;QAClC,iBAAO,CAAC,6BAA6B;QACrC,iBAAO,CAAC,oCAAoC;QAC5C,iBAAO,CAAC,kCAAkC;QAC1C,iBAAO,CAAC,qCAAqC;KAChD,CAAA;IAED,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAChD,CAAC;QACG,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAA4B,EAC/D,CAAC;YACG,IAAI,OAAO,KAAK,sBAAsB,EACtC,CAAC;gBACG,SAAQ;YACZ,CAAC;YACD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAA;YACpC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,2BAAY,CAAC,KAAK,CAAC,CAAA;YAClG,IAAI,CAAC,WAAW,EAChB,CAAC;gBACG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,sEAAsE,CAAC,YAAY,CAAC,CAAA;gBAC3G,SAAQ;YACZ,CAAC;YACD,WAAW,CAAC,KAAK,GAAG,CAAC,CAAA;YAErB,MAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YAC/G,KAAK,MAAM,IAAI,IAAI,kBAAkB,EACrC,CAAC;gBACG,IAAI,CAAC,IAAI,GAAG,iBAAO,CAAC,0BAA0B,CAAA;YAClD,CAAC;YAED,2BAA2B;YAC3B,IAAI,CAAC,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;YACrF,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;QAClD,CAAC;IACL,CAAC;IAED,oBAAoB;IACpB,MAAM,YAAY,GAA2B;QACzC,CAAC,iBAAO,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;QAC1D,CAAC,iBAAO,CAAC,6BAA6B,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;QAC7D,CAAC,iBAAO,CAAC,oCAAoC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;QACpE,CAAC,iBAAO,CAAC,kCAAkC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;QAClE,CAAC,iBAAO,CAAC,qCAAqC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;KACxE,CAAA;IAED,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAC/D,CAAC;QACG,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAM,CAAC,OAAO,CAAC,CAAA;QAC1D,IAAI,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EACzC,CAAC;YACG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;QACxD,CAAC;aAED,CAAC;YACG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,kEAAkE,OAAO,YAAY,CAAC,CAAC;QAClH,CAAC;IACL,CAAC;IAED,2BAA2B;IAC3B,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,2BAAY,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAEhH,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,EACrD,CAAC;QACG,MAAM,oBAAoB,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAA;QACpG,KAAK,MAAM,mBAAmB,IAAI,oBAAoB,EACtD,CAAC;YACG,IAAI,mBAAmB,CAAC,KAAK,EAC7B,CAAC;gBACG,mBAAmB,CAAC,KAAK,IAAI,MAAM,CAAC,sBAAsB,CAAC;YAC/D,CAAC;QACL,CAAC;QAED,MAAM,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAA;QACxF,KAAK,MAAM,YAAY,IAAI,aAAa,EACxC,CAAC;YACG,IAAI,YAAY,CAAC,YAAY,KAAK,SAAS,EAC3C,CAAC;gBACG,YAAY,CAAC,YAAY,IAAI,MAAM,CAAC,wBAAwB,CAAC;YACjE,CAAC;QACL,CAAC;IACL,CAAC;AACL,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.ts new file mode 100644 index 0000000..6dbba0b --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/miscChanges/stashChanges.ts @@ -0,0 +1,104 @@ +import { HideoutAreas } from "@spt/models/enums/HideoutAreas"; +import { Context } from "../contex"; +import { IProfileSides } from "@spt/models/eft/common/tables/IProfileTemplate"; +import { ItemTpl } from "@spt/models/enums/ItemTpl"; +import { Money } from "@spt/models/enums/Money"; +import { currencies } from "../utils"; + +export function changeStashProgression(context: Context): void +{ + //Thanks to ODT and his Softcore mod for this part of the code, most of this function is taken from there with appropriate modifications + //Link to the mod: https://hub.sp-tarkov.com/files/file/998-softcore-proper-singleplayer-experience-for-spt/ + + const config = context.config.stashProgression; + const profs = context.tables.templates.profiles; + + const basicStashBonuses = { + id: "64f5b9e5fa34f11b380756c0", + templateId: ItemTpl.STASH_STANDARD_STASH_10X30, + type: "StashSize" + } + + const startingStashes = [ + ItemTpl.STASH_STANDARD_STASH_10X30, + ItemTpl.STASH_LEFT_BEHIND_STASH_10X40, + ItemTpl.STASH_PREPARE_FOR_ESCAPE_STASH_10X50, + ItemTpl.STASH_EDGE_OF_DARKNESS_STASH_10X68, + ItemTpl.STASH_THE_UNHEARD_EDITION_STASH_10X72 + ] + + for (const [profile, _] of Object.entries(profs)) + { + for (const sidekey of Object.keys(_) as (keyof IProfileSides)[]) + { + if (sidekey === "descriptionLocaleKey") + { + continue + } + const side = profs[profile][sidekey] + const hideoutArea = side.character.Hideout?.Areas.find((area) => area.type === HideoutAreas.STASH) + if (!hideoutArea) + { + context.logger.warning(`HideoutOptionsChanger: doProgressiveStash: hideoutArea for profile ${_} not found`) + continue + } + hideoutArea.level = 1 + + const startingStashItems = side.character.Inventory.items.filter((item) => startingStashes.includes(item._tpl)) + for (const item of startingStashItems) + { + item._tpl = ItemTpl.STASH_STANDARD_STASH_10X30 + } + + // Fix for Unheard profiles + side.character.Bonuses = side.character.Bonuses.filter((x) => x.type !== "StashSize") + side.character.Bonuses.push(basicStashBonuses) + } + } + + //Change stash sizes + const stashUpdates: Record = { + [ItemTpl.STASH_STANDARD_STASH_10X30]: config.stashSizes[0], + [ItemTpl.STASH_LEFT_BEHIND_STASH_10X40]: config.stashSizes[1], + [ItemTpl.STASH_PREPARE_FOR_ESCAPE_STASH_10X50]: config.stashSizes[2], + [ItemTpl.STASH_EDGE_OF_DARKNESS_STASH_10X68]: config.stashSizes[3], + [ItemTpl.STASH_THE_UNHEARD_EDITION_STASH_10X72]: config.stashSizes[4] + } + + for (const [itemTpl, stashSize] of Object.entries(stashUpdates)) + { + const stashItem = context.tables.templates.items![itemTpl] + if (stashItem?._props?.Grids?.[0]?._props) + { + stashItem._props.Grids[0]._props.cellsV = stashSize; + } + else + { + context.logger.warning(`HideoutOptions: doBiggerStash: Failed to modify stash with Tpl ${itemTpl}, skipping`); + } + } + + //Change construction price + const hideoutStashStages = context.tables.hideout.areas.find((area) => area.type === HideoutAreas.STASH).stages; + + for (const stage of Object.values(hideoutStashStages)) + { + const currencyRequirements = stage.requirements.filter((req) => currencies.includes(req.templateId)) + for (const currencyRequirement of currencyRequirements) + { + if (currencyRequirement.count) + { + currencyRequirement.count *= config.stashUpgradeCostFactor; + } + } + + const loyaltylevels = stage.requirements.filter((req) => req.loyaltyLevel !== undefined) + for (const loyaltyLevel of loyaltylevels) + { + if (loyaltyLevel.loyaltyLevel !== undefined) + { + loyaltyLevel.loyaltyLevel += config.stashUpgradeLoyaltyDelta; + } + } + } +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.js new file mode 100644 index 0000000..10d66a1 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.js @@ -0,0 +1,197 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.mod = void 0; +const fs_1 = __importDefault(require("fs")); +// eslint-disable-next-line @typescript-eslint/naming-convention +const json5_1 = __importDefault(require("C:/snapshot/project/node_modules/json5")); +const contex_1 = require("./contex"); +const core_1 = require("./algoRebalancing/core"); +const secureContainer_1 = require("./miscChanges/secureContainer"); +const utils_1 = require("./utils"); +const stashChanges_1 = require("./miscChanges/stashChanges"); +const fleaChanges_1 = require("./miscChanges/fleaChanges"); +const buildChanges_1 = require("./miscChanges/buildChanges"); +const skills_1 = require("./miscChanges/skills"); +const craftChanges_1 = require("./miscChanges/craftChanges"); +const refRepRework_1 = require("./miscChanges/refRepRework"); +const buffSICCCase_1 = require("./miscChanges/buffSICCCase"); +const firChanges_1 = require("./miscChanges/firChanges"); +const customTrades_1 = require("./miscChanges/customTrades"); +const bitcoinChanges_1 = require("./miscChanges/bitcoinChanges"); +const priceChanging_1 = require("./miscChanges/priceChanging"); +const loggerWrapper_1 = require("./loggerWrapper"); +const stackSizeChanges_1 = require("./miscChanges/stackSizeChanges"); +const startingRep_1 = require("./miscChanges/startingRep"); +const path_1 = require("path"); +const questRewards_1 = require("./miscChanges/questRewards."); +class Mod { + context; + preSptLoad(container) { + this.context = new contex_1.Context(); + const preLoader = container.resolve("PreSptModLoader"); + this.context.logger = new loggerWrapper_1.LoggerWrapper(container.resolve("WinstonLogger"), preLoader); + this.safelyReadConfig(); + this.setupRouterForClientSideData(container); + this.safeApplyEarlyModifications(container); + } + postDBLoad(container) { + this.context.database = container.resolve("DatabaseServer"); + this.context.sptConfig = container.resolve("ConfigServer"); + this.context.tables = this.context.database.getTables(); + this.context.itemHelper = container.resolve("ItemHelper"); + container.resolve("PresetController").initialize(); + this.context.presetHelper = container.resolve("PresetHelper"); + this.context.hashUtil = container.resolve("HashUtil"); + this.safelyApplyChanges(); + this.context.logger.success("Finished applying all changes!"); + } + /////////////////////////////////////////////////////////////////////////// + setupRouterForClientSideData(container) { + const staticRouter = container.resolve("StaticRouterModService"); + try { + staticRouter.registerStaticRouter("skillpointsconfigrouter", [ + { + url: "/server-config-router/skillpoints", + // eslint-disable-next-line @typescript-eslint/no-unused-vars + action: (_url, _info, _sessionID, _output) => { + return Promise.resolve(JSON.stringify({ response: this.context.config.skillChanges.SkillPointsSystem })); + } + } + ], "skillpointsconfig"); + } + catch (error) { + this.context.logger.error("Failed to send skill points config data over to the client!"); + if (this.context.config.dev.showFullError) { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + try { + staticRouter.registerStaticRouter("skillsconfigrouter", [ + { + url: "/server-config-router/skillsconfig", + // eslint-disable-next-line @typescript-eslint/no-unused-vars + action: (_url, _info, _sessionID, _output) => { + return Promise.resolve(JSON.stringify({ response: this.context.config.skillChanges.CustomMultipliers })); + } + } + ], "skillsconfig"); + } + catch (error) { + this.context.logger.error("Failed to send skills config data over to the client!"); + if (this.context.config.dev.showFullError) { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + } + safeApplyEarlyModifications(container) { + try { + if (this.context.config.refChanges.enable && this.context.config.refChanges.refStandingOnKill.enable) + (0, refRepRework_1.gainRefRepOnKill)(this.context, container); + } + catch (error) { + this.context.logger.error("Failed to inject ref rep on PMC kill function!"); + if (this.context.config.dev.showFullError) { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + //For ref buying items for GP coins. Requires client patching for fixing wrong GP icon + try { + (0, refRepRework_1.addSupportForGPTraders)(this.context, container); + } + catch (error) { + this.context.logger.error("Failed to patch in support for GP trading!"); + if (this.context.config.dev.showFullError) { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + } + safelyReadConfig() { + try { + const fileContent = fs_1.default.readFileSync((0, path_1.join)((0, utils_1.getModFolder)(), "/config/config.json5"), "utf-8"); + this.context.config = json5_1.default.parse(fileContent); + } + catch (error) { + this.context.logger.error("Main config file failed to load!"); + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + safelyApplyChanges() { + const cfg = this.context.config; + const log = cfg.dev.muteProgressOnServerLoad ? null : this.context.logger; + log?.info("Running algorithmical rebalancing..."); + this.safelyRunIf(cfg.algorithmicalRebalancing.enable, () => (0, core_1.algorithmicallyRebalance)(this.context), "Failed to run algorithmical rebalancing!"); + log?.info("Done!"); + log?.info("Changing stack sizes..."); + this.safelyRunIf(true, () => (0, stackSizeChanges_1.changeStackSizes)(this.context), "Failed to apply changes to stack sizes!"); + log?.info("Done!"); + log?.info("Applying secure container changes..."); + this.safelyRunIf(cfg.secureContainerProgression.enable, () => (0, secureContainer_1.applySecureContainerChanges)(this.context), "Failed to apply secure container changes!"); + log?.info("Done!"); + log?.info("Applying stash progression changes..."); + this.safelyRunIf(cfg.stashProgression.enable, () => (0, stashChanges_1.changeStashProgression)(this.context), "Failed to apply stash progression changes!"); + log?.info("Done!"); + log?.info("Disabling flea market..."); + this.safelyRunIf(cfg.fleaMarketChanges.disableFleaMarket, () => (0, fleaChanges_1.disableFleaMarket)(this.context), "Failed to disable flea market!"); + log?.info("Done!"); + log?.info("Applying changes to hideout build costs..."); + this.safelyRunIf(cfg.hideoutBuildsChanges.enable, () => (0, buildChanges_1.changeHidehoutBuildCosts)(this.context), "Failed to apply changes to hideout build costs!"); + log?.info("Done!"); + log?.info("Applying changes to skills..."); + this.safelyRunIf(cfg.skillChanges.enable, () => (0, skills_1.changeSkills)(this.context), "Failed to apply changes to skills!"); + log?.info("Done!"); + log?.info("Applying changes to craft times and output counts..."); + this.safelyRunIf(true, () => (0, craftChanges_1.changeCrafts)(this.context), "Failed to apply changes to craft times and output counts!"); + log?.info("Done!"); + log?.info("Applying changes to item prices..."); + this.safelyRunIf(true, () => (0, priceChanging_1.changePrices)(this.context), "Failed to apply changes to item prices!"); + log?.info("Done!"); + log?.info("Applying changes to SICC container..."); + this.safelyRunIf(cfg.SICCBuffs.enable, () => (0, buffSICCCase_1.buffSICCCase)(this.context), "Failed to apply changes to SICC container!"); + log?.info("Done!"); + log?.info("Removing FiR requirements..."); + this.safelyRunIf(cfg.misc.removeFirFromQuests, () => (0, firChanges_1.removeFirFromQuests)(this.context), "Failed to remove FiR requirements form quests!"); + this.safelyRunIf(cfg.misc.removeFirFromQuests, () => (0, firChanges_1.removeFirFromRepeatables)(this.context), "Failed to remove FiR requirements from repeatable quests!"); + this.safelyRunIf(cfg.misc.removeFirFromHideout, () => (0, firChanges_1.removeFirFromHideout)(this.context), "Failed to remove FiR requirements from hideout builds!"); + this.safelyRunIf(cfg.misc.removeFirFromFlea, () => (0, firChanges_1.removeFirFromFlea)(this.context), "Failed to remove FiR requirements from flea market listings!"); + log?.info("Done!"); + log?.info("Adding custom trades..."); + this.safelyRunIf(cfg.misc.addCustomTrades, () => (0, customTrades_1.addCustomTrades)(this.context), "Failed to add custom trades!"); + log?.info("Done!"); + log?.info("Applying changes to bitcoin farming..."); + this.safelyRunIf(cfg.bitcoinChanges.enable, () => (0, bitcoinChanges_1.changeBitcoinFarming)(this.context), "Failed to apply changes to bitcoin farming!"); + log?.info("Done!"); + log?.info("Setting initial trader standing..."); + this.safelyRunIf(cfg.bitcoinChanges.enable, () => (0, startingRep_1.setStartingReputation)(this.context), "Failed to set initial trader standing!"); + log?.info("Done!"); + log?.info("Applying changes to Ref item purchasing..."); + this.safelyRunIf(cfg.refChanges.enable, () => (0, refRepRework_1.changeRefPurchasingOptions)(this.context), "Failed to apply changes to Ref item purchasing!"); + log?.info("Done!"); + log?.info("Adding additional quest rewards..."); + this.safelyRunIf(cfg.misc.enableExtraQuestRewards, () => (0, questRewards_1.addAdditionalQuestRewards)(this.context), "Failed to add additional quest rewards!"); + log?.info("Done!"); + } + safelyRunIf(condition, func, message) { + try { + if (condition) { + func(); + } + } + catch (error) { + this.context.logger.error(message); + if (this.context.config.dev.showFullError) { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + } +} +exports.mod = new Mod(); +//# sourceMappingURL=mod.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.js.map new file mode 100644 index 0000000..bd49cc1 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "mod.js", + "sourceRoot": "", + "sources": [ + "mod.ts" + ], + "names": [], + "mappings": ";;;;;;AAAA,4CAAoB;AACpB,gEAAgE;AAChE,mFAA2D;AAK3D,qCAAmC;AACnC,iDAAkE;AAClE,mEAA4E;AAE5E,mCAAuC;AACvC,6DAAoE;AACpE,2DAA8D;AAC9D,6DAAsE;AACtE,iDAAoD;AAEpD,6DAA0D;AAC1D,6DAAkH;AAClH,6DAA0D;AAC1D,yDAAkI;AAClI,6DAA6D;AAC7D,iEAAoE;AACpE,+DAA2D;AAC3D,mDAAgD;AAChD,qEAAkE;AAGlE,2DAAkE;AAElE,+BAA4B;AAE5B,8DAAwE;AAIxE,MAAM,GAAG;IAEG,OAAO,CAAU;IAEzB,UAAU,CAAC,SAA8B;QAErC,IAAI,CAAC,OAAO,GAAG,IAAI,gBAAO,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAkB,iBAAiB,CAAC,CAAC;QACxE,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,6BAAa,CAAC,SAAS,CAAC,OAAO,CAAU,eAAe,CAAC,EAAE,SAAS,CAAC,CAAC;QAEhG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,CAAC,4BAA4B,CAAC,SAAS,CAAC,CAAC;QAE7C,IAAI,CAAC,2BAA2B,CAAC,SAAS,CAAC,CAAC;IAChD,CAAC;IAED,UAAU,CAAC,SAA8B;QAErC,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAiB,gBAAgB,CAAC,CAAC;QAC5E,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAe,cAAc,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;QACxD,IAAI,CAAC,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,OAAO,CAAa,YAAY,CAAC,CAAC;QACtE,SAAS,CAAC,OAAO,CAAmB,kBAAkB,CAAC,CAAC,UAAU,EAAE,CAAC;QACrE,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,OAAO,CAAe,cAAc,CAAC,CAAC;QAC5E,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAW,UAAU,CAAC,CAAC;QAEhE,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAClE,CAAC;IAED,2EAA2E;IAE3E,4BAA4B,CAAC,SAA8B;QAEvD,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAyB,wBAAwB,CAAC,CAAC;QAEzF,IACA,CAAC;YACG,YAAY,CAAC,oBAAoB,CAC7B,yBAAyB,EACzB;gBACI;oBACI,GAAG,EAAE,mCAAmC;oBACxC,6DAA6D;oBAC7D,MAAM,EAAE,CAAC,IAAY,EAAE,KAAU,EAAE,UAAkB,EAAE,OAAe,EAAmB,EAAE;wBAEvF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,iBAAiB,EAAC,CAAC,CAAC,CAAC;oBAC3G,CAAC;iBACJ;aACJ,EACD,mBAAmB,CACtB,CAAA;QACL,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;YACzF,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EACzC,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC;gBACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;QACL,CAAC;QAED,IACA,CAAC;YACG,YAAY,CAAC,oBAAoB,CAC7B,oBAAoB,EACpB;gBACI;oBACI,GAAG,EAAE,oCAAoC;oBACzC,6DAA6D;oBAC7D,MAAM,EAAE,CAAC,IAAY,EAAE,KAAU,EAAE,UAAkB,EAAE,OAAe,EAAmB,EAAE;wBAEvF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,iBAAiB,EAAC,CAAC,CAAC,CAAC;oBAC3G,CAAC;iBACJ;aACJ,EACD,cAAc,CACjB,CAAA;QACL,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;YACnF,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EACzC,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC;gBACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;QACL,CAAC;IACL,CAAC;IAED,2BAA2B,CAAC,SAA8B;QAEtD,IACA,CAAC;YACG,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,MAAM;gBAAE,IAAA,+BAAgB,EAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACpJ,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;YAC5E,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EACzC,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC;gBACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;QACL,CAAC;QAED,sFAAsF;QACtF,IACA,CAAC;YACG,IAAA,qCAAsB,EAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;YACxE,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EACzC,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC;gBACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;QACL,CAAC;IAEL,CAAC;IAED,gBAAgB;QAEZ,IACA,CAAC;YACG,MAAM,WAAW,GAAG,YAAE,CAAC,YAAY,CAAC,IAAA,WAAI,EAAC,IAAA,oBAAY,GAAE,EAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC,CAAC;YAC1F,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,eAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAA;YAC7D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC;YACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAChH,CAAC;IACL,CAAC;IAED,kBAAkB;QAEd,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAChC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QAE1E,GAAG,EAAE,IAAI,CAAC,sCAAsC,CAAC,CAAC;QAClD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,wBAAwB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,+BAAwB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,0CAA0C,CAAC,CAAC;QAChJ,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACrC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAA,mCAAgB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,yCAAyC,CAAC,CAAC;QACxG,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,sCAAsC,CAAC,CAAC;QAClD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,0BAA0B,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,6CAA2B,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,2CAA2C,CAAC,CAAC;QACtJ,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACnD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,qCAAsB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,4CAA4C,CAAC,CAAC;QACxI,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACtC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,IAAA,+BAAiB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,gCAAgC,CAAC,CAAC;QACnI,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACxD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,oBAAoB,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,uCAAwB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,iDAAiD,CAAC,CAAC;QACnJ,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,qBAAY,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,oCAAoC,CAAC,CAAC;QAClH,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,sDAAsD,CAAC,CAAC;QAClE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAA,2BAAY,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,2DAA2D,CAAC,CAAC;QACtH,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAChD,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAA,4BAAY,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,yCAAyC,CAAC,CAAC;QACpG,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACnD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,2BAAY,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,4CAA4C,CAAC,CAAC;QACvH,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC1C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,IAAA,gCAAmB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,gDAAgD,CAAC,CAAC;QAC1I,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,IAAA,qCAAwB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,2DAA2D,CAAC,CAAA;QACzJ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,IAAA,iCAAoB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,wDAAwD,CAAC,CAAC;QACpJ,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,IAAA,8BAAiB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,8DAA8D,CAAC,CAAC;QACpJ,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACrC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC,IAAA,8BAAe,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,8BAA8B,CAAC,CAAC;QAChH,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,wCAAwC,CAAC,CAAC;QACpD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,qCAAoB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,6CAA6C,CAAC,CAAC;QACrI,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAChD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,mCAAqB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,wCAAwC,CAAC,CAAC;QACjI,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACxD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,yCAA0B,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,iDAAiD,CAAC,CAAC;QAC3I,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnB,GAAG,EAAE,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAChD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,IAAA,wCAAyB,EAAC,IAAI,CAAC,OAAO,CAAC,EAAE,yCAAyC,CAAC,CAAC;QAC7I,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAEvB,CAAC;IAED,WAAW,CAAC,SAAkB,EAAE,IAAgB,EAAE,OAAe;QAE7D,IACA,CAAC;YACG,IAAI,SAAS,EACb,CAAC;gBACG,IAAI,EAAE,CAAC;YACX,CAAC;QACL,CAAC;QACD,OAAO,KAAK,EACZ,CAAC;YACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EACzC,CAAC;gBACG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,KAAK,CAAC,CAAC;gBACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC;YAChH,CAAC;QACL,CAAC;IACL,CAAC;CAEJ;AAEY,QAAA,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.ts new file mode 100644 index 0000000..cb03bf7 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/mod.ts @@ -0,0 +1,274 @@ +import fs from "fs"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import JSON5 from "json5"; +import { DependencyContainer } from "tsyringe"; +import { IPostDBLoadMod } from "@spt/models/external/IPostDBLoadMod"; +import { DatabaseServer } from "@spt/servers/DatabaseServer"; +import { ILogger } from "@spt/models/spt/utils/ILogger"; +import { Context } from "./contex"; +import { algorithmicallyRebalance } from "./algoRebalancing/core"; +import { applySecureContainerChanges } from "./miscChanges/secureContainer"; +import { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; +import { getModFolder } from "./utils"; +import { changeStashProgression } from "./miscChanges/stashChanges"; +import { disableFleaMarket } from "./miscChanges/fleaChanges"; +import { changeHidehoutBuildCosts } from "./miscChanges/buildChanges"; +import { changeSkills } from "./miscChanges/skills"; +import { ItemHelper } from "@spt/helpers/ItemHelper"; +import { changeCrafts } from "./miscChanges/craftChanges"; +import { addSupportForGPTraders, gainRefRepOnKill, changeRefPurchasingOptions } from "./miscChanges/refRepRework"; +import { buffSICCCase } from "./miscChanges/buffSICCCase"; +import { removeFirFromFlea, removeFirFromHideout, removeFirFromQuests, removeFirFromRepeatables } from "./miscChanges/firChanges"; +import { addCustomTrades } from "./miscChanges/customTrades"; +import { changeBitcoinFarming } from "./miscChanges/bitcoinChanges"; +import { changePrices } from "./miscChanges/priceChanging"; +import { LoggerWrapper } from "./loggerWrapper"; +import { changeStackSizes } from "./miscChanges/stackSizeChanges"; +import { PresetHelper } from "@spt/helpers/PresetHelper"; +import { PresetController } from "@spt/controllers/PresetController"; +import { setStartingReputation } from "./miscChanges/startingRep"; +import { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; +import { join } from "path"; +import { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; +import { addAdditionalQuestRewards } from "./miscChanges/questRewards."; +import { ConfigServer } from "@spt/servers/ConfigServer"; +import { HashUtil } from "@spt/utils/HashUtil"; + +class Mod implements IPostDBLoadMod, IPreSptLoadMod +{ + private context: Context; + + preSptLoad(container: DependencyContainer): void + { + this.context = new Context(); + const preLoader = container.resolve("PreSptModLoader"); + this.context.logger = new LoggerWrapper(container.resolve("WinstonLogger"), preLoader); + + this.safelyReadConfig(); + + this.setupRouterForClientSideData(container); + + this.safeApplyEarlyModifications(container); + } + + postDBLoad(container: DependencyContainer): void + { + this.context.database = container.resolve("DatabaseServer"); + this.context.sptConfig = container.resolve("ConfigServer"); + this.context.tables = this.context.database.getTables(); + this.context.itemHelper = container.resolve("ItemHelper"); + container.resolve("PresetController").initialize(); + this.context.presetHelper = container.resolve("PresetHelper"); + this.context.hashUtil = container.resolve("HashUtil"); + + this.safelyApplyChanges(); + + this.context.logger.success("Finished applying all changes!"); + } + + /////////////////////////////////////////////////////////////////////////// + + setupRouterForClientSideData(container: DependencyContainer): void + { + const staticRouter = container.resolve("StaticRouterModService"); + + try + { + staticRouter.registerStaticRouter( + "skillpointsconfigrouter", + [ + { + url: "/server-config-router/skillpoints", + // eslint-disable-next-line @typescript-eslint/no-unused-vars + action: (_url: string, _info: any, _sessionID: string, _output: string): Promise => + { + return Promise.resolve(JSON.stringify({response: this.context.config.skillChanges.SkillPointsSystem})); + } + } + ], + "skillpointsconfig" + ) + } + catch (error) + { + this.context.logger.error("Failed to send skill points config data over to the client!"); + if (this.context.config.dev.showFullError) + { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + + try + { + staticRouter.registerStaticRouter( + "skillsconfigrouter", + [ + { + url: "/server-config-router/skillsconfig", + // eslint-disable-next-line @typescript-eslint/no-unused-vars + action: (_url: string, _info: any, _sessionID: string, _output: string): Promise => + { + return Promise.resolve(JSON.stringify({response: this.context.config.skillChanges.CustomMultipliers})); + } + } + ], + "skillsconfig" + ) + } + catch (error) + { + this.context.logger.error("Failed to send skills config data over to the client!"); + if (this.context.config.dev.showFullError) + { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + } + + safeApplyEarlyModifications(container: DependencyContainer): void + { + try + { + if (this.context.config.refChanges.enable && this.context.config.refChanges.refStandingOnKill.enable) gainRefRepOnKill(this.context, container); + } + catch (error) + { + this.context.logger.error("Failed to inject ref rep on PMC kill function!"); + if (this.context.config.dev.showFullError) + { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + + //For ref buying items for GP coins. Requires client patching for fixing wrong GP icon + try + { + addSupportForGPTraders(this.context, container); + } + catch (error) + { + this.context.logger.error("Failed to patch in support for GP trading!"); + if (this.context.config.dev.showFullError) + { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + + } + + safelyReadConfig(): void + { + try + { + const fileContent = fs.readFileSync(join(getModFolder(),"/config/config.json5"), "utf-8"); + this.context.config = JSON5.parse(fileContent); + } + catch (error) + { + this.context.logger.error("Main config file failed to load!") + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + + safelyApplyChanges(): void + { + const cfg = this.context.config; + const log = cfg.dev.muteProgressOnServerLoad ? null : this.context.logger; + + log?.info("Running algorithmical rebalancing..."); + this.safelyRunIf(cfg.algorithmicalRebalancing.enable, () => algorithmicallyRebalance(this.context), "Failed to run algorithmical rebalancing!"); + log?.info("Done!"); + + log?.info("Changing stack sizes..."); + this.safelyRunIf(true, () => changeStackSizes(this.context), "Failed to apply changes to stack sizes!"); + log?.info("Done!"); + + log?.info("Applying secure container changes..."); + this.safelyRunIf(cfg.secureContainerProgression.enable, () => applySecureContainerChanges(this.context), "Failed to apply secure container changes!"); + log?.info("Done!"); + + log?.info("Applying stash progression changes..."); + this.safelyRunIf(cfg.stashProgression.enable, () => changeStashProgression(this.context), "Failed to apply stash progression changes!"); + log?.info("Done!"); + + log?.info("Disabling flea market..."); + this.safelyRunIf(cfg.fleaMarketChanges.disableFleaMarket, () => disableFleaMarket(this.context), "Failed to disable flea market!"); + log?.info("Done!"); + + log?.info("Applying changes to hideout build costs..."); + this.safelyRunIf(cfg.hideoutBuildsChanges.enable, () => changeHidehoutBuildCosts(this.context), "Failed to apply changes to hideout build costs!"); + log?.info("Done!"); + + log?.info("Applying changes to skills..."); + this.safelyRunIf(cfg.skillChanges.enable, () => changeSkills(this.context), "Failed to apply changes to skills!"); + log?.info("Done!"); + + log?.info("Applying changes to craft times and output counts..."); + this.safelyRunIf(true, () => changeCrafts(this.context), "Failed to apply changes to craft times and output counts!"); + log?.info("Done!"); + + log?.info("Applying changes to item prices..."); + this.safelyRunIf(true, () => changePrices(this.context), "Failed to apply changes to item prices!"); + log?.info("Done!"); + + log?.info("Applying changes to SICC container..."); + this.safelyRunIf(cfg.SICCBuffs.enable, () => buffSICCCase(this.context), "Failed to apply changes to SICC container!"); + log?.info("Done!"); + + log?.info("Removing FiR requirements..."); + this.safelyRunIf(cfg.misc.removeFirFromQuests, () => removeFirFromQuests(this.context), "Failed to remove FiR requirements form quests!"); + this.safelyRunIf(cfg.misc.removeFirFromQuests, () => removeFirFromRepeatables(this.context), "Failed to remove FiR requirements from repeatable quests!") + this.safelyRunIf(cfg.misc.removeFirFromHideout, () => removeFirFromHideout(this.context), "Failed to remove FiR requirements from hideout builds!"); + this.safelyRunIf(cfg.misc.removeFirFromFlea, () => removeFirFromFlea(this.context), "Failed to remove FiR requirements from flea market listings!"); + log?.info("Done!"); + + log?.info("Adding custom trades..."); + this.safelyRunIf(cfg.misc.addCustomTrades, () => addCustomTrades(this.context), "Failed to add custom trades!"); + log?.info("Done!"); + + log?.info("Applying changes to bitcoin farming..."); + this.safelyRunIf(cfg.bitcoinChanges.enable, () => changeBitcoinFarming(this.context), "Failed to apply changes to bitcoin farming!"); + log?.info("Done!"); + + log?.info("Setting initial trader standing..."); + this.safelyRunIf(cfg.bitcoinChanges.enable, () => setStartingReputation(this.context), "Failed to set initial trader standing!"); + log?.info("Done!"); + + log?.info("Applying changes to Ref item purchasing..."); + this.safelyRunIf(cfg.refChanges.enable, () => changeRefPurchasingOptions(this.context), "Failed to apply changes to Ref item purchasing!"); + log?.info("Done!"); + + log?.info("Adding additional quest rewards..."); + this.safelyRunIf(cfg.misc.enableExtraQuestRewards, () => addAdditionalQuestRewards(this.context), "Failed to add additional quest rewards!"); + log?.info("Done!"); + + } + + safelyRunIf(condition: boolean, func: () => void, message: string): void + { + try + { + if (condition) + { + func(); + } + } + catch (error) + { + this.context.logger.error(message); + if (this.context.config.dev.showFullError) + { + this.context.logger.error("Error Details:" + error); + this.context.logger.error("Stack Trace:\n" + (error instanceof Error ? error.stack : "No stack available")); + } + } + } + +} + +export const mod = new Mod(); diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.js b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.js new file mode 100644 index 0000000..3d89e60 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.js @@ -0,0 +1,286 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.currencies = void 0; +exports.getDogtagsList = getDogtagsList; +exports.findTrades = findTrades; +exports.getDefaultAttachments = getDefaultAttachments; +exports.unrollAttachments = unrollAttachments; +exports.containsAttachment = containsAttachment; +exports.indexById = indexById; +exports.canBePurchased = canBePurchased; +exports.canAllAttachmentsBePurchased = canAllAttachmentsBePurchased; +exports.lockBehindQuest = lockBehindQuest; +exports.setAreaLevelRequirement = setAreaLevelRequirement; +exports.isQuestLockedCraft = isQuestLockedCraft; +exports.addToLocale = addToLocale; +exports.shareSameNiche = shareSameNiche; +exports.loyaltyFromScore = loyaltyFromScore; +exports.setLoyalty = setLoyalty; +exports.isBarterTrade = isBarterTrade; +exports.isQuestLocked = isQuestLocked; +exports.getModFolder = getModFolder; +exports.bestFiremode = bestFiremode; +exports.pickBestFiremode = pickBestFiremode; +const RewardType_1 = require("C:/snapshot/project/obj/models/enums/RewardType"); +const path_1 = require("path"); +exports.currencies = [ + "5449016a4bdc2d6f028b456f", //Roubles + "5696686a4bdc2da3298b456a", //Dollars + "569668774bdc2da2298b4568", //Euroes + "5d235b4d86f7742e017bc88a", //GP Coin + "6656560053eaaa7a23349c86" //Lega medal +]; +const purchasability = {}; +function getDogtagsList(context) { + const list = []; + for (const [id, item] of Object.entries(context.tables.templates.items)) { + if (item._props.DogTagQualities) + list.push(id); + } + return list; +} +//Find and return the trader-trade pair that sell the given item +function findTrades(itemId, context) { + const found = []; + for (const trader of Object.values(context.tables.traders)) { + if (trader.assort == null) + continue; + for (const trade of trader.assort.items) { + if (trade._tpl == itemId) + found.push({ + trader: trader, + trade: trade + }); + } + } + return found; +} +function getDefaultAttachments(weaponID, context) { + const preset = context.presetHelper.getDefaultWeaponPresets[weaponID]; + if (preset == null || preset == undefined) + return []; + return preset._items.map((item) => item._tpl); +} +//Returns the list of all attachments connected to the given item (recursively) +function unrollAttachments(item, assort) { + const attachments = []; + const newAttachments = assort.filter((candidate) => candidate.parentId == item._id && candidate._tpl != item._tpl); + attachments.push(...newAttachments); + for (const att of newAttachments) { + attachments.push(...unrollAttachments(att, assort)); + } + return attachments; +} +//Does the given item from the given assort contain the given attachment? (as an item table id) +function containsAttachment(item, assort, attachmentID, context) { + //Shortcut items without slots + const template = context.tables.templates.items[item._tpl]; + if (template == null) { + context.logger.error(`Trader item ${item} with table ID ${item._tpl} couldn't be found in the tables!\nMaybe some mod is adding a trade that sells a non-existing item?`); + return false; + } + if (template._props.Slots == null) + return false; + if (template._props.Slots.length == 0) + return false; + return unrollAttachments(item, assort).map((item) => item._tpl).includes(attachmentID); +} +function indexById(byTier) { + const byId = {}; + for (const items of Object.values(byTier)) + for (const item of items) { + byId[item.trade._id] = item; + } + return byId; +} +//Can the given item be purchased before or at the given loyalty tier cutoff? Can optionally exclude barters +//Trade IDs in skip will not be considered as valid purchases (mostly to avoid self-referencing when used in canAllAttachmentsBePurchased) +//If an item is present in tierOverrides the corresponding loyalty level will be used instead of that of the trader +//Careful about the dynamic programming part with purchasability. If this function previously found an item to be purchasable it will say it is again without checking even if things changed +function canBePurchased(itemID, excludeBarters, excludeQuestlocks, tierCutoff, skip, tierOverrides, context) { + if (purchasability[itemID] <= tierCutoff) + return true; + for (const trader of Object.values(context.tables.traders)) { + if (trader.assort == null) + continue; + for (const trade of trader.assort.items) { + const loyalty = tierOverrides[trade._id] != null ? tierOverrides[trade._id].score : trader.assort.loyal_level_items[trade._id]; + if (loyaltyFromScore(loyalty, context.config.algorithmicalRebalancing.clampToMaxLevel) > tierCutoff) + continue; + if ((trade._tpl === itemID && trader.assort.barter_scheme[trade._id] != null) + || containsAttachment(trade, trader.assort.items, itemID, context)) //It's a match! + { + if (excludeBarters && isBarterTrade(trade, trader)) + continue; + if (excludeQuestlocks && isQuestLocked(trade, trader)) + continue; + if (skip.includes(trade._id)) + continue; + if (itemID in purchasability) { + purchasability[itemID] = Math.min(purchasability[itemID], loyalty); + } + else { + purchasability[itemID] = loyalty; + } + return true; + } + } + } + return false; +} +//Can all the attachments on the given item be purchased before the given loyalty tier cutoff? Can optionally exclude barters +//Attachments in the skip list will not be checked +function canAllAttachmentsBePurchased(item, assort, excludeBarters, excludeQuestlocks, tierCutoff, skip, tierOverrides, context) { + const attachments = unrollAttachments(item, assort); + for (const att of attachments) { + if (skip.includes(att._tpl)) + continue; + if (!canBePurchased(att._tpl, excludeBarters, excludeQuestlocks, tierCutoff, [item._id], tierOverrides, context)) { + //context.logger.info(`Unpurchasable attachment: ${context.tables.templates.items[att._tpl]._name}`) + return false; + } + } + //context.logger.success(`Did not penalize ${context.tables.templates.items[item._tpl]._name}`); + return true; +} +function lockBehindQuest(context, traderID, trade, lock, itemID, rewardID, targetID) { + const trader = context.tables.traders[traderID]; + trader.questassort.success[trade] = lock; + const questRewards = context.tables.templates.quests[lock].rewards.Success; + questRewards.push({ + type: RewardType_1.RewardType.ASSORTMENT_UNLOCK, + index: questRewards.length, + unknown: false, + traderId: traderID, + target: targetID, + items: [ + { + _id: targetID, + _tpl: itemID + } + ], + id: rewardID, + availableInGameEditions: [] + }); +} +function setAreaLevelRequirement(craft, level) { + for (const req of craft.requirements) { + if (req.requiredLevel != null) + req.requiredLevel = level; + } +} +function isQuestLockedCraft(craft) { + for (const req of craft.requirements) { + if (req.questId != null) + return true; + } + return false; +} +function addToLocale(locales, id, name, shortname, description) { + for (const locale of Object.values(locales)) { + locale[`${id} Name`] = name; + locale[`${id} ShortName`] = shortname; + locale[`${id} Description`] = description; + } +} +function shareSameNiche(a, b, aTrader, bTrader, context) { + const c = context.config.algorithmicalRebalancing.weaponRules.upshiftRules; + const aTempl = context.tables.templates.items[a._tpl]; + const bTempl = context.tables.templates.items[b._tpl]; + if (c.devideNicheByFiremode) { + if (bestFiremode(aTempl) != bestFiremode(bTempl)) + return false; + } + if (c.devideNicheByCaliber) { + if (aTempl._props.ammoCaliber != bTempl._props.ammoCaliber) + return false; + } + if (c.devideNicheByBarterType) { + if (isBarterTrade(a, aTrader) != isBarterTrade(b, bTrader)) + return false; + } + if (c.devideNicheByQuestLock) { + if (isQuestLocked(a, aTrader) != isQuestLocked(b, bTrader)) + return false; + } + return true; +} +function loyaltyFromScore(score, capToMax) { + const maxLevel = capToMax ? 4 : 999; //Set max level to a dummy value if config states that loyalty > 4 is to be hidden from all trader levels + return Math.max(1, Math.min(maxLevel, Math.floor(score))); //Floor and clamp between 1 and the max level +} +//Sets the loyalty level of the given sale item on the given trader +//If capToMax is false the loyalty level will be allowed to go beyond 4 +function setLoyalty(itemID, loyalty, trader, capToMax) { + if (loyalty == null) + loyalty = 0; + trader.assort.loyal_level_items[itemID] = loyaltyFromScore(loyalty, capToMax); +} +//Checks if the given trade item is offered for barter on the given trader +//Anything that can't be purchased for RUB, EUR, USD, GP or Lega Medals is considered a barter +function isBarterTrade(trade, trader) { + const scheme = trader.assort.barter_scheme[trade._id]; + if (scheme == null) + return false; + for (const ask of scheme) + for (const ask1 of ask) //Cursed, i know + { + if (!exports.currencies.includes(ask1._tpl)) + return true; + } + return false; +} +//Checks if the given trade item is quest locked on the given trader +function isQuestLocked(trade, trader) { + const questLocks = [ + ...Object.keys(trader.questassort["success"]), + ...Object.keys(trader.questassort["started"]), + ...Object.keys(trader.questassort["fail"]) + ]; + for (const questLockedTrade of questLocks) { + if (trade._id == questLockedTrade) + return true; + } + return false; +} +//Returns the path to the mod folder +function getModFolder() { + return (0, path_1.join)(__dirname, "../"); // "./user/mods/Gekos_BetterProgression"; +} +function bestFiremode(item) { + const res = pickBestFiremode(item._props.weapFireType, item._props.BoltAction || !item._props.CanQueueSecondShot); + if (res == "") + console.log(item); + return res; +} +//Returns one of "manual", "semiauto" or "fullauto" depending on which is the best fire mode available between the provided choices +function pickBestFiremode(modes, isManual) { + if (modes == null) { + return ""; + } + const ranks = { + "none": -9999, + "manual": 0, + "doublet": 1, + "semiauto": 1, + "doubleaction": 1, + "single": isManual ? -100 : 1, + "burst": 2, + "fullauto": 3 + }; + let bestMode = "none"; + for (const mode of modes) { + if (ranks[bestMode] < ranks[mode]) + bestMode = mode; + } + if (isManual) + if (ranks[bestMode] < ranks["manual"]) + bestMode = "manual"; + //Rename for simplicity + if (bestMode == "manual" || bestMode == "pumpaction" || (isManual && bestMode == "single")) + return "manual"; + if (bestMode == "single" || bestMode == "doubleaction" || bestMode == "doublet") + return "semiauto"; + return bestMode; +} +//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.js.map b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.js.map new file mode 100644 index 0000000..2b6abcc --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "utils.js", + "sourceRoot": "", + "sources": [ + "utils.ts" + ], + "names": [], + "mappings": ";;;AAmBA,wCAUC;AAGD,gCAkBC;AAGD,sDAKC;AAGD,8CAYC;AAGD,gDAcC;AAED,8BAUC;AAMD,wCAqCC;AAID,oEAmBC;AAED,0CAwBC;AAED,0DAMC;AAED,gDAQC;AAED,kCAQC;AAED,wCA4BC;AAED,4CAIC;AAID,gCAKC;AAID,sCAWC;AAGD,sCAeC;AAGD,oCAGC;AAED,oCAKC;AAGD,4CAiCC;AAxVD,gFAA6E;AAE7E,+BAA4B;AAEf,QAAA,UAAU,GAAa;IAChC,0BAA0B,EAAE,SAAS;IACrC,0BAA0B,EAAE,SAAS;IACrC,0BAA0B,EAAE,QAAQ;IACpC,0BAA0B,EAAE,SAAS;IACrC,0BAA0B,CAAE,YAAY;CAC3C,CAAA;AAED,MAAM,cAAc,GAA2B,EAAE,CAAC;AAElD,SAAgB,cAAc,CAAC,OAAgB;IAE3C,MAAM,IAAI,GAAG,EAAE,CAAC;IAEhB,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EACvE,CAAC;QACG,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,gEAAgE;AAChE,SAAgB,UAAU,CAAC,MAAc,EAAE,OAAgB;IAEvD,MAAM,KAAK,GAAsC,EAAE,CAAC;IAEpD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAC1D,CAAC;QACG,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI;YAAE,SAAS;QAEpC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EACvC,CAAC;YACG,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM;gBAAE,KAAK,CAAC,IAAI,CAAC;oBACjC,MAAM,EAAE,MAAM;oBACd,KAAK,EAAE,KAAK;iBACf,CAAC,CAAA;QACN,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAGD,SAAgB,qBAAqB,CAAC,QAAgB,EAAE,OAAgB;IAEpE,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IACtE,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,SAAS;QAAE,OAAO,EAAE,CAAC;IACrD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,+EAA+E;AAC/E,SAAgB,iBAAiB,CAAC,IAAW,EAAE,MAAe;IAE1D,MAAM,WAAW,GAAY,EAAE,CAAC;IAEhC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,IAAI,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAE,CAAC;IACrH,WAAW,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,cAAc,EAChC,CAAC;QACG,WAAW,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,WAAW,CAAC;AACvB,CAAC;AAED,+FAA+F;AAC/F,SAAgB,kBAAkB,CAAC,IAAW,EAAE,MAAe,EAAE,YAAoB,EAAE,OAAgB;IAEnG,8BAA8B;IAC9B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE3D,IAAI,QAAQ,IAAI,IAAI,EACpB,CAAC;QACG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,IAAI,kBAAkB,IAAI,CAAC,IAAI,qGAAqG,CAAC,CAAC;QAC1K,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI;QAAE,OAAO,KAAK,CAAA;IAC/C,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAEpD,OAAO,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,CAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC7F,CAAC;AAED,SAAgB,SAAS,CAAC,MAAqC;IAE3D,MAAM,IAAI,GAAgC,EAAE,CAAC;IAE7C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,KAAK,MAAM,IAAI,IAAI,KAAK,EACnE,CAAC;YACG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAChC,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,4GAA4G;AAC5G,0IAA0I;AAC1I,mHAAmH;AACnH,6LAA6L;AAC7L,SAAgB,cAAc,CAAC,MAAc,EAAE,cAAuB,EAAE,iBAA0B,EAAE,UAAkB,EAAE,IAAc,EAAE,aAA0C,EAAE,OAAgB;IAEhM,IAAI,cAAc,CAAC,MAAM,CAAC,IAAI,UAAU;QAAE,OAAO,IAAI,CAAC;IAEtD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAC1D,CAAC;QACG,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI;YAAE,SAAS;QAEpC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EACvC,CAAC;YACG,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAC9H,IAAI,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,eAAe,CAAC,GAAG,UAAU;gBAAE,SAAS;YAE9G,IAEI,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;mBACtE,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EACpE,eAAe;aACjB,CAAC;gBACG,IAAI,cAAc,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC;oBAAE,SAAS;gBAC7D,IAAI,iBAAiB,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC;oBAAE,SAAS;gBAChE,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACvC,IAAI,MAAM,IAAI,cAAc,EAC5B,CAAC;oBACG,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;gBACvE,CAAC;qBAED,CAAC;oBACG,cAAc,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;gBACrC,CAAC;gBACD,OAAO,IAAI,CAAC;YAChB,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AAEjB,CAAC;AAED,6HAA6H;AAC7H,kDAAkD;AAClD,SAAgB,4BAA4B,CAAC,IAAW,EAAE,MAAe,EAAE,cAAuB,EAAE,iBAA0B,EAAE,UAAkB,EAAE,IAAc,EAAE,aAA0C,EAAE,OAAgB;IAG5N,MAAM,WAAW,GAAG,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEpD,KAAK,MAAM,GAAG,IAAI,WAAW,EAC7B,CAAC;QACG,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACtC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,EAChH,CAAC;YACG,oGAAoG;YACpG,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED,gGAAgG;IAEhG,OAAO,IAAI,CAAC;AAEhB,CAAC;AAED,SAAgB,eAAe,CAAC,OAAgB,EAAE,QAAgB,EAAE,KAAa,EAAE,IAAY,EAAE,MAAc,EAAE,QAAgB,EAAE,QAAgB;IAE/I,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAEzC,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAE3E,YAAY,CAAC,IAAI,CACb;QACI,IAAI,EAAE,uBAAU,CAAC,iBAAiB;QAClC,KAAK,EAAE,YAAY,CAAC,MAAM;QAC1B,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE;YACH;gBACI,GAAG,EAAE,QAAQ;gBACb,IAAI,EAAE,MAAM;aACf;SACJ;QACD,EAAE,EAAE,QAAQ;QACZ,uBAAuB,EAAE,EAAE;KAC9B,CACJ,CAAA;AACL,CAAC;AAED,SAAgB,uBAAuB,CAAC,KAAyB,EAAE,KAAa;IAE5E,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,YAAY,EACpC,CAAC;QACG,IAAI,GAAG,CAAC,aAAa,IAAI,IAAI;YAAE,GAAG,CAAC,aAAa,GAAG,KAAK,CAAC;IAC7D,CAAC;AACL,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAyB;IAExD,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,YAAY,EACpC,CAAC;QACG,IAAI,GAAG,CAAC,OAAO,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;IACzC,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAgB,WAAW,CAAC,OAA+C,EAAE,EAAS,EAAE,IAAW,EAAE,SAAgB,EAAE,WAAkB;IAErI,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAC3C,CAAC;QACG,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QAC5B,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,GAAG,SAAS,CAAC;QACtC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,GAAG,WAAW,CAAC;IAC9C,CAAC;AACL,CAAC;AAED,SAAgB,cAAc,CAAC,CAAQ,EAAE,CAAQ,EAAE,OAAgB,EAAE,OAAgB,EAAE,OAAgB;IAEnG,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,WAAW,CAAC,YAAY,CAAC;IAE3E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAEtD,IAAI,CAAC,CAAC,qBAAqB,EAC3B,CAAC;QACG,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;IACnE,CAAC;IAED,IAAI,CAAC,CAAC,oBAAoB,EAC1B,CAAC;QACG,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW;YAAE,OAAO,KAAK,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC,CAAC,uBAAuB,EAC7B,CAAC;QACG,IAAI,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC,CAAC,sBAAsB,EAC5B,CAAC;QACG,IAAI,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;IAC7E,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,SAAgB,gBAAgB,CAAC,KAAa,EAAE,QAAiB;IAE7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA,CAAC,yGAAyG;IAC7I,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,6CAA6C;AAC5G,CAAC;AAED,mEAAmE;AACnE,uEAAuE;AACvE,SAAgB,UAAU,CAAC,MAAc,EAAE,OAAe,EAAE,MAAe,EAAE,QAAiB;IAE1F,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,GAAG,CAAC,CAAC;IAEjC,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAClF,CAAC;AAED,0EAA0E;AAC1E,8FAA8F;AAC9F,SAAgB,aAAa,CAAC,KAAY,EAAE,MAAe;IAEvD,MAAM,MAAM,GAAsB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzE,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAEjC,KAAK,MAAM,GAAG,IAAI,MAAM;QAAE,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,gBAAgB;SAClE,CAAC;YACG,IAAI,CAAC,kBAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;QACrD,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,oEAAoE;AACpE,SAAgB,aAAa,CAAC,KAAY,EAAE,MAAe;IAGvD,MAAM,UAAU,GAAG;QACf,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC7C,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QAC7C,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;KAAE,CAAC;IAEjD,KAAK,MAAM,gBAAgB,IAAI,UAAU,EACzC,CAAC;QACG,IAAI,KAAK,CAAC,GAAG,IAAI,gBAAgB;YAAE,OAAO,IAAI,CAAC;IACnD,CAAC;IAED,OAAO,KAAK,CAAC;AAEjB,CAAC;AAED,oCAAoC;AACpC,SAAgB,YAAY;IAExB,OAAO,IAAA,WAAI,EAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,yCAAyC;AAC5E,CAAC;AAED,SAAgB,YAAY,CAAC,IAAmB;IAE5C,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAClH,IAAI,GAAG,IAAI,EAAE;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,GAAG,CAAC;AACf,CAAC;AAED,mIAAmI;AACnI,SAAgB,gBAAgB,CAAC,KAAe,EAAE,QAAiB;IAE/D,IAAI,KAAK,IAAI,IAAI,EACjB,CAAC;QACG,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GACX;QACI,MAAM,EAAG,CAAC,IAAI;QACd,QAAQ,EAAG,CAAC;QACZ,SAAS,EAAG,CAAC;QACb,UAAU,EAAG,CAAC;QACd,cAAc,EAAE,CAAC;QACjB,QAAQ,EAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,EAAG,CAAC;QACX,UAAU,EAAG,CAAC;KACjB,CAAC;IAEF,IAAI,QAAQ,GAAW,MAAM,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EACxB,CAAC;QACG,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;YAAE,QAAQ,GAAG,IAAI,CAAC;IACvD,CAAC;IACD,IAAI,QAAQ;QAAE,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC;YAAE,QAAQ,GAAG,QAAQ,CAAC;IAEzE,uBAAuB;IACvB,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,YAAY,IAAI,CAAC,QAAQ,IAAI,QAAQ,IAAI,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5G,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,IAAI,cAAc,IAAI,QAAQ,IAAI,SAAS;QAAE,OAAO,UAAU,CAAC;IAEnG,OAAO,QAAQ,CAAC;AAEpB,CAAC" +} \ No newline at end of file diff --git a/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.ts b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.ts new file mode 100644 index 0000000..ae2cca5 --- /dev/null +++ b/mods/Gekos Better Progression/user/mods/drunkgeko-gekos_betterprogression/src/utils.ts @@ -0,0 +1,352 @@ +import { IItem } from "@spt/models/eft/common/tables/IItem"; +import { ITemplateItem } from "@spt/models/eft/common/tables/ITemplateItem"; +import { IBarterScheme, ITrader } from "@spt/models/eft/common/tables/ITrader"; +import { Context } from "./contex"; +import { IHideoutProduction } from "@spt/models/eft/hideout/IHideoutProduction"; +import { RewardType } from "@spt/models/enums/RewardType"; +import { ChangedItem } from "./algoRebalancing/types"; +import { join } from "path"; + +export const currencies: string[] = [ + "5449016a4bdc2d6f028b456f", //Roubles + "5696686a4bdc2da3298b456a", //Dollars + "569668774bdc2da2298b4568", //Euroes + "5d235b4d86f7742e017bc88a", //GP Coin + "6656560053eaaa7a23349c86" //Lega medal +] + +const purchasability: Record = {}; + +export function getDogtagsList(context: Context): string[] +{ + const list = []; + + for (const [id, item] of Object.entries(context.tables.templates.items)) + { + if (item._props.DogTagQualities) list.push(id); + } + + return list; +} + +//Find and return the trader-trade pair that sell the given item +export function findTrades(itemId: string, context: Context): {trader: ITrader, trade: IItem}[] +{ + const found: {trader: ITrader, trade: IItem}[] = []; + + for (const trader of Object.values(context.tables.traders)) + { + if (trader.assort == null) continue; + + for (const trade of trader.assort.items) + { + if (trade._tpl == itemId) found.push({ + trader: trader, + trade: trade + }) + } + } + + return found; +} + + +export function getDefaultAttachments(weaponID: string, context: Context): string[] +{ + const preset = context.presetHelper.getDefaultWeaponPresets[weaponID]; + if (preset == null || preset == undefined) return []; + return preset._items.map((item) => item._tpl); +} + +//Returns the list of all attachments connected to the given item (recursively) +export function unrollAttachments(item: IItem, assort: IItem[]): IItem[] +{ + const attachments: IItem[] = []; + + const newAttachments = assort.filter( (candidate) => candidate.parentId == item._id && candidate._tpl != item._tpl ); + attachments.push(...newAttachments); + for (const att of newAttachments) + { + attachments.push(...unrollAttachments(att, assort)); + } + + return attachments; +} + +//Does the given item from the given assort contain the given attachment? (as an item table id) +export function containsAttachment(item: IItem, assort: IItem[], attachmentID: string, context: Context): boolean +{ + //Shortcut items without slots + const template = context.tables.templates.items[item._tpl]; + if (template == null) + { + context.logger.warning(`Trader item ${item} with table ID ${item._tpl} couldn't be found in the tables!\nMaybe some mod is adding a trade that sells a non-existing item?`); + //Skip the shortcut if the template cannot be found + } + else + { + if (template._props.Slots == null) return false + if (template._props.Slots.length == 0) return false; + } + + return unrollAttachments(item, assort).map( (item) => item._tpl ).includes(attachmentID); +} + +export function indexById(byTier: Record): Record +{ + const byId: Record = {}; + + for (const items of Object.values(byTier)) for (const item of items) + { + byId[item.trade._id] = item; + } + + return byId; +} + +//Can the given item be purchased before or at the given loyalty tier cutoff? Can optionally exclude barters +//Trade IDs in skip will not be considered as valid purchases (mostly to avoid self-referencing when used in canAllAttachmentsBePurchased) +//If an item is present in tierOverrides the corresponding loyalty level will be used instead of that of the trader +//Careful about the dynamic programming part with purchasability. If this function previously found an item to be purchasable it will say it is again without checking even if things changed +export function canBePurchased(itemID: string, excludeBarters: boolean, excludeQuestlocks: boolean, tierCutoff: number, skip: string[], tierOverrides: Record, context: Context): boolean +{ + if (purchasability[itemID] <= tierCutoff) return true; + + for (const trader of Object.values(context.tables.traders)) + { + if (trader.assort == null) continue; + + for (const trade of trader.assort.items) + { + const loyalty = tierOverrides[trade._id] != null ? tierOverrides[trade._id].score : trader.assort.loyal_level_items[trade._id] + if (loyaltyFromScore(loyalty, context.config.algorithmicalRebalancing.clampToMaxLevel) > tierCutoff) continue; + + if + ( + (trade._tpl === itemID && trader.assort.barter_scheme[trade._id] != null) + || containsAttachment(trade, trader.assort.items, itemID, context) + ) //It's a match! + { + if (excludeBarters && isBarterTrade(trade, trader)) continue; + if (excludeQuestlocks && isQuestLocked(trade, trader)) continue; + if (skip.includes(trade._id)) continue; + if (itemID in purchasability) + { + purchasability[itemID] = Math.min(purchasability[itemID], loyalty); + } + else + { + purchasability[itemID] = loyalty; + } + return true; + } + } + } + + return false; + +} + +//Can all the attachments on the given item be purchased before the given loyalty tier cutoff? Can optionally exclude barters +//Attachments in the skip list will not be checked +export function canAllAttachmentsBePurchased(item: IItem, assort: IItem[], excludeBarters: boolean, excludeQuestlocks: boolean, tierCutoff: number, skip: string[], tierOverrides: Record, context: Context): boolean +{ + + const attachments = unrollAttachments(item, assort); + + for (const att of attachments) + { + if (skip.includes(att._tpl)) continue; + if (!canBePurchased(att._tpl, excludeBarters, excludeQuestlocks, tierCutoff, [item._id], tierOverrides, context)) + { + //context.logger.info(`Unpurchasable attachment: ${context.tables.templates.items[att._tpl]._name}`) + return false; + } + } + + //context.logger.success(`Did not penalize ${context.tables.templates.items[item._tpl]._name}`); + + return true; + +} + +export function lockBehindQuest(context: Context, traderID: string, trade: string, lock: string, itemID: string, rewardID: string, targetID: string): void +{ + const trader = context.tables.traders[traderID]; + trader.questassort.success[trade] = lock; + + const questRewards = context.tables.templates.quests[lock].rewards.Success; + + questRewards.push( + { + type: RewardType.ASSORTMENT_UNLOCK, + index: questRewards.length, + unknown: false, + traderId: traderID, + target: targetID, + items: [ + { + _id: targetID, + _tpl: itemID + } + ], + id: rewardID, + availableInGameEditions: [] + } + ) +} + +export function setAreaLevelRequirement(craft: IHideoutProduction, level: number): void +{ + for (const req of craft.requirements) + { + if (req.requiredLevel != null) req.requiredLevel = level; + } +} + +export function isQuestLockedCraft(craft: IHideoutProduction): boolean +{ + for (const req of craft.requirements) + { + if (req.questId != null) return true; + } + + return false; +} + +export function addToLocale(locales: Record>, id:string, name:string, shortname:string, description:string): void +{ + for (const locale of Object.values(locales)) + { + locale[`${id} Name`] = name; + locale[`${id} ShortName`] = shortname; + locale[`${id} Description`] = description; + } +} + +export function shareSameNiche(a: IItem, b: IItem, aTrader: ITrader, bTrader: ITrader, context: Context) : boolean +{ + const c = context.config.algorithmicalRebalancing.weaponRules.upshiftRules; + + const aTempl = context.tables.templates.items[a._tpl]; + const bTempl = context.tables.templates.items[b._tpl]; + + if (c.devideNicheByFiremode) + { + if (bestFiremode(aTempl) != bestFiremode(bTempl)) return false; + } + + if (c.devideNicheByCaliber) + { + if (aTempl._props.ammoCaliber != bTempl._props.ammoCaliber) return false; + } + + if (c.devideNicheByBarterType) + { + if (isBarterTrade(a, aTrader) != isBarterTrade(b, bTrader)) return false; + } + + if (c.devideNicheByQuestLock) + { + if (isQuestLocked(a, aTrader) != isQuestLocked(b, bTrader)) return false; + } + + return true; +} + +export function loyaltyFromScore(score: number, capToMax: boolean): number +{ + const maxLevel = capToMax ? 4 : 999 //Set max level to a dummy value if config states that loyalty > 4 is to be hidden from all trader levels + return Math.max(1, Math.min(maxLevel, Math.floor(score))); //Floor and clamp between 1 and the max level +} + +//Sets the loyalty level of the given sale item on the given trader +//If capToMax is false the loyalty level will be allowed to go beyond 4 +export function setLoyalty(itemID: string, loyalty: number, trader: ITrader, capToMax: boolean): void +{ + if (loyalty == null) loyalty = 0; + + trader.assort.loyal_level_items[itemID] = loyaltyFromScore(loyalty, capToMax); +} + +//Checks if the given trade item is offered for barter on the given trader +//Anything that can't be purchased for RUB, EUR, USD, GP or Lega Medals is considered a barter +export function isBarterTrade(trade: IItem, trader: ITrader): boolean +{ + const scheme: IBarterScheme[][] = trader.assort.barter_scheme[trade._id]; + if (scheme == null) return false; + + for (const ask of scheme) for (const ask1 of ask) //Cursed, i know + { + if (!currencies.includes(ask1._tpl)) return true; + } + + return false; +} + +//Checks if the given trade item is quest locked on the given trader +export function isQuestLocked(trade: IItem, trader: ITrader): boolean +{ + + const questLocks = [ + ...Object.keys(trader.questassort["success"]), + ...Object.keys(trader.questassort["started"]), + ...Object.keys(trader.questassort["fail"]) ]; + + for (const questLockedTrade of questLocks) + { + if (trade._id == questLockedTrade) return true; + } + + return false; + +} + +//Returns the path to the mod folder +export function getModFolder(): string +{ + return join(__dirname, "../"); // "./user/mods/Gekos_BetterProgression"; +} + +export function bestFiremode(item: ITemplateItem): string +{ + const res = pickBestFiremode(item._props.weapFireType, item._props.BoltAction || !item._props.CanQueueSecondShot); + if (res == "") console.log(item); + return res; +} + +//Returns one of "manual", "semiauto" or "fullauto" depending on which is the best fire mode available between the provided choices +export function pickBestFiremode(modes: string[], isManual: boolean): string +{ + if (modes == null) + { + return ""; + } + + const ranks: { [mode: string] : number } = + { + "none" : -9999, + "manual" : 0, + "doublet" : 1, + "semiauto" : 1, + "doubleaction": 1, + "single" : isManual ? -100 : 1, + "burst" : 2, + "fullauto" : 3 + }; + + let bestMode: string = "none"; + + for (const mode of modes) + { + if (ranks[bestMode] < ranks[mode]) bestMode = mode; + } + if (isManual) if (ranks[bestMode] < ranks["manual"]) bestMode = "manual"; + + //Rename for simplicity + if (bestMode == "manual" || bestMode == "pumpaction" || (isManual && bestMode == "single")) return "manual"; + if (bestMode == "single" || bestMode == "doubleaction" || bestMode == "doublet") return "semiauto"; + + return bestMode; + +} \ No newline at end of file diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/AssetBundleLoader.dll b/mods/Hideout Cat/BepInEx/plugins/tarkin/AssetBundleLoader.dll new file mode 100644 index 0000000..c4ce436 Binary files /dev/null and b/mods/Hideout Cat/BepInEx/plugins/tarkin/AssetBundleLoader.dll differ diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/CatNodeGraph.json b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/CatNodeGraph.json new file mode 100644 index 0000000..9eae965 --- /dev/null +++ b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/CatNodeGraph.json @@ -0,0 +1,3209 @@ +[ + { + "name": "171c_Waypoint", + "position": { + "x": 10.081, + "y": 0.0, + "z": -2.127 + }, + "connectedTo": [ + "b749_Waypoint", + "b203_Waypoint", + "4810_MedStation_L3", + "9142_MedStation_L2", + "2d28_MedStation_L2", + "8e0f_MedStation_L2", + "209a_MedStation_L1", + "be9a_MedStation_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b749_Waypoint", + "position": { + "x": 10.333, + "y": 0.0, + "z": -2.991 + }, + "connectedTo": [ + "ccb7_Waypoint", + "171c_Waypoint", + "b203_Waypoint", + "8e0f_MedStation_L2", + "c7eb_Kitchen_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "ccb7_Waypoint", + "position": { + "x": 12.669, + "y": 0.0, + "z": -3.048 + }, + "connectedTo": [ + "3c5d_Waypoint", + "b749_Waypoint", + "d045_Waypoint", + "4660_Waypoint", + "8e0f_MedStation_L2", + "209a_MedStation_L1", + "9f57_RestSpace_L1", + "c7eb_Kitchen_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3c5d_Waypoint", + "position": { + "x": 12.847, + "y": 0.0, + "z": -1.509 + }, + "connectedTo": [ + "ccb7_Waypoint", + "ad37_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "4660_Waypoint", + "position": { + "x": 15.088, + "y": 0.0, + "z": -2.937 + }, + "connectedTo": [ + "d045_Waypoint", + "ccb7_Waypoint", + "fe02_Library_L1", + "ad37_Waypoint", + "f001_Heating_L3", + "1e83_Waypoint", + "9f57_RestSpace_L1" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "1e83_Waypoint", + "position": { + "x": 15.663, + "y": 0.0, + "z": -1.296 + }, + "connectedTo": [ + "c595_RestSpace_L3", + "a57a_RestSpace_L2_POSE", + "ad37_Waypoint", + "4660_Waypoint", + "fe02_Library_L1" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "ad37_Waypoint", + "position": { + "x": 14.315, + "y": 0.0, + "z": -1.285 + }, + "connectedTo": [ + "3c5d_Waypoint", + "877a_RestSpace_L2_POSE", + "473b_RestSpace_L3_POSE", + "0aef_RestSpace_L1_POSE", + "4660_Waypoint", + "1e83_Waypoint", + "fe02_Library_L1", + "9f57_RestSpace_L1" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "d045_Waypoint", + "position": { + "x": 14.403, + "y": 0.0, + "z": -3.951 + }, + "connectedTo": [ + "ccb7_Waypoint", + "4660_Waypoint", + "0e35_Heating_L1", + "faae_Heating_L2", + "f001_Heating_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 161.044632, + "poseParameters": [] + }, + { + "name": "b203_Waypoint", + "position": { + "x": 8.589, + "y": 0.0, + "z": -2.874 + }, + "connectedTo": [ + "171c_Waypoint", + "b749_Waypoint", + "3fca_Waypoint", + "be9a_MedStation_L3", + "2d28_MedStation_L2", + "bb04_Waypoint", + "b36c_Waypoint", + "c7eb_Kitchen_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3f27_Waypoint", + "position": { + "x": 4.707, + "y": 0.0, + "z": -2.447 + }, + "connectedTo": [ + "3fca_Waypoint", + "c4b8_Waypoint", + "b36c_Waypoint", + "bb04_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3fca_Waypoint", + "position": { + "x": 7.135, + "y": 0.0, + "z": -2.771 + }, + "connectedTo": [ + "3f27_Waypoint", + "b203_Waypoint", + "b36c_Waypoint", + "574c_Kitchen_L2", + "bb04_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "c4b8_Waypoint", + "position": { + "x": 2.595, + "y": 0.0, + "z": -2.308 + }, + "connectedTo": [ + "3f27_Waypoint", + "1ea8_WaterCollector_L3", + "25a2_Waypoint", + "e508_WaterCollector_L2", + "512f_ShootingRange_L1_POSE", + "b36c_Waypoint", + "13ff_ShootingRange_L3_POSE", + "1062_ShootingRange_L2_POSE" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "25a2_Waypoint", + "position": { + "x": 0.534, + "y": 0.0, + "z": -3.108 + }, + "connectedTo": [ + "f5ae_Generator_L3", + "c4b8_Waypoint", + "fbb8_Waypoint", + "b36c_Waypoint", + "42c5_Waypoint", + "5912_SolarPower_L1", + "668c_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b36c_Waypoint", + "position": { + "x": 5.623, + "y": 0.0, + "z": -4.17 + }, + "connectedTo": [ + "3fca_Waypoint", + "33f8_Kitchen_L3_POSE", + "2b03_Kitchen_L1", + "3f27_Waypoint", + "822b_Kitchen_L2_POSE", + "25a2_Waypoint", + "c4b8_Waypoint", + "b203_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "fbb8_Waypoint", + "position": { + "x": -1.148, + "y": 0.0, + "z": -2.119 + }, + "connectedTo": [ + "21bc_Waypoint", + "25a2_Waypoint", + "42c5_Waypoint", + "2be7_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "21bc_Waypoint", + "position": { + "x": -2.43457222, + "y": 0.0, + "z": -0.1754334 + }, + "connectedTo": [ + "4b86_Waypoint", + "fbb8_Waypoint", + "5ca5_Waypoint", + "117c_Waypoint", + "42c5_Waypoint", + "2be7_Waypoint", + "c15b_Waypoint", + "4cd9_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "4b86_Waypoint", + "position": { + "x": -3.62791324, + "y": 2.38418579E-07, + "z": 1.84881091 + }, + "connectedTo": [ + "5dd0_Waypoint", + "21bc_Waypoint", + "5ca5_Waypoint", + "4cd9_Waypoint", + "4e81_Stash_L4_POSE", + "c639_Stash_L3_POSE", + "ab5e_Stash_L2" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "5aa9_Waypoint", + "position": { + "x": -4.60407925, + "y": 2.38418579E-07, + "z": 4.872308 + }, + "connectedTo": [ + "2166_Waypoint", + "5dd0_Waypoint", + "ed1f_WaterCloset_L2" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "146e_Waypoint", + "position": { + "x": -4.889, + "y": 4.76837158E-07, + "z": 7.225 + }, + "connectedTo": [ + "2166_Waypoint", + "c3b4_WaterCloset_L3", + "5fbb_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2166_Waypoint", + "position": { + "x": -4.414, + "y": 2.38418579E-07, + "z": 6.292 + }, + "connectedTo": [ + "146e_Waypoint", + "5aa9_Waypoint", + "5fbb_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "5dd0_Waypoint", + "position": { + "x": -4.467532, + "y": 4.76837158E-07, + "z": 3.53140783 + }, + "connectedTo": [ + "4b86_Waypoint", + "5aa9_Waypoint", + "ed1f_WaterCloset_L2", + "c639_Stash_L3_POSE", + "ab5e_Stash_L2" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "5ca5_Waypoint", + "position": { + "x": -1.585, + "y": 0.0, + "z": 2.137 + }, + "connectedTo": [ + "9296_Waypoint", + "21bc_Waypoint", + "9d16_IntelligenceCenter_L1", + "117c_Waypoint", + "50bb_IntelligenceCenter_L2", + "42c5_Waypoint", + "4b86_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "9296_Waypoint", + "position": { + "x": -1.478, + "y": 0.0, + "z": 5.581 + }, + "connectedTo": [ + "5ca5_Waypoint", + "9e74_Workbench_L1", + "6e42_Workbench_L2", + "243b_Workbench_L3", + "9202_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "42c5_Waypoint", + "position": { + "x": -0.376, + "y": 0.0, + "z": -0.859 + }, + "connectedTo": [ + "fbb8_Waypoint", + "21bc_Waypoint", + "25a2_Waypoint", + "c1fb_BitcoinFarm_AL", + "5ca5_Waypoint", + "512f_ShootingRange_L1_POSE", + "13ff_ShootingRange_L3_POSE", + "1062_ShootingRange_L2_POSE" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "117c_Waypoint", + "position": { + "x": 0.194, + "y": 0.0, + "z": 1.116 + }, + "connectedTo": [ + "2b7e_BitcoinFarm_AL", + "6b25_IntelligenceCenter_L2_POSE", + "50bb_IntelligenceCenter_L2", + "5ca5_Waypoint", + "21bc_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2be7_Waypoint", + "position": { + "x": -2.876, + "y": 0.0, + "z": -2.717 + }, + "connectedTo": [ + "668c_Waypoint", + "fbb8_Waypoint", + "21bc_Waypoint", + "5912_SolarPower_L1", + "c15b_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "668c_Waypoint", + "position": { + "x": -4.486, + "y": 0.0, + "z": -4.186 + }, + "connectedTo": [ + "7022_Waypoint", + "2be7_Waypoint", + "25a2_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "7022_Waypoint", + "position": { + "x": -6.122, + "y": 0.0, + "z": -5.779 + }, + "connectedTo": [ + "b41d_Security_L0_POSE", + "a168_Security_L1_POSE", + "60f9_Security_L2_POSE", + "668c_Waypoint", + "6e5e_ScavCase_L1" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "5fbb_Waypoint", + "position": { + "x": -3.02116728, + "y": 2.38418579E-07, + "z": 7.164547 + }, + "connectedTo": [ + "2166_Waypoint", + "146e_Waypoint", + "9202_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "9202_Waypoint", + "position": { + "x": -1.65881872, + "y": 0.0, + "z": 6.625329 + }, + "connectedTo": [ + "5fbb_Waypoint", + "9296_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "bb04_Waypoint", + "position": { + "x": 6.809, + "y": 0.0, + "z": -0.612 + }, + "connectedTo": [ + "3fca_Waypoint", + "3f27_Waypoint", + "be9a_MedStation_L3", + "b203_Waypoint", + "0ce0_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "0ce0_Waypoint", + "position": { + "x": 6.87, + "y": 0.0, + "z": 1.839 + }, + "connectedTo": [ + "61ef_EquipmentPresetsStand_L1", + "bb04_Waypoint", + "2b42_Waypoint", + "2be6_Waypoint", + "190f_EquipmentPresetsStand_L2", + "a11d_EquipmentPresetsStand_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2be6_Waypoint", + "position": { + "x": 9.012, + "y": 0.0, + "z": 6.248 + }, + "connectedTo": [ + "0ce0_Waypoint", + "c0b5_Waypoint", + "db95_Gym_L1_POSE" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b93f_Waypoint", + "position": { + "x": 8.482, + "y": 0.01, + "z": 10.354 + }, + "connectedTo": [ + "710d_Waypoint", + "fd8e_Gym_L1", + "9ca2_Waypoint", + "c0b5_Waypoint", + "db95_Gym_L1_POSE" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "710d_Waypoint", + "position": { + "x": 4.771, + "y": 0.0096, + "z": 10.3 + }, + "connectedTo": [ + "b93f_Waypoint", + "2b42_Waypoint", + "8cbe_Gym_L1", + "899e_PlaceOfFame_L2", + "5a5c_PlaceOfFame_L2", + "cd90_PlaceOfFame_L1", + "fcd0_PlaceOfFame_L3_POSE" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2b42_Waypoint", + "position": { + "x": 4.098, + "y": 0.007999897, + "z": 7.285 + }, + "connectedTo": [ + "0ce0_Waypoint", + "61ef_EquipmentPresetsStand_L1", + "671d_EquipmentPresetsStand_L1_POSE", + "710d_Waypoint", + "899e_PlaceOfFame_L2", + "5a5c_PlaceOfFame_L2", + "cd90_PlaceOfFame_L1", + "fcd0_PlaceOfFame_L3_POSE", + "3ef9_EquipmentPresetsStand_L1_POSE", + "7c88_EquipmentPresetsStand_L2", + "ce91_EquipmentPresetsStand_L3" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "9ca2_Waypoint", + "position": { + "x": 7.121, + "y": 0.015, + "z": 9.034 + }, + "connectedTo": [ + "899e_PlaceOfFame_L2", + "b93f_Waypoint", + "c0b5_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "c0b5_Waypoint", + "position": { + "x": 8.726, + "y": 0.00999999, + "z": 8.872 + }, + "connectedTo": [ + "9ca2_Waypoint", + "b93f_Waypoint", + "2be6_Waypoint", + "db95_Gym_L1_POSE" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "c15b_Waypoint", + "position": { + "x": -4.285, + "y": -2.38418579E-07, + "z": -1.45 + }, + "connectedTo": [ + "4cd9_Waypoint", + "21bc_Waypoint", + "2be7_Waypoint" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "4cd9_Waypoint", + "position": { + "x": -4.671, + "y": -2.38418579E-07, + "z": 0.132 + }, + "connectedTo": [ + "4b86_Waypoint", + "c15b_Waypoint", + "21bc_Waypoint", + "4e81_Stash_L4_POSE", + "c639_Stash_L3_POSE", + "ab5e_Stash_L2", + "79fc_Stash_L2" + ], + "forwardJump": false, + "areaType": "NotSet", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b41d_Security_L0_POSE", + "position": { + "x": -8.824, + "y": 0.0309, + "z": -7.168 + }, + "connectedTo": [ + "7022_Waypoint" + ], + "forwardJump": false, + "areaType": "Security", + "areaLevel": 0, + "poseRotation": 37.94974, + "poseParameters": [ + { + "Name": "Defecating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "a168_Security_L1_POSE", + "position": { + "x": -7.741, + "y": 0.0, + "z": -8.661 + }, + "connectedTo": [ + "7022_Waypoint" + ], + "forwardJump": false, + "areaType": "Security", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [ + { + "Name": "Defecating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "60f9_Security_L2_POSE", + "position": { + "x": -7.476, + "y": 0.0, + "z": -8.233 + }, + "connectedTo": [ + "7022_Waypoint" + ], + "forwardJump": false, + "areaType": "Security", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [ + { + "Name": "Defecating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "c3b4_WaterCloset_L3", + "position": { + "x": -6.008, + "y": 0.0, + "z": 6.387 + }, + "connectedTo": [ + "3618_WaterCloset_L3", + "146e_Waypoint" + ], + "forwardJump": false, + "areaType": "WaterCloset", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3618_WaterCloset_L3", + "position": { + "x": -6.371, + "y": 0.163, + "z": 6.197 + }, + "connectedTo": [ + "2806_WaterCloset_L3_POSE", + "c3b4_WaterCloset_L3" + ], + "forwardJump": false, + "areaType": "WaterCloset", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2806_WaterCloset_L3_POSE", + "position": { + "x": -6.877, + "y": 0.6057, + "z": 6.254 + }, + "connectedTo": [ + "3618_WaterCloset_L3" + ], + "forwardJump": false, + "areaType": "WaterCloset", + "areaLevel": 3, + "poseRotation": 91.2597656, + "poseParameters": [ + { + "Name": "Defecating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "ed1f_WaterCloset_L2", + "position": { + "x": -5.729, + "y": 0.0, + "z": 4.388 + }, + "connectedTo": [ + "5dd0_Waypoint", + "5aa9_Waypoint", + "aa3f_WaterCloset_L2" + ], + "forwardJump": false, + "areaType": "WaterCloset", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "aa3f_WaterCloset_L2", + "position": { + "x": -6.259, + "y": 0.1237, + "z": 4.44 + }, + "connectedTo": [ + "e96e_WaterCloset_L2_POSE", + "ed1f_WaterCloset_L2" + ], + "forwardJump": false, + "areaType": "WaterCloset", + "areaLevel": 2, + "poseRotation": 271.734436, + "poseParameters": [] + }, + { + "name": "e96e_WaterCloset_L2_POSE", + "position": { + "x": -6.8735, + "y": 0.527, + "z": 4.3927 + }, + "connectedTo": [ + "aa3f_WaterCloset_L2" + ], + "forwardJump": false, + "areaType": "WaterCloset", + "areaLevel": 2, + "poseRotation": 90.41485, + "poseParameters": [ + { + "Name": "Defecating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "4e81_Stash_L4_POSE", + "position": { + "x": -5.528, + "y": 0.0, + "z": 0.792 + }, + "connectedTo": [ + "4b86_Waypoint", + "4cd9_Waypoint" + ], + "forwardJump": false, + "areaType": "Stash", + "areaLevel": 4, + "poseRotation": 126.067039, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "c639_Stash_L3_POSE", + "position": { + "x": -5.653, + "y": 0.0, + "z": 1.97 + }, + "connectedTo": [ + "4cd9_Waypoint", + "4b86_Waypoint", + "5dd0_Waypoint" + ], + "forwardJump": false, + "areaType": "Stash", + "areaLevel": 3, + "poseRotation": 129.27504, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "4fae_Stash_L2_POSE", + "position": { + "x": -6.898, + "y": 1.108, + "z": 1.897 + }, + "connectedTo": [ + "a789_Stash_L2" + ], + "forwardJump": false, + "areaType": "Stash", + "areaLevel": 2, + "poseRotation": 89.0439453, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "a789_Stash_L2", + "position": { + "x": -7.041, + "y": 0.555, + "z": 1.261 + }, + "connectedTo": [ + "79fc_Stash_L2", + "4fae_Stash_L2_POSE" + ], + "forwardJump": false, + "areaType": "Stash", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "79fc_Stash_L2", + "position": { + "x": -6.467, + "y": 0.032, + "z": 0.915 + }, + "connectedTo": [ + "a789_Stash_L2", + "ab5e_Stash_L2", + "4cd9_Waypoint" + ], + "forwardJump": false, + "areaType": "Stash", + "areaLevel": 2, + "poseRotation": 357.56076, + "poseParameters": [] + }, + { + "name": "ab5e_Stash_L2", + "position": { + "x": -4.979, + "y": 0.0, + "z": 1.658 + }, + "connectedTo": [ + "4b86_Waypoint", + "4cd9_Waypoint", + "5dd0_Waypoint", + "79fc_Stash_L2" + ], + "forwardJump": false, + "areaType": "Stash", + "areaLevel": 2, + "poseRotation": 133.903854, + "poseParameters": [] + }, + { + "name": "f5ae_Generator_L3", + "position": { + "x": 0.666, + "y": 0.0, + "z": -3.945 + }, + "connectedTo": [ + "69ab_Generator_L3", + "b341_Generator_L3" + ], + "forwardJump": false, + "areaType": "Generator", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "69ab_Generator_L3", + "position": { + "x": 0.669, + "y": 1.112, + "z": -4.64 + }, + "connectedTo": [ + "2504_Generator_L3" + ], + "forwardJump": false, + "areaType": "Generator", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2504_Generator_L3", + "position": { + "x": 0.183, + "y": 1.6487, + "z": -5.255 + }, + "connectedTo": [ + "f0c9_Generator_L3_POSE", + "f88b_Generator_L3" + ], + "forwardJump": false, + "areaType": "Generator", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "f0c9_Generator_L3_POSE", + "position": { + "x": -1.113, + "y": 1.6487, + "z": -5.39 + }, + "connectedTo": [ + "f88b_Generator_L3" + ], + "forwardJump": false, + "areaType": "Generator", + "areaLevel": 3, + "poseRotation": 19.14253, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "f88b_Generator_L3", + "position": { + "x": -0.386, + "y": 1.641, + "z": -5.173 + }, + "connectedTo": [ + "b341_Generator_L3", + "2504_Generator_L3" + ], + "forwardJump": false, + "areaType": "Generator", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b341_Generator_L3", + "position": { + "x": -0.304, + "y": 0.034, + "z": -4.23 + }, + "connectedTo": [ + "f5ae_Generator_L3", + "fbb8_Waypoint", + "25a2_Waypoint", + "668c_Waypoint", + "5912_SolarPower_L1" + ], + "forwardJump": false, + "areaType": "Generator", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "0e35_Heating_L1", + "position": { + "x": 14.389, + "y": 0.0, + "z": -4.969 + }, + "connectedTo": [ + "d045_Waypoint", + "7b0e_Heating_L1_POSE" + ], + "forwardJump": false, + "areaType": "Heating", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "7b0e_Heating_L1_POSE", + "position": { + "x": 14.3222, + "y": 0.5158, + "z": -5.6864 + }, + "connectedTo": [ + "0e35_Heating_L1" + ], + "forwardJump": false, + "areaType": "Heating", + "areaLevel": 1, + "poseRotation": 41.2075348, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "faae_Heating_L2", + "position": { + "x": 14.254, + "y": 0.0, + "z": -4.908 + }, + "connectedTo": [ + "d045_Waypoint", + "e6f3_Heating_L2_POSE" + ], + "forwardJump": false, + "areaType": "Heating", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "e6f3_Heating_L2_POSE", + "position": { + "x": 14.185, + "y": 0.5142, + "z": -5.522 + }, + "connectedTo": [ + "faae_Heating_L2" + ], + "forwardJump": false, + "areaType": "Heating", + "areaLevel": 2, + "poseRotation": 83.11946, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "f001_Heating_L3", + "position": { + "x": 15.617, + "y": 0.0, + "z": -4.363 + }, + "connectedTo": [ + "d045_Waypoint", + "af18_Heating_L3_POSE", + "4660_Waypoint" + ], + "forwardJump": false, + "areaType": "Heating", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "af18_Heating_L3_POSE", + "position": { + "x": 15.975, + "y": 0.5157, + "z": -4.94 + }, + "connectedTo": [ + "f001_Heating_L3" + ], + "forwardJump": false, + "areaType": "Heating", + "areaLevel": 3, + "poseRotation": 223.098648, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "e107_WaterCollector_L3", + "position": { + "x": 3.234, + "y": 1.411, + "z": -0.395 + }, + "connectedTo": [ + "6e07_WaterCollector_L3" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "6e07_WaterCollector_L3", + "position": { + "x": 4.082, + "y": 1.666, + "z": -0.605 + }, + "connectedTo": [ + "e107_WaterCollector_L3", + "a16b_WaterCollector_L3" + ], + "forwardJump": true, + "areaType": "WaterCollector", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2b63_WaterCollector_L3", + "position": { + "x": 2.765, + "y": 0.846, + "z": -0.553 + }, + "connectedTo": [ + "e107_WaterCollector_L3", + "1ea8_WaterCollector_L3" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "1ea8_WaterCollector_L3", + "position": { + "x": 2.108, + "y": 0.0, + "z": -1.036 + }, + "connectedTo": [ + "c4b8_Waypoint", + "2b63_WaterCollector_L3", + "c1fb_BitcoinFarm_AL", + "13ff_ShootingRange_L3_POSE", + "1062_ShootingRange_L2_POSE" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "0d5b_WaterCollector_L3", + "position": { + "x": 4.669, + "y": 1.671, + "z": -1.525 + }, + "connectedTo": [ + "3f27_Waypoint" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "a16b_WaterCollector_L3", + "position": { + "x": 4.521, + "y": 1.709, + "z": -1.072 + }, + "connectedTo": [ + "0d5b_WaterCollector_L3" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "e508_WaterCollector_L2", + "position": { + "x": 2.401, + "y": 0.0, + "z": -1.179 + }, + "connectedTo": [ + "de45_WaterCollector_L2" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "de45_WaterCollector_L2", + "position": { + "x": 2.737, + "y": 0.818, + "z": -0.535 + }, + "connectedTo": [ + "c249_WaterCollector_L2" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "c249_WaterCollector_L2", + "position": { + "x": 3.268, + "y": 1.39, + "z": -0.333 + }, + "connectedTo": [ + "f7cd_WaterCollector_L2" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "f7cd_WaterCollector_L2", + "position": { + "x": 3.876, + "y": 1.649, + "z": -0.53 + }, + "connectedTo": [ + "068e_WaterCollector_L2_POSE" + ], + "forwardJump": true, + "areaType": "WaterCollector", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "068e_WaterCollector_L2_POSE", + "position": { + "x": 4.215, + "y": 1.719, + "z": -0.616 + }, + "connectedTo": [ + "3f27_Waypoint" + ], + "forwardJump": false, + "areaType": "WaterCollector", + "areaLevel": 2, + "poseRotation": 240.857391, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "8a65_MedStation_L3_POSE", + "position": { + "x": 11.293, + "y": 0.8567, + "z": -0.943 + }, + "connectedTo": [ + "4810_MedStation_L3" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 3, + "poseRotation": 139.427979, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "4810_MedStation_L3", + "position": { + "x": 10.863, + "y": 0.0, + "z": -1.304 + }, + "connectedTo": [ + "171c_Waypoint", + "8a65_MedStation_L3_POSE" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "be9a_MedStation_L3", + "position": { + "x": 8.815, + "y": 0.0, + "z": -1.411 + }, + "connectedTo": [ + "b203_Waypoint", + "171c_Waypoint", + "bb04_Waypoint" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "8e0f_MedStation_L2", + "position": { + "x": 11.672, + "y": 0.0, + "z": -2.026 + }, + "connectedTo": [ + "171c_Waypoint", + "9142_MedStation_L2", + "ccb7_Waypoint", + "b749_Waypoint" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "9142_MedStation_L2", + "position": { + "x": 11.415, + "y": 0.0, + "z": -1.004 + }, + "connectedTo": [ + "171c_Waypoint", + "9da9_MedStation_L2_POSE", + "8e0f_MedStation_L2" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "9da9_MedStation_L2_POSE", + "position": { + "x": 11.484, + "y": 0.5793, + "z": -0.417 + }, + "connectedTo": [ + "9142_MedStation_L2" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 2, + "poseRotation": 267.247131, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "2d28_MedStation_L2", + "position": { + "x": 9.667, + "y": 0.0, + "z": -0.875 + }, + "connectedTo": [ + "171c_Waypoint", + "b442_MedStation_L2_POSE", + "b203_Waypoint" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b442_MedStation_L2_POSE", + "position": { + "x": 10.433, + "y": 0.0, + "z": -0.37 + }, + "connectedTo": [ + "2d28_MedStation_L2" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 2, + "poseRotation": 232.56134, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "209a_MedStation_L1", + "position": { + "x": 11.135, + "y": 0.0, + "z": -1.668 + }, + "connectedTo": [ + "171c_Waypoint", + "3ac6_MedStation_L1_POSE", + "ccb7_Waypoint" + ], + "forwardJump": false, + "areaType": "MedStation", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3ac6_MedStation_L1_POSE", + "position": { + "x": 11.267, + "y": 0.172, + "z": -0.605 + }, + "connectedTo": [ + "209a_MedStation_L1" + ], + "forwardJump": true, + "areaType": "MedStation", + "areaLevel": 1, + "poseRotation": 232.468079, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "2b03_Kitchen_L1", + "position": { + "x": 5.814, + "y": 0.0, + "z": -4.983 + }, + "connectedTo": [ + "b36c_Waypoint", + "08ee_Kitchen_L1_POSE" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "08ee_Kitchen_L1_POSE", + "position": { + "x": 5.837, + "y": 0.849, + "z": -5.591 + }, + "connectedTo": [ + "2b03_Kitchen_L1" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 1, + "poseRotation": 247.737686, + "poseParameters": [ + { + "Name": "Eating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "574c_Kitchen_L2", + "position": { + "x": 6.519, + "y": 0.0, + "z": -5.1 + }, + "connectedTo": [ + "3fca_Waypoint", + "4c17_Kitchen_L2" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "822b_Kitchen_L2_POSE", + "position": { + "x": 5.553, + "y": 0.759, + "z": -4.738 + }, + "connectedTo": [ + "b36c_Waypoint", + "4c17_Kitchen_L2" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 2, + "poseRotation": 188.3781, + "poseParameters": [ + { + "Name": "Eating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "4c17_Kitchen_L2", + "position": { + "x": 5.907, + "y": 0.76, + "z": -5.195 + }, + "connectedTo": [ + "822b_Kitchen_L2_POSE", + "574c_Kitchen_L2" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "33f8_Kitchen_L3_POSE", + "position": { + "x": 5.553, + "y": 0.753, + "z": -4.738 + }, + "connectedTo": [ + "b36c_Waypoint" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 3, + "poseRotation": 198.506958, + "poseParameters": [ + { + "Name": "Eating", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "c7eb_Kitchen_L3", + "position": { + "x": 11.224, + "y": 0.0, + "z": -4.493 + }, + "connectedTo": [ + "ccb7_Waypoint", + "f4d5_Kitchen_L3_POSE", + "b749_Waypoint", + "b203_Waypoint" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "f4d5_Kitchen_L3_POSE", + "position": { + "x": 11.293, + "y": 1.003, + "z": -5.093 + }, + "connectedTo": [ + "c7eb_Kitchen_L3" + ], + "forwardJump": false, + "areaType": "Kitchen", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "0aef_RestSpace_L1_POSE", + "position": { + "x": 15.417, + "y": 0.205, + "z": -0.885 + }, + "connectedTo": [ + "ad37_Waypoint", + "9f57_RestSpace_L1" + ], + "forwardJump": true, + "areaType": "RestSpace", + "areaLevel": 1, + "poseRotation": 139.590744, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "9f57_RestSpace_L1", + "position": { + "x": 14.996, + "y": 0.0, + "z": -1.678 + }, + "connectedTo": [ + "4660_Waypoint", + "0aef_RestSpace_L1_POSE", + "ad37_Waypoint", + "ccb7_Waypoint" + ], + "forwardJump": false, + "areaType": "RestSpace", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "a57a_RestSpace_L2_POSE", + "position": { + "x": 15.7150927, + "y": 0.596, + "z": -0.6032072 + }, + "connectedTo": [ + "1e83_Waypoint" + ], + "forwardJump": false, + "areaType": "RestSpace", + "areaLevel": 2, + "poseRotation": 180.907837, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "877a_RestSpace_L2_POSE", + "position": { + "x": 14.2759085, + "y": 0.400000036, + "z": -0.6034045 + }, + "connectedTo": [ + "ad37_Waypoint" + ], + "forwardJump": false, + "areaType": "RestSpace", + "areaLevel": 2, + "poseRotation": 154.183533, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "f07b_RestSpace_L3_POSE", + "position": { + "x": 16.0603, + "y": 0.667, + "z": -0.5576 + }, + "connectedTo": [ + "c595_RestSpace_L3" + ], + "forwardJump": false, + "areaType": "RestSpace", + "areaLevel": 3, + "poseRotation": 173.629562, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "c595_RestSpace_L3", + "position": { + "x": 15.67, + "y": 0.667, + "z": -0.744 + }, + "connectedTo": [ + "f07b_RestSpace_L3_POSE", + "1e83_Waypoint" + ], + "forwardJump": false, + "areaType": "RestSpace", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "473b_RestSpace_L3_POSE", + "position": { + "x": 14.451, + "y": 0.474, + "z": -0.677 + }, + "connectedTo": [ + "ad37_Waypoint" + ], + "forwardJump": false, + "areaType": "RestSpace", + "areaLevel": 3, + "poseRotation": 169.624084, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "243b_Workbench_L3", + "position": { + "x": 0.019, + "y": 0.0, + "z": 5.522 + }, + "connectedTo": [ + "20ab_Workbench_L3_POSE", + "9296_Waypoint" + ], + "forwardJump": false, + "areaType": "Workbench", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "20ab_Workbench_L3_POSE", + "position": { + "x": 0.58, + "y": 0.891, + "z": 5.632 + }, + "connectedTo": [ + "243b_Workbench_L3" + ], + "forwardJump": false, + "areaType": "Workbench", + "areaLevel": 3, + "poseRotation": 343.864075, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "6e42_Workbench_L2", + "position": { + "x": 0.336, + "y": 0.0, + "z": 5.797 + }, + "connectedTo": [ + "f494_Workbench_L2_POSE", + "9296_Waypoint" + ], + "forwardJump": false, + "areaType": "Workbench", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "f494_Workbench_L2_POSE", + "position": { + "x": 0.773, + "y": 0.893, + "z": 5.893 + }, + "connectedTo": [ + "6e42_Workbench_L2" + ], + "forwardJump": false, + "areaType": "Workbench", + "areaLevel": 2, + "poseRotation": 1.16966879, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "9e74_Workbench_L1", + "position": { + "x": 0.263, + "y": 0.0, + "z": 5.894 + }, + "connectedTo": [ + "9a45_Workbench_L1_POSE", + "9296_Waypoint" + ], + "forwardJump": false, + "areaType": "Workbench", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "9a45_Workbench_L1_POSE", + "position": { + "x": 0.804, + "y": 0.891, + "z": 5.945 + }, + "connectedTo": [ + "9e74_Workbench_L1" + ], + "forwardJump": false, + "areaType": "Workbench", + "areaLevel": 1, + "poseRotation": 354.120331, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "9d16_IntelligenceCenter_L1", + "position": { + "x": 0.242, + "y": 0.0, + "z": 2.083 + }, + "connectedTo": [ + "3751_IntelligenceCenter_L1_POSE" + ], + "forwardJump": false, + "areaType": "IntelligenceCenter", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3751_IntelligenceCenter_L1_POSE", + "position": { + "x": 0.695, + "y": 0.85, + "z": 2.119 + }, + "connectedTo": [ + "5ca5_Waypoint" + ], + "forwardJump": false, + "areaType": "IntelligenceCenter", + "areaLevel": 1, + "poseRotation": 354.9018, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "50bb_IntelligenceCenter_L2", + "position": { + "x": 0.153, + "y": 0.0, + "z": 2.126 + }, + "connectedTo": [ + "109f_IntelligenceCenter_L2_POSE", + "117c_Waypoint" + ], + "forwardJump": false, + "areaType": "IntelligenceCenter", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "6b25_IntelligenceCenter_L2_POSE", + "position": { + "x": 0.7264, + "y": 0.856, + "z": 1.115 + }, + "connectedTo": [ + "109f_IntelligenceCenter_L2_POSE" + ], + "forwardJump": false, + "areaType": "IntelligenceCenter", + "areaLevel": 2, + "poseRotation": 309.6646, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "109f_IntelligenceCenter_L2_POSE", + "position": { + "x": 0.7057, + "y": 0.855999947, + "z": 2.1877 + }, + "connectedTo": [ + "6b25_IntelligenceCenter_L2_POSE", + "50bb_IntelligenceCenter_L2" + ], + "forwardJump": false, + "areaType": "IntelligenceCenter", + "areaLevel": 2, + "poseRotation": 192.29274, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "7ed2_IntelligenceCenter_L3_POSE", + "position": { + "x": 0.757, + "y": 0.85800004, + "z": 2.01 + }, + "connectedTo": [ + "2b7e_BitcoinFarm_AL" + ], + "forwardJump": false, + "areaType": "IntelligenceCenter", + "areaLevel": 3, + "poseRotation": 202.501053, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "512f_ShootingRange_L1_POSE", + "position": { + "x": 1.632, + "y": 0.010999918, + "z": -0.416 + }, + "connectedTo": [ + "1ea8_WaterCollector_L3", + "c4b8_Waypoint", + "42c5_Waypoint" + ], + "forwardJump": false, + "areaType": "ShootingRange", + "areaLevel": 1, + "poseRotation": 265.190918, + "poseParameters": [ + { + "Name": "", + "Type": "Trigger", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "1062_ShootingRange_L2_POSE", + "position": { + "x": 1.743, + "y": 0.010999918, + "z": -0.523 + }, + "connectedTo": [ + "1ea8_WaterCollector_L3", + "c4b8_Waypoint", + "42c5_Waypoint" + ], + "forwardJump": false, + "areaType": "ShootingRange", + "areaLevel": 2, + "poseRotation": 265.190918, + "poseParameters": [ + { + "Name": "", + "Type": "Trigger", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "13ff_ShootingRange_L3_POSE", + "position": { + "x": 1.761, + "y": 0.010999918, + "z": -0.46 + }, + "connectedTo": [ + "1ea8_WaterCollector_L3", + "c4b8_Waypoint", + "42c5_Waypoint", + "c1fb_BitcoinFarm_AL" + ], + "forwardJump": false, + "areaType": "ShootingRange", + "areaLevel": 3, + "poseRotation": 265.190918, + "poseParameters": [ + { + "Name": "", + "Type": "Trigger", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "fe02_Library_L1", + "position": { + "x": 15.797, + "y": 0.0, + "z": -2.943 + }, + "connectedTo": [ + "4660_Waypoint", + "f3d0_Library_L1_POSE", + "1e83_Waypoint", + "ad37_Waypoint" + ], + "forwardJump": false, + "areaType": "Library", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "f3d0_Library_L1_POSE", + "position": { + "x": 16.101, + "y": 0.4227, + "z": -3.487 + }, + "connectedTo": [ + "fe02_Library_L1" + ], + "forwardJump": false, + "areaType": "Library", + "areaLevel": 1, + "poseRotation": 312.68866, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "6e5e_ScavCase_L1", + "position": { + "x": -6.309, + "y": 0.0, + "z": -6.988 + }, + "connectedTo": [ + "7022_Waypoint", + "bb92_ScavCase_L1_POSE" + ], + "forwardJump": false, + "areaType": "ScavCase", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "bb92_ScavCase_L1_POSE", + "position": { + "x": -5.89, + "y": 0.637, + "z": -7.377 + }, + "connectedTo": [ + "6e5e_ScavCase_L1" + ], + "forwardJump": false, + "areaType": "ScavCase", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "cd90_PlaceOfFame_L1", + "position": { + "x": 2.822, + "y": 0.009600043, + "z": 10.618 + }, + "connectedTo": [ + "4f41_PlaceOfFame_L1_POSE", + "710d_Waypoint", + "2b42_Waypoint", + "8cbe_Gym_L1" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "4f41_PlaceOfFame_L1_POSE", + "position": { + "x": 2.263, + "y": 0.467, + "z": 10.529 + }, + "connectedTo": [ + "cd90_PlaceOfFame_L1" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 1, + "poseRotation": 93.17244, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "899e_PlaceOfFame_L2", + "position": { + "x": 5.373, + "y": 0.009599924, + "z": 8.478 + }, + "connectedTo": [ + "b9b3_PlaceOfFame_L2_POSE", + "710d_Waypoint", + "2b42_Waypoint", + "9ca2_Waypoint" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b9b3_PlaceOfFame_L2_POSE", + "position": { + "x": 5.79, + "y": 0.477, + "z": 8.0 + }, + "connectedTo": [ + "899e_PlaceOfFame_L2" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "5a5c_PlaceOfFame_L2", + "position": { + "x": 2.401, + "y": 0.009600043, + "z": 10.302 + }, + "connectedTo": [ + "3be1_PlaceOfFame_L2_POSE", + "710d_Waypoint", + "2b42_Waypoint", + "8cbe_Gym_L1" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3be1_PlaceOfFame_L2_POSE", + "position": { + "x": 1.999, + "y": 0.459, + "z": 10.447 + }, + "connectedTo": [ + "5a5c_PlaceOfFame_L2" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 2, + "poseRotation": 182.096542, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "fcd0_PlaceOfFame_L3_POSE", + "position": { + "x": 4.116, + "y": 0.009600043, + "z": 8.577 + }, + "connectedTo": [ + "2b42_Waypoint", + "710d_Waypoint" + ], + "forwardJump": false, + "areaType": "PlaceOfFame", + "areaLevel": 3, + "poseRotation": 287.1828, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "5912_SolarPower_L1", + "position": { + "x": -2.56, + "y": 0.0139, + "z": -4.797 + }, + "connectedTo": [ + "0864_SolarPower_L1_POSE", + "25a2_Waypoint", + "2be7_Waypoint" + ], + "forwardJump": false, + "areaType": "SolarPower", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "0864_SolarPower_L1_POSE", + "position": { + "x": -2.229, + "y": 0.7307, + "z": -5.173 + }, + "connectedTo": [ + "5912_SolarPower_L1" + ], + "forwardJump": false, + "areaType": "SolarPower", + "areaLevel": 1, + "poseRotation": 342.003021, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "666b_BitcoinFarm_AL", + "position": { + "x": 0.935, + "y": 0.858, + "z": 0.709 + }, + "connectedTo": [ + "10de_BitcoinFarm_L1_POSE", + "6780_BitcoinFarm_AL" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "2b7e_BitcoinFarm_AL", + "position": { + "x": 0.734, + "y": 0.858, + "z": 1.112 + }, + "connectedTo": [ + "666b_BitcoinFarm_AL", + "7ed2_IntelligenceCenter_L3_POSE", + "117c_Waypoint" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "c1fb_BitcoinFarm_AL", + "position": { + "x": 1.147, + "y": 0.011, + "z": -0.263 + }, + "connectedTo": [ + "42c5_Waypoint", + "1ea8_WaterCollector_L3", + "512f_ShootingRange_L1_POSE", + "13ff_ShootingRange_L3_POSE", + "1062_ShootingRange_L2_POSE" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "6780_BitcoinFarm_AL", + "position": { + "x": 1.149, + "y": 0.938, + "z": 0.469 + }, + "connectedTo": [ + "0337_BitcoinFarm_L2_POSE", + "113e_BitcoinFarm_L3_POSE" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": -1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "10de_BitcoinFarm_L1_POSE", + "position": { + "x": 1.17, + "y": 0.466, + "z": 0.219 + }, + "connectedTo": [ + "c1fb_BitcoinFarm_AL" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": 1, + "poseRotation": 179.969986, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "0337_BitcoinFarm_L2_POSE", + "position": { + "x": 1.156, + "y": 0.937999964, + "z": 0.276 + }, + "connectedTo": [ + "c1fb_BitcoinFarm_AL" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": 2, + "poseRotation": 180.0, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "113e_BitcoinFarm_L3_POSE", + "position": { + "x": 1.157, + "y": 0.938, + "z": 0.2 + }, + "connectedTo": [ + "c1fb_BitcoinFarm_AL" + ], + "forwardJump": false, + "areaType": "BitcoinFarm", + "areaLevel": 3, + "poseRotation": 180.0, + "poseParameters": [ + { + "Name": "LyingBelly", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "671d_EquipmentPresetsStand_L1_POSE", + "position": { + "x": 3.176, + "y": 0.004, + "z": 4.491 + }, + "connectedTo": [ + "61ef_EquipmentPresetsStand_L1", + "2b42_Waypoint", + "3ef9_EquipmentPresetsStand_L1_POSE" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 1, + "poseRotation": 91.39798, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "61ef_EquipmentPresetsStand_L1", + "position": { + "x": 3.954, + "y": 0.008, + "z": 4.458 + }, + "connectedTo": [ + "671d_EquipmentPresetsStand_L1_POSE", + "0ce0_Waypoint", + "2b42_Waypoint", + "3ef9_EquipmentPresetsStand_L1_POSE" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "3ef9_EquipmentPresetsStand_L1_POSE", + "position": { + "x": 2.802, + "y": 0.007999897, + "z": 6.35 + }, + "connectedTo": [ + "2b42_Waypoint", + "671d_EquipmentPresetsStand_L1_POSE", + "61ef_EquipmentPresetsStand_L1" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 1, + "poseRotation": 263.262573, + "poseParameters": [ + { + "Name": "SharpeningVertical", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "190f_EquipmentPresetsStand_L2", + "position": { + "x": 5.33, + "y": 0.0, + "z": 3.119 + }, + "connectedTo": [ + "aaa3_EquipmentPresetsStand_L2", + "0ce0_Waypoint", + "dcc3_EquipmentPresetsStand_L2" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "dcc3_EquipmentPresetsStand_L2", + "position": { + "x": 5.039, + "y": 0.033, + "z": 3.649 + }, + "connectedTo": [ + "7c88_EquipmentPresetsStand_L2", + "190f_EquipmentPresetsStand_L2" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "aaa3_EquipmentPresetsStand_L2", + "position": { + "x": 4.805, + "y": 0.035, + "z": 3.086 + }, + "connectedTo": [ + "b8af_EquipmentPresetsStand_L2_POSE", + "190f_EquipmentPresetsStand_L2" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "b8af_EquipmentPresetsStand_L2_POSE", + "position": { + "x": 2.987, + "y": 0.0349999666, + "z": 3.34 + }, + "connectedTo": [ + "8c19_EquipmentPresetsStand_L2", + "aaa3_EquipmentPresetsStand_L2" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 2, + "poseRotation": 84.3693, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "8c19_EquipmentPresetsStand_L2", + "position": { + "x": 4.067, + "y": 0.0350000858, + "z": 4.32 + }, + "connectedTo": [ + "7c88_EquipmentPresetsStand_L2", + "b8af_EquipmentPresetsStand_L2_POSE" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "7c88_EquipmentPresetsStand_L2", + "position": { + "x": 4.31, + "y": 0.0, + "z": 4.694 + }, + "connectedTo": [ + "2b42_Waypoint", + "8c19_EquipmentPresetsStand_L2", + "dcc3_EquipmentPresetsStand_L2" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 2, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "a11d_EquipmentPresetsStand_L3", + "position": { + "x": 6.207, + "y": 0.0, + "z": 2.445 + }, + "connectedTo": [ + "a911_EquipmentPresetsStand_L3" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "a911_EquipmentPresetsStand_L3", + "position": { + "x": 5.736, + "y": 0.034, + "z": 2.435 + }, + "connectedTo": [ + "ce91_EquipmentPresetsStand_L3", + "a11d_EquipmentPresetsStand_L3" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "ce91_EquipmentPresetsStand_L3", + "position": { + "x": 4.531, + "y": 0.03399992, + "z": 2.215 + }, + "connectedTo": [ + "734d_EquipmentPresetsStand_L3_POSE", + "2b42_Waypoint", + "a911_EquipmentPresetsStand_L3" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 3, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "734d_EquipmentPresetsStand_L3_POSE", + "position": { + "x": 4.331, + "y": 0.03399968, + "z": 1.69 + }, + "connectedTo": [ + "ce91_EquipmentPresetsStand_L3" + ], + "forwardJump": false, + "areaType": "EquipmentPresetsStand", + "areaLevel": 3, + "poseRotation": 20.13704, + "poseParameters": [ + { + "Name": "Sitting", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "fd8e_Gym_L1", + "position": { + "x": 8.401, + "y": 0.0123, + "z": 11.489 + }, + "connectedTo": [ + "76e7_Gym_L1_POSE", + "b93f_Waypoint" + ], + "forwardJump": false, + "areaType": "Gym", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "76e7_Gym_L1_POSE", + "position": { + "x": 8.342, + "y": 0.084, + "z": 12.106 + }, + "connectedTo": [ + "fd8e_Gym_L1", + "e70e_Gym_L1" + ], + "forwardJump": false, + "areaType": "Gym", + "areaLevel": 1, + "poseRotation": 182.3337, + "poseParameters": [ + { + "Name": "LyingSide", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + }, + { + "name": "e70e_Gym_L1", + "position": { + "x": 6.889, + "y": 0.08400011, + "z": 11.807 + }, + "connectedTo": [ + "76e7_Gym_L1_POSE", + "8cbe_Gym_L1" + ], + "forwardJump": false, + "areaType": "Gym", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "8cbe_Gym_L1", + "position": { + "x": 6.392, + "y": 0.019, + "z": 11.556 + }, + "connectedTo": [ + "710d_Waypoint", + "e70e_Gym_L1", + "5a5c_PlaceOfFame_L2", + "cd90_PlaceOfFame_L1" + ], + "forwardJump": false, + "areaType": "Gym", + "areaLevel": 1, + "poseRotation": 0.0, + "poseParameters": [] + }, + { + "name": "db95_Gym_L1_POSE", + "position": { + "x": 9.764, + "y": 0.0123000145, + "z": 10.057 + }, + "connectedTo": [ + "b93f_Waypoint", + "2be6_Waypoint", + "c0b5_Waypoint" + ], + "forwardJump": false, + "areaType": "Gym", + "areaLevel": 1, + "poseRotation": 230.689346, + "poseParameters": [ + { + "Name": "Grooming", + "Type": "Bool", + "BoolValue": true, + "FloatValue": 0.0, + "IntValue": 0 + } + ] + } +] \ No newline at end of file diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat new file mode 100644 index 0000000..99bc450 --- /dev/null +++ b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d7d7912a301152e669c05426a2251f1c9cc25dbafce9a7f987aaa54c0b9b904 +size 19550276 diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat.manifest b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat.manifest new file mode 100644 index 0000000..b47560b --- /dev/null +++ b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat.manifest @@ -0,0 +1,46 @@ +ManifestFileVersion: 0 +CRC: 1018226052 +Hashes: + AssetFileHash: + serializedVersion: 2 + Hash: 3b4f1efd816306755099433f1e337cc8 + TypeTreeHash: + serializedVersion: 2 + Hash: e422a73b4511c0e6dd16f04435cec9ce + IncrementalBuildHash: + serializedVersion: 2 + Hash: 3b4f1efd816306755099433f1e337cc8 +HashAppended: 0 +ClassTypes: +- Class: 1 + Script: {instanceID: 0} +- Class: 4 + Script: {instanceID: 0} +- Class: 21 + Script: {instanceID: 0} +- Class: 28 + Script: {instanceID: 0} +- Class: 43 + Script: {instanceID: 0} +- Class: 48 + Script: {instanceID: 0} +- Class: 74 + Script: {instanceID: 0} +- Class: 90 + Script: {instanceID: 0} +- Class: 91 + Script: {instanceID: 0} +- Class: 95 + Script: {instanceID: 0} +- Class: 137 + Script: {instanceID: 0} +SerializeReferenceClassIdentifiers: [] +Assets: +- Assets/Cats/hideoutcat/Material/SimpleUV/MAINTEX_BLACK.png +- Assets/Cats/hideoutcat/Material/SimpleUV/MAINTEX_BROWN.png +- Assets/Cats/hideoutcat/Material/SimpleUV/MAINTEX_GREY.png +- Assets/Cats/hideoutcat/Material/SimpleUV/MAINTEX_WHITE.png +- Assets/Cats/hideoutcat/Material/SimpleUV/MAINTEX_BICOLOR.png +- Assets/Cats/hideoutcat/Material/SimpleUV/MAINTEX_ORANGE.png +- Assets/Cats/hideoutcat/hideoutcat.prefab +Dependencies: [] diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_audio b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_audio new file mode 100644 index 0000000..0e6c5db Binary files /dev/null and b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_audio differ diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_audio.manifest b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_audio.manifest new file mode 100644 index 0000000..307a5e8 --- /dev/null +++ b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_audio.manifest @@ -0,0 +1,338 @@ +ManifestFileVersion: 0 +CRC: 1336228228 +Hashes: + AssetFileHash: + serializedVersion: 2 + Hash: 3110ed8caede5af2b40742b9c27d9432 + TypeTreeHash: + serializedVersion: 2 + Hash: 9a2ca7bdbd1871f7131daf57de908e0c + IncrementalBuildHash: + serializedVersion: 2 + Hash: 3110ed8caede5af2b40742b9c27d9432 +HashAppended: 0 +ClassTypes: +- Class: 83 + Script: {instanceID: 0} +SerializeReferenceClassIdentifiers: [] +Assets: +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt17.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_concrete_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt21.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt24.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt18.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt25.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_01_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_metal_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_garbage_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_glass_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_grid_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_metal_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_metal_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_wet_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_garbagePlank_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/ok/cat_meow_ok_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/ok/cat_meow_ok_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_garbagePlank_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_plastic_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_plastic_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt20.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_garbage_01_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_dirt_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt22.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_cardboard_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_concrete_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt25.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/ok/cat_meow_ok_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt18.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_01_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/ok/cat_meow_ok_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt19.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt17.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_parquet_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_metal_sheet_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_loud_metal_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_plastic_empty_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt23.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_grid_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt24.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_grid_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_mud_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_parquet_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt25.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt24.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/ok/cat_meow_ok_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt21.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_wet_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_debug_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_metal_sheet_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_camera_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_01_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt27.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_loud_metal_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_tile_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt30.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt17.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_carpet_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_fabric_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_concrete_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt18.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_wood_hard_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_fabric_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_camera_01_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_tile_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_garbagePlank_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_plastic_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_metal_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_parquet_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_parquet_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt17.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_camera_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_plastic_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt23.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_metal_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_slime_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_plastic_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_carpet_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_metal_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt22.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/purr/cat_purr_single_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt16.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt19.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_water_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_dirt_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_water_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_tile_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_slime_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_glass_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_slime_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_wet_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_grid_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_fabric_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt19.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt20.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt22.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_water_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_plastic_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_mud_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_wood_hard_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_carpet_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_paper_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_metal_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_plastic_empty_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_garbage_01_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_plastic_empty_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_metal_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt26.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_camera_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt17.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_glass_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_plastic_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_garbage_01_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt23.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt19.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_garbage_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_paper_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_wood_hard_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_dirt_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt17.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt15.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_cardboard_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_plastic_alt04.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_garbage_01_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_metal_sheet_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_paper_01_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt07.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt09.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_holo_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_concrete_03_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt03.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_plastic_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/ok/cat_meow_ok_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt10.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_grid_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt28.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt06.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/look/cat_meow_look_alt08.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt14.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/grumpy/cat_meow_grumpy_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt20.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_mud_01_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_loud_metal_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/generic/cat_generic_meow_02_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_cardboard_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_parquet_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt21.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_carpet_03_alt01.ogg +- Assets/Cats/hideoutcat/StrayAudio/meows/jump/cat_meow_after_jump_alt13.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt12.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_tile_02_alt11.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_toyo_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_wood_02_alt29.ogg +- Assets/Cats/hideoutcat/StrayAudio/scratch/cat_scratch_tree_alt18.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_paper_01_alt05.ogg +- Assets/Cats/hideoutcat/StrayAudio/land/cat_land_metal_alt02.ogg +- Assets/Cats/hideoutcat/StrayAudio/walk/cat_walk_cardboard_01_alt13.ogg +Dependencies: [] diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_props b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_props new file mode 100644 index 0000000..e8a9478 Binary files /dev/null and b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_props differ diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_props.manifest b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_props.manifest new file mode 100644 index 0000000..2d6261b --- /dev/null +++ b/mods/Hideout Cat/BepInEx/plugins/tarkin/bundles/hideoutcat_props.manifest @@ -0,0 +1,34 @@ +ManifestFileVersion: 0 +CRC: 1087788891 +Hashes: + AssetFileHash: + serializedVersion: 2 + Hash: fd26a6940a14986cc6c6ab3d51fae25c + TypeTreeHash: + serializedVersion: 2 + Hash: d27d605e56b00c7d193d3604b99a7461 + IncrementalBuildHash: + serializedVersion: 2 + Hash: fd26a6940a14986cc6c6ab3d51fae25c +HashAppended: 0 +ClassTypes: +- Class: 1 + Script: {instanceID: 0} +- Class: 4 + Script: {instanceID: 0} +- Class: 21 + Script: {instanceID: 0} +- Class: 23 + Script: {instanceID: 0} +- Class: 28 + Script: {instanceID: 0} +- Class: 33 + Script: {instanceID: 0} +- Class: 43 + Script: {instanceID: 0} +- Class: 48 + Script: {instanceID: 0} +SerializeReferenceClassIdentifiers: [] +Assets: +- Assets/Cats/hideoutcat/props/herring_opened.fbx +Dependencies: [] diff --git a/mods/Hideout Cat/BepInEx/plugins/tarkin/hideoutcat.dll b/mods/Hideout Cat/BepInEx/plugins/tarkin/hideoutcat.dll new file mode 100644 index 0000000..36d27a3 Binary files /dev/null and b/mods/Hideout Cat/BepInEx/plugins/tarkin/hideoutcat.dll differ diff --git a/mods/Hideout Cat/meta.ini b/mods/Hideout Cat/meta.ini new file mode 100644 index 0000000..02cbf1b --- /dev/null +++ b/mods/Hideout Cat/meta.ini @@ -0,0 +1,28 @@ +[General] +gameName=spt +modid=0 +version=d2025.5.13.0 +newestVersion= +category="-1," +nexusFileStatus=1 +installationFile=hideoutcat-1.0.1.zip +repository=Nexus +ignoredVersion= +comments= +notes= +nexusDescription= +url= +hasCustomURL=true +lastNexusQuery= +lastNexusUpdate= +nexusLastModified=2025-05-13T08:45:10Z +nexusCategory=0 +converted=false +validated=false +color=@Variant(\0\0\0\x43\0\xff\xff\0\0\0\0\0\0\0\0) +tracked=0 + +[installedFiles] +1\modid=0 +1\fileid=0 +size=1 diff --git a/mods/Hideout Changes_separator/meta.ini b/mods/Hideout Changes_separator/meta.ini new file mode 100644 index 0000000..55a1c1d --- /dev/null +++ b/mods/Hideout Changes_separator/meta.ini @@ -0,0 +1,9 @@ +[General] +modid=0 +version= +newestVersion= +category=0 +installationFile= + +[installedFiles] +size=0 diff --git a/mods/HollywoodFX/BepInEx/config/com.janky.hollywoodfx.cfg b/mods/HollywoodFX/BepInEx/config/com.janky.hollywoodfx.cfg new file mode 100644 index 0000000..a688f46 --- /dev/null +++ b/mods/HollywoodFX/BepInEx/config/com.janky.hollywoodfx.cfg @@ -0,0 +1,63 @@ +## Settings file was created by plugin Janky's HollywoodFX v1.0.0 +## Plugin GUID: com.janky.hollywoodfx + +[1. Effect Size (Changes have no effect in-Raid)] + +## Impacts with less or equal energy to this value trigger a small effect. A 4g bullet travelling at 550m/s has roughly 750J energy. +# Setting type: Single +# Default value: 750 +# Acceptable value range: From 100 to 5000 +Small impact energy upper bound (Joules) = 750 + +## Impacts with more or equal energy to this trigger a large effect. A 5g bullet travelling at 1000m/s has 2500J energy. +# Setting type: Single +# Default value: 2500 +# Acceptable value range: From 100 to 5000 +Chonky impact energy lower bound (Joules) = 2500 + +## Scales the size of effects triggered by light ammo. +# Setting type: Single +# Default value: 0.5 +# Acceptable value range: From 0.1 to 2 +Small Effect Scale = 0.5 + +## Scales the size of effects triggered by mid-weight ammo. +# Setting type: Single +# Default value: 1 +# Acceptable value range: From 0.1 to 2 +Medium Effect Scale = 1 + +## Scales the size of effects triggered by chonky ammo. +# Setting type: Single +# Default value: 1.25 +# Acceptable value range: From 0.1 to 2 +Chonky Effect Scale = 1.25 + +[2. Blood/Gore Settings (Changes have no effect in-Raid)] + +## Toggles whether blood effects are rendered at all. When toggled off, only the default BSG blood effects will show. +# Setting type: Boolean +# Default value: true +Enable Blood Effects = true + +## Toggles the major blood splatters. Some people have Views. This toggle is for them. +# Setting type: Boolean +# Default value: true +Enable Blood Splatters = true + +## Toggles the fine blood splatter that artisanally flies around following a 3d perlin noise. +# Setting type: Boolean +# Default value: true +Enable Blood Fine Splatters = true + +## Toggles the fine mist/cloudy effect. +# Setting type: Boolean +# Default value: true +Enable Blood Puffs/Clouds = true + +## Adjusts the size (not the quantity or quality) of blood effects. Multiplicative with the general effect scaling! +# Setting type: Single +# Default value: 1 +# Acceptable value range: From 0 to 5 +Blood Effect Size = 1 + diff --git a/mods/HollywoodFX/BepInEx/plugins/HollywoodFX/HollywoodFX.dll b/mods/HollywoodFX/BepInEx/plugins/HollywoodFX/HollywoodFX.dll new file mode 100644 index 0000000..65ef2c8 Binary files /dev/null and b/mods/HollywoodFX/BepInEx/plugins/HollywoodFX/HollywoodFX.dll differ diff --git a/mods/HollywoodFX/BepInEx/plugins/HollywoodFX/hfx impacts b/mods/HollywoodFX/BepInEx/plugins/HollywoodFX/hfx impacts new file mode 100644 index 0000000..d3bba55 --- /dev/null +++ b/mods/HollywoodFX/BepInEx/plugins/HollywoodFX/hfx impacts @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb0d5f9cfb95d1864b72f8e753b8a17bde3ca6f07d7c00ddcfd9f48795c00034 +size 248708064 diff --git a/mods/HollywoodFX/meta.ini b/mods/HollywoodFX/meta.ini new file mode 100644 index 0000000..f994753 --- /dev/null +++ b/mods/HollywoodFX/meta.ini @@ -0,0 +1,28 @@ +[General] +gameName=spt +modid=0 +version=d2025.5.13.0 +newestVersion= +category="-1," +nexusFileStatus=1 +installationFile=HollywoodFX_Alpha6.zip +repository=Nexus +ignoredVersion= +comments= +notes= +nexusDescription= +url= +hasCustomURL=true +lastNexusQuery= +lastNexusUpdate= +nexusLastModified=2025-05-13T08:47:03Z +nexusCategory=0 +converted=false +validated=false +color=@Variant(\0\0\0\x43\0\xff\xff\0\0\0\0\0\0\0\0) +tracked=0 + +[installedFiles] +1\modid=0 +1\fileid=0 +size=1 diff --git a/mods/Interactable Exfils API/BepInEx/plugins/InteractableExfilsAPI.dll b/mods/Interactable Exfils API/BepInEx/plugins/InteractableExfilsAPI.dll deleted file mode 100644 index 64d341a..0000000 Binary files a/mods/Interactable Exfils API/BepInEx/plugins/InteractableExfilsAPI.dll and /dev/null differ diff --git a/mods/Interactable Exfils API/BepInEx/plugins/InteractableExfilsAPI/InteractableExfilsAPI.dll b/mods/Interactable Exfils API/BepInEx/plugins/InteractableExfilsAPI/InteractableExfilsAPI.dll new file mode 100644 index 0000000..614663a Binary files /dev/null and b/mods/Interactable Exfils API/BepInEx/plugins/InteractableExfilsAPI/InteractableExfilsAPI.dll differ diff --git a/mods/Interactable Exfils API/User/Mods/JEHREE-InteractableExfilsAPI-LICENSE.txt b/mods/Interactable Exfils API/User/Mods/JEHREE-InteractableExfilsAPI-LICENSE.txt deleted file mode 100644 index 7f67dfa..0000000 --- a/mods/Interactable Exfils API/User/Mods/JEHREE-InteractableExfilsAPI-LICENSE.txt +++ /dev/null @@ -1 +0,0 @@ -Interactable Exfils API © 2024 by Jehree is licensed under Creative Commons Attribution-NoDerivatives 4.0 International. To view a copy of this license, visit https://creativecommons.org/licenses/by-nd/4.0/ diff --git a/mods/Interactable Exfils API/User/Mods/JEHREE-InteractableExfilsAPI-README.md b/mods/Interactable Exfils API/User/Mods/JEHREE-InteractableExfilsAPI-README.md deleted file mode 100644 index 5c54724..0000000 --- a/mods/Interactable Exfils API/User/Mods/JEHREE-InteractableExfilsAPI-README.md +++ /dev/null @@ -1,103 +0,0 @@ -# Interactable Exfils API - -The main purpose of this api mod is to expose the ability to conveniently add custom interaction options to the interactable areas that this mod creates. - -## API Usage - -### Initial setup in your project -The only thing you need to do to start development with Interactable Exfils API is to reference the dll in your `.csproj` file: - -```xml - - - $(PathToSPT)\BepInEx\plugins\InteractableExfilsAPI.dll - - -``` - -Then you have to create at least one handler and register it with the instance of the `InteractableExfilsService` - -### Create your custom handler - -```cs -// This example will add an enabled static action to every single extract in the game -public static class Examples -{ - // this static function is an exfil actions handler you can register with the InteractableExfilsService - public static OnActionsAppliedResult SimpleExample(ExfiltrationPoint exfil, CustomExfilTrigger customExfilTrigger, bool exfilIsAvailableToPlayer) - { - // This part of the code is ran everytime the prompt is created/refreshed - // It occurs when: - // 1. the player enter the exfil zone - // 2. the player interact with the prompt (i.e. press the "F" key) - // 3. the player changed a BepInEx config in InteractableExfilsAPI - // 4. the customExfilTrigger.RefreshPrompt() method has been invoked - // 5. the InteractableExfilsService.RefreshPrompt() method has been invoked - - bool isDisabled = false; - - // This represent the definition of 1 prompt item - CustomExfilAction customExfilAction = new CustomExfilAction( - "Example Interaction", - isDisabled, - () => { - // This part of the code is ran when the player interact with this prompt item - NotificationManagerClass.DisplayMessageNotification("Simple Interaction Example Selected!"); - } - ); - - // Here you have control over the ordering of the actions - List actions = [customExfilAction]; - - return new OnActionsAppliedResult(actions); - } -} -``` - -Take a look to the [Examples class](./Examples.cs) for more. - -### Register your actions - -You can do this whenever you want but the recommended way for doing it as early as possible is in the `Start()` method of your plugin class - -```cs -public class Plugin : BaseUnityPlugin { - private void Awake() { - // enable your patches here - } - private void Start() { - // retrieve the interactable exfil singleton service - InteractableExfilsService ieService = InteractableExfilsService.Instance(); - - // register SimpleExample handler - ieService.OnActionsAppliedEvent += Examples.SimpleExample; - } -} -``` - -### Disable vanilla actions -If you don't want to let Interactable Exfils API show the car exfils and labs elevator exfils prompts, it's possible to disable them. Be aware that in this situation your mod should handle the extraction logic by itself otherwise the player couldn't extract. - -```cs -public static void DisableVanillaAction() -{ - // e.g. disable vanilla action (for cars and labs elevator) - InteractableExfilsService.Instance().DisableVanillaActions = true; -} -``` - -### Retrieve all active exfils - -If you need a list of all the active exfils in raid, you can get it via the `InteractableExfilsSession` component - -```cs -public static List ExampleGetExfils() { - InteractableExfilsSession session = InteractableExfilsService.GetSession(); - return session.ActiveExfils; -} -``` - -## Mods that use Interactable Exfils API -If your mod use Interactable Exfils API, please make a PR here so we can point it as an example. - -- [Path To Tarkov](https://hub.sp-tarkov.com/files/file/569-path-to-tarkov/): used in [PTT.Services.ExfilPromptService](https://github.com/guillaumearm/PathToTarkov/blob/cc5a24140ae3acd9e212b9e73729e42b77780a7d/PTT-Plugin/Services/ExfilPromptService.cs) diff --git a/mods/Interactable Exfils API/meta.ini b/mods/Interactable Exfils API/meta.ini index 50d74c0..efed719 100644 --- a/mods/Interactable Exfils API/meta.ini +++ b/mods/Interactable Exfils API/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.17.0 +version=d2025.5.13.0 newestVersion= category="1," nexusFileStatus=1 -installationFile=Jehree-InteractableExfilsAPI-2.0.0.zip +installationFile=InteractableExfilsAPI-2.0.1.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Load Order Editor/LoEDD/LoEDD.exe b/mods/Load Order Editor/LoEDD/LoEDD.exe index 5ed401f..8705d37 100644 Binary files a/mods/Load Order Editor/LoEDD/LoEDD.exe and b/mods/Load Order Editor/LoEDD/LoEDD.exe differ diff --git a/mods/Looting Bots/BepInEx/plugins/skwizzy.LootingBots.dll b/mods/Looting Bots/BepInEx/plugins/skwizzy.LootingBots.dll index 3ea4891..2a419a8 100644 Binary files a/mods/Looting Bots/BepInEx/plugins/skwizzy.LootingBots.dll and b/mods/Looting Bots/BepInEx/plugins/skwizzy.LootingBots.dll differ diff --git a/mods/Looting Bots/meta.ini b/mods/Looting Bots/meta.ini index 5654511..8fb8e22 100644 --- a/mods/Looting Bots/meta.ini +++ b/mods/Looting Bots/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.16.0 +version=d2025.5.13.0 newestVersion= category="1," nexusFileStatus=1 -installationFile=Skwizzy-LootingBots-1.4.0.zip +installationFile=Skwizzy-LootingBots-1.5.2.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/config/config.json b/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/config/config.json index 1a76132..7080069 100644 --- a/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/config/config.json +++ b/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/config/config.json @@ -1,4 +1,4 @@ { "pmcSpawnWithLoot": true, - "scavSpawnWithLoot": false + "scavSpawnWithLoot": true } \ No newline at end of file diff --git a/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/package.json b/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/package.json index ff499e7..1868737 100644 --- a/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/package.json +++ b/mods/Looting Bots/user/mods/Skwizzy-LootingBots-ServerMod/package.json @@ -1,10 +1,10 @@ { "name": "LootingBots-ServerMod", - "version": "1.4.0", + "version": "1.5.2", "main": "src/mod.js", "license": "MIT", "author": "Skwizzy", - "sptVersion": "~3.10", + "sptVersion": "~3.11", "scripts": { "setup": "npm i", "build": "node ./packageBuild.ts" @@ -20,4 +20,4 @@ "tsyringe": "4.7.0", "typescript": "4.9.4" } -} +} \ No newline at end of file diff --git a/mods/Mod Sync/BepInEx/plugins/Corter-ModSync.dll b/mods/Mod Sync/BepInEx/plugins/Corter-ModSync.dll index 19c3874..f6c42ed 100644 Binary files a/mods/Mod Sync/BepInEx/plugins/Corter-ModSync.dll and b/mods/Mod Sync/BepInEx/plugins/Corter-ModSync.dll differ diff --git a/mods/Mod Sync/ModSync.Updater.exe b/mods/Mod Sync/ModSync.Updater.exe deleted file mode 100644 index dcdc23f..0000000 Binary files a/mods/Mod Sync/ModSync.Updater.exe and /dev/null differ diff --git a/mods/Mod Sync/meta.ini b/mods/Mod Sync/meta.ini index b8ccd31..504a3bc 100644 --- a/mods/Mod Sync/meta.ini +++ b/mods/Mod Sync/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.16.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=Corter-ModSync-v0.10.0.zip +installationFile=Corter-ModSync-v0.11.1.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/package.json b/mods/Mod Sync/user/mods/Corter-ModSync/package.json index c792ec5..e4fcb9c 100644 --- a/mods/Mod Sync/user/mods/Corter-ModSync/package.json +++ b/mods/Mod Sync/user/mods/Corter-ModSync/package.json @@ -1,8 +1,8 @@ { "name": "Corter-ModSync", - "version": "0.10.0", + "version": "0.11.1", "main": "src/mod.js", - "sptVersion": "~3.10", + "sptVersion": "~3.11", "loadBefore": [], "loadAfter": [], "incompatibilities": [], @@ -17,12 +17,14 @@ }, "devDependencies": { "@biomejs/biome": "1.9.3", + "@types/fs-extra": "^11.0.4", "@types/node": "20.11", "@types/shelljs": "^0.8.15", "@typescript-eslint/eslint-plugin": "7.2", "@typescript-eslint/parser": "7.2", "@vitest/coverage-istanbul": "^2.1.2", - "fs-extra": "11.2", + "atomically": "^2.0.3", + "fs-extra": "~11.2.0", "glob": "^11.0.0", "i18n": "^0.15.1", "ignore": "^5.2", diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/config.ts b/mods/Mod Sync/user/mods/Corter-ModSync/src/config.ts index c60561e..0b8d656 100644 --- a/mods/Mod Sync/user/mods/Corter-ModSync/src/config.ts +++ b/mods/Mod Sync/user/mods/Corter-ModSync/src/config.ts @@ -1,12 +1,13 @@ import path from "node:path"; import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import type { JsonUtil } from "@spt/utils/JsonUtil"; -import type { VFS } from "@spt/utils/VFS"; +import type { FileSystem } from "@spt/utils/FileSystem"; import { glob } from "./utility/glob"; import { unixPath } from "./utility/misc"; import type { ILogger } from "@spt/models/spt/utils/ILogger"; export type SyncPath = { + name?: string; path: string; enabled?: boolean; enforced?: boolean; @@ -26,6 +27,7 @@ const DEFAULT_CONFIG = `{ "BepInEx/config", { "enabled": false, + "name": "(Optional) Server mods", "path": "user/mods", "restartRequired": false } @@ -41,7 +43,7 @@ const DEFAULT_CONFIG = `{ // Fika "user/mods/fika-server/types", "user/mods/fika-server/cache", - "BepInEx/plugins/Fika.Dedicated.dll", + "BepInEx/plugins/Fika.Headless.dll", // Live Flea Prices "user/mods/zzDrakiaXYZ-LiveFleaPrices/config", // Questing Bots @@ -55,6 +57,8 @@ const DEFAULT_CONFIG = `{ // ADD MISSING QUEST WEAPON REQUIREMENTS "user/mods/zz_guiltyman-addmissingquestweaponrequirements/log.log", "user/mods/zz_guiltyman-addmissingquestweaponrequirements/user/logs", + // Acid's Progressive Bot System + "user/mods/acidphantasm-progressivebotsystem/logs", // Corter ModSync "BepInEx/patchers/Corter-ModSync-Patcher.dll", "**/*.nosync", @@ -65,11 +69,6 @@ const DEFAULT_CONFIG = `{ "user/mods/**/*.js", "user/mods/**/*.js.map", "**/*:Zone.Identifier" - // Quest Tracker - "BepInEx/config/xyz.drakia.questtracker.cfg", - "BepInEx/plugins/DrakiaXYZ-QuestTracker/config/", - // Dynamic Maps - "BepInEx/config/com.mpstark.DynamicMaps.cfg" ] }`; @@ -83,19 +82,16 @@ export class Config { } public isExcluded(filePath: string): boolean { - return this._globs.some( - (glob) => - glob.test(unixPath(filePath)), - ); + return this._globs.some((glob) => glob.test(unixPath(filePath))); } } export class ConfigUtil { constructor( - private vfs: VFS, + private vfs: FileSystem, private jsonUtil: JsonUtil, private modImporter: PreSptModLoader, private logger: ILogger, - ) { } + ) {} /** * @throws {Error} If the config file does not exist @@ -104,12 +100,12 @@ export class ConfigUtil { const modPath = this.modImporter.getModPath("Corter-ModSync"); const configPath = path.join(modPath, "config.jsonc"); - if (!this.vfs.exists(configPath)) - await this.vfs.writeFilePromisify(configPath, DEFAULT_CONFIG); + if (!(await this.vfs.exists(configPath))) { + await this.vfs.write(configPath, DEFAULT_CONFIG); + } return this.jsonUtil.deserializeJsonC( - // @ts-expect-error I am right, SPT is wrong - await this.vfs.readFilePromisify(configPath, { encoding: "utf-8" }), + await this.vfs.read(configPath), "config.jsonc", ) as RawConfig; } @@ -128,7 +124,28 @@ export class ConfigUtil { "Corter-ModSync: config.jsonc 'exclusions' is not an array. Please verify your config is correct and try again.", ); + const uniquePaths = new Set(); for (const syncPath of config.syncPaths) { + if ( + typeof syncPath === "object" && + typeof syncPath.name !== "undefined" && + typeof syncPath.name !== "string" + ) { + throw new Error( + "Corter-ModSync: config.jsonc 'syncPaths.name' must be a string. Please verify your config is correct and try again.", + ); + } + + if ( + typeof syncPath === "object" && + typeof syncPath.name === "string" && + /[\n\t\\"'\[\]]/.test(syncPath.name) + ) { + throw new Error( + `Corter-ModSync: config.jsonc 'syncPaths.name' contains invalid characters. Please make sure your name does not include any of the following characters: \n \t \\ " ' [ ]`, + ); + } + if (typeof syncPath !== "string" && !("path" in syncPath)) throw new Error( "Corter-ModSync: config.jsonc 'syncPaths' is missing 'path'. Please verify your config is correct and try again.", @@ -157,6 +174,22 @@ export class ConfigUtil { throw new Error( `Corter-ModSync: SyncPaths must within SPT server root. Invalid path '${syncPath}'`, ); + + if ( + uniquePaths.has(typeof syncPath === "string" ? syncPath : syncPath.path) + ) + throw new Error( + `Corter-ModSync: SyncPaths must be unique. Duplicate path '${syncPath}'`, + ); + + if ( + config.exclusions.includes( + typeof syncPath === "string" ? syncPath : syncPath.path, + ) + ) + throw new Error( + `Corter-ModSync: '${syncPath}' has been added as a sync path and is also in the 'exclusions' array. This probably isn't doing what you want. If you no longer want to sync this path, remove it from the 'exclusions' and 'syncPaths' arrays.`, + ); } } @@ -172,6 +205,7 @@ export class ConfigUtil { silent: true, restartRequired: false, path: "ModSync.Updater.exe", + name: "(Builtin) ModSync Updater", }, { enabled: true, @@ -179,6 +213,7 @@ export class ConfigUtil { silent: true, restartRequired: true, path: "BepInEx/plugins/Corter-ModSync.dll", + name: "(Builtin) ModSync Plugin", }, ...rawConfig.syncPaths .map((syncPath) => ({ @@ -186,6 +221,7 @@ export class ConfigUtil { enforced: false, silent: false, restartRequired: true, + name: typeof syncPath === "string" ? syncPath : syncPath.path, ...(typeof syncPath === "string" ? { path: syncPath } : syncPath), })) .sort((a, b) => b.path.length - a.path.length), diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/mod.ts b/mods/Mod Sync/user/mods/Corter-ModSync/src/mod.ts index 11b3d3a..953bc20 100644 --- a/mods/Mod Sync/user/mods/Corter-ModSync/src/mod.ts +++ b/mods/Mod Sync/user/mods/Corter-ModSync/src/mod.ts @@ -5,13 +5,14 @@ import type { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; import type { ILogger } from "@spt/models/spt/utils/ILogger"; import type { HttpListenerModService } from "@spt/services/mod/httpListener/HttpListenerModService"; import type { HttpFileUtil } from "@spt/utils/HttpFileUtil"; -import type { VFS } from "@spt/utils/VFS"; +import type { FileSystem } from "@spt/utils/FileSystem"; import type { JsonUtil } from "@spt/utils/JsonUtil"; import { ConfigUtil, type Config } from "./config"; import { SyncUtil } from "./sync"; import { Router } from "./router"; import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import type { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; +import { Statter } from "./utility/statter"; class Mod implements IPreSptLoadMod { private static container: DependencyContainer; @@ -22,7 +23,7 @@ class Mod implements IPreSptLoadMod { public async preSptLoad(container: DependencyContainer): Promise { Mod.container = container; const logger = container.resolve("WinstonLogger"); - const vfs = container.resolve("VFS"); + const vfs = container.resolve("FileSystem"); const jsonUtil = container.resolve("JsonUtil"); const modImporter = container.resolve("PreSptModLoader"); const configUtil = new ConfigUtil(vfs, jsonUtil, modImporter, logger); @@ -46,12 +47,16 @@ class Mod implements IPreSptLoadMod { if (!vfs.exists("ModSync.Updater.exe")) { Mod.loadFailed = true; - logger.error("Corter-ModSync: ModSync.Updater.exe not found! Please ensure ALL files from the release zip are extracted onto the server."); + logger.error( + "Corter-ModSync: ModSync.Updater.exe not found! Please ensure ALL files from the release zip are extracted onto the server.", + ); } if (!vfs.exists("BepInEx/plugins/Corter-ModSync.dll")) { Mod.loadFailed = true; - logger.error("Corter-ModSync: Corter-ModSync.dll not found! Please ensure ALL files from the release zip are extracted onto the server."); + logger.error( + "Corter-ModSync: Corter-ModSync.dll not found! Please ensure ALL files from the release zip are extracted onto the server.", + ); } } @@ -65,17 +70,19 @@ class Mod implements IPreSptLoadMod { res: ServerResponse, ): Promise { const logger = Mod.container.resolve("WinstonLogger"); - const vfs = Mod.container.resolve("VFS"); + const vfs = Mod.container.resolve("FileSystem"); const httpFileUtil = Mod.container.resolve("HttpFileUtil"); const httpServerHelper = Mod.container.resolve("HttpServerHelper"); const modImporter = Mod.container.resolve("PreSptModLoader"); - const syncUtil = new SyncUtil(vfs, Mod.config, logger); + const statter = new Statter(); + const syncUtil = new SyncUtil(vfs, statter, Mod.config, logger); const router = new Router( Mod.config, syncUtil, vfs, + statter, httpFileUtil, httpServerHelper, modImporter, diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/router.ts b/mods/Mod Sync/user/mods/Corter-ModSync/src/router.ts index bbbfab5..aa46f2a 100644 --- a/mods/Mod Sync/user/mods/Corter-ModSync/src/router.ts +++ b/mods/Mod Sync/user/mods/Corter-ModSync/src/router.ts @@ -3,12 +3,13 @@ import type { SyncUtil } from "./sync"; import { glob } from "./utility/glob"; import type { IncomingMessage, ServerResponse } from "node:http"; import path from "node:path"; -import type { VFS } from "@spt/utils/VFS"; +import type { FileSystem } from "@spt/utils/FileSystem"; import type { Config } from "./config"; import { HttpError, winPath } from "./utility/misc"; import type { ILogger } from "@spt/models/spt/utils/ILogger"; import type { PreSptModLoader } from "@spt/loaders/PreSptModLoader"; import type { HttpServerHelper } from "@spt/helpers/HttpServerHelper"; +import type { IStatter } from "./utility/statter"; const FALLBACK_SYNCPATHS: Record = {}; @@ -20,22 +21,22 @@ FALLBACK_SYNCPATHS[undefined] = [ FALLBACK_SYNCPATHS["0.8.0"] = FALLBACK_SYNCPATHS["0.8.1"] = FALLBACK_SYNCPATHS["0.8.2"] = - [ - { - enabled: true, - enforced: true, - path: "BepInEx\\plugins\\Corter-ModSync.dll", - restartRequired: true, - silent: false, - }, - { - enabled: true, - enforced: true, - path: "ModSync.Updater.exe", - restartRequired: false, - silent: false, - }, - ]; + [ + { + enabled: true, + enforced: true, + path: "BepInEx\\plugins\\Corter-ModSync.dll", + restartRequired: true, + silent: false, + }, + { + enabled: true, + enforced: true, + path: "ModSync.Updater.exe", + restartRequired: false, + silent: false, + }, + ]; const FALLBACK_HASHES: Record = {}; @@ -47,28 +48,29 @@ FALLBACK_HASHES[undefined] = { FALLBACK_HASHES["0.8.0"] = FALLBACK_HASHES["0.8.1"] = FALLBACK_HASHES["0.8.2"] = - { - "BepInEx\\plugins\\Corter-ModSync.dll": { + { "BepInEx\\plugins\\Corter-ModSync.dll": { - crc: 999999999, - nosync: false, + "BepInEx\\plugins\\Corter-ModSync.dll": { + crc: 999999999, + nosync: false, + }, }, - }, - "ModSync.Updater.exe": { - "ModSync.Updater.exe": { crc: 999999999, nosync: false }, - }, - }; + "ModSync.Updater.exe": { + "ModSync.Updater.exe": { crc: 999999999, nosync: false }, + }, + }; export class Router { constructor( private config: Config, private syncUtil: SyncUtil, - private vfs: VFS, + private vfs: FileSystem, + private statter: IStatter, private httpFileUtil: HttpFileUtil, private httpServerHelper: HttpServerHelper, private modImporter: PreSptModLoader, private logger: ILogger, - ) { } + ) {} /** * @internal @@ -80,13 +82,8 @@ export class Router { _params: URLSearchParams, ) { const modPath = this.modImporter.getModPath("Corter-ModSync"); - const packageJson = JSON.parse( - // @ts-expect-error readFile returns a string when given a valid encoding - await this.vfs - // @ts-expect-error readFile takes in an options object, including an encoding option - .readFilePromisify(path.join(modPath, "package.json"), { - encoding: "utf-8", - }), + const packageJson = await this.vfs.readJson( + path.join(modPath, "package.json"), ); res.setHeader("Content-Type", "application/json"); @@ -185,19 +182,19 @@ export class Router { this.config.syncPaths, ); - if (!this.vfs.exists(sanitizedPath)) + if (!(await this.vfs.exists(sanitizedPath))) throw new HttpError( 404, `Attempt to access non-existent path ${filePath}`, ); try { - const fileStats = await this.vfs.statPromisify(sanitizedPath); + const fileStats = await this.statter.stat(sanitizedPath); res.setHeader("Accept-Ranges", "bytes"); res.setHeader( "Content-Type", this.httpServerHelper.getMimeText(path.extname(filePath)) || - "text/plain", + "text/plain", ); res.setHeader("Content-Length", fileStats.size); return this.httpFileUtil.sendFileAsync(res, sanitizedPath); diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/sync.ts b/mods/Mod Sync/user/mods/Corter-ModSync/src/sync.ts index 61334ed..4e1756e 100644 --- a/mods/Mod Sync/user/mods/Corter-ModSync/src/sync.ts +++ b/mods/Mod Sync/user/mods/Corter-ModSync/src/sync.ts @@ -1,11 +1,11 @@ -import type { VFS } from "@spt/utils/VFS"; -import path from "node:path"; -import { hashFile } from "./utility/imoHash"; +import path from "node:path"; +import type { ILogger } from "@spt/models/spt/utils/ILogger"; +import type { FileSystem } from "@spt/utils/FileSystem"; import type { Config, SyncPath } from "./config"; +import { hashFile } from "./utility/imoHash"; import { HttpError, winPath } from "./utility/misc"; import { Semaphore } from "./utility/semaphore"; -import type { ILogger } from "@spt/models/spt/utils/ILogger"; -import type { } from "node:fs"; +import type { IStatter } from "./utility/statter"; type ModFile = { hash: string; @@ -16,24 +16,25 @@ export class SyncUtil { private limiter = new Semaphore(1024); constructor( - private vfs: VFS, + private vfs: FileSystem, + private statter: IStatter, private config: Config, private logger: ILogger, - ) { } + ) {} private async getFilesInDir(baseDir: string, dir: string): Promise { - if (!this.vfs.exists(dir)) { + if (!(await this.vfs.exists(dir))) { this.logger.warning( `Corter-ModSync: Directory '${dir}' does not exist, will be ignored.`, ); return []; } - const stats = await this.vfs.statPromisify(dir); + const stats = await this.statter.stat(dir); if (stats.isFile()) return [dir]; const files: string[] = []; - for (const fileName of this.vfs.getFiles(dir)) { + for (const fileName of await this.vfs.getFiles(dir)) { const file = path.join(dir, fileName); if (this.config.isExcluded(file)) continue; @@ -41,18 +42,27 @@ export class SyncUtil { files.push(file); } - for (const dirName of this.vfs.getDirs(dir)) { + for (const dirName of await this.vfs.getDirectories(dir)) { const subDir = path.join(dir, dirName); if (this.config.isExcluded(subDir)) continue; const subFiles = await this.getFilesInDir(baseDir, subDir); - if (!subFiles.length) files.push(subDir) + if ( + (await this.vfs.getFiles(subDir)).length === 0 && + (await this.vfs.getDirectories(subDir)).length === 0 + ) + files.push(subDir); files.push(...subFiles); } - if (stats.isDirectory() && files.length === 0) files.push(dir); + if ( + stats.isDirectory() && + (await this.vfs.getFiles(dir)).length === 0 && + (await this.vfs.getDirectories(dir)).length === 0 + ) + files.push(dir); return files; } @@ -60,9 +70,9 @@ export class SyncUtil { private async buildModFile( file: string, // biome-ignore lint/correctness/noEmptyPattern: - { }: Required, + {}: Required, ): Promise { - const stats = await this.vfs.statPromisify(file); + const stats = await this.statter.stat(file); if (stats.isDirectory()) return { hash: "", directory: true }; let retryCount = 0; @@ -77,8 +87,15 @@ export class SyncUtil { directory: false, }; } catch (e) { - if (e instanceof Error && 'code' in e && e.code === "EBUSY" && retryCount < 5) { - this.logger.error(`Error reading '${file}'. Retrying (${retryCount}/5)...`); + if ( + e instanceof Error && + "code" in e && + e.code === "EBUSY" && + retryCount < 5 + ) { + this.logger.error( + `Error reading '${file}'. Retrying (${retryCount}/5)...`, + ); await new Promise((resolve) => setTimeout(resolve, 500)); retryCount++; continue; @@ -86,7 +103,10 @@ export class SyncUtil { this.logger.error(`Error reading '${file}'. Exiting...`); this.logger.error(`${e}`); - throw new HttpError(500, `Corter-ModSync: Error reading '${file}'\n${e}`); + throw new HttpError( + 500, + `Corter-ModSync: Error reading '${file}'\n${e}`, + ); } } } @@ -113,7 +133,9 @@ export class SyncUtil { result[winPath(syncPath.path)] = filesResult; } - this.logger.info(`Corter-ModSync: Hashed ${processedFiles.size} files in ${performance.now() - startTime}ms`); + this.logger.info( + `Corter-ModSync: Hashed ${processedFiles.size} files in ${performance.now() - startTime}ms`, + ); return result; } diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metroHash.ts b/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metroHash.ts index f56f0ac..5af3180 100644 --- a/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metroHash.ts +++ b/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metroHash.ts @@ -69,17 +69,17 @@ const wasmInstance = new WebAssembly.Instance(wasmModule, { takeObject(arg0); }, - __wbg_buffer_ccaed51a635d8a2d: (arg0: number) => { + __wbg_buffer_609cc3eee51ed158: (arg0: number) => { const ret = getObject(arg0).buffer; return addHeapObject(ret); }, - __wbg_newwithbyteoffsetandlength_7e3eb787208af730: (arg0: number, arg1: number, arg2: number) => { + __wbg_newwithbyteoffsetandlength_d97e637ebe145a9a: (arg0: number, arg1: number, arg2: number) => { const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0); return addHeapObject(ret); }, - __wbg_new_fec2611eb9180f95: (arg0: number) => { + __wbg_new_a12002a7f91c75be: (arg0: number) => { const ret = new Uint8Array(getObject(arg0)); return addHeapObject(ret); }, diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metrohash.wasm b/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metrohash.wasm index 6588bbc..c786f68 100644 Binary files a/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metrohash.wasm and b/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/metrohash.wasm differ diff --git a/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/statter.ts b/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/statter.ts new file mode 100644 index 0000000..fb475e8 --- /dev/null +++ b/mods/Mod Sync/user/mods/Corter-ModSync/src/utility/statter.ts @@ -0,0 +1,9 @@ +import { stat, type Stats } from "fs-extra"; + +export interface IStatter { + stat: (path: string) => Promise; +} + +export class Statter implements IStatter { + public stat = stat; +} diff --git a/mods/Mod Sync/user/mods/ModSync.Updater.exe b/mods/Mod Sync/user/mods/ModSync.Updater.exe new file mode 100644 index 0000000..2dfa516 --- /dev/null +++ b/mods/Mod Sync/user/mods/ModSync.Updater.exe @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c436498e480ed791c51bf704322a4168d725e46945c0270bbef4f7c4bb14ce01 +size 1619511 diff --git a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Greed.exe b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Greed.exe index 6333fe0..e34ff0a 100644 Binary files a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Greed.exe and b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Greed.exe differ diff --git a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Loader/loader.json b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Loader/loader.json index a1b632c..9951fdb 100644 --- a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Loader/loader.json +++ b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/Loader/loader.json @@ -1,3 +1,3 @@ { -"CurrentlySelectedPreset":"Default" +"CurrentlySelectedPreset":"null" } \ No newline at end of file diff --git a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/package.json b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/package.json index aeee86b..4c5b8c0 100644 --- a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/package.json +++ b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/package.json @@ -1,8 +1,8 @@ { "name": "SVM", "author": "GhostFenixx", - "version": "1.10.1", - "sptVersion": "3.10.x", + "version": "1.11.1", + "sptVersion": "3.11.x", "license": "PUSL", "devdependencies": {}, "main": "src/SVM.js" diff --git a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Arrays.json b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Arrays.json index ec7ee77..0f54ee6 100644 --- a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Arrays.json +++ b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Arrays.json @@ -3,9 +3,10 @@ "ARifle":["762x39", "545x39", "556x45", "9x39", "366", "762x35", "300blk", "ATL15", "GRENDEL", "50WLF", "KURZ"], "Shotgun":["12x70", "20x70", "23x75"], "MRifle":["762x51", "68x51", "762х54R", "762x54r", "86x70", "127x55", "277", "BMG"], + "OddKeys":["5448ba0b4bdc2d02308b456c","63a399193901f439517cafb6","63a39fc0af870e651d58e6ae","63a39fdf1e21260da44a0256","6582dbf0b8d7830efc45016f","664d3db6db5dea2bad286955","664d3dd590294949fe2d81b7","664d3ddfdda2e85aca370d75","664d3de85f2355673b09aed5","664d4b0103ef2c61246afb56","6761a6ccd9bbb27ad703c48a","6761a6f90575f25e020816a4"], "MarkedKeys":["5780cf7f2459777de4559322", "5d80c60f86f77440373c4ece", "5d80c62a86f7744036212b3f", "5ede7a8229445733cb4c18e2", "63a3a93f8a56922e82001f5d", "64ccc25f95763a1ae376e447", "62987dfc402c7f69bf010923"], - "traderArray": ["54cb50c76803fa8b248b4571", "54cb57776803fa99248b456e", "58330581ace78e27b8b10cee", "5935c25fb3acc3127c3d8cd9", "5a7c2eca46aef81a7ca2145d", "5ac3b934156ae10c4430e83c", "5c0647fdd443bc2504c2d371"], - "CasesID":["59fb016586f7746d0d4b423a","5783c43d2459774bbe137486","60b0f6c058e0b0481a09ad11","5e2af55f86f7746d4159f07c","59fb042886f7746c5005a7b2","59fb023c86f7746d0d4b423c","5b7c710788a4506dec015957","5aafbde786f774389d0cbc0f","5c127c4486f7745625356c13","5c093e3486f77430cb02e593","5aafbcd986f7745e590fff23","5c0a840b86f7742ffa4f2482","5b6d9ce188a4501afc1b2b25","5d235bb686f77443f4331278","59fafd4b86f7745ca07e1232","590c60fc86f77412b13fddcf","567143bf4bdc2d1a0f8b4567","5c093db286f7740a1b2617e3","619cbf7d23893217ec30b689","619cbf9e0a7c3a1a2731940a","62a09d3bcf4a99369e262447","66bc98a01a47be227a5e956e"], + "traderArray": ["54cb50c76803fa8b248b4571", "54cb57776803fa99248b456e","579dc571d53a0658a154fbec", "58330581ace78e27b8b10cee", "5935c25fb3acc3127c3d8cd9", "5a7c2eca46aef81a7ca2145d", "5ac3b934156ae10c4430e83c", "5c0647fdd443bc2504c2d371","6617beeaa9cfa777ca915b7c"], + "CasesID":["59fb016586f7746d0d4b423a","5783c43d2459774bbe137486","60b0f6c058e0b0481a09ad11","5e2af55f86f7746d4159f07c","59fb042886f7746c5005a7b2","59fb023c86f7746d0d4b423c","5b7c710788a4506dec015957","5aafbde786f774389d0cbc0f","5c127c4486f7745625356c13","5c093e3486f77430cb02e593","5aafbcd986f7745e590fff23","5c0a840b86f7742ffa4f2482","5b6d9ce188a4501afc1b2b25","5d235bb686f77443f4331278","59fafd4b86f7745ca07e1232","590c60fc86f77412b13fddcf","567143bf4bdc2d1a0f8b4567","5c093db286f7740a1b2617e3","619cbf7d23893217ec30b689","619cbf9e0a7c3a1a2731940a","62a09d3bcf4a99369e262447","66bc98a01a47be227a5e956e","67600929bd0a0549d70993f6"], "SecConID":["544a11ac4bdc2d470e8b456a","5c093ca986f7740a1867ab12","5857a8b324597729ab0a0e7d","59db794186f77448bc595262","5857a8bc2459772bad15db29","665ee77ccf2d642e98220bca","5732ee6a24597719ae0c0281","64f6f4c5911bcdfe8b03b0dc"], "AIAmount":["AsOnline","Low","Medium","High","Horde","NoBots"], "AIDifficulty":["AsOnline","Easy","Medium","Hard","Impossible","Random"], diff --git a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Misc.json b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Misc.json index 76119f4..f9270be 100644 --- a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Misc.json +++ b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/Misc.json @@ -1,6 +1,5 @@ { "funni":[ - "So, how's Erdtree? Or maybe you're into Dawntrail?", "Have you tried Minecraft though?", "I hope Nikita is proud of me", "This release provides you 16x time the details according to Todd Howard", @@ -27,7 +26,6 @@ "Don't play Gaijin games, such a cash grab, good source of classified data tho", "YOU, YES YOU, YOU ARE AWESOME, NOW LIVE WITH THAT!", "Did you wash your dishes?", - "Sanitar did nothing wrong", "I actually played tarkov, finally", "rat attac", "Don't worry, I won't judge your preferences, you're my favorite casual <3", @@ -52,14 +50,11 @@ "No more Autumn in Tarkov", "The Holy Fox is gone", "Don't play MMO's, those are unhealthy", - "I swear, stop putting those lines on Reddit", "I really hope you do not restart the server to read those", "There will be Up. Date. A creator on website nodes his head.", "Stay Hydrated", - "Have you tried Gray Zone Warfare? :D", "Still filled with bugs, don't worry", "Pls leave a like and subscribe, don't forget to click a bell button", - "Google Clearly doesn't like me ;-;", "Name of your PMC is disgusting, please revaluate your life choices", "Why do you want to carry 2000 rounds in one slot?", "Really? 20x2 cell sized pockets?", @@ -70,7 +65,6 @@ "Each new line added in message of the day is increasing the load on your CPU, let that sink in", "Totally against Nikita(tm) vision(tm).", "3 HEAVY BLEEDS IN ONE SHOT, THANKS SCAV!", - "What else can be so joyful than making a mod on my birthday? Not playing SPT afterwards!", "Nice work bitch!", "You can never go wrong loading .366 into 7.62x39 chamber", "There was a blue sky and bright yellow field, but now it is always dark and gray", @@ -82,11 +76,9 @@ "They added a Deagle in the game...Makes me miss KMC once again", "Swedish siesta is fun ngl", "Yep, people still seem to fail to install this mod", - "3.9.8 huh? SPT lads do stack those numbers high", "Cats are still superior than dogs, CWX.", - "Someday the war will be over and world won't be the same...", + "Someday the war will be over but the world won't be the same...", "Do yourself a favor, stop shitposting", - "If SPT can go 3.10, so is SVM can go 1.10", "Show must go on!", ">Are You Happy? >No. >Why? >I play Escape from Tarkov", "Deagle or Rsh12? That's the question.", @@ -117,11 +109,19 @@ "More music? Fine: Saint Asonia, Alesti, Memphis May Fire", "Not really hyped for 2025 ngl", "Four letters is better than three", - "No, these messages is not made by ChatGPT", + "No, these messages are not made by ChatGPT", "I finally tried stalker 2, could've been better", "Unironically gonna listen to C418", "Want some DnB? Celldweller, Raizer, Blue Stahli", "I hate winter", "Astrobot being GOTY is a joke", + "I really ran out of stuff to type here", + "Denuvo sucks, big corpos never learn", + "I'll miss Ubisoft", + "Yes, i hate Jaeger quests sometimes as well", + "Haha, your SSD goes BRRRRR", + "Not a fan or orange color really, neither of alternate current", + "XcQ, IYKYK", + "\nI/Ii\nI|/I_", "You cannot Escape From -REDACTED BY LICENSE VIOLATION-"] } \ No newline at end of file diff --git a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/SVM.js b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/SVM.js index 5987015..d468628 100644 --- a/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/SVM.js +++ b/mods/Server Value Modifier/User/Mods/[SVM] Server Value Modifier/src/SVM.js @@ -21,68 +21,10 @@ class MainSVM { Logger.error(e.message + "\n"); return } - const PresetLoader = require('../Loader/loader.json'); const Config = require('../Presets/' + PresetLoader.CurrentlySelectedPreset + '.json'); const StaticRouterModService = container.resolve("StaticRouterModService"); const HttpResponse = container.resolve("HttpResponseUtil"); - const repeatableQuestController = container.resolve("RepeatableQuestController"); - - //PRE LOAD - QUESTS SECTION - if (Config.Quests.EnableQuests && Config.Quests.EnableQuestsMisc)//Horrible, as usual - { - try { - container.afterResolution("QuestCallbacks", (_t, result) => { - result.activityPeriods = (url, info, sessionID) => { - let Edited = repeatableQuestController.getClientRepeatableQuests(sessionID); - for (let quests in Edited) { - for (let act in Edited[quests].activeQuests) {//this needs to be trimmed as well, eventually. - if (Edited[quests].activeQuests[act].changeCost[0].count !== undefined) { - Edited[quests].activeQuests[act].changeCost[0].count = 5000 * Config.Quests.QuestCostMult; - } - if (Config.Quests.QuestRepToZero) { - Edited[quests].activeQuests[act].changeStandingCost = 0; - } - } - for (let inact in Edited[quests].inactiveQuests) { - if (Edited[quests].inactiveQuests[inact].changeCost[0].count !== undefined) { - Edited[quests].inactiveQuests[inact].changeCost[0].count = 5000 * Config.Quests.QuestCostMult; - } - if (Config.Quests.QuestRepToZero) { - Edited[quests].inactiveQuests[inact].changeStandingCost = 0; - } - } - for (let req in Edited[quests].changeRequirement) { - Edited[quests].changeRequirement[req].changeCost[0].count = 5000 * Config.Quests.QuestCostMult; - if (Config.Quests.QuestRepToZero) { - Edited[quests].changeRequirement[req].changeStandingCost = 0; - } - } - } - return HttpResponse.getBody(Edited); - } - }, { frequency: "Always" }); - - container.afterResolution("QuestCallbacks", (_t, result) => { - result.changeRepeatableQuest = (pmcData, body, sessionID) => { - //const repeatableQuestController = container.resolve("RepeatableQuestController"); - let Edited = repeatableQuestController.changeRepeatableQuest(pmcData, body, sessionID); - for (let quests in Edited.profileChanges) { - for (let test in Edited.profileChanges[quests].repeatableQuests[0].changeRequirement) { - Edited.profileChanges[quests].repeatableQuests[0].changeRequirement[test].changeCost[0].count = 5000 * Config.Quests.QuestCostMult; - if (Config.Quests.QuestRepToZero) { - Edited.profileChanges[quests].repeatableQuests[0].changeRequirement[test].changeStandingCost = 0; - } - } - } - return Edited; - } - }, { frequency: "Always" }); - } - catch (e) { - Logger.error("[SVM] REPEATABLE QUEST HANDLED EXCEPTION - Something wrong attempting to change quest reroll price\n" + e.message + "\n"); - } - } //PRE LOAD - RAIDS SECTION if (Config.Raids.RaidEvents.Halloween || Config.Raids.RaidEvents.Christmas)//Extra check, just in case { @@ -123,7 +65,7 @@ class MainSVM { if (Config.Raids.SafeExit) { container.afterResolution("MatchCallbacks", (_t, result) => { result.endLocalRaid = (url, info, sessionID) => { - if (info.results.result == "Left") { + if (info.results.result == "Left" && info.results.profile.Info.Side !== "Savage") { info.results.result = "Runner" } const MatchController = container.resolve("MatchController"); @@ -135,7 +77,7 @@ class MainSVM { if (Config.Raids.SaveGearAfterDeath) { container.afterResolution("MatchCallbacks", (_t, result) => { result.endLocalRaid = (url, info, sessionID) => { - if (info.results.result !== "Survived") { + if (info.results.result !== "Survived" && info.results.profile.Info.Side !== "Savage") { info.results.result = "Runner" } const MatchController = container.resolve("MatchController"); @@ -189,7 +131,7 @@ class MainSVM { return HttpResponse.nullResponse(); } catch (e) { - Logger.error("[SVM] CSM CUSTOM POCKETS - New profile detected, Cancelling function, restart the game to fix it.\n" + e.message) + Logger.warning("[SVM] CSM CUSTOM POCKETS - New profile detected, Cancelling function, restart the game to fix it.\n" + e.message) return HttpResponse.nullResponse(); } } @@ -232,7 +174,7 @@ class MainSVM { return HttpResponse.nullResponse(); } catch (e) { - Logger.error("[SVM] PMC/SCAV HEALTH/STATS - Didn't manage to apply settings, new profile?\n" + e) + Logger.warning("[SVM] PMC/SCAV HEALTH/STATS - Didn't manage to apply settings, new profile?\n" + e) return HttpResponse.nullResponse(); } } @@ -240,6 +182,7 @@ class MainSVM { } if (Config.Scav.EnableScavHealth || Config.Scav.ScavCustomPockets || Config.Scav.EnableStats) { // TO OVERRIDE NEXT SCAVS HEALTH + POCKETS //May Omnissiah save our souls, have to use both because register affects deaths and resolution affects extracts. Don't ask + // ###### SURVIVED container.afterResolution("ProfileController", (_t, result) => { result.generatePlayerScav = (sessionID) => { const playerScavGenerator = container.resolve("PlayerScavGenerator"); @@ -262,13 +205,13 @@ class MainSVM { return scavData; } }, { frequency: "Always" }); - + // ###### DIED StaticRouterModService.registerStaticRouter("EditHealthv2", [ { url: "/client/match/local/end", action: (url, info, sessionID) => { - if (info.results.result !== "survived" && info.results.result !== "runner") // 3.9.0 If statement for avoiding rerolling survived SCAV, biggest issue of 1.8.3 + if (info.results.result !== "Survived" && info.results.result !== "Transit" && info.results.result !== "Runner") // 3.9.0 If statement for avoiding rerolling survived SCAV, biggest issue of 1.8.3 { const saveServer = container.resolve("SaveServer"); const playerScavGenerator = container.resolve("PlayerScavGenerator"); @@ -351,6 +294,7 @@ class MainSVM { //const Inventory = configServer.getConfig("aki-inventory"); const BlackItems = configServer.getConfig("spt-item"); const PMC = configServer.getConfig("spt-pmc") + //############## FLEAMARKET SECTION ########### if (Config.Fleamarket.EnableFleamarket) { if (Config.Fleamarket.EnablePlayerOffers) { @@ -599,39 +543,20 @@ class MainSVM { const BotTypeID = ["assault", "marksman", "pmcbot", "exusec", "pmc", "boss", "follower"] for (let durab in BotTypeID) { - Bots.durability[BotTypeID[durab]].weapon.lowestMax = BotWepMinID[durab]; - Bots.durability[BotTypeID[durab]].weapon.highestMax = BotWepMaxID[durab]; - Bots.durability[BotTypeID[durab]].armor.maxDelta = 100 - BotArmorMinID[durab]; - Bots.durability[BotTypeID[durab]].armor.minDelta = 100 - BotArmorMaxID[durab]; + AdjustDurab(Bots.durability[BotTypeID[durab]], BotWepMinID[durab], BotWepMaxID[durab], BotArmorMinID[durab], BotArmorMaxID[durab]) switch (BotTypeID[durab]) { case "assault": - Bots.durability["cursedassault"].weapon.lowestMax = BotWepMinID[durab] - Bots.durability["cursedassault"].weapon.highestMax = BotWepMaxID[durab] - Bots.durability["cursedassault"].armor.maxDelta = 100 - BotArmorMinID[durab]; - Bots.durability["cursedassault"].armor.minDelta = 100 - BotArmorMaxID[durab]; - - Bots.durability["crazyassaultevent"].weapon.lowestMax = BotWepMinID[durab] - Bots.durability["crazyassaultevent"].weapon.highestMax = BotWepMaxID[durab] - Bots.durability["crazyassaultevent"].armor.maxDelta = 100 - BotArmorMinID[durab]; - Bots.durability["crazyassaultevent"].armor.minDelta = 100 - BotArmorMaxID[durab]; + AdjustDurab(Bots.durability["cursedassault"], BotWepMinID[durab], BotWepMaxID[durab], BotArmorMinID[durab], BotArmorMaxID[durab]) + AdjustDurab(Bots.durability["crazyassaultevent"], BotWepMinID[durab], BotWepMaxID[durab], BotArmorMinID[durab], BotArmorMaxID[durab]) break; case "pmcbot": - Bots.durability["arenafighterevent"].weapon.lowestMax = BotWepMinID[durab] - Bots.durability["arenafighterevent"].weapon.highestMax = BotWepMaxID[durab] - Bots.durability["arenafighterevent"].armor.maxDelta = 100 - BotArmorMinID[durab]; - Bots.durability["arenafighterevent"].armor.minDelta = 100 - BotArmorMaxID[durab]; + AdjustDurab(Bots.durability["arenafighterevent"], BotWepMinID[durab], BotWepMaxID[durab], BotArmorMinID[durab], BotArmorMaxID[durab]) break case "boss": - Bots.durability["sectantpriest"].weapon.lowestMax = BotWepMinID[durab]; - Bots.durability["sectantpriest"].weapon.highestMax = BotWepMaxID[durab]; - Bots.durability["sectantpriest"].armor.maxDelta = 100 - BotArmorMinID[durab]; - Bots.durability["sectantpriest"].armor.minDelta = 100 - BotArmorMaxID[durab]; + AdjustDurab(Bots.durability["sectantpriest"], BotWepMinID[durab], BotWepMaxID[durab], BotArmorMinID[durab], BotArmorMaxID[durab]) break; case "follower": - Bots.durability["sectantwarrior"].weapon.lowestMax = BotWepMinID[durab]; - Bots.durability["sectantwarrior"].weapon.highestMax = BotWepMaxID[durab]; - Bots.durability["sectantwarrior"].armor.maxDelta = 100 - BotArmorMinID[durab]; - Bots.durability["sectantwarrior"].armor.minDelta = 100 - BotArmorMaxID[durab]; + AdjustDurab(Bots.durability["sectantwarrior"], BotWepMinID[durab], BotWepMaxID[durab], BotArmorMinID[durab], BotArmorMaxID[durab]) break; } } @@ -874,7 +799,8 @@ class MainSVM { Cases.InjectorCase, Cases.KeycardHolderCase, Cases.GKeychain, - Cases.StreamerCase + Cases.StreamerCase, + Cases.ArmorPlateCase ] const Filts = [ // I think i can shortcut this eventually Cases.MoneyCase.Filter, @@ -898,7 +824,8 @@ class MainSVM { Cases.InjectorCase.Filter, Cases.KeycardHolderCase.Filter, Cases.GKeychain.Filter, - Cases.StreamerCase.Filter + Cases.StreamerCase.Filter, + Cases.ArmorPlateCase.Filter ] for (let Case in Arrays.CasesID) { items[Arrays.CasesID[Case]]._props.Grids[0]._props["cellsV"] = Size[Case].Height; @@ -988,6 +915,7 @@ class MainSVM { if (base._type !== "Node" && base._type !== undefined && (base._parent !== "557596e64bdc2dc2118b4571" || base._parent !== "55d720f24bdc2d88028b456d")) { EditSimpleItemData(id, "Weight", parseFloat(Config.Items.WeightChanger * base._props.Weight).toFixed(3)); } + //Remove gear penalty if (Config.Items.NoGearPenalty) { if (base._props.mousePenalty) { EditSimpleItemData(id, "mousePenalty", 0) @@ -1055,14 +983,17 @@ class MainSVM { //Remove the keys usage - God i hate how i wrote it if (Config.Items.EnableKeys) { if ((base._parent == "5c99f98d86f7745c314214b3") && base._props.MaximumNumberOfUsage !== undefined && Config.Items.InfiniteKeys) { - if (base._parent == "5c99f98d86f7745c314214b3" && base._props.MaximumNumberOfUsage == 1 && !Config.Items.AvoidSingleKeys) { base._props.MaximumNumberOfUsage = 0 } if (Arrays.MarkedKeys.includes(base._id) && !Config.Items.AvoidMarkedKeys) { base._props.MaximumNumberOfUsage = 0 } - if (!(Arrays.MarkedKeys.includes(base._id)) && base._props.MaximumNumberOfUsage !== 1) { + if (Arrays.OddKeys.includes(base._id) && !Config.Items.AvoidOddKeys)//Currently list is static, maybe i'll rework to just consider changing due to odd numbers. + { + base._props.MaximumNumberOfUsage = 0 + } + if (!Arrays.MarkedKeys.includes(base._id) && !Arrays.OddKeys.includes(base._id) && base._props.MaximumNumberOfUsage !== 1) { base._props.MaximumNumberOfUsage = 0 } } @@ -1232,7 +1163,7 @@ class MainSVM { globals.Stamina.BaseRestorationRate = Config.Player.RegenStaminaLegs globals.Stamina.JumpConsumption = Config.Player.JumpConsumption globals.Stamina.StandupConsumption.x = Config.Player.LayToStand - globals.Stamina.PoseLevelConsumptionPerNotch.x = Config.Player.CrouchToStand/10; + globals.Stamina.PoseLevelConsumptionPerNotch.x = Config.Player.CrouchToStand / 10; } if (Config.Player.EnableStaminaHands) { // globals.Stamina.AimDrainRate = Config.Player. @@ -1271,6 +1202,7 @@ class MainSVM { hideout.settings.airFilterUnitFlowRate *= Config.Hideout.AirFilterRate; hideout.settings.gpuBoostRate *= Config.Hideout.GPUBoostRate; HideoutConfig.cultistCircle.maxRewardItemCount = Config.Hideout.CultistMaxRewards + HideoutConfig.cultistCircle.hideoutTaskRewardTimeSeconds = parseInt(HideoutConfig.cultistCircle.hideoutTaskRewardTimeSeconds * Config.Hideout.CultistTime) for (let time in HideoutConfig.cultistCircle.craftTimeThreshholds) { HideoutConfig.cultistCircle.craftTimeThreshholds[time].craftTimeSeconds = parseInt(HideoutConfig.cultistCircle.craftTimeThreshholds[time].craftTimeSeconds * Config.Hideout.CultistTime) } @@ -1333,7 +1265,7 @@ class MainSVM { } } //Remove construction requirements - if (Config.Hideout.RemoveConstructionsRequirements || Config.Hideout.RemoveSkillRequirements || Config.Hideout.RemoveTraderLevelRequirements) { + if (Config.Hideout.RemoveConstructionsRequirements || Config.Hideout.RemoveSkillRequirements || Config.Hideout.RemoveTraderLevelRequirements || Config.Hideout.RemoveConstructionsFIRRequirements) { for (const data in hideout.areas) { let areaData = hideout.areas[data] for (const stage in areaData.stages) { @@ -1341,6 +1273,9 @@ class MainSVM { let rewriter = []; for (let req in areaData.stages[stage].requirements)//This is horrible { + if (areaData.stages[stage].requirements[req].hasOwnProperty("isSpawnedInSession") && Config.Hideout.RemoveConstructionsFIRRequirements) { + areaData.stages[stage].requirements[req].isSpawnedInSession = false; + } if (areaData.stages[stage].requirements[req].hasOwnProperty("templateId") && !Config.Hideout.RemoveConstructionsRequirements) { rewriter.push(areaData.stages[stage].requirements[req]) } @@ -1442,6 +1377,7 @@ class MainSVM { } if (locations[i].base.exits[x].PassageRequirement == "TransferItem" && Config.Raids.EnableCarCoop) { locations[i].base.exits[x].ExfiltrationTime = Config.Raids.Exfils.CarExtractTime; + locations[i].base.exits[x].ExfiltrationTimePVE = Config.Raids.Exfils.CarExtractTime; switch (i) { case "woods": if (Config.Raids.Exfils.CarWoods !== 0) { @@ -1586,6 +1522,7 @@ class MainSVM { for (let x in locations[i].base.exits) { if (locations[i].base.exits[x].Name !== "EXFIL_Train") { locations[i].base.exits[x].Chance = 100; + locations[i].base.exits[x].ChancePVE = 100; } } } @@ -1907,7 +1844,7 @@ class MainSVM { } globals.BTRSettings.BasePriceTaxi = Config.Raids.BTRTaxiPrice globals.BTRSettings.CleanUpPrice = Config.Raids.BTRCoverPrice - globals.BTRSettings.BearPriceMod = Config.Raids.BearMults + globals.BTRSettings.BearPriceMod = Config.Raids.BearMult globals.BTRSettings.UsecPriceMod = Config.Raids.UsecMult globals.BTRSettings.ScavPriceMod = Config.Raids.ScavMult } @@ -1951,17 +1888,12 @@ class MainSVM { Mark.Peacekeeper, Mark.Mechanic, Mark.Ragman, - Mark.Jaeger + Mark.Jaeger, + Mark.Ref ] - let i = 0; - for (let CurTrader in traders) {//Bad solution to avoid modded traders. - if (CurTrader !== "ragfair" && (CurTrader == "5a7c2eca46aef81a7ca2145d" || CurTrader == "5ac3b934156ae10c4430e83c" || - CurTrader == "5c0647fdd443bc2504c2d371" || CurTrader == "54cb50c76803fa8b248b4571" || CurTrader == "54cb57776803fa99248b456e" || - CurTrader == "579dc571d53a0658a154fbec" || CurTrader == "5935c25fb3acc3127c3d8cd9" || CurTrader == "58330581ace78e27b8b10cee")) { - for (let level in traders[CurTrader].base.loyaltyLevels) { - traders[CurTrader].base.loyaltyLevels[level].buy_price_coef = 100 - MarkArray[i] - } - i++ + for (let CurTrader in Arrays.traderArray) { + for (let level in traders[Arrays.traderArray[CurTrader]].base.loyaltyLevels) { + traders[Arrays.traderArray[CurTrader]].base.loyaltyLevels[level].buy_price_coef = 100 - MarkArray[CurTrader] } } //Enable all the quests @@ -2039,23 +1971,27 @@ class MainSVM { Sell.Peacekeeper, Sell.Mechanic, Sell.Ragman, - Sell.Jaeger + Sell.Jaeger, + Sell.Ref ] let p = 0; for (let CurTrader in Arrays.traderArray) { - for (let assortment in traders[Arrays.traderArray[CurTrader]].assort.barter_scheme) { - let TradeAssort = traders[Arrays.traderArray[CurTrader]].assort.barter_scheme[assortment][0][0]; - switch (TradeAssort._tpl) { - case "5449016a4bdc2d6f028b456f": - case "569668774bdc2da2298b4568": - case "5696686a4bdc2da3298b456a": - if (TradeAssort.count !== undefined) { - TradeAssort.count = parseFloat((TradeAssort.count * SellArray[p]).toFixed(2)); - } - break; + if (Arrays.traderArray[CurTrader] != "579dc571d53a0658a154fbec") { //Bandaid, added it to fit markup yet there is no assort to edit for fence + for (let assortment in traders[Arrays.traderArray[CurTrader]].assort.barter_scheme) { + let TradeAssort = traders[Arrays.traderArray[CurTrader]].assort.barter_scheme[assortment][0][0]; + switch (TradeAssort._tpl) { + case "5449016a4bdc2d6f028b456f": + case "569668774bdc2da2298b4568": + case "5696686a4bdc2da3298b456a": + case "5d235b4d86f7742e017bc88a": + if (TradeAssort.count !== undefined) { + TradeAssort.count = parseFloat((TradeAssort.count * SellArray[p]).toFixed(2)); + } + break; + } } + p++; } - p++; } if (Config.Traders.RemoveCurrencyOffers || Config.Traders.RemoveBarterOffers) { for (let CurTrader in traders) { @@ -2096,36 +2032,19 @@ class MainSVM { } } } - //############## PMC SECTION ##################, + //############## PMC SECTION ################## if (Config.PMC.EnablePMC) { - if (Config.PMC.EnableConvert) { - PMC.convertIntoPmcChance.default.assault.min = Config.PMC.AItoPMC.ScavToPMC; - PMC.convertIntoPmcChance.default.cursedassault.min = Config.PMC.AItoPMC.CursedToPMC; - PMC.convertIntoPmcChance.default.pmcbot.min = Config.PMC.AItoPMC.RaiderToPMC; - PMC.convertIntoPmcChance.default.exusec.min = Config.PMC.AItoPMC.RogueToPMC; - PMC.convertIntoPmcChance.default.marksman = {}; - PMC.convertIntoPmcChance.default.marksman.min = Config.PMC.AItoPMC.SnipertoPMC; - - PMC.convertIntoPmcChance.default.assault.max = Config.PMC.AItoPMC.ScavToPMC; - PMC.convertIntoPmcChance.default.cursedassault.max = Config.PMC.AItoPMC.CursedToPMC; - PMC.convertIntoPmcChance.default.pmcbot.max = Config.PMC.AItoPMC.RaiderToPMC; - PMC.convertIntoPmcChance.default.exusec.max = Config.PMC.AItoPMC.RogueToPMC; - PMC.convertIntoPmcChance.default.marksman.max = Config.PMC.AItoPMC.SniperToPMC; - - PMC.convertIntoPmcChance.factory4_day.assault.min = Config.PMC.AItoPMC.ScavToPMCFactory; - PMC.convertIntoPmcChance.factory4_day.assault.max = Config.PMC.AItoPMC.ScavToPMCFactory; - PMC.isUsec = Config.PMC.PMCRatio; - for (let i in locations) { - if (i !== "base" && locations[i].base.BossLocationSpawn !== undefined) {//I Really think this is overkill, but oh well. - for (let ai in locations[i].base.BossLocationSpawn) { - if (locations[i].base.BossLocationSpawn[ai].BossName == "pmcBEAR" || locations[i].base.BossLocationSpawn[ai].BossName == "pmcUSEC") { - let randnum = Math.floor(Math.random() * 100) + 1 - if (randnum > Config.PMC.PMCRatio) { - locations[i].base.BossLocationSpawn[ai].BossName = "pmcBEAR"; - } - else { - locations[i].base.BossLocationSpawn[ai].BossName = "pmcUSEC"; - } + PMC.isUsec = Config.PMC.PMCRatio; + for (let i in locations) { + if (i !== "base" && locations[i].base.BossLocationSpawn !== undefined) {//I Really think this is overkill, but oh well. + for (let ai in locations[i].base.BossLocationSpawn) { + if (locations[i].base.BossLocationSpawn[ai].BossName == "pmcBEAR" || locations[i].base.BossLocationSpawn[ai].BossName == "pmcUSEC") { + let randnum = Math.floor(Math.random() * 100) + 1 + if (randnum > Config.PMC.PMCRatio) { + locations[i].base.BossLocationSpawn[ai].BossName = "pmcBEAR"; + } + else { + locations[i].base.BossLocationSpawn[ai].BossName = "pmcUSEC"; } } } @@ -2198,6 +2117,17 @@ class MainSVM { const Daily = Config.Quests.DailyQuests; const Weekly = Config.Quests.WeeklyQuests; const ScavDaily = Config.Quests.ScavQuests; + //Logger.info(DB.templates.repeatableQuests.templates) + if (Config.Quests.EnableQuestsMisc) { + for (let types in DB.templates.repeatableQuests.templates) { + DB.templates.repeatableQuests.templates[types].changeCost[0].count *= Config.Quests.QuestCostMult + } + if (Config.Quests.QuestRepToZero) { + for (let types in Quest.repeatableQuests) { + Quest.repeatableQuests[types].standingChangeCost = [0]; + } + } + } //Requirements QuestDetails(Daily, "0") QuestDetails(Weekly, "1") @@ -2388,9 +2318,9 @@ class MainSVM { case 2: Quest.repeatableQuests[2].types.push(Arrays.Types[0], Arrays.Types[1]) } } - Quest.repeatableQuests[Digit].numQuests = Type.QuestAmount; - + Quest.repeatableQuests[Digit].freeChanges = Type.Reroll + Quest.repeatableQuests[Digit].freeChangesAvailable = Type.Reroll Quest.repeatableQuests[Digit].minPlayerLevel = Type.Access Quest.repeatableQuests[Digit].rewardScaling.rewardSpread = Type.Spread; Quest.repeatableQuests[Digit].questConfig.Exploration.maxExtracts = Type.Extracts; @@ -2533,6 +2463,12 @@ class MainSVM { return JSON.parse(object) } } + function AdjustDurab(Type, WepMin, WepMax, ArmMin, ArmMax) { + Type.weapon.lowestMax = WepMin; + Type.weapon.highestMax = WepMax; + Type.armor.maxDelta = 100 - ArmMax; + Type.armor.minDelta = 100 - ArmMin; + } function SymbolCheck(Value) { return (Value == "*" || Value == "/" || Value == "+" || Value == "-"); } diff --git a/mods/Server Value Modifier/meta.ini b/mods/Server Value Modifier/meta.ini index 7967a66..508e990 100644 --- a/mods/Server Value Modifier/meta.ini +++ b/mods/Server Value Modifier/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.12.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=SVM.Server.Value.Modifier1.10.1.zip +installationFile=SVM.Server.Value.Modifier1.11.1.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Station Refueling/BepInEx/plugins/StationRefueling.dll b/mods/Station Refueling/BepInEx/plugins/StationRefueling.dll deleted file mode 100644 index 9e17809..0000000 Binary files a/mods/Station Refueling/BepInEx/plugins/StationRefueling.dll and /dev/null differ diff --git a/mods/Station Refueling/BepInEx/plugins/StationRefuelling.dll b/mods/Station Refueling/BepInEx/plugins/StationRefuelling.dll new file mode 100644 index 0000000..822fd5f Binary files /dev/null and b/mods/Station Refueling/BepInEx/plugins/StationRefuelling.dll differ diff --git a/mods/Station Refueling/meta.ini b/mods/Station Refueling/meta.ini index 9b3c23c..7902564 100644 --- a/mods/Station Refueling/meta.ini +++ b/mods/Station Refueling/meta.ini @@ -1,12 +1,12 @@ [General] gameName=spt modid=0 -version=d2025.1.5.0 +version=d2025.5.13.0 newestVersion= category="1," nexusFileStatus=1 installationFile=Station Refueling.zip -repository=Nexus +repository= ignoredVersion= comments= notes= diff --git a/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.Net.dll b/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.Net.dll index 7120ed9..8393634 100644 Binary files a/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.Net.dll and b/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.Net.dll differ diff --git a/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.dll b/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.dll index 9df9eaf..14105a8 100644 Binary files a/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.dll and b/mods/UI Fixes/BepInEx/plugins/Tyfon.UIFixes.dll differ diff --git a/mods/UI Fixes/meta.ini b/mods/UI Fixes/meta.ini index 8bfccd4..9aa9e9a 100644 --- a/mods/UI Fixes/meta.ini +++ b/mods/UI Fixes/meta.ini @@ -1,12 +1,12 @@ [General] gameName=spt modid=0 -version=d2025.1.8.0 +version=d2025.5.13.0 newestVersion= category="1," nexusFileStatus=1 -installationFile=Tyfon-UIFixes-3.1.1.zip -repository= +installationFile=Tyfon-UIFixes-4.2.1.zip +repository=Nexus ignoredVersion= comments= notes= diff --git a/mods/UI Fixes/user/mods/tyfon-uifixes/package.json b/mods/UI Fixes/user/mods/tyfon-uifixes/package.json index a8e62ba..8e428f2 100644 --- a/mods/UI Fixes/user/mods/tyfon-uifixes/package.json +++ b/mods/UI Fixes/user/mods/tyfon-uifixes/package.json @@ -1,10 +1,10 @@ { "name": "uifixes", - "version": "3.1.1", + "version": "4.2.1", "main": "src/mod.js", "license": "MIT", "author": "Tyfon", - "sptVersion": "~3.10", + "sptVersion": "~3.11", "loadBefore": [], "loadAfter": [], "incompatibilities": [], @@ -16,7 +16,7 @@ "buildinfo": "node ./build.mjs --verbose" }, "devDependencies": { - "@types/node": "20.11", + "@types/node": "22.12.0", "@typescript-eslint/eslint-plugin": "7.2", "@typescript-eslint/parser": "7.2", "archiver": "^6.0", @@ -25,9 +25,8 @@ "eslint-plugin-prettier": "^5.1.3", "fs-extra": "11.2", "ignore": "^5.2", - "os": "^0.1", "tsyringe": "4.8.0", - "typescript": "5.4", + "typescript": "5.8.2", "winston": "3.12" } } diff --git a/mods/UI Fixes/user/mods/tyfon-uifixes/src/assortUnlocks.ts b/mods/UI Fixes/user/mods/tyfon-uifixes/src/assortUnlocks.ts index 721144b..faa7a2d 100644 --- a/mods/UI Fixes/user/mods/tyfon-uifixes/src/assortUnlocks.ts +++ b/mods/UI Fixes/user/mods/tyfon-uifixes/src/assortUnlocks.ts @@ -27,8 +27,8 @@ export const assortUnlocks = (container: DependencyContainer) => { const questId = trader.questassort[questStatus][assortId]; if (!quests[questId]) { - logger.error( - `UIFixes: Trader ${traderId} questassort references unknown quest ${JSON.stringify(questId)}!` + logger.warning( + `UIFixes: Trader ${traderId} questassort references unknown quest ${JSON.stringify(questId)}! Check that whatever mod added that trader and/or quest is installed correctly.` ); continue; } diff --git a/mods/UI Fixes/user/mods/tyfon-uifixes/src/putToolsBack.ts b/mods/UI Fixes/user/mods/tyfon-uifixes/src/putToolsBack.ts index a89a51a..9bf504d 100644 --- a/mods/UI Fixes/user/mods/tyfon-uifixes/src/putToolsBack.ts +++ b/mods/UI Fixes/user/mods/tyfon-uifixes/src/putToolsBack.ts @@ -3,10 +3,14 @@ import type { DependencyContainer } from "tsyringe"; import type { HideoutHelper } from "@spt/helpers/HideoutHelper"; import type { InventoryHelper } from "@spt/helpers/InventoryHelper"; import type { ItemHelper } from "@spt/helpers/ItemHelper"; +import type { IPmcData } from "@spt/models/eft/common/IPmcData"; +import type { IItem } from "@spt/models/eft/common/tables/IItem"; import type { IHideoutSingleProductionStartRequestData } from "@spt/models/eft/hideout/IHideoutSingleProductionStartRequestData"; import type { ILogger } from "@spt/models/spt/utils/ILogger"; import type { ICloner } from "@spt/utils/cloners/ICloner"; +const returnToProperty = "uifixes.returnTo"; + export const putToolsBack = (container: DependencyContainer) => { const logger = container.resolve("PrimaryLogger"); const cloner = container.resolve("RecursiveCloner"); @@ -37,7 +41,7 @@ export const putToolsBack = (container: DependencyContainer) => { continue; } - tools[i]["uifixes.returnTo"] = [originalTool.parentId, originalTool.slotId]; + tools[i][returnToProperty] = [originalTool.parentId, originalTool.slotId]; } } } catch (error) { @@ -61,55 +65,44 @@ export const putToolsBack = (container: DependencyContainer) => { // If a tool marked with uifixes is there, try to return it to its original container const tool = itemWithModsToAddClone[0]; - if (tool["uifixes.returnTo"]) { + if (tool[returnToProperty]) { try { - const [containerId, slotId] = tool["uifixes.returnTo"]; + const [containerId, slotId] = tool[returnToProperty]; - const container = pmcData.Inventory.items.find(x => x._id === containerId); - if (container) { - const [foundTemplate, containerTemplate] = itemHelper.getItem(container._tpl); - if (foundTemplate && containerTemplate) { - const containerFS2D = inventoryHelper.getContainerMap( - containerTemplate._props.Grids[0]._props.cellsH, - containerTemplate._props.Grids[0]._props.cellsV, - pmcData.Inventory.items, - containerId - ); + // Clean the item + delete tool[returnToProperty]; - // will change the array so clone it - if ( - inventoryHelper.canPlaceItemInContainer( - cloner.clone(containerFS2D), - itemWithModsToAddClone - ) - ) { - // At this point everything should succeed - inventoryHelper.placeItemInContainer( - containerFS2D, - itemWithModsToAddClone, - containerId, - slotId - ); + const [foundContainerFS2D, foundSlotId] = findGridFS2DForItems( + inventoryHelper, + containerId, + slotId, + itemWithModsToAddClone, + pmcData + ); - // protected function, bypass typescript - inventoryHelper["setFindInRaidStatusForItem"]( - itemWithModsToAddClone, - request.foundInRaid - ); + if (foundContainerFS2D) { + // At this point everything should succeed + inventoryHelper.placeItemInContainer( + foundContainerFS2D, + itemWithModsToAddClone, + containerId, + foundSlotId + ); - // Add item + mods to output and profile inventory - output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone); - pmcData.Inventory.items.push(...itemWithModsToAddClone); + // protected function, bypass typescript + inventoryHelper["setFindInRaidStatusForItem"](itemWithModsToAddClone, request.foundInRaid); - logger.debug( - `Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${ - itemWithModsToAddClone[0]._tpl - } with: ${itemWithModsToAddClone.length - 1} mods to ${containerId}` - ); + // Add item + mods to output and profile inventory + output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone); + pmcData.Inventory.items.push(...itemWithModsToAddClone); - return; - } - } + logger.debug( + `Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${ + itemWithModsToAddClone[0]._tpl + } with: ${itemWithModsToAddClone.length - 1} mods to ${containerId}` + ); + + return; } } catch (error) { logger.error(`UIFixes: Encounted an error trying to put tool back.\n ${error}`); @@ -123,4 +116,51 @@ export const putToolsBack = (container: DependencyContainer) => { }, { frequency: "Always" } ); + + function findGridFS2DForItems( + inventoryHelper: InventoryHelper, + containerId: string, + startingGrid: string, + items: IItem[], + pmcData: IPmcData + ): [number[][], string] | undefined { + const container = pmcData.Inventory.items.find(x => x._id === containerId); + if (!container) { + return; + } + + const [foundTemplate, containerTemplate] = itemHelper.getItem(container._tpl); + if (!foundTemplate || !containerTemplate) { + return; + } + + let originalGridIndex = containerTemplate._props.Grids.findIndex(g => g._name === startingGrid); + if (originalGridIndex < 0) { + originalGridIndex = 0; + } + + // Loop through grids, starting from the original grid + for ( + let gridIndex = originalGridIndex; + gridIndex < containerTemplate._props.Grids.length + originalGridIndex; + gridIndex++ + ) { + const grid = containerTemplate._props.Grids[gridIndex % containerTemplate._props.Grids.length]; + const gridItems = pmcData.Inventory.items.filter( + x => x.parentId === containerId && x.slotId === grid._name + ); + + const containerFS2D = inventoryHelper.getContainerMap( + grid._props.cellsH, + grid._props.cellsV, + gridItems, + containerId + ); + + // will change the array so clone it + if (inventoryHelper.canPlaceItemInContainer(cloner.clone(containerFS2D), items)) { + return [containerFS2D, grid._name]; + } + } + } }; diff --git a/mods/Version 1.38.0_separator/meta.ini b/mods/Version 2.0.0_separator/meta.ini similarity index 100% rename from mods/Version 1.38.0_separator/meta.ini rename to mods/Version 2.0.0_separator/meta.ini diff --git a/mods/Virtual's Custom Quest Loader/BepInEx/plugins/VCQLQuestZones.dll b/mods/Virtual's Custom Quest Loader/BepInEx/plugins/VCQLQuestZones.dll index 9b39915..990fa15 100644 Binary files a/mods/Virtual's Custom Quest Loader/BepInEx/plugins/VCQLQuestZones.dll and b/mods/Virtual's Custom Quest Loader/BepInEx/plugins/VCQLQuestZones.dll differ diff --git a/mods/Virtual's Custom Quest Loader/meta.ini b/mods/Virtual's Custom Quest Loader/meta.ini index a0c1fc7..f4332a0 100644 --- a/mods/Virtual's Custom Quest Loader/meta.ini +++ b/mods/Virtual's Custom Quest Loader/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2024.12.16.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=VCQL-2.0.3.zip +installationFile=VCQL-2.0.4.zip repository=Nexus ignoredVersion= comments= diff --git a/mods/Virtual's Custom Quest Loader/user/mods/Virtual's Custom Quest Loader/package.json b/mods/Virtual's Custom Quest Loader/user/mods/Virtual's Custom Quest Loader/package.json index a7da848..dae619f 100644 --- a/mods/Virtual's Custom Quest Loader/user/mods/Virtual's Custom Quest Loader/package.json +++ b/mods/Virtual's Custom Quest Loader/user/mods/Virtual's Custom Quest Loader/package.json @@ -1,10 +1,10 @@ { "name": "Virtuals Custom Quest Loader", - "version":"2.0.3", + "version":"2.0.4", "main": "src/mod.js", "license": "MIT", "author": "Virtual", - "sptVersion": "3.10.x", + "sptVersion": "3.11.x", "scripts": { "setup": "npm i", "build": "node ./packageBuild.ts" diff --git a/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll b/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll index f9befc3..71ba10e 100644 Binary files a/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll and b/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll differ diff --git a/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/bigmap-navmesh.bundle b/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/bigmap-navmesh.bundle index bb48c3d..8ed9200 100644 --- a/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/bigmap-navmesh.bundle +++ b/mods/Waypoints/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/bigmap-navmesh.bundle @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d2700c08e6b056eca8f9c05670883cba84235b2babb21025b60fd87b784b2d8 -size 3263943 +oid sha256:897270a49f6c840fd76ae391394a13959dc7fdbf327192d1d38fa114a8eb63c6 +size 3419396 diff --git a/mods/Waypoints/meta.ini b/mods/Waypoints/meta.ini index 26a5f4e..77094ae 100644 --- a/mods/Waypoints/meta.ini +++ b/mods/Waypoints/meta.ini @@ -1,11 +1,11 @@ [General] gameName=spt modid=0 -version=d2025.1.20.0 +version=d2025.5.13.0 newestVersion= category="1,2" nexusFileStatus=1 -installationFile=DrakiaXYZ-Waypoints-1.6.2.7z +installationFile=DrakiaXYZ-Waypoints-1.7.0.7z repository=Nexus ignoredVersion= comments= diff --git a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll b/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll deleted file mode 100644 index 8de342b..0000000 Binary files a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/DrakiaXYZ-Waypoints.dll and /dev/null differ diff --git a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/LICENSE b/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/LICENSE deleted file mode 100644 index d360fc8..0000000 --- a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 DrakiaXYZ - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/interchange-navmesh.bundle b/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/interchange-navmesh.bundle deleted file mode 100644 index 625d394..0000000 --- a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/interchange-navmesh.bundle +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0669cdf83741c249e66294d73503a2425b1abb366f5cb23bce38a3d64e14a0fc -size 4217627 diff --git a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/lighthouse-navmesh.bundle b/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/lighthouse-navmesh.bundle deleted file mode 100644 index c8190fb..0000000 --- a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/lighthouse-navmesh.bundle +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2bce0bdfb7e832c6f7a99707a4b09fa8073b00ef2a6483f789f317717ef84132 -size 11215514 diff --git a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/shoreline-navmesh.bundle b/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/shoreline-navmesh.bundle deleted file mode 100644 index d4330f0..0000000 --- a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/shoreline-navmesh.bundle +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d63eb0825f7f07a8b0ae01567fbf30b9e0a2f8684a514e88249485b18732ae6f -size 11900025 diff --git a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/woods-navmesh.bundle b/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/woods-navmesh.bundle deleted file mode 100644 index 9d3e6fd..0000000 --- a/mods/Waypoints_backup2/BepInEx/plugins/DrakiaXYZ-Waypoints/navmesh/woods-navmesh.bundle +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b3ed5401da31f1974b0ad42f6d8c7de064c42e162e352bfc23d8f0c0fae4d5fb -size 10068985 diff --git a/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.dll b/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.dll index a68b30f..cb9da7d 100644 Binary files a/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.dll and b/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.dll differ diff --git a/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.pdb b/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.pdb new file mode 100644 index 0000000..10f277f Binary files /dev/null and b/mods/Weapon Customizer/BepInEx/plugins/Tyfon.WeaponCustomizer.pdb differ diff --git a/mods/Weapon Customizer/meta.ini b/mods/Weapon Customizer/meta.ini index cb8a874..3c9864f 100644 --- a/mods/Weapon Customizer/meta.ini +++ b/mods/Weapon Customizer/meta.ini @@ -1,12 +1,12 @@ [General] gameName=spt modid=0 -version=d2025.1.7.0 +version=d2025.5.13.0 newestVersion= category="2," nexusFileStatus=1 -installationFile=Tyfon-WeaponCustomizer-1.0.1.zip -repository=Nexus +installationFile=Tyfon-WeaponCustomizer-2.0.1.zip +repository= ignoredVersion= comments= notes= diff --git a/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/package.json b/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/package.json index 29ddf6d..74315db 100644 --- a/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/package.json +++ b/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/package.json @@ -1,10 +1,10 @@ { "name": "weapon-customizer", - "version": "1.0.1", + "version": "2.0.1", "main": "src/mod.js", "license": "MIT", "author": "Tyfon", - "sptVersion": "~3.10", + "sptVersion": "~3.11", "loadBefore": [], "loadAfter": [], "incompatibilities": [], @@ -25,7 +25,6 @@ "eslint-plugin-prettier": "^5.1.3", "fs-extra": "11.2", "ignore": "^5.2", - "os": "^0.1", "tsyringe": "4.8.0", "typescript": "5.4", "winston": "3.12" diff --git a/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/src/mod.ts b/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/src/mod.ts index f6269cf..a16fa24 100644 --- a/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/src/mod.ts +++ b/mods/Weapon Customizer/user/mods/tyfon-weaponcustomizer/src/mod.ts @@ -1,13 +1,16 @@ import type { DependencyContainer } from "tsyringe"; +import type { ItemHelper } from "@spt/helpers/ItemHelper"; import type { ProfileHelper } from "@spt/helpers/ProfileHelper"; +import type { IItem } from "@spt/models/eft/common/tables/IItem"; import type { IPostSptLoadMod } from "@spt/models/external/IPostSptLoadMod"; import type { IPreSptLoadMod } from "@spt/models/external/IPreSptLoadMod"; import { LogTextColor } from "@spt/models/spt/logging/LogTextColor"; import type { ILogger } from "@spt/models/spt/utils/ILogger"; import type { StaticRouterModService } from "@spt/services/mod/staticRouter/StaticRouterModService"; -import { VFS } from "@spt/utils/VFS"; -import fs from "node:fs"; +import type { ICloner } from "@spt/utils/cloners/ICloner"; +import type { FileSystem } from "@spt/utils/FileSystem"; + import path from "node:path"; type Vector3 = { @@ -16,30 +19,50 @@ type Vector3 = { z: number; }; -type CustomPosition = { - original: Vector3; - modified: Vector3; +type Quaternion = { + w: number; + x: number; + y: number; + z: number; }; -type CustomizePayload = { - weaponId: string; - slots: Record; +type Customization = { + position?: Vector3; + rotation?: Quaternion; }; -type Customizations = Record>; +type CustomizedObject = { + id: string; + type: "weapon" | "preset" | "unknown"; + name?: string; + slots: Record; +}; + +type Customizations = Record; + +type FileFormat = { + description: string; + version: number; + customizations: Customizations; +}; + +type V1FileFormat = Record>; + +const currentSaveFormatVersion = 2; class WeaponCustomizer implements IPreSptLoadMod, IPostSptLoadMod { private logger: ILogger; - private vfs: VFS; + private fileSystem: FileSystem; private profileHelper: ProfileHelper; private customizations: Customizations = null; private filepath: string; public preSptLoad(container: DependencyContainer): void { this.logger = container.resolve("PrimaryLogger"); - this.vfs = container.resolve("VFS"); + this.fileSystem = container.resolve("FileSystem"); const staticRouterModService = container.resolve("StaticRouterModService"); + const cloner = container.resolve("RecursiveCloner"); this.filepath = path.resolve(__dirname, "../customizations.json"); this.load(); @@ -49,7 +72,7 @@ class WeaponCustomizer implements IPreSptLoadMod, IPostSptLoadMod { [ { url: "/weaponcustomizer/save", - action: async (url, info: CustomizePayload, sessionId, output) => this.saveCustomization(info) + action: async (url, info: CustomizedObject[], sessionId, output) => this.saveCustomization(info) }, { url: "/weaponcustomizer/load", @@ -58,42 +81,96 @@ class WeaponCustomizer implements IPreSptLoadMod, IPostSptLoadMod { ], "custom-static-weapon-customizer" ); + + // listen to ItemHelper.replaceIDs to keep in sync + container.afterResolution( + "ItemHelper", + (_, itemHelper: ItemHelper) => { + const originalReplaceIDs = itemHelper.replaceIDs; + itemHelper.replaceIDs = (originalItems, pmcData, insuredItems, fastPanel) => { + const results: IItem[] = originalReplaceIDs.call( + itemHelper, + originalItems, + pmcData, + insuredItems, + fastPanel + ); + + let dirty = false; + for (let i = 0; i < originalItems.length; i++) { + const oldId = originalItems[i]._id; + if (oldId in this.customizations) { + const newId = results[i]._id; + this.customizations[newId] = cloner.clone(this.customizations[oldId]); + + dirty = true; + this.logger.logWithColor( + `WeaponCustomizer: Weapon ${oldId} is now ${newId}, customizations copied`, + LogTextColor.CYAN + ); + } + } + + if (dirty) { + this.save(); + } + + return results; + }; + }, + { frequency: "Always" } + ); } public postSptLoad(container: DependencyContainer): void { this.profileHelper = container.resolve("ProfileHelper"); this.clean(); + + const count = Object.keys(this.customizations).length; + if (count > 0) { + this.logger.logWithColor(`WeaponCustomizer: ${count} weapon customizations loaded`, LogTextColor.CYAN); + } } - private async saveCustomization(payload: CustomizePayload): Promise { - //this.logger.info(`WeaponCustomizer: Saving customization for weapon ${payload.weaponId}`); - if (Object.keys(payload.slots).length === 0) { - delete this.customizations[payload.weaponId]; - } else { - this.customizations[payload.weaponId] = payload.slots; + private async saveCustomization(payload: CustomizedObject[]): Promise { + for (const customizedObject of payload) { + if (!customizedObject || !customizedObject.id) { + this.logger.error("WeaponCustomizer: Bad save payload!"); + return; + } + if (Object.keys(customizedObject.slots).length === 0) { + delete this.customizations[customizedObject.id]; + } else { + this.customizations[customizedObject.id] = customizedObject; + } } + await this.save(); return JSON.stringify({ success: true }); } - private load() { + private async load() { try { - if (this.vfs.exists(this.filepath)) { - this.customizations = JSON.parse(this.vfs.readFile(this.filepath)); + if (await this.fileSystem.exists(this.filepath)) { + const file = await this.fileSystem.readJson(this.filepath); + switch (file.version) { + case undefined: + this.customizations = this.convertV1ToCurrent(file); + this.save(); + break; + case currentSaveFormatVersion: + this.customizations = file.customizations; + break; + default: + throw "Unknown file version!"; + } } else { this.customizations = {}; - - // Create the file with fs - vfs.writeFile pukes on windows paths if it needs to create the file - fs.writeFileSync(this.filepath, JSON.stringify(this.customizations)); - } - - const count = Object.keys(this.customizations).length; - if (count > 0) { - this.logger.logWithColor(`WeaponCustomizer: ${count} weapon customizations loaded.`, LogTextColor.CYAN); + this.save(); } } catch (error) { - this.logger.error("WeaponCustomizer: Failed to load weapon customization! " + error); + this.logger.error("WeaponCustomizer: Failed to load weapon customizations! " + error); this.customizations = {}; } } @@ -106,38 +183,74 @@ class WeaponCustomizer implements IPreSptLoadMod, IPostSptLoadMod { } for (const profile of Object.values(this.profileHelper.getProfiles())) { - for (const item of profile.characters.pmc.Inventory.items) { + const items = profile.characters?.pmc?.Inventory?.items ?? []; + for (const item of items) { if (map.has(item._id)) { map.set(item._id, true); } } + + const presets = profile.userbuilds?.weaponBuilds ?? []; + for (const preset of presets) { + if (map.has(preset.Id)) { + map.set(preset.Id, true); + } + } + + for (const insurance of profile.insurance ?? []) { + for (const item of insurance.items ?? []) { + if (map.has(item._id)) { + map.set(item._id, true); + } + } + } } let dirtyCount = 0; - for (const [weaponId, found] of Object.entries(map)) { + for (const [id, found] of Object.entries(map)) { if (!found) { - delete this.customizations[weaponId]; + delete this.customizations[id]; dirtyCount++; } } if (dirtyCount > 0) { - this.logger.logWithColor( - `WeaponCustomizer: Cleaned up ${dirtyCount} customizations for weapons that no longer exist`, - LogTextColor.CYAN - ); + // this.logger.logWithColor( + // `WeaponCustomizer: Cleaned up ${dirtyCount} customizations for weapons/presets that no longer exist`, + // LogTextColor.CYAN + // ); await this.save(); } } private async save() { + const file: FileFormat = { + description: + "This is a record of all customizations that WeaponCustomizer has made. You can delete this file and restart your server to reset all customizations. Modify this file at your own risk.", + version: currentSaveFormatVersion, + customizations: this.customizations + }; + try { - await this.vfs.writeFileAsync(this.filepath, JSON.stringify(this.customizations, null, 2)); + await this.fileSystem.writeJson(this.filepath, file, 2); } catch (error) { - this.logger.error("WeaponCustomizer: Failed to save weapon customization! " + error); + this.logger.error("WeaponCustomizer: Failed to save weapon customizations! " + error); } } + + private convertV1ToCurrent(file: V1FileFormat): Customizations { + const result: Customizations = {}; + for (const [key, value] of Object.entries(file)) { + result[key] = { + id: key, + type: "unknown", + slots: value + }; + } + + return result; + } } export const mod = new WeaponCustomizer(); diff --git a/profiles/Multiplayer/modlist.txt b/profiles/Multiplayer/modlist.txt index 66fa1e4..70cabcd 100644 --- a/profiles/Multiplayer/modlist.txt +++ b/profiles/Multiplayer/modlist.txt @@ -1,6 +1,6 @@ # This file was automatically generated by Mod Organizer. -Unsorted_separator --Version 1.38.0_separator +-Version 2.0.0_separator -Performance Improvements -Third Person -WTT - Menu Overhaul @@ -9,15 +9,17 @@ -Backburner_separator +Config Files -Modlist Addons_separator -+Amands Graphics -+Bright Lasers --VFX & Atmosphere_separator -Tarky Menu -Bot Debug -Tools & Debugging_separator ++Amands Graphics ++Bright Lasers ++HollywoodFX +-VFX & Atmosphere_separator +Visceral Combat +Nerf Bot Grenades -+Progressive Bot System ++Acids Bot Placement System ++Acids Progressive Bot System +Dynamic Goons +MOAR - Ultra Lite Spawn Mod +Questing Bots @@ -43,6 +45,8 @@ -New Weapons & Gear_separator +Make Val & VSS Great Again -Weapon Tweaks & Balancing_separator ++Hideout Cat +-Hideout Changes_separator +Painter +Ref SPT Friendly Quests -New Traders & Tweaks_separator @@ -62,6 +66,7 @@ +Increased Look Direction +Shibs Expanded Crafting +Simple Workout QTE ++Gekos Better Progression -Gameplay Tweaks_separator +Bosses Have Lega Medals +Skeleton Key diff --git a/profiles/Multiplayer/modlist.txt.2025_05_13_05_29_24 b/profiles/Multiplayer/modlist.txt.2025_05_13_05_29_24 new file mode 100644 index 0000000..60e808e --- /dev/null +++ b/profiles/Multiplayer/modlist.txt.2025_05_13_05_29_24 @@ -0,0 +1,124 @@ +-Unsorted_separator +-Version 2.0.0_separator +-Performance Improvements +-Third Person +-WTT - Menu Overhaul +-Custom Asset Importer +-SWAG + DONUTS +-Backburner_separator ++Config Files +-Modlist Addons_separator +-Tarky Menu +-Bot Debug +-Tools & Debugging_separator ++Amands Graphics ++Bright Lasers ++HollywoodFX +-VFX & Atmosphere_separator ++Visceral Combat ++Nerf Bot Grenades ++Acids Bot Placement System ++Acids Progressive Bot System ++Dynamic Goons ++MOAR - Ultra Lite Spawn Mod ++Questing Bots ++Looting Bots ++That's Lit - Sync ++That's Lit ++SAIN +-AI & Combat Tweaks_separator ++Little Drummer Boy ++AUG Attachment Pack ++RPG-7 ++SVD ++M249 ++AK-5C ++HK-417 ++Armsel Striker-12 ++Kel-Tec KSG-12 ++China Lake ++Peacemaker ++Black Core ++Additional Clothing BEAR ++Additional Clothing USEC +-New Weapons & Gear_separator ++Make Val & VSS Great Again +-Weapon Tweaks & Balancing_separator ++Hideout Cat +-Hideout Changes_separator ++Painter ++Ref SPT Friendly Quests +-New Traders & Tweaks_separator ++Weapon Customizer ++Plant Time Modifier ++Scope and Red Dot Sight Overhaul ++Unlock Arena Lockboxes ++Station Refueling ++Quick Throw Grenades ++Shoulder Swap on Lean ++Merge Consumables ++Increase Climb Height ++Flare Event Notifier ++Auto-Sprint Mod ++Healing Auto Cancel ++Continuous Healing ++Increased Look Direction ++Shibs Expanded Crafting ++Simple Workout QTE ++Gekos Better Progression +-Gameplay Tweaks_separator ++Bosses Have Lega Medals ++Skeleton Key +-New Items & Keys_separator ++Vocal Player ++Better Bullet Cracks +-SFX & Music_separator ++Item Context Menu Extended ++Open The Damn Context Menu ++Dynamic Maps ++Quicksell ++Auto Deposit ++Hide Top Glow ++Use Loose Loot ++Better Keys NG ++More Tag Colors ++Magazine Inspector ++Player Encumbrance Bar +-More Checkmarks - Server ++More Checkmarks ++Item Sell Price ++Trader Scrolling ++Equip From Weapon Rack ++Quest Tracker ++Quick Move to Container ++Stash Search ++Borkel's Realistic Night Vision Goggles ++Raid Review ++Game Panel HUD +-Interface & HUD_separator ++Unflashbang Hideout ++Stop Hotkey Auto Vault ++Weather Fixes ++Shadow Flicker Fix ++Fontaines FOV Fix ++Hands Are Not Busy ++Default Fire Mode Fix ++Expanded Task Text ++UI Fixes ++Ram Cleaner Interval +-Bug Fixes & Optimizations_separator ++Custom Interactions ++Interactable Exfils API ++Virtual's Custom Quest Loader ++Mod Sync ++Waypoints ++Big Brain ++Server Value Modifier ++Load Order Editor ++EFT API ++Unity Toolkit +-Essentials_separator +-Fika Dedicated ++Fika Server ++Fika Core +-Fika_separator diff --git a/profiles/Server/modlist.txt b/profiles/Server/modlist.txt index f71becf..d9ca1ff 100644 --- a/profiles/Server/modlist.txt +++ b/profiles/Server/modlist.txt @@ -1,5 +1,5 @@ +Unsorted_separator --Version 1.38.0_separator +-Version 2.0.0_separator -Performance Improvements -Third Person -WTT - Menu Overhaul @@ -8,15 +8,17 @@ +Backburner_separator +Config Files +Modlist Addons_separator --Amands Graphics -+Bright Lasers -+VFX & Atmosphere_separator -Tarky Menu -Bot Debug +Tools & Debugging_separator +-Amands Graphics ++Bright Lasers +-HollywoodFX ++VFX & Atmosphere_separator +Visceral Combat +Nerf Bot Grenades -+Progressive Bot System +-Acids Bot Placement System ++Acids Progressive Bot System +Dynamic Goons +MOAR - Ultra Lite Spawn Mod +Questing Bots @@ -42,6 +44,8 @@ +New Weapons & Gear_separator +Make Val & VSS Great Again -Weapon Tweaks & Balancing_separator +-Hideout Cat +-Hideout Changes_separator +Painter +Ref SPT Friendly Quests +New Traders & Tweaks_separator @@ -61,6 +65,7 @@ -Increased Look Direction +Shibs Expanded Crafting +Simple Workout QTE +-Gekos Better Progression +Gameplay Tweaks_separator +Bosses Have Lega Medals +Skeleton Key