Various Mod Updates for SPT 3.11
This commit is contained in:
parent
306af72daf
commit
37519a6093
|
@ -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.
|
Binary file not shown.
|
@ -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
|
BIN
mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/ABPSConfig.exe (Stored with Git LFS)
Normal file
BIN
mods/Acids Bot Placement System/user/mods/acidphantasm-botplacementsystem/ABPSConfig.exe (Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -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.
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<string, IBossLocationSpawn[]> = {};
|
||||
public scavMapCache: Record<string, IWave[]> = {};
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<ILocationConfig>(ConfigTypes.LOCATION);
|
||||
this.pmcConfig = this.configServer.getConfig<IPmcConfig>(ConfigTypes.PMC);
|
||||
this.botConfig = this.configServer.getConfig<IBotConfig>(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 = {};
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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: []
|
||||
};
|
|
@ -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"
|
||||
]
|
|
@ -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": ""
|
||||
}
|
||||
]
|
|
@ -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
|
||||
}
|
|
@ -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<DifficultyConfig, number>,
|
||||
pmcConfig: PMCConfig,
|
||||
scavConfig: ScavConfig,
|
||||
bossDifficulty: Record<DifficultyConfig, number>,
|
||||
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
|
||||
}
|
|
@ -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>("PreSptModLoader");
|
||||
this.logger = container.resolve<ILogger>("WinstonLogger");
|
||||
this.fileSystemSync = container.resolve<FileSystemSync>("FileSystemSync");
|
||||
this.cloner = container.resolve<ICloner>("PrimaryCloner");
|
||||
this.staticRouterModService = container.resolve<StaticRouterModService>("StaticRouterModService");
|
||||
|
||||
// Custom Classes
|
||||
this.container.register<VanillaAdjustmentControl>("VanillaAdjustmentControl", VanillaAdjustmentControl, { lifecycle: Lifecycle.Singleton })
|
||||
this.container.register<BossSpawnControl>("BossSpawnControl", BossSpawnControl, { lifecycle: Lifecycle.Singleton })
|
||||
this.container.register<ScavSpawnControl>("ScavSpawnControl", ScavSpawnControl, { lifecycle: Lifecycle.Singleton })
|
||||
this.container.register<PMCSpawnControl>("PMCSpawnControl", PMCSpawnControl, { lifecycle: Lifecycle.Singleton })
|
||||
this.container.register<MapSpawnControl>("MapSpawnControl", MapSpawnControl, { lifecycle: Lifecycle.Singleton })
|
||||
|
||||
this.container.register<StaticRouterHooks>("StaticRouterHooks", StaticRouterHooks, { lifecycle: Lifecycle.Singleton })
|
||||
this.staticRouterHooks = container.resolve<StaticRouterHooks>("StaticRouterHooks");
|
||||
|
||||
// Custom Special
|
||||
|
||||
// Resolve this last to set mod configs
|
||||
this.container.register<ModConfig>("ModConfig", ModConfig, { lifecycle: Lifecycle.Singleton })
|
||||
this.modConfig = container.resolve<ModConfig>("ModConfig");
|
||||
}
|
||||
|
||||
public postDBLoad(container: DependencyContainer): void
|
||||
{
|
||||
// SPT Classes
|
||||
this.databaseService = container.resolve<DatabaseService>("DatabaseService");
|
||||
this.weightedRandomHelper = container.resolve<WeightedRandomHelper>("WeightedRandomHelper");
|
||||
this.vanillaAdjustmentControl = container.resolve<VanillaAdjustmentControl>("VanillaAdjustmentControl");
|
||||
this.bossSpawnControl = container.resolve<BossSpawnControl>("BossSpawnControl");
|
||||
this.scavSpawnControl = container.resolve<ScavSpawnControl>("ScavSpawnControl");
|
||||
this.pmcSpawnControl = container.resolve<PMCSpawnControl>("PMCSpawnControl");
|
||||
this.mapSpawnControl = container.resolve<MapSpawnControl>("MapSpawnControl");
|
||||
}
|
||||
|
||||
public postSptLoad(container: DependencyContainer): void
|
||||
{
|
||||
// SPT Classes
|
||||
this.databaseService = container.resolve<DatabaseService>("DatabaseService");
|
||||
}
|
||||
}
|
|
@ -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<string>
|
||||
{
|
||||
if (!payload)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.bossTrackingData = payload;
|
||||
}
|
||||
|
||||
await this.save();
|
||||
return JSON.stringify({ success: true });
|
||||
}
|
||||
|
||||
private async save(): Promise<void>
|
||||
{
|
||||
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<void>
|
||||
{
|
||||
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<string, Record<string, BossTrackingInfo>>;
|
|
@ -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<T>(itemsToAddToArray: T[], randomUtil: RandomUtil, cloner: ICloner): ExhaustableArray<T>
|
||||
{
|
||||
return new ExhaustableArray<T>(itemsToAddToArray, randomUtil, cloner);
|
||||
}
|
|
@ -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<ILogger>("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>("FileSystemSync");
|
||||
const configServer = container.resolve<ConfigServer>("ConfigServer");
|
||||
const sptConfig = configServer.getConfig<ICoreConfig>(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>("FileSystemSync");
|
||||
const packageJsonPath: string = path.join(__dirname, "../package.json");
|
||||
const modSptVersion = fileSysem.readJson(packageJsonPath).sptVersion;
|
||||
|
||||
return minVersion(modSptVersion)
|
||||
}
|
||||
}
|
||||
|
||||
export const mod = new ABPS();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue