diff --git a/.gitignore b/.gitignore
index 8d5862f..44526f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,36 +1,36 @@
-sm_*
-.*/
-scripting_ext/
-!scripting/include/guesswho/*
-!scripting/include/feedthetrolls
-!scripting/include/hideandseek
-!scripting/include/guessswho/*
-!scripting/hideandseek.sp
-!scripting/guessswho.sp
-!scripting/sm_namespamblock.sp
-!plugins/sm_namespamblock.smx
-.push.settings.jsonc*
-template.config.js
-scripting/sm_give.sp
-scripting/include/steamtools.inc
-scripting/L4D2Testing.sp
-plugins/L4D2Testing.smx
-plugins/sm_give.smx
-plugins/l4d2_stats_recorder.smx
-scripting/l4d2_stats_recorder.sp
-scripting/include/smlib.inc
-scripting/l4d2_custom.sp
-plugins/l4d2_custom.smx
-plugins/disable_cameras.smx
-plugins/l4d_esfp.smx
-plugins/customstatus.smx
-scripting/l4d_esfp.sp
-scripting/customstatus.sp
-plugins/ssh.smx
-scripting/include/ssh.inc
-scripting/include/stats
-scripting/ssh.sp
-scripting/l4d2_witch_force_attack_cmd.sp
-l4d2_stats_plugin/
-data
-!sql/*
+sm_*
+.*/
+scripting_ext/
+!scripting/include/guesswho/*
+!scripting/include/feedthetrolls
+!scripting/include/hideandseek
+!scripting/include/guessswho/*
+!scripting/hideandseek.sp
+!scripting/guessswho.sp
+!scripting/sm_namespamblock.sp
+!plugins/sm_namespamblock.smx
+.push.settings.jsonc*
+template.config.js
+scripting/sm_give.sp
+scripting/include/steamtools.inc
+scripting/L4D2Testing.sp
+plugins/L4D2Testing.smx
+plugins/sm_give.smx
+plugins/l4d2_stats_recorder.smx
+scripting/l4d2_stats_recorder.sp
+scripting/include/smlib.inc
+scripting/l4d2_custom.sp
+plugins/l4d2_custom.smx
+plugins/disable_cameras.smx
+plugins/l4d_esfp.smx
+plugins/customstatus.smx
+scripting/l4d_esfp.sp
+scripting/customstatus.sp
+plugins/ssh.smx
+scripting/include/ssh.inc
+scripting/include/stats
+scripting/ssh.sp
+scripting/l4d2_witch_force_attack_cmd.sp
+l4d2_stats_plugin/
+data
+!sql/*
diff --git a/LICENSE b/LICENSE
index f288702..3877ae0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,674 +1,674 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
index 1889543..5ec4f3d 100644
--- a/README.md
+++ b/README.md
@@ -1,466 +1,466 @@
-# Sourcemod L4D2 Plugins
-This is a collection of sourcemod plugins, most are used on my servers. The majority of the plugins are created by me, but some are modifications of other plugins.
-Some of the plugins / changes are very specific, but may be useful to someone.
-
-Not always the latest versions. If you have any interest with a plugin, I can make sure to upload the latest.
-
-Useful things:
-1. **Netprop Viewer** [L4D2 Netprops](https://jackz.me/netprops/l4d2) and [L4D2 Datamaps](https://jackz.me/netprops/l4d2-data)
-
-## Plugin List
-
-### Created by Me
- * [l4d2-manual-director](#l4d2-manual-director) - Spawn specials on demand via director or at your cursor
- * [l4d2-info-cmd](#l4d2-info-cmd) - Prints a full state of all survivors, useful for external information
- * [AutoWarpBot](#autowarpbot) - Abandoned
- * [L4D2FFKickProtection](#l4d2ffkickprotection) - Prevents players being voted off from friendly firing and prevents admins from being kicked
- * [l4d2_avoid_minigun](#l4d2_avoid_minigun) - Makes bots avoid being infront of any in-use miniguns. Useful for spawned miniguns
- * [l4d2_ai_minigun](#l4d2_ai_minigun) - Based off [Silver's Survivor Bot Holdout plugin](https://forums.alliedmods.net/showthread.php?p=1741099), allows you to spawn survivor bots but with no limit.
- * [L4D2Tools](#l4d2tools) - A collection of utilities, mostly just used with [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) and the /model command
- * [l4d2_swarm](#l4d2_swarm) - Uses vscript RushVictim to make all zombies target a player, like a more subtle vomitplayer
- * [l4d2_feedthetrolls](#l4d2_feedthetrolls) - Full collection of tools to troll your friends or troll the trolls
- * [l4d2_autobotcrown](#l4d2_autobotcrown) - Bots will auto crown
- * [l4d2_extraplayeritems](#l4d2_extraplayeritems) - Includes tons of utilities for 5+ games, such as 5+ player hud, extra kit / item spawning, and more
- * [l4d2_population_control](#l4d2_population_control) - Allows you to custom the type of zombies that spawn (% of clowns, mud men, etc..)
- * [globalbans](#globalbans) - Bans synced via mysql, way lighter than the sourcebans cesspool.
- * [l4d2_rollback](#l4d2_rollback) - Abandoned and broken, but makes periodic backup of all player's items
- * [l4d2_autorestart](#l4d2_autorestart) - Restarts server if it's been on for a certain uptime or when empty with just bots.
- * [l4d2_TKStopper](#l4d2_tkstopper) - All the teamkiller and shitty-aim player punishments. Auto increasing reverse ff and teamkill detection
- * [l4d2_crescendo_control](#l4d2_crescendo_control) - Prevents players from running far ahead and starting events, and logs button presses
- * [l4d2_vocalize_control](#l4d2_vocalize_control) - Allows you to locally mute someone from vocalizing
- * [l4d2_guesswho](#l4d2_guesswho) - Garry's Mod's Guess Who in l4d2, inspired by hide and seek
- * [l4d2_hideandseek](#l4d2_hideandseek) - An enhancement to the base hide and seek mutation
- * [l4d2_hats](#l4d2_hats) - Entity Hats & Entity editing
- * [l4d2_prophunt](#l4d2_prophunt) - Garry's Mod inspired prop hunt, inspired by hide and seek
- * [sm_namespamblock](#sm_namespamblock) - Basic plugin that bans players if they change their name in rapid succession
- * [l4d2-stats-plugin](https://github.com/jackzmc/l4d2-stats-plugin) - Custom stats recorder, see https://stats.jackz.me
- * [l4d2-ai-tweaks](#l4d2_ai_tweaks) - Very minor tweaks to survivor bots' behavior
- * [sm_player_notes](#sm_player_notes) - Add notes to players
-
-### Modified Others
-* [200IQBots_FlyYouFools](#200iqbots_flyyoufools) - Improved code to make it support multiple tanks and work better
-* [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) - Use with [L4D2Tools](#l4d2tools) to change models, some fixes
-* [BetterWitchAvoidance](#betterwitchavoidance)
-* l4d_anti_rush - Modified plugin to add a forward, so other plugins (like feedthetrolls) can do something. In addition, use highest flow value achieved for players (fixes issue when admins go back and players who haven't moved suddenly get punished)
-* [l4d2_sb_fix](#l4d2_sb_fix) - Updated to 1.11 & latest sourcepawn syntax & removed the FCVAR_NOTIFY from all cvars (why is that added?)
-* GrabEnt - Improved version that prevents moving certain entities (such as invisual walls, ragdolls, etc) and improved some code
-
-## Dependencies
-This is a list of most common dependencies, independent if they are used for a certain plugin.
-Check the plugin info for an exact list.
-
-* [Left 4 Dhooks Direct](https://forums.alliedmods.net/showthread.php?t=321696)
-* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
-
-### Development Dependencies
-Most L4D2 plugins use my own include: jutils.inc, it's provided in this repo.
-Some do require newer includes for my modified plugins (such as my improved survivor identity fix)
-
-## Descriptions
-
-### l4d2-manual-director
-~~Probably going to be posted publicly sometime.~~ Allows you to spawn specials on cursor, or via director, forcefully, bypassing limits
-* **Convars:**
- * `manual_director_version|mandirector_version` - ... gets version
- * `mandirector_notify_spawn <1/0>` - Should spawning specials notify on use?
- * `mandirector_announce_level <0/1/2/3>` - Announcement types. 0 - None, 1 - Only bosses, 2 - Only specials+, 3 - Everything
- * `mandirector_enable_tank <0/1>` - Should tanks be allowed to be spawned?
- * `mandirector_enable_witch <0/1>` - Should witches be allowed to be spawned?
- * `mandirector_enable_mob <0/1>` - Should mobs be allowed to be spawned
-* **Commands:**
- * `sm_spawnspecial [amount]` - Spawn a special via director
- * `sm_forcespecial [amount]` - Force spawn a special via director, bypassing spawn limits
- * `sm_forcecursor [amount]` - Force spawn a special at cursor, bypassing spawn limits
- * `sm_cursormenu` - Show the spawn menu for cursor spawning
- * `sm_specialmenu` - Show the spawn menu for director spawning
- * `sm_directormenu` (Same as sm_specialmenu for now)
-
-
-### l4d2-info-cmd
-Technically 'l4d2 game info', haven't changed name. Just prints general information, used for a project
-* **Commands:**
- * `sm_gameinfo`
-* Example Response:
- ```
- >map,diff,mode,tempoState,totalSeconds
- c1m1_hotel,1,coop,3,1622
- >id,name,bot,health,status,throwSlot,kitSlot,pillSlot,survivorType,velocity,primaryWpn,secondaryWpn
- 1,Jackz,0,80,alive,0,,first_aid_kit,,Bill,0,,pistol
- 3,Zoey,1,75,alive,0,,first_aid_kit,,Zoey,0,,pistol
- 4,Francis,1,76,alive,0,,,,Francis,0,,pistol
- 5,Louis,1,90,alive,0,,first_aid_kit,,Louis,0,,pistol
- ```
-
-
-### AutoWarpBot
-Simple l4d2 plugin that will auto teleport bots to checkpoint once all real players have reached the saferoom.
-Doesn't really work well. Abandoned.
-
-
-### 200IQBots_FlyYouFools
-Updated version of ConnerRia's plugin. Improves bots avoidance of tanks. Change from original is updated sourcepawn syntax, some optimizations/cleanup, and fixes such as bots avoiding tank that has not been activated, or not escaping in vehicle due to presence of tank.
-Latest version now has support for multiple tanks, the bots might not avoid them as effectively as they would with one tank but they still try their best.
-* **Convars:**
- * `FlyYouFools_Version` - Prints the version of plugin
-
-
-### BetterWitchAvoidance
-Inspired by the 200IQBots_FlyYouFools. Bots avoid witch if its over 40% anger when close, or a little bigger range at 60% or more. Not recommended to use, normal behavior seems fine.
-
-
-### L4D2FFKickProtection
-Simple plugin that prevents a player that is being vote-kicked from doing any ff damage to teammates.
-It also prevents vote kicking of admins, instead will notify admins.
-It also makes any vote kicks created by an admin to instantly be accepted by all players
-
-* **Convars:**
- * `sm_votekick_force_threshold <#>` - The threshold of damage where the offending player is just immediately kicked. 0 -> Any attempted damage, -1 -> No auto kick.
-
-### l4d2_avoid_minigun
-Makes the bots avoid standing in front of or on top of the player that is using a minigun. It checks every 2.0 seconds if they are in the way, then forces them to move to behind you. There is no configuration, all automatic.
-
-
-### l4d2_ai_minigun
-Requires: [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-
-Spawn the holdout bots used in the passing. This supports all 8 characters, including with the minigun. They can spawn with any weapon or default to ak47.
-
-**Notes:**
-* The minigun holdout bot has to internally be Louis, so it will be Louis making sounds, with whatever model specified being shown. This doesn't apply for normal holdout bot.
-* \ can be "bill" or their numeric id (4).
-
-Code modified from https://forums.alliedmods.net/showthread.php?p=1741099
-
-* **Commands:**
- * `sm_ai_minigun ` - Spawns an ai bot with minigun infront of wherever you are looking. Can also use numbers (0-7).
- * `sm_ai_holdout [wpn]` - Spawns a normal ai holdout bot (no minigun), with any weapon w/ laser sight (default is ak).
- * `sm_ai_remove_far` - Removes any holdout or minigun bots that are 750 units or more from any player.
-
-
-### L4D2Tools
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
-* [Modified L4D Survivor Identity Fix](#l4d_survivor_identity_fix)
-
-A collection of small tools:
- * Notification of when someone picks up laser sights (only the first user, includes bots),
- * Record time it takes for a finale or gauntlet run to be completed.
- * Record the amount of friendly fire damage done
- * Set the survivor models of any survivor with updated [l4d_survivor_identity_fix](#l4d_survivor_identity_fix)
- * Automatically returns melee weapons that an idle bot dropped once no longer idle
- * Automatically make players go idle when ping spikes
- * Slowly kill any zombies attacking survivor bot's blind spots (Fixes brain dead bots stuck taking damage and not killing them)
-
-* **Convars:**
- * `sm_laser_use_notice <0/1>` - Enable notification of when a laser box was used first
- * `sm_time_finale <0/1/2>` - Record the time it takes to complete finale. 0 -> OFF, 1 -> Gauntlets Only, 2 -> All finales
- * `sm_ff_notice <0/1/2>` - Should we record FF damages? 0: OFF, 1: To chat, 2: To HUD text.
- * `sm_autoidle_ping_max <30.0...>` - "The highest ping a player can have until they will automatically go idle.\n0=OFF, Min is 30
-* **Commands:**
- * `sm_model ` - Sets the survivor model of the target player(s). 'character' is name or ID of character.
- * `sm_surv ` - Sets the m_survivorCharacter prop only of the target player(s). 'character' is name or ID of character.
-
-
-### l4d2_swarm
-This plugin is used to counter trolls and otherwise bad players. It simply uses the new script function RushVictim() to make all zombies in X radius attack Y target. It's that simple.
-
-This really only affects wandering zombies, mobs and panic events, but it may work slightly when bile or pipes are thrown. It does not and can't change the targets of zombies.
-
-* **Convars:**
- * `sm_swarm_default_range <20-Infinity>` - The default range for commands & menus. Defaults to 7,500
-* **Commands:**
- * `sm_swarm [player] [range]` - Swarm a player, or random if none."
- * Aliases: `sm_rush`
- * `sm_rushmenu` - Opens a menu to quickly rush any player. Can be bound to a key to quickly rush as well
- * Aliases: `sm_rmenu`
- * `sm_swarmtoggle [range]` - Will continuously run the swarm method on the player at the range. Use the command again or type "disable" for player to disable. Switching players will not disable, just switches target.
- * Aliases: `sm_rushtoggle`, `sm_rt`
- * `sm_rushtogglemenu` - Will open a menu to quickly select a player to continuously rush.
- * Aliases: `sm_rtmenu`
-
-
-### l4d2_feedthetrolls
-Requires:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-* (Optional) [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
-* (Optional) [Actions](https://forums.alliedmods.net/showthread.php?t=336374)
-* (Optional) [Modified L4D Antirush](#l4d_anti_rush)
-
-This plugin allows you to enact certain troll modes on any player, some are subtle some are less so. Either way, it works great to deal with a rusher, an asshole or even your friends.
-
-See the up-to-date list of trolls and their descriptions:
-https://admin.jackz.me/docs/ftt
-
-* **Convars:**
- * `sm_ftt_autopunish_mode <#>` - (Not used, WIP) Sets the modes that auto punish will activate for. 1 -> Early crescendo activations
- * `sm_ftt_autopunish_action <#>` - Which actions will autopunish activate? Add bits together. 0=None, 1=TankMagnet 2=SpecialMagnet 4=Swarm 8=VomitPlayer
- * `sm_ftt_autopunish_expires <0...>` - How many minutes (in gameticks) until autopunish trolls are removed. 0 for never.
-* **Commands:**
- * `sm_fta [player]` - The main command, opens a menu to select a troll to apply, with modifiers and flags
- * `sm_ftr [player]` - Removes all active trolls from a player
- * `sm_ftc [player]` - Opens a menu to select a combo of trolls
- * `sm_ftl` - Lists all players that have a mode applied.
- * `sm_ftm` - Lists all troll options & their descriptions
- * `sm_mark` - Toggles marking a player to be banned when they fully disconnect
- * `sm_insta [player] [special]` - (No arguments opens menu) - Spawns a special via the director that will only target the victim
- * `sm_inface [player] [special]` - Identical to above, but special will be spawned as close as possible to survivor. Boomers auto explode, jockeys on their head, etc.
- * `sm_bots_attack [target health]` - Slightly broken, but makes all bots shoot at player until they hit X health or a timeout is reached. Turn on `sb_friendlyfire` for it to be effective.
- * `sm_stagger ` - Makes a player stagger, shortcut to the Stagger troll
- * `sm_witch_attack ` - Makes all witches agro on the player
- * `sm_scharge [timeout seconds]` - Will wait till there's no obstructions and players in the way, then spawns a charger behind them to charge them.
- * `sm_healbots [# bots or 0 default]` - Makes n amount of bots chase a player down to heal them. Won't stop until they are healed, they die, or you run command again.
- * `sm_csplat - Shortcut to Car Splat. Spawns car in top/front/back that hits the player`
-
-### l4d2_autobotcrown
-Makes any suitable bot (> 40 hp, has shotgun) automatically crown a witch. Supports multiple bots and witches, but only one bot can crown one witch at a time. Plugin is disabled in realism, and is really on suitable for coop or versus. Even works with idle players.
-
-Bots do sometimes miss, but sometimes still manage to kill witch. They also don't care if there is danger in the way (fire, acid, angry witch).
-
-* **Convars:**
- * `l4d2_autocrown_allowed_difficulty ` - The difficulties the plugin is active on. 1=Easy, 2=Normal 4=Advanced 8=Expert. Add numbers together.
- * `l4d2_autocrown_modes_tog ` - (Not implemented) - Turn on the plugin in these game modes. 0=All, 1=Coop, 2=Survival, 4=Versus, 8=Scavenge. Add numbers together
-
-
-### l4d2_extraplayeritems
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-* [L4D Info Editor](https://forums.alliedmods.net/showthread.php?p=2614626)
-* (Development dependency) Updated l4d2_weapon_stocks.inc
-
-A well rounded tool that provides extra utilities to a 5+ co-op campaign.
-
-Features:
-* Automatically giving extra kits for each extra player in saferooms
-* Increasing item count for items randomly depending on player count
-* Fix same-models survivors having to fight over ammo pack usage
-* Automatically lock the first saferoom door for every chapter, until a threshold of players or time has passed
-* Includes a HUD that shows all the survivors and their items, and optionally their ping (breaks randomly not sure why)
-* Includes a new 5+ special spawning director
-* Spawn an extra tank that scales with player count
-
-* **Convars:**
- * `epi_item_chance` - The base chance (multiplied by player count) of an extra item being spawned. Default: 0.056
- * `epi_kitmode` - Decides how extra kits should be added. Default is 0
- * 0 -> Overwrites previous extra kits
- * 1 -> Adds onto previous extra kits
- * `epi_updateminplayers` - Should the plugin update abm's cvar min_players convar to the player count? (0 no, 1 yes)
- * `epi_doorunlock_percent` - The percent of players that need to be loaded in before saferoom door is opened.
- * Default is 0.8, set to 0 to disable door locking
- * `epi_doorunlock_wait` - How many seconds after to unlock saferoom door. 0 to disable timer
- * `epi_doorunlock_open` - Controls when or if the door automatically opens after unlocked. Add bits together.
- * 0 = Never, 1 = When timer expires, 2 = When all players loaded in
- * `epi_hudstate` - Controls when the extra player hud shows.
- * 0 = Never, 1 = When 5+ players, 2 = Always on
- * `epi_sp_spawning` - Determines what specials are spawned. Add bits together.
- * 1 = Specials, 2 = Witches, 4 = Tanks
- * `epi_enabled` - When should epi be enabled?
- * 0 = OFF, 1 = Only when 5+ and official map, 2 = Only when 5+, 3 = Always
- * `epi_tank_chunkhp` - The amount of health to add to a tank per extra player
- * `epi_gamemodes` - Comma separated list of allowed gamemodes.
-
-
-### l4d_survivor_identity_fix
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-
-A fork of [Survivor Identity Fix plugin](https://forums.alliedmods.net/showthread.php?t=280539) that adds support for other plugins to update the model cache. This is used by [L4D2Tools](#L4D2Tools) to update the identity when someone changes their model with `sm_model`. It also will clear the memory of model when a player disconnects entirely or on a new map.
-
-In addition, has a fix for the passing finale, and will automatically temporarily change L4D characters to L4D2 until finale starts preventing game messing up their characters.
-
-
-### l4d2_population_control
-Allows you to set the chances that a common spawns as a certain uncommon. The order of the cvars is the order the percentages are ran
-* **Convars:**
- * `l4d2_population_chance <0.0-1.0>` Default: 1.0, the chance that the code runs on a spawn (basically if 0.0, none of the % chances will run for all types)
- * `l4d2_population_clowns <0.0-1.0>` The chance that on a common spawn that the special will be a clown.
- * `l4d2_population_mud <0.0-1.0>` The chance that on a common spawn that the special will be a mud common.
- * `l4d2_population_ceda <0.0-1.0>` The chance that on a common spawn that the special will be a ceda common.
- * `l4d2_population_worker <0.0-1.0>` The chance that on a common spawn that the special will be a worker common.
- * `l4d2_population_riot <0.0-1.0>` The chance that on a common spawn that the special will be a riot common.
- * `l4d2_population_jimmy <0.0-1.0>` The chance that on a common spawn that the special will be a jimmy common
- * `l4d2_population_common <#>` - The maximum amount of commons that can spawn.
- * 0 will turn off,
- * value > 0 will enforce the exact value
- * value < 0 will enforce z_common_limit + | value |
-* **Commands:**
- * `sm_populations` or `sm_population_list` - Lists all the cvar values
-
-### globalbans
-This plugin will store bans in a database and read from it on connect. This allows you to easily have bans global between servers.
-It will automatically intercept any ban that calls OnBanIdentity or OnBanClient (so sm_ban will work normally)
-Note: All admin players are ignored
-
-[Database File](sql/globalbans.sql)
-
-* **Convars:**
- * `sm_globalbans_kick_type <0/1/2>`
- * 0 = Do not kick, just notify
- * 1 = Kick if banned
- * 2 = Kick if cannot reach database
-
-
-### l4d2_rollback
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-
-An idea that you can either manually or have events (friendly fire, new player joining) trigger saving all the player's states. Then if say, a troll comes and kills you and/or incaps your team, you can just quick restore to exactly the point you were at with the same items, health, etc.
-
-Currently **abandoned.**
-
-Currently auto triggers:
-
-1. On any recent friendly fire (only triggers once per 100 game ticks)
-2. Any new player joins (only triggers once per 100 game ticks)
-
-* **Commands:**
- * `sm_save` - Initiates a manual save of all player's states
- * `sm_state` - Lists all the states
- * `sm_restore ` - Restores the selected player's state. @all for all
-
-### l4d2_autorestart
-Plugin that automatically restarts server when the server is NOT hibernating, with bots around and no players.
-This fixes an issue with custom maps that force sb_all_bot_game to 1 and disable hibernation.
-
-### l4d2_TKStopper
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-
-Plugin that prevents team killers by checking multiple criterias. Default system is as:
-Any survivor that attacks another survivor
-
-1. If within first 2 minutes of joining, no damage is dealt to either victim or attacker. This prevents the next person to join being punished.
-2. If during the finale vehicle arrival, they do 0x damage to victim and take 2x reverse friendly fire
-3. If neither #1 or #2, both the victim and the attacker take 1/2 the original damage
-4. If victim is in a saferoom, no damage is dealt.
-
-See https://admin.jackz.me/docs/plugins#tkstopper for some more implementation information
-
-
-During any of the above three conditions, if they deal (or attempt to deal) over 75 HP in 15 seconds (configurable) they will be instantly banned for a set period of time (60 minutes). If they are for sure a team killer, it can be extended to a permanent ban.
-
-* **Cvars:**
- * `l4d2_tk_forgiveness_time <#>` - The minimum amount of seconds to pass (in seconds) where a player's previous accumulated FF is forgive. Default is 15s
- * `l4d2_tk_bantime` - How long in minutes should a player be banned for? 0 for permanently. Default is 60
- * `l4d2_tk_ban_ff_threshold` - How much damage does a player need to do before being instantly banned. Default 75 HP
- * `l4d2_tk_ban_join_time` - Upto how many minutes should any new player's FF be ignored. Default is 2 Minutes
-
-
-
-### l4d2_crescendo_control
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-
-This plugin prevents the activation of buttons ahead of the team. It will prevent players from starting crescendos (and some small other activities as a side effect) until a certain threshold of the team has reached the area.
-
-_This plugin is not perfect, sometimes it may trigger early, or not trigger at all depending on the map. Sometimes you need to as admins, move forward to allow non-admins to activate events._
-
-
-* **Cvars:**
- * `l4d2_crescendo_percent`
- * `l4d2_crescendo_range`
-
-
-### l4d2_vocalize_control
-A very small plugin that simply allows a player to mute another player's vocalizations only for them.
-
-* **Commands:**
- * `sm_vgag ` - Vocalize gag or ungags selected player(s) for the command activator only
-
-### l4d2_sb_fix
-A fork of https://forums.alliedmods.net/showthread.php?p=2757330
-- Updated to latest sourcepawn syntax (now 1.11)
-- Fixed some stupid things (all cvars being FCVAR_NOTIFY)
-
-
-### l4d2_hideandseek
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
-
-A sourcemod extenstion of the vscript gamemode (https://steamcommunity.com/sharedfiles/filedetails/?id=2467133506)
-- Custom map boundaries, portals (tunnel through walls), and extra map props to spice up the maps
-- Optional climbable infected ladders
-- Heart beat sounds when a seeker is nearby
-- Quality of life improvements (winner messages, change seeker mid-game, change round time)
-- and a lot more that I've forgotten
-
-> [!TIP]
-> **Note:** Starting the gamemode is very tricky, sometimes you need to slay all players multiple times and sometimes reload the plugin for it to properly start. Once it works, it should be stable.
-
-
-### l4d2_guesswho
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
-
-Based off gmod guess who game, find the real players amongst a group of bots.
-All logic is written in this plugin, thus is required.
-Vscript required for hud & mutation
-
-Gamemode: https://steamcommunity.com/sharedfiles/filedetails/?id=2823719841
-
-Requires l4dtoolz and left4dhooks, and optionally skip intro cutscene
-
-### l4d2_prophunt
-Plugin requirements:
-* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
-* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
-
-Based off gmod prop hunt, find the real players amongst a group of props.
-All logic is written in this plugin, thus is required.
-Vscript required for hud & mutation
-
-* Gamemode: https://steamcommunity.com/sharedfiles/filedetails/?id=2850550331
-
-* Demo Map: https://steamcommunity.com/sharedfiles/filedetails/?id=2855027013 (makes most prop_static -> prop_dynamic)
-
-Requires l4dtoolz and left4dhooks, and optionally skip intro cutscene
-
-### l4d2_hats (Hats & Editor)
-
-Lets you hat any entity (has blacklist support), including players. Yeet, place, restore, and more with the hats. Also includes an entire prop editor that replaces any other prop spawner. You can create custom invisible walls and spawn props (In the future, may become a separate plugin.)
-
-[Prop Spawner Cheatsheat](https://admin.jackz.me/docs/props)
-
-The prop spawner includes:
-
-* Recents (recently spawned props)
-* Spawn Previews
-* Manual Placement (Move prop with cursor before spawning)
-* Prop Search
-* Better prop categories
-* Item spawning
-* Schematics (in early development)
-* Favorited props (in alpha)
-
-> [!WARNING]
-> **Be warned, hatting can cause server crashes.** It's a LOT more stable now, but some features are disabled by default (reverse hats, visible hats) as they caused too many crashes. Most crashes are caused by hats and ladders, but the default settings prevent it.
-
-### sm_namespamblock
-
-If a user changes their name 3 times within 10 seconds, they will be temp banned for 10 minutes.
-Requires recompile to change.
-
-* **Commands:**
- * `status2` - Shitty name, but shows all non-admin players, sorted by last joined ascending (up top). Shows steamid and the first name they joined the server as
- * `sm_status2` - Same command, but allows /status2 in chat
-
-### l4d2_ai_tweaks
-
-Simply, prevents an idle bot (that is a bot for an idle player) from healing another player unless:
-1. The target is black and white
-2. The player has been idle for over **ALLOW_HEALING_MIN_IDLE_TIME** (a \#define) seconds (default is 3 minutes)
-
-Requires recompile to change.
-
-### sm_player_notes
-
-Simply lets you add notes to any player, and includes reputation summary and automatic actions (for my other plugins). When a player joins, all admins will see their notes.
-
-[Database File](sql/sm_player_notes.sql) | [Docs for Automatic Actions](https://admin.jackz.me/docs/notes)
-
-* **Commands:**
- * `sm_note ` - Add notes to player, message does not need to be surrounded with quotes
- * `sm_notedisconnected` - Shows menu of all players that have disconnected
- * `sm_notes ` - View notes for player
+# Sourcemod L4D2 Plugins
+This is a collection of sourcemod plugins, most are used on my servers. The majority of the plugins are created by me, but some are modifications of other plugins.
+Some of the plugins / changes are very specific, but may be useful to someone.
+
+Not always the latest versions. If you have any interest with a plugin, I can make sure to upload the latest.
+
+Useful things:
+1. **Netprop Viewer** [L4D2 Netprops](https://jackz.me/netprops/l4d2) and [L4D2 Datamaps](https://jackz.me/netprops/l4d2-data)
+
+## Plugin List
+
+### Created by Me
+ * [l4d2-manual-director](#l4d2-manual-director) - Spawn specials on demand via director or at your cursor
+ * [l4d2-info-cmd](#l4d2-info-cmd) - Prints a full state of all survivors, useful for external information
+ * [AutoWarpBot](#autowarpbot) - Abandoned
+ * [L4D2FFKickProtection](#l4d2ffkickprotection) - Prevents players being voted off from friendly firing and prevents admins from being kicked
+ * [l4d2_avoid_minigun](#l4d2_avoid_minigun) - Makes bots avoid being infront of any in-use miniguns. Useful for spawned miniguns
+ * [l4d2_ai_minigun](#l4d2_ai_minigun) - Based off [Silver's Survivor Bot Holdout plugin](https://forums.alliedmods.net/showthread.php?p=1741099), allows you to spawn survivor bots but with no limit.
+ * [L4D2Tools](#l4d2tools) - A collection of utilities, mostly just used with [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) and the /model command
+ * [l4d2_swarm](#l4d2_swarm) - Uses vscript RushVictim to make all zombies target a player, like a more subtle vomitplayer
+ * [l4d2_feedthetrolls](#l4d2_feedthetrolls) - Full collection of tools to troll your friends or troll the trolls
+ * [l4d2_autobotcrown](#l4d2_autobotcrown) - Bots will auto crown
+ * [l4d2_extraplayeritems](#l4d2_extraplayeritems) - Includes tons of utilities for 5+ games, such as 5+ player hud, extra kit / item spawning, and more
+ * [l4d2_population_control](#l4d2_population_control) - Allows you to custom the type of zombies that spawn (% of clowns, mud men, etc..)
+ * [globalbans](#globalbans) - Bans synced via mysql, way lighter than the sourcebans cesspool.
+ * [l4d2_rollback](#l4d2_rollback) - Abandoned and broken, but makes periodic backup of all player's items
+ * [l4d2_autorestart](#l4d2_autorestart) - Restarts server if it's been on for a certain uptime or when empty with just bots.
+ * [l4d2_TKStopper](#l4d2_tkstopper) - All the teamkiller and shitty-aim player punishments. Auto increasing reverse ff and teamkill detection
+ * [l4d2_crescendo_control](#l4d2_crescendo_control) - Prevents players from running far ahead and starting events, and logs button presses
+ * [l4d2_vocalize_control](#l4d2_vocalize_control) - Allows you to locally mute someone from vocalizing
+ * [l4d2_guesswho](#l4d2_guesswho) - Garry's Mod's Guess Who in l4d2, inspired by hide and seek
+ * [l4d2_hideandseek](#l4d2_hideandseek) - An enhancement to the base hide and seek mutation
+ * [l4d2_hats](#l4d2_hats) - Entity Hats & Entity editing
+ * [l4d2_prophunt](#l4d2_prophunt) - Garry's Mod inspired prop hunt, inspired by hide and seek
+ * [sm_namespamblock](#sm_namespamblock) - Basic plugin that bans players if they change their name in rapid succession
+ * [l4d2-stats-plugin](https://github.com/jackzmc/l4d2-stats-plugin) - Custom stats recorder, see https://stats.jackz.me
+ * [l4d2-ai-tweaks](#l4d2_ai_tweaks) - Very minor tweaks to survivor bots' behavior
+ * [sm_player_notes](#sm_player_notes) - Add notes to players
+
+### Modified Others
+* [200IQBots_FlyYouFools](#200iqbots_flyyoufools) - Improved code to make it support multiple tanks and work better
+* [l4d_survivor_identity_fix](#l4d_survivor_identity_fix) - Use with [L4D2Tools](#l4d2tools) to change models, some fixes
+* [BetterWitchAvoidance](#betterwitchavoidance)
+* l4d_anti_rush - Modified plugin to add a forward, so other plugins (like feedthetrolls) can do something. In addition, use highest flow value achieved for players (fixes issue when admins go back and players who haven't moved suddenly get punished)
+* [l4d2_sb_fix](#l4d2_sb_fix) - Updated to 1.11 & latest sourcepawn syntax & removed the FCVAR_NOTIFY from all cvars (why is that added?)
+* GrabEnt - Improved version that prevents moving certain entities (such as invisual walls, ragdolls, etc) and improved some code
+
+## Dependencies
+This is a list of most common dependencies, independent if they are used for a certain plugin.
+Check the plugin info for an exact list.
+
+* [Left 4 Dhooks Direct](https://forums.alliedmods.net/showthread.php?t=321696)
+* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
+
+### Development Dependencies
+Most L4D2 plugins use my own include: jutils.inc, it's provided in this repo.
+Some do require newer includes for my modified plugins (such as my improved survivor identity fix)
+
+## Descriptions
+
+### l4d2-manual-director
+~~Probably going to be posted publicly sometime.~~ Allows you to spawn specials on cursor, or via director, forcefully, bypassing limits
+* **Convars:**
+ * `manual_director_version|mandirector_version` - ... gets version
+ * `mandirector_notify_spawn <1/0>` - Should spawning specials notify on use?
+ * `mandirector_announce_level <0/1/2/3>` - Announcement types. 0 - None, 1 - Only bosses, 2 - Only specials+, 3 - Everything
+ * `mandirector_enable_tank <0/1>` - Should tanks be allowed to be spawned?
+ * `mandirector_enable_witch <0/1>` - Should witches be allowed to be spawned?
+ * `mandirector_enable_mob <0/1>` - Should mobs be allowed to be spawned
+* **Commands:**
+ * `sm_spawnspecial [amount]` - Spawn a special via director
+ * `sm_forcespecial [amount]` - Force spawn a special via director, bypassing spawn limits
+ * `sm_forcecursor [amount]` - Force spawn a special at cursor, bypassing spawn limits
+ * `sm_cursormenu` - Show the spawn menu for cursor spawning
+ * `sm_specialmenu` - Show the spawn menu for director spawning
+ * `sm_directormenu` (Same as sm_specialmenu for now)
+
+
+### l4d2-info-cmd
+Technically 'l4d2 game info', haven't changed name. Just prints general information, used for a project
+* **Commands:**
+ * `sm_gameinfo`
+* Example Response:
+ ```
+ >map,diff,mode,tempoState,totalSeconds
+ c1m1_hotel,1,coop,3,1622
+ >id,name,bot,health,status,throwSlot,kitSlot,pillSlot,survivorType,velocity,primaryWpn,secondaryWpn
+ 1,Jackz,0,80,alive,0,,first_aid_kit,,Bill,0,,pistol
+ 3,Zoey,1,75,alive,0,,first_aid_kit,,Zoey,0,,pistol
+ 4,Francis,1,76,alive,0,,,,Francis,0,,pistol
+ 5,Louis,1,90,alive,0,,first_aid_kit,,Louis,0,,pistol
+ ```
+
+
+### AutoWarpBot
+Simple l4d2 plugin that will auto teleport bots to checkpoint once all real players have reached the saferoom.
+Doesn't really work well. Abandoned.
+
+
+### 200IQBots_FlyYouFools
+Updated version of ConnerRia's plugin. Improves bots avoidance of tanks. Change from original is updated sourcepawn syntax, some optimizations/cleanup, and fixes such as bots avoiding tank that has not been activated, or not escaping in vehicle due to presence of tank.
+Latest version now has support for multiple tanks, the bots might not avoid them as effectively as they would with one tank but they still try their best.
+* **Convars:**
+ * `FlyYouFools_Version` - Prints the version of plugin
+
+
+### BetterWitchAvoidance
+Inspired by the 200IQBots_FlyYouFools. Bots avoid witch if its over 40% anger when close, or a little bigger range at 60% or more. Not recommended to use, normal behavior seems fine.
+
+
+### L4D2FFKickProtection
+Simple plugin that prevents a player that is being vote-kicked from doing any ff damage to teammates.
+It also prevents vote kicking of admins, instead will notify admins.
+It also makes any vote kicks created by an admin to instantly be accepted by all players
+
+* **Convars:**
+ * `sm_votekick_force_threshold <#>` - The threshold of damage where the offending player is just immediately kicked. 0 -> Any attempted damage, -1 -> No auto kick.
+
+### l4d2_avoid_minigun
+Makes the bots avoid standing in front of or on top of the player that is using a minigun. It checks every 2.0 seconds if they are in the way, then forces them to move to behind you. There is no configuration, all automatic.
+
+
+### l4d2_ai_minigun
+Requires: [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+
+Spawn the holdout bots used in the passing. This supports all 8 characters, including with the minigun. They can spawn with any weapon or default to ak47.
+
+**Notes:**
+* The minigun holdout bot has to internally be Louis, so it will be Louis making sounds, with whatever model specified being shown. This doesn't apply for normal holdout bot.
+* \ can be "bill" or their numeric id (4).
+
+Code modified from https://forums.alliedmods.net/showthread.php?p=1741099
+
+* **Commands:**
+ * `sm_ai_minigun ` - Spawns an ai bot with minigun infront of wherever you are looking. Can also use numbers (0-7).
+ * `sm_ai_holdout [wpn]` - Spawns a normal ai holdout bot (no minigun), with any weapon w/ laser sight (default is ak).
+ * `sm_ai_remove_far` - Removes any holdout or minigun bots that are 750 units or more from any player.
+
+
+### L4D2Tools
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
+* [Modified L4D Survivor Identity Fix](#l4d_survivor_identity_fix)
+
+A collection of small tools:
+ * Notification of when someone picks up laser sights (only the first user, includes bots),
+ * Record time it takes for a finale or gauntlet run to be completed.
+ * Record the amount of friendly fire damage done
+ * Set the survivor models of any survivor with updated [l4d_survivor_identity_fix](#l4d_survivor_identity_fix)
+ * Automatically returns melee weapons that an idle bot dropped once no longer idle
+ * Automatically make players go idle when ping spikes
+ * Slowly kill any zombies attacking survivor bot's blind spots (Fixes brain dead bots stuck taking damage and not killing them)
+
+* **Convars:**
+ * `sm_laser_use_notice <0/1>` - Enable notification of when a laser box was used first
+ * `sm_time_finale <0/1/2>` - Record the time it takes to complete finale. 0 -> OFF, 1 -> Gauntlets Only, 2 -> All finales
+ * `sm_ff_notice <0/1/2>` - Should we record FF damages? 0: OFF, 1: To chat, 2: To HUD text.
+ * `sm_autoidle_ping_max <30.0...>` - "The highest ping a player can have until they will automatically go idle.\n0=OFF, Min is 30
+* **Commands:**
+ * `sm_model ` - Sets the survivor model of the target player(s). 'character' is name or ID of character.
+ * `sm_surv ` - Sets the m_survivorCharacter prop only of the target player(s). 'character' is name or ID of character.
+
+
+### l4d2_swarm
+This plugin is used to counter trolls and otherwise bad players. It simply uses the new script function RushVictim() to make all zombies in X radius attack Y target. It's that simple.
+
+This really only affects wandering zombies, mobs and panic events, but it may work slightly when bile or pipes are thrown. It does not and can't change the targets of zombies.
+
+* **Convars:**
+ * `sm_swarm_default_range <20-Infinity>` - The default range for commands & menus. Defaults to 7,500
+* **Commands:**
+ * `sm_swarm [player] [range]` - Swarm a player, or random if none."
+ * Aliases: `sm_rush`
+ * `sm_rushmenu` - Opens a menu to quickly rush any player. Can be bound to a key to quickly rush as well
+ * Aliases: `sm_rmenu`
+ * `sm_swarmtoggle [range]` - Will continuously run the swarm method on the player at the range. Use the command again or type "disable" for player to disable. Switching players will not disable, just switches target.
+ * Aliases: `sm_rushtoggle`, `sm_rt`
+ * `sm_rushtogglemenu` - Will open a menu to quickly select a player to continuously rush.
+ * Aliases: `sm_rtmenu`
+
+
+### l4d2_feedthetrolls
+Requires:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+* (Optional) [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
+* (Optional) [Actions](https://forums.alliedmods.net/showthread.php?t=336374)
+* (Optional) [Modified L4D Antirush](#l4d_anti_rush)
+
+This plugin allows you to enact certain troll modes on any player, some are subtle some are less so. Either way, it works great to deal with a rusher, an asshole or even your friends.
+
+See the up-to-date list of trolls and their descriptions:
+https://admin.jackz.me/docs/ftt
+
+* **Convars:**
+ * `sm_ftt_autopunish_mode <#>` - (Not used, WIP) Sets the modes that auto punish will activate for. 1 -> Early crescendo activations
+ * `sm_ftt_autopunish_action <#>` - Which actions will autopunish activate? Add bits together. 0=None, 1=TankMagnet 2=SpecialMagnet 4=Swarm 8=VomitPlayer
+ * `sm_ftt_autopunish_expires <0...>` - How many minutes (in gameticks) until autopunish trolls are removed. 0 for never.
+* **Commands:**
+ * `sm_fta [player]` - The main command, opens a menu to select a troll to apply, with modifiers and flags
+ * `sm_ftr [player]` - Removes all active trolls from a player
+ * `sm_ftc [player]` - Opens a menu to select a combo of trolls
+ * `sm_ftl` - Lists all players that have a mode applied.
+ * `sm_ftm` - Lists all troll options & their descriptions
+ * `sm_mark` - Toggles marking a player to be banned when they fully disconnect
+ * `sm_insta [player] [special]` - (No arguments opens menu) - Spawns a special via the director that will only target the victim
+ * `sm_inface [player] [special]` - Identical to above, but special will be spawned as close as possible to survivor. Boomers auto explode, jockeys on their head, etc.
+ * `sm_bots_attack [target health]` - Slightly broken, but makes all bots shoot at player until they hit X health or a timeout is reached. Turn on `sb_friendlyfire` for it to be effective.
+ * `sm_stagger ` - Makes a player stagger, shortcut to the Stagger troll
+ * `sm_witch_attack ` - Makes all witches agro on the player
+ * `sm_scharge [timeout seconds]` - Will wait till there's no obstructions and players in the way, then spawns a charger behind them to charge them.
+ * `sm_healbots [# bots or 0 default]` - Makes n amount of bots chase a player down to heal them. Won't stop until they are healed, they die, or you run command again.
+ * `sm_csplat - Shortcut to Car Splat. Spawns car in top/front/back that hits the player`
+
+### l4d2_autobotcrown
+Makes any suitable bot (> 40 hp, has shotgun) automatically crown a witch. Supports multiple bots and witches, but only one bot can crown one witch at a time. Plugin is disabled in realism, and is really on suitable for coop or versus. Even works with idle players.
+
+Bots do sometimes miss, but sometimes still manage to kill witch. They also don't care if there is danger in the way (fire, acid, angry witch).
+
+* **Convars:**
+ * `l4d2_autocrown_allowed_difficulty ` - The difficulties the plugin is active on. 1=Easy, 2=Normal 4=Advanced 8=Expert. Add numbers together.
+ * `l4d2_autocrown_modes_tog ` - (Not implemented) - Turn on the plugin in these game modes. 0=All, 1=Coop, 2=Survival, 4=Versus, 8=Scavenge. Add numbers together
+
+
+### l4d2_extraplayeritems
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+* [L4D Info Editor](https://forums.alliedmods.net/showthread.php?p=2614626)
+* (Development dependency) Updated l4d2_weapon_stocks.inc
+
+A well rounded tool that provides extra utilities to a 5+ co-op campaign.
+
+Features:
+* Automatically giving extra kits for each extra player in saferooms
+* Increasing item count for items randomly depending on player count
+* Fix same-models survivors having to fight over ammo pack usage
+* Automatically lock the first saferoom door for every chapter, until a threshold of players or time has passed
+* Includes a HUD that shows all the survivors and their items, and optionally their ping (breaks randomly not sure why)
+* Includes a new 5+ special spawning director
+* Spawn an extra tank that scales with player count
+
+* **Convars:**
+ * `epi_item_chance` - The base chance (multiplied by player count) of an extra item being spawned. Default: 0.056
+ * `epi_kitmode` - Decides how extra kits should be added. Default is 0
+ * 0 -> Overwrites previous extra kits
+ * 1 -> Adds onto previous extra kits
+ * `epi_updateminplayers` - Should the plugin update abm's cvar min_players convar to the player count? (0 no, 1 yes)
+ * `epi_doorunlock_percent` - The percent of players that need to be loaded in before saferoom door is opened.
+ * Default is 0.8, set to 0 to disable door locking
+ * `epi_doorunlock_wait` - How many seconds after to unlock saferoom door. 0 to disable timer
+ * `epi_doorunlock_open` - Controls when or if the door automatically opens after unlocked. Add bits together.
+ * 0 = Never, 1 = When timer expires, 2 = When all players loaded in
+ * `epi_hudstate` - Controls when the extra player hud shows.
+ * 0 = Never, 1 = When 5+ players, 2 = Always on
+ * `epi_sp_spawning` - Determines what specials are spawned. Add bits together.
+ * 1 = Specials, 2 = Witches, 4 = Tanks
+ * `epi_enabled` - When should epi be enabled?
+ * 0 = OFF, 1 = Only when 5+ and official map, 2 = Only when 5+, 3 = Always
+ * `epi_tank_chunkhp` - The amount of health to add to a tank per extra player
+ * `epi_gamemodes` - Comma separated list of allowed gamemodes.
+
+
+### l4d_survivor_identity_fix
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+
+A fork of [Survivor Identity Fix plugin](https://forums.alliedmods.net/showthread.php?t=280539) that adds support for other plugins to update the model cache. This is used by [L4D2Tools](#L4D2Tools) to update the identity when someone changes their model with `sm_model`. It also will clear the memory of model when a player disconnects entirely or on a new map.
+
+In addition, has a fix for the passing finale, and will automatically temporarily change L4D characters to L4D2 until finale starts preventing game messing up their characters.
+
+
+### l4d2_population_control
+Allows you to set the chances that a common spawns as a certain uncommon. The order of the cvars is the order the percentages are ran
+* **Convars:**
+ * `l4d2_population_chance <0.0-1.0>` Default: 1.0, the chance that the code runs on a spawn (basically if 0.0, none of the % chances will run for all types)
+ * `l4d2_population_clowns <0.0-1.0>` The chance that on a common spawn that the special will be a clown.
+ * `l4d2_population_mud <0.0-1.0>` The chance that on a common spawn that the special will be a mud common.
+ * `l4d2_population_ceda <0.0-1.0>` The chance that on a common spawn that the special will be a ceda common.
+ * `l4d2_population_worker <0.0-1.0>` The chance that on a common spawn that the special will be a worker common.
+ * `l4d2_population_riot <0.0-1.0>` The chance that on a common spawn that the special will be a riot common.
+ * `l4d2_population_jimmy <0.0-1.0>` The chance that on a common spawn that the special will be a jimmy common
+ * `l4d2_population_common <#>` - The maximum amount of commons that can spawn.
+ * 0 will turn off,
+ * value > 0 will enforce the exact value
+ * value < 0 will enforce z_common_limit + | value |
+* **Commands:**
+ * `sm_populations` or `sm_population_list` - Lists all the cvar values
+
+### globalbans
+This plugin will store bans in a database and read from it on connect. This allows you to easily have bans global between servers.
+It will automatically intercept any ban that calls OnBanIdentity or OnBanClient (so sm_ban will work normally)
+Note: All admin players are ignored
+
+[Database File](sql/globalbans.sql)
+
+* **Convars:**
+ * `sm_globalbans_kick_type <0/1/2>`
+ * 0 = Do not kick, just notify
+ * 1 = Kick if banned
+ * 2 = Kick if cannot reach database
+
+
+### l4d2_rollback
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+
+An idea that you can either manually or have events (friendly fire, new player joining) trigger saving all the player's states. Then if say, a troll comes and kills you and/or incaps your team, you can just quick restore to exactly the point you were at with the same items, health, etc.
+
+Currently **abandoned.**
+
+Currently auto triggers:
+
+1. On any recent friendly fire (only triggers once per 100 game ticks)
+2. Any new player joins (only triggers once per 100 game ticks)
+
+* **Commands:**
+ * `sm_save` - Initiates a manual save of all player's states
+ * `sm_state` - Lists all the states
+ * `sm_restore ` - Restores the selected player's state. @all for all
+
+### l4d2_autorestart
+Plugin that automatically restarts server when the server is NOT hibernating, with bots around and no players.
+This fixes an issue with custom maps that force sb_all_bot_game to 1 and disable hibernation.
+
+### l4d2_TKStopper
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+
+Plugin that prevents team killers by checking multiple criterias. Default system is as:
+Any survivor that attacks another survivor
+
+1. If within first 2 minutes of joining, no damage is dealt to either victim or attacker. This prevents the next person to join being punished.
+2. If during the finale vehicle arrival, they do 0x damage to victim and take 2x reverse friendly fire
+3. If neither #1 or #2, both the victim and the attacker take 1/2 the original damage
+4. If victim is in a saferoom, no damage is dealt.
+
+See https://admin.jackz.me/docs/plugins#tkstopper for some more implementation information
+
+
+During any of the above three conditions, if they deal (or attempt to deal) over 75 HP in 15 seconds (configurable) they will be instantly banned for a set period of time (60 minutes). If they are for sure a team killer, it can be extended to a permanent ban.
+
+* **Cvars:**
+ * `l4d2_tk_forgiveness_time <#>` - The minimum amount of seconds to pass (in seconds) where a player's previous accumulated FF is forgive. Default is 15s
+ * `l4d2_tk_bantime` - How long in minutes should a player be banned for? 0 for permanently. Default is 60
+ * `l4d2_tk_ban_ff_threshold` - How much damage does a player need to do before being instantly banned. Default 75 HP
+ * `l4d2_tk_ban_join_time` - Upto how many minutes should any new player's FF be ignored. Default is 2 Minutes
+
+
+
+### l4d2_crescendo_control
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+
+This plugin prevents the activation of buttons ahead of the team. It will prevent players from starting crescendos (and some small other activities as a side effect) until a certain threshold of the team has reached the area.
+
+_This plugin is not perfect, sometimes it may trigger early, or not trigger at all depending on the map. Sometimes you need to as admins, move forward to allow non-admins to activate events._
+
+
+* **Cvars:**
+ * `l4d2_crescendo_percent`
+ * `l4d2_crescendo_range`
+
+
+### l4d2_vocalize_control
+A very small plugin that simply allows a player to mute another player's vocalizations only for them.
+
+* **Commands:**
+ * `sm_vgag ` - Vocalize gag or ungags selected player(s) for the command activator only
+
+### l4d2_sb_fix
+A fork of https://forums.alliedmods.net/showthread.php?p=2757330
+- Updated to latest sourcepawn syntax (now 1.11)
+- Fixed some stupid things (all cvars being FCVAR_NOTIFY)
+
+
+### l4d2_hideandseek
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
+
+A sourcemod extenstion of the vscript gamemode (https://steamcommunity.com/sharedfiles/filedetails/?id=2467133506)
+- Custom map boundaries, portals (tunnel through walls), and extra map props to spice up the maps
+- Optional climbable infected ladders
+- Heart beat sounds when a seeker is nearby
+- Quality of life improvements (winner messages, change seeker mid-game, change round time)
+- and a lot more that I've forgotten
+
+> [!TIP]
+> **Note:** Starting the gamemode is very tricky, sometimes you need to slay all players multiple times and sometimes reload the plugin for it to properly start. Once it works, it should be stable.
+
+
+### l4d2_guesswho
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
+
+Based off gmod guess who game, find the real players amongst a group of bots.
+All logic is written in this plugin, thus is required.
+Vscript required for hud & mutation
+
+Gamemode: https://steamcommunity.com/sharedfiles/filedetails/?id=2823719841
+
+Requires l4dtoolz and left4dhooks, and optionally skip intro cutscene
+
+### l4d2_prophunt
+Plugin requirements:
+* [Left4DHooks](https://forums.alliedmods.net/showthread.php?t=321696)
+* [Scene Processor](https://forums.alliedmods.net/showthread.php?p=2147410)
+
+Based off gmod prop hunt, find the real players amongst a group of props.
+All logic is written in this plugin, thus is required.
+Vscript required for hud & mutation
+
+* Gamemode: https://steamcommunity.com/sharedfiles/filedetails/?id=2850550331
+
+* Demo Map: https://steamcommunity.com/sharedfiles/filedetails/?id=2855027013 (makes most prop_static -> prop_dynamic)
+
+Requires l4dtoolz and left4dhooks, and optionally skip intro cutscene
+
+### l4d2_hats (Hats & Editor)
+
+Lets you hat any entity (has blacklist support), including players. Yeet, place, restore, and more with the hats. Also includes an entire prop editor that replaces any other prop spawner. You can create custom invisible walls and spawn props (In the future, may become a separate plugin.)
+
+[Prop Spawner Cheatsheat](https://admin.jackz.me/docs/props)
+
+The prop spawner includes:
+
+* Recents (recently spawned props)
+* Spawn Previews
+* Manual Placement (Move prop with cursor before spawning)
+* Prop Search
+* Better prop categories
+* Item spawning
+* Schematics (in early development)
+* Favorited props (in alpha)
+
+> [!WARNING]
+> **Be warned, hatting can cause server crashes.** It's a LOT more stable now, but some features are disabled by default (reverse hats, visible hats) as they caused too many crashes. Most crashes are caused by hats and ladders, but the default settings prevent it.
+
+### sm_namespamblock
+
+If a user changes their name 3 times within 10 seconds, they will be temp banned for 10 minutes.
+Requires recompile to change.
+
+* **Commands:**
+ * `status2` - Shitty name, but shows all non-admin players, sorted by last joined ascending (up top). Shows steamid and the first name they joined the server as
+ * `sm_status2` - Same command, but allows /status2 in chat
+
+### l4d2_ai_tweaks
+
+Simply, prevents an idle bot (that is a bot for an idle player) from healing another player unless:
+1. The target is black and white
+2. The player has been idle for over **ALLOW_HEALING_MIN_IDLE_TIME** (a \#define) seconds (default is 3 minutes)
+
+Requires recompile to change.
+
+### sm_player_notes
+
+Simply lets you add notes to any player, and includes reputation summary and automatic actions (for my other plugins). When a player joins, all admins will see their notes.
+
+[Database File](sql/sm_player_notes.sql) | [Docs for Automatic Actions](https://admin.jackz.me/docs/notes)
+
+* **Commands:**
+ * `sm_note ` - Add notes to player, message does not need to be surrounded with quotes
+ * `sm_notedisconnected` - Shows menu of all players that have disconnected
+ * `sm_notes ` - View notes for player
diff --git a/data/guesswho/config.cfg b/data/guesswho/config.cfg
index a42efa4..9d209eb 100644
--- a/data/guesswho/config.cfg
+++ b/data/guesswho/config.cfg
@@ -1,87 +1,87 @@
-"guesswho"
-{
- "c1m1_hotel"
- {
- "spawnpoint" "442.905334 5640.576660 2656.031250"
- "ents"
- {
- "FENCE"
- {
- "origin" "1602.161499 5618.440917 2656.031250"
- "rotation" "0.000000 -178.836151 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\barricade001_128_reference.mdl"
- }
- "VENDING"
- {
- "origin" "406.428405 5625.409667 2656.031250"
- "rotation" "0.000000 90.238922 0.000000"
- "type" "prop_dynamic"
- "model" "props\cs_office\vending_machine.mdl"
- }
- }
-
- }
- "c8m5_rooftop"
- {
- "spawnpoint" "5386.052246 8413.505859 5536.031250"
- }
- "c2m2_fairgrounds"
- {
- "spawnpoint" "-3451.616943 -818.726989 128.031250"
- "ents"
- {
- "DROP_BLOCK"
- {
- "origin" "-3073.630371 -910.963195 192.623626"
- "scale" "30 260 100"
- }
- "ENTRANCE_BLOCK"
- {
- "origin" "-2826.833251 -1978.528808 -127.915451"
- "rotation" "0.000000 -1.978949 0.000000"
- "type" "prop_dynamic"
- "model" "props_urban\wood_fence001_128.mdl"
- }
- "ENTRANCE_BLOCK_PROP"
- {
- "origin" "-2826.833251 -1978.528808 -127.915451"
- "scale" "20 100 500"
- }
- "STAIR_BLOCK_PROP"
- {
- "origin" "-3241.681884 -1817.223266 256.031250"
- "rotation" "0.000000 92.011253 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "STAIR_BLOCK"
- {
- "origin" "-3321.681884 -1817.223266 300.031250"
- "scale" "140 20 60"
- }
- }
- }
- "c11m4_terminal"
- {
- "spawnpoint" "3081.824951 4571.367187 152.031250"
- "ents"
- {
- "ENTRANCE_BLOCKER"
- {
- "origin" "2700.844482 1865.502807 177.313140"
- "scale" "200 100 200"
- }
- "fire"
- {
- "origin" "2700.844482 1865.502807 179.313140"
- "type" "env_fire"
- "scale" "1.0 256 8"
- }
- }
- "inputs"
- {
- "checkpoint_entrance" "Kill"
- }
- }
-}
+"guesswho"
+{
+ "c1m1_hotel"
+ {
+ "spawnpoint" "442.905334 5640.576660 2656.031250"
+ "ents"
+ {
+ "FENCE"
+ {
+ "origin" "1602.161499 5618.440917 2656.031250"
+ "rotation" "0.000000 -178.836151 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\barricade001_128_reference.mdl"
+ }
+ "VENDING"
+ {
+ "origin" "406.428405 5625.409667 2656.031250"
+ "rotation" "0.000000 90.238922 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_office\vending_machine.mdl"
+ }
+ }
+
+ }
+ "c8m5_rooftop"
+ {
+ "spawnpoint" "5386.052246 8413.505859 5536.031250"
+ }
+ "c2m2_fairgrounds"
+ {
+ "spawnpoint" "-3451.616943 -818.726989 128.031250"
+ "ents"
+ {
+ "DROP_BLOCK"
+ {
+ "origin" "-3073.630371 -910.963195 192.623626"
+ "scale" "30 260 100"
+ }
+ "ENTRANCE_BLOCK"
+ {
+ "origin" "-2826.833251 -1978.528808 -127.915451"
+ "rotation" "0.000000 -1.978949 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_urban\wood_fence001_128.mdl"
+ }
+ "ENTRANCE_BLOCK_PROP"
+ {
+ "origin" "-2826.833251 -1978.528808 -127.915451"
+ "scale" "20 100 500"
+ }
+ "STAIR_BLOCK_PROP"
+ {
+ "origin" "-3241.681884 -1817.223266 256.031250"
+ "rotation" "0.000000 92.011253 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "STAIR_BLOCK"
+ {
+ "origin" "-3321.681884 -1817.223266 300.031250"
+ "scale" "140 20 60"
+ }
+ }
+ }
+ "c11m4_terminal"
+ {
+ "spawnpoint" "3081.824951 4571.367187 152.031250"
+ "ents"
+ {
+ "ENTRANCE_BLOCKER"
+ {
+ "origin" "2700.844482 1865.502807 177.313140"
+ "scale" "200 100 200"
+ }
+ "fire"
+ {
+ "origin" "2700.844482 1865.502807 179.313140"
+ "type" "env_fire"
+ "scale" "1.0 256 8"
+ }
+ }
+ "inputs"
+ {
+ "checkpoint_entrance" "Kill"
+ }
+ }
+}
diff --git a/data/hideandseek.cfg b/data/hideandseek.cfg
index 698862d..952ca58 100644
--- a/data/hideandseek.cfg
+++ b/data/hideandseek.cfg
@@ -1,2083 +1,2083 @@
-"hideandseek"
-{
- "c4m1_milltown_a"
- {
- "spawnpoint" "-3904.694580 7205.333984 199.836639"
- "ents"
- {
- "TRUCK_SKIP"
- {
- "origin" "-3369.870117 7448.359375 96.031250"
- "rotation" "0.000000 24.135879 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "BARRICADE_1"
- {
- "origin" "2074.847167 2896.825439 96.031250"
- "rotation" "0.000000 175.428447 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "BARRICADE_2"
- {
- "origin" "2065.847167 2779.825439 96.031250"
- "rotation" "0.000000 175.428447 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "BARRICADE_3"
- {
- "origin" "2071 2670 96.031250"
- "rotation" "0.000000 175.428447 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "DOOR_BLOCK"
- {
- "origin" "2000.038940 2530.103759 104.031250"
- "rotation" "90.000000 180.681259 0.000000"
- "type" "prop_dynamic"
- "model" "props_mill/wood_stack.mdl"
- }
- "BARRICADE_SIDE"
- {
- "origin" "1938.229248 2108.860839 96.353591"
- "rotation" "0.000000 175.428447 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "HOUSE_BLOCK_1"
- {
- "origin" "1917.533691 2347.451416 104.031250"
- "rotation" "0.000000 0.041839 0.000000"
- "type" "prop_dynamic"
- "model" "props/cs_office/bookshelf2.mdl"
- }
- "HOUSE_BLOCK_2"
- {
- "origin" "1917.533691 2247.451416 104.031250"
- "rotation" "0.000000 0.041839 0.000000"
- "type" "prop_dynamic"
- "model" "props/cs_office/bookshelf2.mdl"
- }
- "TREEHOUSE_BLOCKER"
- {
- "origin" "2072.242675 3038.970703 206.251251"
- "scale" "5.000000 1500.000000 400.000000"
- "type" "env_physics_blocker"
- }
- "ENTRANCE_STOPPER"
- {
- "origin" "-4057.503173 7158.272949 201.491455"
- "scale" "5.000000 2500.000000 800.000000"
- "type" "env_physics_blocker"
- }
-
- }
- }
- "c5m4_quarter"
- {
- "spawnpoint" "-3663.735351 3156.915039 64.031250"
- "sets"
- {
- "block"
- {
- "spawnpoint" "-1322.446411 2238.070800 72.031250"
- }
- "block_long"
- {
- "spawnpoint" "-1213.040527 325.299438 176.126968"
- }
- }
- "ents"
- {
- "END_BLOCK"
- {
- "origin" "-1534.599975 -1606.185058 256.031250"
- "rotation" "0.000000 -177.669448 0.000000"
- "type" "prop_dynamic"
- "model" "props_urban/fridge001.mdl"
- }
- "END_BLOCK_2"
- {
- "origin" "-1802.811401 -1206.369750 260.031250"
- "type" "env_physics_blocker"
- "scale" "100 100 50"
- }
- "BEGIN_BLOCK"
- {
- "origin" "-1548.047607 2458.536437 60.044128"
- "scale" "1600.000000 10.000000 400.000000"
- "type" "env_physics_blocker"
- "set" "block"
- }
- "STAIRS"
- {
- "origin" "-1118.047607 915.536437 244.044128"
- "rotation" "0.000000 180.843437 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/stairs_house_01.mdl"
- }
- "GATE_TP"
- {
- "origin" "-1630.467895 -396.031250 184.031250"
- "type" "_portal"
- "offset" "-1623.645263 -332.286193 135.869384"
- "scale" "10 10 10"
- }
- "PORTAL_1"
- {
- "origin" "-1446.877319 -358.595733 203.116073"
- "type" "_relportal"
- "offset" "0.000000 10.000000 0.000000"
- "scale" "100 40 50"
- }
- "PORTAL_1_PROPA"
- {
- "origin" "-1575.466674 -420.767852 65.182296"
- "rotation" "0.000000 180.843437 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/stairs_house_01.mdl"
- }
- "PORTAL_1_PROPB"
- {
- "origin" "-1575.466674 -310.767852 65.182296"
- "rotation" "0.000000 180.843437 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/stairs_house_01.mdl"
- }
- "FLAT_BLOCK"
- {
- "origin" "-3188.373779 3200.747802 300.585388"
- "scale" "500 400 50"
- "type" "env_physics_blocker"
- }
- "FENCE_SKIP"
- {
- "origin" "-1555.620239 2514.195068 64.031250"
- "rotation" "0.0 -71.973693 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "SHORT_BLOCK"
- {
- "origin" "-563.721069 1031.170043 143.387512"
- "rotation" "62.338180 6.194050 0.000000"
- "type" "prop_dynamic"
- "model" "props_interiors\couch.mdl"
- "set" "block_long"
- }
- "SHORT_BLOCK_SUPPORT"
- {
-
- "origin" "-608.215881 1030.887817 96.031250"
- "rotation" "0.530479 -82.875152 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "set" "block_long"
- }
- "SHORT_BLOCK_SUPPORT2"
- {
-
- "origin" "-608.215881 1030.887817 136.031250"
- "rotation" "0.530479 -82.875152 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "set" "block_long"
- }
- "TRACTOR_EARLY_HELP"
- {
- "origin" "-1503.682861 539.312591 255.984069"
- "rotation" "89.000000 0.671798 90.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors\fence002.mdl"
- }
- "GNOME"
- {
- "origin" "-3511.222656 2463.375488 432.031250"
- "type" "prop_physics_multiplayer"
- "model" "props_junk/gnome.mdl"
- }
- "lol"
- {
- "origin" "-2386.989013 1119.776489 80.031250"
- "rotation" "0.000000 -86.269416 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_teenangst.mdl"
- }
- }
- "inputs"
- {
- "float_tracktrain" "TeleportToPathTrack float_path2"
- "float_final_nav_blocker" "BlockNav"
- "float_music" "Kill"
- "float_start_nav_blocker2" "Kill"
- "float_start_nav_blocker3" "Kill"
- "courtyard_spawn1_breakable" "Kill"
- "courtyard_spawn2_breakable" "Kill"
- }
- }
- "c8m3_sewers"
- {
- "ents"
- {
- "A"
- {
- "origin" "13265.965820 8547.057617 -250.7"
- "type" "env_physics_blocker"
- }
- "B"
- {
- "origin" "14130.535156 8026.46386 -254.7"
- "type" "env_physics_blocker"
- }
- "A_PROP"
- {
- "origin" "13265.965820 8497.057617 -240.7"
- "type" "prop_dynamic"
- "model" "props/cs_office/shelves_metal.mdl"
- "rotation" "90 90 0"
- }
- "B_PROP"
- {
- "origin" "14130.535156 8026.46386 -254.7"
- "type" "prop_dynamic"
- "model" "props_swamp/river_sign01.mdl"
- "rotation" "90 0 0"
- }
- "STAIR_HELP"
- {
- "origin" "11896.693359 7291.923828 143.372573"
- "rotation" "0.000000 -90.646430 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- }
- "ROOF_EXIT"
- {
- "origin" "11353 5861 204"
- "rotation" "0 96 0"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "ROOF_FIX_FIXER"
- {
- "origin" "10579.599609 5120.623535 280.031250"
- "scale" "20 1000 400"
- "type" "env_physics_blocker"
- }
- "ROOF_FIX_FIXER_CORNER"
- {
- "origin" "10617.877929 5216.473144 280.031250"
- "scale" "40 60 400"
- "type" "env_physics_blocker"
- }
- "ROOF_NO_SNEAKY_JUMP"
- {
- "origin" "11127.424804 5662.353515 408.031250"
- "scale" "20 300 500"
- "type" "env_physics_blocker"
- }
- "ROOF_BRIDGE"
- {
- "origin" "12421.961914 4886.637207 568.031250"
- "rotation" "-11.137193 -159.704940 0.000000"
- "type" "prop_dynamic"
- "model" "props_unique\rope_bridge.mdl"
-
- }
- "ROOF_STOP"
- {
- "origin" "11895.786132 4616.688476 712.031250"
- "rotation" "0.137193 0.704940 0.000000"
- "type" "prop_dynamic"
- "model" "props_wasteland\coolingtank01.mdl"
- }
- "ROOF_STOP2"
- {
-
- "origin" "11641.057617 4630.232421 712.031250"
- "rotation" "0.137193 0.704940 0.000000"
- "type" "prop_dynamic"
- "model" "props_wasteland\coolingtank01.mdl"
- }
- "ROOF_STOP3"
- {
- "origin" "11432.921875 4660.313476 712.031250"
- "rotation" "0.137193 0.704940 0.000000"
- "type" "prop_dynamic"
- "model" "props_wasteland\coolingtank01.mdl"
- }
- "ROOF_STOP_WALL"
- {
- "origin" "11641.057617 4630.232421 712.031250"
- "type" "env_physics_blocker"
- "scale" "2000 90 600"
- }
- "ROOF_DEATH_PREVENT"
- {
- "origin" "12456.858398 4781.894042 568.031250"
- "rotation" "0.000000 -131.235916 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\barricade001_128_reference.mdl"
- }
- "ROOF_DEATH_PREVENT_WALL"
- {
- "origin" "12310.758789 4753.980957 568.031250"
- "type" "env_physics_blocker"
- "scale" "120 60 400"
- }
- "ROOF_DEATH_PREVENT_PREVENTER"
- {
- "origin" "11994.040039 4736.263671 712.031250"
- "rotation" "0.000000 -4.221647 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\barricade001_128_reference.mdl"
- }
- "STAIR_ACCESS"
- {
-
- "origin" "11100.574218 5208.395996 317.781250"
- "rotation" "0.290893 -80.968573 32.000000"
- "type" "prop_dynamic"
- "model" "props_swamp\plank001b_192.mdl"
- }
- "GAS_STOP"
- {
- "origin" "12682.116210 5713.574218 568.031250"
- "rotation" "0.000000 73.767333 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\barricade001_128_reference.mdl"
- }
- "GAS_STOP2"
- {
- "origin" "12580.110351 5731.003417 568.031250"
- "rotation" "0.000000 73.767333 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\barricade001_128_reference.mdl"
- }
- "GAS_BLOCK"
- {
- "origin" "12812.621093 5929.395507 463.586090"
- "type" "env_physics_blocker"
- "scale" "300 1000 1000"
- }
- "NO_DIE"
- {
- "origin" "11323.022460 4793.813476 712.031250"
- "rotation" "0.000000 -134.307327 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\barricade001_128_reference.mdl"
- }
- }
- "inputs"
- {
- "anv_mapfixes_unattackable_wrongway1" "Kill"
- }
- }
- "c3m1_plankcountry"
- {
- "spawnpoint" "-12070.341796 10517.305664 183.448410"
- "sets"
- {
- "area2"
- {
- "spawnpoint" "-4240.322265 6052.394531 28.160285"
- }
- "end"
- {
- "spawnpoint" "-1030.998535 4821.037109 144.160293"
- }
- "large"
- {
- "spawnpoint" "-1030.998535 4821.037109 144.160293"
- }
- }
- "ents"
- {
- "divider"
- {
- "origin" "-1023.450622 4939.198242 144.160293"
- "rotation" "0.000000 -90.445228 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- "set" "area2"
- }
- "divider"
- {
- "origin" "-1023.450622 4939.198242 144.160293"
- "rotation" "0.000000 -90.445228 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- "set" "end"
- }
- }
- "inputs"
- {
- "func_breakable" "Break"
- }
- }
- "c3m3_shantytown"
- {
- "sets"
- {
- "hut"
- {
- "spawnpoint" "-3914.085449 -3092.477050 62.502365"
- }
- "end-large"
- {
- "spawnpoint" "-557.272216 -2464.328125 4.269320"
- "inputs"
- {
- "delete_coop_ents_postIO" "Trigger"
- "bridge_minifinale" "Close"
- "bridge_minifinale" "SetSpeed 200"
- }
- }
- }
- "ents"
- {
- "door_block"
- {
- "origin" "-4004.808593 -3011.982666 62.502365"
- "rotation" "0 0 0"
- "type" "prop_dynamic"
- "model" "props_urban\fridge002.mdl"
- }
- "door_wall"
- {
- "origin" "-4000.808593 -3011.982666 62.502365"
- "scale" "20 1050 300"
- }
- "door_block_tree"
- {
- "origin" "-4135.271972 -3837.605224 -0.291338"
- "rotation" "-0.559620 173.978195 0.000000"
- "type" "prop_dynamic"
- "model" "props_foliage\swamp_cypress_knee01.mdl"
- }
- "door_block_tree"
- {
- "origin" "-4116.226562 -3944.508056 0.005385"
- "rotation" "-0.559620 173.978195 0.000000"
- "type" "prop_dynamic"
- "model" "props_foliage\swamp_cypress_knee01.mdl"
- }
- "large_block"
- {
- "origin" "-1745.625976 -2745.440429 0.031250"
- "set" "end-large"
- "scale" "50 2000 400"
- }
- "large_block_prop"
- {
- "origin" "-1780.625976 -2880.440429 0.031250"
- "type" "prop_dynamic"
- "set" "end-large"
- "rotation" "0 90 0"
- "model" "props_debris\airliner_wreckage3.mdl"
- }
- "large_block_prop"
- {
- "origin" "-1573.762573 -3763.656494 -10.686201"
- "type" "prop_dynamic"
- "set" "end-large"
- "rotation" "0 90 0"
- "model" "props_debris\dead_cow_smallpile.mdl"
- }
- }
- "inputs"
- {
- "bridge_button" "Kill"
- }
- }
- "c3m4_plantation"
- {
- "sets"
- {
- "begin"
- {
-
- }
- "mansion"
- {
- "climbing" "on"
- "spawnpoint" "1656.207519 -120.207824 224.031250"
- }
- }
- "defaultset" "begin"
- "ents"
- {
- "A"
- {
- "origin" "2000.802612 -426.686829 402.803497"
- "type" "env_physics_blocker"
- "scale" "150.0 150.00 1.0"
- "set" "begin"
- }
- "A_PROP"
- {
- "origin" "2000.802612 -426.686829 402.803497"
- "type" "prop_dynamic"
- "model" "props_swamp/boardwalk_384.mdl"
- "rotation" "0 90 0"
- "set" "begin"
- }
- "MANSION_ENT_RAIL"
- {
- "model" "props_exteriors/wood_railing004c.mdl"
- "type" "prop_dynamic"
- "origin" "2529.63 -804.10 642.56"
- "angles" "0.00 178.84 0.00"
- "set" "mansion"
- }
- "MANSION_ENT_BLOCK"
- {
- "origin" "2563.37 -818.00 366.03"
- "type" "env_physics_blocker"
- "scale" "441 10 400"
- "set" "mansion"
- }
- "MANSION_ENT_GATE"
- {
- "model" "props_street/police_barricade_496in.mdl"
- "origin" "1693.63 -1178.33 169.86"
- "type" "prop_dynamic"
- "set" "mansion"
- }
- }
- "inputs"
- {
- "env_physics_blocker" "Kill"
- "gate_explode_relay" "Trigger"
- "prop_minigun" "Kill"
- }
- }
- "c1m3_mall"
- {
- "sets"
- {
- "mid"
- {
- "spawnpoint" "1457.455078 -1059.256469 280.031250"
- }
- }
- "defaultset" "begin"
- "ents"
- {
- "A"
- {
- "origin" "1581.286865 -1029.394043 280.079254"
- "type" "prop_dynamic"
- "model" "props_junk/dumpster.mdl"
- "rotation" "0 90 0"
- }
- "B"
- {
- "origin" "1714.133179 -1023.777527 347.735168"
- "scale" "25.0 25.00 100.0"
- "type" "env_physics_blocker"
- "set" "begin"
- }
- "B"
- {
- "origin" "1714.133179 -1023.777527 347.735168"
- "scale" "25.0 25.00 100.0"
- "type" "env_physics_blocker"
- "set" "mid"
- }
- "lol"
- {
- "origin" "2129.125976 -2346.968750 0.031250"
- "rotation" "0.000000 39.811450 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_namvet.mdl"
- }
- "VENT_WARPER"
- {
- "origin" "1203.086791 -2000.914306 384.031250"
- "type" "_portal"
- "scale" "40 40 50"
- "offset" "1159.986694 -2063.281738 456.013153"
- "set" "mid"
- }
- "EXIT_BLOCK"
- {
- "origin" "1283.903686 -5341.520019 536.031250"
- "rotation" "0.000000 0.968292 0.000000"
- "type" "prop_dynamic"
- "model" "props\cs_office\vending_machine.mdl"
- "set" "mid"
- }
-
- }
- "inputs"
- {
- "sound_alarm" "StopSound"
- "sound_alarm" "Kill"
- "blocker_stairwell" "Kill"
- "hurt_stairwell" "Kill"
- "door_stairwell" "Unlock"
- "blocker_stairwell_clip" "Kill"
- "door_hallway" "Kill"
- "breakwall_bathroom_wall" "Break"
- "breakble_glass_minifinale" "Break"
- "env_player_blocker" "Kill"
- "relay_elevator_path_02" "Trigger"
- "soundscapes-hall" "StopSound"
- "soundscapes-hall.entry" "StopSound"
- }
- }
- "c6m2_bedlam"
- {
- "ents"
- {
- "A"
- {
- "origin" "349.935608 3156.456543 263.583710"
- "scale" "100.0 50.00 300.0"
- "type" "env_physics_blocker"
- "set" "short"
- }
- "A_PROP"
- {
- "origin" "350.063263 3171.198730 198.247498"
- "type" "prop_dynamic"
- "model" "props_industrial/barrel_fuel.mdl"
- "rotation" "0 -91 -0.000109"
- "set" "short"
- }
- "B_PROP"
- {
- "origin" "1138.566650 1374.530884 -194.578400"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 180 0"
- }
- "B_PROP2"
- {
- "origin" "1363.922241 1381.682617 -194.578400"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 0 0"
- }
- "DOOR_BLOCK"
- {
- "origin" "1401.782592 4686.330566 -101.095458"
- "type" "env_physics_blocker"
- "scale" "60 20 100"
- }
- "DOOR_BLOCK_PROP"
- {
- "origin" "1401.782592 4686.330566 -160.095458"
- "type" "prop_dynamic"
- "model" "props\de_train\pallet_barrels.mdl"
- "rotation" "0 -91 -0.000109"
- }
- "RETURN_HELPER"
- {
- "origin" "1251.872436 1316.629760 -78.262710"
- "rotation" "0.000000 178.612152 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- }
- "inputs"
- {
- "gate_1_alarm" "Kill"
- "sound_alarm" "Kill"
- "gate_2_alarm" "Kill"
- }
- }
- "c7m1_docks"
- {
- "climbing" "off"
- "spawnpoint" "11068.093750 -207.805831 -67.623802"
- "ents"
- {
- "A_PROP"
- {
- "origin" "8469.404297 854.461121 14.257706"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 -90 0"
- }
- "B_PROP"
- {
- "origin" "3522.576416 647.513244 176.031250"
- "rotation" "0.000000 -88.406250 0.000000"
- "type" "prop_dynamic"
- "model" "props_doors/roll-up_door_full.mdl"
- }
- "B_PROP"
- {
- "origin" "3266.576416 647.513244 176.031250"
- "rotation" "0.000000 -88.406250 0.000000"
- "type" "prop_dynamic"
- "model" "props_doors/roll-up_door_full.mdl"
- }
- "DUMPSTER_EXIT"
- {
- "origin" "3604 263 149"
- "model" "props_junk/dumpster_2.mdl"
- "type" "prop_dynamic"
- "rotation" "0 180 0"
- }
- "DUMPSTER_ENTER"
- {
- "origin" "3872.412841 588.284240 176.031250"
- "model" "props_junk/dumpster_2.mdl"
- "type" "prop_dynamic"
- "rotation" "0 0 0"
- }
- "CRATE_SHORTCUT"
- {
- "origin" "5288.236328 596.502136 146.863418"
- "rotation" "0.000000 178.612152 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "SPAWN_BLOCK_PROP"
- {
- "origin" "11404.483398 -227.757766 -63.968750"
- "rotation" "0.966560 -176.921936 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "SPAWN_BLOCK"
- {
- "origin" "11404.483398 -227.757766 0.968750"
- "scale" "20 1000 100"
- "type" "env_physics_blocker"
- }
- "lol"
- {
- "origin" "7641.535644 2735.968750 240.031250"
- "rotation" "8.966513 -31.319810 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_namvet.mdl"
- }
- }
- "inputs"
- {
- "tankdoorin" "Open"
- "tankdoorout" "Open"
- "traincar_navblocker" "Kill"
- "tankdoorin_button" "Kill"
- "tankdoorout_button" "Kill"
- "tank_door_clip" "Kill"
- "tank_sound_timer" "Disable"
- "delete_coop_ents_postIO" "Trigger"
- "battlefield_cleared" "UnblockNav"
- }
- }
- "c10m2_drainage"
- {
- "ents"
- {
- "TOP_HELPER"
- {
- "origin" "-9874.015625 -7637.003417 -467.968750"
- "rotation" "0.000000 -90.964706 0.000000"
- "type" "prop_dynamic"
- "model" "props_interiors/desk_executive.mdl"
- }
- "STAIRS"
- {
- "origin" "-8120.544433 -7200.660156 -561.968750"
- "rotation" "0.000000 180.672305 -8.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- }
- "BOX"
- {
- "origin" "-8713.857421 -7792.103027 -395.968750"
- "rotation" "0.000000 90.578536 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "PORTAL1_ENT"
- {
- "origin" "-8713.857421 -7792.103027 -300.968750"
- "type" "_relportal"
- "offset" "0 150 50"
- "scale" "60 30 5"
- }
- "PORTAL1_EXIT"
- {
-
- "origin" "-8698.404296 -7663.968750 -260.968750"
- "type" "_portal"
- "offset" "-8776.213867 -7811.048339 -395.968750"
- "scale" "60 30 50"
- }
- "PORTAL2_EXIT"
- {
-
- "origin" "-9443.846679 -7276.223144 -307.968750"
- "type" "_portal"
- "offset" "-9520.279296 -7281.130859 -467.968750"
- "scale" "20 100 100"
- }
- "PORTAL3_EXIT"
- {
-
- "origin" "-9867.969726 -6852.831054 -307.968750"
- "type" "_portal"
- "offset" "-9871.308593 -6921.020507 -467.968750"
- "scale" "100 20 100"
- }
- "PORTAL4_EXIT"
- {
-
- "origin" "-8984.172851 -7881.412597 -307.968750"
- "type" "_portal"
- "offset" "-8928.278320 -7888.108886 -336.968750"
- "scale" "20 100 100"
- }
- "FALL_BARRIER"
- {
- "origin" "-8830.380859 -8800.031250 -307.968750"
- "rotation" "0.000000 94.068542 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors\fence002.mdl"
- }
- "PORTAL_5_ENTRANCE_PROP"
- {
- "origin" "-8932.521484 -8515.070312 -400.968750"
- "rotation" "0.000000 -178.028656 0.000000"
- "type" "prop_dynamic"
- "model" "props/de_nuke/crate_small.mdl"
- }
- "PORTAL_5_ENTRACE"
- {
- "origin" "-8970.968750 -8521.311523 -307.968750"
- "type" "_portal"
- "offset" "-9147.531250 -8549.711914 -307.968750"
- "scale" "5 50 20"
- }
- "PORTAL5_EXIT"
- {
-
- "origin" "-9072.031250 -8532.894531 -307.968750"
- "type" "_portal"
- "offset" "-8942.968750 -8528.318359 -330.968750"
- "scale" "20 100 100"
- }
- "BRIDGE_COLLIDE"
- {
- "origin" "-8402.264648 -8553.781250 -400.065338"
- "scale" "400 40 5"
- "type" "env_physics_blocker"
- }
- "WINDOW_BLOCK_PROP"
- {
- "origin" "-6125.968750 -6989.230957 -63.608612"
- "rotation" "0.000000 -177.974670 0.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles/army_truck.mdl"
- }
- "WINDOW_BLOCK"
- {
- "origin" "-6131.369140 -7206.083496 -17.568002"
- "scale" "50 200 200"
- "type" "env_physics_blocker"
- }
- "PIPE_HELPER"
- {
- "origin" "-8035.905273 -8131.730468 -481.552062"
- "scale" "20 80 10"
- "type" "env_physics_blocker"
- }
- }
- "inputs"
- {
- "sound_alarm" "Kill"
- "sound_floodgate_loop" "Kill"
- "watergate_bottom" "BlockNav"
- "gate_push_01" "Disable"
- "gate_blocker" "Disable"
- "gate_collide" "Enable"
- "watergate" "UnblockNav"
- }
- }
- "c14m1_junkyard"
- {
- "ents"
- {
- "C"
- {
- "origin" "-1101.485352 -4729.446777 -109.968750"
- "scale" "25.0 25.00 30.0"
- "type" "env_physics_blocker"
- }
- "A_PROP"
- {
- "origin" "-1100.091553 -4585 -300.641998"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 90 0"
- }
- "B"
- {
- "origin" "-4259.611328 2193.898682 119.894897"
- "scale" "55.0 55.00 100.0"
- "type" "env_physics_blocker"
- }
- }
- }
- "c1m1_hotel"
- {
- "buttons" "no"
- "ents"
- {
- "A"
- {
- "origin" "2164.177246 5723.599609 2526.031250"
- "scale" "60 20 50"
- "type" "env_physics_blocker"
- }
- }
- "inputs"
- {
- "elevator_1_door2" "close"
- "elevator_1_door1" "close"
- }
- }
- "c2m2_fairgrounds"
- {
- "sets"
- {
- "rides" {
- "spawnpoint" "362.696716 -268.582977 -16.631347"
- }
- }
- "ents"
- {
- "END_STOP"
- {
- "origin" "495.141967 -279.631744 0.031250"
- "scale" "5.000000 220.000000 200.000000"
- "type" "env_physics_blocker"
- }
- "END_PROP"
- {
- "origin" "474.110717 -191.851898 -3.251148"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "END_PROP_2"
- {
- "origin" "474.110717 -323.851898 -3.251148"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "STAIRS"
- {
- "type" "prop_dynamic"
- "origin" "1951.127319 -317.584213 5.031250"
- "rotation" "0.843326 90.637062 0.000000"
- "model" "props_exteriors/wood_stairs_120.mdl"
- }
- "HELPER"
- {
- "origin" "2108.085937 -463.634277 160.352081"
- "rotation" "-26.078588 -90.851776 0.000000"
- "type" "prop_dynamic"
- "model" "props_junk/dumpster.mdl"
- }
- "HEDGE_SHOWER"
- {
- "set" "rides"
- "origin" "-3043.151611 -5329.417480 -63.968750"
- "rotation" "0.000000 1.148354 0.000000"
- "type" "prop_dynamic"
- "model" "props_junk/dumpster_2.mdl"
- }
- "NO_SAFEROOM"
- {
- "set" "rides"
- "origin" "-3926.420166 -5486.845214 -66.968750"
- "rotation" "0.000000 -11.019555 0.000000"
- "type" "prop_dynamic"
- "model" "props_fairgrounds\kiddyland_ridestructure_closed.mdl"
- }
- "binning_fence_explainer"
- {
- "set" "rides"
- "origin" "-1559.084960 -1815.552001 128.996826"
- "rotation" "0.185535 -60.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "binning_fence_explainer_inner"
- {
- "set" "rides"
- "origin" "-702.269836 -2566.092041 128.031250"
- "rotation" "0.185535 -90.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\police_barrier001_128_reference.mdl"
- }
- "binning_fence_explainer_innerb"
- {
- "set" "rides"
- "origin" "-830.394409 -2502.136230 128.031250"
- "rotation" "0.185535 -119.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\police_barrier001_128_reference.mdl"
- }
- "binning_fence_explainer_innerc"
- {
- "set" "rides"
- "origin" "-572.269836 -2536.092041 128.031250"
- "rotation" "0.185535 -80.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\police_barrier001_128_reference.mdl"
- }
- "shelf"
- {
- "set" "rides"
- "origin" "-3134.213378 -336.267608 128.031250"
- "rotation" "0.581893 89.038597 0.000000"
- "type" "prop_dynamic"
- "model" "props\cs_office\shelves_metal.mdl"
- }
- "HEDGE_SHOWER"
- {
- "set" "rides_short"
- "origin" "-3043.151611 -5329.417480 -63.968750"
- "rotation" "0.000000 1.148354 0.000000"
- "type" "prop_dynamic"
- "model" "props_junk/dumpster_2.mdl"
- }
- "NO_SAFEROOM"
- {
- "set" "rides_short"
- "origin" "-3926.420166 -5486.845214 -66.968750"
- "rotation" "0.000000 -11.019555 0.000000"
- "type" "prop_dynamic"
- "model" "props_fairgrounds\kiddyland_ridestructure_closed.mdl"
- }
- "binning_fence_explainer"
- {
- "set" "rides_short"
- "origin" "-1559.084960 -1815.552001 128.996826"
- "rotation" "0.185535 -60.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "binning_fence_explainer_inner"
- {
- "set" "rides_short"
- "origin" "-702.269836 -2566.092041 128.031250"
- "rotation" "0.185535 -90.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\police_barrier001_128_reference.mdl"
- }
- "binning_fence_explainer_innerb"
- {
- "set" "rides_short"
- "origin" "-830.394409 -2502.136230 128.031250"
- "rotation" "0.185535 -119.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\police_barrier001_128_reference.mdl"
- }
- "binning_fence_explainer_innerc"
- {
- "set" "rides_short"
- "origin" "-572.269836 -2536.092041 128.031250"
- "rotation" "0.185535 -80.982719 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications\police_barrier001_128_reference.mdl"
- }
- "shelf"
- {
- "set" "rides_short"
- "origin" "-3134.213378 -336.267608 128.031250"
- "rotation" "0.581893 89.038597 0.000000"
- "type" "prop_dynamic"
- "model" "props\cs_office\shelves_metal.mdl"
- }
- "short_bottom_block"
- {
- "origin" "-2300.978027 -3836.044189 -128.968750"
- "rotation" "0 -90 0"
- "type" "prop_dynamic"
- "model" "props_urban\fence_gate001_256.mdl"
- "set" "rides_short"
- }
- "short_bottom_block2"
- {
- "origin" "-2394.099365 -3841.655029 -126.968750"
- "rotation" "0 -90 0"
- "type" "prop_dynamic"
- "model" "props_urban\fence_cover001_64.mdl"
- "set" "rides_short"
- }
- "short_top_block"
- {
- "origin" "-2496.688232 -3847.620117 32.031250"
- "rotation" "0 -90 0"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- "set" "rides_short"
- }
- "stairs_trains"
- {
- "origin" "-600.644897 520.514068 -82.156204"
- "rotation" "0.000000 -90.962295 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors\wood_stairs_120.mdl"
- }
- "stairs_trains2"
- {
- "origin" "-600.644897 665.611511 20.675038"
- "rotation" "0.000000 -90.962295 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors\wood_stairs_120.mdl"
- }
- "train_pole"
- {
- "origin" "-2723.161621 -784.884704 134.031250"
- "rotation" "80.000000 172.499984 0.000000"
- "type" "prop_dynamic"
- "model" "props_c17\utilitypole01d.mdl"
- }
- }
- "inputs"
- {
- "coop_prop" "kill"
- "anv_mapfixes_dispcollision_fairgrounds" "Kill"
- "func_breakable" "Break"
- "anv_mapfixes_cliprework_beanbinninga" "Kill"
- "coop_blocker" "Kill"
- "!3749406" "Kill"
- "!3762835" "Kill"
- "tol_clip_brush" "Kill"
- }
- }
- "c2m3_coaster"
- {
- "spawnpoint" "2829.782958 2042.384155 21.309265"
- "defaultset" "large"
- "ents"
- {
- "SAFEROOM_BLOCK_PROP"
- {
- "origin" "3012.181884 2051.005371 60.447525"
- "rotation" "0.000000 -175.885253 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "VENT_SUS_1"
- {
- "origin" "437.521789 3700.031250 208.031250"
- "rotation" "0.000000 -151.672363 0.000000"
- "type" "prop_dynamic"
- "model" "props_foliage\flower_barrel.mdl"
- }
- "VENT_SUS_2"
- {
- "origin" "179.974365 3701.934082 218.263107"
- "rotation" "0.000000 93.777389 0.000000"
- "type" "prop_dynamic"
- "model" "props_waterfront\footlocker01.mdl"
- }
- "COASTER_STOP"
- {
- "origin" "-1971.579467 2021.431396 0.031250"
- "rotation" "0.000000 -168.560409 0.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles\deliveryvan_armored.mdl"
- }
- "COASTER_STOP_BLOCKER"
- {
- "origin" "-1816.031250 2094.495849 18.641880"
- "type" "env_physics_blocker"
- "scale" "20 20 100"
- }
- "END_PORTAL_INBOUNDS"
- {
- "origin" "-111.468742 3868.442138 -30.968750"
- "type" "_portal_xen"
- "offset" "-218.711853 3872.825927 -16.254243"
- }
- "END_PORTAL_OUTBOUNDS"
- {
- "origin" "-175.322891 3874.135742 -25.896236"
- "type" "_portal_xen"
- "offset" "-6.273283 3868.246337 -39.968750"
- }
- "ENT_PORTAL_OUTBOUNDS"
- {
- "origin" "781.968750 5089.447265 -12.105030"
- "type" "_portal_xen"
- "offset" "848.368774 4996.333496 44.031250"
- }
- "ENT_PORTAL_INBOUNDS"
- {
- "origin" "818.531250 5120.259765 5.968750"
- "type" "_portal_xen"
- "offset" "693.952026 5122.255859 2.038572"
- "set" "large"
- }
- "NO_JUMP_HOLE_PROP"
- {
- "origin" "298.292388 3772.014892 200.059875"
- "rotation" "-84.000000 90.560409 -10.000000"
- "type" "prop_dynamic"
- "model" "props\cs_militia\sheetrock_leaning.mdl"
- "set" "short"
- }
- "NO_JUMP_WALL_PROP"
- {
- "origin" "-60.321926 3621.998046 208.031250"
- "rotation" "0.000000 177.283248 0.000000"
- "type" "prop_dynamic"
- "model" "props\cs_assault\box_stack1.mdl"
- "set" "short"
- }
- }
- "inputs"
- {
- "finale_alarm_sound" "Kill"
- "relay_start_onslaught" "Trigger"
- "prop_gate_coaster" "Kill"
- "navblock_coop" "Kill"
- "func_detail_blocker" "Kill"
- "brushes_coop" "Kill"
- "func_brush" "Kill"
- "breakwall" "Break"
- }
- }
- "c2m4_barns"
- {
- "ents"
- {
- "gate_stop"
- {
- "origin" "-2251.308349 1434.023315 -255.968750"
- "scale" "20 100 100"
- }
- "lol"
- {
- "origin" "-255.596389 712.626953 -205.968750"
- "rotation" "0.000000 180.269416 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_teenangst.mdl"
- }
- }
- "inputs"
- {
- "brushes_coop" "Kill"
- "clips_coop" "Kill"
- "navblock_coop" "Kill"
- "props_coop" "Kill"
- "minifinale_gates_door1" "Lock"
- "minifinale_gates_door1" "Close"
- "minifinale_gates_door2" "Lock"
- "minifinale_gates_door2" "Close"
- "minifinale_gates_door3" "Lock"
- "minifinale_gates_door3" "Close"
- "minifinale_gates_door4" "Lock"
- "minifinale_gates_door4" "Close"
- "minifinale_gates_door5" "Lock"
- "minifinale_gates_door5" "Close"
- "minifinale_gates_door6" "Lock"
- "minifinale_gates_door6" "Close"
- }
- }
- "c1m2_streets"
- {
- "climbing" "off"
- "sets"
- {
- "cola"
- {
- "spawnpoint" "-5427.254882 938.835754 568.031250"
- }
- }
- "ents"
- {
- "A_PROP"
- {
- "origin" "-2536 2258 0"
- "type" "prop_dynamic"
- "model" "props_junk/dumpster.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2"
- {
- "origin" "-2536 2418 0"
- "type" "prop_dynamic"
- "model" "props_junk/dumpster.mdl"
- "rotation" "0 0 0"
- }
- "B_PROP"
- {
- "origin" "-2305 2159 0"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "B_PROP2"
- {
- "origin" "-2305 2159 40"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "B_PROP3"
- {
- "origin" "-2305 2200 0"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "C_PROP"
- {
- "origin" "-4377.309570 1749.962646 80.593964"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 10 0"
- }
- "C_PROP2"
- {
- "origin" " -4383.500488 2161.401611 226.530548"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 10 0"
- }
- "C_PROP2"
- {
- "origin" " -4383.500488 2161.401611 266.530548"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 10 0"
- }
- "C_PROP2"
- {
- "origin" " -4383.500488 2121.401611 226.530548"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 10 0"
- }
- "D_Prop"
- {
- "origin" "-5170.352051 870.512817 465.251526"
- "type" "prop_dynamic"
- "model" "props_street/police_barricade.mdl"
- "rotation" "0 180 0"
- }
- "D"
- {
- "origin" "-5180.282227 905.176453 540.750977"
- "scale" "80 70 80"
- }
- "E_Prop"
- {
- "origin" "1399.475464 2483.173096 572.186157"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "E_Prop2"
- {
- "origin" "1315.670288 2500.615234 670.690247"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "BUSH_BLOCK"
- {
- "origin" "800.609313 2733.161376 695.719726"
- "scale" "190 50 100"
- }
- "G_Prop"
- {
- "origin" "-2219.452637 1219.065308 2.395943"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 90 0"
- }
- "cola_replace"
- {
- "origin" "-7376.00 -1338.00 395.00"
- "type" "prop_physics_multiplayer"
- "model" "w_models/weapons/w_cola.mdl"
- "rotation" "0 0 0"
- }
- "fence"
- {
- "origin" "-5458.898437 823.603088 610.031250"
- "rotation" "0.000000 -89.520973 0.000000"
- "type" "prop_dynamic"
- "model" "props_urban\fence002_128.mdl"
- }
- "fence_stop"
- {
- "origin" "-5250.609375 824.685974 746.281250"
- "scale" "60 10 80"
- }
- "lol"
- {
- "origin" "-7634.699218 -373.631134 752.381225"
- "rotation" "90.000000 -90.269416 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_teenangst.mdl"
- }
- "tug"
- {
- "origin" "-5836.837890 2780.815917 391.636169"
- "rotation" "0.000000 -90.999050 -15.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles\boat_rescue_tug.mdl"
- }
- }
- "inputs"
- {
- "store_door01_dynamic" "open"
- "store_door02_dynamic" "open"
- "store_alarm_generic" "StopSound"
- "store_alarm_generic" "Kill"
- "cola" "Kill"
- "!100452" "_setteam"
- "InstanceAuto36-gunshop.ent" "Disable"
- "InstanceAuto36-stairwell.ent" "Disable"
- "InstanceAuto36-stairwell.ent" "Disable"
- "stripmall_door1_breakable" "Break"
- "stripmall_door2_breakable" "Break"
- "tanker_destroy_relay" "Trigger"
- }
- }
- "c1m4_atrium"
- {
- "spawnpoint" "-4440.582519 -3930.253417 136.031250"
- "ents"
- {
- "exitportalwarp"
- {
- "origin" "-4445.884277 -2288.304687 30.806552"
- "scale" "40 65 80"
- "offset" "0 60 0"
- "type" "_relportal"
- }
- "lol"
- {
- "origin" "-5760.802246 -3997.028320 1024.031250"
- "rotation" "0.608940 26.964090 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_teenangst.mdl"
- }
- "lol2"
- {
- "origin" "-3363.302001 -4017.320068 744.781250"
- "rotation" "0.608940 159.964090 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_namvet.mdl"
- }
- "lol3"
- {
- "origin" "-5184.031250 -2790.462402 280.031250"
- "rotation" "0.000000 0.649614 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_biker.mdl"
- }
- "lol4"
- {
- "origin" "-3024.509521 -4495.127929 536.031250"
- "rotation" "0.000000 123.649614 0.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_manager.mdl"
- }
- "lol4_block"
- {
- "origin" "-3024.509521 -4495.127929 536.031250"
- "scale" "40 25 100"
- }
- "cheater_warp"
- {
- "origin" "-3295.968750 -4299.645019 650.031250"
- "type" "_portal"
- "scale" "40 25 10"
- "offset" "-3358.754394 -4298.374511 640"
- }
- "gnome"
- {
- "origin" "-4735.599609 -3408.180175 86.600860"
- "rotation" "59.756366 -110.296585 0.000000"
- "type" "prop_physics_multiplayer"
- "model" "props_junk/gnome.mdl"
- }
- }
- "inputs"
- {
- "anv_mapfixes_collision_atrium" "Kill"
- "elevator" "SetMaxSpeed 1000"
- "elevator" "MoveToFloor bottom"
- "event_elevator_deny" "Kill"
- "exitdoor_portal" "Trigger"
- "fx_intro" "Kill"
- "fade_outro_3" "Kill"
- "camera_outro_wide" "Disable"
- "camera_outro_wide" "Kill"
- "camera_outro_tele" "Disable"
- "camera_outro_tele" "Kill"
- "trigger_finale" "Kill"
- "trigger_finale" "Kill"
- "escape_3" "Enable"
- "escape_4" "Enable"
- "escape_2" "Enable"
- "escape_6" "Enable"
- "escape_7" "Enable"
- "escape_1" "Enable"
- "env_player_blocker" "Kill"
- "env_physics_blocker" "Kill"
- "gas_nozzle" "Kill"
- "relay_outro_endseq" "Trigger"
- "cam_selector_2" "Trigger"
- "exitdoor_portal" "Open"
- "mall_directory" "Enable"
- "charger_hurt" "Kill"
- }
- }
- "c8m5_rooftop"
- {
- "ents"
- {
- "A_PROP"
- {
- "origin" "7255 8925 6085"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2"
- {
- "origin" "7255 8925 6125"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP3"
- {
- "origin" "7300 8925 6085"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- }
- }
- "c8m2_subway"
- {
- "ents"
- {
- "A_PROP"
- {
- "origin" "7498 3020 18"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2"
- {
- "origin" "7342 3020 130"
- "type" "prop_dynamic"
- "model" "props_exteriors/wood_stairs_120.mdl"
- "rotation" "0 0 0"
- }
- "C"
- {
- "origin" "8670 4958 200"
- "scale" "50 700 200"
- "type" "env_physics_blocker"
- }
- "B_PROP"
- {
- "origin" "8480 4970 15"
- "type" "prop_dynamic"
- "model" "props_vehicles/semi_trailer_wrecked.mdl"
- "rotation" "0 -25 -2"
- }
- "B_PROP2"
- {
- "origin" "8595 5355 12"
- "type" "prop_dynamic"
- "model" "props_vehicles/taxi_rural.mdl"
- "rotation" "0 -25 3"
- }
- "B_PROP3"
- {
- "origin" "8393 4642 12"
- "type" "prop_dynamic"
- "model" "props_junk/trashcluster01b_corner.mdl"
- "rotation" "0 0 0"
- }
- }
- }
- "c11m3_garage"
- {
- "ents"
- {
- "A_PROP"
- {
- "origin" "-6055 -2213 12"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2"
- {
- "origin" "-6055 -2213 52"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP3"
- {
- "origin" "-6055 -2213 92"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROPB"
- {
- "origin" "-6055 -2253 12"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2B"
- {
- "origin" "-6055 -2253 52"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROPC"
- {
- "origin" "-6095 -2213 12"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2C"
- {
- "origin" "-6095 -2213 52"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "A_PROP2D"
- {
- "origin" "-6095 -2253 12"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- "rotation" "0 0 0"
- }
- "B_PROP"
- {
- "origin" "-2326.5 2005 29"
- "type" "prop_dynamic"
- "model" "props_vehicles/taxi_rural.mdl"
- "rotation" "0 180 -10"
- }
- "B"
- {
- "origin" "-2396.5 2005 29"
- "scale" "100 5 150"
- "type" "env_physics_blocker"
- }
- "C_PROP2"
- {
- "origin" "-2165.216797 1856.723877 16"
- "rotation" "0 0 0 "
- "type" "prop_dynamic"
- "model" "props_junk/dumpster_2.mdl"
- }
- "C_PROP3"
- {
- "origin" "-2165.216797 1946.723877 16"
- "rotation" "0 0 0 "
- "type" "prop_dynamic"
- "model" "props_junk/dumpster_2.mdl"
- }
- "C"
- {
- "origin" "-2210.216797 1900.723877 35"
- "scale" "15 100 100"
- "type" "env_physics_blocker"
- }
- "gnome"
- {
-
- "origin" "-5489.493164 1160.628540 36.031250"
- "rotation" "0.000000 90.606277 0.000000"
- "type" "prop_physics_multiplayer"
- "model" "props_junk/gnome.mdl"
- }
- }
- "inputs"
- {
- "construction_clip" "kill"
- "fuel_barricade" "kill"
- "barricade_gas_can" "kill"
- "fuel_barricade_break1" "kill"
- "fuel_barricade_break2" "kill"
- "fuel_barricade_break3" "kill"
- "door_exitl" "Lock"
- "door_exitr" "Lock"
- }
- }
- "c11m4_terminal"
- {
- "spawnpoint" "-482.813659 3566.486572 296.031250"
- "sets"
- {
- "terminal"
- {
- "spawnpoint" "1811.834716 1570.712402 264.607604"
- }
- }
- "ents"
- {
- "BAG_HOLE_A"
- {
- "origin" "382.787200 2708.387207 40.031250"
- "rotation" "0.000000 -174.077285 0.000000"
- "type" "prop_dynamic"
- "model" "props_unique/airport/luggage_pile1.mdl"
- }
- "BAG_HOLE_B"
- {
- "origin" "402.307434 2415.856201 40.031250"
- "rotation" "0.000000 -174.077285 0.000000"
- "type" "prop_dynamic"
- "model" "props_unique/airport/luggage_pile1.mdl"
- }
- "BAG_HOLE_C"
- {
- "origin" "401.137786 2110.304199 40.031250"
- "rotation" "0.000000 -174.077285 0.000000"
- "type" "prop_dynamic"
- "model" "props_unique/airport/luggage_pile1.mdl"
- }
- "DETECTOR_NO_DETECT"
- {
- "origin" "1023.898742 1750.977294 16.031250"
- "scale" "20 650 300"
- "set" "terminal"
- }
- "DETECTOR_PROP"
- {
- "origin" "1030.929931 1755.157714 37.031250"
- "rotation" "90 0 0"
- "type" "prop_dynamic"
- "model" "props_interiors\chairs_airport.mdl"
- "set" "terminal"
- }
- "lol"
- {
- "origin" "2350.819335 1851.536376 152.235565"
- "rotation" "10.137485 -178.424728 15.000000"
- "type" "prop_dynamic"
- "model" "survivors/survivor_teenangst.mdl"
- "set" "terminal"
- }
- }
- "inputs"
- {
- "van_blocker" "Kill"
- "van_start_relay" "Trigger"
- "!3098419" "Kill"
- "breakwall01" "Break"
- "func_breakable" "Kill"
- "breakwall01_illusionary" "Kill"
- "!6802112" "Open"
- "!6765323" "Kill"
- "!6765365" "Kill"
- "securityalarmlight1" "TurnOn"
- "securityspotlight1" "LightOn"
- "securityrotator1" "StopSound"
- "securityalarmbase1" "Skin 1"
- "securityalarmsprite1" "ShowSprite"
- "alarm_safety_relay" "Kill"
- "env_soundscape" "Kill"
- }
- }
- "c12m1_hilltop"
- {
- "sets"
- {
- "A"
- {
- "spawnpoint" "-6506.025878 -6803.962402 377.974670"
- }
- }
- }
- "c12m4_barn"
- {
- "spawnpoint" "10523.914062 -6057.256347 -63.968750"
- "ents"
- {
- "BRIDGE_STOP"
- {
- "origin" "10485.244140 -3690.727294 -60.199607"
- "rotation" "0.000000 -87.237983 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/barricade001_128_reference.mdl"
- }
- "BRIDGE_STOP2"
- {
- "origin" "10403.553710 -3663.791503 -63.968750"
- "rotation" "0.000000 89.303184 0.000000"
- "type" "prop_dynamic"
- "model" "props_street/police_barricade.mdl"
- }
- "BRIDGE_BLOCK"
- {
- "origin" "10463 -3604 -63"
- "scale" "120 100 200"
- "type" "env_physics_blocker"
- }
- "FENCE_SKIP_HELPER"
- {
- "origin" "11317.451171 -4665.616210 -365.052124"
- "rotation" "0.895275 90.865699 15.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles\deliveryvan_glass.mdl"
- }
- "FENCE_SKIP_HELPER"
- {
- "origin" "11317.451171 -4665.616210 -365.052124"
- "rotation" "0.895275 90.865699 15.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles\deliveryvan.mdl"
- }
- "FENCE_SKIP_HELPER_B"
- {
- "origin" "11358.471679 -4625.372070 -259.812072"
- "rotation" "15.114483 0.347351 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "FENCE_SKIP_HELPER_B2_TOP"
- {
- "origin" "11358.471679 -4625.372070 -220.812072"
- "rotation" "15.114483 0.347351 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "FENCE_SKIP_HELPER_B2_TOP2"
- {
- "origin" "11368.471679 -4625.372070 -170.812072"
- "rotation" "15.114483 0.347351 0.000000"
- "type" "prop_dynamic"
- "model" "props\cs_militia\footlocker01_closed.mdl"
- }
- "FENCE_SKIP_HELPER_B3"
- {
- "origin" "11318.471679 -4625.372070 -249.812072"
- "rotation" "15.114483 0.347351 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "FENCE_SKIP_EXIT_STAIRS"
- {
- "origin" "11307.600585 -4320.291992 -325.535629"
- "rotation" "0.901843 90.274330 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/stairs_house_01.mdl"
- }
- "FENCE_SKIP_EXIT_STAIRS_2"
- {
- "origin" "11306.600585 -4108.039062 -498.862701"
- "rotation" "0.901843 90.274330 0.000000"
- "type" "prop_dynamic"
- "model" "props_exteriors/stairs_house_01.mdl"
- }
- "FENCE_SKIP_EXIT_STAIRS_SUPPORT"
- {
-
- "origin" "11324.499023 -4397.913574 -469.508117"
- "rotation" "0 0 0"
- "type" "prop_dynamic"
- "model" "props_interiors\concretepillar01.mdl"
- }
- "FENCE_SKIP_EXIT_STAIRS_SUPPORT"
- {
- "origin" "11324.499023 -4397.913574 -341.508117"
- "rotation" "0 0 0"
- "type" "prop_dynamic"
- "model" "props_interiors\concretepillar01.mdl"
- }
- "MY_SKIP_SUPPORT"
- {
- "origin" "11070.869140 -5625.584960 -65.968738"
- "rotation" "0 0 0"
- "type" "prop_dynamic"
- "model" "props_swamp\plank001b_192.mdl"
- }
- "HOUSE_PLANKS"
- {
- "origin" "11070.869140 -5817.584960 -65.968738"
- "rotation" "0 0 0"
- "type" "prop_dynamic"
- "model" "props_swamp\plank001b_192.mdl"
- }
- "HOUSE_PLANKS"
- {
- "origin" "11070.869140 -6009.24960 -65.968738"
- "rotation" "0 0 0"
- "type" "prop_dynamic"
- "model" "props_swamp\plank001b_192.mdl"
- }
- "ROOF_ACCESS"
- {
- "origin" "10692.570312 -7545.357910 -76.285079"
- "rotation" "0.000000 -105.753463 0.000000"
- "type" "prop_dynamic"
- "model" "props\de_train\pallet_barrels.mdl"
- }
- "ROOF_ACCESS_TOP"
- {
- "origin" "10692.570312 -7545.357910 -26.285079"
- "rotation" "0.000000 -105.753463 0.000000"
- "type" "prop_dynamic"
- "model" "props\de_train\pallet_barrels.mdl"
- }
- "ROOF_ACCESS_2"
- {
- "origin" "10692.479492 -7470.440917 -76.285079"
- "rotation" "0.000000 -105.753463 0.000000"
- "type" "prop_dynamic"
- "model" "props\de_train\pallet_barrels.mdl"
- }
- "OUT_OF_BOUNDS_ROOF"
- {
- "origin" "10630.428710 -7433.587890 274.640625"
- "rotation" "86.179300 -90.960174 0.000000"
- "type" "prop_dynamic"
- "model" "props_c17\utilitypole01d.mdl"
- "set" "outbounds"
- }
- "ROOF_HELPER"
- {
- "origin" "10693.650390 -7695.398925 195.193389"
- "rotation" "54.007286 -84.799293 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "BLOCK_SPAWN"
- {
- "type" "env_physics_blocker"
- "origin" "10222.888671 -8366.800781 233.956771"
- "scale" "55 400 300"
- }
-
- "MYPROP"
- {
- "origin" "10693.677734 -7559.968750 25.075805"
- "rotation" "0.000000 0.139984 0.000000"
- "type" "_lantern"
- "model" "props_unique/spawn_apartment/lantern.mdl"
- }
- }
- }
- "c13m2_southpinestream"
- {
- "spawnpoint" "7472.512695 2728.908203 454.170593"
- "sets"
- {
- "A"
- {
- "spawnpoint" "631.415039 3729.930664 329.250244"
- }
- }
- "ents"
- {
- "A_HELPER"
- {
- "origin" "3266.013671 2010.126708 458.291046"
- "rotation" "0.000000 148.362182 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "FENCE_JUMPER"
- {
- "origin" "1832.684692 2264.726562 422.658752"
- "rotation" "0.000000 70.022712 -4.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles/cement_truck01.mdl"
- }
- "RETURNER"
- {
- "origin" "539.416015 2654.438476 177.077484"
- "rotation" "-30.582356 8.396564 0.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles/airplane_piperwreck.mdl"
- }
- "RETURNER_SUPPORT_A"
- {
- "origin" "630.816955 2635.709228 196.630294"
- "rotation" "-8.000000 120.049987 -32.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles/car005a.mdl"
- }
- "RETURNER_SUPPORT_B"
- {
- "origin" "539.403564 2561.882568 129.099700"
- "rotation" "-30.000000 32.022949 0.000000"
- "type" "prop_dynamic"
- "model" "props_vehicles/car001a_phy.mdl"
- }
- "WALL"
- {
- "origin" "-837.842651 5280.024414 272.031250"
- "rotation" "0.202026 -89.108253 0.000000"
- "type" "prop_dynamic"
- "model" "props_fortifications/concrete_wall001_96_reference.mdl"
- }
- "WALL_BLOCK"
- {
- "origin" "-837.842651 5280.024414 272.031250"
- "type" "env_physics_blocker"
- "scale" "66 26 200"
- }
- "RETURN_A"
- {
- "origin" "3874.732421 1896.033325 405.846710"
- "rotation" "0.000000 37.927692 0.000000"
- "type" "prop_dynamic"
- "model" "props_crates/static_crate_40.mdl"
- }
- "SPAWN_BLOCK_PROP"
- {
- "origin" "7883.347167 3302.165527 534.600952"
- "rotation" "0.000000 -5.696259 0.000000"
- "type" "prop_dynamic"
- "model" "props_wasteland/rock_cliff01.mdl"
- }
- "SPAWN_BLOCK"
- {
-
- "origin" "7848.883300 3287.961914 794.680236"
- "type" "env_physics_blocker"
- "scale" "900 900 900"
- }
- "TANKER_SKIP"
- {
- "origin" "443.717864 4325.977050 497.610931"
- "type" "env_physics_blocker"
- "scale" "100 100 150"
- }
- "TANKER_SKIP_2"
- {
- "origin" "673.124267 4100.726562 558.114318"
- "type" "env_physics_blocker"
- "scale" "100 100 150"
- }
- }
- }
+"hideandseek"
+{
+ "c4m1_milltown_a"
+ {
+ "spawnpoint" "-3904.694580 7205.333984 199.836639"
+ "ents"
+ {
+ "TRUCK_SKIP"
+ {
+ "origin" "-3369.870117 7448.359375 96.031250"
+ "rotation" "0.000000 24.135879 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "BARRICADE_1"
+ {
+ "origin" "2074.847167 2896.825439 96.031250"
+ "rotation" "0.000000 175.428447 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "BARRICADE_2"
+ {
+ "origin" "2065.847167 2779.825439 96.031250"
+ "rotation" "0.000000 175.428447 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "BARRICADE_3"
+ {
+ "origin" "2071 2670 96.031250"
+ "rotation" "0.000000 175.428447 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "DOOR_BLOCK"
+ {
+ "origin" "2000.038940 2530.103759 104.031250"
+ "rotation" "90.000000 180.681259 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_mill/wood_stack.mdl"
+ }
+ "BARRICADE_SIDE"
+ {
+ "origin" "1938.229248 2108.860839 96.353591"
+ "rotation" "0.000000 175.428447 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "HOUSE_BLOCK_1"
+ {
+ "origin" "1917.533691 2347.451416 104.031250"
+ "rotation" "0.000000 0.041839 0.000000"
+ "type" "prop_dynamic"
+ "model" "props/cs_office/bookshelf2.mdl"
+ }
+ "HOUSE_BLOCK_2"
+ {
+ "origin" "1917.533691 2247.451416 104.031250"
+ "rotation" "0.000000 0.041839 0.000000"
+ "type" "prop_dynamic"
+ "model" "props/cs_office/bookshelf2.mdl"
+ }
+ "TREEHOUSE_BLOCKER"
+ {
+ "origin" "2072.242675 3038.970703 206.251251"
+ "scale" "5.000000 1500.000000 400.000000"
+ "type" "env_physics_blocker"
+ }
+ "ENTRANCE_STOPPER"
+ {
+ "origin" "-4057.503173 7158.272949 201.491455"
+ "scale" "5.000000 2500.000000 800.000000"
+ "type" "env_physics_blocker"
+ }
+
+ }
+ }
+ "c5m4_quarter"
+ {
+ "spawnpoint" "-3663.735351 3156.915039 64.031250"
+ "sets"
+ {
+ "block"
+ {
+ "spawnpoint" "-1322.446411 2238.070800 72.031250"
+ }
+ "block_long"
+ {
+ "spawnpoint" "-1213.040527 325.299438 176.126968"
+ }
+ }
+ "ents"
+ {
+ "END_BLOCK"
+ {
+ "origin" "-1534.599975 -1606.185058 256.031250"
+ "rotation" "0.000000 -177.669448 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_urban/fridge001.mdl"
+ }
+ "END_BLOCK_2"
+ {
+ "origin" "-1802.811401 -1206.369750 260.031250"
+ "type" "env_physics_blocker"
+ "scale" "100 100 50"
+ }
+ "BEGIN_BLOCK"
+ {
+ "origin" "-1548.047607 2458.536437 60.044128"
+ "scale" "1600.000000 10.000000 400.000000"
+ "type" "env_physics_blocker"
+ "set" "block"
+ }
+ "STAIRS"
+ {
+ "origin" "-1118.047607 915.536437 244.044128"
+ "rotation" "0.000000 180.843437 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/stairs_house_01.mdl"
+ }
+ "GATE_TP"
+ {
+ "origin" "-1630.467895 -396.031250 184.031250"
+ "type" "_portal"
+ "offset" "-1623.645263 -332.286193 135.869384"
+ "scale" "10 10 10"
+ }
+ "PORTAL_1"
+ {
+ "origin" "-1446.877319 -358.595733 203.116073"
+ "type" "_relportal"
+ "offset" "0.000000 10.000000 0.000000"
+ "scale" "100 40 50"
+ }
+ "PORTAL_1_PROPA"
+ {
+ "origin" "-1575.466674 -420.767852 65.182296"
+ "rotation" "0.000000 180.843437 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/stairs_house_01.mdl"
+ }
+ "PORTAL_1_PROPB"
+ {
+ "origin" "-1575.466674 -310.767852 65.182296"
+ "rotation" "0.000000 180.843437 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/stairs_house_01.mdl"
+ }
+ "FLAT_BLOCK"
+ {
+ "origin" "-3188.373779 3200.747802 300.585388"
+ "scale" "500 400 50"
+ "type" "env_physics_blocker"
+ }
+ "FENCE_SKIP"
+ {
+ "origin" "-1555.620239 2514.195068 64.031250"
+ "rotation" "0.0 -71.973693 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "SHORT_BLOCK"
+ {
+ "origin" "-563.721069 1031.170043 143.387512"
+ "rotation" "62.338180 6.194050 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_interiors\couch.mdl"
+ "set" "block_long"
+ }
+ "SHORT_BLOCK_SUPPORT"
+ {
+
+ "origin" "-608.215881 1030.887817 96.031250"
+ "rotation" "0.530479 -82.875152 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "set" "block_long"
+ }
+ "SHORT_BLOCK_SUPPORT2"
+ {
+
+ "origin" "-608.215881 1030.887817 136.031250"
+ "rotation" "0.530479 -82.875152 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "set" "block_long"
+ }
+ "TRACTOR_EARLY_HELP"
+ {
+ "origin" "-1503.682861 539.312591 255.984069"
+ "rotation" "89.000000 0.671798 90.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors\fence002.mdl"
+ }
+ "GNOME"
+ {
+ "origin" "-3511.222656 2463.375488 432.031250"
+ "type" "prop_physics_multiplayer"
+ "model" "props_junk/gnome.mdl"
+ }
+ "lol"
+ {
+ "origin" "-2386.989013 1119.776489 80.031250"
+ "rotation" "0.000000 -86.269416 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_teenangst.mdl"
+ }
+ }
+ "inputs"
+ {
+ "float_tracktrain" "TeleportToPathTrack float_path2"
+ "float_final_nav_blocker" "BlockNav"
+ "float_music" "Kill"
+ "float_start_nav_blocker2" "Kill"
+ "float_start_nav_blocker3" "Kill"
+ "courtyard_spawn1_breakable" "Kill"
+ "courtyard_spawn2_breakable" "Kill"
+ }
+ }
+ "c8m3_sewers"
+ {
+ "ents"
+ {
+ "A"
+ {
+ "origin" "13265.965820 8547.057617 -250.7"
+ "type" "env_physics_blocker"
+ }
+ "B"
+ {
+ "origin" "14130.535156 8026.46386 -254.7"
+ "type" "env_physics_blocker"
+ }
+ "A_PROP"
+ {
+ "origin" "13265.965820 8497.057617 -240.7"
+ "type" "prop_dynamic"
+ "model" "props/cs_office/shelves_metal.mdl"
+ "rotation" "90 90 0"
+ }
+ "B_PROP"
+ {
+ "origin" "14130.535156 8026.46386 -254.7"
+ "type" "prop_dynamic"
+ "model" "props_swamp/river_sign01.mdl"
+ "rotation" "90 0 0"
+ }
+ "STAIR_HELP"
+ {
+ "origin" "11896.693359 7291.923828 143.372573"
+ "rotation" "0.000000 -90.646430 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ }
+ "ROOF_EXIT"
+ {
+ "origin" "11353 5861 204"
+ "rotation" "0 96 0"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "ROOF_FIX_FIXER"
+ {
+ "origin" "10579.599609 5120.623535 280.031250"
+ "scale" "20 1000 400"
+ "type" "env_physics_blocker"
+ }
+ "ROOF_FIX_FIXER_CORNER"
+ {
+ "origin" "10617.877929 5216.473144 280.031250"
+ "scale" "40 60 400"
+ "type" "env_physics_blocker"
+ }
+ "ROOF_NO_SNEAKY_JUMP"
+ {
+ "origin" "11127.424804 5662.353515 408.031250"
+ "scale" "20 300 500"
+ "type" "env_physics_blocker"
+ }
+ "ROOF_BRIDGE"
+ {
+ "origin" "12421.961914 4886.637207 568.031250"
+ "rotation" "-11.137193 -159.704940 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_unique\rope_bridge.mdl"
+
+ }
+ "ROOF_STOP"
+ {
+ "origin" "11895.786132 4616.688476 712.031250"
+ "rotation" "0.137193 0.704940 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_wasteland\coolingtank01.mdl"
+ }
+ "ROOF_STOP2"
+ {
+
+ "origin" "11641.057617 4630.232421 712.031250"
+ "rotation" "0.137193 0.704940 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_wasteland\coolingtank01.mdl"
+ }
+ "ROOF_STOP3"
+ {
+ "origin" "11432.921875 4660.313476 712.031250"
+ "rotation" "0.137193 0.704940 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_wasteland\coolingtank01.mdl"
+ }
+ "ROOF_STOP_WALL"
+ {
+ "origin" "11641.057617 4630.232421 712.031250"
+ "type" "env_physics_blocker"
+ "scale" "2000 90 600"
+ }
+ "ROOF_DEATH_PREVENT"
+ {
+ "origin" "12456.858398 4781.894042 568.031250"
+ "rotation" "0.000000 -131.235916 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\barricade001_128_reference.mdl"
+ }
+ "ROOF_DEATH_PREVENT_WALL"
+ {
+ "origin" "12310.758789 4753.980957 568.031250"
+ "type" "env_physics_blocker"
+ "scale" "120 60 400"
+ }
+ "ROOF_DEATH_PREVENT_PREVENTER"
+ {
+ "origin" "11994.040039 4736.263671 712.031250"
+ "rotation" "0.000000 -4.221647 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\barricade001_128_reference.mdl"
+ }
+ "STAIR_ACCESS"
+ {
+
+ "origin" "11100.574218 5208.395996 317.781250"
+ "rotation" "0.290893 -80.968573 32.000000"
+ "type" "prop_dynamic"
+ "model" "props_swamp\plank001b_192.mdl"
+ }
+ "GAS_STOP"
+ {
+ "origin" "12682.116210 5713.574218 568.031250"
+ "rotation" "0.000000 73.767333 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\barricade001_128_reference.mdl"
+ }
+ "GAS_STOP2"
+ {
+ "origin" "12580.110351 5731.003417 568.031250"
+ "rotation" "0.000000 73.767333 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\barricade001_128_reference.mdl"
+ }
+ "GAS_BLOCK"
+ {
+ "origin" "12812.621093 5929.395507 463.586090"
+ "type" "env_physics_blocker"
+ "scale" "300 1000 1000"
+ }
+ "NO_DIE"
+ {
+ "origin" "11323.022460 4793.813476 712.031250"
+ "rotation" "0.000000 -134.307327 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\barricade001_128_reference.mdl"
+ }
+ }
+ "inputs"
+ {
+ "anv_mapfixes_unattackable_wrongway1" "Kill"
+ }
+ }
+ "c3m1_plankcountry"
+ {
+ "spawnpoint" "-12070.341796 10517.305664 183.448410"
+ "sets"
+ {
+ "area2"
+ {
+ "spawnpoint" "-4240.322265 6052.394531 28.160285"
+ }
+ "end"
+ {
+ "spawnpoint" "-1030.998535 4821.037109 144.160293"
+ }
+ "large"
+ {
+ "spawnpoint" "-1030.998535 4821.037109 144.160293"
+ }
+ }
+ "ents"
+ {
+ "divider"
+ {
+ "origin" "-1023.450622 4939.198242 144.160293"
+ "rotation" "0.000000 -90.445228 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ "set" "area2"
+ }
+ "divider"
+ {
+ "origin" "-1023.450622 4939.198242 144.160293"
+ "rotation" "0.000000 -90.445228 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ "set" "end"
+ }
+ }
+ "inputs"
+ {
+ "func_breakable" "Break"
+ }
+ }
+ "c3m3_shantytown"
+ {
+ "sets"
+ {
+ "hut"
+ {
+ "spawnpoint" "-3914.085449 -3092.477050 62.502365"
+ }
+ "end-large"
+ {
+ "spawnpoint" "-557.272216 -2464.328125 4.269320"
+ "inputs"
+ {
+ "delete_coop_ents_postIO" "Trigger"
+ "bridge_minifinale" "Close"
+ "bridge_minifinale" "SetSpeed 200"
+ }
+ }
+ }
+ "ents"
+ {
+ "door_block"
+ {
+ "origin" "-4004.808593 -3011.982666 62.502365"
+ "rotation" "0 0 0"
+ "type" "prop_dynamic"
+ "model" "props_urban\fridge002.mdl"
+ }
+ "door_wall"
+ {
+ "origin" "-4000.808593 -3011.982666 62.502365"
+ "scale" "20 1050 300"
+ }
+ "door_block_tree"
+ {
+ "origin" "-4135.271972 -3837.605224 -0.291338"
+ "rotation" "-0.559620 173.978195 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_foliage\swamp_cypress_knee01.mdl"
+ }
+ "door_block_tree"
+ {
+ "origin" "-4116.226562 -3944.508056 0.005385"
+ "rotation" "-0.559620 173.978195 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_foliage\swamp_cypress_knee01.mdl"
+ }
+ "large_block"
+ {
+ "origin" "-1745.625976 -2745.440429 0.031250"
+ "set" "end-large"
+ "scale" "50 2000 400"
+ }
+ "large_block_prop"
+ {
+ "origin" "-1780.625976 -2880.440429 0.031250"
+ "type" "prop_dynamic"
+ "set" "end-large"
+ "rotation" "0 90 0"
+ "model" "props_debris\airliner_wreckage3.mdl"
+ }
+ "large_block_prop"
+ {
+ "origin" "-1573.762573 -3763.656494 -10.686201"
+ "type" "prop_dynamic"
+ "set" "end-large"
+ "rotation" "0 90 0"
+ "model" "props_debris\dead_cow_smallpile.mdl"
+ }
+ }
+ "inputs"
+ {
+ "bridge_button" "Kill"
+ }
+ }
+ "c3m4_plantation"
+ {
+ "sets"
+ {
+ "begin"
+ {
+
+ }
+ "mansion"
+ {
+ "climbing" "on"
+ "spawnpoint" "1656.207519 -120.207824 224.031250"
+ }
+ }
+ "defaultset" "begin"
+ "ents"
+ {
+ "A"
+ {
+ "origin" "2000.802612 -426.686829 402.803497"
+ "type" "env_physics_blocker"
+ "scale" "150.0 150.00 1.0"
+ "set" "begin"
+ }
+ "A_PROP"
+ {
+ "origin" "2000.802612 -426.686829 402.803497"
+ "type" "prop_dynamic"
+ "model" "props_swamp/boardwalk_384.mdl"
+ "rotation" "0 90 0"
+ "set" "begin"
+ }
+ "MANSION_ENT_RAIL"
+ {
+ "model" "props_exteriors/wood_railing004c.mdl"
+ "type" "prop_dynamic"
+ "origin" "2529.63 -804.10 642.56"
+ "angles" "0.00 178.84 0.00"
+ "set" "mansion"
+ }
+ "MANSION_ENT_BLOCK"
+ {
+ "origin" "2563.37 -818.00 366.03"
+ "type" "env_physics_blocker"
+ "scale" "441 10 400"
+ "set" "mansion"
+ }
+ "MANSION_ENT_GATE"
+ {
+ "model" "props_street/police_barricade_496in.mdl"
+ "origin" "1693.63 -1178.33 169.86"
+ "type" "prop_dynamic"
+ "set" "mansion"
+ }
+ }
+ "inputs"
+ {
+ "env_physics_blocker" "Kill"
+ "gate_explode_relay" "Trigger"
+ "prop_minigun" "Kill"
+ }
+ }
+ "c1m3_mall"
+ {
+ "sets"
+ {
+ "mid"
+ {
+ "spawnpoint" "1457.455078 -1059.256469 280.031250"
+ }
+ }
+ "defaultset" "begin"
+ "ents"
+ {
+ "A"
+ {
+ "origin" "1581.286865 -1029.394043 280.079254"
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster.mdl"
+ "rotation" "0 90 0"
+ }
+ "B"
+ {
+ "origin" "1714.133179 -1023.777527 347.735168"
+ "scale" "25.0 25.00 100.0"
+ "type" "env_physics_blocker"
+ "set" "begin"
+ }
+ "B"
+ {
+ "origin" "1714.133179 -1023.777527 347.735168"
+ "scale" "25.0 25.00 100.0"
+ "type" "env_physics_blocker"
+ "set" "mid"
+ }
+ "lol"
+ {
+ "origin" "2129.125976 -2346.968750 0.031250"
+ "rotation" "0.000000 39.811450 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_namvet.mdl"
+ }
+ "VENT_WARPER"
+ {
+ "origin" "1203.086791 -2000.914306 384.031250"
+ "type" "_portal"
+ "scale" "40 40 50"
+ "offset" "1159.986694 -2063.281738 456.013153"
+ "set" "mid"
+ }
+ "EXIT_BLOCK"
+ {
+ "origin" "1283.903686 -5341.520019 536.031250"
+ "rotation" "0.000000 0.968292 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_office\vending_machine.mdl"
+ "set" "mid"
+ }
+
+ }
+ "inputs"
+ {
+ "sound_alarm" "StopSound"
+ "sound_alarm" "Kill"
+ "blocker_stairwell" "Kill"
+ "hurt_stairwell" "Kill"
+ "door_stairwell" "Unlock"
+ "blocker_stairwell_clip" "Kill"
+ "door_hallway" "Kill"
+ "breakwall_bathroom_wall" "Break"
+ "breakble_glass_minifinale" "Break"
+ "env_player_blocker" "Kill"
+ "relay_elevator_path_02" "Trigger"
+ "soundscapes-hall" "StopSound"
+ "soundscapes-hall.entry" "StopSound"
+ }
+ }
+ "c6m2_bedlam"
+ {
+ "ents"
+ {
+ "A"
+ {
+ "origin" "349.935608 3156.456543 263.583710"
+ "scale" "100.0 50.00 300.0"
+ "type" "env_physics_blocker"
+ "set" "short"
+ }
+ "A_PROP"
+ {
+ "origin" "350.063263 3171.198730 198.247498"
+ "type" "prop_dynamic"
+ "model" "props_industrial/barrel_fuel.mdl"
+ "rotation" "0 -91 -0.000109"
+ "set" "short"
+ }
+ "B_PROP"
+ {
+ "origin" "1138.566650 1374.530884 -194.578400"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 180 0"
+ }
+ "B_PROP2"
+ {
+ "origin" "1363.922241 1381.682617 -194.578400"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 0 0"
+ }
+ "DOOR_BLOCK"
+ {
+ "origin" "1401.782592 4686.330566 -101.095458"
+ "type" "env_physics_blocker"
+ "scale" "60 20 100"
+ }
+ "DOOR_BLOCK_PROP"
+ {
+ "origin" "1401.782592 4686.330566 -160.095458"
+ "type" "prop_dynamic"
+ "model" "props\de_train\pallet_barrels.mdl"
+ "rotation" "0 -91 -0.000109"
+ }
+ "RETURN_HELPER"
+ {
+ "origin" "1251.872436 1316.629760 -78.262710"
+ "rotation" "0.000000 178.612152 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ }
+ "inputs"
+ {
+ "gate_1_alarm" "Kill"
+ "sound_alarm" "Kill"
+ "gate_2_alarm" "Kill"
+ }
+ }
+ "c7m1_docks"
+ {
+ "climbing" "off"
+ "spawnpoint" "11068.093750 -207.805831 -67.623802"
+ "ents"
+ {
+ "A_PROP"
+ {
+ "origin" "8469.404297 854.461121 14.257706"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 -90 0"
+ }
+ "B_PROP"
+ {
+ "origin" "3522.576416 647.513244 176.031250"
+ "rotation" "0.000000 -88.406250 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_doors/roll-up_door_full.mdl"
+ }
+ "B_PROP"
+ {
+ "origin" "3266.576416 647.513244 176.031250"
+ "rotation" "0.000000 -88.406250 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_doors/roll-up_door_full.mdl"
+ }
+ "DUMPSTER_EXIT"
+ {
+ "origin" "3604 263 149"
+ "model" "props_junk/dumpster_2.mdl"
+ "type" "prop_dynamic"
+ "rotation" "0 180 0"
+ }
+ "DUMPSTER_ENTER"
+ {
+ "origin" "3872.412841 588.284240 176.031250"
+ "model" "props_junk/dumpster_2.mdl"
+ "type" "prop_dynamic"
+ "rotation" "0 0 0"
+ }
+ "CRATE_SHORTCUT"
+ {
+ "origin" "5288.236328 596.502136 146.863418"
+ "rotation" "0.000000 178.612152 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "SPAWN_BLOCK_PROP"
+ {
+ "origin" "11404.483398 -227.757766 -63.968750"
+ "rotation" "0.966560 -176.921936 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "SPAWN_BLOCK"
+ {
+ "origin" "11404.483398 -227.757766 0.968750"
+ "scale" "20 1000 100"
+ "type" "env_physics_blocker"
+ }
+ "lol"
+ {
+ "origin" "7641.535644 2735.968750 240.031250"
+ "rotation" "8.966513 -31.319810 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_namvet.mdl"
+ }
+ }
+ "inputs"
+ {
+ "tankdoorin" "Open"
+ "tankdoorout" "Open"
+ "traincar_navblocker" "Kill"
+ "tankdoorin_button" "Kill"
+ "tankdoorout_button" "Kill"
+ "tank_door_clip" "Kill"
+ "tank_sound_timer" "Disable"
+ "delete_coop_ents_postIO" "Trigger"
+ "battlefield_cleared" "UnblockNav"
+ }
+ }
+ "c10m2_drainage"
+ {
+ "ents"
+ {
+ "TOP_HELPER"
+ {
+ "origin" "-9874.015625 -7637.003417 -467.968750"
+ "rotation" "0.000000 -90.964706 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_interiors/desk_executive.mdl"
+ }
+ "STAIRS"
+ {
+ "origin" "-8120.544433 -7200.660156 -561.968750"
+ "rotation" "0.000000 180.672305 -8.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ }
+ "BOX"
+ {
+ "origin" "-8713.857421 -7792.103027 -395.968750"
+ "rotation" "0.000000 90.578536 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "PORTAL1_ENT"
+ {
+ "origin" "-8713.857421 -7792.103027 -300.968750"
+ "type" "_relportal"
+ "offset" "0 150 50"
+ "scale" "60 30 5"
+ }
+ "PORTAL1_EXIT"
+ {
+
+ "origin" "-8698.404296 -7663.968750 -260.968750"
+ "type" "_portal"
+ "offset" "-8776.213867 -7811.048339 -395.968750"
+ "scale" "60 30 50"
+ }
+ "PORTAL2_EXIT"
+ {
+
+ "origin" "-9443.846679 -7276.223144 -307.968750"
+ "type" "_portal"
+ "offset" "-9520.279296 -7281.130859 -467.968750"
+ "scale" "20 100 100"
+ }
+ "PORTAL3_EXIT"
+ {
+
+ "origin" "-9867.969726 -6852.831054 -307.968750"
+ "type" "_portal"
+ "offset" "-9871.308593 -6921.020507 -467.968750"
+ "scale" "100 20 100"
+ }
+ "PORTAL4_EXIT"
+ {
+
+ "origin" "-8984.172851 -7881.412597 -307.968750"
+ "type" "_portal"
+ "offset" "-8928.278320 -7888.108886 -336.968750"
+ "scale" "20 100 100"
+ }
+ "FALL_BARRIER"
+ {
+ "origin" "-8830.380859 -8800.031250 -307.968750"
+ "rotation" "0.000000 94.068542 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors\fence002.mdl"
+ }
+ "PORTAL_5_ENTRANCE_PROP"
+ {
+ "origin" "-8932.521484 -8515.070312 -400.968750"
+ "rotation" "0.000000 -178.028656 0.000000"
+ "type" "prop_dynamic"
+ "model" "props/de_nuke/crate_small.mdl"
+ }
+ "PORTAL_5_ENTRACE"
+ {
+ "origin" "-8970.968750 -8521.311523 -307.968750"
+ "type" "_portal"
+ "offset" "-9147.531250 -8549.711914 -307.968750"
+ "scale" "5 50 20"
+ }
+ "PORTAL5_EXIT"
+ {
+
+ "origin" "-9072.031250 -8532.894531 -307.968750"
+ "type" "_portal"
+ "offset" "-8942.968750 -8528.318359 -330.968750"
+ "scale" "20 100 100"
+ }
+ "BRIDGE_COLLIDE"
+ {
+ "origin" "-8402.264648 -8553.781250 -400.065338"
+ "scale" "400 40 5"
+ "type" "env_physics_blocker"
+ }
+ "WINDOW_BLOCK_PROP"
+ {
+ "origin" "-6125.968750 -6989.230957 -63.608612"
+ "rotation" "0.000000 -177.974670 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/army_truck.mdl"
+ }
+ "WINDOW_BLOCK"
+ {
+ "origin" "-6131.369140 -7206.083496 -17.568002"
+ "scale" "50 200 200"
+ "type" "env_physics_blocker"
+ }
+ "PIPE_HELPER"
+ {
+ "origin" "-8035.905273 -8131.730468 -481.552062"
+ "scale" "20 80 10"
+ "type" "env_physics_blocker"
+ }
+ }
+ "inputs"
+ {
+ "sound_alarm" "Kill"
+ "sound_floodgate_loop" "Kill"
+ "watergate_bottom" "BlockNav"
+ "gate_push_01" "Disable"
+ "gate_blocker" "Disable"
+ "gate_collide" "Enable"
+ "watergate" "UnblockNav"
+ }
+ }
+ "c14m1_junkyard"
+ {
+ "ents"
+ {
+ "C"
+ {
+ "origin" "-1101.485352 -4729.446777 -109.968750"
+ "scale" "25.0 25.00 30.0"
+ "type" "env_physics_blocker"
+ }
+ "A_PROP"
+ {
+ "origin" "-1100.091553 -4585 -300.641998"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 90 0"
+ }
+ "B"
+ {
+ "origin" "-4259.611328 2193.898682 119.894897"
+ "scale" "55.0 55.00 100.0"
+ "type" "env_physics_blocker"
+ }
+ }
+ }
+ "c1m1_hotel"
+ {
+ "buttons" "no"
+ "ents"
+ {
+ "A"
+ {
+ "origin" "2164.177246 5723.599609 2526.031250"
+ "scale" "60 20 50"
+ "type" "env_physics_blocker"
+ }
+ }
+ "inputs"
+ {
+ "elevator_1_door2" "close"
+ "elevator_1_door1" "close"
+ }
+ }
+ "c2m2_fairgrounds"
+ {
+ "sets"
+ {
+ "rides" {
+ "spawnpoint" "362.696716 -268.582977 -16.631347"
+ }
+ }
+ "ents"
+ {
+ "END_STOP"
+ {
+ "origin" "495.141967 -279.631744 0.031250"
+ "scale" "5.000000 220.000000 200.000000"
+ "type" "env_physics_blocker"
+ }
+ "END_PROP"
+ {
+ "origin" "474.110717 -191.851898 -3.251148"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "END_PROP_2"
+ {
+ "origin" "474.110717 -323.851898 -3.251148"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "STAIRS"
+ {
+ "type" "prop_dynamic"
+ "origin" "1951.127319 -317.584213 5.031250"
+ "rotation" "0.843326 90.637062 0.000000"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ }
+ "HELPER"
+ {
+ "origin" "2108.085937 -463.634277 160.352081"
+ "rotation" "-26.078588 -90.851776 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster.mdl"
+ }
+ "HEDGE_SHOWER"
+ {
+ "set" "rides"
+ "origin" "-3043.151611 -5329.417480 -63.968750"
+ "rotation" "0.000000 1.148354 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster_2.mdl"
+ }
+ "NO_SAFEROOM"
+ {
+ "set" "rides"
+ "origin" "-3926.420166 -5486.845214 -66.968750"
+ "rotation" "0.000000 -11.019555 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fairgrounds\kiddyland_ridestructure_closed.mdl"
+ }
+ "binning_fence_explainer"
+ {
+ "set" "rides"
+ "origin" "-1559.084960 -1815.552001 128.996826"
+ "rotation" "0.185535 -60.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "binning_fence_explainer_inner"
+ {
+ "set" "rides"
+ "origin" "-702.269836 -2566.092041 128.031250"
+ "rotation" "0.185535 -90.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\police_barrier001_128_reference.mdl"
+ }
+ "binning_fence_explainer_innerb"
+ {
+ "set" "rides"
+ "origin" "-830.394409 -2502.136230 128.031250"
+ "rotation" "0.185535 -119.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\police_barrier001_128_reference.mdl"
+ }
+ "binning_fence_explainer_innerc"
+ {
+ "set" "rides"
+ "origin" "-572.269836 -2536.092041 128.031250"
+ "rotation" "0.185535 -80.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\police_barrier001_128_reference.mdl"
+ }
+ "shelf"
+ {
+ "set" "rides"
+ "origin" "-3134.213378 -336.267608 128.031250"
+ "rotation" "0.581893 89.038597 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_office\shelves_metal.mdl"
+ }
+ "HEDGE_SHOWER"
+ {
+ "set" "rides_short"
+ "origin" "-3043.151611 -5329.417480 -63.968750"
+ "rotation" "0.000000 1.148354 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster_2.mdl"
+ }
+ "NO_SAFEROOM"
+ {
+ "set" "rides_short"
+ "origin" "-3926.420166 -5486.845214 -66.968750"
+ "rotation" "0.000000 -11.019555 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fairgrounds\kiddyland_ridestructure_closed.mdl"
+ }
+ "binning_fence_explainer"
+ {
+ "set" "rides_short"
+ "origin" "-1559.084960 -1815.552001 128.996826"
+ "rotation" "0.185535 -60.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "binning_fence_explainer_inner"
+ {
+ "set" "rides_short"
+ "origin" "-702.269836 -2566.092041 128.031250"
+ "rotation" "0.185535 -90.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\police_barrier001_128_reference.mdl"
+ }
+ "binning_fence_explainer_innerb"
+ {
+ "set" "rides_short"
+ "origin" "-830.394409 -2502.136230 128.031250"
+ "rotation" "0.185535 -119.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\police_barrier001_128_reference.mdl"
+ }
+ "binning_fence_explainer_innerc"
+ {
+ "set" "rides_short"
+ "origin" "-572.269836 -2536.092041 128.031250"
+ "rotation" "0.185535 -80.982719 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications\police_barrier001_128_reference.mdl"
+ }
+ "shelf"
+ {
+ "set" "rides_short"
+ "origin" "-3134.213378 -336.267608 128.031250"
+ "rotation" "0.581893 89.038597 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_office\shelves_metal.mdl"
+ }
+ "short_bottom_block"
+ {
+ "origin" "-2300.978027 -3836.044189 -128.968750"
+ "rotation" "0 -90 0"
+ "type" "prop_dynamic"
+ "model" "props_urban\fence_gate001_256.mdl"
+ "set" "rides_short"
+ }
+ "short_bottom_block2"
+ {
+ "origin" "-2394.099365 -3841.655029 -126.968750"
+ "rotation" "0 -90 0"
+ "type" "prop_dynamic"
+ "model" "props_urban\fence_cover001_64.mdl"
+ "set" "rides_short"
+ }
+ "short_top_block"
+ {
+ "origin" "-2496.688232 -3847.620117 32.031250"
+ "rotation" "0 -90 0"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ "set" "rides_short"
+ }
+ "stairs_trains"
+ {
+ "origin" "-600.644897 520.514068 -82.156204"
+ "rotation" "0.000000 -90.962295 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors\wood_stairs_120.mdl"
+ }
+ "stairs_trains2"
+ {
+ "origin" "-600.644897 665.611511 20.675038"
+ "rotation" "0.000000 -90.962295 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors\wood_stairs_120.mdl"
+ }
+ "train_pole"
+ {
+ "origin" "-2723.161621 -784.884704 134.031250"
+ "rotation" "80.000000 172.499984 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_c17\utilitypole01d.mdl"
+ }
+ }
+ "inputs"
+ {
+ "coop_prop" "kill"
+ "anv_mapfixes_dispcollision_fairgrounds" "Kill"
+ "func_breakable" "Break"
+ "anv_mapfixes_cliprework_beanbinninga" "Kill"
+ "coop_blocker" "Kill"
+ "!3749406" "Kill"
+ "!3762835" "Kill"
+ "tol_clip_brush" "Kill"
+ }
+ }
+ "c2m3_coaster"
+ {
+ "spawnpoint" "2829.782958 2042.384155 21.309265"
+ "defaultset" "large"
+ "ents"
+ {
+ "SAFEROOM_BLOCK_PROP"
+ {
+ "origin" "3012.181884 2051.005371 60.447525"
+ "rotation" "0.000000 -175.885253 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "VENT_SUS_1"
+ {
+ "origin" "437.521789 3700.031250 208.031250"
+ "rotation" "0.000000 -151.672363 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_foliage\flower_barrel.mdl"
+ }
+ "VENT_SUS_2"
+ {
+ "origin" "179.974365 3701.934082 218.263107"
+ "rotation" "0.000000 93.777389 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_waterfront\footlocker01.mdl"
+ }
+ "COASTER_STOP"
+ {
+ "origin" "-1971.579467 2021.431396 0.031250"
+ "rotation" "0.000000 -168.560409 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles\deliveryvan_armored.mdl"
+ }
+ "COASTER_STOP_BLOCKER"
+ {
+ "origin" "-1816.031250 2094.495849 18.641880"
+ "type" "env_physics_blocker"
+ "scale" "20 20 100"
+ }
+ "END_PORTAL_INBOUNDS"
+ {
+ "origin" "-111.468742 3868.442138 -30.968750"
+ "type" "_portal_xen"
+ "offset" "-218.711853 3872.825927 -16.254243"
+ }
+ "END_PORTAL_OUTBOUNDS"
+ {
+ "origin" "-175.322891 3874.135742 -25.896236"
+ "type" "_portal_xen"
+ "offset" "-6.273283 3868.246337 -39.968750"
+ }
+ "ENT_PORTAL_OUTBOUNDS"
+ {
+ "origin" "781.968750 5089.447265 -12.105030"
+ "type" "_portal_xen"
+ "offset" "848.368774 4996.333496 44.031250"
+ }
+ "ENT_PORTAL_INBOUNDS"
+ {
+ "origin" "818.531250 5120.259765 5.968750"
+ "type" "_portal_xen"
+ "offset" "693.952026 5122.255859 2.038572"
+ "set" "large"
+ }
+ "NO_JUMP_HOLE_PROP"
+ {
+ "origin" "298.292388 3772.014892 200.059875"
+ "rotation" "-84.000000 90.560409 -10.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_militia\sheetrock_leaning.mdl"
+ "set" "short"
+ }
+ "NO_JUMP_WALL_PROP"
+ {
+ "origin" "-60.321926 3621.998046 208.031250"
+ "rotation" "0.000000 177.283248 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_assault\box_stack1.mdl"
+ "set" "short"
+ }
+ }
+ "inputs"
+ {
+ "finale_alarm_sound" "Kill"
+ "relay_start_onslaught" "Trigger"
+ "prop_gate_coaster" "Kill"
+ "navblock_coop" "Kill"
+ "func_detail_blocker" "Kill"
+ "brushes_coop" "Kill"
+ "func_brush" "Kill"
+ "breakwall" "Break"
+ }
+ }
+ "c2m4_barns"
+ {
+ "ents"
+ {
+ "gate_stop"
+ {
+ "origin" "-2251.308349 1434.023315 -255.968750"
+ "scale" "20 100 100"
+ }
+ "lol"
+ {
+ "origin" "-255.596389 712.626953 -205.968750"
+ "rotation" "0.000000 180.269416 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_teenangst.mdl"
+ }
+ }
+ "inputs"
+ {
+ "brushes_coop" "Kill"
+ "clips_coop" "Kill"
+ "navblock_coop" "Kill"
+ "props_coop" "Kill"
+ "minifinale_gates_door1" "Lock"
+ "minifinale_gates_door1" "Close"
+ "minifinale_gates_door2" "Lock"
+ "minifinale_gates_door2" "Close"
+ "minifinale_gates_door3" "Lock"
+ "minifinale_gates_door3" "Close"
+ "minifinale_gates_door4" "Lock"
+ "minifinale_gates_door4" "Close"
+ "minifinale_gates_door5" "Lock"
+ "minifinale_gates_door5" "Close"
+ "minifinale_gates_door6" "Lock"
+ "minifinale_gates_door6" "Close"
+ }
+ }
+ "c1m2_streets"
+ {
+ "climbing" "off"
+ "sets"
+ {
+ "cola"
+ {
+ "spawnpoint" "-5427.254882 938.835754 568.031250"
+ }
+ }
+ "ents"
+ {
+ "A_PROP"
+ {
+ "origin" "-2536 2258 0"
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2"
+ {
+ "origin" "-2536 2418 0"
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster.mdl"
+ "rotation" "0 0 0"
+ }
+ "B_PROP"
+ {
+ "origin" "-2305 2159 0"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "B_PROP2"
+ {
+ "origin" "-2305 2159 40"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "B_PROP3"
+ {
+ "origin" "-2305 2200 0"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "C_PROP"
+ {
+ "origin" "-4377.309570 1749.962646 80.593964"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 10 0"
+ }
+ "C_PROP2"
+ {
+ "origin" " -4383.500488 2161.401611 226.530548"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 10 0"
+ }
+ "C_PROP2"
+ {
+ "origin" " -4383.500488 2161.401611 266.530548"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 10 0"
+ }
+ "C_PROP2"
+ {
+ "origin" " -4383.500488 2121.401611 226.530548"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 10 0"
+ }
+ "D_Prop"
+ {
+ "origin" "-5170.352051 870.512817 465.251526"
+ "type" "prop_dynamic"
+ "model" "props_street/police_barricade.mdl"
+ "rotation" "0 180 0"
+ }
+ "D"
+ {
+ "origin" "-5180.282227 905.176453 540.750977"
+ "scale" "80 70 80"
+ }
+ "E_Prop"
+ {
+ "origin" "1399.475464 2483.173096 572.186157"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "E_Prop2"
+ {
+ "origin" "1315.670288 2500.615234 670.690247"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "BUSH_BLOCK"
+ {
+ "origin" "800.609313 2733.161376 695.719726"
+ "scale" "190 50 100"
+ }
+ "G_Prop"
+ {
+ "origin" "-2219.452637 1219.065308 2.395943"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 90 0"
+ }
+ "cola_replace"
+ {
+ "origin" "-7376.00 -1338.00 395.00"
+ "type" "prop_physics_multiplayer"
+ "model" "w_models/weapons/w_cola.mdl"
+ "rotation" "0 0 0"
+ }
+ "fence"
+ {
+ "origin" "-5458.898437 823.603088 610.031250"
+ "rotation" "0.000000 -89.520973 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_urban\fence002_128.mdl"
+ }
+ "fence_stop"
+ {
+ "origin" "-5250.609375 824.685974 746.281250"
+ "scale" "60 10 80"
+ }
+ "lol"
+ {
+ "origin" "-7634.699218 -373.631134 752.381225"
+ "rotation" "90.000000 -90.269416 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_teenangst.mdl"
+ }
+ "tug"
+ {
+ "origin" "-5836.837890 2780.815917 391.636169"
+ "rotation" "0.000000 -90.999050 -15.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles\boat_rescue_tug.mdl"
+ }
+ }
+ "inputs"
+ {
+ "store_door01_dynamic" "open"
+ "store_door02_dynamic" "open"
+ "store_alarm_generic" "StopSound"
+ "store_alarm_generic" "Kill"
+ "cola" "Kill"
+ "!100452" "_setteam"
+ "InstanceAuto36-gunshop.ent" "Disable"
+ "InstanceAuto36-stairwell.ent" "Disable"
+ "InstanceAuto36-stairwell.ent" "Disable"
+ "stripmall_door1_breakable" "Break"
+ "stripmall_door2_breakable" "Break"
+ "tanker_destroy_relay" "Trigger"
+ }
+ }
+ "c1m4_atrium"
+ {
+ "spawnpoint" "-4440.582519 -3930.253417 136.031250"
+ "ents"
+ {
+ "exitportalwarp"
+ {
+ "origin" "-4445.884277 -2288.304687 30.806552"
+ "scale" "40 65 80"
+ "offset" "0 60 0"
+ "type" "_relportal"
+ }
+ "lol"
+ {
+ "origin" "-5760.802246 -3997.028320 1024.031250"
+ "rotation" "0.608940 26.964090 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_teenangst.mdl"
+ }
+ "lol2"
+ {
+ "origin" "-3363.302001 -4017.320068 744.781250"
+ "rotation" "0.608940 159.964090 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_namvet.mdl"
+ }
+ "lol3"
+ {
+ "origin" "-5184.031250 -2790.462402 280.031250"
+ "rotation" "0.000000 0.649614 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_biker.mdl"
+ }
+ "lol4"
+ {
+ "origin" "-3024.509521 -4495.127929 536.031250"
+ "rotation" "0.000000 123.649614 0.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_manager.mdl"
+ }
+ "lol4_block"
+ {
+ "origin" "-3024.509521 -4495.127929 536.031250"
+ "scale" "40 25 100"
+ }
+ "cheater_warp"
+ {
+ "origin" "-3295.968750 -4299.645019 650.031250"
+ "type" "_portal"
+ "scale" "40 25 10"
+ "offset" "-3358.754394 -4298.374511 640"
+ }
+ "gnome"
+ {
+ "origin" "-4735.599609 -3408.180175 86.600860"
+ "rotation" "59.756366 -110.296585 0.000000"
+ "type" "prop_physics_multiplayer"
+ "model" "props_junk/gnome.mdl"
+ }
+ }
+ "inputs"
+ {
+ "anv_mapfixes_collision_atrium" "Kill"
+ "elevator" "SetMaxSpeed 1000"
+ "elevator" "MoveToFloor bottom"
+ "event_elevator_deny" "Kill"
+ "exitdoor_portal" "Trigger"
+ "fx_intro" "Kill"
+ "fade_outro_3" "Kill"
+ "camera_outro_wide" "Disable"
+ "camera_outro_wide" "Kill"
+ "camera_outro_tele" "Disable"
+ "camera_outro_tele" "Kill"
+ "trigger_finale" "Kill"
+ "trigger_finale" "Kill"
+ "escape_3" "Enable"
+ "escape_4" "Enable"
+ "escape_2" "Enable"
+ "escape_6" "Enable"
+ "escape_7" "Enable"
+ "escape_1" "Enable"
+ "env_player_blocker" "Kill"
+ "env_physics_blocker" "Kill"
+ "gas_nozzle" "Kill"
+ "relay_outro_endseq" "Trigger"
+ "cam_selector_2" "Trigger"
+ "exitdoor_portal" "Open"
+ "mall_directory" "Enable"
+ "charger_hurt" "Kill"
+ }
+ }
+ "c8m5_rooftop"
+ {
+ "ents"
+ {
+ "A_PROP"
+ {
+ "origin" "7255 8925 6085"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2"
+ {
+ "origin" "7255 8925 6125"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP3"
+ {
+ "origin" "7300 8925 6085"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ }
+ }
+ "c8m2_subway"
+ {
+ "ents"
+ {
+ "A_PROP"
+ {
+ "origin" "7498 3020 18"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2"
+ {
+ "origin" "7342 3020 130"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/wood_stairs_120.mdl"
+ "rotation" "0 0 0"
+ }
+ "C"
+ {
+ "origin" "8670 4958 200"
+ "scale" "50 700 200"
+ "type" "env_physics_blocker"
+ }
+ "B_PROP"
+ {
+ "origin" "8480 4970 15"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/semi_trailer_wrecked.mdl"
+ "rotation" "0 -25 -2"
+ }
+ "B_PROP2"
+ {
+ "origin" "8595 5355 12"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/taxi_rural.mdl"
+ "rotation" "0 -25 3"
+ }
+ "B_PROP3"
+ {
+ "origin" "8393 4642 12"
+ "type" "prop_dynamic"
+ "model" "props_junk/trashcluster01b_corner.mdl"
+ "rotation" "0 0 0"
+ }
+ }
+ }
+ "c11m3_garage"
+ {
+ "ents"
+ {
+ "A_PROP"
+ {
+ "origin" "-6055 -2213 12"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2"
+ {
+ "origin" "-6055 -2213 52"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP3"
+ {
+ "origin" "-6055 -2213 92"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROPB"
+ {
+ "origin" "-6055 -2253 12"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2B"
+ {
+ "origin" "-6055 -2253 52"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROPC"
+ {
+ "origin" "-6095 -2213 12"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2C"
+ {
+ "origin" "-6095 -2213 52"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "A_PROP2D"
+ {
+ "origin" "-6095 -2253 12"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ "rotation" "0 0 0"
+ }
+ "B_PROP"
+ {
+ "origin" "-2326.5 2005 29"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/taxi_rural.mdl"
+ "rotation" "0 180 -10"
+ }
+ "B"
+ {
+ "origin" "-2396.5 2005 29"
+ "scale" "100 5 150"
+ "type" "env_physics_blocker"
+ }
+ "C_PROP2"
+ {
+ "origin" "-2165.216797 1856.723877 16"
+ "rotation" "0 0 0 "
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster_2.mdl"
+ }
+ "C_PROP3"
+ {
+ "origin" "-2165.216797 1946.723877 16"
+ "rotation" "0 0 0 "
+ "type" "prop_dynamic"
+ "model" "props_junk/dumpster_2.mdl"
+ }
+ "C"
+ {
+ "origin" "-2210.216797 1900.723877 35"
+ "scale" "15 100 100"
+ "type" "env_physics_blocker"
+ }
+ "gnome"
+ {
+
+ "origin" "-5489.493164 1160.628540 36.031250"
+ "rotation" "0.000000 90.606277 0.000000"
+ "type" "prop_physics_multiplayer"
+ "model" "props_junk/gnome.mdl"
+ }
+ }
+ "inputs"
+ {
+ "construction_clip" "kill"
+ "fuel_barricade" "kill"
+ "barricade_gas_can" "kill"
+ "fuel_barricade_break1" "kill"
+ "fuel_barricade_break2" "kill"
+ "fuel_barricade_break3" "kill"
+ "door_exitl" "Lock"
+ "door_exitr" "Lock"
+ }
+ }
+ "c11m4_terminal"
+ {
+ "spawnpoint" "-482.813659 3566.486572 296.031250"
+ "sets"
+ {
+ "terminal"
+ {
+ "spawnpoint" "1811.834716 1570.712402 264.607604"
+ }
+ }
+ "ents"
+ {
+ "BAG_HOLE_A"
+ {
+ "origin" "382.787200 2708.387207 40.031250"
+ "rotation" "0.000000 -174.077285 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_unique/airport/luggage_pile1.mdl"
+ }
+ "BAG_HOLE_B"
+ {
+ "origin" "402.307434 2415.856201 40.031250"
+ "rotation" "0.000000 -174.077285 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_unique/airport/luggage_pile1.mdl"
+ }
+ "BAG_HOLE_C"
+ {
+ "origin" "401.137786 2110.304199 40.031250"
+ "rotation" "0.000000 -174.077285 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_unique/airport/luggage_pile1.mdl"
+ }
+ "DETECTOR_NO_DETECT"
+ {
+ "origin" "1023.898742 1750.977294 16.031250"
+ "scale" "20 650 300"
+ "set" "terminal"
+ }
+ "DETECTOR_PROP"
+ {
+ "origin" "1030.929931 1755.157714 37.031250"
+ "rotation" "90 0 0"
+ "type" "prop_dynamic"
+ "model" "props_interiors\chairs_airport.mdl"
+ "set" "terminal"
+ }
+ "lol"
+ {
+ "origin" "2350.819335 1851.536376 152.235565"
+ "rotation" "10.137485 -178.424728 15.000000"
+ "type" "prop_dynamic"
+ "model" "survivors/survivor_teenangst.mdl"
+ "set" "terminal"
+ }
+ }
+ "inputs"
+ {
+ "van_blocker" "Kill"
+ "van_start_relay" "Trigger"
+ "!3098419" "Kill"
+ "breakwall01" "Break"
+ "func_breakable" "Kill"
+ "breakwall01_illusionary" "Kill"
+ "!6802112" "Open"
+ "!6765323" "Kill"
+ "!6765365" "Kill"
+ "securityalarmlight1" "TurnOn"
+ "securityspotlight1" "LightOn"
+ "securityrotator1" "StopSound"
+ "securityalarmbase1" "Skin 1"
+ "securityalarmsprite1" "ShowSprite"
+ "alarm_safety_relay" "Kill"
+ "env_soundscape" "Kill"
+ }
+ }
+ "c12m1_hilltop"
+ {
+ "sets"
+ {
+ "A"
+ {
+ "spawnpoint" "-6506.025878 -6803.962402 377.974670"
+ }
+ }
+ }
+ "c12m4_barn"
+ {
+ "spawnpoint" "10523.914062 -6057.256347 -63.968750"
+ "ents"
+ {
+ "BRIDGE_STOP"
+ {
+ "origin" "10485.244140 -3690.727294 -60.199607"
+ "rotation" "0.000000 -87.237983 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/barricade001_128_reference.mdl"
+ }
+ "BRIDGE_STOP2"
+ {
+ "origin" "10403.553710 -3663.791503 -63.968750"
+ "rotation" "0.000000 89.303184 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_street/police_barricade.mdl"
+ }
+ "BRIDGE_BLOCK"
+ {
+ "origin" "10463 -3604 -63"
+ "scale" "120 100 200"
+ "type" "env_physics_blocker"
+ }
+ "FENCE_SKIP_HELPER"
+ {
+ "origin" "11317.451171 -4665.616210 -365.052124"
+ "rotation" "0.895275 90.865699 15.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles\deliveryvan_glass.mdl"
+ }
+ "FENCE_SKIP_HELPER"
+ {
+ "origin" "11317.451171 -4665.616210 -365.052124"
+ "rotation" "0.895275 90.865699 15.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles\deliveryvan.mdl"
+ }
+ "FENCE_SKIP_HELPER_B"
+ {
+ "origin" "11358.471679 -4625.372070 -259.812072"
+ "rotation" "15.114483 0.347351 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "FENCE_SKIP_HELPER_B2_TOP"
+ {
+ "origin" "11358.471679 -4625.372070 -220.812072"
+ "rotation" "15.114483 0.347351 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "FENCE_SKIP_HELPER_B2_TOP2"
+ {
+ "origin" "11368.471679 -4625.372070 -170.812072"
+ "rotation" "15.114483 0.347351 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\cs_militia\footlocker01_closed.mdl"
+ }
+ "FENCE_SKIP_HELPER_B3"
+ {
+ "origin" "11318.471679 -4625.372070 -249.812072"
+ "rotation" "15.114483 0.347351 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "FENCE_SKIP_EXIT_STAIRS"
+ {
+ "origin" "11307.600585 -4320.291992 -325.535629"
+ "rotation" "0.901843 90.274330 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/stairs_house_01.mdl"
+ }
+ "FENCE_SKIP_EXIT_STAIRS_2"
+ {
+ "origin" "11306.600585 -4108.039062 -498.862701"
+ "rotation" "0.901843 90.274330 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_exteriors/stairs_house_01.mdl"
+ }
+ "FENCE_SKIP_EXIT_STAIRS_SUPPORT"
+ {
+
+ "origin" "11324.499023 -4397.913574 -469.508117"
+ "rotation" "0 0 0"
+ "type" "prop_dynamic"
+ "model" "props_interiors\concretepillar01.mdl"
+ }
+ "FENCE_SKIP_EXIT_STAIRS_SUPPORT"
+ {
+ "origin" "11324.499023 -4397.913574 -341.508117"
+ "rotation" "0 0 0"
+ "type" "prop_dynamic"
+ "model" "props_interiors\concretepillar01.mdl"
+ }
+ "MY_SKIP_SUPPORT"
+ {
+ "origin" "11070.869140 -5625.584960 -65.968738"
+ "rotation" "0 0 0"
+ "type" "prop_dynamic"
+ "model" "props_swamp\plank001b_192.mdl"
+ }
+ "HOUSE_PLANKS"
+ {
+ "origin" "11070.869140 -5817.584960 -65.968738"
+ "rotation" "0 0 0"
+ "type" "prop_dynamic"
+ "model" "props_swamp\plank001b_192.mdl"
+ }
+ "HOUSE_PLANKS"
+ {
+ "origin" "11070.869140 -6009.24960 -65.968738"
+ "rotation" "0 0 0"
+ "type" "prop_dynamic"
+ "model" "props_swamp\plank001b_192.mdl"
+ }
+ "ROOF_ACCESS"
+ {
+ "origin" "10692.570312 -7545.357910 -76.285079"
+ "rotation" "0.000000 -105.753463 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\de_train\pallet_barrels.mdl"
+ }
+ "ROOF_ACCESS_TOP"
+ {
+ "origin" "10692.570312 -7545.357910 -26.285079"
+ "rotation" "0.000000 -105.753463 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\de_train\pallet_barrels.mdl"
+ }
+ "ROOF_ACCESS_2"
+ {
+ "origin" "10692.479492 -7470.440917 -76.285079"
+ "rotation" "0.000000 -105.753463 0.000000"
+ "type" "prop_dynamic"
+ "model" "props\de_train\pallet_barrels.mdl"
+ }
+ "OUT_OF_BOUNDS_ROOF"
+ {
+ "origin" "10630.428710 -7433.587890 274.640625"
+ "rotation" "86.179300 -90.960174 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_c17\utilitypole01d.mdl"
+ "set" "outbounds"
+ }
+ "ROOF_HELPER"
+ {
+ "origin" "10693.650390 -7695.398925 195.193389"
+ "rotation" "54.007286 -84.799293 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "BLOCK_SPAWN"
+ {
+ "type" "env_physics_blocker"
+ "origin" "10222.888671 -8366.800781 233.956771"
+ "scale" "55 400 300"
+ }
+
+ "MYPROP"
+ {
+ "origin" "10693.677734 -7559.968750 25.075805"
+ "rotation" "0.000000 0.139984 0.000000"
+ "type" "_lantern"
+ "model" "props_unique/spawn_apartment/lantern.mdl"
+ }
+ }
+ }
+ "c13m2_southpinestream"
+ {
+ "spawnpoint" "7472.512695 2728.908203 454.170593"
+ "sets"
+ {
+ "A"
+ {
+ "spawnpoint" "631.415039 3729.930664 329.250244"
+ }
+ }
+ "ents"
+ {
+ "A_HELPER"
+ {
+ "origin" "3266.013671 2010.126708 458.291046"
+ "rotation" "0.000000 148.362182 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "FENCE_JUMPER"
+ {
+ "origin" "1832.684692 2264.726562 422.658752"
+ "rotation" "0.000000 70.022712 -4.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/cement_truck01.mdl"
+ }
+ "RETURNER"
+ {
+ "origin" "539.416015 2654.438476 177.077484"
+ "rotation" "-30.582356 8.396564 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/airplane_piperwreck.mdl"
+ }
+ "RETURNER_SUPPORT_A"
+ {
+ "origin" "630.816955 2635.709228 196.630294"
+ "rotation" "-8.000000 120.049987 -32.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/car005a.mdl"
+ }
+ "RETURNER_SUPPORT_B"
+ {
+ "origin" "539.403564 2561.882568 129.099700"
+ "rotation" "-30.000000 32.022949 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_vehicles/car001a_phy.mdl"
+ }
+ "WALL"
+ {
+ "origin" "-837.842651 5280.024414 272.031250"
+ "rotation" "0.202026 -89.108253 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_fortifications/concrete_wall001_96_reference.mdl"
+ }
+ "WALL_BLOCK"
+ {
+ "origin" "-837.842651 5280.024414 272.031250"
+ "type" "env_physics_blocker"
+ "scale" "66 26 200"
+ }
+ "RETURN_A"
+ {
+ "origin" "3874.732421 1896.033325 405.846710"
+ "rotation" "0.000000 37.927692 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_crates/static_crate_40.mdl"
+ }
+ "SPAWN_BLOCK_PROP"
+ {
+ "origin" "7883.347167 3302.165527 534.600952"
+ "rotation" "0.000000 -5.696259 0.000000"
+ "type" "prop_dynamic"
+ "model" "props_wasteland/rock_cliff01.mdl"
+ }
+ "SPAWN_BLOCK"
+ {
+
+ "origin" "7848.883300 3287.961914 794.680236"
+ "type" "env_physics_blocker"
+ "scale" "900 900 900"
+ }
+ "TANKER_SKIP"
+ {
+ "origin" "443.717864 4325.977050 497.610931"
+ "type" "env_physics_blocker"
+ "scale" "100 100 150"
+ }
+ "TANKER_SKIP_2"
+ {
+ "origin" "673.124267 4100.726562 558.114318"
+ "type" "env_physics_blocker"
+ "scale" "100 100 150"
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/data/prophunt/maps.cfg b/data/prophunt/maps.cfg
index 1324fa5..70ddfac 100644
--- a/data/prophunt/maps.cfg
+++ b/data/prophunt/maps.cfg
@@ -1,28 +1,28 @@
-"prophunt"
-{
- "c1m1_hotel"
- {
- "props"
- {
- "SOFA_1"
- {
- "model" "props_urban/hotel_chair001.mdl"
- "origin" "1532.456787 4609 1184"
- "rotation" "0 323.5 0"
- }
- "SOFA_2"
- {
- "origin" "1649 4529 1184"
- "rotation" "0 170 0"
- "model" "props_urban/hotel_chair001.mdl"
- }
- }
- }
- "c8m1_apartments"
- {
- "props"
- {
-
- }
- }
+"prophunt"
+{
+ "c1m1_hotel"
+ {
+ "props"
+ {
+ "SOFA_1"
+ {
+ "model" "props_urban/hotel_chair001.mdl"
+ "origin" "1532.456787 4609 1184"
+ "rotation" "0 323.5 0"
+ }
+ "SOFA_2"
+ {
+ "origin" "1649 4529 1184"
+ "rotation" "0 170 0"
+ "model" "props_urban/hotel_chair001.mdl"
+ }
+ }
+ }
+ "c8m1_apartments"
+ {
+ "props"
+ {
+
+ }
+ }
}
\ No newline at end of file
diff --git a/data/prophunt/props.cfg b/data/prophunt/props.cfg
index c524e38..2c7b538 100644
--- a/data/prophunt/props.cfg
+++ b/data/prophunt/props.cfg
@@ -1,23 +1,23 @@
-"props"
-{
- "props/cs_office/Fire_Extinguisher.mdl" "5"
- "props_urban/ashtray_stand001.mdl" "5"
- "props_furniture/hotel_chair.mdl" "10"
- "props_urban/hotel_chair001.mdl" "20"
- "props_interiors/tv.mdl" "10"
- "props_downtown/ironing_board.mdl" "15"
- "props_urban/hotel_lamp001.mdl" "10"
- "props_windows/hotel_window_glass001.mdl" "30"
- "props/cs_office/shelves_metal1.mdl" "50"
- "props/cs_office/shelves_metal.mdl" "50"
- "props_downtown/dresser.mdl" "50"
- "props_interiors_Hotel_Cart.mdl" "40"
- "props_downtown/side_table.mdl" "10"
- "props_interiors/ac_wallunit.mdl" "30"
- "props_downtown/bed_motel01.mdl" "50"
- "props_interiors/coffee_table_oval.mdl" "20"
- "props_downtown/mini_fridge.mdl" "10"
- "props_interiors/fridge_mini.mdl" "10"
- "props/cs_office/vending_machine.mdl" "50"
- "props_interiors/toilet.mdl" "30"
+"props"
+{
+ "props/cs_office/Fire_Extinguisher.mdl" "5"
+ "props_urban/ashtray_stand001.mdl" "5"
+ "props_furniture/hotel_chair.mdl" "10"
+ "props_urban/hotel_chair001.mdl" "20"
+ "props_interiors/tv.mdl" "10"
+ "props_downtown/ironing_board.mdl" "15"
+ "props_urban/hotel_lamp001.mdl" "10"
+ "props_windows/hotel_window_glass001.mdl" "30"
+ "props/cs_office/shelves_metal1.mdl" "50"
+ "props/cs_office/shelves_metal.mdl" "50"
+ "props_downtown/dresser.mdl" "50"
+ "props_interiors_Hotel_Cart.mdl" "40"
+ "props_downtown/side_table.mdl" "10"
+ "props_interiors/ac_wallunit.mdl" "30"
+ "props_downtown/bed_motel01.mdl" "50"
+ "props_interiors/coffee_table_oval.mdl" "20"
+ "props_downtown/mini_fridge.mdl" "10"
+ "props_interiors/fridge_mini.mdl" "10"
+ "props/cs_office/vending_machine.mdl" "50"
+ "props_interiors/toilet.mdl" "30"
}
\ No newline at end of file
diff --git a/data/randomizer/c1m1_hotel.json b/data/randomizer/c1m1_hotel.json
index 43398dc..47c76a2 100644
--- a/data/randomizer/c1m1_hotel.json
+++ b/data/randomizer/c1m1_hotel.json
@@ -1,26 +1,26 @@
-{
- "group1": {
- "chance": 0.1,
- "exclusions": ["group2"],
- "variants": [
- {
- "weight": 1,
- "entities": [
-
- {
- "origin": [1421.24, 5780.23, 2881.16],
- "angles": [0.00, 0.00, 0.00],
- "size": [5.00, 5.00, 5.00],
- "model": "models/props/cs_assault/forklift.mdl"
- },
- {
- "origin": [1139.59, 5685.23, 2883.25],
- "angles": [0.00, 0.00, 0.00],
- "size": [5.00, 5.00, 5.00],
- "model": "models/props_unique/airportdeparturescreen01.mdl"
- }
- ]
- }
- ]
- }
+{
+ "group1": {
+ "chance": 0.1,
+ "exclusions": ["group2"],
+ "variants": [
+ {
+ "weight": 1,
+ "entities": [
+
+ {
+ "origin": [1421.24, 5780.23, 2881.16],
+ "angles": [0.00, 0.00, 0.00],
+ "size": [5.00, 5.00, 5.00],
+ "model": "models/props/cs_assault/forklift.mdl"
+ },
+ {
+ "origin": [1139.59, 5685.23, 2883.25],
+ "angles": [0.00, 0.00, 0.00],
+ "size": [5.00, 5.00, 5.00],
+ "model": "models/props_unique/airportdeparturescreen01.mdl"
+ }
+ ]
+ }
+ ]
+ }
}
\ No newline at end of file
diff --git a/data/randomizer/c8m1_apartment.json b/data/randomizer/c8m1_apartment.json
index f9ae71b..6d3f5a0 100644
--- a/data/randomizer/c8m1_apartment.json
+++ b/data/randomizer/c8m1_apartment.json
@@ -1,123 +1,123 @@
-{
- "alleyskip": {
- "chance": 0.30,
- "variants": [
- {
- "weight": 1,
- "entities": [
- {
- "model": "models/props_urban/fire_escape_wide_lower.mdl",
- "origin": [2203.81, 2381.96, 212.84],
- "angles": [0.00, 1.07, 0.00],
- "size": [181.27, 2.22, 132.22]
- },
- {
- "model": "models/props_vehicles/ambulance.mdl",
- "origin": [2317.06, 2282.30, 16.03],
- "angles": [0.00, 270.39, 0.00],
- "size": [90.09, 115.60, 109.48]
- },
- {
- "model": "models/props/cs_militia/boxes_garage_lower.mdl",
- "origin": [2265.46, 2308.65, 124.13],
- "angles": [0.00, 92.14, 0.00],
- "size": [37.36, 35.15, 65.06]
- },
- {
- "model": "models/props_fortifications/barricade001_128_reference.mdl",
- "origin": [2013.47, 2254.53, 15.83],
- "angles": [0.00, 178.81, 0.00],
- "size": [21.75, 48.25, 144.25]
- },
- {
- "model": "models/props_fortifications/barricade001_64_reference.mdl",
- "origin": [2001.55, 2345.92, 15.51],
- "angles": [0.00, 177.42, 0.00],
- "size": [24.25, 24.25, 144.25]
- }
- ]
- }
- ]
- },
- "mainblock": {
- "chance": 0.5,
- "variants": [
- {
- "weight": 1,
- "entities": [
- {
- "model": "models/props_street/police_barricade2.mdl",
- "origin": [2744.79, 3975.41, 16.03],
- "angles": [0.00, 105.00, 0.00],
- "size": [28.70, 71.14, 119.31]
- },
- {
- "model": "models/props_street/police_barricade_496in.mdl",
- "origin": [2428.55, 3960.12, 8.03],
- "angles": [0.00, 89.61, 0.00],
- "size": [28.77, 245.37, 119.31]
- },
- {
- "model": "models/props_street/police_barricade2.mdl",
- "origin": [2120.48, 3963.20, 16.03],
- "angles": [0.00, 87.27, 0.00],
- "size": [28.70, 71.14, 119.31]
- }
- ]
- }
- ]
- },
- "subwayblock": {
- "chance": 0.2,
- "variants": [
- {
- "entities": [
- {
- "model": "models/props_vehicles/hmmwv.mdl",
- "origin": [2755.60, 4139.53, 12.72],
- "angles": [0.00, -180.00, 0.00],
- "size": [57.97, 105.86, 124.61]
- }
- ]
- },
- {
- "entities": [
- {
- "model": "models/props_vehicles/pickup_truck_78.mdl",
- "origin": [2755.60, 4139.53, 12.72],
- "angles": [0.00, -180.00, 0.00],
- "size": [57.97, 105.86, 124.61]
- }
- ]
- }
- ]
- },
- "doorblock": {
- "chance": 0.2,
- "variants": [
- {
- "weight": 2,
- "entities": [
- {
- "model": "models/props_junk/dumpster.mdl",
- "origin": [1519.27, 2626.97, 55.79],
- "angles": [0.00, -86.23, 90.00],
- "size": [26.37, 40.17, 54.13]
- }
- ]
- },
- {
- "weight": 1,
- "entities": [
- {
- "model": "models/props_junk/dumpster.mdl",
- "origin": [1498.78, 2630.72, 16.03],
- "angles": [0.00, 3.76, 0.00],
- "size": [26.37, 40.17, 54.13]
- }
- ]
- }
- ]
- }
-}
-
+{
+ "alleyskip": {
+ "chance": 0.30,
+ "variants": [
+ {
+ "weight": 1,
+ "entities": [
+ {
+ "model": "models/props_urban/fire_escape_wide_lower.mdl",
+ "origin": [2203.81, 2381.96, 212.84],
+ "angles": [0.00, 1.07, 0.00],
+ "size": [181.27, 2.22, 132.22]
+ },
+ {
+ "model": "models/props_vehicles/ambulance.mdl",
+ "origin": [2317.06, 2282.30, 16.03],
+ "angles": [0.00, 270.39, 0.00],
+ "size": [90.09, 115.60, 109.48]
+ },
+ {
+ "model": "models/props/cs_militia/boxes_garage_lower.mdl",
+ "origin": [2265.46, 2308.65, 124.13],
+ "angles": [0.00, 92.14, 0.00],
+ "size": [37.36, 35.15, 65.06]
+ },
+ {
+ "model": "models/props_fortifications/barricade001_128_reference.mdl",
+ "origin": [2013.47, 2254.53, 15.83],
+ "angles": [0.00, 178.81, 0.00],
+ "size": [21.75, 48.25, 144.25]
+ },
+ {
+ "model": "models/props_fortifications/barricade001_64_reference.mdl",
+ "origin": [2001.55, 2345.92, 15.51],
+ "angles": [0.00, 177.42, 0.00],
+ "size": [24.25, 24.25, 144.25]
+ }
+ ]
+ }
+ ]
+ },
+ "mainblock": {
+ "chance": 0.5,
+ "variants": [
+ {
+ "weight": 1,
+ "entities": [
+ {
+ "model": "models/props_street/police_barricade2.mdl",
+ "origin": [2744.79, 3975.41, 16.03],
+ "angles": [0.00, 105.00, 0.00],
+ "size": [28.70, 71.14, 119.31]
+ },
+ {
+ "model": "models/props_street/police_barricade_496in.mdl",
+ "origin": [2428.55, 3960.12, 8.03],
+ "angles": [0.00, 89.61, 0.00],
+ "size": [28.77, 245.37, 119.31]
+ },
+ {
+ "model": "models/props_street/police_barricade2.mdl",
+ "origin": [2120.48, 3963.20, 16.03],
+ "angles": [0.00, 87.27, 0.00],
+ "size": [28.70, 71.14, 119.31]
+ }
+ ]
+ }
+ ]
+ },
+ "subwayblock": {
+ "chance": 0.2,
+ "variants": [
+ {
+ "entities": [
+ {
+ "model": "models/props_vehicles/hmmwv.mdl",
+ "origin": [2755.60, 4139.53, 12.72],
+ "angles": [0.00, -180.00, 0.00],
+ "size": [57.97, 105.86, 124.61]
+ }
+ ]
+ },
+ {
+ "entities": [
+ {
+ "model": "models/props_vehicles/pickup_truck_78.mdl",
+ "origin": [2755.60, 4139.53, 12.72],
+ "angles": [0.00, -180.00, 0.00],
+ "size": [57.97, 105.86, 124.61]
+ }
+ ]
+ }
+ ]
+ },
+ "doorblock": {
+ "chance": 0.2,
+ "variants": [
+ {
+ "weight": 2,
+ "entities": [
+ {
+ "model": "models/props_junk/dumpster.mdl",
+ "origin": [1519.27, 2626.97, 55.79],
+ "angles": [0.00, -86.23, 90.00],
+ "size": [26.37, 40.17, 54.13]
+ }
+ ]
+ },
+ {
+ "weight": 1,
+ "entities": [
+ {
+ "model": "models/props_junk/dumpster.mdl",
+ "origin": [1498.78, 2630.72, 16.03],
+ "angles": [0.00, 3.76, 0.00],
+ "size": [26.37, 40.17, 54.13]
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/plugins/GrabEnt.smx b/plugins/GrabEnt.smx
index 191128c..5847acf 100644
Binary files a/plugins/GrabEnt.smx and b/plugins/GrabEnt.smx differ
diff --git a/plugins/L4D2Tools.smx b/plugins/L4D2Tools.smx
index a15440c..30d6c5f 100644
Binary files a/plugins/L4D2Tools.smx and b/plugins/L4D2Tools.smx differ
diff --git a/plugins/adminpanel.smx b/plugins/adminpanel.smx
index 109fedf..743e461 100644
Binary files a/plugins/adminpanel.smx and b/plugins/adminpanel.smx differ
diff --git a/plugins/jutils.smx b/plugins/jutils.smx
deleted file mode 100644
index bba3408..0000000
Binary files a/plugins/jutils.smx and /dev/null differ
diff --git a/plugins/l4d2_TKStopper.smx b/plugins/l4d2_TKStopper.smx
index cbc19a5..ef60d65 100644
Binary files a/plugins/l4d2_TKStopper.smx and b/plugins/l4d2_TKStopper.smx differ
diff --git a/plugins/l4d2_ai_tweaks.smx b/plugins/l4d2_ai_tweaks.smx
index 0f561d8..aaa4d45 100644
Binary files a/plugins/l4d2_ai_tweaks.smx and b/plugins/l4d2_ai_tweaks.smx differ
diff --git a/plugins/l4d2_extraplayeritems.smx b/plugins/l4d2_extraplayeritems.smx
index 854f163..5166d97 100644
Binary files a/plugins/l4d2_extraplayeritems.smx and b/plugins/l4d2_extraplayeritems.smx differ
diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx
index 8455350..9e9ceab 100644
Binary files a/plugins/l4d2_feedthetrolls.smx and b/plugins/l4d2_feedthetrolls.smx differ
diff --git a/plugins/l4d2_guesswho.smx b/plugins/l4d2_guesswho.smx
index fbe50ff..b4eb551 100644
Binary files a/plugins/l4d2_guesswho.smx and b/plugins/l4d2_guesswho.smx differ
diff --git a/plugins/l4d2_hats.smx b/plugins/l4d2_hats.smx
index 7e60e60..c90d1a8 100644
Binary files a/plugins/l4d2_hats.smx and b/plugins/l4d2_hats.smx differ
diff --git a/plugins/l4d2_randomizer.smx b/plugins/l4d2_randomizer.smx
index d898a4b..11fb734 100644
Binary files a/plugins/l4d2_randomizer.smx and b/plugins/l4d2_randomizer.smx differ
diff --git a/plugins/sm_player_notes.smx b/plugins/sm_player_notes.smx
index 6296987..1b61811 100644
Binary files a/plugins/sm_player_notes.smx and b/plugins/sm_player_notes.smx differ
diff --git a/scripting/GrabEnt.sp b/scripting/GrabEnt.sp
index 52f5eb8..419ea27 100644
--- a/scripting/GrabEnt.sp
+++ b/scripting/GrabEnt.sp
@@ -36,7 +36,7 @@ int g_BeamSprite;
int g_HaloSprite;
int g_iLaserIndex;
-#define MAX_FORBIDDEN_CLASSNAMES 9
+#define MAX_FORBIDDEN_CLASSNAMES 10
static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
// "env_physics_blocker",
// "env_player_blocker",
@@ -49,7 +49,8 @@ static char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
"func_tracktrain",
// "infected",
"func_lod",
- "prop_ragdoll"
+ "prop_ragdoll",
+ "move_rope"
};
#define MAX_FORBIDDEN_MODELS 2
@@ -65,9 +66,11 @@ static char HIGHLIGHTED_CLASSNAMES[MAX_HIGHLIGHTED_CLASSNAMES][] = {
"func_brush"
}
+ConVar g_cvarEnabled;
public void OnPluginStart()
{
+ g_cvarEnabled = CreateConVar("sm_grabent_allow", "1", "Is grabent allowed", FCVAR_NONE, true, 0.0, true, 1.0);
RegAdminCmd("sm_grabent_freeze", Cmd_ReleaseFreeze, ADMFLAG_CHEATS, "<0/1> - Toggle entity freeze/unfreeze on release.");
RegAdminCmd("sm_grab", Cmd_Grab, ADMFLAG_CHEATS, "Toggle Grab the entity in your crosshair.");
RegAdminCmd("+grabent", Cmd_Grab, ADMFLAG_CHEATS, "Grab the entity in your crosshair.");
@@ -131,11 +134,13 @@ public Action Cmd_ReleaseFreeze(client, args)
//============================================================================
// GRAB ENTITY COMMAND //
//============================================================================
-public Action Cmd_Grab(client, args) {
- if (client < 1 || client > MaxClients || !IsClientInGame(client))
+Action Cmd_Grab(int client, int args) {
+ if(!g_cvarEnabled.BoolValue) {
+ ReplyToCommand(client, "[SM] Grabent is disabled");
return Plugin_Handled;
-
- if (g_pGrabbedEnt[client] > 0 && IsValidEntity(g_pGrabbedEnt[client])) {
+ } else if (client < 1 || client > MaxClients || !IsClientInGame(client)) {
+ return Plugin_Handled;
+ } else if (g_pGrabbedEnt[client] > 0 && IsValidEntity(g_pGrabbedEnt[client])) {
Cmd_Release(client, 0);
return Plugin_Handled;
}
@@ -572,6 +577,7 @@ bool Filter_IgnoreForbidden(int entity, int mask, int data) {
}
bool CheckBlacklist(int entity) {
+ if(entity == 0) return false;
static char buffer[64];
GetEntityClassname(entity, buffer, sizeof(buffer));
for(int i = 0; i < MAX_FORBIDDEN_CLASSNAMES; i++) {
@@ -591,5 +597,6 @@ bool CheckBlacklist(int entity) {
if(StrEqual(buffer, "l4d2_randomizer")) {
return false;
}
+ GetEntityClassname(entity, buffer, sizeof(buffer));
return true;
}
\ No newline at end of file
diff --git a/scripting/L4D2Tools.sp b/scripting/L4D2Tools.sp
index b55b332..d643de1 100644
--- a/scripting/L4D2Tools.sp
+++ b/scripting/L4D2Tools.sp
@@ -461,6 +461,10 @@ Action Command_SetClientModel(int client, int args) {
void SetCharacter(int target, int survivorIndex, L4DModelId modelIndex, bool keepModel) {
SetEntProp(target, Prop_Send, "m_survivorCharacter", survivorIndex);
+ if(!PrecacheModel(MODELS[view_as(modelIndex)])) {
+ LogError("SetCharacter: INVALID MODEL: %s", MODELS[view_as(modelIndex)]);
+ return;
+ }
SetEntityModel(target, MODELS[view_as(modelIndex)]);
if (IsFakeClient(target)) {
char name[32];
@@ -629,6 +633,9 @@ public void OnMapStart() {
PrecacheSound(PRECACHE_SOUNDS[i]);
}
#endif
+ for(int i = 0; i < 8; i++) {
+ PrecacheModel(MODELS[i]);
+ }
HookEntityOutput("info_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
HookEntityOutput("trigger_changelevel", "OnStartTouch", EntityOutput_OnStartTouchSaferoom);
diff --git a/scripting/adminpanel.sp b/scripting/adminpanel.sp
index 2185a25..fe0ce3d 100644
--- a/scripting/adminpanel.sp
+++ b/scripting/adminpanel.sp
@@ -14,7 +14,8 @@
#include
#include
#include
-#include
+#undef REQUIRE_PLUGIN
+#tryinclude
#pragma newdecls required
@@ -85,7 +86,7 @@ public void OnPluginStart() {
g_socket.SetOption(SocketSendBuffer, BUFFER_SIZE);
uptime = GetTime();
- cvar_debug = CreateConVar("sm_adminpanel_debug", "1", "Turn on debug mode", FCVAR_DONTRECORD, true, 0.0, true, 1.0);
+ cvar_debug = CreateConVar("sm_adminpanel_debug", "0", "Turn on debug mode", FCVAR_DONTRECORD, true, 0.0, true, 1.0);
cvar_authToken = CreateConVar("sm_adminpanel_authtoken", "", "The token for authentication", FCVAR_PROTECTED);
cvar_authToken.AddChangeHook(OnCvarChanged);
@@ -172,10 +173,7 @@ void OnSocketError(Socket socket, int errorType, int errorNumber, int any) {
}
void SendFullSync() {
- PrintToServer("SendFullSync");
if(StartPayload(true)) {
- PrintToServer("SendFullSync : Started");
-
AddGameRecord();
int stage = L4D2_GetCurrentFinaleStage();
if(stage != 0)
@@ -254,15 +252,19 @@ void OnSocketReceive(Socket socket, const char[] receiveData, int dataSize, int
}
void ProcessCommand(int id, const char[] command, const char[] cmdNamespace = "") {
- char output[128];
+ char output[1024];
if(!StartPayload(true)) return;
if(cmdNamespace[0] == '\0' || StrEqual(cmdNamespace, "default")) {
- SplitString(command, " ", output, sizeof(output));
+ // If command has no spaces, we need to manually copy the command to the split part
+ if(SplitString(command, " ", output, sizeof(output)) == -1) {
+ strcopy(output, sizeof(output), command);
+ }
if(CommandExists(output)) {
ServerCommandEx(output, sizeof(output), "%s", command);
AddCommandResponseRecord(id, Result_Boolean, 1, output);
} else {
- AddCommandResponseRecord(id, Result_Error, -1, "Command does not exist");
+ Format(output, sizeof(output), "Command \"%s\" does not exist", output);
+ AddCommandResponseRecord(id, Result_Error, -1, output);
}
} else if(StrEqual(cmdNamespace, "builtin")) {
CommandResultType type;
diff --git a/scripting/include/guesswho/gwents.inc b/scripting/include/guesswho/gwents.inc
index 670c13e..b6b6878 100644
--- a/scripting/include/guesswho/gwents.inc
+++ b/scripting/include/guesswho/gwents.inc
@@ -50,7 +50,9 @@ stock void EntFire(const char[] name, const char[] input) {
}
}
-
+void CreateCoinEntity(float pos[3]) {
+ // int ent = CreateProp("prop_dynamic", "")
+}
void SetupEntities(bool blockers = true, bool props = true, bool portals = true) {
#if defined DEBUG_BLOCKERS
diff --git a/scripting/include/guesswho/gwgame.inc b/scripting/include/guesswho/gwgame.inc
index 9911593..e7c0e5c 100644
--- a/scripting/include/guesswho/gwgame.inc
+++ b/scripting/include/guesswho/gwgame.inc
@@ -125,6 +125,12 @@ methodmap GuessWhoGame < BaseGame {
}
}
+ property int TargetCoinCount {
+ public get() {
+ return 8;
+ }
+ }
+
public void Start() {
}
@@ -301,6 +307,14 @@ methodmap GuessWhoGame < BaseGame {
SDKUnhook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
SDKUnhook(client, SDKHook_WeaponEquip, OnWeaponEquip);
}
+
+ public void PopulateCoins() {
+ float pos[3];
+ for(int i = 0; i < this.TargetCoinCount; i++) {
+ movePoints.GetRandomPoint(pos);
+
+ }
+ }
}
stock bool ArePlayersJoining() {
diff --git a/scripting/include/guesswho/gwtimers.inc b/scripting/include/guesswho/gwtimers.inc
index 1a37392..22135aa 100644
--- a/scripting/include/guesswho/gwtimers.inc
+++ b/scripting/include/guesswho/gwtimers.inc
@@ -13,13 +13,18 @@ Action Timer_RecordPoints(Handle h, int i) {
vecLastLocation[i] = meta.pos;
}
}
+ Game.MapTime++;
PrintHintText(i, "Points: %d / %d", movePoints.Length, MAX_VALID_LOCATIONS);
return Plugin_Continue;
}
+
bool firstCheckDone = false;
Action Timer_WaitForPlayers(Handle h) {
- if(!isEnabled) return Plugin_Stop;
+ if(!isEnabled) {
+ waitTimer = null;
+ return Plugin_Stop;
+ }
if(!ArePlayersJoining()) {
Game.Debug("No players pending, ready to go");
if(!firstCheckDone) {
@@ -28,6 +33,7 @@ Action Timer_WaitForPlayers(Handle h) {
} else {
firstCheckDone = false;
InitGamemode();
+ waitTimer = null;
return Plugin_Stop;
}
}
diff --git a/scripting/include/hats.inc b/scripting/include/hats.inc
deleted file mode 100644
index 186370d..0000000
--- a/scripting/include/hats.inc
+++ /dev/null
@@ -1 +0,0 @@
-native bool SpawnSchematic(const char name[32], const float pos[3], const float angles[3] = NULL_VECTOR);
\ No newline at end of file
diff --git a/scripting/include/hats/editor.sp b/scripting/include/hats/editor.sp
index d170aee..803640f 100644
--- a/scripting/include/hats/editor.sp
+++ b/scripting/include/hats/editor.sp
@@ -1,7 +1,6 @@
int BUILDER_COLOR[4] = { 0, 255, 0, 235 };
int GLOW_BLUE[4] = { 3, 148, 252 };
int GLOW_RED_ALPHA[4] = { 255, 0, 0, 235 };
-int GLOW_RED[3] = { 255, 0, 0};
int GLOW_WHITE[4] = { 255, 255, 255, 255 };
int GLOW_GREEN[4] = { 3, 252, 53 };
float ORIGIN_SIZE[3] = { 2.0, 2.0, 2.0 };
@@ -27,26 +26,21 @@ char MODE_NAME[5][] = {
"Freelook"
}
-enum editFlag {
+enum {
Edit_None,
Edit_Copy = 1,
Edit_Preview = 2,
- Edit_WallCreator = 4
+ Edit_WallCreator = 4,
+ Edit_Manager = 8
}
enum buildType {
Build_Solid,
Build_Physics,
Build_NonSolid,
+ // TODO: Build_Weapon (spawn as weapon?)
}
-enum CompleteType {
- Complete_WallSuccess,
- Complete_WallError,
- Complete_PropSpawned,
- Complete_PropError,
- Complete_EditSuccess
-}
enum StackerDirection {
Stack_Off,
@@ -88,6 +82,7 @@ enum struct EditorData {
int colorIndex;
int axis;
int snapAngle;
+ float rotateSpeed;
int moveSpeed;
float moveDistance;
int entity;
@@ -97,11 +92,14 @@ enum struct EditorData {
editMode mode;
buildType buildType;
- editFlag flags;
+ int flags;
+
+ PrivateForward callback;
+ bool isEditCallback;
void Reset(bool initial = false) {
// Clear preview entity
- if(this.entity != INVALID_ENT_REFERENCE && this.flags & Edit_Preview && IsValidEntity(this.entity)) {
+ if(this.entity != INVALID_ENT_REFERENCE && (this.flags & Edit_Preview) && IsValidEntity(this.entity)) {
RemoveEntity(this.entity);
}
this.stackerDirection = Stack_Off;
@@ -111,12 +109,13 @@ enum struct EditorData {
this.size[0] = this.size[1] = this.size[2] = 5.0;
this.angles[0] = this.angles[1] = this.angles[2] = 0.0;
this.colorIndex = 0;
- this.axis = 1;
+ this.axis = 0;
this.moveDistance = 200.0;
this.flags = Edit_None;
this.classname[0] = '\0';
this.CalculateMins();
this.SetMode(INACTIVE);
+ this.rotateSpeed = 0.1;
// Settings that don't get reset on new spawns:
if(initial) {
this.color[0] = this.color[1] = this.color[2] = this.color[3] = 255;
@@ -138,6 +137,12 @@ enum struct EditorData {
if(this.flags & Edit_WallCreator || this.entity == INVALID_ENT_REFERENCE) {
Effect_DrawBeamBoxRotatableToAll(this.origin, this.mins, this.size, this.angles, g_iLaserIndex, 0, 0, 30, lifetime, 0.4, 0.4, 0, amplitude, color, 0);
} else {
+ if(this.snapAngle != 1) {
+ this.angles[0] = RoundToNearestInterval(this.angles[0], this.snapAngle);
+ this.angles[1] = RoundToNearestInterval(this.angles[1], this.snapAngle);
+ this.angles[2] = RoundToNearestInterval(this.angles[2], this.snapAngle);
+
+ }
TeleportEntity(this.entity, this.origin, this.angles, NULL_VECTOR);
}
Effect_DrawAxisOfRotationToAll(this.origin, this.angles, ORIGIN_SIZE, g_iLaserIndex, 0, 0, 30, 0.2, 0.1, 0.1, 0, 0.0, 0);
@@ -147,9 +152,6 @@ enum struct EditorData {
void UpdateEntity() {
int alpha = this.color[3];
// Keep previews transparent
- if(this.flags & Edit_Preview) {
- alpha = 200;
- }
SetEntityRenderColor(this.entity, this.color[0], this.color[1], this.color[2], alpha);
}
@@ -178,6 +180,10 @@ enum struct EditorData {
void SetName(const char[] name) {
strcopy(this.name, sizeof(this.name), name);
}
+ void SetCallback(PrivateForward callback, bool isEditCallback) {
+ this.callback = callback;
+ this.isEditCallback = isEditCallback;
+ }
void CycleMode() {
// Remove frozen state when cycling
@@ -210,43 +216,43 @@ enum struct EditorData {
PrintToChat(this.client, "\x04[Editor]\x01 Mode: \x05%s\x01 (Press \x04RELOAD\x01 to change)", MODE_NAME[this.mode]);
}
- void CycleStacker(float tick) {
- if(tick - cmdThrottle[this.client] <= 0.10) return;
+ void CycleStacker() {
int newDirection = view_as(this.stackerDirection) + 1;
if(newDirection == view_as(Stack_Down)) newDirection = 0;
this.stackerDirection = view_as(newDirection);
PrintToChat(this.client, "\x04[Editor]\x01 Stacker: %s\x01", STACK_DIRECTION_NAME[this.stackerDirection]);
- cmdThrottle[this.client] = tick;
}
- void ToggleCollision(float tick) {
- if(tick - cmdThrottle[this.client] <= 0.25) return;
+ void ToggleCollision() {
this.hasCollision = !this.hasCollision
PrintToChat(this.client, "\x04[Editor]\x01 Collision: %s", ON_OFF_STRING[view_as(this.hasCollision)]);
- cmdThrottle[this.client] = tick;
}
- void ToggleCollisionRotate(float tick) {
- if(tick - cmdThrottle[this.client] <= 0.20) return;
+ void ToggleCollisionRotate() {
this.hasCollisionRotate = !this.hasCollisionRotate
PrintToChat(this.client, "\x04[Editor]\x01 Rotate with Collision: %s", ON_OFF_STRING[view_as(this.hasCollisionRotate)]);
- cmdThrottle[this.client] = tick;
}
- void CycleAxis(float tick) {
- if(tick - cmdThrottle[this.client] <= 0.1) return;
+ void CycleAxis() {
+ // if(tick - cmdThrottle[this.client] <= 0.1) return;
if(this.axis == 0) {
this.axis = 1;
- PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05HEADING (Y)\x01");
- } else if(this.axis == 1) {
- this.axis = 2;
- PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05PITCH (X)\x01");
+ PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05ROLL (Z)\x01");
} else {
this.axis = 0;
- PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05ROLL (Z)\x01");
+ PrintToChat(this.client, "\x04[Editor]\x01 Rotate Axis: \x05PITCH AND HEADING (X, Y)\x01");
+ }
+ // cmdThrottle[this.client] = tick;
+ }
+
+ void IncrementAxis(int axis, int mouse) {
+ if(this.snapAngle == 1) {
+ this.angles[axis] += mouse * this.rotateSpeed;
+ } else {
+ if(mouse > 0) this.angles[axis] += this.snapAngle;
+ else if(mouse < 0) this.angles[axis] -= this.snapAngle;
}
- cmdThrottle[this.client] = tick;
}
void CycleSnapAngle(float tick) {
@@ -328,18 +334,38 @@ enum struct EditorData {
// Complete the edit, wall creation, or spawning
CompleteType Done(int& entity) {
+ CompleteType type;
if(this.flags & Edit_WallCreator) {
- return this._FinishWall(entity) ? Complete_WallSuccess : Complete_WallError;
+ type = this._FinishWall(entity) ? Complete_WallSuccess : Complete_WallError;
} else if(this.flags & Edit_Preview) {
- return this._FinishPreview(entity) ? Complete_PropSpawned : Complete_PropError;
+ type = this._FinishPreview(entity) ? Complete_PropSpawned : Complete_PropError;
} else {
// Is edit, do nothing, just reset
PrintHintText(this.client, "Edit Complete");
this.Reset();
entity = 0;
- return Complete_EditSuccess;
+ type = Complete_EditSuccess;
}
+ if(this.callback) {
+ Call_StartForward(this.callback);
+ Call_PushCell(this.client);
+ Call_PushCell(entity);
+ Call_PushCell(type);
+ bool result;
+ Call_Finish(result);
+ // Cancel menu:
+ if(this.isEditCallback) delete this.callback;
+ if(this.isEditCallback || !result) {
+ // No native way to close a menu, so open a dummy menu and close it:
+ // Handler doesn't matter, no options are added
+ Menu menu = new Menu(Spawn_RootHandler);
+ menu.Display(this.client, 1);
+ } else {
+ delete this.callback;
+ }
+ }
+ return type;
}
bool _FinishWall(int& id) {
@@ -569,7 +595,7 @@ enum struct EditorData {
DispatchKeyValue(entity, "targetname", "prop_preview");
DispatchKeyValue(entity, "solid", "0");
DispatchKeyValue(entity, "rendercolor", "255 128 255");
- DispatchKeyValue(entity, "renderamt", "200");
+ DispatchKeyValue(entity, "renderamt", "255");
DispatchKeyValue(entity, "rendermode", "1");
TeleportEntity(entity, this.origin, NULL_VECTOR, NULL_VECTOR);
if(!DispatchSpawn(entity)) {
@@ -583,8 +609,11 @@ enum struct EditorData {
return IsValidEntity(entity);
}
- // Adds an existing entity to the editor, to move it.
- // asWallCopy: to instead copy the wall's size and position (walls only)
+ /**
+ * Adds an existing entity to the editor, to move it.
+ * asWallCopy: to instead copy the wall's size and position (walls only)
+ * @deprecated
+ */
void Import(int entity, bool asWallCopy = false, editMode mode = SCALE) {
this.Reset();
GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
@@ -599,6 +628,22 @@ enum struct EditorData {
this.SetMode(mode);
}
+ /**
+ * Imports an entity
+ */
+ void ImportEntity(int entity, int flags = 0, editMode mode = SCALE) {
+ this.Reset();
+ this.flags = flags;
+ GetEntPropVector(entity, Prop_Send, "m_vecOrigin", this.origin);
+ GetEntPropVector(entity, Prop_Send, "m_angRotation", this.angles);
+ this.prevOrigin = this.origin;
+ this.prevAngles = this.angles;
+ GetEntPropVector(entity, Prop_Send, "m_vecMins", this.mins);
+ GetEntPropVector(entity, Prop_Send, "m_vecMaxs", this.size);
+ this.entity = entity;
+ this.SetMode(mode);
+ }
+
// Cancels the current placement. If the edit is a copy/preview, the entity is also deleted
// If entity is not a wall, it will be returned
void Cancel() {
@@ -611,9 +656,22 @@ enum struct EditorData {
}
this.SetMode(INACTIVE);
PrintHintText(this.client, "Cancelled");
+ if(this.callback) {
+ delete this.callback;
+ }
// CPrintToChat(this.client, "\x04[Editor]\x01 Cancelled.");
}
}
+
+void SendEditorMessage(int client, const char[] format, any ...) {
+ char message[256];
+ VFormat(message, sizeof(message), format, 3);
+ CPrintToChat(client, "\x04`[Editor]\x01 %s", message);
+}
+
+stock float RoundToNearestInterval(float value, int interval) {
+ return float(RoundFloat(value / float(interval)) * interval);
+}
EditorData Editor[MAXPLAYERS+1];
Action OnWallClicked(int entity, int activator, int caller, UseType type, float value) {
diff --git a/scripting/include/hats/hats.sp b/scripting/include/hats/hats.sp
index 204678d..d8f846c 100644
--- a/scripting/include/hats/hats.sp
+++ b/scripting/include/hats/hats.sp
@@ -43,7 +43,7 @@ int lastHatRequestTime[MAXPLAYERS+1];
HatInstance hatData[MAXPLAYERS+1];
StringMap g_HatPresets;
-#define MAX_FORBIDDEN_CLASSNAMES 14
+#define MAX_FORBIDDEN_CLASSNAMES 15
char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
"prop_door_rotating_checkpoint",
"env_physics_blocker",
@@ -59,7 +59,8 @@ char FORBIDDEN_CLASSNAMES[MAX_FORBIDDEN_CLASSNAMES][] = {
// "infected",
"func_lod",
"func_door",
- "prop_ragdoll"
+ "prop_ragdoll",
+ "move_rope"
};
#define MAX_FORBIDDEN_MODELS 2
@@ -73,7 +74,7 @@ char FORBIDDEN_MODELS[MAX_FORBIDDEN_MODELS][] = {
// Classnames that should automatically trigger reverse infected
static char REVERSE_CLASSNAMES[MAX_REVERSE_CLASSNAMES][] = {
"infected",
- "func_movelinear"
+ "func_movelinear",
};
Action Command_DoAHat(int client, int args) {
diff --git a/scripting/include/hats/natives.sp b/scripting/include/hats/natives.sp
new file mode 100644
index 0000000..f2911ad
--- /dev/null
+++ b/scripting/include/hats/natives.sp
@@ -0,0 +1,122 @@
+
+int Native_StartEdit(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ int entity = GetNativeCell(2);
+ Editor[client].Import(entity, false);
+ PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
+ fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(3));
+ Editor[client].SetCallback(fwd, true);
+ return 0;
+}
+int Native_StartSpawner(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ g_PropData[client].Selector.Cancel();
+ PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell);
+ fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(2));
+ Editor[client].SetCallback(fwd, false);
+ ShowCategoryList(client, ROOT_CATEGORY);
+ return 0;
+}
+int Native_CancelEdit(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ Editor[client].Cancel();
+ return 0;
+}
+int Native_IsEditorActive(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ Editor[client].IsActive();
+ return 0;
+}
+
+int Native_StartSelector(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ int color[3] = { 0, 255, 0 };
+ PrivateForward fwd = new PrivateForward(ET_Single, Param_Cell, Param_Cell);
+ fwd.AddFunction(plugin, GetNativeFunction(2));
+ GetNativeArray(3, color, 3);
+ int limit = GetNativeCell(4);
+ g_PropData[client].Selector.Start(color, 0, limit);
+ g_PropData[client].Selector.SetOnEnd(fwd);
+ return 0;
+}
+int Native_CancelSelector(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ g_PropData[client].Selector.Cancel();
+ return 0;
+}
+int Native_IsSelectorActive(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ g_PropData[client].Selector.IsActive();
+ return 0;
+}
+int Native_Selector_Start(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ int color[3] = { 0, 255, 0 };
+ GetNativeArray(2, color, 3);
+ int flags = GetNativeCell(3);
+ int limit = GetNativeCell(4);
+ g_PropData[client].Selector.Start(color, flags, limit);
+ return 0;
+}
+int Native_Selector_GetCount(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ if(!g_PropData[client].Selector.IsActive()) {
+ return -1;
+ } else {
+ return g_PropData[client].Selector.list.Length;
+ }
+}
+int Native_Selector_GetActive(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ return g_PropData[client].Selector.IsActive();
+}
+int Native_Selector_SetOnEnd(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
+ fwd.AddFunction(plugin, GetNativeFunction(2));
+ g_PropData[client].Selector.SetOnEnd(fwd);
+ return 0;
+}
+int Native_Selector_SetOnPreSelect(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ PrivateForward fwd = new PrivateForward(ET_Single, Param_Cell, Param_Cell);
+ if(!fwd.AddFunction(plugin, GetNativeFunction(2))) return 0;
+ g_PropData[client].Selector.SetOnPreSelect(fwd);
+ return 1;
+}
+int Native_Selector_SetOnPostSelect(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
+ if(!fwd.AddFunction(plugin, GetNativeFunction(2))) return 0;
+ g_PropData[client].Selector.SetOnPostSelect(fwd);
+ return 1;
+}
+int Native_Selector_SetOnUnselect(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
+ if(!fwd.AddFunction(plugin, GetNativeFunction(2))) return 0;
+ g_PropData[client].Selector.SetOnUnselect(fwd);
+ return 1;
+}
+int Native_Selector_AddEntity(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ int entity = GetNativeCell(2);
+ g_PropData[client].Selector.AddEntity(entity, false);
+ return 0;
+}
+int Native_Selector_RemoveEntity(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ int entity = GetNativeCell(2);
+ g_PropData[client].Selector.RemoveEntity(entity);
+ return 0;
+}
+int Native_Selector_Cancel(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ g_PropData[client].Selector.Cancel();
+ return 0;
+}
+int Native_Selector_End(Handle plugin, int numParams) {
+ int client = GetNativeCell(1);
+ g_PropData[client].Selector.End();
+ return 0;
+}
\ No newline at end of file
diff --git a/scripting/include/hats/props/base.sp b/scripting/include/hats/props/base.sp
index 36fbbec..5864850 100644
--- a/scripting/include/hats/props/base.sp
+++ b/scripting/include/hats/props/base.sp
@@ -25,6 +25,8 @@ enum SaveType {
Save_Schematic
}
+int GLOW_MANAGER[3] = { 52, 174, 235 };
+
enum struct Schematic {
char name[64];
char creatorSteamid[32];
@@ -134,6 +136,197 @@ public any Native_SpawnSchematic(Handle plugin, int numParams) {
delete list;
return true;
}
+
+enum struct PropSelectorIterator {
+ ArrayList _list;
+ int _index;
+ int Entity;
+
+ void _Init(ArrayList list) {
+ this._list = list;
+ this._index = -1;
+ }
+
+ bool Next() {
+ this._index++;
+ return this._index + 1 < this._list.Length;
+ }
+
+}
+
+
+enum struct PropSelector {
+ int selectColor[3];
+ int limit;
+ ArrayList list;
+ PrivateForward endCallback;
+ PrivateForward selectPreCallback;
+ PrivateForward selectPostCallback;
+ PrivateForward unSelectCallback;
+ int _client;
+
+ PropSelectorIterator Iter() {
+ PropSelectorIterator iter;
+ iter._Init(this.list);
+ return iter;
+ }
+
+ void Reset() {
+ if(this.endCallback) delete this.endCallback;
+ if(this.selectPreCallback) delete this.selectPreCallback;
+ if(this.selectPostCallback) delete this.selectPostCallback;
+ if(this.unSelectCallback) delete this.unSelectCallback;
+ if(this.list) delete this.list;
+ }
+
+ void Start(int color[3], int flags = 0, int limit = 0) {
+ this.selectColor = color;
+ this.limit = 0;
+ this.list = new ArrayList();
+ SendEditorMessage(this._client, "Left click to select, right click to unselect");
+ SendEditorMessage(this._client, "Press WALK+USE to confirm, DUCK+USE to cancel");
+ }
+
+ void SetOnEnd(PrivateForward callback) {
+ this.endCallback = callback;
+ }
+ void SetOnPreSelect(PrivateForward callback) {
+ this.selectPreCallback = callback;
+ }
+ void SetOnPostSelect(PrivateForward callback) {
+ this.selectPostCallback = callback;
+ }
+ void SetOnUnselect(PrivateForward callback) {
+ this.unSelectCallback = callback;
+ }
+
+ void StartDirect(int color[3], SelectDoneCallback callback, int limit = 0) {
+ PrivateForward fwd = new PrivateForward(ET_Ignore, Param_Cell, Param_Cell);
+ fwd.AddFunction(INVALID_HANDLE, callback);
+ this.Start(color, 0, limit);
+ this.SetOnEnd(fwd);
+ }
+
+ bool IsActive() {
+ return this.list != null;
+ }
+
+ void End() {
+ if(this.list == null) return;
+ SendEditorMessage(this._client, "Selection completed");
+ // Reset glows, remove selection from our spawned props
+ for(int i = 0; i < this.list.Length; i++) {
+ int ref = this.list.Get(i);
+ if(IsValidEntity(ref)) {
+ L4D2_RemoveEntityGlow(ref);
+ RemoveSpawnedProp(ref);
+ }
+ }
+ if(this.endCallback) {
+ if(GetForwardFunctionCount(this.endCallback) == 0) {
+ PrintToServer("[Editor] Warn: Selector.End(): callback has no functions assigned to it.");
+ }
+ Call_StartForward(this.endCallback);
+ Call_PushCell(this._client);
+ Call_PushCell(this.list.Clone());
+ int result = Call_Finish();
+ if(result != SP_ERROR_NONE) {
+ PrintToServer("[Editor] Warn: Selector.End() forward error: %d", result);
+ }
+ } else {
+ PrintToServer("[Editor] Warn: Selector.End() called but no callback assigned, voiding list");
+ }
+ this.Reset();
+ }
+
+ void Cancel() {
+ if(this.endCallback) {
+ Call_StartForward(this.endCallback);
+ Call_PushCell(this._client);
+ Call_PushCell(INVALID_HANDLE);
+ Call_Finish();
+ }
+ if(this.list) {
+ for(int i = 0; i < this.list.Length; i++) {
+ int ref = this.list.Get(i);
+ L4D2_RemoveEntityGlow(ref);
+ }
+ }
+ PrintToChat(this._client, "\x04[Editor]\x01 Selection cancelled.");
+ this.Reset();
+ }
+
+ int GetEntityRefIndex(int ref) {
+ int index = this.list.FindValue(ref);
+ if(index > -1) {
+ return index;
+ }
+ return -1;
+ }
+
+ /** Removes entity from list
+ * @return returns entity ref of entity removed
+ */
+ int RemoveEntity(int entity) {
+ if(this.list == null) return -2;
+
+ L4D2_RemoveEntityGlow(entity);
+ int ref = EntIndexToEntRef(entity);
+ int index = this.GetEntityRefIndex(ref);
+ if(index > -1) {
+ this.list.Erase(index);
+ if(this.unSelectCallback != null) {
+ Call_StartForward(this.unSelectCallback)
+ Call_PushCell(this._client);
+ Call_PushCell(EntRefToEntIndex(ref));
+ Call_Finish();
+ }
+ return ref;
+ }
+ return INVALID_ENT_REFERENCE;
+ }
+
+ /**
+ * Adds entity to list
+ * @return index into list of entity
+ * @return -1 if already added
+ * @return -2 if callback rejected
+ */
+ int AddEntity(int entity, bool useCallback = true) {
+ if(this.list == null) return -2;
+
+ int ref = EntIndexToEntRef(entity);
+ if(this.GetEntityRefIndex(ref) == -1) {
+ PrintToServer("Selector.AddEntity: PRE CALLBACK");
+ // FIXME: crashes server, sourcemod bug
+ /*if(this.selectPreCallback != null && useCallback) {
+ Call_StartForward(this.selectPreCallback)
+ Call_PushCell(this._client);
+ Call_PushCell(entity);
+ bool allowed = true;
+ PrintToServer("Selector.AddEntity: PRE CALLBACK pre finish");
+ Call_Finish(allowed);
+ PrintToServer("Selector.AddEntity: PRE CALLBACK pre result %b", allowed);
+ if(!allowed) return -2;
+ }*/
+
+ L4D2_SetEntityGlow(entity, L4D2Glow_Constant, 10000, 0, this.selectColor, false);
+ int index = this.list.Push(ref);
+ PrintToServer("Selector.AddEntity: post CALLBACK pre");
+ //FIXME: crashes server, sourcemod bug
+ /*if(this.selectPostCallback != null && useCallback) {
+ Call_StartForward(this.selectPostCallback)
+ Call_PushCell(this._client);
+ Call_PushCell(entity);
+ //Call_PushCell(index);
+ Call_Finish();
+ }*/
+ PrintToServer("Selector.AddEntity: post CALLBACK post");
+ return index;
+ }
+ return -1;
+ }
+}
enum struct PlayerPropData {
ArrayList categoryStack;
ArrayList itemBuffer;
@@ -144,14 +337,19 @@ enum struct PlayerPropData {
int lastActiveTime;
char classnameOverride[64];
ChatPrompt chatPrompt;
- ArrayList markedProps;
+ PropSelector Selector;
SaveType pendingSaveType;
Schematic schematic;
+ int highlightedEntityRef;
+ int managerEntityRef;
+ void Init(int client) {
+ this.Selector._client = client;
+ }
// Called on PlayerDisconnect
void Reset() {
- if(this.markedProps != null) delete this.markedProps;
+ if(this.Selector.IsActive()) this.Selector.Cancel();
this.chatPrompt = Prompt_None;
this.clearListBuffer = false;
this.lastCategoryIndex = 0;
@@ -161,6 +359,19 @@ enum struct PlayerPropData {
this.CleanupBuffers();
this.pendingSaveType = Save_None;
this.schematic.Reset();
+ this.managerEntityRef = INVALID_ENT_REFERENCE;
+ this.StopHighlight();
+ }
+
+ void StartHighlight(int entity) {
+ this.highlightedEntityRef = EntIndexToEntRef(entity);
+ L4D2_SetEntityGlow(entity, L4D2Glow_Constant, 10000, 0, GLOW_MANAGER, false);
+ }
+ void StopHighlight() {
+ if(IsValidEntity(this.highlightedEntityRef)) {
+ L4D2_RemoveEntityGlow(this.highlightedEntityRef);
+ }
+ this.highlightedEntityRef = INVALID_ENT_REFERENCE;
}
void StartSchematic(int client, const char[] name) {
diff --git a/scripting/include/hats/props/menu_handlers.sp b/scripting/include/hats/props/menu_handlers.sp
index 9cf8e38..55133b3 100644
--- a/scripting/include/hats/props/menu_handlers.sp
+++ b/scripting/include/hats/props/menu_handlers.sp
@@ -8,6 +8,8 @@ public void OnAdminMenuReady(Handle topMenuHandle) {
topMenu.AddItem("editor_edit", AdminMenu_Edit, g_propSpawnerCategory, "sm_prop");
topMenu.AddItem("editor_delete", AdminMenu_Delete, g_propSpawnerCategory, "sm_prop");
topMenu.AddItem("editor_saveload", AdminMenu_SaveLoad, g_propSpawnerCategory, "sm_prop");
+ topMenu.AddItem("editor_manager", AdminMenu_Manager, g_propSpawnerCategory, "sm_prop");
+ topMenu.AddItem("editor_selector", AdminMenu_Selector, g_propSpawnerCategory, "sm_prop");
}
g_topMenu = topMenu;
}
@@ -24,6 +26,15 @@ void Category_Handler(TopMenu topmenu, TopMenuAction action, TopMenuObject topob
}
}
+void AdminMenu_Selector(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
+ if(action == TopMenuAction_DisplayOption) {
+ Format(buffer, maxlength, "Selector");
+ } else if(action == TopMenuAction_SelectOption) {
+ ShowManagerSelectorMenu(param);
+ }
+}
+
+
void AdminMenu_Spawn(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
if(action == TopMenuAction_DisplayOption) {
Format(buffer, maxlength, "Spawn Props");
@@ -79,6 +90,14 @@ void AdminMenu_SaveLoad(TopMenu topmenu, TopMenuAction action, TopMenuObject obj
}
}
+void AdminMenu_Manager(TopMenu topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength) {
+ if(action == TopMenuAction_DisplayOption) {
+ Format(buffer, maxlength, "Manager (ALPHA)");
+ } else if(action == TopMenuAction_SelectOption) {
+ Spawn_ShowManagerMainMenu(param);
+ }
+}
+
int SaveLoadMainMenuHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[2];
@@ -87,7 +106,7 @@ int SaveLoadMainMenuHandler(Menu menu, MenuAction action, int client, int param2
ShowSaves(client, type);
} else if (action == MenuAction_Cancel) {
if(param2 == MenuCancel_ExitBack) {
- Spawn_ShowSaveLoadMainMenu(client);
+ DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
}
} else if (action == MenuAction_End)
delete menu;
@@ -127,6 +146,7 @@ int SaveLoadSceneHandler(Menu menu, MenuAction action, int client, int param2) {
return 0;
}
+
int SaveLoadSchematicHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char saveName[64];
@@ -189,40 +209,160 @@ int SaveLoadConfirmHandler(Menu menu, MenuAction action, int client, int param2)
delete menu;
return 0;
}
-
-int DeleteHandler(Menu menu, MenuAction action, int client, int param2) {
+int ManagerHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[8];
menu.GetItem(param2, info, sizeof(info));
- int index = StringToInt(info);
- if(index == -1) {
+ if(info[0] != '\0') {
+ int index = StringToInt(info);
+ int ref = g_spawnedItems.Get(index);
+ // TODO: add delete confirm
+ if(!IsValidEntity(ref)) {
+ SendEditorMessage(client, "Entity has disappeared");
+ } else {
+ int entity = EntRefToEntIndex(ref);
+ g_PropData[client].managerEntityRef = ref;
+ g_PropData[client].StartHighlight(entity);
+ ShowManagerEntityMenu(client, entity);
+ }
+ }
+ } else if (action == MenuAction_Cancel) {
+ if(param2 == MenuCancel_ExitBack) {
+ Spawn_ShowSaveLoadMainMenu(client);
+ }
+ } else if (action == MenuAction_End)
+ delete menu;
+ return 0;
+}
+int ManagerEntityHandler(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ g_PropData[client].StopHighlight();
+ char info[32];
+ menu.GetItem(param2, info, sizeof(info));
+ int ref = g_PropData[client].managerEntityRef;
+ if(!IsValidEntity(ref)) {
+ SendEditorMessage(client, "Entity disappeared");
+ return 0;
+ }
+ if(StrEqual(info, "edit")) {
+ Editor[client].ImportEntity(EntRefToEntIndex(ref), Edit_Manager);
+ Spawn_ShowManagerMainMenu(client);
+ } else if(StrEqual(info, "delete")) {
+ for(int i = 0; i < g_spawnedItems.Length; i++) {
+ int spawnedRef = g_spawnedItems.Get(i);
+ if(spawnedRef == ref) {
+ g_spawnedItems.Erase(i);
+ break;
+ }
+ }
+ if(IsValidEntity(ref)) {
+ RemoveEntity(ref);
+ }
+ Spawn_ShowManagerMainMenu(client);
+ } else if(StrEqual(info, "view")) {
+ ReplyToCommand(client, "Maybe soon.");
+ } else if(StrEqual(info, "select")) {
+ ShowManagerSelectorMenu(client);
+ int entity = EntRefToEntIndex(ref);
+ g_PropData[client].Selector.AddEntity(entity);
+ } else {
+ SendEditorMessage(client, "Unknown option / not implemented");
+ }
+ } else if (action == MenuAction_Cancel) {
+ g_PropData[client].StopHighlight();
+ if(param2 == MenuCancel_ExitBack) {
+ Spawn_ShowManagerMainMenu(client);
+ }
+ } else if (action == MenuAction_End)
+ delete menu;
+ return 0;
+}
+int ManagerSelectorMainMenuHandler(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ if(!EntitySelector.FromClient(client).Active) {
+ return 0;
+ }
+ char info[32];
+ menu.GetItem(param2, info, sizeof(info));
+ if(StrEqual(info, "list")) {
+ SendEditorMessage(client, "Not implemented");
+ } else if(StrEqual(info, "actions")) {
+ ShowManagerSelectorActionsMenu(client);
+ } else if(StrEqual(info, "cancel")) {
+ g_PropData[client].Selector.Cancel();
+ }
+ } else if (action == MenuAction_Cancel) {
+ g_PropData[client].Selector.Cancel();
+ } else if (action == MenuAction_End)
+ delete menu;
+ return 0;
+}
+int ManagerSelectorActionHandler(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ if(!g_PropData[client].Selector.IsActive()) {
+ return 0;
+ }
+ char info[32];
+ menu.GetItem(param2, info, sizeof(info));
+ if(StrEqual(info, "delete")) {
+ for(int i = 0; i < g_PropData[client].Selector.list.Length; i++) {
+ int ref = g_PropData[client].Selector.list.Get(i);
+ if(IsValidEntity(ref)) {
+ RemoveEntity(ref);
+ }
+ }
+ g_PropData[client].Selector.End();
+ Spawn_ShowManagerMainMenu(client);
+ } else if(StrEqual(info, "save")) {
+ // TODO: implement
+ SendEditorMessage(client, "Not implemented");
+ } else {
+ SendEditorMessage(client, "Unknown option / not implemented");
+ }
+ } else if (action == MenuAction_Cancel) {
+ if(param2 == MenuCancel_ExitBack) {
+ Spawn_ShowSaveLoadMainMenu(client);
+ }
+ } else if (action == MenuAction_End)
+ delete menu;
+ return 0;
+}
+int COLOR_DELETE[3] = { 255, 0, 0 }
+
+int DeleteHandler(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ char info[128];
+ menu.GetItem(param2, info, sizeof(info));
+ int ref = StringToInt(info[2]);
+ int option = StringToInt(info);
+ if(option == -1) {
// Delete all (everyone)
int count = DeleteAll();
PrintToChat(client, "\x04[Editor]\x01 Deleted \x05%d\x01 items", count);
ShowDeleteList(client);
- } else if(index == -2) {
+ } else if(option == -2) {
// Delete all (mine only)
int count = DeleteAll(client);
PrintToChat(client, "\x04[Editor]\x01 Deleted \x05%d\x01 items", count);
ShowDeleteList(client);
- } else if(index == -3) {
- if(g_PropData[client].markedProps != null) {
- EndDeleteTool(client, false);
+ } else if(option == -3) {
+ if(g_PropData[client].Selector.IsActive()) {
+ g_PropData[client].Selector.End();
+ PrintToChat(client, "\x04[Editor]\x01 Delete tool cancelled");
} else {
- g_PropData[client].markedProps = new ArrayList();
+ g_PropData[client].Selector.StartDirect(COLOR_DELETE, OnDeleteToolEnd);
PrintToChat(client, "\x04[Editor]\x01 Delete tool active. Press \x05Left Mouse\x01 to mark props, \x05Right Mouse\x01 to undo. SHIFT+USE to spawn, CTRL+USE to cancel");
}
ShowDeleteList(client);
} else {
- int ref = g_spawnedItems.Get(index);
- // TODO: add delete confirm
+ int index = g_spawnedItems.FindValue(ref);
if(IsValidEntity(ref)) {
RemoveEntity(ref);
}
- g_spawnedItems.Erase(index);
- if(index > 0) {
+ if(index > -1) {
+ g_spawnedItems.Erase(index);
index--;
- }
+ } else { index = 0; }
ShowDeleteList(client, index);
}
} else if (action == MenuAction_Cancel) {
@@ -317,16 +457,18 @@ int EditHandler(Menu menu, MenuAction action, int client, int param2) {
if (action == MenuAction_Select) {
char info[8];
menu.GetItem(param2, info, sizeof(info));
- int index = StringToInt(info);
- int ref = g_spawnedItems.Get(index);
+ int ref = StringToInt(info);
+ int index = g_spawnedItems.FindValue(ref);
int entity = EntRefToEntIndex(ref);
if(entity > 0) {
Editor[client].Import(entity, false);
PrintToChat(client, "\x04[Editor]\x01 Editing entity \x05%d", entity);
} else {
PrintToChat(client, "\x04[Editor]\x01 Entity disappeared.");
- g_spawnedItems.Erase(index);
- index--;
+ if(index > -1) {
+ g_spawnedItems.Erase(index);
+ index--;
+ } else { index = 0; }
}
ShowEditList(client, index);
} else if (action == MenuAction_Cancel) {
diff --git a/scripting/include/hats/props/menu_methods.sp b/scripting/include/hats/props/menu_methods.sp
index b33cddf..716bb1e 100644
--- a/scripting/include/hats/props/menu_methods.sp
+++ b/scripting/include/hats/props/menu_methods.sp
@@ -3,11 +3,11 @@
/////////////
void ShowSpawnRoot(int client) {
Menu menu = new Menu(Spawn_RootHandler);
- menu.SetTitle("Choose list:");
- menu.AddItem("f", "Favorites (WIP)");
- menu.AddItem("r", "Recents");
- menu.AddItem("s", "Search");
- menu.AddItem("n", "Prop List");
+ menu.SetTitle("Choose spawn list:");
+ menu.AddItem("f", "Favorites (Broken :D)");
+ menu.AddItem("r", "Recently Spawned Props");
+ menu.AddItem("s", "Search for Props");
+ menu.AddItem("n", "Browse Props");
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, MENU_TIME_FOREVER);
@@ -17,6 +17,7 @@ void Spawn_ShowRecents(int client) {
ArrayList items = GetRecentsItemList();
if(items.Length == 0) {
CReplyToCommand(client, "\x04[Editor] \x01No recent props spawned.");
+ DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
return;
}
ShowTempItemMenu(client, items, "Recents");
@@ -26,6 +27,11 @@ void Spawn_ShowSearch(int client) {
CReplyToCommand(client, "\x04[Editor] \x01Please enter search query in chat:");
}
void ShowDeleteList(int client, int index = -3) {
+ if(g_spawnedItems.Length == 0) {
+ SendEditorMessage(client, "No spawned items to delete");
+ DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
+ return;
+ }
Menu menu = new Menu(DeleteHandler);
menu.SetTitle("Delete Props");
@@ -38,7 +44,7 @@ void ShowDeleteList(int client, int index = -3) {
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = GetSpawnedItem(i);
if(ref == -1) continue;
- IntToString(i, info, sizeof(info));
+ Format(info, sizeof(info), "0|%d", ref);
GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
index = FindCharInString(buffer, '/', true);
if(index != -1)
@@ -52,6 +58,11 @@ void ShowDeleteList(int client, int index = -3) {
menu.DisplayAt(client, 0, MENU_TIME_FOREVER);
}
void ShowEditList(int client, int index = 0) {
+ if(g_spawnedItems.Length == 0) {
+ SendEditorMessage(client, "No spawned items to edit");
+ DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
+ return;
+ }
Menu menu = new Menu(EditHandler);
menu.SetTitle("Edit Prop");
@@ -60,7 +71,7 @@ void ShowEditList(int client, int index = 0) {
for(int i = 0; i < g_spawnedItems.Length; i++) {
int ref = GetSpawnedItem(i);
if(ref == -1) continue;
- IntToString(i, info, sizeof(info));
+ Format(info, sizeof(info), "%d", ref);
GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
index = FindCharInString(buffer, '/', true);
if(index != -1)
@@ -104,6 +115,8 @@ void _showItemMenu(int client, ArrayList items, const char[] title = "", bool cl
items = g_PropData[client].itemBuffer;
if(items == null) {
LogError("Previous list does not exist and no new list was provided ShowItemMenu(%N)", client);
+ PrintToChat(client, "\x04[Editor]\x01 An error occurred (no list)");
+ return;
}
} else {
// Populate the buffer with this list
@@ -194,6 +207,81 @@ void Spawn_ShowSaveLoadMainMenu(int client) {
menu.Display(client, MENU_TIME_FOREVER);
}
+void Spawn_ShowManagerMainMenu(int client, int index = 0) {
+ if(g_spawnedItems.Length == 0) {
+ SendEditorMessage(client, "No spawned items to manage");
+ DisplayTopMenuCategory(g_topMenu, g_propSpawnerCategory, client);
+ return;
+ }
+ Menu menu = new Menu(ManagerHandler);
+ menu.SetTitle("Manager");
+ // Id is SaveType
+ char info[8];
+ char buffer[128];
+ for(int i = 0; i < g_spawnedItems.Length; i++) {
+ int ref = GetSpawnedItem(i);
+ if(ref == -1) continue;
+ IntToString(i, info, sizeof(info));
+ GetEntPropString(ref, Prop_Data, "m_ModelName", buffer, sizeof(buffer));
+ index = FindCharInString(buffer, '/', true);
+ if(index != -1)
+ menu.AddItem(info, buffer[index + 1]);
+ }
+
+ menu.ExitBackButton = true;
+ menu.ExitButton = true;
+ menu.DisplayAt(client, index, MENU_TIME_FOREVER);
+}
+void ShowManagerEntityMenu(int client, int entity) {
+ if(!IsValidEntity(entity)) {
+ SendEditorMessage(client, "Item has vanished");
+ Spawn_ShowManagerMainMenu(client);
+ return;
+ }
+ Menu menu = new Menu(ManagerEntityHandler);
+ menu.SetTitle("Manage %d", entity);
+ menu.AddItem("edit", "Edit");
+ menu.AddItem("delete", "Delete");
+ menu.AddItem("select", "Select");
+ menu.AddItem("view", "View");
+ menu.ExitBackButton = true;
+ menu.ExitButton = true;
+ menu.Display(client, MENU_TIME_FOREVER);
+}
+void ShowManagerSelectorMenu(int client) {
+ EntitySelector sel = EntitySelector.FromClient(client);
+ if(!sel.Active) {
+ sel.Start(GLOW_MANAGER);
+ sel.SetOnEnd(OnManagerSelectorEnd);
+ sel.SetOnPostSelect(OnManagerSelectorSelect);
+ sel.SetOnUnselect(OnManagerSelectorSelect);
+ }
+ Menu menu = new Menu(ManagerSelectorMainMenuHandler);
+ menu.SetTitle("Selector");
+ menu.AddItem("list", "> List Entities");
+ menu.AddItem("actions", "> Actions");
+ menu.AddItem("add-self", "Add All Self-Spawned");
+ menu.AddItem("add-all", "Add All Spawned");
+ menu.ExitBackButton = false;
+ menu.ExitButton = true;
+ menu.Display(client, MENU_TIME_FOREVER);
+}
+void ShowManagerSelectorActionsMenu(int client) {
+ Menu menu = new Menu(ManagerSelectorActionHandler);
+ menu.SetTitle("Selector: Select action");
+ char display[32];
+ Format(display, sizeof(display), "Entities: %d", g_PropData[client].Selector.list.Length);
+ menu.AddItem("", display, ITEMDRAW_DISABLED);
+
+ // menu.AddItem("edit", "Edit");
+ menu.AddItem("delete", "Delete");
+ // menu.AddItem("select", "Select");
+ menu.AddItem("save", "Save");
+ menu.ExitBackButton = true;
+ menu.ExitButton = true;
+ menu.Display(client, MENU_TIME_FOREVER);
+}
+
void ShowSaves(int client, SaveType type) {
ArrayList saves;
Menu newMenu;
diff --git a/scripting/include/hats/props/methods.sp b/scripting/include/hats/props/methods.sp
index a303a5c..f1ef852 100644
--- a/scripting/include/hats/props/methods.sp
+++ b/scripting/include/hats/props/methods.sp
@@ -378,9 +378,7 @@ ArrayList SearchItems(const char[] query) {
SearchData data;
for(int i = 0; i < results.Length; i++) {
results.GetArray(i, data);
- PrintToConsoleAll("%d | data=\"%s\"", i, data.model);
item.FromSearchData(data);
- PrintToConsoleAll("%d | item=\"%s\"", i, item.model);
items.PushArray(item);
}
delete results;
@@ -446,26 +444,28 @@ bool RemoveSpawnedProp(int ref) {
return false;
}
-void EndDeleteTool(int client, bool deleteEntities = false) {
- if(g_PropData[client].markedProps != null) {
- int count;
- for(int i = 0; i < g_PropData[client].markedProps.Length; i++) {
- int ref = g_PropData[client].markedProps.Get(i);
- if(IsValidEntity(ref)) {
- count++;
- if(deleteEntities) {
- RemoveSpawnedProp(ref);
- RemoveEntity(ref);
- }
- else L4D2_RemoveEntityGlow(EntRefToEntIndex(ref));
- }
+void OnDeleteToolEnd(int client, ArrayList entities) {
+ int count;
+ for(int i = 0; i < entities.Length; i++) {
+ int ref = entities.Get(i);
+ if(IsValidEntity(ref)) {
+ count++;
+ RemoveSpawnedProp(ref);
+ RemoveEntity(ref);
}
- delete g_PropData[client].markedProps;
- if(deleteEntities)
- PrintToChat(client, "\x04[Editor]\x01 \x05%d\x01 entities deleted", count);
- else
- PrintToChat(client, "\x04[Editor]\x01 Delete tool cancelled");
}
+ delete entities;
+ PrintToChat(client, "\x04[Editor]\x01 \x05%d\x01 entities deleted", count);
+}
+
+void OnManagerSelectorEnd(int client, ArrayList entities) {
+ // TODO: implement manager selector cb
+ ReplyToCommand(client, "Not Implemented");
+ delete entities;
+}
+void OnManagerSelectorSelect(int client, int entity) {
+ // update entity count
+ ShowManagerSelectorMenu(client);
}
int DeleteAll(int onlyPlayer = 0) {
diff --git a/scripting/include/hats_editor.inc b/scripting/include/hats_editor.inc
new file mode 100644
index 0000000..c2f7330
--- /dev/null
+++ b/scripting/include/hats_editor.inc
@@ -0,0 +1,155 @@
+#if defined _editor_included_
+ #endinput
+#endif
+#define _editor_included_
+
+public SharedPlugin __pl_editor_ = {
+ name = "editor",
+ file = "hats.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+#if !defined REQUIRE_PLUGIN
+public void __pl_editor__SetNTVOptional()
+{
+ MarkNativeAsOptional("SpawnSchematic");
+ MarkNativeAsOptional("StartEdit");
+ MarkNativeAsOptional("StartSpawner");
+ MarkNativeAsOptional("CancelEdit");
+ MarkNativeAsOptional("IsEditorActive");
+
+ MarkNativeAsOptional("StartSelector");
+ MarkNativeAsOptional("CancelSelector");
+ MarkNativeAsOptional("IsSelectorActive");
+
+ MarkNativeAsOptional("Selector.Count.get");
+ MarkNativeAsOptional("Selector.Active.get");
+ MarkNativeAsOptional("Selector.Start");
+ MarkNativeAsOptional("Selector.SetOnEnd");
+ MarkNativeAsOptional("Selector.SetOnPreSelect");
+ MarkNativeAsOptional("Selector.SetOnPostSelect");
+ MarkNativeAsOptional("Selector.SetOnUnselect");
+ MarkNativeAsOptional("Selector.AddEntity");
+ MarkNativeAsOptional("Selector.RemoveEntity");
+ MarkNativeAsOptional("Selector.Cancel");
+ MarkNativeAsOptional("Selector.End");
+}
+#endif
+
+
+native bool SpawnSchematic(const char name[32], const float pos[3], const float angles[3] = NULL_VECTOR);
+
+/** Called when edit is done or cancelled
+ * @param client - client doing the edit
+ * @param entity - The entity edited
+ * @param result - Result of the edit, or cancelled
+ * @return boolean - only for StartSpawner, true to continue, false to end spawning
+ */
+typeset EditorDoneCallback {
+ function void (int client, int entity, CompleteType result);
+ function bool (int client, int entity, CompleteType result);
+}
+
+/** Called when an item is to be selected.
+ * @return boolean - TRUE to allow item to be selected, FALSE to reject
+ */
+typedef SelectPreAddCallback = function bool (int client, int entity);
+/** Called when an item has been selected */
+typedef SelectPostAddCallback = function void (int client, int entity);
+
+/** Called when an item is to be unselected. */
+typedef SelectRemoveCallback = function void (int client, int entity);
+/** Called when a user is done selecting items
+ * @param client - client doing the selection
+ * @param entities - if null, selection was cancelled. if not null, contains list of entity references, must be deleted.
+ */
+typedef SelectDoneCallback = function void (int client, ArrayList entities);
+
+/** Starts editing an entity
+ * @param client - The client that is editing
+ * @param entity - The entity to edit
+ * @param doneCallback - Called when edit is done
+ */
+native void StartEdit(int client, int entity, EditorDoneCallback doneCallback);
+/** Let client pick prop(s) to spawn
+ * @param client - The client that is editing
+ * @param entity - The entity to edit
+ * @param doneCallback - Called when edit is done
+ */
+native void StartSpawner(int client, EditorDoneCallback doneCallback);
+native void CancelEdit(int client);
+// Includes non-plugin started edits
+native bool IsEditorActive(int client);
+
+/** Starts a selection, where the client can click on entities to select or deselect them.
+ * @param client - the client that can select
+ * @param callback - called when user is done seleting or cancelled
+ * @param highlightColor - the color to highlight selected items, default solid green
+ * @param maxEntities - the max number of selections, 0 for infinite
+ */
+native void StartSelector(int client, SelectDoneCallback callback, int highlightColor[3] = { 0, 255, 0 }, int maxEntities = 0);
+
+methodmap EntitySelector {
+ public EntitySelector(int client) {
+ return view_as(client);
+ }
+
+ public static EntitySelector FromClient(int client) {
+ return view_as(client);
+ }
+
+ /** Starts a new selector for client
+ * @param highlightColor - the color to highlight selected items, default solid green
+ * @param flags - not used.
+ * @param maxEntities - the max number of selections, 0 for infinite
+ */
+ public native EntitySelector Start(int highlightColor[3], int flags = 0, int maxEntities = 0);
+
+
+ property int Count {
+ /** Returns the number of entities in selector. Returns -1 if not active */
+ public native get();
+ }
+
+ property bool Active {
+ public native get();
+ }
+
+ /** Sets the callback for when the selector is ended (or cancelled) */
+ public native void SetOnEnd(SelectDoneCallback callback);
+
+ /** Sets the callback for when an item is to be added to the selector. */
+ public native void SetOnPreSelect(SelectPreAddCallback callback);
+
+ /** Sets the callback for when an item has been added to the selector. */
+ public native void SetOnPostSelect(SelectPostAddCallback callback);
+
+ /** Sets the callback for when an item is removed from selector. */
+ public native void SetOnUnselect(SelectRemoveCallback callback);
+
+ /** Adds an entity to selection. Does not call SelectAddCallback */
+ public native void AddEntity(int entity);
+
+ /** Removes an entity from selection. Does not call SelectAddCallback */
+ public native void RemoveEntity(int entity);
+
+ public native void Cancel();
+
+ public native void End();
+}
+
+
+native void CancelSelector(int client);
+native bool IsSelectorActive(int client);
+
+enum CompleteType {
+ Complete_WallSuccess,
+ Complete_WallError,
+ Complete_PropSpawned,
+ Complete_PropError,
+ Complete_EditSuccess
+}
diff --git a/scripting/include/overlay.inc b/scripting/include/overlay.inc
index da57d7f..b448d99 100644
--- a/scripting/include/overlay.inc
+++ b/scripting/include/overlay.inc
@@ -2,21 +2,50 @@
#endinput
#endif
#define _overlay_included
-#include
-native bool SendTempUI(int client, const char[] id, int lifetime, JSONObject element);
+public SharedPlugin __pl_overlay = {
+ name = "overlay",
+ file = "overlay.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
-native bool ShowUI(int client, const char[] elemNamespace, const char[] elemId, JSONObject variables);
+#define ACTION_ARG_LENGTH 128 // The length each arg (separated by space) can be
-native bool HideUI(int client, const char[] elemNamespace, const char[] elemId);
+// typedef ActionFallbackHandlerCallback = function void (const char[] actionName, const char[][] args, int numArgs);
+// typedef ActionHandlerCallback = function void (const char[][] args, int numArgs);
+typedef ActionFallbackHandlerCallback = function void (const char[] actionName, UIActionEvent event, int client);
+typedef ActionHandlerCallback = function void (UIActionEvent event, int client);
-native bool PlayAudio(int client, const char[] url);
native bool IsOverlayConnected();
-forward void OnUIAction(const char[] elemNamespace, const char[] elemId, const char[] action);
+// myplugin:action_name
+// Handles any action for actionNamespace and actionName
+native void RegisterActionHandler(const char[] actionNamespace, const char[] actionName, ActionFallbackHandlerCallback cb);
+// Handles all actions for namespace that were not caught by RegisterActionHandler
+native void RegisterActionAnyHandler(const char[] actionNamespace, ActionHandlerCallback cb);
-typedef UIActionCallback = function void (const char[][] args, int numArgs);
+methodmap UIActionEvent {
+ public UIActionEvent(ArrayList list) {
+ return view_as(list);
+ }
+
+ public void GetArg(int argNum, char[] output, int maxlen) {
+ view_as(this).GetString(argNum, output, maxlen);
+ }
+
+ public void _Delete() {
+ delete view_as(this);
+ }
+
+ property int Args {
+ public get() { return view_as(this).Length; }
+ }
+}
methodmap UIElement < JSONObject {
public UIElement(const char[] elemNamespace, const char[] elemId) {
@@ -24,6 +53,7 @@ methodmap UIElement < JSONObject {
obj.SetString("namespace", elemNamespace);
obj.SetString("elem_id", elemId);
obj.SetBool("visibility", false);
+ obj.Set("steamids", new JSONArray());
obj.Set("variables", new JSONObject());
return view_as(obj);
@@ -35,16 +65,6 @@ methodmap UIElement < JSONObject {
}
public set(bool value) {
view_as(this).SetBool("visibility", value);
- this.Send();
- }
- }
-
- /** Is the UI element globally sent to all connected players?
- * Specify players with .AddClient() or clear with .ClearClients()
- */
- property bool Global {
- public get() {
- return !view_as(this).HasKey("steamids")
}
}
@@ -68,35 +88,13 @@ methodmap UIElement < JSONObject {
view_as(this).SetBool(id, value);
}
- public void SetActionCallback(UIActionCallback callback) {}
-
- public void AddClient(const char[] steamid) {
- // if(!IsClientInGame(client) || steamidCache[client][0] == '\0') ThrowError("Client %d is not connected, ingame, or authorized");
- JSONObject obj = view_as(this);
- JSONArray steamids = view_as(obj.Get("steamids"));
- if(steamids == null) {
- steamids = new JSONArray();
- obj.Set("steamids", steamids)
+ public native bool SendAll();
+ public native bool SendTo(int client);
+ public bool SendToMultiple(int[] clientIds, int numClients) {
+ for(int i = 0; i < numClients; i++) {
+ this.SendTo(clientIds[i]);
}
- steamids.PushString(steamid);
}
-
- public void ClearClients() {
- view_as(this).Remove("steamids");
- }
-
- public void Clear() {
- view_as(this).Clear();
- }
-
- public void Hide() {
- this.Visibility = false;
- }
- public void Show() {
- this.Visibility = true;
- }
-
- public native bool Send();
}
methodmap UIPosition < JSONObject {
@@ -118,6 +116,25 @@ methodmap UIPosition < JSONObject {
}
}
+methodmap UISize < JSONObject {
+ public UISize(int width, int height) {
+ JSONObject obj = new JSONObject();
+ obj.SetInt("width", width);
+ obj.SetInt("height", height);
+ return view_as(obj);
+ }
+
+ property int Width {
+ public get() { return view_as(this).GetInt("width"); }
+ public set(int value) { view_as(this).SetInt("height", value); }
+ }
+
+ property int Height {
+ public get() { return view_as(this).GetInt("height"); }
+ public set(int value) { view_as(this).SetInt("height", value); }
+ }
+}
+
methodmap UIColor < JSONObject {
/// Creates a new UIColor with RGB between 0-255, alpha is normalized 0.0-1.0
public UIColor(int r = 255, int g = 255, int b = 255) {
@@ -168,11 +185,15 @@ methodmap TempUIElementDefaults < JSONObject {
}
property UIPosition Position {
public get() { return view_as(view_as(this).Get("position")); }
- public set(UIPosition pos) { view_as(this).Set("position", view_as(pos)); }
+ // public set(UIPosition pos) { view_as(this).Set("position", view_as(pos)); }
}
property UIColor BackgroundColor {
public get() { return view_as(view_as(this).Get("bgColor")); }
- public set(UIColor color) { view_as(this).Set("bgColor", view_as(color)); }
+ // public set(UIColor color) { view_as(this).Set("bgColor", view_as(color)); }
+ }
+ property UISize Size {
+ public get() { return view_as(view_as(this).Get("size")); }
+ // public set(UISize size) { view_as(this).Set("size", view_as(size)); }
}
/// Returns or sets opacity, -1 is not set
property int Opacity {
@@ -203,6 +224,7 @@ enum UIType {
Element_Unknown = -1,
Element_Text,
Element_List,
+ Element_Audio
}
enum UIFlags {
Element_None
@@ -287,27 +309,29 @@ methodmap TempUI {
public TempUI(const char[] elemId, const char[] type, int lifetime = 0) {
JSONObject obj = new JSONObject();
obj.SetString("elem_id", elemId);
+ obj.Set("steamids", new JSONArray());
+ obj.SetInt("expires_seconds", 0);
TempUIElement element = new TempUIElement(type);
obj.Set("element", element);
return view_as(obj);
}
+ /// How long the temp UI lasts, 0 for never.
+ property int Duration {
+ public get() {
+ return view_as(this).GetInt("expires_seconds");
+ }
+ public set(int value) {
+ view_as(this).SetInt("expires_seconds", value);
+ }
+ }
+
property bool Visible {
public get() {
return view_as(this).GetBool("visibility");
}
public set(bool value) {
view_as(this).SetBool("visibility", value);
- this.Send();
- }
- }
-
- /** Is the UI element globally sent to all connected players?
- * Specify players with .AddClient() or clear with .ClearClients()
- */
- property bool Global {
- public get() {
- return !view_as(this).HasKey("steamids")
}
}
@@ -316,38 +340,127 @@ methodmap TempUI {
return view_as(view_as(this).Get("element"));
}
public set(TempUIElement newElement) {
+ // Delete old element
+ JSON elem = view_as(this).Get("element");
+ if(elem != null) delete elem;
+
view_as(this).Set("element", view_as(newElement));
}
}
- public void SetActionCallback(UIActionCallback callback) {}
-
- public void AddClient(const char[] steamid) {
- // if(!IsClientInGame(client) || steamidCache[client][0] == '\0') ThrowError("Client %d is not connected, ingame, or authorized");
- JSONObject obj = view_as(this);
- JSONArray steamids = view_as(obj.Get("steamids"));
- if(steamids == null) {
- steamids = new JSONArray();
- obj.Set("steamids", steamids)
- }
- steamids.PushString(steamid);
- }
-
- public void ClearClients() {
- view_as(this).Remove("steamids");
- }
-
public void Clear() {
view_as(this).Clear();
}
- public void Hide() {
- this.Visibility = false;
+ public void Hide() {
+ this.Visible = false;
}
public void Show() {
- this.Visibility = true;
+ this.Visible = true;
}
- public native bool Send();
+ public native bool SendAll();
+ public native bool SendTo(int client);
+ public bool SendToMultiple(int[] clientIds, int numClients) {
+ for(int i = 0; i < numClients; i++) {
+ this.SendTo(clientIds[i]);
+ }
+ }
+}
-}
\ No newline at end of file
+enum AudioState {
+ // Audio stopped, reset to startTime
+ Audio_Stopped,
+ // Pauses audio at current time
+ Audio_Paused,
+ Audio_Play
+}
+
+methodmap ClientList < JSONArray {
+ public ClientList() {
+ return view_as(new JSONArray());
+ }
+
+ property int Length {
+ public get() { return view_as(this).Length; }
+ }
+
+ public native void AddClient(int client);
+
+ public native bool HasClient(int client);
+
+ public void Clear() {
+ view_as(this).Clear();
+ }
+}
+
+methodmap AudioResource < JSONObject {
+ public AudioResource(const char[] url, float volume = 0.5) {
+ JSONObject obj = new JSONObject();
+ obj.SetString("source", url);
+ obj.SetFloat("volume", volume);
+ obj.SetInt("state", 0);
+ obj.Set("steamids", new JSONArray());
+ obj.SetBool("repeat", false)
+ return view_as(obj);
+ }
+
+ property AudioState State {
+ public get() {
+ return view_as(view_as(this).GetInt("state"));
+ }
+ public set(AudioState state) {
+ view_as(this).SetInt("state", view_as(state));
+ }
+ }
+
+ property float Volume {
+ public get() {
+ return view_as(this).GetFloat("volume");
+ }
+ public set(float volume) {
+ view_as(this).SetFloat("volume", volume);
+ }
+ }
+
+ property bool Repeat {
+ public get() {
+ return view_as(this).GetBool("repeat");
+ }
+ public set(bool repeat) {
+ view_as(this).SetBool("repeat", repeat);
+ }
+ }
+
+ property ClientList Clients {
+ public get() {
+ return view_as(view_as(this).Get("steamids"));
+ }
+ }
+ /// Plays or resumes playing
+ public native void Play();
+ /// Stops playing audio, clients will reset to beginning
+ public native void Stop();
+ /// Pauses audio, resuming to current play duration
+ public native void Pause();
+
+ public void Clear() {
+ view_as(this).Clear();
+ }
+}
+
+#if !defined REQUIRE_PLUGIN
+public void __pl_overlay_SetNTVOptional() {
+ MarkNativeAsOptional("IsOverlayConnected");
+ MarkNativeAsOptional("RegisterActionAnyHandler");
+ MarkNativeAsOptional("RegisterActionHandler");
+
+ MarkNativeAsOptional("UIElement.SendAll");
+ MarkNativeAsOptional("UIElement.SendTo");
+ MarkNativeAsOptional("TempUI.SendAll");
+ MarkNativeAsOptional("TempUI.SendTo");
+ MarkNativeAsOptional("AudioResource.Play");
+ MarkNativeAsOptional("AudioResource.Stop");
+ MarkNativeAsOptional("AudioResource.Pause");
+}
+#endif
\ No newline at end of file
diff --git a/scripting/include/randomizer/rbuild.sp b/scripting/include/randomizer/rbuild.sp
new file mode 100644
index 0000000..b0fe246
--- /dev/null
+++ b/scripting/include/randomizer/rbuild.sp
@@ -0,0 +1,135 @@
+/// MENUS
+public void OpenMainMenu(int client) {
+ Menu menu = new Menu(BuilderHandler_MainMenu);
+ menu.SetTitle("Randomizer Builder");
+ if(g_builder.mapData == null) {
+ menu.AddItem("load", "Load Map Data");
+ menu.AddItem("new", "New Map Data");
+ } else {
+ menu.AddItem("save", "Save Map Data");
+ menu.AddItem("selector", "Start Selector");
+ menu.AddItem("spawner", "Start Spawner");
+ menu.AddItem("cursor", "Add Entity At Cursor");
+ menu.AddItem("scenes", "Scenes");
+ }
+ menu.Display(client, 0);
+}
+
+void OpenScenesMenu(int client) {
+ Menu menu = new Menu(BuilderHandler_ScenesMenu);
+ menu.SetTitle("Select a scene");
+ char id[64], display[32];
+ JSONObjectKeys iterator = g_builder.mapData.Keys();
+ while(iterator.ReadKey(id, sizeof(id))) {
+ if(StrEqual(id, g_builder.selectedSceneId)) {
+ Format(display, sizeof(display), "%s (selected)", id);
+ } else {
+ Format(display, sizeof(display), "%s", id);
+ }
+ menu.AddItem(id, display);
+ }
+ menu.Display(client, 0);
+}
+
+void OpenVariantsMenu(int client) {
+ Menu menu = new Menu(BuilderHandler_VariantsMenu);
+ menu.SetTitle("%s > Variants", g_builder.selectedSceneId);
+ char id[8], display[32];
+ menu.AddItem("new", "New");
+ menu.AddItem("-1", "None (Shared Scene)");
+
+ JSONArray variants = view_as(g_builder.selectedSceneData.Get("variants"));
+ JSONObject varObj;
+ JSONArray entities;
+ for(int i = 0; i < variants.Length; i++) {
+ varObj = view_as(variants.Get(i));
+ entities = view_as(varObj.Get("entities"));
+ if(i == g_builder.selectedVariantIndex) {
+ Format(display, sizeof(display), "%d entities (selected)", entities.Length);
+ } else {
+ Format(display, sizeof(display), "%d entities", entities.Length);
+ }
+ IntToString(i, id, sizeof(id));
+ menu.AddItem(id, display);
+ }
+ menu.Display(client, 0);
+}
+
+/// HANDLERS
+
+int BuilderHandler_MainMenu(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ char info[32];
+ menu.GetItem(param2, info, sizeof(info));
+ if(StrEqual(info, "new")) {
+ JSONObject temp = LoadMapJson(currentMap);
+ GetCmdArg(2, info, sizeof(info));
+ if(temp != null) {
+ Menu nMenu = new Menu(BuilderHandler_MainMenu);
+ nMenu.SetTitle("Existing map data exists");
+ nMenu.AddItem("new_confirm", "Overwrite");
+ nMenu.Display(client, 0);
+ delete temp;
+ return 0;
+ } else {
+ FakeClientCommand(client, "sm_rbuild new");
+ }
+ } else if(StrEqual(info, "new_confirm")) {
+ FakeClientCommand(client, "sm_rbuild new confirm");
+ } else if(StrEqual(info, "scenes")) {
+ OpenScenesMenu(client);
+ return 0;
+ } else {
+ FakeClientCommand(client, "sm_rbuild %s", info);
+ } /*else if(StrEqual(info, "cursor")) {
+ Menu menu = new Menu(BuilderHandler_)
+ }*/
+ OpenMainMenu(client);
+ } else if(action == MenuAction_Cancel) {
+ if(param2 == MenuCancel_ExitBack) {
+ }
+ } else if (action == MenuAction_End) {
+ delete menu;
+ }
+ return 0;
+}
+
+int BuilderHandler_ScenesMenu(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ char info[64];
+ menu.GetItem(param2, info, sizeof(info));
+ if(StrEqual(info, "new")) {
+ FakeClientCommand(client, "sm_rbuild scenes new");
+ OpenScenesMenu(client);
+ } else {
+ FakeClientCommand(client, "sm_rbuild scenes select %s", info);
+ OpenVariantsMenu(client);
+ }
+ } else if(action == MenuAction_Cancel) {
+ if(param2 == MenuCancel_ExitBack) {
+ }
+ } else if (action == MenuAction_End) {
+ delete menu;
+ }
+ return 0;
+}
+
+int BuilderHandler_VariantsMenu(Menu menu, MenuAction action, int client, int param2) {
+ if (action == MenuAction_Select) {
+ char info[64];
+ menu.GetItem(param2, info, sizeof(info));
+ if(StrEqual(info, "new")) {
+ FakeClientCommand(client, "sm_rbuild scenes variants new");
+ } else {
+ FakeClientCommand(client, "sm_rbuild scenes variants select %s", info);
+ }
+ OpenVariantsMenu(client);
+ } else if(action == MenuAction_Cancel) {
+ if(param2 == MenuCancel_ExitBack) {
+ }
+ } else if (action == MenuAction_End) {
+ delete menu;
+ }
+ return 0;
+}
+
diff --git a/scripting/l4d2_TKStopper.sp b/scripting/l4d2_TKStopper.sp
index 35d32f4..967a6c2 100644
--- a/scripting/l4d2_TKStopper.sp
+++ b/scripting/l4d2_TKStopper.sp
@@ -249,8 +249,8 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
CPrintChatToAdmins("{olive}%N{default} has been banned for %d minutes (marked as troll). If this was a mistake, you can discard their ban from the admin panel at {yellow}https://admin.jackz.me", client, hBanTime.IntValue);
else
CPrintChatToAdmins("{olive}%N{default} has been permanently banned (marked as troll). If this was a mistake, you can discard their ban from the admin panel at {yellow}https://admin.jackz.me", client);
- pData[client].isTroll = false;
}
+ pData[client].isTroll = false;
if(!IsFakeClient(client)) {
float minutesSinceiLastFFTime = GetLastFFMinutes(client);
diff --git a/scripting/l4d2_extraplayeritems.sp b/scripting/l4d2_extraplayeritems.sp
index 58b0248..bd19141 100644
--- a/scripting/l4d2_extraplayeritems.sp
+++ b/scripting/l4d2_extraplayeritems.sp
@@ -90,7 +90,7 @@ ConVar cvEPICommonCountScale, cvEPICommonCountScaleMax;
ConVar g_ffFactorCvar, hExtraTankThreshold;
-ConVar cvZCommonLimit; int zCommonLimitPrevValue;
+ConVar cvZCommonLimit; int commonLimitBase; bool isSettingLimit;
int g_extraKitsAmount, g_extraKitsStart, g_saferoomDoorEnt, g_prevPlayerCount;
bool g_forcedSurvivorCount, g_extraKitsSpawnedFinale;
@@ -306,25 +306,29 @@ public void OnPluginStart() {
hExtraItemBasePercentage = CreateConVar("epi_item_chance", "0.034", "The base chance (multiplied by player count) of an extra item being spawned.", FCVAR_NONE, true, 0.0, true, 1.0);
hExtraSpawnBasePercentage = CreateConVar("epi_spawn_chance", "0.01", "The base chance (multiplied by player count) of an extra item spawner being created.", FCVAR_NONE, true, 0.0, true, 1.0);
- hAddExtraKits = CreateConVar("epi_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits\n1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0);
- hUpdateMinPlayers = CreateConVar("epi_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO\n1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0);
- hMinPlayersSaferoomDoor = CreateConVar("epi_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
- hSaferoomDoorWaitSeconds = CreateConVar("epi_doorunlock_wait", "25", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
- hSaferoomDoorAutoOpen = CreateConVar("epi_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
- hEPIHudState = CreateConVar("epi_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0);
- hExtraFinaleTank = CreateConVar("epi_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
- hExtraTankThreshold = CreateConVar("epi_extra_tanks_min_players", "6", "The minimum number of players for extra tanks to spawn. When disabled, normal 5+ tank health applies", FCVAR_NONE, true, 0.0);
- hSplitTankChance = CreateConVar("epi_splittank_chance", "0.65", "The % chance of a split tank occurring in non-finales", FCVAR_NONE, true, 0.0, true, 1.0);
- cvDropDisconnectTime = CreateConVar("epi_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
- cvFFDecreaseRate = CreateConVar("epi_ff_decrease_rate", "0.3", "The friendly fire factor is subtracted from the formula (playerCount-4) * this rate. Effectively reduces ff penalty when more players. 0.0 to subtract none", FCVAR_NONE, true, 0.0);
- cvEPIHudFlags = CreateConVar("epi_hud_flags", "3", "Add together.\n1 = Scrolling hud, 2 = Show ping", FCVAR_NONE, true, 0.0);
- cvEPISpecialSpawning = CreateConVar("epi_sp_spawning", "2", "Determines what specials are spawned. Add bits together.\n1 = Normal specials\n2 = Witches\n4 = Tanks", FCVAR_NONE, true, 0.0);
- cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
- cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
- cvEPIEnabledMode = CreateConVar("epi_enabled", "1", "Is EPI enabled?\n0=OFF\n1=Auto (Official Maps Only)(5+)\n2=Auto (Any map) (5+)\n3=Forced on", FCVAR_NONE, true, 0.0, true, 3.0);
- cvEPICommonCountScale = CreateConVar("epi_commons_scale_multiplier", "0", "This value is multiplied by the number of extra players playing. It's then added to z_common_limit. 5 players with value 5 would be z_common_limit + ", FCVAR_NONE, true, 0.0);
- cvEPICommonCountScaleMax = CreateConVar("epi_commons_scale_max", "60", "The maximum amount that z_common_limit can be scaled to.", FCVAR_NONE, true, 0.0);
+ hAddExtraKits = CreateConVar("epi_kitmode", "0", "Decides how extra kits should be added.\n0 -> Overwrites previous extra kits\n1 -> Adds onto previous extra kits", FCVAR_NONE, true, 0.0, true, 1.0);
+ hUpdateMinPlayers = CreateConVar("epi_updateminplayers", "1", "Should the plugin update abm\'s cvar min_players convar to the player count?\n 0 -> NO\n1 -> YES", FCVAR_NONE, true, 0.0, true, 1.0);
+ hMinPlayersSaferoomDoor = CreateConVar("epi_doorunlock_percent", "0.75", "The percent of players that need to be loaded in before saferoom door is opened.\n 0 to disable", FCVAR_NONE, true, 0.0, true, 1.0);
+ hSaferoomDoorWaitSeconds = CreateConVar("epi_doorunlock_wait", "25", "How many seconds after to unlock saferoom door. 0 to disable", FCVAR_NONE, true, 0.0);
+ hSaferoomDoorAutoOpen = CreateConVar("epi_doorunlock_open", "0", "Controls when the door automatically opens after unlocked. Add bits together.\n0 = Never, 1 = When timer expires, 2 = When all players loaded in", FCVAR_NONE, true, 0.0);
+ hEPIHudState = CreateConVar("epi_hudstate", "1", "Controls when the hud displays.\n0 -> OFF, 1 = When 5+ players, 2 = ALWAYS", FCVAR_NONE, true, 0.0, true, 3.0);
+ hExtraFinaleTank = CreateConVar("epi_extra_tanks", "3", "Add bits together. 0 = Normal tank spawning, 1 = 50% tank split on non-finale (half health), 2 = Tank split (full health) on finale ", FCVAR_NONE, true, 0.0, true, 3.0);
+ hExtraTankThreshold = CreateConVar("epi_extra_tanks_min_players", "6", "The minimum number of players for extra tanks to spawn. When disabled, normal 5+ tank health applies", FCVAR_NONE, true, 0.0);
+ hSplitTankChance = CreateConVar("epi_splittank_chance", "0.65", "The % chance of a split tank occurring in non-finales", FCVAR_NONE, true, 0.0, true, 1.0);
+ cvDropDisconnectTime = CreateConVar("epi_disconnect_time", "120.0", "The amount of seconds after a player has actually disconnected, where their character slot will be void. 0 to disable", FCVAR_NONE, true, 0.0);
+ cvFFDecreaseRate = CreateConVar("epi_ff_decrease_rate", "0.3", "The friendly fire factor is subtracted from the formula (playerCount-4) * this rate. Effectively reduces ff penalty when more players. 0.0 to subtract none", FCVAR_NONE, true, 0.0);
+ cvEPIHudFlags = CreateConVar("epi_hud_flags", "3", "Add together.\n1 = Scrolling hud, 2 = Show ping", FCVAR_NONE, true, 0.0);
+ cvEPISpecialSpawning = CreateConVar("epi_sp_spawning", "2", "Determines what specials are spawned. Add bits together.\n1 = Normal specials\n2 = Witches\n4 = Tanks", FCVAR_NONE, true, 0.0);
+ cvEPITankHealth = CreateConVar("epi_tank_chunkhp", "2500", "The amount of health added to tank, for each extra player", FCVAR_NONE, true, 0.0);
+ cvEPIGamemodes = CreateConVar("epi_gamemodes", "coop,realism,versus", "Gamemodes where plugin is active. Comma-separated", FCVAR_NONE);
+ cvEPIEnabledMode = CreateConVar("epi_enabled", "1", "Is EPI enabled?\n0=OFF\n1=Auto (Official Maps Only)(5+)\n2=Auto (Any map) (5+)\n3=Forced on", FCVAR_NONE, true, 0.0, true, 3.0);
+ cvEPICommonCountScale = CreateConVar("epi_commons_scale_multiplier", "0", "This value is multiplied by the number of extra players playing. It's then added to z_common_limit. 5 players with value 5 would be z_common_limit + ", FCVAR_NONE, true, 0.0);
+ cvEPICommonCountScaleMax = CreateConVar("epi_commons_scale_max", "60", "The maximum amount that z_common_limit can be scaled to.", FCVAR_NONE, true, 0.0);
cvZCommonLimit = FindConVar("z_common_limit");
+
+ cvEPICommonCountScale.AddChangeHook(Cvar_CommonScaleChange);
+ cvEPICommonCountScaleMax.AddChangeHook(Cvar_CommonScaleChange);
+ cvZCommonLimit.AddChangeHook(Cvar_CommonScaleChange);
// TODO: hook flags, reset name index / ping mode
cvEPIHudFlags.AddChangeHook(Cvar_HudStateChange);
@@ -428,6 +432,7 @@ public void OnPluginEnd() {
delete weaponMaxClipSizes;
delete g_ammoPacks;
L4D2_ExecVScriptCode(HUD_SCRIPT_CLEAR);
+ _UnsetCommonLimit();
}
///////////////////////////////////////////////////////////////////////////////
@@ -482,6 +487,19 @@ void Cvar_HudStateChange(ConVar convar, const char[] oldValue, const char[] newV
TryStartHud();
}
}
+void Cvar_CommonScaleChange(ConVar convar, const char[] oldValue, const char[] newValue) {
+ if(convar == cvZCommonLimit) {
+ PrintToServer("z_common_limit changed [value=%d] [isSettingLimit=%b]", convar.IntValue, isSettingLimit);
+ // Ignore our own changes:
+ if(isSettingLimit) {
+ isSettingLimit = false;
+ return;
+ }
+ commonLimitBase = convar.IntValue;
+ }
+ _SetCommonLimit();
+
+}
void TryStartHud() {
int threshold = 0;
// Default to 0 for state == 2 (force)
@@ -572,6 +590,7 @@ Action Command_EpiVal(int client, int args) {
PrintToConsole(client, "restCount = %d", g_restCount);
PrintToConsole(client, "extraFinaleTankEnabled = %b", g_extraFinaleTankEnabled);
PrintToConsole(client, "g_areItemsPopulated = %b", g_areItemsPopulated);
+ PrintToConsole(client, "commonLimitBase = %d", commonLimitBase);
ReplyToCommand(client, "Values printed to console");
return Plugin_Handled;
}
@@ -1343,7 +1362,7 @@ public void OnMapStart() {
if(L4D_IsMissionFinalMap()) {
// Disable tank split on hard rain finale
g_extraFinaleTankEnabled = true;
- if(StrEqual(map, "c4m5_milltown_escape")) {
+ if(StrEqual(map, "c4m5_milltown_escape") || StrEqual(map, "c14m2_lighthouse")) {
g_extraFinaleTankEnabled = false;
}
}
@@ -1454,6 +1473,7 @@ public void OnConfigsExecuted() {
if(hUpdateMinPlayers.BoolValue && hMinPlayers != null) {
hMinPlayers.IntValue = g_realSurvivorCount;
}
+ _SetCommonLimit();
}
public void OnMapEnd() {
@@ -2102,29 +2122,42 @@ void UpdateSurvivorCount() {
} else if(wasActive) {
OnEPIInactive();
}
-
- if(isActive)
- SetFFFactor(g_epiEnabled);
+ if(isActive) {
+ SetFFFactor(g_epiEnabled);
+ _SetCommonLimit();
+ }
}
void OnEPIActive() {
- zCommonLimitPrevValue = cvZCommonLimit.IntValue;
+ _SetCommonLimit();
+}
+
+void OnEPIInactive() {
+ _UnsetCommonLimit();
+}
+
+void _UnsetCommonLimit() {
+ if(commonLimitBase > 0) {
+ cvZCommonLimit.IntValue = commonLimitBase;
+ }
+ commonLimitBase = 0;
+}
+void _SetCommonLimit() {
+ if(!g_epiEnabled || commonLimitBase <= 0) return;
// TODO: lag check for common limit
- if(cvEPICommonCountScale.IntValue > 0) {
- int newLimit = zCommonLimitPrevValue + RoundFloat(cvEPICommonCountScale.FloatValue * float(g_realSurvivorCount));
+ if(cvEPICommonCountScale.IntValue > 0 && commonLimitBase > 0) {
+ int newLimit = commonLimitBase + RoundFloat(cvEPICommonCountScale.FloatValue * float(g_realSurvivorCount - 4));
+ PrintDebug(DEBUG_INFO, "Setting common scale: %d + (%f * %d) [max=%d] = %d", commonLimitBase, cvEPICommonCountScale.FloatValue, g_realSurvivorCount - 4, cvEPICommonCountScaleMax.IntValue, newLimit);
if(newLimit > 0) {
if(newLimit > cvEPICommonCountScaleMax.IntValue) {
newLimit = cvEPICommonCountScaleMax.IntValue;
}
+ isSettingLimit = true;
+ cvZCommonLimit.IntValue = newLimit;
}
- cvZCommonLimit.IntValue = newLimit;
}
}
-
-void OnEPIInactive() {
- cvZCommonLimit.IntValue = zCommonLimitPrevValue;
-}
void SetFFFactor(bool enabled) {
static float prevValue;
// Restore the previous value (we use the value for the calculations of new value)
diff --git a/scripting/l4d2_guesswho.sp b/scripting/l4d2_guesswho.sp
index 548780f..f0b5b0d 100644
--- a/scripting/l4d2_guesswho.sp
+++ b/scripting/l4d2_guesswho.sp
@@ -213,6 +213,7 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[
HookEvent("player_bot_replace", Event_PlayerToBot);
HookEvent("player_ledge_grab", Event_LedgeGrab);
AddCommandListener(OnGoAwayFromKeyboard, "go_away_from_keyboard");
+ InitGamemode();
} else if(!lateLoaded) {
cvarStorage.Restore();
delete cvarStorage;
@@ -462,7 +463,8 @@ Action Timer_SpawnBots(Handle h, int max) {
if(AddSurvivor()) {
count++;
return Plugin_Continue;
- } else {
+ } else if(count < 0) {
+ // Fail if we couldnt make enough bots
PrintToChatAll("GUESS WHO: FATAL ERROR: AddSurvivor() failed");
LogError("Guess Who: Fatal Error: AddSurvivor() failed");
count = 0;
@@ -561,6 +563,7 @@ Action Timer_WaitForStart(Handle h) {
Game.State = State_Starting;
Game.Tick = 0;
Game.MapTime = RoundFloat(seedTime);
+ Game.PopulateCoins();
CreateTimer(seedTime, Timer_StartSeeker);
return Plugin_Stop;
}
@@ -585,6 +588,7 @@ Action Timer_StartSeeker(Handle h) {
Action Timer_TimesUp(Handle h) {
Game.Broadcast("The seeker ran out of time. Hiders win!");
Game.End(State_HidersWin);
+ timesUpTimer = null;
return Plugin_Handled;
}
diff --git a/scripting/l4d2_hats.sp b/scripting/l4d2_hats.sp
index 359b056..ac2162e 100644
--- a/scripting/l4d2_hats.sp
+++ b/scripting/l4d2_hats.sp
@@ -46,6 +46,8 @@ char g_currentMap[64];
#include
#include
#include
+#include
+#include
public Plugin myinfo = {
name = "L4D2 Hats & Editor",
@@ -56,6 +58,32 @@ public Plugin myinfo = {
};
ArrayList NavAreas;
+public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
+ RegPluginLibrary("editor");
+ // CreateNative("SpawnSchematic", Native_SpawnSchematic);
+ CreateNative("StartEdit", Native_StartEdit);
+ CreateNative("StartSpawner", Native_StartSpawner);
+ CreateNative("CancelEdit", Native_CancelEdit);
+ CreateNative("IsEditorActive", Native_IsEditorActive);
+
+
+ CreateNative("StartSelector", Native_StartSelector);
+ CreateNative("CancelSelector", Native_CancelSelector);
+ CreateNative("IsSelectorActive", Native_IsSelectorActive);
+
+ CreateNative("EntitySelector.Start", Native_Selector_Start);
+ CreateNative("EntitySelector.Count.get", Native_Selector_GetCount);
+ CreateNative("EntitySelector.Active.get", Native_Selector_GetActive);
+ CreateNative("EntitySelector.SetOnEnd", Native_Selector_SetOnEnd);
+ CreateNative("EntitySelector.SetOnPreSelect", Native_Selector_SetOnPreSelect);
+ CreateNative("EntitySelector.SetOnPostSelect", Native_Selector_SetOnPostSelect);
+ CreateNative("EntitySelector.SetOnUnselect", Native_Selector_SetOnUnselect);
+ CreateNative("EntitySelector.AddEntity", Native_Selector_AddEntity);
+ CreateNative("EntitySelector.RemoveEntity", Native_Selector_RemoveEntity);
+ CreateNative("EntitySelector.Cancel", Native_Selector_Cancel);
+ CreateNative("EntitySelector.End", Native_Selector_End);
+ return APLRes_Success;
+}
public void OnPluginStart() {
@@ -116,6 +144,7 @@ public void OnPluginStart() {
for(int i = 1; i <= MaxClients; i++) {
Editor[i].client = i;
Editor[i].Reset(true);
+ g_PropData[i].Init(i);
hatData[i].yeetGroundTimer = null;
}
@@ -456,6 +485,7 @@ ArrayList GetSpawnLocations() {
return newList;
}
+
void ChooseRandomPosition(float pos[3], int ignoreClient = 0) {
if(NavAreas.Length > 0 && GetURandomFloat() > 0.5) {
int nav = NavAreas.Get(GetURandomInt() % (NavAreas.Length - 1));
@@ -578,34 +608,34 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
if(g_PropData[client].pendingSaveType == Save_Schematic) {
// move cursor? or should be editor anyway
}
- } else if(g_PropData[client].markedProps != null) {
+ } else if(g_PropData[client].Selector.IsActive()) {
SetWeaponDelay(client, 0.5);
if(tick - cmdThrottle[client] >= 0.20) {
if(buttons & IN_ATTACK) {
int entity = GetLookingEntity(client, Filter_ValidHats);
if(entity > 0) {
- g_PropData[client].markedProps.Push(EntIndexToEntRef(entity));
- GlowEntity(entity, 0.0, GLOW_RED);
+ if(g_PropData[client].Selector.AddEntity(entity) != -1) {
+ PrecacheSound("ui/beep07.wav");
+ EmitSoundToClient(client, "ui/beep07.wav", entity, SND_CHANGEVOL, .volume = 0.5);
+ }
} else {
PrintHintText(client, "No entity found");
}
} else if(buttons & IN_ATTACK2) {
int entity = GetLookingEntity(client, Filter_ValidHats);
if(entity > 0) {
- int ref = EntIndexToEntRef(entity);
- int index = g_PropData[client].markedProps.FindValue(ref);
- if(index > -1) {
- g_PropData[client].markedProps.Erase(index);
- L4D2_RemoveEntityGlow(entity);
+ if(g_PropData[client].Selector.RemoveEntity(entity)) {
+ PrecacheSound("ui/beep22.wav");
+ EmitSoundToClient(client, "ui/beep22.wav", entity, SND_CHANGEVOL, .volume = 0.5);
}
}
} else if(buttons & IN_USE) {
if(buttons & IN_SPEED) {
//Delete
- EndDeleteTool(client, true);
+ g_PropData[client].Selector.End();
} else if(buttons & IN_DUCK) {
//Cancel
- EndDeleteTool(client, false);
+ g_PropData[client].Selector.Cancel();
}
}
cmdThrottle[client] = tick;
@@ -628,31 +658,37 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
}
if(!(oldButtons & IN_JUMP) && (buttons & IN_JUMP)) {
buttons &= ~IN_JUMP;
- Editor[client].CycleStacker(tick);
+ Editor[client].CycleStacker();
} else if(!(oldButtons & IN_SPEED) && (buttons & IN_SPEED)) {
- Editor[client].ToggleCollision(tick);
+ Editor[client].ToggleCollision();
return Plugin_Handled;
} else if(!(oldButtons & IN_DUCK) && (buttons & IN_DUCK)) {
- Editor[client].ToggleCollisionRotate(tick);
+ Editor[client].ToggleCollisionRotate();
return Plugin_Handled;
} else {
PrintCenterText(client, "%.1f %.1f %.1f", Editor[client].angles[0], Editor[client].angles[1], Editor[client].angles[2]);
isRotate = true;
SetEntityFlags(client, flags |= FL_FROZEN);
- if(!(oldButtons & IN_ATTACK) && (buttons & IN_ATTACK)) Editor[client].CycleAxis(tick);
- else if(buttons & IN_ATTACK2) Editor[client].CycleSnapAngle(tick);
+ if(!(oldButtons & IN_ATTACK) && (buttons & IN_ATTACK)) Editor[client].CycleAxis();
+ else if(!(oldButtons & IN_ATTACK2) && (buttons & IN_ATTACK2)) Editor[client].CycleSnapAngle(tick);
// Rotation control:
// Turn off rotate when player wants rotate
Editor[client].hasCollisionRotate = false;
if(tick - cmdThrottle[client] > 0.1) {
- if(Editor[client].axis == 2) {
- if(mouse[1] > 10) Editor[client].angles[2] += Editor[client].snapAngle;
- else if(mouse[1] < -10) Editor[client].angles[2] -= Editor[client].snapAngle;
- } else {
- if(mouse[0] > 10) Editor[client].angles[Editor[client].axis] += Editor[client].snapAngle;
- else if(mouse[0] < -10) Editor[client].angles[Editor[client].axis] -= Editor[client].snapAngle;
-
+ if(Editor[client].axis == 0) {
+ int mouseXAbs = IntAbs(mouse[0]);
+ int mouseYAbs = IntAbs(mouse[1]);
+ bool XOverY = mouseXAbs > mouseYAbs;
+ if(mouseYAbs > 10 && !XOverY) {
+ Editor[client].IncrementAxis(0, mouse[1]);
+ } else if(mouseXAbs > 10 && XOverY) {
+ Editor[client].IncrementAxis(1, mouse[0]);
+ }
+ }
+ else if(Editor[client].axis == 1) {
+ if(mouse[0] > 10) Editor[client].angles[2] += Editor[client].snapAngle;
+ else if(mouse[0] < -10) Editor[client].angles[2] -= Editor[client].snapAngle;
}
cmdThrottle[client] = tick;
}
@@ -701,24 +737,27 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
}
case COLOR: {
SetWeaponDelay(client, 0.5);
- PrintCenterText(client, "%d %d %d %d", Editor[client].color[0], Editor[client].color[1], Editor[client].color[2], Editor[client].color[3]);
+ PrintHintText(client, "%d %d %d %d", Editor[client].color[0], Editor[client].color[1], Editor[client].color[2], Editor[client].color[3]);
if(buttons & IN_USE) {
Editor[client].CycleColorComponent(tick);
- } else if(buttons & IN_ATTACK) {
+ } else if(buttons & IN_ATTACK2) {
Editor[client].IncreaseColor(1);
allowMove = false;
- } else if(buttons & IN_ATTACK2) {
+ } else if(buttons & IN_ATTACK) {
Editor[client].IncreaseColor(-1);
allowMove = false;
}
}
}
- if(!(oldButtons & IN_USE) && buttons & IN_USE) {
+ if(buttons & IN_DUCK) {
+
+ }
+ if(Editor[client].mode != COLOR && !(oldButtons & IN_USE) && buttons & IN_USE) {
if(buttons & IN_SPEED) {
Editor[client].Cancel();
} else if(buttons & IN_DUCK) {
- if(Editor[client].flags & Edit_Preview)
- Editor[client].CycleBuildType();
+ Editor[client].CycleBuildType();
+ // Editor[client].ShowExtraOptions();
} else {
int entity;
Editor[client].Done(entity);
@@ -735,6 +774,12 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3
return Plugin_Continue;
}
+int IntAbs(int a) {
+ if(a < 0) {
+ return a * -1;
+ }
+ return a;
+}
// Don't show real entity to hat wearer (Show for ALL but hat wearer)
Action OnRealTransmit(int entity, int client) {
@@ -914,6 +959,7 @@ bool Filter_ValidHats(int entity, int mask, int data) {
}
bool CheckBlacklist(int entity) {
+ if(entity == 0) return false;
if(cvar_sm_hats_blacklist_enabled.BoolValue) {
static char buffer[64];
GetEntityClassname(entity, buffer, sizeof(buffer));
@@ -1024,4 +1070,4 @@ stock bool CalculateEditorPosition(int client, TraceEntityFilter filter) {
return true;
}
return false;
-}
+}
\ No newline at end of file
diff --git a/scripting/l4d2_randomizer.sp b/scripting/l4d2_randomizer.sp
index 8369361..ee1740b 100644
--- a/scripting/l4d2_randomizer.sp
+++ b/scripting/l4d2_randomizer.sp
@@ -11,9 +11,12 @@
#include
//#include
#include
-#include
+// #include
+#include
#include
#include
+#undef REQUIRE_PLUGIN
+#include
int g_iLaserIndex;
#if defined DEBUG_BLOCKERS
@@ -27,6 +30,77 @@ int g_iLaserIndex;
#define MAX_SCENE_NAME_LENGTH 32
#define MAX_INPUTS_CLASSNAME_LENGTH 64
+
+ConVar cvarEnabled;
+enum struct ActiveSceneData {
+ char name[MAX_SCENE_NAME_LENGTH];
+ int variantIndex;
+}
+MapData g_MapData;
+BuilderData g_builder;
+char currentMap[64];
+
+enum struct BuilderData {
+ JSONObject mapData;
+
+ JSONObject selectedSceneData;
+ char selectedSceneId[64];
+
+ JSONObject selectedVariantData;
+ int selectedVariantIndex;
+
+ void Cleanup() {
+ this.selectedSceneData = null;
+ this.selectedVariantData = null;
+ this.selectedVariantIndex = -1;
+ this.selectedSceneId[0] = '\0';
+ if(this.mapData != null)
+ delete this.mapData;
+ // JSONcleanup_and_delete(this.mapData);
+ }
+
+ bool SelectScene(const char[] group) {
+ if(!g_builder.mapData.HasKey(group)) return false;
+ this.selectedSceneData = view_as(g_builder.mapData.Get(group));
+ strcopy(this.selectedSceneId, sizeof(this.selectedSceneId), group);
+ return true;
+ }
+
+ /**
+ * Select a variant, enter -1 to not select any (scene's entities)
+ */
+ bool SelectVariant(int index = -1) {
+ if(this.selectedSceneData == null) LogError("SelectVariant called, but no group selected");
+ JSONArray variants = view_as(this.selectedSceneData.Get("variants"));
+ if(index >= variants.Length) return false;
+ else if(index < -1) return false;
+ else if(index > -1) {
+ this.selectedVariantData = view_as(variants.Get(index));
+ } else {
+ this.selectedVariantData = null;
+ }
+ this.selectedVariantIndex = index;
+ return true;
+ }
+
+ void AddEntity(int entity, ExportType exportType = Export_Model) {
+ JSONArray entities;
+ if(g_builder.selectedVariantData == null) {
+ // Create .entities if doesn't exist:
+ if(!g_builder.selectedSceneData.HasKey("entities")) {
+ g_builder.selectedSceneData.Set("entities", new JSONArray());
+ }
+ entities = view_as(g_builder.selectedSceneData.Get("entities"));
+ } else {
+ entities = view_as(g_builder.selectedVariantData.Get("entities"));
+ }
+ JSONObject entityData = ExportEntity(entity, Export_Model);
+ entities.Push(entityData);
+ }
+}
+
+#include
+
public Plugin myinfo =
{
name = "L4D2 Randomizer",
@@ -36,13 +110,6 @@ public Plugin myinfo =
url = "https://github.com/Jackzmc/sourcemod-plugins"
};
-ConVar cvarEnabled;
-enum struct ActiveSceneData {
- char name[MAX_SCENE_NAME_LENGTH];
- int variantIndex;
-}
-MapData g_MapData;
-
public void OnPluginStart() {
EngineVersion g_Game = GetEngineVersion();
if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) {
@@ -51,15 +118,14 @@ public void OnPluginStart() {
RegAdminCmd("sm_rcycle", Command_CycleRandom, ADMFLAG_CHEATS);
RegAdminCmd("sm_expent", Command_ExportEnt, ADMFLAG_GENERIC);
+ RegAdminCmd("sm_rbuild", Command_RandomizerBuild, ADMFLAG_CHEATS);
cvarEnabled = CreateConVar("sm_randomizer_enabled", "0");
g_MapData.activeScenes = new ArrayList(sizeof(ActiveSceneData));
}
-char currentMap[64];
-bool hasRan;
// TODO: on round start
public void OnMapStart() {
g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt", true);
@@ -69,6 +135,7 @@ public void OnMapStart() {
}
public void OnMapEnd() {
+ g_builder.Cleanup();
Cleanup();
}
@@ -124,9 +191,7 @@ public Action Command_CycleRandom(int client, int args) {
if(args > 0) {
DeleteCustomEnts();
- char arg1[8];
- GetCmdArg(1, arg1, sizeof(arg1));
- int flags = StringToInt(arg1) | view_as(FLAG_REFRESH);
+ int flags = GetCmdArgInt(1) | view_as(FLAG_REFRESH);
RunMap(currentMap, flags);
if(client > 0)
PrintCenterText(client, "Cycled flags=%d", flags);
@@ -141,7 +206,7 @@ public Action Command_CycleRandom(int client, int args) {
return Plugin_Handled;
}
-public Action Command_ExportEnt(int client, int args) {
+Action Command_ExportEnt(int client, int args) {
float origin[3];
int entity = GetLookingPosition(client, Filter_IgnorePlayer, origin);
float angles[3];
@@ -188,6 +253,271 @@ public Action Command_ExportEnt(int client, int args) {
}
return Plugin_Handled;
}
+Action Command_RandomizerBuild(int client, int args) {
+ char arg[64];
+ GetCmdArg(1, arg, sizeof(arg));
+ if(StrEqual(arg, "new")) {
+ JSONObject temp = LoadMapJson(currentMap);
+ GetCmdArg(2, arg, sizeof(arg));
+ if(temp != null && !StrEqual(arg, "confirm")) {
+ delete temp;
+ ReplyToCommand(client, "Existing map data found, enter /rbuild new confirm to overwrite.");
+ return Plugin_Handled;
+ }
+ g_builder.Cleanup();
+ g_builder.mapData = new JSONObject();
+ SaveMapJson(currentMap, g_builder.mapData);
+ ReplyToCommand(client, "Started new map data for %s", currentMap);
+ } else if(StrEqual(arg, "load")) {
+ if(args >= 2) {
+ GetCmdArg(2, arg, sizeof(arg));
+ } else {
+ strcopy(arg, sizeof(arg), currentMap);
+ }
+ g_builder.Cleanup();
+ g_builder.mapData = LoadMapJson(arg);
+ if(g_builder.mapData != null) {
+ ReplyToCommand(client, "Loaded map data for %s", arg);
+ } else {
+ ReplyToCommand(client, "No map data found for %s", arg);
+ }
+ } else if(StrEqual(arg, "menu")) {
+ OpenMainMenu(client);
+ } else if(g_builder.mapData == null) {
+ ReplyToCommand(client, "No map data for %s, either load with /rbuild load, or start new /rbuild new", currentMap);
+ return Plugin_Handled;
+ } else if(StrEqual(arg, "save")) {
+ SaveMapJson(currentMap, g_builder.mapData);
+ ReplyToCommand(client, "Saved %s", currentMap);
+ } else if(StrEqual(arg, "scenes")) {
+ Command_RandomizerBuild_Scenes(client, args);
+ } else if(StrEqual(arg, "sel") || StrEqual(arg, "selector")) {
+ if(g_builder.selectedVariantData == null) {
+ ReplyToCommand(client, "Please load map data, select a scene and a variant.");
+ return Plugin_Handled;
+ }
+ StartSelector(client, OnSelectorDone);
+ } else if(StrEqual(arg, "spawner")) {
+ if(g_builder.selectedVariantData == null) {
+ ReplyToCommand(client, "Please load map data, select a scene and a variant.");
+ return Plugin_Handled;
+ }
+ StartSpawner(client, OnSpawnerDone);
+ ReplyToCommand(client, "Spawn props to add to variant");
+ } else if(StrEqual(arg, "cursor")) {
+ if(g_builder.selectedVariantData == null) {
+ ReplyToCommand(client, "Please load map data, select a scene and a variant.");
+ return Plugin_Handled;
+ }
+ float origin[3];
+ char arg1[32];
+ int entity = GetLookingPosition(client, Filter_IgnorePlayer, origin);
+ GetCmdArg(2, arg1, sizeof(arg1));
+ ExportType exportType = Export_Model;
+ if(StrEqual(arg1, "hammerid")) {
+ exportType = Export_HammerId;
+ } else if(StrEqual(arg1, "targetname")) {
+ exportType = Export_TargetName;
+ }
+ if(entity > 0) {
+ g_builder.AddEntity(entity, exportType);
+ ReplyToCommand(client, "Added entity #%d to variant #%d", entity, g_builder.selectedVariantIndex);
+ } else {
+ ReplyToCommand(client, "No entity found");
+ }
+ } else if(StrEqual(arg, "entityid")) {
+ char arg1[32];
+ int entity = GetCmdArgInt(2);
+ GetCmdArg(3, arg1, sizeof(arg));
+ ExportType exportType = Export_Model;
+ if(StrEqual(arg1, "hammerid")) {
+ exportType = Export_HammerId;
+ } else if(StrEqual(arg1, "targetname")) {
+ exportType = Export_TargetName;
+ }
+ if(entity > 0) {
+ g_builder.AddEntity(entity, exportType);
+ ReplyToCommand(client, "Added entity #%d to variant #%d", entity, g_builder.selectedVariantIndex);
+ } else {
+ ReplyToCommand(client, "No entity found");
+ }
+ } else {
+ ReplyToCommand(client, "Unknown arg. Try: new, load, save, scenes, cursor");
+ }
+ return Plugin_Handled;
+}
+
+enum ExportType {
+ Export_HammerId,
+ Export_TargetName,
+ Export_Model
+}
+JSONObject ExportEntity(int entity, ExportType exportType = Export_Model) {
+ float origin[3], angles[3], size[3];
+ GetEntPropVector(entity, Prop_Send, "m_vecOrigin", origin);
+ GetEntPropVector(entity, Prop_Send, "m_angRotation", angles);
+ GetEntPropVector(entity, Prop_Send, "m_vecMaxs", size);
+
+ char model[64];
+ JSONObject entityData = new JSONObject();
+ GetEntityClassname(entity, model, sizeof(model));
+ if(StrContains(model, "prop_") == -1) {
+ entityData.Set("scale", VecToArray(size));
+ }
+ if(exportType == Export_HammerId) {
+ int hammerid = GetEntProp(entity, Prop_Data, "m_iHammerID");
+ entityData.SetString("type", "hammerid");
+ char id[16];
+ IntToString(hammerid, id, sizeof(id));
+ entityData.SetString("model", id);
+ } else if(exportType == Export_TargetName) {
+ GetEntPropString(entity, Prop_Data, "m_iName", model, sizeof(model));
+ entityData.SetString("type", "targetname");
+ entityData.SetString("model", model);
+ } else {
+ GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model));
+ entityData.SetString("model", model);
+ }
+ entityData.Set("origin", VecToArray(origin));
+ entityData.Set("angles", VecToArray(angles));
+ return entityData;
+}
+
+bool OnSpawnerDone(int client, int entity, CompleteType result) {
+ PrintToServer("Randomizer OnSpawnerDone");
+ if(result == Complete_PropSpawned && entity > 0) {
+ JSONObject entityData = ExportEntity(entity, Export_Model);
+ JSONArray entities = view_as(g_builder.selectedVariantData.Get("entities"));
+ entities.Push(entityData);
+ ReplyToCommand(client, "Added entity to variant");
+ RemoveEntity(entity);
+ }
+ return result == Complete_PropSpawned;
+}
+void OnSelectorDone(int client, ArrayList entities) {
+ JSONArray entArray = view_as(g_builder.selectedVariantData.Get("entities"));
+ if(entities != null) {
+ JSONObject entityData;
+ for(int i = 0; i < entities.Length; i++) {
+ int ref = entities.Get(i);
+ entityData = ExportEntity(ref, Export_Model);
+ entArray.Push(entityData);
+ delete entityData; //?
+ RemoveEntity(ref);
+ }
+ PrintToChat(client, "Added %d entities to variant", entities.Length);
+ delete entities;
+ }
+}
+
+JSONArray VecToArray(float vec[3]) {
+ JSONArray arr = new JSONArray();
+ arr.PushFloat(vec[0]);
+ arr.PushFloat(vec[1]);
+ arr.PushFloat(vec[2]);
+ return arr;
+}
+
+void Command_RandomizerBuild_Scenes(int client, int args) {
+ char arg[16];
+ GetCmdArg(2, arg, sizeof(arg));
+ if(StrEqual(arg, "new")) {
+ if(args < 4) {
+ ReplyToCommand(client, "Syntax: /rbuild scenes new ");
+ } else {
+ char name[64];
+ GetCmdArg(3, name, sizeof(name));
+ GetCmdArg(4, arg, sizeof(arg));
+ float chance = StringToFloat(arg);
+ JSONObject scene = new JSONObject();
+ scene.SetFloat("chance", chance);
+ scene.Set("variants", new JSONArray());
+ g_builder.mapData.Set(name, scene);
+ g_builder.SelectScene(name);
+ JSONArray variants = view_as(g_builder.selectedSceneData.Get("variants"));
+
+ JSONObject variantObj = new JSONObject();
+ variantObj.SetInt("weight", 1);
+ variantObj.Set("entities", new JSONArray());
+ variants.Push(variantObj);
+ g_builder.SelectVariant(0);
+ ReplyToCommand(client, "Created & selected scene & variant %s#0", name);
+ StartSelector(client, OnSelectorDone);
+ }
+ } else if(StrEqual(arg, "select") || StrEqual(arg, "load") || StrEqual(arg, "choose")) {
+ GetCmdArg(3, arg, sizeof(arg));
+ if(g_builder.SelectScene(arg)) {
+ int variantIndex;
+ if(GetCmdArgIntEx(4, variantIndex)) {
+ if(g_builder.SelectVariant(variantIndex)) {
+ ReplyToCommand(client, "Selected scene: %s#%d", arg, variantIndex);
+ } else {
+ ReplyToCommand(client, "Unknown variant for scene");
+ }
+ } else {
+ ReplyToCommand(client, "Selected scene: %s", arg);
+ }
+ } else {
+ ReplyToCommand(client, "No scene found");
+ }
+ } else if(StrEqual(arg, "variants")) {
+ Command_RandomizerBuild_Variants(client, args);
+ } else if(args > 1) {
+ ReplyToCommand(client, "Unknown argument, try: new, select, variants");
+ } else {
+ ReplyToCommand(client, "Scenes:");
+ JSONObjectKeys iterator = g_builder.mapData.Keys();
+ while(iterator.ReadKey(arg, sizeof(arg))) {
+ if(StrEqual(arg, g_builder.selectedSceneId)) {
+ ReplyToCommand(client, "\t%s (selected)", arg);
+ } else {
+ ReplyToCommand(client, "\t%s", arg);
+ }
+ }
+ }
+}
+
+void Command_RandomizerBuild_Variants(int client, int args) {
+ if(g_builder.selectedSceneId[0] == '\0') {
+ ReplyToCommand(client, "No scene selected, select with /rbuild groups select ");
+ return;
+ }
+ char arg[16];
+ GetCmdArg(3, arg, sizeof(arg));
+ if(StrEqual(arg, "new")) {
+ // /rbuild group variants new [weight]
+ int weight;
+ if(!GetCmdArgIntEx(4, weight)) {
+ weight = 1;
+ }
+ JSONArray variants = view_as(g_builder.selectedSceneData.Get("variants"));
+ JSONObject variantObj = new JSONObject();
+ variantObj.SetInt("weight", weight);
+ variantObj.Set("entities", new JSONArray());
+ int index = variants.Push(variantObj);
+ g_builder.SelectVariant(index);
+ ReplyToCommand(client, "Created variant #%d", index);
+ } else if(StrEqual(arg, "select")) {
+ int index = GetCmdArgInt(4);
+ if(g_builder.SelectVariant(index)) {
+ ReplyToCommand(client, "Selected variant: %s#%d", g_builder.selectedSceneId, index);
+ } else {
+ ReplyToCommand(client, "No variant found");
+ }
+ } else {
+ ReplyToCommand(client, "Variants:");
+ JSONObject variantObj;
+ JSONArray variants = view_as(g_builder.selectedSceneData.Get("variants"));
+ for(int i = 0; i < variants.Length; i++) {
+ variantObj = view_as(variants.Get(i));
+ int weight = 1;
+ if(variantObj.HasKey("weight"))
+ weight = variantObj.GetInt("weight");
+ JSONArray entities = view_as(variantObj.Get("entities"));
+ ReplyToCommand(client, " #%d. [W:%d] [#E:%d]", i, weight, entities.Length);
+ }
+ }
+}
enum struct SceneData {
@@ -211,10 +541,12 @@ enum struct SceneVariantData {
int weight;
ArrayList inputsList;
ArrayList entities;
+ ArrayList forcedScenes;
void Cleanup() {
delete this.inputsList;
delete this.entities;
+ delete this.forcedScenes;
}
}
@@ -366,6 +698,7 @@ enum struct LumpEditData {
}
enum struct MapData {
+ StringMap scenesKv;
ArrayList scenes;
ArrayList lumpEdits;
ArrayList activeScenes;
@@ -376,30 +709,40 @@ enum loadFlags {
FLAG_ALL_SCENES = 1, // Pick all scenes, no random chance
FLAG_ALL_VARIANTS = 2, // Pick all variants (for debug purposes),
FLAG_REFRESH = 4, // Load data bypassing cache
+ FLAG_FORCE_ACTIVE = 8 // Similar to ALL_SCENES, bypasses % chance
}
// Reads (mapname).json file and parses it
-public bool LoadMapData(const char[] map, int flags) {
+public JSONObject LoadMapJson(const char[] map) {
Debug("Loading config for %s", map);
char filePath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map);
if(!FileExists(filePath)) {
Log("[Randomizer] No map config file (data/randomizer/%s.json), not loading", map);
- return false;
+ return null;
}
- char buffer[65536];
- File file = OpenFile(filePath, "r");
- if(file == null) {
- LogError("Could not open map config file (data/randomizer/%s.json)", map);
- return false;
- }
- file.ReadString(buffer, sizeof(buffer));
- JSON_Object data = json_decode(buffer);
+ JSONObject data = JSONObject.FromFile(filePath);
+ if(data == null) {
+ LogError("Could not parse map config file (data/randomizer/%s.json)", map);
+ return null;
+ }
+ return data;
+}
+public void SaveMapJson(const char[] map, JSONObject json) {
+ Debug("Saving config for %s", map);
+ char filePath[PLATFORM_MAX_PATH], filePathTemp[PLATFORM_MAX_PATH];
+ BuildPath(Path_SM, filePathTemp, sizeof(filePath), "data/randomizer/%s.json.tmp", map);
+ BuildPath(Path_SM, filePath, sizeof(filePath), "data/randomizer/%s.json", map);
+
+ json.ToFile(filePathTemp, JSON_INDENT(4));
+ RenameFile(filePath, filePathTemp);
+ SetFilePermissions(filePath, FPERM_U_WRITE | FPERM_U_READ | FPERM_G_WRITE | FPERM_G_READ | FPERM_O_READ);
+}
+
+public bool LoadMapData(const char[] map, int flags) {
+ JSONObject data = LoadMapJson(map);
if(data == null) {
- json_get_last_error(buffer, sizeof(buffer));
- LogError("Could not parse map config file (data/randomizer/%s.json): %s", map, buffer);
- delete file;
return false;
}
@@ -407,51 +750,51 @@ public bool LoadMapData(const char[] map, int flags) {
Cleanup();
g_MapData.scenes = new ArrayList(sizeof(SceneData));
+ g_MapData.scenesKv = new StringMap();
g_MapData.lumpEdits = new ArrayList(sizeof(LumpEditData));
g_MapData.activeScenes.Clear();
Profiler profiler = new Profiler();
profiler.Start();
- int length = data.Length;
+ JSONObjectKeys iterator = data.Keys();
char key[32];
- for (int i = 0; i < length; i += 1) {
- data.GetKey(i, key, sizeof(key));
-
+ while(iterator.ReadKey(key, sizeof(key))) {
if(key[0] == '_') {
if(StrEqual(key, "_lumps")) {
- JSON_Array lumpsList = view_as(data.GetObject(key));
+ JSONArray lumpsList = view_as(data.Get(key));
if(lumpsList != null) {
for(int l = 0; l < lumpsList.Length; l++) {
- loadLumpData(g_MapData.lumpEdits, lumpsList.GetObject(l));
+ loadLumpData(g_MapData.lumpEdits, view_as(lumpsList.Get(l)));
}
}
} else {
Debug("Unknown special entry \"%s\", skipping", key);
}
} else {
- if(data.GetType(key) != JSON_Type_Object) {
- Debug("Invalid normal entry \"%s\" (not an object), skipping", key);
- continue;
- }
- JSON_Object scene = data.GetObject(key);
+ // if(data.GetType(key) != JSONType_Object) {
+ // Debug("Invalid normal entry \"%s\" (not an object), skipping", key);
+ // continue;
+ // }
+ JSONObject scene = view_as(data.Get(key));
// Parses scene data and inserts to scenes
loadScene(key, scene);
}
}
- json_cleanup_and_delete(data);
+ delete data;
profiler.Stop();
Log("Parsed map file for %s(%d) and found %d scenes in %.4f seconds", map, flags, g_MapData.scenes.Length, profiler.Time);
delete profiler;
- delete file;
return true;
}
// Calls LoadMapData (read&parse (mapname).json) then select scenes
public bool RunMap(const char[] map, int flags) {
if(g_MapData.scenes == null || flags & view_as(FLAG_REFRESH)) {
- LoadMapData(map, flags);
+ if(!LoadMapData(map, flags)) {
+ return false;
+ }
}
Profiler profiler = new Profiler();
@@ -463,7 +806,7 @@ public bool RunMap(const char[] map, int flags) {
return true;
}
-void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) {
+void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSONObject sceneData) {
SceneData scene;
scene.name = key;
scene.chance = sceneData.GetFloat("chance");
@@ -471,38 +814,73 @@ void loadScene(const char key[MAX_SCENE_NAME_LENGTH], JSON_Object sceneData) {
LogError("Scene \"%s\" has invalid chance (%f)", scene.name, scene.chance);
return;
}
+ // TODO: load "entities", merge with choice.entities
sceneData.GetString("group", scene.group, sizeof(scene.group));
scene.variants = new ArrayList(sizeof(SceneVariantData));
- JSON_Array variants = view_as(sceneData.GetObject("variants"));
+ if(!sceneData.HasKey("variants")) {
+ ThrowError("Failed to load: Scene \"%s\" has missing \"variants\" array", scene.name);
+ return;
+ }
+ JSONArray entities;
+ if(sceneData.HasKey("entities")) {
+ entities = view_as(sceneData.Get("entities"));
+ }
+
+ JSONArray variants = view_as(sceneData.Get("variants"));
for(int i = 0; i < variants.Length; i++) {
// Parses choice and loads to scene.choices
- loadChoice(scene, variants.GetObject(i));
+ loadChoice(scene, view_as(variants.Get(i)), entities);
}
g_MapData.scenes.PushArray(scene);
+ g_MapData.scenesKv.SetArray(scene.name, scene, sizeof(scene));
}
-void loadChoice(SceneData scene, JSON_Object choiceData) {
+void loadChoice(SceneData scene, JSONObject choiceData, JSONArray extraEntities) {
SceneVariantData choice;
- choice.weight = choiceData.GetInt("weight", 1);
+ choice.weight = 1;
+ if(choiceData.HasKey("weight"))
+ choice.weight = choiceData.GetInt("weight");
choice.entities = new ArrayList(sizeof(VariantEntityData));
choice.inputsList = new ArrayList(sizeof(VariantInputData));
- JSON_Array entities = view_as(choiceData.GetObject("entities"));
- if(entities != null) {
+ choice.forcedScenes = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH));
+ // Load in any variant-based entities
+ if(choiceData.HasKey("entities")) {
+ JSONArray entities = view_as(choiceData.Get("entities"));
for(int i = 0; i < entities.Length; i++) {
// Parses entities and loads to choice.entities
- loadChoiceEntity(choice.entities, entities.GetObject(i));
+ loadChoiceEntity(choice.entities, view_as(entities.Get(i)));
}
+ delete entities;
}
- JSON_Array inputsList = view_as(choiceData.GetObject("inputs"));
- if(inputsList != null) {
- for(int i = 0; i < inputsList.Length; i++) {
- loadChoiceInput(choice.inputsList, inputsList.GetObject(i));
+ // Load in any entities that the scene has
+ if(extraEntities != null) {
+ for(int i = 0; i < extraEntities.Length; i++) {
+ // Parses entities and loads to choice.entities
+ loadChoiceEntity(choice.entities, view_as(extraEntities.Get(i)));
}
+ delete extraEntities;
+ }
+ // Load all inputs
+ if(choiceData.HasKey("inputs")) {
+ JSONArray inputsList = view_as(choiceData.Get("inputs"));
+ for(int i = 0; i < inputsList.Length; i++) {
+ loadChoiceInput(choice.inputsList, view_as(inputsList.Get(i)));
+ }
+ delete inputsList;
+ }
+ if(choiceData.HasKey("force_scenes")) {
+ JSONArray scenes = view_as(choiceData.Get("force_scenes"));
+ char sceneId[32];
+ for(int i = 0; i < scenes.Length; i++) {
+ scenes.GetString(i, sceneId, sizeof(sceneId));
+ choice.forcedScenes.PushString(sceneId);
+ }
+ delete scenes;
}
scene.variants.PushArray(choice);
}
-void loadChoiceInput(ArrayList list, JSON_Object inputData) {
+void loadChoiceInput(ArrayList list, JSONObject inputData) {
VariantInputData input;
// Check classname -> targetname -> hammerid
if(!inputData.GetString("classname", input.name, sizeof(input.name))) {
@@ -527,7 +905,7 @@ void loadChoiceInput(ArrayList list, JSON_Object inputData) {
list.PushArray(input);
}
-void loadLumpData(ArrayList list, JSON_Object inputData) {
+void loadLumpData(ArrayList list, JSONObject inputData) {
LumpEditData input;
// Check classname -> targetname -> hammerid
if(!inputData.GetString("classname", input.name, sizeof(input.name))) {
@@ -553,7 +931,7 @@ void loadLumpData(ArrayList list, JSON_Object inputData) {
list.PushArray(input);
}
-void loadChoiceEntity(ArrayList list, JSON_Object entityData) {
+void loadChoiceEntity(ArrayList list, JSONObject entityData) {
VariantEntityData entity;
entityData.GetString("model", entity.model, sizeof(entity.model));
if(!entityData.GetString("type", entity.type, sizeof(entity.type))) {
@@ -569,18 +947,20 @@ void loadChoiceEntity(ArrayList list, JSON_Object entityData) {
list.PushArray(entity);
}
-void GetVector(JSON_Object obj, const char[] key, float out[3]) {
- JSON_Array vecArray = view_as(obj.GetObject(key));
+bool GetVector(JSONObject obj, const char[] key, float out[3]) {
+ if(!obj.HasKey(key)) return false;
+ JSONArray vecArray = view_as(obj.Get(key));
if(vecArray != null) {
out[0] = vecArray.GetFloat(0);
out[1] = vecArray.GetFloat(1);
out[2] = vecArray.GetFloat(2);
}
+ return true;
}
-void GetColor(JSON_Object obj, const char[] key, int out[4]) {
- JSON_Array vecArray = view_as(obj.GetObject(key));
- if(vecArray != null) {
+void GetColor(JSONObject obj, const char[] key, int out[4], int defaultColor[4] = { 255, 255, 255, 255 }) {
+ if(obj.HasKey(key)) {
+ JSONArray vecArray = view_as(obj.Get(key));
out[0] = vecArray.GetInt(0);
out[1] = vecArray.GetInt(1);
out[2] = vecArray.GetInt(2);
@@ -589,10 +969,7 @@ void GetColor(JSON_Object obj, const char[] key, int out[4]) {
else
out[3] = 255;
} else {
- out[0] = 255;
- out[1] = 255;
- out[2] = 255;
- out[3] = 255;
+ out = defaultColor;
}
}
@@ -600,6 +977,8 @@ void selectScenes(int flags = 0) {
SceneData scene;
StringMap groups = new StringMap();
ArrayList list;
+ // Select and spawn non-group scenes
+ // TODO: refactor to use .scenesKv
for(int i = 0; i < g_MapData.scenes.Length; i++) {
g_MapData.scenes.GetArray(i, scene);
// TODO: Exclusions
@@ -607,6 +986,7 @@ void selectScenes(int flags = 0) {
if(scene.group[0] == '\0') {
selectScene(scene, flags);
} else {
+ // Load it into group list
if(!groups.GetValue(scene.group, list)) {
list = new ArrayList();
}
@@ -615,25 +995,61 @@ void selectScenes(int flags = 0) {
}
}
+ // Iterate through groups and select a random scene:
StringMapSnapshot snapshot = groups.Snapshot();
char key[MAX_SCENE_NAME_LENGTH];
for(int i = 0; i < snapshot.Length; i++) {
snapshot.GetKey(i, key, sizeof(key));
groups.GetValue(key, list);
+ // Select a random scene from the group:
int index = GetURandomInt() % list.Length;
index = list.Get(index);
g_MapData.scenes.GetArray(index, scene);
+
Debug("Selected scene \"%s\" for group %s (%d members)", scene.name, key, list.Length);
selectScene(scene, flags);
delete list;
}
+ // Traverse active scenes, loading any other scene it requires (via .force_scenes)
+ ActiveSceneData aScene;
+ SceneVariantData choice;
+ ArrayList forcedScenes = new ArrayList(ByteCountToCells(MAX_SCENE_NAME_LENGTH));
+ for(int i = 0; i < g_MapData.activeScenes.Length; i++) {
+ g_MapData.activeScenes.GetArray(i, aScene);
+ g_MapData.scenes.GetArray(i, scene);
+ scene.variants.GetArray(aScene.variantIndex, choice);
+ if(choice.forcedScenes != null) {
+ for(int j = 0; j < choice.forcedScenes.Length; j++) {
+ choice.forcedScenes.GetString(j, key, sizeof(key));
+ forcedScenes.PushString(key);
+ }
+ }
+ }
+ // Iterate and activate any forced scenes
+ for(int i = 0; i < forcedScenes.Length; i++) {
+ forcedScenes.GetString(i, key, sizeof(key));
+ // Check if scene was already loaded
+ bool isSceneAlreadyLoaded = false;
+ for(int j = 0; j < g_MapData.activeScenes.Length; i++) {
+ g_MapData.activeScenes.GetArray(j, aScene);
+ if(StrEqual(aScene.name, key)) {
+ isSceneAlreadyLoaded = true;
+ break;
+ }
+ }
+ if(isSceneAlreadyLoaded) continue;
+ g_MapData.scenesKv.GetArray(key, scene, sizeof(scene));
+ selectScene(scene, flags | view_as(FLAG_FORCE_ACTIVE));
+ }
+
+ delete forcedScenes;
delete snapshot;
delete groups;
}
void selectScene(SceneData scene, int flags) {
- // Use the .chance field unless FLAG_ALL_SCENES is set
- if(~flags & view_as(FLAG_ALL_SCENES) && GetURandomFloat() > scene.chance) {
+ // Use the .chance field unless FLAG_ALL_SCENES or FLAG_FORCE_ACTIVE is set
+ if(~flags & view_as(FLAG_ALL_SCENES) && ~flags & view_as(FLAG_FORCE_ACTIVE) && GetURandomFloat() > scene.chance) {
return;
}
@@ -645,20 +1061,26 @@ void selectScene(SceneData scene, int flags) {
ArrayList choices = new ArrayList();
SceneVariantData choice;
int index;
+ Debug("Scene %s has %d variants", scene.name, scene.variants.Length);
// Weighted random: Push N times dependent on weight
for(int i = 0; i < scene.variants.Length; i++) {
scene.variants.GetArray(i, choice);
if(flags & view_as(FLAG_ALL_VARIANTS)) {
spawnVariant(choice);
} else {
+ if(choice.weight <= 0) {
+ PrintToServer("Warn: Variant %d in scene %s has invalid weight", i, scene.name);
+ continue;
+ }
for(int c = 0; c < choice.weight; c++) {
choices.Push(i);
}
}
}
+ Debug("Total choices: %d", choices.Length);
if(flags & view_as(FLAG_ALL_VARIANTS)) {
delete choices;
- } else {
+ } else if(choices.Length > 0) {
index = GetURandomInt() % choices.Length;
index = choices.Get(index);
delete choices;
@@ -666,7 +1088,6 @@ void selectScene(SceneData scene, int flags) {
scene.variants.GetArray(index, choice);
spawnVariant(choice);
}
-
ActiveSceneData aScene;
strcopy(aScene.name, sizeof(aScene.name), scene.name);
aScene.variantIndex = index;
diff --git a/scripting/sm_overlay.sp b/scripting/sm_overlay.sp
index 87178a0..996b145 100644
--- a/scripting/sm_overlay.sp
+++ b/scripting/sm_overlay.sp
@@ -20,6 +20,9 @@ int connectAttempts;
authState g_authState;
JSONObject g_globalVars;
+StringMap actionFallbackHandlers; // namespace -> action name has no handler, falls to this.
+StringMap actionNamespaceHandlers; // { namespace: { [action name] -> handler } }
+
enum authState {
AuthError = -1,
NotAuthorized,
@@ -53,8 +56,16 @@ char OUT_EVENT_IDS[view_as(Event_Invalid)][] = {
char steamidCache[MAXPLAYERS+1][32];
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
- CreateNative("UIElement.Send", Native_UpdateUI);
- CreateNative("TempUI.Send", Native_UpdateTempUI);
+ RegPluginLibrary("overlay");
+
+ CreateNative("IsOverlayConnected", Native_IsOverlayConnected);
+ CreateNative("RegisterActionAnyHandler", Native_ActionHandler);
+ CreateNative("RegisterActionHandler", Native_ActionHandler);
+
+ CreateNative("UIElement.SendAll", Native_UpdateUI);
+ CreateNative("UIElement.SendTo", Native_UpdateUI);
+ CreateNative("TempUI.SendAll", Native_UpdateTempUI);
+ CreateNative("TempUI.SendTo", Native_UpdateTempUI);
return APLRes_Success;
}
@@ -64,6 +75,9 @@ public void OnPluginStart() {
SetFailState("This plugin is for L4D/L4D2 only.");
}
+ actionFallbackHandlers = new StringMap();
+ actionNamespaceHandlers = new StringMap();
+
g_globalVars = new JSONObject();
cvarManagerUrl = CreateConVar("sm_overlay_manager_url", "ws://localhost:3011/socket", "");
@@ -171,6 +185,7 @@ void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
if(GetClientCount(false) == 0) {
DisconnectManager();
}
+ steamidCache[client][0] = '\0';
}
void OnWSConnect(WebSocket ws, any arg) {
@@ -210,12 +225,78 @@ void OnWSJson(WebSocket ws, JSON message, any data) {
message.ToString(buffer, sizeof(buffer));
PrintToServer("[Overlay] Error: %s", buffer);
} else {
+ char type[32];
+ obj.GetString("type", type, sizeof(type));
+ if(StrEqual(type, "action")) {
+ OnAction(obj);
+ }
+
char buffer[2048];
message.ToString(buffer, sizeof(buffer));
PrintToServer("[Overlay] Got JSON: %s", buffer);
}
}
+stock int ExplodeStringToArrayList(const char[] text, const char[] split, ArrayList buffers, int maxStringLength) {
+ int reloc_idx, idx, total;
+
+ if (buffers == null || !split[0]) {
+ return 0;
+ }
+
+ char[] item = new char[maxStringLength];
+ while ((idx = SplitString(text[reloc_idx], split, item, maxStringLength)) != -1) {
+ reloc_idx += idx;
+ ++total;
+ buffers.PushString(item);
+ }
+ ++total;
+ buffers.PushString(text[reloc_idx]);
+
+ return buffers.Length;
+}
+
+void OnAction(JSONObject obj) {
+ char steamid[32];
+ obj.GetString("steamid", steamid, sizeof(steamid));
+ char ns[64];
+ obj.GetString("namespace", ns, sizeof(ns));
+ char id[64];
+ obj.GetString("elem_id", id, sizeof(id));
+ char action[256];
+ obj.GetString("action", action, sizeof(action));
+
+ int client = FindClientBySteamId2(steamid);
+
+ StringMap nsHandler;
+ PrivateForward fwd;
+ if(!actionNamespaceHandlers.GetValue(ns, nsHandler) || !nsHandler.GetValue(id, fwd)) {
+ if(!actionFallbackHandlers.GetValue(ns, fwd)) {
+ // No handler or catch all namespace handler
+ return;
+ }
+ }
+
+ ArrayList args = new ArrayList(ACTION_ARG_LENGTH);
+ ExplodeStringToArrayList(action, " ", args, ACTION_ARG_LENGTH);
+ UIActionEvent event = UIActionEvent(args);
+
+ Call_StartForward(fwd);
+ Call_PushCell(event);
+ Call_PushCell(client);
+ Call_Finish();
+ event._Delete();
+}
+
+int FindClientBySteamId2(const char[] steamid) {
+ for(int i = 1; i <= MaxClients; i++) {
+ if(StrEqual(steamidCache[i], steamid)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
void ConnectManager() {
DisconnectManager();
@@ -281,11 +362,15 @@ void SendEvent_UpdateUI(const char[] elemNamespace, const char[] elemId, bool vi
}
//SendTempUI(int client, const char[] id, int lifetime, JSONObject element);
-bool Native_SendTempUI(Handle plugin, int numParams) {
+any Native_SendTempUI(Handle plugin, int numParams) {
if(!isManagerReady()) return false;
int client = GetNativeCell(1);
- if(steamidCache[client][0] == '\0') return false;
+ if (client <= 0 || client > MaxClients) {
+ return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client);
+ } else if (!IsClientConnected(client) || steamidCache[client][0] == '\0') {
+ return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected/authorized yet", client);
+ }
char id[64];
GetNativeString(2, id, sizeof(id));
@@ -299,7 +384,7 @@ bool Native_SendTempUI(Handle plugin, int numParams) {
}
//ShowUI(int client, const char[] elemNamespace, const char[] elemId, JSONObject variables);
-bool Native_ShowUI(Handle plugin, int numParams) {
+any Native_ShowUI(Handle plugin, int numParams) {
if(!isManagerReady()) return false;
char elemNamespace[64], id[64];
@@ -313,7 +398,7 @@ bool Native_ShowUI(Handle plugin, int numParams) {
}
//HideUI(int client, const char[] elemNamespace, const char[] elemId);
-bool Native_HideUI(Handle plugin, int numParams) {
+any Native_HideUI(Handle plugin, int numParams) {
if(!isManagerReady()) return false;
char elemNamespace[64], id[64];
@@ -325,7 +410,7 @@ bool Native_HideUI(Handle plugin, int numParams) {
}
//PlayAudio(int client, const char[] url);
-bool Native_PlayAudio(Handle plugin, int numParams) {
+any Native_PlayAudio(Handle plugin, int numParams) {
if(!isManagerReady()) return false;
char url[256];
@@ -335,27 +420,92 @@ bool Native_PlayAudio(Handle plugin, int numParams) {
return true;
}
-bool Native_UpdateUI(Handle plugin, int numParams) {
+any Native_UpdateUI(Handle plugin, int numParams) {
if(!isManagerReady()) return false;
UIElement elem = GetNativeCell(1);
+ JSONObject obj = view_as(elem);
+
+ JSONArray arr = view_as(obj.Get("steamids"));
+ if(numParams == 0) {
+ arr.Clear();
+ } else if(numParams == 1) {
+ int client = GetNativeCell(2);
+ if (client <= 0 || client > MaxClients) {
+ return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client);
+ } else if (!IsClientConnected(client) || steamidCache[client][0] == '\0') {
+ return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected/authorized yet", client);
+ }
+ arr.PushString(steamidCache[client]);
+ }
g_ws.Write(view_as(elem));
+ arr.Clear();
return true;
}
-bool Native_UpdateTempUI(Handle plugin, int numParams) {
+any Native_UpdateTempUI(Handle plugin, int numParams) {
if(!isManagerReady()) return false;
TempUI elem = GetNativeCell(1);
+ JSONObject obj = view_as(elem);
+
+ JSONArray arr = view_as(obj.Get("steamids"));
+ if(numParams == 0) {
+ arr.Clear();
+ } else if(numParams == 1) {
+ int client = GetNativeCell(2);
+ if (client <= 0 || client > MaxClients) {
+ return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index (%d)", client);
+ } else if (!IsClientConnected(client) || steamidCache[client][0] == '\0') {
+ return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not connected/authorized yet", client);
+ }
+ arr.PushString(steamidCache[client]);
+ }
g_ws.Write(view_as(elem));
+ arr.Clear();
return true;
}
//IsOverlayConnected();
-bool Native_IsOverlayConnected(Handle plugin, int numParams) {
+any Native_IsOverlayConnected(Handle plugin, int numParams) {
return isManagerReady();
+}
+
+//RegisterActionHandler
+//RegisterActionAnyHandler
+any Native_ActionHandler(Handle plugin, int numParams) {
+ char ns[64];
+ GetNativeString(1, ns, sizeof(ns));
+
+ if(numParams == 3) {
+ // RegisterActionHandler
+ StringMap nsHandlers;
+ if(!actionNamespaceHandlers.GetValue(ns, nsHandlers)) {
+ nsHandlers = new StringMap();
+ }
+
+ char actionId[64];
+ GetNativeString(2, actionId, sizeof(actionId));
+
+ PrivateForward fwd;
+ if(!nsHandlers.GetValue(actionId, fwd)) {
+ fwd = new PrivateForward(ET_Ignore, Param_Cell);
+ }
+ fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(3));
+ nsHandlers.SetValue(actionId, fwd);
+ } else {
+ // RegisterActionAnyHandler
+
+ PrivateForward fwd;
+ if(!actionFallbackHandlers.GetValue(ns, fwd)) {
+ fwd = new PrivateForward(ET_Ignore, Param_Cell);
+ }
+ fwd.AddFunction(INVALID_HANDLE, GetNativeFunction(2));
+ actionFallbackHandlers.SetValue(ns, fwd);
+ }
+ return 1;
}
\ No newline at end of file
diff --git a/scripting/sm_player_notes.sp b/scripting/sm_player_notes.sp
index 16d984b..eb5adf3 100644
--- a/scripting/sm_player_notes.sp
+++ b/scripting/sm_player_notes.sp
@@ -242,15 +242,12 @@ public Action OnClientSayCommand(int client, const char[] command, const char[]
PrintToChat(client, "Note cancelled.");
} else {
int size = 2 * strlen(sArgs) + 1;
- char[] sArgsTrimmed = new char[size];
- DB.Escape(sArgs, sArgsTrimmed, size);
- TrimString(sArgsTrimmed);
char buffer[32];
GetClientAuthId(client, AuthId_Steam2, buffer, sizeof(buffer));
// TODO: escape content
- DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgsTrimmed);
+ DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", menuNoteTarget, buffer, sArgs);
DB.Query(DB_AddNote, query);
- LogAction(client, -1, "added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgsTrimmed);
+ LogAction(client, -1, "added note for \"%s\" (%s): \"%s\"", client, menuNoteTargetName, menuNoteTarget, sArgs);
Format(buffer, sizeof(buffer), "%N: ", client);
CShowActivity2(client, buffer, "added a note for {green}%s: {default}\"%s\"", menuNoteTargetName, sArgs);
}
@@ -310,10 +307,7 @@ public Action Command_AddNote(int client, int args) {
char authMarker[32];
if(client > 0)
GetClientAuthId(client, AuthId_Steam2, authMarker, sizeof(authMarker));
- int size = 2 * strlen(reason) + 1;
- char[] content = new char[size];
- DB.Escape(reason, content, size);
- DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", auth, authMarker, content);
+ DB.Format(query, sizeof(query), "INSERT INTO `notes` (steamid, markedBy, content) VALUES ('%s', '%s', '%s')", auth, authMarker, reason);
DB.Query(DB_AddNote, query);
LogAction(client, target_list[0], "\"%L\" added note for \"%L\": \"%s\"", client, target_list[0], reason);
CShowActivity(client, "added a note for {green}%N: {default}\"%s\"", target_list[0], reason);