diff --git a/resources/Interaction-Menu/.gitignore b/resources/Interaction-Menu/.gitignore
new file mode 100644
index 000000000..6fd0a376d
--- /dev/null
+++ b/resources/Interaction-Menu/.gitignore
@@ -0,0 +1,41 @@
+# Compiled Lua sources
+luac.out
+
+# luarocks build files
+*.src.rock
+*.zip
+*.tar.gz
+
+# Object files
+*.o
+*.os
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+*.def
+*.exp
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
diff --git a/resources/Interaction-Menu/LICENSE b/resources/Interaction-Menu/LICENSE
new file mode 100644
index 000000000..f288702d2
--- /dev/null
+++ b/resources/Interaction-Menu/LICENSE
@@ -0,0 +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
+.
diff --git a/resources/Interaction-Menu/README.md b/resources/Interaction-Menu/README.md
new file mode 100644
index 000000000..eb98e6a91
--- /dev/null
+++ b/resources/Interaction-Menu/README.md
@@ -0,0 +1,30 @@
+[](https://semdevelopment.com/discord)
+
+# SEM_InteractionMenu
+*Multi Purpose FiveM Interaction Menu*
+
+This resource is a menu with actions for LEO, Fire and Civ including Vehicle Controls and Emotes!
+
+Each section has features that would be used by each of the professions, for example the LEO Menu has Cuff, Drag, Seat, etc.
+Some menu features also have commands out of ease during RP.
+
+SEM_InteractionMenu was created using NativeUI [LUA]
+
+
+### Information:
+Current Version: **v1.7.1**
+
+Changes: **• Fixes menu item skipping**
+
+**THIS UPDATE *ONLY* AFFECTS THE DEPENDENCY NATIVEUI - YOU CAN JUST UPDATE THAT FILE *(NO OTHER FILES WERE CHANGED!)***
+
+
+### Links:
+
+Support/Discord: [Click Here](https://semdevelopment.com/discord)
+
+Information: [Click Here](https://semdevelopment.com/releases/interactionmenu)
+
+Instruction/Docs: [Click here](https://semdevelopment.com/releases/interactionmenu/docs)
+
+*For full changes checkout [here](https://semdevelopment.com/releases/interactionmenu/docs/changelog)*
diff --git a/resources/Interaction-Menu/client.lua b/resources/Interaction-Menu/client.lua
new file mode 100644
index 000000000..12f8e1f6d
--- /dev/null
+++ b/resources/Interaction-Menu/client.lua
@@ -0,0 +1,898 @@
+--[[
+──────────────────────────────────────────────────────────────
+
+ SEM_InteractionMenu (client.lua) - Created by Scott M
+ Current Version: v1.7.1 (Sep 2021)
+
+ Support: https://semdevelopment.com/discord
+
+ !!! Change vaules in the 'config.lua' !!!
+ DO NOT EDIT THIS IF YOU DON'T KNOW WHAT YOU ARE DOING
+
+──────────────────────────────────────────────────────────────
+]]
+
+
+
+--Cuffing Event
+local isCuffed = false
+RegisterNetEvent('SEM_InteractionMenu:Cuff')
+AddEventHandler('SEM_InteractionMenu:Cuff', function()
+ local Ped = PlayerPedId()
+ if (DoesEntityExist(Ped)) then
+ Citizen.CreateThread(function()
+ RequestAnimDict('mp_arresting')
+ while not HasAnimDictLoaded('mp_arresting') do
+ Citizen.Wait(0)
+ end
+
+ if isCuffed then
+ isCuffed = false
+ Citizen.Wait(500)
+ SetEnableHandcuffs(Ped, false)
+ ClearPedTasksImmediately(Ped)
+ else
+ isCuffed = true
+ SetEnableHandcuffs(Ped, true)
+ TaskPlayAnim(Ped, 'mp_arresting', 'idle', 8.0, -8, -1, 49, 0, 0, 0, 0)
+ end
+ end)
+ end
+end)
+
+--Cuff Animation & Restructions
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(1)
+
+ if isCuffed then
+ if not IsEntityPlayingAnim(GetPlayerPed(PlayerId()), 'mp_arresting', 'idle', 3) then
+ TaskPlayAnim(GetPlayerPed(PlayerId()), 'mp_arresting', 'idle', 8.0, -8, -1, 49, 0, 0, 0, 0)
+ end
+
+ SetCurrentPedWeapon(PlayerPedId(), 'weapon_unarmed', true)
+
+ if not Config.VehEnterCuffed then
+ DisableControlAction(1, 23, true) --F | Enter Vehicle
+ DisableControlAction(1, 75, true) --F | Exit Vehicle
+ end
+ DisableControlAction(1, 140, true) --R
+ DisableControlAction(1, 141, true) --Q
+ DisableControlAction(1, 142, true) --LMB
+ SetPedPathCanUseLadders(GetPlayerPed(PlayerId()), false)
+ if IsPedInAnyVehicle(GetPlayerPed(PlayerId()), false) then
+ DisableControlAction(0, 59, true) --Vehicle Driving
+ end
+ end
+ end
+end)
+
+
+
+--Dragging Event
+local Drag = false
+local OfficerDrag = -1
+RegisterNetEvent('SEM_InteractionMenu:Drag')
+AddEventHandler('SEM_InteractionMenu:Drag', function(ID)
+ Drag = not Drag
+ OfficerDrag = ID
+
+ if not Drag then
+ DetachEntity(PlayerPedId(), true, false)
+ end
+end)
+
+--Drag Attachment
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(1)
+
+ if Drag then
+ local Ped = GetPlayerPed(GetPlayerFromServerId(OfficerDrag))
+ local Ped2 = PlayerPedId()
+ AttachEntityToEntity(Ped2, Ped, 4103, 0.35, 0.38, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true)
+ DisableControlAction(1, 140, true) --R
+ DisableControlAction(1, 141, true) --Q
+ DisableControlAction(1, 142, true) --LMB
+ end
+ end
+end)
+
+
+
+--Force Seat Player Event
+RegisterNetEvent('SEM_InteractionMenu:Seat')
+AddEventHandler('SEM_InteractionMenu:Seat', function(Veh)
+ local Pos = GetEntityCoords(PlayerPedId())
+ local EntityWorld = GetOffsetFromEntityInWorldCoords(PlayerPedId(), 0.0, 20.0, 0.0)
+ local RayHandle = CastRayPointToPoint(Pos.x, Pos.y, Pos.z, EntityWorld.x, EntityWorld.y, EntityWorld.z, 10, PlayerPedId(), 0)
+ local _, _, _, _, VehicleHandle = GetRaycastResult(RayHandle)
+ if VehicleHandle ~= nil then
+ SetPedIntoVehicle(PlayerPedId(), VehicleHandle, 1)
+ end
+end)
+
+
+
+--Force Unseat Player Event
+RegisterNetEvent('SEM_InteractionMenu:Unseat')
+AddEventHandler('SEM_InteractionMenu:Unseat', function(ID)
+ local Ped = GetPlayerPed(ID)
+ ClearPedTasksImmediately(Ped)
+ PlayerPos = GetEntityCoords(PlayerPedId(), true)
+ local X = PlayerPos.x - 0
+ local Y = PlayerPos.y - 0
+
+ SetEntityCoords(PlayerPedId(), X, Y, PlayerPos.z)
+end)
+
+
+
+--Spike Strip Spawn Event
+local SpawnedSpikes = {}
+RegisterNetEvent('SEM_InteractionMenu:Spikes-SpawnSpikes')
+AddEventHandler('SEM_InteractionMenu:Spikes-SpawnSpikes', function(Length)
+ if IsPedInAnyVehicle(PlayerPedId(), false) then
+ Notify('~r~You can\'t set spikes while in a vehicle!')
+ return
+ end
+
+ local SpawnCoords = GetOffsetFromEntityInWorldCoords(GetPlayerPed(PlayerId()) , 0.0, 2.0, 0.0)
+ for a = 1, Length do
+ local Spike = CreateObject(GetHashKey('P_ld_stinger_s'), SpawnCoords.x, SpawnCoords.y, SpawnCoords.z, 1, 1, 1)
+ local NetID = NetworkGetNetworkIdFromEntity(Spike)
+ SetNetworkIdExistsOnAllMachines(NetID, true)
+ SetNetworkIdCanMigrate(NetID, false)
+ SetEntityHeading(Spike, GetEntityHeading(GetPlayerPed(PlayerId()) ))
+ PlaceObjectOnGroundProperly(Spike)
+ FreezeEntityPosition(Spike, true)
+ SpawnCoords = GetOffsetFromEntityInWorldCoords(Spike, 0.0, 4.0, 0.0)
+ table.insert(SpawnedSpikes, NetID)
+ end
+end)
+
+--Spike Strip Delete Event
+RegisterNetEvent('SEM_InteractionMenu:Spikes-DeleteSpikes')
+AddEventHandler('SEM_InteractionMenu:Spikes-DeleteSpikes', function()
+ for a = 1, #SpawnedSpikes do
+ local Spike = NetworkGetEntityFromNetworkId(SpawnedSpikes[a])
+ DeleteEntity(Spike)
+ end
+ Notify('~r~Spikes Strips Removed!')
+ SpawnedSpikes = {}
+end)
+
+--Spike Strip Tire Popping
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(25)
+
+ if IsPedInAnyVehicle(PlayerPedId() , false) then
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId() , false)
+
+ if GetPedInVehicleSeat(Vehicle, -1) == PlayerPedId() then
+ local VehiclePos = GetEntityCoords(Vehicle, false)
+ local Spike = GetClosestObjectOfType(VehiclePos.x, VehiclePos.y, VehiclePos.z, 2.0, GetHashKey('P_ld_stinger_s'), 1, 1, 1)
+
+ if Spike ~= 0 then
+ local Tires = {
+ {bone = 'wheel_lf', index = 0},
+ {bone = 'wheel_rf', index = 1},
+ {bone = 'wheel_lm', index = 2},
+ {bone = 'wheel_rm', index = 3},
+ {bone = 'wheel_lr', index = 4},
+ {bone = 'wheel_rr', index = 5}
+ }
+
+ for a = 1, #Tires do
+ local TirePos = GetWorldPositionOfEntityBone(Vehicle, GetEntityBoneIndexByName(Vehicle, Tires[a].bone))
+ local Spike = GetClosestObjectOfType(TirePos.x, TirePos.y, TirePos.z, 2.0, GetHashKey('P_ld_stinger_s'), 1, 1, 1)
+ local SpikePos = GetEntityCoords(Spike, false)
+ local Distance = Vdist(TirePos.x, TirePos.y, TirePos.z, SpikePos.x, SpikePos.y, SpikePos.z)
+
+ if Distance < 1.8 then
+ if not IsVehicleTyreBurst(Vehicle, Tires[a].index, true) or IsVehicleTyreBurst(Vehicle, Tires[a].index, false) then
+ SetVehicleTyreBurst(Vehicle, Tires[a].index, false, 1000.0)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end)
+
+
+
+--Backup
+RegisterNetEvent('SEM_InteractionMenu:CallBackup')
+AddEventHandler('SEM_InteractionMenu:CallBackup', function(Code, StreetName, Coords)
+ if LEORestrict() then
+ local BackupBlip = nil
+ local BackupBlips = {}
+
+ local function CreateBlip(x, y, z, Name, Sprite, Size, Colour)
+ BackupBlip = AddBlipForCoord(x, y, z)
+ SetBlipSprite(BackupBlip, Sprite)
+ SetBlipDisplay(BackupBlip, 4)
+ SetBlipScale(BackupBlip, Size)
+ SetBlipColour(BackupBlip, Colour)
+ SetBlipAsShortRange(BackupBlip, true)
+
+ BeginTextCommandSetBlipName('STRING')
+ AddTextComponentString(Name)
+ EndTextCommandSetBlipName(BackupBlip)
+ table.insert(BackupBlips, BackupBlip)
+ Citizen.Wait(Config.BackupBlipTimeout * 60000)
+ for _, Blip in pairs(BackupBlips) do
+ RemoveBlip(Blip)
+ end
+ end
+
+ if Code == 1 then
+ Notify('An officer is requesting ~g~Code 1 ~w~backup at ~b~' .. StreetName)
+ CreateBlip(Coords.x, Coords.y, Coords.z, 'Code 1 Backup Requested', 56, 0.8, 2)
+ elseif Code == 2 then
+ Notify('An officer is requesting ~y~Code 2 ~w~backup at ~b~' .. StreetName)
+ CreateBlip(Coords.x, Coords.y, Coords.z, 'Code 2 Backup Requested', 56, 0.8, 17)
+ elseif Code == 3 then
+ Notify('An officer is requesting ~r~Code 3 ~w~backup at ~b~' .. StreetName)
+ CreateBlip(Coords.x, Coords.y, Coords.z, 'Code 3 Backup Requested', 56, 1.0, 49)
+ elseif Code == 99 then
+ Notify('An officer is requesting ~r~Code 99 ~w~backup at ~b~' .. StreetName)
+ CreateBlip(Coords.x, Coords.y, Coords.z, 'Code 99 Backup Requested', 56, 1.2, 49)
+ elseif Code == 'panic' then
+ Notify('An officer has pressed their ~r~Panic Button ~w~at ~b~' .. StreetName)
+ CreateBlip(Coords.x, Coords.y, Coords.z, 'Panic Button Pressed', 103, 1.2, 49)
+ end
+ end
+end)
+
+
+
+--Jail
+CurrentlyJailed = false
+EarlyRelease = false
+OriginalJailTime = 0
+RegisterNetEvent('SEM_InteractionMenu:JailPlayer')
+AddEventHandler('SEM_InteractionMenu:JailPlayer', function(JailTime)
+ if CurrentlyJailed then
+ return
+ end
+ if CurrentlyHospitaled then
+ return
+ end
+
+ OriginalJailTime = JailTime
+
+ local Ped = PlayerPedId()
+ if DoesEntityExist(Ped) then
+ Citizen.CreateThread(function()
+ SetEntityCoords(Ped, Config.JailLocation.Jail.x, Config.JailLocation.Jail.y, Config.JailLocation.Jail.z)
+ SetEntityHeading(Ped, Config.JailLocation.Jail.h)
+ CurrentlyJailed = true
+
+ while JailTime >= 0 and not EarlyRelease do
+ SetEntityInvincible(Ped, true)
+ if IsPedInAnyVehicle(Ped, false) then
+ ClearPedTasksImmediately(Ped)
+ end
+
+ if JailTime % 30 == 0 and JailTime ~= 0 then
+ TriggerEvent('chat:addMessage', {
+ multiline = true,
+ color = {86, 96, 252},
+ args = {'Judge', JailTime .. ' months until release.'},
+ })
+ end
+
+ Citizen.Wait(1000)
+
+ local Location = GetEntityCoords(Ped, true)
+ local Distance = Vdist(Config.JailLocation.Jail.x, Config.JailLocation.Jail.y, Config.JailLocation.Jail.z, Location['x'], Location['y'], Location['z'])
+ if Distance > 100 then
+ SetEntityCoords(Ped, Config.JailLocation.Jail.x, Config.JailLocation.Jail.y, Config.JailLocation.Jail.z)
+ SetEntityHeading(Ped, Config.JailLocation.Jail.h)
+ TriggerEvent('chat:addMessage', {
+ multiline = true,
+ color = {86, 96, 252},
+ args = {'Judge', 'Don\'t try escape, its impossible'},
+ })
+ end
+
+ JailTime = JailTime - 1
+ end
+
+ if EarlyRelease then
+ TriggerServerEvent('SEM_InteractionMenu:GlobalChat', {86, 96, 252}, 'Judge', GetPlayerName(PlayerId()) .. ' was released from Jail on Parole')
+ else
+ TriggerServerEvent('SEM_InteractionMenu:GlobalChat', {86, 96, 252}, 'Judge', GetPlayerName(PlayerId()) .. ' was released from Jail after ' .. OriginalJailTime .. ' months(s).')
+ end
+ SetEntityCoords(Ped, Config.JailLocation.Release.x, Config.JailLocation.Release.y, Config.JailLocation.Release.z)
+ SetEntityHeading(Ped, Config.JailLocation.Release.h)
+ CurrentlyJailed = false
+ EarlyRelease = false
+ end)
+ end
+end)
+
+RegisterNetEvent('SEM_InteractionMenu:UnjailPlayer')
+AddEventHandler('SEM_InteractionMenu:UnjailPlayer', function()
+ EarlyRelease = true
+end)
+
+
+
+--Toggle LEO Weapons
+CarbineEquipped = false
+ShotgunEquipped = false
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(50)
+
+ if Config.UnrackWeapons == 1 then
+ local Ped = PlayerPedId()
+ local CurrentWeapon = GetSelectedPedWeapon(Ped)
+
+ if CarbineEquipped then
+ SetCurrentPedWeapon(Ped, 'weapon_carbinerifle', true)
+ else
+ if tostring(CurrentWeapon) == '-2084633992' then
+ Notify('~o~You need to unrack your rifle before you can use it')
+ SetCurrentPedWeapon(Ped, 'weapon_unarmed', true)
+ end
+ end
+
+ if ShotgunEquipped then
+ SetCurrentPedWeapon(Ped, 'weapon_pumpshotgun', true)
+ else
+ if tostring(CurrentWeapon) == '487013001' then
+ Notify('~o~You need to unrack your shotgun before you can use it')
+ SetCurrentPedWeapon(Ped, 'weapon_unarmed', true)
+ end
+ end
+ end
+ end
+end)
+
+
+
+--Civilian Adverts
+RegisterNetEvent('SEM_InteractionMenu:SyncAds')
+AddEventHandler('SEM_InteractionMenu:SyncAds',function(Text, Name, Loc, File, ID)
+ Ad(Text, Name, Loc, File, ID)
+end)
+
+
+
+--Inventory
+RegisterNetEvent('SEM_InteractionMenu:InventoryResult')
+AddEventHandler('SEM_InteractionMenu:InventoryResult', function(Inventory)
+ Citizen.Wait(5000)
+
+ if Inventory == nil then
+ Inventory = 'Empty'
+ end
+
+ Notify('~b~Inventory Items: ~g~' .. Inventory)
+end)
+
+
+
+--BAC
+RegisterNetEvent('SEM_InteractionMenu:BACResult')
+AddEventHandler('SEM_InteractionMenu:BACResult', function(BACLevel)
+ Citizen.Wait(5000)
+
+ if BACLevel == nil then
+ BACLevel = 0.00
+ end
+
+ if tonumber(BACLevel) < 0.08 then
+ Notify('~b~BAC Level: ~g~' .. tostring(BACLevel))
+ else
+ Notify('~b~BAC Level: ~r~' .. tostring(BACLevel))
+ end
+end)
+
+
+
+
+--Hospital
+CurrentlyHospitalized = false
+EarlyDischarge = false
+OriginalHospitalTime = 0
+RegisterNetEvent('SEM_InteractionMenu:HospitalizePlayer')
+AddEventHandler('SEM_InteractionMenu:HospitalizePlayer', function(HospitalTime, HospitalLocation)
+ if CurrentlyHospitaled then
+ return
+ end
+ if CurrentlyJailed then
+ return
+ end
+
+ OriginalHospitalTime = HospitalTime
+
+ local Ped = PlayerPedId()
+ if DoesEntityExist(Ped) then
+ Citizen.CreateThread(function()
+ SetEntityCoords(Ped, HospitalLocation.Hospital.x, HospitalLocation.Hospital.y, HospitalLocation.Hospital.z)
+ SetEntityHeading(Ped, HospitalLocation.Hospital.h)
+ CurrentlyHospitaled = true
+
+ while HospitalTime >= 0 and not EarlyDischarge do
+ SetEntityInvincible(Ped, true)
+ if IsPedInAnyVehicle(Ped, false) then
+ ClearPedTasksImmediately(Ped)
+ end
+
+ if HospitalTime % 30 == 0 and HospitalTime ~= 0 then
+ TriggerEvent('chat:addMessage', {
+ multiline = true,
+ color = {86, 96, 252},
+ args = {'Doctor', HospitalTime .. ' months until release.'},
+ })
+ end
+
+ Citizen.Wait(1000)
+
+ local Location = GetEntityCoords(Ped, true)
+ local Distance = Vdist(HospitalLocation.Hospital.x, HospitalLocation.Hospital.y, HospitalLocation.Hospital.z, Location['x'], Location['y'], Location['z'])
+ if Distance > 30 then
+ SetEntityCoords(Ped, HospitalLocation.Hospital.x, HospitalLocation.Hospital.y, HospitalLocation.Hospital.z)
+ SetEntityHeading(Ped, HospitalLocation.Hospital.h)
+ TriggerEvent('chat:addMessage', {
+ multiline = true,
+ color = {86, 96, 252},
+ args = {'Doctor', 'You cannot discharge yourself!'},
+ })
+ end
+
+ HospitalTime = HospitalTime - 1
+ end
+
+ if EarlyDischarge then
+ TriggerServerEvent('SEM_InteractionMenu:GlobalChat', {86, 96, 252}, 'Doctor', GetPlayerName(PlayerId()) .. ' was discharged from Hospital early')
+ else
+ TriggerServerEvent('SEM_InteractionMenu:GlobalChat', {86, 96, 252}, 'Doctor', GetPlayerName(PlayerId()) .. ' was discharged from Hospital after ' .. OriginalHospitalTime .. ' months(s).')
+ end
+ SetEntityCoords(Ped, HospitalLocation.Release.x, HospitalLocation.Release.y, HospitalLocation.Release.z)
+ SetEntityHeading(Ped, HospitalLocation.Release.h)
+ CurrentlyHospitaled = false
+ EarlyDischarge = false
+ end)
+ end
+end)
+
+RegisterNetEvent('SEM_InteractionMenu:UnhospitalizePlayer')
+AddEventHandler('SEM_InteractionMenu:UnhospitalizePlayer', function()
+ EarlyDischarge = true
+end)
+
+
+
+--Station Blips
+Citizen.CreateThread(function()
+ if Config.DisplayStationBlips then
+ local function CreateBlip(x, y, z, Name, Colour, Sprite)
+ StationBlip = AddBlipForCoord(x, y, z)
+ SetBlipSprite(StationBlip, Sprite)
+ if Config.StationBlipsDispalyed == 1 then
+ SetBlipDisplay(StationBlip, 3)
+ elseif Config.StationBlipsDispalyed == 2 then
+ SetBlipDisplay(StationBlip, 5)
+ else
+ SetBlipDisplay(StationBlip, 2)
+ end
+ SetBlipScale(StationBlip, 1.0)
+ SetBlipColour(StationBlip, Colour)
+ SetBlipAsShortRange(StationBlip, true)
+
+ BeginTextCommandSetBlipName('STRING')
+ AddTextComponentString(Name)
+ EndTextCommandSetBlipName(StationBlip)
+ end
+
+ for _, Station in pairs(Config.LEOStations) do
+ CreateBlip(Station.coords.x, Station.coords.y, Station.coords.z, 'Police Station', 38, 60)
+ end
+ for _, Station in pairs(Config.FireStations) do
+ CreateBlip(Station.coords.x, Station.coords.y, Station.coords.z, 'Fire Station', 1, 60)
+ end
+ for _, Station in pairs(Config.HospitalStations) do
+ CreateBlip(Station.coords.x, Station.coords.y, Station.coords.z, 'Hospital', 2, 61)
+ end
+ end
+end)
+
+
+
+--Permissions
+LEOAce = false
+TriggerServerEvent('SEM_InteractionMenu:LEOPerms')
+RegisterNetEvent('SEM_InteractionMenu:LEOPermsResult')
+AddEventHandler('SEM_InteractionMenu:LEOPermsResult', function(Allowed)
+ if Allowed then
+ LEOAce = true
+ else
+ LEOAce = false
+ end
+end)
+
+FireAce = false
+TriggerServerEvent('SEM_InteractionMenu:FirePerms')
+RegisterNetEvent('SEM_InteractionMenu:FirePermsResult')
+AddEventHandler('SEM_InteractionMenu:FirePermsResult', function(Allowed)
+ if Allowed then
+ FireAce = true
+ else
+ FireAce = false
+ end
+end)
+
+UnjailAllowed = false
+TriggerServerEvent('SEM_InteractionMenu:UnjailPerms')
+RegisterNetEvent('SEM_InteractionMenu:UnjailPermsResult')
+AddEventHandler('SEM_InteractionMenu:UnjailPermsResult', function(Allowed)
+ if Allowed then
+ UnjailAllowed = true
+ else
+ UnjailAllowed = false
+ end
+end)
+
+UnhospitalAllowed = false
+TriggerServerEvent('SEM_InteractionMenu:UnhospitalPerms')
+RegisterNetEvent('SEM_InteractionMenu:UnhospitalPermsResult')
+AddEventHandler('SEM_InteractionMenu:UnhospitalPermsResult', function(Allowed)
+ if Allowed then
+ UnhospitalAllowed = true
+ else
+ UnhospitalAllowed = false
+ end
+end)
+
+
+
+--Emote
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(1)
+
+ if EmotePlaying then
+ if Config.EmoteHelp then
+ NotifyHelp('You are playing an Emote, ~b~Move to Cancel')
+ end
+
+ -- Spacebar W S A D
+ if (IsControlPressed(0, 22) or IsControlPressed(0, 32) or IsControlPressed(0, 33) or IsControlPressed(0, 34) or IsControlPressed(0, 35)) then
+ CancelEmote()
+ end
+ end
+ end
+end)
+
+
+
+--Commands
+Citizen.CreateThread(function()
+ if EmoteRestrict() then
+ local Index = 0
+ local Emotes = ''
+ for _, Emote in pairs(Config.EmotesList) do
+ Index = Index + 1
+ if Index == 1 then
+ Emotes = Emotes .. Emote.name
+ else
+ Emotes = Emotes .. ', ' .. Emote.name
+ end
+ end
+
+ TriggerEvent('chat:addSuggestion', '/emotes', 'List of Current Avaliable Emotes')
+ TriggerEvent('chat:addSuggestion', '/emote', 'Play Emote', {{name = 'Emote Name', help = 'Emotes: ' .. Emotes}})
+ else
+ TriggerEvent('chat:removeSuggestion', '/emotes')
+ TriggerEvent('chat:removeSuggestion', '/emote')
+ end
+
+ TriggerEvent('chat:addSuggestion', '/eng', 'Toggles Engine')
+ TriggerEvent('chat:addSuggestion', '/hood', 'Toggles Vehicle\'s Hood')
+ TriggerEvent('chat:addSuggestion', '/trunk', 'Toggles Vehicle\'s Trunk')
+ TriggerEvent('chat:addSuggestion', '/clear', 'Clears all Weapons')
+ TriggerEvent('chat:addSuggestion', '/cuff', 'Cuff Player', {{name = 'ID', help = 'Players Server ID'}})
+ TriggerEvent('chat:addSuggestion', '/drag', 'Drag Player', {{name = 'ID', help = 'Players Server ID'}})
+ TriggerEvent('chat:addSuggestion', '/dropweapon', 'Drops Weapon in Hand')
+ TriggerEvent('chat:addSuggestion', '/loadout', 'Equips LEO Weapon Loadout')
+ TriggerEvent('chat:addSuggestion', '/coords', 'Shows Current Player Coords and Heading')
+
+ if Config.Radar ~= 0 then
+ TriggerEvent('chat:addSuggestion', '/radar', 'Toggle Radar Menu')
+ end
+
+ if Config.LEOAccess == 3 or Config.FireAccess == 3 then
+ if Config.OndutyPSWDActive then
+ TriggerEvent('chat:addSuggestion', '/onduty', 'Enable LEO/Fire Menu', {{name = 'Department', help = 'LEO or Fire'}, {name = 'Password', help = 'Onduty Password'}})
+ else
+ TriggerEvent('chat:addSuggestion', '/onduty', 'Enable LEO/Fire Menu', {{name = 'Department', help = 'LEO or Fire'}})
+ end
+ else
+ TriggerEvent('chat:removeSuggestion', '/onduty')
+ end
+end)
+
+LEOOnduty = false
+FireOnduty = false
+RegisterCommand('onduty', function(source, args, rawCommand)
+ if Config.LEOAccess == 3 or Config.FireAccess == 3 then
+ if Config.OndutyPSWDActive then
+ if args[2] == Config.OndutyPSWD then
+ local Department = args[1]:lower()
+ if Department == 'leo' then
+ LEOOnduty = not LEOOnduty
+ if LEOOnduty then
+ Notify('~g~You are onduty as an LEO')
+ else
+ Notify('~o~You are no longer onduty as an LEO')
+ end
+ elseif Department == 'fire' then
+ FireOnduty = not FireOnduty
+ if FireOnduty == true then
+ Notify('~g~You are onduty as an Firefighter')
+ else
+ Notify('~o~You are no longer onduty as an Firefighter')
+ end
+ else
+ Notify('~r~Invalid Department!')
+ end
+ else
+ Notify('~r~Incorrect Password')
+ end
+ else
+ local Department = args[1]:lower()
+ if Department == 'leo' then
+ LEOOnduty = not LEOOnduty
+ if LEOOnduty then
+ Notify('~g~You are onduty as an LEO')
+ else
+ Notify('~o~You are no longer onduty as an LEO')
+ end
+ elseif Department == 'fire' then
+ FireOnduty = not FireOnduty
+ if FireOnduty == true then
+ Notify('~g~You are onduty as an Firefighter')
+ else
+ Notify('~o~You are no longer onduty as an Firefighter')
+ end
+ else
+ Notify('~r~Invalid Department!')
+ end
+ end
+ end
+end)
+
+function IsOndutyLEO()
+ return LEOOnduty
+end
+function IsOndutyFire()
+ return FireOnduty
+end
+
+RegisterCommand('cuff', function(source, args, rawCommand)
+ if LEORestrict() or FireRestrict() then
+ if args[1] ~= nil then
+ local ID = tonumber(args[1])
+ if Config.CommandDistanceChecked then
+ if GetDistance(source) < Config.CommandDistance then
+ TriggerServerEvent('SEM_InteractionMenu:CuffNear', ID)
+ else
+ Notify('~r~That player is too far away')
+ end
+ else
+ TriggerServerEvent('SEM_InteractionMenu:CuffNear', ID)
+ end
+ else
+ TriggerServerEvent('SEM_InteractionMenu:CuffNear', GetClosestPlayer())
+ end
+ else
+ Notify('~r~Insufficient Permissions')
+ end
+end)
+
+RegisterCommand('drag', function(source, args, rawCommand)
+ if LEORestrict() or FireRestrict() then
+ if args[1] ~= nil then
+ local ID = tonumber(args[1])
+ if Config.CommandDistanceChecked then
+ if GetDistance(source) < Config.CommandDistance then
+ TriggerServerEvent('SEM_InteractionMenu:DragNear', ID)
+ else
+ Notify('~r~That player is too far away')
+ end
+ else
+ TriggerServerEvent('SEM_InteractionMenu:DragNear', ID)
+ end
+ else
+ TriggerServerEvent('SEM_InteractionMenu:DragNear', GetClosestPlayer())
+ end
+ else
+ Notify('~r~Insufficient Permissions')
+ end
+end)
+
+RegisterCommand('radar', function(source, args, rawCommand)
+ if Config.Radar ~= 0 then
+ if LEORestrict() or FireRestrict() then
+ ToggleRadar()
+ else
+ Notify('~r~Insufficient Permissions')
+ end
+ end
+end)
+
+RegisterCommand('loadout', function(source, args, rawCommand)
+ if LEORestrict() then
+ if args[1] then
+ local RequestedLoadout = args[1]
+
+ for Name, Loadout in pairs(Config.LEOLoadouts) do
+ if Name:lower() == RequestedLoadout:lower() then
+ SetEntityHealth(GetPlayerPed(-1), 200)
+ RemoveAllPedWeapons(GetPlayerPed(-1), true)
+ AddArmourToPed(GetPlayerPed(-1), 100)
+
+ for _, Weapon in pairs(Loadout) do
+ GiveWeapon(Weapon.weapon)
+
+ for _, Component in pairs(Weapon.components) do
+ AddWeaponComponent(Weapon.weapon, Component)
+ end
+ end
+ return
+ end
+ end
+
+ Notify('~r~Invalid Loadout')
+ else
+ SetEntityHealth(PlayerPedId(), 200)
+ RemoveAllPedWeapons(PlayerPedId(), true)
+ AddArmourToPed(PlayerPedId(), 100)
+ GiveWeapon('weapon_nightstick')
+ GiveWeapon('weapon_flashlight')
+ GiveWeapon('weapon_fireextinguisher')
+ GiveWeapon('weapon_flare')
+ GiveWeapon('weapon_stungun')
+ GiveWeapon('weapon_combatpistol')
+ AddWeaponComponent('weapon_combatpistol', 'component_at_pi_flsh')
+ Notify('~g~Loadout Spawned')
+ end
+ else
+ Notify('~r~You aren\'t an LEO')
+ end
+end)
+
+RegisterCommand('hu', function(source, args, rawCommand)
+ local Ped = PlayerPedId()
+ if DoesEntityExist(Ped) and not HandCuffed then
+ Citizen.CreateThread(function()
+ LoadAnimation('random@mugging3')
+ if IsEntityPlayingAnim(Ped, 'random@mugging3', 'handsup_standing_base', 3) or HandCuffed then
+ ClearPedSecondaryTask(Ped)
+ SetEnableHandcuffs(Ped, false)
+ elseif not IsEntityPlayingAnim(Ped, 'random@mugging3', 'handsup_standing_base', 3) or not HandCuffed then
+ TaskPlayAnim(Ped, 'random@mugging3', 'handsup_standing_base', 8.0, -8, -1, 49, 0, 0, 0, 0)
+ SetEnableHandcuffs(Ped, true)
+ end
+ end)
+ end
+end)
+
+RegisterCommand('huk', function(source, args, rawCommand)
+ local Ped = PlayerPedId()
+ if (DoesEntityExist(Ped) and not IsEntityDead(Ped)) and not HandCuffed then
+ Citizen.CreateThread(function()
+ LoadAnimation('random@arrests')
+ if (IsEntityPlayingAnim(Ped, 'random@arrests', 'kneeling_arrest_idle', 3)) then
+ TaskPlayAnim(Ped, 'random@arrests', 'kneeling_arrest_get_up', 8.0, 1.0, -1, 128, 0, 0, 0, 0)
+ else
+ TaskPlayAnim(Ped, 'random@arrests', 'idle_2_hands_up', 8.0, 1.0, -1, 2, 0, 0, 0, 0)
+ Wait (4000)
+ TaskPlayAnim(Ped, 'random@arrests', 'kneeling_arrest_idle', 8.0, 1.0, -1, 2, 0, 0, 0, 0)
+ end
+ end)
+ end
+end)
+
+RegisterCommand('dropweapon', function(source, args, rawCommand)
+ local CurrentWeapon = GetSelectedPedWeapon(PlayerPedId())
+ SetPedDropsInventoryWeapon(PlayerPedId(), CurrentWeapon, -2.0, 0.0, 0.5, 30)
+ Notify('~r~Weapon Dropped!')
+end)
+
+RegisterCommand('clear', function(source, args, rawCommand)
+ SetEntityHealth(PlayerPedId(), 200)
+ RemoveAllPedWeapons(PlayerPedId(), true)
+ Notify('~r~All Weapons Cleared!')
+end)
+
+RegisterCommand('eng', function(source, args, rawCommand)
+ local Veh = GetVehiclePedIsIn(PlayerPedId(), false)
+ if Veh ~= nil and Veh ~= 0 and GetPedInVehicleSeat(Veh, 0) then
+ SetVehicleEngineOn(Veh, (not GetIsVehicleEngineRunning(Veh)), false, true)
+ Notify('~g~Engine Toggled!')
+ end
+end)
+
+RegisterCommand('hood', function(source, args, rawCommand)
+ local Veh = GetVehiclePedIsIn(PlayerPedId(), false)
+
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 4) > 0 then
+ SetVehicleDoorShut(Veh, 4, false)
+ else
+ SetVehicleDoorOpen(Veh, 4, false, false)
+ end
+ end
+
+ Notify('~g~Hood Toggled!')
+end)
+
+RegisterCommand('trunk', function(source, args, rawCommand)
+ local Veh = GetVehiclePedIsIn(PlayerPedId(), false)
+
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 5) > 0 then
+ SetVehicleDoorShut(Veh, 5, false)
+ else
+ SetVehicleDoorOpen(Veh, 5, false, false)
+ end
+ end
+
+ Notify('~g~Trunk Toggled!')
+end)
+
+RegisterCommand('emotes', function(source, args, rawCommand)
+ if EmoteRestrict() then
+ local Index = 0
+ local Emotes = ''
+ for _, Emote in pairs(Config.EmotesList) do
+ Index = Index + 1
+ if Index == 1 then
+ Emotes = Emotes .. Emote.name
+ else
+ Emotes = Emotes .. ', ' .. Emote.name
+ end
+ end
+
+ TriggerEvent('chat:addMessage', {
+ multiline = true,
+ color = {255, 0 ,0},
+ args = {'Emotes', '\n^r^7' .. Emotes},
+ })
+ end
+end)
+
+RegisterCommand('emote', function(source, args, rawCommand)
+ if EmoteRestrict() then
+ local SelectedEmote = args[1]
+
+ for _, Emote in pairs(Config.EmotesList) do
+ if Emote.name == SelectedEmote then
+ PlayEmote(Emote.emote, Emote.name)
+ return
+ end
+ end
+
+ TriggerEvent('chat:addMessage', {
+ multiline = true,
+ color = {255, 0, 0},
+ args = {'Emotes', 'Invalid Emote!'},
+ })
+ end
+end)
+
+RegisterCommand('coords', function(source, args, rawCommand)
+ local Coords = GetEntityCoords(PlayerPedId())
+ local Heading = GetEntityHeading(PlayerPedId())
+
+ TriggerEvent('chatMessage', 'Coords', {255, 255, 0}, '\nX: ' .. Coords.x .. '\nY: ' .. Coords.y .. '\nZ: ' .. Coords.z .. '\nHeading: ' .. Heading)
+end)
diff --git a/resources/Interaction-Menu/config.lua b/resources/Interaction-Menu/config.lua
new file mode 100644
index 000000000..0503e3400
--- /dev/null
+++ b/resources/Interaction-Menu/config.lua
@@ -0,0 +1,803 @@
+--[[
+───────────────────────────────────────────────────────────────
+
+ SEM_InteractionMenu (config.lua) - Created by Scott M
+ Current Version: v1.7.1 (Sep 2021)
+
+ Support: https://semdevelopment.com/discord
+
+───────────────────────────────────────────────────────────────
+]]
+
+
+
+Config = {}
+
+
+
+---------------------------------------------------------------
+-- --
+-- Menu Features --
+-- --
+---------------------------------------------------------------
+
+--This is how the version check will be displayed in the server console
+--Full = 0 [Default] | Simple = 1 | Disabled = 2
+Config.VersionChecker = 1
+
+--This is how you open the menu either via a command or button
+--Button = 0 [Default] | Command = 1
+Config.OpenMenu = 0
+
+--This is the button that will open the menu (If chosen at Config.OpenMenu)
+--Default = 244 [M] | To change the button check out https://docs.fivem.net/game-references/controls/
+--Controller Support for this resource is DISABLED!
+Config.MenuButton = 166
+
+--This is the command that will open the menu (If chosen at Config.OpenMenu)
+Config.Command = 'policemenu'
+
+--This is the width of the menu when open
+--Default = 80
+Config.MenuWidth = 80
+
+--This is the position of the menu when open
+--Left = 0 [Default] | Right = 1
+Config.MenuOrientation = 0
+
+--This is the title of the menu dispalyed
+--Default = The default title of the menu is 'Interaction Menu'
+--Player Name = This is the name of the player
+--Custom = This is a custom title set by you at Config.MenuTitleCustom
+--Default = 0 [Default] | Player Name = 1 | Custom = 2
+Config.MenuTitle = 0
+
+--This is the custom title you can set for the menu (If chosen at Config.MenuTitle)
+Config.MenuTitleCustom = 'Custom Menu Title'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---------------------------------------------------------------
+-- --
+-- General/Shared Features --
+-- --
+---------------------------------------------------------------
+
+--This determines if the onduty password is active, if false the password will NOT be required when doing the command
+Config.OndutyPSWDActive = false
+
+--This is the onduty password, only people with the password can access the menu if chosen at Config.LEOAccess/Config.FireAccess
+Config.OndutyPSWD = 'OndutyPSWD'
+
+--This determines if the distance between the player using the command and the person being cuffed/dragged is checked
+Config.CommandDistanceChecked = true
+
+--This determines how close you need to be to cuff/drag someone using their ID
+--Default = 50
+Config.CommandDistance = 50
+
+--This determines if the stations section of the LEO & Fire menu will be visible
+--Station Locations can be set at Config.LEOStations & Config.FireStations
+Config.ShowStations = true
+
+--This determines if the stations menu will have a teleport section, if set to false ONLY the waypoint option will be visible
+Config.AllowStationTeleport = true
+
+--This determines if the stations set in the Config.LEOStations & Config.FireStations have blips on the map
+Config.DisplayStationBlips = true
+
+--This sets where the station blips will be displayed (Mini Map / Main Map)
+--On Mini Map & Main Map = 0 [Default] | Only on Main Map = 1 | Only on Mini Map = 2
+Config.StationBlipsDispalyed = 0
+
+--These are the props avaliable via the LEO & Fire menus
+Config.Props = {
+ --[[
+ EXAMPLE:
+ {name = 'a', spawncode = 'b'},
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title that shows in the menu
+ 'b' is the spawn code for prop that will be spawned
+ ]]
+ {name = 'Police Barrier', spawncode = 'prop_barrier_work05'},
+ {name = 'Barrier', spawncode = 'prop_barrier_work06a'},
+ {name = 'Traffic Cone', spawncode = 'prop_roadcone01a'},
+ {name = 'Cone', spawncode = 'prop_roadcone02b'},
+ {name = 'Work Barrier', spawncode = 'prop_mp_barrier_02b'},
+ {name = 'Work Barrier 2', spawncode = 'prop_barrier_work01a'},
+ {name = 'Lighting', spawncode = 'prop_worklight_03b'},
+ {name = 'Tent', spawncode = 'prop_gazebo_02'},
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---------------------------------------------------------------
+-- --
+-- LEO Features --
+-- --
+---------------------------------------------------------------
+
+--This sets who can access the LEO menu
+--!!! NOTE: If LEO Peds is selected then onlys peds from the Config.LEOUniforms will have access to the menu
+--Disabled = 0 | Everyone = 1 [Default] | LEO Peds = 2 | Onduty Command = 3 | Ace Permissions = 4
+Config.LEOAccess = 1
+
+--This determines if the radar button will be displayed
+--NOTE: Wraith Radar is the ONLY radar script that works with the menu at the moment (This also includes any editied version) - Both his old and new radar are compatiable, link below
+--[[
+ Links:
+ WraithRS | Advanced Radar System: https://forum.cfx.re/t/release-wraithrs-advanced-radar-system-1-0-2/48543
+ Ascaped Plate Reader Edit: https://forum.cfx.re/t/release-edit-wraithrs-new-plate-reader/147269
+
+ Wraith ARS 2X Radar & Plate Reader: https://forum.cfx.re/t/release-wraith-ars-2x-police-radar-and-plate-reader-v1-2-4/1058277
+
+ **Other modified version of these resoruce should work**
+]]
+--Disabled = 0 [Default] | Wraith ARS 2x = 1 | WraithRS = 2
+Config.Radar = 0
+
+--This determines when someone if cuffed if they can enter or exit a vehicle
+Config.VehEnterCuffed = false
+
+--This determines if you need to unrack the carbine rifle of pumpshotun from a vehicle to obtain it
+--Disabled = 0 | Constant = 1 | Free-hand = 2 [Default]
+--Constant = Once unracked it is unable to be removed from hand until racked again in a vehicle
+--Free-hand = Once unracked it is able to be removed from hand
+Config.UnrackWeapons = 2
+
+--This sets if the Jail functions will be visible in the menu
+Config.LEOJail = true
+
+--This is the max time that someone can be jailed for (Seconds)
+Config.MaxJailTime = 300
+
+--These is the location of the jail and release point
+Config.JailLocation = {
+ Jail = {x = 1675.28, y = 2648.55, z = 45.56, h = 49.50},
+ Release = {x = 1851.24, y = 2585.77, z = 45.67, h = 271.44},
+}
+
+--This determines if the backup section of the LEO menu will be visible
+Config.DisplayBackup = true
+
+--This sets the time between the blip being created and removed (Minutes)
+--Default = 5
+Config.BackupBlipTimeout = 5
+
+--This determines if the LEO props menu will be available
+Config.DisplayProps = true
+
+--These are the station available via the station menu
+Config.LEOStations = {
+ {name = 'Sandy Shores', coords = {x = 1850.04, y = 3679.36, z = 34.26 , h = 208.84}},
+ {name = 'Paleto Bay', coords = {x = -438.51, y = 6017.93, z = 31.49 , h = 352.90}},
+
+ {name = 'Mission Row', coords = {x = 432.08, y = -985.25, z = 30.71 , h = 44.02}},
+ {name = 'Davis', coords = {x = 373.99, y = -1607.59, z = 29.29 , h = 192.15}},
+ {name = 'Vinewood', coords = {x = 638.03, y = -1.85, z = 82.78 , h = 290.18}},
+ {name = 'Vespucci', coords = {x = -1090.87, y = -807.29, z = 19.26 , h = 64.92}},
+
+ {name = 'NOOSE Headquarters', coords = {x = 2504.29, y = -384.11, z = 94.12, h = 264.01}},
+}
+
+--This determines if the LEO Unfiroms section will be visible
+Config.DisplayLEOUniforms = true
+
+--These are the LEO uniforms that are available via the loadouts - these will also be the uniforms which will give access to the LEO menu if that option if chosen at Config.LEOAccess
+Config.LEOUniforms = {
+ --[[
+ EXAMPLE:
+ {name = 'a', spawncode = 'b'},
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title that shows in the menu
+ 'b' is the spawn code for uniform that will be spawned
+ ]]
+ {name = 'LSPD', spawncode = 's_m_y_cop_01'},
+ {name = 'BCSO', spawncode = 's_m_y_sheriff_01'},
+ {name = 'SAHP', spawncode = 's_m_y_hwaycop_01'},
+ {name = 'SWAT', spawncode = 's_m_y_swat_01'},
+ {name = 'Undercover', spawncode = 's_m_m_ciasec_01'},
+}
+
+--This determines if the LEO Loadouts section will be visible
+Config.DisplayLEOLoadouts = true
+
+--These are the weapon loadouts available via the loadouts
+Config.LEOLoadouts = {
+ --[[
+ EXAMPLE:
+ a = {
+ {weapon = 'b', components = 'c', 'c'},
+ },
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title of the Loadout
+ 'b' is the weapon which you want to be added [A link to weapon names can be found below]
+ 'c' is the components which you want to be added to the weapon [A link to available weapon components can be found below]
+
+ Weapon Names https://forum.fivem.net/t/list-of-weapon-spawn-names-after-hours/90750
+ Weapon Components https://wiki.rage.mp/index.php?title=Weapons_Components
+ ]]
+ ['Standard'] = {
+ {weapon = 'weapon_flashlight', components = {''}},
+ {weapon = 'weapon_combatpistol', components = {'component_at_pi_flsh'}},
+ {weapon = 'weapon_stungun', components = {''}},
+ {weapon = 'weapon_carbinerifle', components = {'component_at_ar_flsh', 'component_at_scope_medium', 'component_at_ar_afgrip'}},
+ {weapon = 'weapon_pumpshotgun', components = {'component_at_ar_flsh'}},
+ {weapon = 'weapon_fireextinguisher', components = {''}},
+ {weapon = 'weapon_flare', components = {''}},
+ },
+
+ ['SWAT'] = {
+ {weapon = 'weapon_flashlight', components = {''}},
+ {weapon = 'weapon_combatpistol', components = {'component_at_pi_flsh'}},
+ {weapon = 'weapon_stungun', components = {''}},
+ {weapon = 'weapon_smg', components = {'component_at_ar_flsh', 'component_ar_scope_macro_02'}},
+ {weapon = 'weapon_carbinerifle', components = {'component_at_ar_flsh', 'component_at_scope_medium', 'component_at_ar_afgrip'}},
+ {weapon = 'weapon_pumpshotgun', components = {'component_at_ar_flsh'}},
+ {weapon = 'weapon_sniperrifle', components = {'comonent_at_scope_max'}},
+ {weapon = 'weapon_bzgas', components = {''}},
+ {weapon = 'weapon_fireextinguisher', components = {''}},
+ {weapon = 'weapon_flare', components = {''}},
+ }
+}
+
+--This determines if the LEO vehicles section if available
+Config.ShowLEOVehicles = true
+
+--This determines if the vehicle spawn codes are displayed next to the name
+Config.ShowLEOSpawnCode = true
+
+--These are the LEO vehicles which are avaiable via the menu
+Config.LEOVehiclesCategories = {
+ --[[
+ EXAMPLE:
+ ['a'] = {
+ {name = 'b', spawncode = 'c', livery = d, extras = {e, e}},
+ }
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title of the Category
+ 'b' is the title of the vehicle that shows in the menu
+ 'c' is the spawn code for vehicle that will be spawned
+ d is the number of the livery which you want it to spawn with
+ e is the number(s) of extra(s) which you want it to spawn with
+
+ **NOTE: Sometimes the sections do NOT display if the order in the config below**
+ ]]
+
+ ['lapd'] = {
+ {name = 'LAPD', spawncode = 'lapd09charger1'},
+ {name = 'LAPD', spawncode = 'lapd09charger2'},
+ {name = 'LAPD', spawncode = 'lapd09dual'},
+ {name = 'LAPD', spawncode = 'lapd14charger'},
+ {name = 'LAPD', spawncode = 'lapd14chargertd'},
+ {name = 'LAPD', spawncode = 'metrocharger'},
+ {name = 'LAPD', spawncode = 'lapdcvpialpr'},
+ {name = 'LAPD', spawncode = 'lapdcvpithermal'},
+ {name = 'LAPD', spawncode = '2005LAPDCVPI_Hybrid1'},
+ {name = 'LAPD', spawncode = '2005LAPDCVPI_Hybrid2'},
+ {name = 'LAPD', spawncode = '2005LAPDCVPI_HybridH'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_77th1'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_77th2'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_Hardtop1'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_Hardtop2'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_Hybrid1'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_Hybrid2'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_HybridT'},
+ {name = 'LAPD', spawncode = '2008LAPDCVPI_Traffic1'},
+ {name = 'LAPD', spawncode = '08hybridcvpi1'},
+ {name = 'LAPD', spawncode = '08hybridcvpi2'},
+ {name = 'LAPD', spawncode = '08lapdcvpi1'},
+ {name = 'LAPD', spawncode = '08lapdcvpi2'},
+ {name = 'LAPD', spawncode = '08lapdcvpighost'},
+ {name = 'LAPD', spawncode = '08lapdcvpiharbor'},
+ {name = 'LAPD', spawncode = '08lapdcvpisecsd'},
+ {name = 'LAPD', spawncode = '08lapdsap'},
+ {name = 'LAPD', spawncode = 'lapdtahoe08'},
+ {name = 'LAPD', spawncode = 'lapd16fpiu'},
+ {name = 'LAPD', spawncode = 'lapd16fpiu2'},
+ {name = 'LAPD', spawncode = 'lapd16fpiusecsd'},
+ {name = 'LAPD', spawncode = 'lapd16fpiutraffic'},
+ {name = 'LAPD', spawncode = 'lapd16fpiutraffic2'},
+ {name = 'LAPD', spawncode = 'metro16fpiu'},
+ {name = 'LAPD', spawncode = 'metro16fpiu2'},
+ {name = 'LAPD', spawncode = 'metro16fpiu3'},
+ {name = 'LAPD', spawncode = 'lapdfpis'},
+ {name = 'LAPD', spawncode = 'lapdfpis2'},
+ {name = 'LAPD', spawncode = 'lapdfpis3'},
+ {name = 'LAPD', spawncode = 'LAPD12000'},
+ {name = 'LAPD', spawncode = '17colorado'},
+ {name = 'LAPD', spawncode = 'hybrid20fpiu'},
+ {name = 'LAPD', spawncode = 'lapd20fpiu'},
+ {name = 'LAPD', spawncode = 'lapd20fpiu2'},
+ {name = 'LAPD', spawncode = 'lapd20fpiutraffic'},
+ {name = 'LAPD', spawncode = 'lapd20fpiutraffic2'},
+ {name = 'LAPD', spawncode = 'SHOP00000'},
+ {name = 'LAPD', spawncode = 'SHOP80693'},
+ {name = 'LAPD', spawncode = 'SHOP81622'},
+ {name = 'LAPD', spawncode = 'SHOP83039'},
+ {name = 'LAPD', spawncode = 'SHOP89471'},
+ {name = 'LAPD', spawncode = 'SHOPFPIS'},
+ {name = 'LAPD', spawncode = 'BombSquadF450'},
+ {name = 'LAPD', spawncode = 'lapdurango'},
+ },
+
+ ['LASD'] = {
+ {name = 'LASD', spawncode = 'lasd1990caprice'},
+ {name = 'LASD', spawncode = 'lasd1991caprice'},
+ {name = 'LASD', spawncode = 'lasd1992'},
+ {name = 'LASD', spawncode = 'lasd1993caprice'},
+ {name = 'LASD', spawncode = 'lasd1993capricemerger'},
+ {name = 'LASD', spawncode = 'lasd1993capriceparamount'},
+ {name = 'LASD', spawncode = 'lasd1996caprice'},
+ {name = 'LASD', spawncode = 'lasd1996capriceamber'},
+ {name = 'LASD', spawncode = 'lasd1996capricefull'},
+ {name = 'LASD', spawncode = 'LASDSEDANSford'},
+ {name = 'LASD', spawncode = 'lasdtaurus'},
+ {name = 'LASD', spawncode = 'lacpvic'},
+ {name = 'LASD', spawncode = 'lasdcrownvic'},
+ {name = 'LASD', spawncode = 'lasdlithium'},
+ {name = 'LASD', spawncode = 'lasdparamount'},
+ {name = 'LASD', spawncode = 'lasdsd7100'},
+ {name = 'LASD', spawncode = 'lasd05cvpi'},
+ {name = 'LASD', spawncode = 'lasd06cvpi'},
+ {name = 'LASD', spawncode = 'lasd06cvpicarson'},
+ {name = 'LASD', spawncode = 'lasd06cvpisd7100'},
+ {name = 'LASD', spawncode = 'lasd08cvpi'},
+ {name = 'LASD', spawncode = 'lasd08cvpilomita'},
+ {name = 'LASD', spawncode = 'lasd08cvpitransit'},
+ {name = 'LASD', spawncode = 'lasd1997'},
+ {name = 'LASD', spawncode = 'lasd1997amber'},
+ {name = 'LASD', spawncode = 'lasd1997paramount'},
+ {name = 'LASD', spawncode = 'lasd1998'},
+ {name = 'LASD', spawncode = 'lasd1998compton'},
+ {name = 'LASD', spawncode = 'lasd1998hg'},
+ {name = 'LASD', spawncode = 'lasd1998hub'},
+ {name = 'LASD', spawncode = 'lasd1998temple'},
+ {name = 'LASD', spawncode = 'lasd1999'},
+ {name = 'LASD', spawncode = 'lasd2000'},
+ {name = 'LASD', spawncode = 'lasd2000stage'},
+ {name = 'LASD', spawncode = 'lasd2000two'},
+ {name = 'LASD', spawncode = 'lasd2001'},
+ {name = 'LASD', spawncode = 'lasd2002'},
+ {name = 'LASD', spawncode = 'lasd2003'},
+ {name = 'LASD', spawncode = 'LASDSEDANSdodge'},
+ {name = 'LASD', spawncode = 'lasdchrg14'},
+ {name = 'LASD', spawncode = 'lasd18chrg'},
+ {name = 'LASD', spawncode = 'LASDSEDANSdodge'},
+ {name = 'LASD', spawncode = 'fordexplorerLASDS'},
+ {name = 'LASD', spawncode = 'lasd13fasap'},
+ {name = 'LASD', spawncode = 'lasd13fpiu1'},
+ {name = 'LASD', spawncode = 'lasd13fpiu2'},
+ {name = 'LASD', spawncode = 'lasd13fpiu3'},
+ {name = 'LASD', spawncode = 'lasd13fpiu4'},
+ {name = 'LASD', spawncode = 'lasd13fpiu5'},
+ {name = 'LASD', spawncode = 'lasd13fsfpiu'},
+ {name = 'LASD', spawncode = 'lasd13funm'},
+ {name = 'LASD', spawncode = 'lasd13funm2'},
+ {name = 'LASD', spawncode = 'lasd13funm3'},
+ {name = 'LASD', spawncode = 'lasd13funm4'},
+ {name = 'LASD', spawncode = 'lasd16asap'},
+ {name = 'LASD', spawncode = 'lasd16asapb'},
+ {name = 'LASD', spawncode = 'lasd16asapc'},
+ {name = 'LASD', spawncode = 'lasd16asapd'},
+ {name = 'LASD', spawncode = 'lasd16fpiu'},
+ {name = 'LASD', spawncode = 'lasd16fpiub'},
+ {name = 'LASD', spawncode = 'lasd16rfr'},
+ {name = 'LASD', spawncode = 'lasd16rfrb'},
+ {name = 'LASD', spawncode = 'chevytahoe'},
+ {name = 'LASD', spawncode = 'lasd08tahoe'},
+ {name = 'LASD', spawncode = 'lasd08tahoe2'},
+ {name = 'LASD', spawncode = 'lasd14tahoe'},
+ {name = 'LASD', spawncode = 'lasd15tahoe'},
+ {name = 'LASD', spawncode = 'lasd15tahoeb'},
+ {name = 'LASD', spawncode = 'lasd15k9'},
+ {name = 'LASD', spawncode = 'lasd15k9b'},
+ {name = 'LASD', spawncode = 'lasd20tahoe'},
+ {name = 'LASD', spawncode = 'lasd20tfs'},
+ {name = 'LASD', spawncode = 'lasd20k9'},
+ {name = 'LASD', spawncode = 'lasd20lp'},
+ {name = 'LASD', spawncode = 'lasd23tahoe'},
+ {name = 'LASD', spawncode = 'lasd23k9t'},
+ {name = 'LASD', spawncode = 'lasd22tunm'},
+ {name = 'LASD', spawncode = '19Yukonrb'},
+ {name = 'LASD', spawncode = '19yukonslick'},
+ {name = 'LASD', spawncode = 'lasd1999tahoe'},
+ {name = 'LASD', spawncode = 'chevytahoe'},
+ {name = 'LASD', spawncode = 'LASDTRUCKS'},
+ {name = 'LASD', spawncode = '23gmcleo'},
+ {name = 'LASD', spawncode = '23gmcleoslick'},
+ {name = 'LASD', spawncode = '21f150'},
+ {name = 'LASD', spawncode = '22f150rb'},
+ {name = 'LASD', spawncode = '23f150pr'},
+ {name = 'LASD', spawncode = '24tactrd'},
+ {name = 'LASD', spawncode = 'f350bubba'},
+ {name = 'LASD', spawncode = '14F250'},
+ {name = 'LASD', spawncode = '23f150st'},
+ {name = 'LASD', spawncode = 'Commandcenter'},
+ {name = 'LASD', spawncode = 'commandtruck'},
+ {name = 'LASD', spawncode = 'LASDTRUCKS'},
+ {name = 'LASD', spawncode = 'LASDUNMARK'},
+ {name = 'LASD', spawncode = 'lasdurango'},
+ {name = 'LASD', spawncode = 'sebcharger'},
+ {name = 'LASD', spawncode = 'sebexp'},
+ {name = 'LASD', spawncode = 'sebexp2'},
+ {name = 'LASD', spawncode = 'sebtahoe2'},
+ {name = 'LASD', spawncode = 'lasdcharger'},
+ {name = 'LASD', spawncode = 'lasdfpiu'},
+ {name = 'LASD', spawncode = 'lasdsap'},
+ {name = 'LASD', spawncode = 'lasdtaurus'},
+ {name = 'LASD', spawncode = 'sebtahoe'},
+ {name = 'LASD', spawncode = '24tactrd'},
+ {name = 'LASD', spawncode = 'LASDUNMARK'},
+ },
+
+ ['CHP'] = {
+ {name = 'CHP', spawncode = 'chp23charg'},
+ {name = 'CHP', spawncode = 'chp23charggr'},
+ {name = 'CHP', spawncode = 'chp20fpiup'},
+ {name = 'CHP', spawncode = 'chp21tahoe'},
+ {name = 'CHP', spawncode = 'chp211tahoe'},
+ {name = 'CHP', spawncode = 'chpfpiuk9'},
+ {name = 'CHP', spawncode = 'chp23durango'},
+ {name = 'CHP', spawncode = 'chp23durangop'},
+ {name = 'CHP', spawncode = 'chp18charger'},
+ {name = 'CHP', spawncode = 'chp1200rt'},
+ {name = 'CHP', spawncode = 'chp13harley'},
+ {name = 'CHP', spawncode = 'chp20fpiuS'},
+ {name = 'CHP', spawncode = 'chp20tahoe'},
+ {name = 'CHP', spawncode = 'chp16fpiu'},
+ {name = 'CHP', spawncode = 'chp181charger'},
+ {name = 'CHP', spawncode = 'chp11cvpiS'},
+ {name = 'CHP', spawncode = 'chp11cvpi'},
+ {name = 'CHP', spawncode = 'chp18chargerS'},
+ {name = 'CHP', spawncode = 'chp13fpiuS'},
+ {name = 'CHP', spawncode = 'chp13fpiu'},
+ {name = 'CHP', spawncode = 'chp18chargerS'},
+ {name = 'CHP', spawncode = 'chp20tahoeS'},
+ {name = 'CHP', spawncode = 'chp20tahoe'},
+ },
+}
+
+--This determines if the ai traffic manager will can accessible
+Config.DisplayTrafficManager = true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---------------------------------------------------------------
+-- --
+-- Fire Features --
+-- --
+---------------------------------------------------------------
+
+--This sets who can access the Fire menu
+--!!! NOTE: If Fire Peds is selected then onlys peds from the Config.FireUniforms will have access to the menu
+--Disabled = 0 | Everyone = 1 [Default] | Fire Peds = 2 | Onduty Command = 3 | Ace Permissions = 4
+Config.FireAccess = 1
+
+--This sets if the Hospitalize functions will be visible in the menu
+Config.FireHospital = true
+
+--This is the max time that someone can be hospitalized for (Seconds)
+Config.MaxHospitalTime = 300
+
+--These is the location of the hospital and release point
+--I would recommend using a MLO Interior/Ymap for the hospital
+Config.HospitalLocation = {
+ ['Pillbox Hill'] = {
+ Hospital = {x = 358.34, y = -589.98, z = 28.79, h = 257.19},
+ Release = {x = 372.78, y = -595.04, z = 28.84, h = 248.29},
+ },
+ ['Paleto Bay'] = {
+ Hospital = {x = -247.34, y = 6332.39, z = 32.42 , h = 226.90},
+ Release = {x = -247.34, y = 6332.39, z = 32.42 , h = 226.90},
+ }
+}
+
+--These are the station available via the station menu
+Config.FireStations = {
+ {name = 'Sandy Shores', coords = {x = 1693.57, y = 3582.68, z = 35.62 , h = 227.29}},
+ {name = 'Paleto Bay', coords = {x = -382.50, y = 6116.76, z = 31.47 , h = 7.29}},
+
+ {name = 'Davis', coords = {x = 201.16, y = -1631.67, z = 29.75, h = 296.67}},
+ {name = 'Rockford Hill', coords = {x = -636.47, y = -117.02, z = 38.02, h = 79.64}},
+ {name = 'El Burro Heights', coords = {x = 1191.83, y = -1461.74, z = 34.88, h = 329.54}},
+}
+
+--These are the locations of hospitals avaiable via the hospital menu
+Config.HospitalStations = {
+ {name = 'Sandy Shores', coords = {x = 1839.13, y = 3673.26, z = 34.27 , h = 210.83}},
+ {name = 'Paleto Bay', coords = {x = -247.34, y = 6332.39, z = 32.42 , h = 226.90}},
+
+ {name = 'Pillbox', coords = {x = 357.19, y = -593.46, z = 28.78, h = 260.70}},
+ {name = 'Davis', coords = {x = 294.59, y = -1448.17, z = 29.96, h = 320.92}},
+}
+
+--This determines if the LEO Unfiroms section will be visible
+Config.DisplayFireUniforms = true
+
+--These are the Fire uniforms that are available via the loadouts - these will also be the uniforms which will give access to the Fire menu if that option if chosen at Config.FireAccess
+Config.FireUniforms = {
+ --[[
+ EXAMPLE:
+ {name = 'a', spawncode = 'b'},
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title that shows in the menu
+ 'b' is the spawn code for uniform that will be spawned
+ ]]
+ {name = 'Firefighter', spawncode = 's_m_y_fireman_01'},
+ {name = 'Paramedic', spawncode = 's_m_m_paramedic_01'},
+}
+
+--This determines if the LEO Loadouts section will be visible
+Config.DisplayFireLoadouts = true
+
+--This determines if the Fire vehicles section if available
+Config.ShowFireVehicles = true
+
+--This determines if the vehicle spawn codes are displayed next to the name
+Config.ShowFireSpawnCode = true
+
+--These are the Fire vehicles which are avaiable via the menu
+Config.FireVehicles = {
+ --[[
+ EXAMPLE:
+ {name = 'a', spawncode = 'b', livery = c, extras = {d, d}},
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title of the vehicle that shows in the menu
+ 'b' is the spawn code for vehicle that will be spawned
+ 'c' is the number of the livery which you want it to spawn with
+ 'd' is the number(s) of extra(s) which you want it to spawn with
+
+ **NOTE: Sometimes the sections do NOT display if the order in the config below**
+ ]]
+
+ --These are the Vehicles that will show in the Category and there spawn codes
+ {name = 'Ambulance', spawncode = 'rambulance'},
+ {name = 'Ambulance', spawncode = 'ALSrescue1'},
+ {name = 'Ambulance', spawncode = 'f250ambo'},
+ {name = 'Ambulance', spawncode = 'CAMBO'},
+ {name = 'Ambulance', spawncode = 'medic22'},
+ {name = 'Ambulance', spawncode = 'amrvan'},
+ {name = 'Ambulance', spawncode = '24ramambo'},
+ {name = 'Ambulance', spawncode = 'e350vanb'},
+ {name = 'Ambulance', spawncode = 'e450ambo'},
+ {name = 'Ambulance', spawncode = '20ramambo'},
+ {name = 'Fire Engine', spawncode = 'firetruk'},
+ {name = 'Ambulance', spawncode = 'ambulance'},
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---------------------------------------------------------------
+-- --
+-- Civilian Features --
+-- --
+---------------------------------------------------------------
+
+--This sets who can access the Civlian menu
+--!!! NOTE: If Fire Peds is selected then onlys peds from the Config.FireUniforms will have access to the menu
+--Disabled = 0 | Everyone = 1 [Default]
+Config.CivAccess = 1
+
+--This determines if the Civilian vehicles section if available
+Config.ShowCivVehicles = true
+
+--This determines if the vehicle spawn codes are displayed next to the name
+Config.ShowCivSpawnCode = true
+
+--These are the Civilian vehicles which are avaiable via the menu
+Config.CivVehicles = {
+ --[[
+ EXAMPLE:
+ {name = 'a', spawncode = 'b'},
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title of the vehicle that shows in the menu
+ 'b' is the spawn code for vehicle that will be spawned
+ ]]
+
+ --These are the Vehicles that will show in the Category and there spawn codes
+ {name = 'Adder', spawncode = 'adder'},
+ {name = 'Baller', spawncode = 'baller'},
+}
+
+--This determines if the civilian adverts sections of the menu if visible
+--NOTE: When someone sends an advert it will display the company name and then "Advertisement ##", this is the Server ID of the person that sent the advert
+Config.ShowCivAdverts = true
+
+--These are the adverts that are avaiable via the ads menu
+--NOTE: You can add additional adverts from https://wiki.gtanet.work/index.php?title=Notification_Pictures
+Config.CivAdverts = {
+ --[[
+ EXAMPLE:
+ {name = 'a', loc = 'b', file = 'c'},
+ ────────────────────────────────────────────────────────────────
+ 'a' is the title of the Adverts
+ 'b' is the location for the Advert's Image
+ 'c' is the file name for the Advert's Image
+ ]]
+
+
+
+ -- !!!!! Wouldn't Recommend Changing These Unless You Know What You're Doing !!!!!
+ -- !!!!! Wouldn't Recommend Changing These Unless You Know What You're Doing !!!!!
+ -- !!!!! Wouldn't Recommend Changing These Unless You Know What You're Doing !!!!!
+
+ {name = '24/7', loc = 'CHAR_FLOYD', file = '247'},
+ {name = 'Ammunation', loc = 'CHAR_AMMUNATION', file = 'CHAR_AMMUNATION'},
+ {name = 'Bugstars', loc = 'CHAR_BUGSTARS', file = 'CHAR_BUGSTARS'},
+ {name = 'Cluckin\' Bell', loc = 'CHAR_FLOYD', file = 'BELL'},
+ {name = 'Downtown Cab Co.', loc = 'CHAR_TAXI', file = 'CHAR_TAXI'},
+ {name = 'Dynasty 8', loc = 'CHAR_FLOYD', file = 'D8'},
+ {name = 'Fleeca Bank', loc = 'CHAR_BANK_FLEECA', file = 'CHAR_BANK_FLEECA'},
+ {name = 'Gruppe6', loc = 'CHAR_FLOYD', file = 'GRUPPE6'},
+ {name = 'Merry Weather', loc = 'CHAR_MP_MERRYWEATHER', file = 'CHAR_MP_MERRYWEATHER'},
+ {name = 'Limited Gasoline', loc = 'CHAR_FLOYD', file = 'LTD'},
+ {name = 'Liquor Ace', loc = 'CHAR_FLOYD', file = 'ACE'},
+ {name = 'Smoke on the Water', loc = 'CHAR_FLOYD', file = 'SOTW'},
+ {name = 'Pegasus', loc = 'CHAR_PEGASUS_DELIVERY', file = 'CHAR_PEGASUS_DELIVERY'},
+ {name = 'Los Santos Customs', loc = 'CHAR_LS_CUSTOMS', file = 'CHAR_LS_CUSTOMS'},
+ {name = 'Los Santos Traffic Info', loc = 'CHAR_LS_TOURIST_BOARD', file = 'CHAR_LS_TOURIST_BOARD'},
+ {name = 'Los Santos Water and Power', loc = 'CHAR_FLOYD', file = 'LSWP'},
+ {name = 'Mors Mutual Insurance', loc = 'CHAR_MP_MORS_MUTUAL', file = 'CHAR_MP_MORS_MUTUAL'},
+ {name = 'PostOP', loc = 'CHAR_FLOYD', file = 'OP'},
+ {name = 'Vanilla Unicorn', loc = 'CHAR_MP_STRIPCLUB_PR', file = 'CHAR_MP_STRIPCLUB_PR'},
+ {name = 'Weazel News', loc = 'CHAR_FLOYD', file = 'NEWS'},
+ {name = 'Facebook', loc = 'CHAR_FACEBOOK', file = 'CHAR_FACEBOOK'},
+ {name = 'Life Invader', loc = 'CHAR_LIFEINVADER', file = 'CHAR_LIFEINVADER'},
+ {name = 'YouTube', loc = 'CHAR_YOUTUBE', file = 'CHAR_YOUTUBE'},
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---------------------------------------------------------------
+-- --
+-- Vehicle Features --
+-- --
+---------------------------------------------------------------
+
+--This sets when players can access the vehicle menu
+--Disabled = 0 | All the Time = 1 [Default] | When in Vehicle = 2
+Config.VehicleAccess = 1
+
+--This determines if the vehicle options are avaiable, these include: Fix, Clean, Delete
+Config.VehicleOptions = true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+---------------------------------------------------------------
+-- --
+-- Emote Features --
+-- --
+---------------------------------------------------------------
+
+--This sets which players can access the emote menu
+--Disabled = 0 | Everyone = 1 [Default]
+Config.EmoteAccess = 1
+
+--This sets if a help message is displayed when playing an emote
+Config.EmoteHelp = true
+
+--These are the emotes avaiable via the menu and the '/emotes' & '/emote [Emote]' commands
+Config.EmotesList = {
+ {name = 'binoculars', emote = 'WORLD_HUMAN_BINOCULARS'},
+ {name = 'camera', emote = 'WORLD_HUMAN_PAPARAZZI'},
+ {name = 'clean', emote = 'WORLD_HUMAN_MAID_CLEAN'},
+ {name = 'clipboard', emote = 'WORLD_HUMAN_CLIPBOARD'},
+ {name = 'coffee', emote = 'WORLD_HUMAN_AA_COFFEE'},
+ {name = 'cheer', emote = 'WORLD_HUMAN_CHEERING'},
+ {name = 'cop', emote = 'WORLD_HUMAN_COP_IDLES'},
+ {name = 'film', emote = 'WORLD_HUMAN_MOBILE_FILM_SHOCKING'},
+ {name = 'fish', emote = 'WORLD_HUMAN_STAND_FISHING'},
+ {name = 'flex', emote = 'WORLD_HUMAN_MUSCLE_FLEX'},
+ {name = 'guard', emote = 'WORLD_HUMAN_GUARD_STAND'},
+ {name = 'hammer', emote = 'WORLD_HUMAN_HAMMERING'},
+ {name = 'homeless', emote = 'WORLD_HUMAN_BUM_FREEWAY'},
+ {name = 'impatient', emote = 'WORLD_HUMAN_STAND_IMPATIENT'},
+ {name = 'jog', emote = 'WORLD_HUMAN_JOG_STANDING'},
+ {name = 'kneel', emote = 'CODE_HUMAN_MEDIC_KNEEL'},
+ {name = 'lean', emote = 'WORLD_HUMAN_LEANING'},
+ {name = 'mechanic', emote = 'WORLD_HUMAN_VEHICLE_MECHANIC'},
+ {name = 'medic', emote = 'CODE_HUMAN_MEDIC_TEND_TO_DEAD'},
+ {name = 'music', emote = 'WORLD_HUMAN_MUSICIAN'},
+ {name = 'notepad', emote = 'CODE_HUMAN_MEDIC_TIME_OF_DEATH'},
+ {name = 'party', emote = 'WORLD_HUMAN_PARTYING'},
+ {name = 'phone', emote = 'WORLD_HUMAN_STAND_MOBILE'},
+ {name = 'phonecall', emote = 'WORLD_HUMAN_STAND_MOBILE_UPRIGHT'},
+ {name = 'selfie', emote = 'WORLD_HUMAN_TOURIST_MOBILE'},
+ {name = 'sit', emote = 'WORLD_HUMAN_PICNIC'},
+ {name = 'sleep', emote = 'WORLD_HUMAN_BUM_SLUMPED'},
+ {name = 'smoke', emote = 'WORLD_HUMAN_SMOKING'},
+ {name = 'statue', emote = 'WORLD_HUMAN_HUMAN_STATUE'},
+ {name = 'stupor', emote = 'WORLD_HUMAN_STUPOR'},
+ {name = 'sunbathe', emote = 'WORLD_HUMAN_SUNBATHE'},
+ {name = 'sunbathe2', emote = 'WORLD_HUMAN_SUNBATHE_BACK'},
+ {name = 'traffic', emote = 'WORLD_HUMAN_CAR_PARK_ATTENDANT'},
+ {name = 'weed', emote = 'WORLD_HUMAN_SMOKING_POT'},
+ {name = 'weights', emote = 'WORLD_HUMAN_MUSCLE_FREE_WEIGHTS'},
+ {name = 'weld', emote = 'WORLD_HUMAN_WELDING'},
+ {name = 'yoga', emote = 'WORLD_HUMAN_YOGA'},
+}
diff --git a/resources/Interaction-Menu/dependencies/NativeUI.lua b/resources/Interaction-Menu/dependencies/NativeUI.lua
new file mode 100644
index 000000000..f574b20bd
--- /dev/null
+++ b/resources/Interaction-Menu/dependencies/NativeUI.lua
@@ -0,0 +1,3884 @@
+UIResRectangle = setmetatable({}, UIResRectangle)
+UIResRectangle.__index = UIResRectangle
+UIResRectangle.__call = function() return "Rectangle" end
+
+UIResText = setmetatable({}, UIResText)
+UIResText.__index = UIResText
+UIResText.__call = function() return "Text" end
+
+Sprite = setmetatable({}, Sprite)
+Sprite.__index = Sprite
+Sprite.__call = function() return "Sprite" end
+
+UIMenuItem = setmetatable({}, UIMenuItem)
+UIMenuItem.__index = UIMenuItem
+UIMenuItem.__call = function() return "UIMenuItem", "UIMenuItem" end
+
+UIMenuCheckboxItem = setmetatable({}, UIMenuCheckboxItem)
+UIMenuCheckboxItem.__index = UIMenuCheckboxItem
+UIMenuCheckboxItem.__call = function() return "UIMenuItem", "UIMenuCheckboxItem" end
+
+UIMenuListItem = setmetatable({}, UIMenuListItem)
+UIMenuListItem.__index = UIMenuListItem
+UIMenuListItem.__call = function() return "UIMenuItem", "UIMenuListItem" end
+
+UIMenuSliderItem = setmetatable({}, UIMenuSliderItem)
+UIMenuSliderItem.__index = UIMenuSliderItem
+UIMenuSliderItem.__call = function() return "UIMenuItem", "UIMenuSliderItem" end
+
+UIMenuColouredItem = setmetatable({}, UIMenuColouredItem)
+UIMenuColouredItem.__index = UIMenuColouredItem
+UIMenuColouredItem.__call = function() return "UIMenuItem", "UIMenuColouredItem" end
+
+UIMenuProgressItem = setmetatable({}, UIMenuProgressItem)
+UIMenuProgressItem.__index = UIMenuProgressItem
+UIMenuProgressItem.__call = function() return "UIMenuItem", "UIMenuProgressItem" end
+
+UIMenuHeritageWindow = setmetatable({}, UIMenuHeritageWindow)
+UIMenuHeritageWindow.__index = UIMenuHeritageWindow
+UIMenuHeritageWindow.__call = function() return "UIMenuWindow", "UIMenuHeritageWindow" end
+
+UIMenuGridPanel = setmetatable({}, UIMenuGridPanel)
+UIMenuGridPanel.__index = UIMenuGridPanel
+UIMenuGridPanel.__call = function() return "UIMenuPanel", "UIMenuGridPanel" end
+
+UIMenuColourPanel = setmetatable({}, UIMenuColourPanel)
+UIMenuColourPanel.__index = UIMenuColourPanel
+UIMenuColourPanel.__call = function() return "UIMenuPanel", "UIMenuColourPanel" end
+
+UIMenuPercentagePanel = setmetatable({}, UIMenuPercentagePanel)
+UIMenuPercentagePanel.__index = UIMenuPercentagePanel
+UIMenuPercentagePanel.__call = function() return "UIMenuPanel", "UIMenuPercentagePanel" end
+
+UIMenu = setmetatable({}, UIMenu)
+UIMenu.__index = UIMenu
+UIMenu.__call = function() return "UIMenu" end
+
+MenuPool = setmetatable({}, MenuPool)
+MenuPool.__index = MenuPool
+
+NativeUI = {}
+
+CharacterMap = { [' '] = 6, ['!'] = 6, ['"'] = 6, ['#'] = 11,['$'] = 10, ['%'] = 17,['&'] = 13, ['\\'] = 4,['('] = 6, [')'] = 6,['*'] = 7, ['+'] = 10, [','] = 4, ['-'] = 6, ['.'] = 4, ['/'] = 7, ['0'] = 12, ['1'] = 7, ['2'] = 11, ['3'] = 11, ['4'] = 11, ['5'] = 11, ['6'] = 12, ['7'] = 10, ['8'] = 11, ['9'] = 11, [':'] = 5, [';'] = 4, ['<'] = 9, ['='] = 9, ['>'] = 9, ['?'] = 10, ['@'] = 15, ['A'] = 12, ['B'] = 13, ['C'] = 14, ['D'] = 14, ['E'] = 12, ['F'] = 12, ['G'] = 15, ['H'] = 14, ['I'] = 5, ['J'] = 11, ['K'] = 13, ['L'] = 11, ['M'] = 16, ['N'] = 14, ['O'] = 16, ['P'] = 12, ['Q'] = 15, ['R'] = 13, ['S'] = 12, ['T'] = 11, ['U'] = 13, ['V'] = 12, ['W'] = 18, ['X'] = 11, ['Y'] = 11, ['Z'] = 12, ['['] = 6, [']'] = 6, ['^'] = 9, ['_'] = 18, ['`'] = 8, ['a'] = 11, ['b'] = 12, ['c'] = 11, ['d'] = 12, ['e'] = 12, ['f'] = 5, ['g'] = 13, ['h'] = 11, ['i'] = 4, ['j'] = 4, ['k'] = 10, ['l'] = 4, ['m'] = 18, ['n'] = 11, ['o'] = 12, ['p'] = 12, ['q'] = 12, ['r'] = 7, ['s'] = 9, ['t'] = 5, ['u'] = 11, ['v'] = 10, ['w'] = 14, ['x'] = 9, ['y'] = 10, ['z'] = 9, ['{'] = 6, ['|'] = 3, ['}'] = 6 }
+
+BadgeStyle = { None = 0, BronzeMedal = 1, GoldMedal = 2, SilverMedal = 3, Alert = 4, Crown = 5, Ammo = 6, Armour = 7, Barber = 8, Clothes = 9, Franklin = 10, Bike = 11, Car = 12, Gun = 13, Heart = 14, Makeup = 15, Mask = 16, Michael = 17, Star = 18, Tattoo = 19, Trevor = 20, Lock = 21, Tick = 22 }
+
+BadgeTexture = {
+ [0] = function() return "" end,
+ [1] = function() return "mp_medal_bronze" end,
+ [2] = function() return "mp_medal_gold" end,
+ [3] = function() return "medal_silver" end,
+ [4] = function() return "mp_alerttriangle" end,
+ [5] = function() return "mp_hostcrown" end,
+ [6] = function(Selected) if Selected then return "shop_ammo_icon_b" else return "shop_ammo_icon_a" end end,
+ [7] = function(Selected) if Selected then return "shop_armour_icon_b" else return "shop_armour_icon_a" end end,
+ [8] = function(Selected) if Selected then return "shop_barber_icon_b" else return "shop_barber_icon_a" end end,
+ [9] = function(Selected) if Selected then return "shop_clothing_icon_b" else return "shop_clothing_icon_a" end end,
+ [10] = function(Selected) if Selected then return "shop_franklin_icon_b" else return "shop_franklin_icon_a" end end,
+ [11] = function(Selected) if Selected then return "shop_garage_bike_icon_b" else return "shop_garage_bike_icon_a" end end,
+ [12] = function(Selected) if Selected then return "shop_garage_icon_b" else return "shop_garage_icon_a" end end,
+ [13] = function(Selected) if Selected then return "shop_gunclub_icon_b" else return "shop_gunclub_icon_a" end end,
+ [14] = function(Selected) if Selected then return "shop_health_icon_b" else return "shop_health_icon_a" end end,
+ [15] = function(Selected) if Selected then return "shop_makeup_icon_b" else return "shop_makeup_icon_a" end end,
+ [16] = function(Selected) if Selected then return "shop_mask_icon_b" else return "shop_mask_icon_a" end end,
+ [17] = function(Selected) if Selected then return "shop_michael_icon_b" else return "shop_michael_icon_a" end end,
+ [18] = function() return "shop_new_star" end,
+ [19] = function(Selected) if Selected then return "shop_tattoos_icon_b" else return "shop_tattoos_icon_a" end end,
+ [20] = function(Selected) if Selected then return "shop_trevor_icon_b" else return "shop_trevor_icon_a" end end,
+ [21] = function() return "shop_lock" end,
+ [22] = function() return "shop_tick_icon" end,
+}
+
+BadgeDictionary = {
+ [0] = function(Selected)
+ if Selected then
+ return "commonmenu"
+ else
+ return "commonmenu"
+ end
+ end,
+}
+
+BadgeColour = {
+ [5] = function(Selected) if Selected then return 0, 0, 0, 255 else return 255, 255, 255, 255 end end,
+ [21] = function(Selected) if Selected then return 0, 0, 0, 255 else return 255, 255, 255, 255 end end,
+ [22] = function(Selected) if Selected then return 0, 0, 0, 255 else return 255, 255, 255, 255 end end,
+}
+
+Colours = {
+ PureWhite = {255, 255, 255, 255},
+ White = {240, 240, 240, 255},
+ Black = {0, 0, 0, 255},
+ Grey = {155, 155, 155, 255},
+ GreyLight = {205, 205, 205, 255},
+ GreyDark = {77, 77, 77, 255},
+ Red = {224, 50, 50, 255},
+ RedLight = {240, 153, 153, 255},
+ RedDark = {112, 25, 25, 255},
+ Blue = {93, 182, 229, 255},
+ BlueLight = {174, 219, 242, 255},
+ BlueDark = {47, 92, 115, 255},
+ Yellow = {240, 200, 80, 255},
+ YellowLight = {254, 235, 169, 255},
+ YellowDark = {126, 107, 41, 255},
+ Orange = {255, 133, 85, 255},
+ OrangeLight = {255, 194, 170, 255},
+ OrangeDark = {127, 66, 42, 255},
+ Green = {114, 204, 114, 255},
+ GreenLight = {185, 230, 185, 255},
+ GreenDark = {57, 102, 57, 255},
+ Purple = {132, 102, 226, 255},
+ PurpleLight = {192, 179, 239, 255},
+ PurpleDark = {67, 57, 111, 255},
+ Pink = {203, 54, 148, 255},
+ RadarHealth = {53, 154, 71, 255},
+ RadarArmour = {93, 182, 229, 255},
+ RadarDamage = {235, 36, 39, 255},
+ NetPlayer1 = {194, 80, 80, 255},
+ NetPlayer2 = {156, 110, 175, 255},
+ NetPlayer3 = {255, 123, 196, 255},
+ NetPlayer4 = {247, 159, 123, 255},
+ NetPlayer5 = {178, 144, 132, 255},
+ NetPlayer6 = {141, 206, 167, 255},
+ NetPlayer7 = {113, 169, 175, 255},
+ NetPlayer8 = {211, 209, 231, 255},
+ NetPlayer9 = {144, 127, 153, 255},
+ NetPlayer10 = {106, 196, 191, 255},
+ NetPlayer11 = {214, 196, 153, 255},
+ NetPlayer12 = {234, 142, 80, 255},
+ NetPlayer13 = {152, 203, 234, 255},
+ NetPlayer14 = {178, 98, 135, 255},
+ NetPlayer15 = {144, 142, 122, 255},
+ NetPlayer16 = {166, 117, 94, 255},
+ NetPlayer17 = {175, 168, 168, 255},
+ NetPlayer18 = {232, 142, 155, 255},
+ NetPlayer19 = {187, 214, 91, 255},
+ NetPlayer20 = {12, 123, 86, 255},
+ NetPlayer21 = {123, 196, 255, 255},
+ NetPlayer22 = {171, 60, 230, 255},
+ NetPlayer23 = {206, 169, 13, 255},
+ NetPlayer24 = {71, 99, 173, 255},
+ NetPlayer25 = {42, 166, 185, 255},
+ NetPlayer26 = {186, 157, 125, 255},
+ NetPlayer27 = {201, 225, 255, 255},
+ NetPlayer28 = {240, 240, 150, 255},
+ NetPlayer29 = {237, 140, 161, 255},
+ NetPlayer30 = {249, 138, 138, 255},
+ NetPlayer31 = {252, 239, 166, 255},
+ NetPlayer32 = {240, 240, 240, 255},
+ SimpleBlipDefault = {159, 201, 166, 255},
+ MenuBlue = {140, 140, 140, 255},
+ MenuGreyLight = {140, 140, 140, 255},
+ MenuBlueExtraDark = {40, 40, 40, 255},
+ MenuYellow = {240, 160, 0, 255},
+ MenuYellowDark = {240, 160, 0, 255},
+ MenuGreen = {240, 160, 0, 255},
+ MenuGrey = {140, 140, 140, 255},
+ MenuGreyDark = {60, 60, 60, 255},
+ MenuHighlight = {30, 30, 30, 255},
+ MenuStandard = {140, 140, 140, 255},
+ MenuDimmed = {75, 75, 75, 255},
+ MenuExtraDimmed = {50, 50, 50, 255},
+ BriefTitle = {95, 95, 95, 255},
+ MidGreyMp = {100, 100, 100, 255},
+ NetPlayer1Dark = {93, 39, 39, 255},
+ NetPlayer2Dark = {77, 55, 89, 255},
+ NetPlayer3Dark = {124, 62, 99, 255},
+ NetPlayer4Dark = {120, 80, 80, 255},
+ NetPlayer5Dark = {87, 72, 66, 255},
+ NetPlayer6Dark = {74, 103, 83, 255},
+ NetPlayer7Dark = {60, 85, 88, 255},
+ NetPlayer8Dark = {105, 105, 64, 255},
+ NetPlayer9Dark = {72, 63, 76, 255},
+ NetPlayer10Dark = {53, 98, 95, 255},
+ NetPlayer11Dark = {107, 98, 76, 255},
+ NetPlayer12Dark = {117, 71, 40, 255},
+ NetPlayer13Dark = {76, 101, 117, 255},
+ NetPlayer14Dark = {65, 35, 47, 255},
+ NetPlayer15Dark = {72, 71, 61, 255},
+ NetPlayer16Dark = {85, 58, 47, 255},
+ NetPlayer17Dark = {87, 84, 84, 255},
+ NetPlayer18Dark = {116, 71, 77, 255},
+ NetPlayer19Dark = {93, 107, 45, 255},
+ NetPlayer20Dark = {6, 61, 43, 255},
+ NetPlayer21Dark = {61, 98, 127, 255},
+ NetPlayer22Dark = {85, 30, 115, 255},
+ NetPlayer23Dark = {103, 84, 6, 255},
+ NetPlayer24Dark = {35, 49, 86, 255},
+ NetPlayer25Dark = {21, 83, 92, 255},
+ NetPlayer26Dark = {93, 98, 62, 255},
+ NetPlayer27Dark = {100, 112, 127, 255},
+ NetPlayer28Dark = {120, 120, 75, 255},
+ NetPlayer29Dark = {152, 76, 93, 255},
+ NetPlayer30Dark = {124, 69, 69, 255},
+ NetPlayer31Dark = {10, 43, 50, 255},
+ NetPlayer32Dark = {95, 95, 10, 255},
+ Bronze = {180, 130, 97, 255},
+ Silver = {150, 153, 161, 255},
+ Gold = {214, 181, 99, 255},
+ Platinum = {166, 221, 190, 255},
+ Gang1 = {29, 100, 153, 255},
+ Gang2 = {214, 116, 15, 255},
+ Gang3 = {135, 125, 142, 255},
+ Gang4 = {229, 119, 185, 255},
+ SameCrew = {252, 239, 166, 255},
+ Freemode = {45, 110, 185, 255},
+ PauseBg = {0, 0, 0, 255},
+ Friendly = {93, 182, 229, 255},
+ Enemy = {194, 80, 80, 255},
+ Location = {240, 200, 80, 255},
+ Pickup = {114, 204, 114, 255},
+ PauseSingleplayer = {114, 204, 114, 255},
+ FreemodeDark = {22, 55, 92, 255},
+ InactiveMission = {154, 154, 154, 255},
+ Damage = {194, 80, 80, 255},
+ PinkLight = {252, 115, 201, 255},
+ PmMitemHighlight = {252, 177, 49, 255},
+ ScriptVariable = {0, 0, 0, 255},
+ Yoga = {109, 247, 204, 255},
+ Tennis = {241, 101, 34, 255},
+ Golf = {214, 189, 97, 255},
+ ShootingRange = {112, 25, 25, 255},
+ FlightSchool = {47, 92, 115, 255},
+ NorthBlue = {93, 182, 229, 255},
+ SocialClub = {234, 153, 28, 255},
+ PlatformBlue = {11, 55, 123, 255},
+ PlatformGreen = {146, 200, 62, 255},
+ PlatformGrey = {234, 153, 28, 255},
+ FacebookBlue = {66, 89, 148, 255},
+ IngameBg = {0, 0, 0, 255},
+ Darts = {114, 204, 114, 255},
+ Waypoint = {164, 76, 242, 255},
+ Michael = {101, 180, 212, 255},
+ Franklin = {171, 237, 171, 255},
+ Trevor = {255, 163, 87, 255},
+ GolfP1 = {240, 240, 240, 255},
+ GolfP2 = {235, 239, 30, 255},
+ GolfP3 = {255, 149, 14, 255},
+ GolfP4 = {246, 60, 161, 255},
+ WaypointLight = {210, 166, 249, 255},
+ WaypointDark = {82, 38, 121, 255},
+ PanelLight = {0, 0, 0, 255},
+ MichaelDark = {72, 103, 116, 255},
+ FranklinDark = {85, 118, 85, 255},
+ TrevorDark = {127, 81, 43, 255},
+ ObjectiveRoute = {240, 200, 80, 255},
+ PausemapTint = {0, 0, 0, 255},
+ PauseDeselect = {100, 100, 100, 255},
+ PmWeaponsPurchasable = {45, 110, 185, 255},
+ PmWeaponsLocked = {240, 240, 240, 255},
+ ScreenBg = {0, 0, 0, 255},
+ Chop = {224, 50, 50, 255},
+ PausemapTintHalf = {0, 0, 0, 255},
+ NorthBlueOfficial = {0, 71, 133, 255},
+ ScriptVariable2 = {0, 0, 0, 255},
+ H = {33, 118, 37, 255},
+ HDark = {37, 102, 40, 255},
+ T = {234, 153, 28, 255},
+ TDark = {225, 140, 8, 255},
+ HShard = {20, 40, 0, 255},
+ ControllerMichael = {48, 255, 255, 255},
+ ControllerFranklin = {48, 255, 0, 255},
+ ControllerTrevor = {176, 80, 0, 255},
+ ControllerChop = {127, 0, 0, 255},
+ VideoEditorVideo = {53, 166, 224, 255},
+ VideoEditorAudio = {162, 79, 157, 255},
+ VideoEditorText = {104, 192, 141, 255},
+ HbBlue = {29, 100, 153, 255},
+ HbYellow = {234, 153, 28, 255},
+ VideoEditorScore = {240, 160, 1, 255},
+ VideoEditorAudioFadeout = {59, 34, 57, 255},
+ VideoEditorTextFadeout = {41, 68, 53, 255},
+ VideoEditorScoreFadeout = {82, 58, 10, 255},
+ HeistBackground = {37, 102, 40, 255},
+ VideoEditorAmbient = {240, 200, 80, 255},
+ VideoEditorAmbientFadeout = {80, 70, 34, 255},
+ Gb = {255, 133, 85, 255},
+ G = {255, 194, 170, 255},
+ B = {255, 133, 85, 255},
+ LowFlow = {240, 200, 80, 255},
+ LowFlowDark = {126, 107, 41, 255},
+ G1 = {247, 159, 123, 255},
+ G2 = {226, 134, 187, 255},
+ G3 = {239, 238, 151, 255},
+ G4 = {113, 169, 175, 255},
+ G5 = {160, 140, 193, 255},
+ G6 = {141, 206, 167, 255},
+ G7 = {181, 214, 234, 255},
+ G8 = {178, 144, 132, 255},
+ G9 = {0, 132, 114, 255},
+ G10 = {216, 85, 117, 255},
+ G11 = {30, 100, 152, 255},
+ G12 = {43, 181, 117, 255},
+ G13 = {233, 141, 79, 255},
+ G14 = {137, 210, 215, 255},
+ G15 = {134, 125, 141, 255},
+ Adversary = {109, 34, 33, 255},
+ DegenRed = {255, 0, 0, 255},
+ DegenYellow = {255, 255, 0, 255},
+ DegenGreen = {0, 255, 0, 255},
+ DegenCyan = {0, 255, 255, 255},
+ DegenBlue = {0, 0, 255, 255},
+ DegenMagenta = {255, 0, 255, 255},
+ Stunt1 = {38, 136, 234, 255},
+ Stunt2 = {224, 50, 50, 255},
+}
+
+--[[
+ Utils.lua
+ Utilities
+--]]
+
+function GetResolution()
+ local W, H = GetActiveScreenResolution()
+ if (W/H) > 3.5 then
+ return GetScreenResolution()
+ else
+ return W, H
+ end
+end
+
+function FormatXWYH(Value, Value2)
+ return Value/1920, Value2/1080
+end
+
+function math.round(num, numDecimalPlaces)
+ return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num))
+end
+
+function tobool(input)
+ if input == "true" or tonumber(input) == 1 or input == true then
+ return true
+ else
+ return false
+ end
+end
+
+function string.split(inputstr, sep)
+ if sep == nil then
+ sep = "%s"
+ end
+ local t={} ; i=1
+ for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
+ t[i] = str
+ i = i + 1
+ end
+
+ return t
+end
+
+function string.starts(String, Start)
+ return string.sub(String, 1, string.len(Start)) == Start
+end
+
+function IsMouseInBounds(X, Y, Width, Height)
+ local MX, MY = math.round(GetControlNormal(0, 239) * 1920), math.round(GetControlNormal(0, 240) * 1080)
+ MX, MY = FormatXWYH(MX, MY)
+ X, Y = FormatXWYH(X, Y)
+ Width, Height = FormatXWYH(Width, Height)
+ return (MX >= X and MX <= X + Width) and (MY > Y and MY < Y + Height)
+end
+
+function GetSafeZoneBounds()
+ local SafeSize = GetSafeZoneSize()
+ SafeSize = math.round(SafeSize, 2)
+ SafeSize = (SafeSize * 100) - 90
+ SafeSize = 10 - SafeSize
+
+ local W, H = 1920, 1080
+
+ return {X = math.round(SafeSize * ((W/H) * 5.4)), Y = math.round(SafeSize * 5.4)}
+end
+
+function Controller()
+ return not IsInputDisabled(2)
+end
+
+--[[
+ UIResRectangle.lua
+ Elements
+--]]
+
+function UIResRectangle.New(X, Y, Width, Height, R, G, B, A)
+ local _UIResRectangle = {
+ X = tonumber(X) or 0,
+ Y = tonumber(Y) or 0,
+ Width = tonumber(Width) or 0,
+ Height = tonumber(Height) or 0,
+ _Colour = {R = tonumber(R) or 255, G = tonumber(G) or 255, B = tonumber(B) or 255, A = tonumber(A) or 255},
+ }
+ return setmetatable(_UIResRectangle, UIResRectangle)
+end
+
+function UIResRectangle:Position(X, Y)
+ if tonumber(X) and tonumber(Y) then
+ self.X = tonumber(X)
+ self.Y = tonumber(Y)
+ else
+ return {X = self.X, Y = self.Y}
+ end
+end
+
+function UIResRectangle:Size(Width, Height)
+ if tonumber(Width) and tonumber(Height) then
+ self.Width = tonumber(Width)
+ self.Height = tonumber(Height)
+ else
+ return {Width = self.Width, Height = self.Height}
+ end
+end
+
+function UIResRectangle:Colour(R, G, B, A)
+ if tonumber(R) or tonumber(G) or tonumber(B) or tonumber(A) then
+ self._Colour.R = tonumber(R) or 255
+ self._Colour.B = tonumber(B) or 255
+ self._Colour.G = tonumber(G) or 255
+ self._Colour.A = tonumber(A) or 255
+ else
+ return self._Colour
+ end
+end
+
+function UIResRectangle:Draw()
+ local Position = self:Position()
+ local Size = self:Size()
+ Size.Width, Size.Height = FormatXWYH(Size.Width, Size.Height)
+ Position.X, Position.Y = FormatXWYH(Position.X, Position.Y)
+ DrawRect(Position.X + Size.Width * 0.5, Position.Y + Size.Height * 0.5, Size.Width, Size.Height, self._Colour.R, self._Colour.G, self._Colour.B, self._Colour.A)
+end
+
+function DrawRectangle(X, Y, Width, Height, R, G, B, A)
+ X, Y, Width, Height = X or 0, Y or 0, Width or 0, Height or 0
+ X, Y = FormatXWYH(X, Y)
+ Width, Height = FormatXWYH(Width, Height)
+ DrawRect(X + Width * 0.5, Y + Height * 0.5, Width, Height, tonumber(R) or 255, tonumber(G) or 255, tonumber(B) or 255, tonumber(A) or 255)
+end
+
+--[[
+ UIResText.lua
+ Elements
+--]]
+
+function GetCharacterCount(str)
+ local characters = 0
+ for c in str:gmatch("[%z\1-\127\194-\244][\128-\191]*") do
+ local a = c:byte(1, -1)
+ if a ~= nil then
+ characters = characters + 1
+ end
+ end
+ return characters
+end
+
+function GetByteCount(str)
+ local bytes = 0
+
+ for c in str:gmatch("[%z\1-\127\194-\244][\128-\191]*") do
+ local a,b,c,d = c:byte(1, -1)
+ if a ~= nil then
+ bytes = bytes + 1
+ end
+ if b ~= nil then
+ bytes = bytes + 1
+ end
+ if c ~= nil then
+ bytes = bytes + 1
+ end
+ if d ~= nil then
+ bytes = bytes + 1
+ end
+ end
+ return bytes
+end
+
+function AddLongStringForAscii(str)
+ local maxbytelength = 99
+ for i = 0, GetCharacterCount(str), 99 do
+ AddTextComponentSubstringPlayerName(string.sub(str, i, math.min(maxbytelength, GetCharacterCount(str) - i))) --needs changed
+ end
+end
+
+function AddLongStringForUtf8(str)
+ local maxbytelength = 99
+ local bytecount = GetByteCount(str)
+
+ if bytecount < maxbytelength then
+ AddTextComponentSubstringPlayerName(str)
+ return
+ end
+
+ local startIndex = 0
+
+ for i = 0, GetCharacterCount(str), 1 do
+ local length = i - startIndex
+ if GetByteCount(string.sub(str, startIndex, length)) > maxbytelength then
+ AddTextComponentSubstringPlayerName(string.sub(str, startIndex, length - 1))
+ i = i - 1
+ startIndex = startIndex + (length - 1)
+ end
+ end
+ AddTextComponentSubstringPlayerName(string.sub(str, startIndex, GetCharacterCount(str) - startIndex))
+end
+
+function AddLongString(str)
+ local bytecount = GetByteCount(str)
+ if bytecount == GetCharacterCount(str) then
+ AddLongStringForAscii(str)
+ else
+ AddLongStringForUtf8(str)
+ end
+end
+
+function MeasureStringWidthNoConvert(str, font, scale)
+ BeginTextCommandWidth("STRING")
+ AddLongString(str)
+ SetTextFont(font or 0)
+ SetTextScale(1.0, scale or 0)
+ return EndTextCommandGetWidth(true)
+end
+
+function MeasureStringWidth(str, font, scale)
+ return MeasureStringWidthNoConvert(str, font, scale) * 1920
+end
+
+function UIResText.New(Text, X, Y, Scale, R, G, B, A, Font, Alignment, DropShadow, Outline, WordWrap)
+ local _UIResText = {
+ _Text = tostring(Text) or "",
+ X = tonumber(X) or 0,
+ Y = tonumber(Y) or 0,
+ Scale = tonumber(Scale) or 0,
+ _Colour = {R = tonumber(R) or 255, G = tonumber(G) or 255, B = tonumber(B) or 255, A = tonumber(A) or 255},
+ Font = tonumber(Font) or 0,
+ Alignment = Alignment or nil,
+ DropShadow = Dropshadow or nil,
+ Outline = Outline or nil,
+ WordWrap = tonumber(WordWrap) or 0,
+ }
+ return setmetatable(_UIResText, UIResText)
+end
+
+function UIResText:Position(X, Y)
+ if tonumber(X) and tonumber(Y) then
+ self.X = tonumber(X)
+ self.Y = tonumber(Y)
+ else
+ return {X = self.X, Y = self.Y}
+ end
+end
+
+function UIResText:Colour(R, G, B, A)
+ if tonumber(R) and tonumber(G) and tonumber(B) and tonumber(A) then
+ self._Colour.R = tonumber(R)
+ self._Colour.B = tonumber(B)
+ self._Colour.G = tonumber(G)
+ self._Colour.A = tonumber(A)
+ else
+ return self._Colour
+ end
+end
+
+function UIResText:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self._Text = tostring(Text)
+ else
+ return self._Text
+ end
+end
+
+function UIResText:Draw()
+ local Position = self:Position()
+ Position.X, Position.Y = FormatXWYH(Position.X, Position.Y)
+
+ SetTextFont(self.Font)
+ SetTextScale(1.0, self.Scale)
+ SetTextColour(self._Colour.R, self._Colour.G, self._Colour.B, self._Colour.A)
+
+ if self.DropShadow then
+ SetTextDropShadow()
+ end
+ if self.Outline then
+ SetTextOutline()
+ end
+
+ if self.Alignment ~= nil then
+ if self.Alignment == 1 or self.Alignment == "Center" or self.Alignment == "Centre" then
+ SetTextCentre(true)
+ elseif self.Alignment == 2 or self.Alignment == "Right" then
+ SetTextRightJustify(true)
+ SetTextWrap(0, Position.X)
+ end
+ end
+
+ if tonumber(self.WordWrap) then
+ if tonumber(self.WordWrap) ~= 0 then
+ SetTextWrap(Position.X, Position.X + (tonumber(self.WordWrap) / Resolution.Width))
+ end
+ end
+
+ BeginTextCommandDisplayText("STRING")
+ AddLongString(self._Text)
+ EndTextCommandDisplayText(Position.X, Position.Y)
+end
+
+function RenderText(Text, X, Y, Font, Scale, R, G, B, A, Alignment, DropShadow, Outline, WordWrap)
+ Text = tostring(Text)
+ X, Y = FormatXWYH(X, Y)
+ SetTextFont(Font or 0)
+ SetTextScale(1.0, Scale or 0)
+ SetTextColour(R or 255, G or 255, B or 255, A or 255)
+
+ if DropShadow then
+ SetTextDropShadow()
+ end
+ if Outline then
+ SetTextOutline()
+ end
+
+ if Alignment ~= nil then
+ if Alignment == 1 or Alignment == "Center" or Alignment == "Centre" then
+ SetTextCentre(true)
+ elseif Alignment == 2 or Alignment == "Right" then
+ SetTextRightJustify(true)
+ SetTextWrap(0, X)
+ end
+ end
+
+ if tonumber(WordWrap) then
+ if tonumber(WordWrap) ~= 0 then
+ WordWrap, _ = FormatXWYH(WordWrap, 0)
+ SetTextWrap(WordWrap, X - WordWrap)
+ end
+ end
+
+ BeginTextCommandDisplayText("STRING")
+ AddLongString(Text)
+ EndTextCommandDisplayText(X, Y)
+end
+
+--[[
+ Sprite.lua
+ Elements
+--]]
+
+function Sprite.New(TxtDictionary, TxtName, X, Y, Width, Height, Heading, R, G, B, A)
+ local _Sprite = {
+ TxtDictionary = tostring(TxtDictionary),
+ TxtName = tostring(TxtName),
+ X = tonumber(X) or 0,
+ Y = tonumber(Y) or 0,
+ Width = tonumber(Width) or 0,
+ Height = tonumber(Height) or 0,
+ Heading = tonumber(Heading) or 0,
+ _Colour = {R = tonumber(R) or 255, G = tonumber(G) or 255, B = tonumber(B) or 255, A = tonumber(A) or 255},
+ }
+ return setmetatable(_Sprite, Sprite)
+end
+
+function Sprite:Position(X, Y)
+ if tonumber(X) and tonumber(Y) then
+ self.X = tonumber(X)
+ self.Y = tonumber(Y)
+ else
+ return {X = self.X, Y = self.Y}
+ end
+end
+
+function Sprite:Size(Width, Height)
+ if tonumber(Width) and tonumber(Width) then
+ self.Width = tonumber(Width)
+ self.Height = tonumber(Height)
+ else
+ return {Width = self.Width, Height = self.Height}
+ end
+end
+
+function Sprite:Colour(R, G, B, A)
+ if tonumber(R) or tonumber(G) or tonumber(B) or tonumber(A) then
+ self._Colour.R = tonumber(R) or 255
+ self._Colour.B = tonumber(B) or 255
+ self._Colour.G = tonumber(G) or 255
+ self._Colour.A = tonumber(A) or 255
+ else
+ return self._Colour
+ end
+end
+
+function Sprite:Draw()
+ if not HasStreamedTextureDictLoaded(self.TxtDictionary) then
+ RequestStreamedTextureDict(self.TxtDictionary, true)
+ end
+ local Position = self:Position()
+ local Size = self:Size()
+ Size.Width, Size.Height = FormatXWYH(Size.Width, Size.Height)
+ Position.X, Position.Y = FormatXWYH(Position.X, Position.Y)
+ DrawSprite(self.TxtDictionary, self.TxtName, Position.X + Size.Width * 0.5, Position.Y + Size.Height * 0.5, Size.Width, Size.Height, self.Heading, self._Colour.R, self._Colour.G, self._Colour.B, self._Colour.A)
+end
+
+function DrawTexture(TxtDictionary, TxtName, X, Y, Width, Height, Heading, R, G, B, A)
+ if not HasStreamedTextureDictLoaded(tostring(TxtDictionary) or "") then
+ RequestStreamedTextureDict(tostring(TxtDictionary) or "", true)
+ end
+ X, Y, Width, Height = X or 0, Y or 0, Width or 0, Height or 0
+ X, Y = FormatXWYH(X, Y)
+ Width, Height = FormatXWYH(Width, Height)
+ DrawSprite(tostring(TxtDictionary) or "", tostring(TxtName) or "", X + Width * 0.5, Y + Height * 0.5, Width, Height, tonumber(Heading) or 0, tonumber(R) or 255, tonumber(G) or 255, tonumber(B) or 255, tonumber(A) or 255)
+end
+
+--[[
+ StringMeasurer.lua
+ Elements
+--]]
+
+function MeasureString(str)
+ local output = 0
+ for i = 1, GetCharacterCount(str), 1 do
+ if CharacterMap[string.sub(str, i, i)] then
+ output = output + CharacterMap[string.sub(str, i, i)] + 1
+ end
+ end
+ return output
+end
+
+--[[
+ Badge.lua
+ Elements
+--]]
+
+function GetBadgeTexture(Badge, Selected)
+ if BadgeTexture[Badge] then
+ return BadgeTexture[Badge](Selected)
+ else
+ return ""
+ end
+end
+
+function GetBadgeDictionary(Badge, Selected)
+ if BadgeDictionary[Badge] then
+ return BadgeDictionary[Badge](Selected)
+ else
+ return "commonmenu"
+ end
+end
+
+function GetBadgeColour(Badge, Selected)
+ if BadgeColour[Badge] then
+ return BadgeColour[Badge](Selected)
+ else
+ return 255, 255, 255, 255
+ end
+end
+
+--[[
+ Colours.lua
+ Elements
+--]]
+
+--[[
+ UIMenuItem.lua
+ Items
+--]]
+
+function UIMenuItem.New(Text, Description)
+ _UIMenuItem = {
+ Rectangle = UIResRectangle.New(0, 0, 431, 38, 255, 255, 255, 20),
+ Text = UIResText.New(tostring(Text) or "", 8, 0, 0.33, 245, 245, 245, 255, 0),
+ _Description = tostring(Description) or "";
+ SelectedSprite = Sprite.New("commonmenu", "gradient_nav", 0, 0, 431, 38),
+ LeftBadge = { Sprite = Sprite.New("commonmenu", "", 0, 0, 40, 40), Badge = 0},
+ RightBadge = { Sprite = Sprite.New("commonmenu", "", 0, 0, 40, 40), Badge = 0},
+ Label = {
+ Text = UIResText.New("", 0, 0, 0.35, 245, 245, 245, 255, 0, "Right"),
+ MainColour = {R = 255, G = 255, B = 255, A = 255},
+ HighlightColour = {R = 0, G = 0, B = 0, A = 255},
+ },
+ _Selected = false,
+ _Hovered = false,
+ _Enabled = true,
+ _Offset = {X = 0, Y = 0},
+ ParentMenu = nil,
+ Panels = {},
+ Activated = function(menu, item, panels) end,
+ ActivatedPanel = function(menu, item, panel, panelvalue) end,
+ }
+ return setmetatable(_UIMenuItem, UIMenuItem)
+end
+
+function UIMenuItem:SetParentMenu(Menu)
+ if Menu ~= nil and Menu() == "UIMenu" then
+ self.ParentMenu = Menu
+ else
+ return self.ParentMenu
+ end
+end
+
+function UIMenuItem:Selected(bool)
+ if bool ~= nil then
+ self._Selected = tobool(bool)
+ else
+ return self._Selected
+ end
+end
+
+function UIMenuItem:Hovered(bool)
+ if bool ~= nil then
+ self._Hovered = tobool(bool)
+ else
+ return self._Hovered
+ end
+end
+
+function UIMenuItem:Enabled(bool)
+ if bool ~= nil then
+ self._Enabled = tobool(bool)
+ else
+ return self._Enabled
+ end
+end
+
+function UIMenuItem:Description(str)
+ if tostring(str) and str ~= nil then
+ self._Description = tostring(str)
+ else
+ return self._Description
+ end
+end
+
+function UIMenuItem:Offset(X, Y)
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self._Offset.Y = tonumber(Y)
+ end
+ else
+ return self._Offset
+ end
+end
+
+function UIMenuItem:Position(Y)
+ if tonumber(Y) then
+ self.Rectangle:Position(self._Offset.X, Y + 144 + self._Offset.Y)
+ self.SelectedSprite:Position(0 + self._Offset.X, Y + 144 + self._Offset.Y)
+ self.Text:Position(8 + self._Offset.X, Y + 147 + self._Offset.Y)
+ self.LeftBadge.Sprite:Position(0 + self._Offset.X, Y + 142 + self._Offset.Y)
+ self.RightBadge.Sprite:Position(385 + self._Offset.X, Y + 142 + self._Offset.Y)
+ self.Label.Text:Position(420 + self._Offset.X, Y + 148 + self._Offset.Y)
+ end
+end
+
+function UIMenuItem:RightLabel(Text, MainColour, HighlightColour)
+ if tostring(Text) and Text ~= nil then
+ if type(MainColour) == "table" then
+ self.Label.MainColour = MainColour
+ end
+ if type(HighlightColour) == "table" then
+ self.Label.HighlightColour = HighlightColour
+ end
+ self.Label.Text:Text(tostring(Text))
+ else
+ return self.Label.Text:Text()
+ end
+end
+
+function UIMenuItem:SetLeftBadge(Badge)
+ if tonumber(Badge) then
+ self.LeftBadge.Badge = tonumber(Badge)
+ end
+end
+
+function UIMenuItem:SetRightBadge(Badge)
+ if tonumber(Badge) then
+ self.RightBadge.Badge = tonumber(Badge)
+ end
+end
+
+function UIMenuItem:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self.Text:Text(tostring(Text))
+ else
+ return self.Text:Text()
+ end
+end
+
+function UIMenuItem:AddPanel(Panel)
+ if Panel() == "UIMenuPanel" then
+ table.insert(self.Panels, Panel)
+ Panel:SetParentItem(self)
+ end
+end
+
+function UIMenuItem:RemovePanelAt(Index)
+ if tonumber(Index) then
+ if self.Panels[Index] then
+ table.remove(self.Panels, tonumber(Index))
+ end
+ end
+end
+
+function UIMenuItem:FindPanelIndex(Panel)
+ if Panel() == "UIMenuPanel" then
+ for Index = 1, #self.Panels do
+ if self.Panels[Index] == Panel then
+ return Index
+ end
+ end
+ end
+ return nil
+end
+
+function UIMenuItem:FindPanelItem()
+ for Index = #self.Items, 1, -1 do
+ if self.Items[Index].Panel then
+ return Index
+ end
+ end
+ return nil
+end
+
+function UIMenuItem:Draw()
+ self.Rectangle:Size(431 + self.ParentMenu.WidthOffset, self.Rectangle.Height)
+ self.SelectedSprite:Size(431 + self.ParentMenu.WidthOffset, self.SelectedSprite.Height)
+
+ if self._Hovered and not self._Selected then
+ self.Rectangle:Draw()
+ end
+
+ if self._Selected then
+ self.SelectedSprite:Draw()
+ end
+
+ if self._Enabled then
+ if self._Selected then
+ self.Text:Colour(0, 0, 0, 255)
+ self.Label.Text:Colour(self.Label.HighlightColour.R, self.Label.HighlightColour.G, self.Label.HighlightColour.B, self.Label.HighlightColour.A)
+ else
+ self.Text:Colour(245, 245, 245, 255)
+ self.Label.Text:Colour(self.Label.MainColour.R, self.Label.MainColour.G, self.Label.MainColour.B, self.Label.MainColour.A)
+ end
+ else
+ self.Text:Colour(163, 159, 148, 255)
+ self.Label.Text:Colour(163, 159, 148, 255)
+ end
+
+ if self.LeftBadge.Badge == BadgeStyle.None then
+ self.Text:Position(8 + self._Offset.X, self.Text.Y)
+ else
+ self.Text:Position(35 + self._Offset.X, self.Text.Y)
+ self.LeftBadge.Sprite.TxtDictionary = GetBadgeDictionary(self.LeftBadge.Badge, self._Selected)
+ self.LeftBadge.Sprite.TxtName = GetBadgeTexture(self.LeftBadge.Badge, self._Selected)
+ self.LeftBadge.Sprite:Colour(GetBadgeColour(self.LeftBadge.Badge, self._Selected))
+ self.LeftBadge.Sprite:Draw()
+ end
+
+ if self.RightBadge.Badge ~= BadgeStyle.None then
+ self.RightBadge.Sprite:Position(385 + self._Offset.X + self.ParentMenu.WidthOffset, self.RightBadge.Sprite.Y)
+ self.RightBadge.Sprite.TxtDictionary = GetBadgeDictionary(self.RightBadge.Badge, self._Selected)
+ self.RightBadge.Sprite.TxtName = GetBadgeTexture(self.RightBadge.Badge, self._Selected)
+ self.RightBadge.Sprite:Colour(GetBadgeColour(self.RightBadge.Badge, self._Selected))
+ self.RightBadge.Sprite:Draw()
+ end
+
+ if self.Label.Text:Text() ~= "" and string.len(self.Label.Text:Text()) > 0 then
+ self.Label.Text:Position(420 + self._Offset.X + self.ParentMenu.WidthOffset, self.Label.Text.Y)
+ self.Label.Text:Draw()
+ end
+
+ self.Text:Draw()
+end
+
+--[[
+ UIMenuCheckboxItem.lua
+ Items
+--]]
+
+function UIMenuCheckboxItem.New(Text, Check, Description)
+ local _UIMenuCheckboxItem = {
+ Base = UIMenuItem.New(Text or "", Description or ""),
+ CheckedSprite = Sprite.New("commonmenu", "shop_box_blank", 410, 95, 50, 50),
+ Checked = tobool(Check),
+ CheckboxEvent = function(menu, item, checked) end,
+ }
+ return setmetatable(_UIMenuCheckboxItem, UIMenuCheckboxItem)
+end
+
+function UIMenuCheckboxItem:SetParentMenu(Menu)
+ if Menu() == "UIMenu" then
+ self.Base.ParentMenu = Menu
+ else
+ return self.Base.ParentMenu
+ end
+end
+
+function UIMenuCheckboxItem:Position(Y)
+ if tonumber(Y) then
+ self.Base:Position(Y)
+ self.CheckedSprite:Position(380 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, Y + 138 + self.Base._Offset.Y)
+ end
+end
+
+function UIMenuCheckboxItem:Selected(bool)
+ if bool ~= nil then
+ self.Base._Selected = tobool(bool)
+ else
+ return self.Base._Selected
+ end
+end
+
+function UIMenuCheckboxItem:Hovered(bool)
+ if bool ~= nil then
+ self.Base._Hovered = tobool(bool)
+ else
+ return self.Base._Hovered
+ end
+end
+
+function UIMenuCheckboxItem:Enabled(bool)
+ if bool ~= nil then
+ self.Base._Enabled = tobool(bool)
+ else
+ return self.Base._Enabled
+ end
+end
+
+function UIMenuCheckboxItem:Description(str)
+ if tostring(str) and str ~= nil then
+ self.Base._Description = tostring(str)
+ else
+ return self.Base._Description
+ end
+end
+
+function UIMenuCheckboxItem:Offset(X, Y)
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self.Base._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self.Base._Offset.Y = tonumber(Y)
+ end
+ else
+ return self.Base._Offset
+ end
+end
+
+function UIMenuCheckboxItem:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self.Base.Text:Text(tostring(Text))
+ else
+ return self.Base.Text:Text()
+ end
+end
+
+function UIMenuCheckboxItem:SetLeftBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuCheckboxItem:SetRightBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuCheckboxItem:RightLabel()
+ error("This item does not support a right label")
+end
+
+function UIMenuCheckboxItem:Draw()
+ self.Base:Draw()
+ self.CheckedSprite:Position(380 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, self.CheckedSprite.Y)
+ if self.Base:Selected() then
+ if self.Checked then
+ self.CheckedSprite.TxtName = "shop_box_tickb"
+ else
+ self.CheckedSprite.TxtName = "shop_box_blankb"
+ end
+ else
+ if self.Checked then
+ self.CheckedSprite.TxtName = "shop_box_tick"
+ else
+ self.CheckedSprite.TxtName = "shop_box_blank"
+ end
+ end
+ self.CheckedSprite:Draw()
+end
+
+--[[
+ UIMenuListItem.lua
+ Items
+--]]
+
+function UIMenuListItem.New(Text, Items, Index, Description)
+ if type(Items) ~= "table" then Items = {} end
+ if Index == 0 then Index = 1 end
+ local _UIMenuListItem = {
+ Base = UIMenuItem.New(Text or "", Description or ""),
+ Items = Items,
+ LeftArrow = Sprite.New("commonmenu", "arrowleft", 110, 105, 30, 30),
+ RightArrow = Sprite.New("commonmenu", "arrowright", 280, 105, 30, 30),
+ ItemText = UIResText.New("", 290, 104, 0.35, 255, 255, 255, 255, 0, "Right"),
+ _Index = tonumber(Index) or 1,
+ Panels = {},
+ OnListChanged = function(menu, item, newindex) end,
+ OnListSelected = function(menu, item, newindex) end,
+ }
+ return setmetatable(_UIMenuListItem, UIMenuListItem)
+end
+
+function UIMenuListItem:SetParentMenu(Menu)
+ if Menu ~= nil and Menu() == "UIMenu" then
+ self.Base.ParentMenu = Menu
+ else
+ return self.Base.ParentMenu
+ end
+end
+
+function UIMenuListItem:Position(Y)
+ if tonumber(Y) then
+ self.LeftArrow:Position(300 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 147 + Y + self.Base._Offset.Y)
+ self.RightArrow:Position(400 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 147 + Y + self.Base._Offset.Y)
+ self.ItemText:Position(300 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 147 + Y + self.Base._Offset.Y)
+ self.Base:Position(Y)
+ end
+end
+
+function UIMenuListItem:Selected(bool)
+ if bool ~= nil then
+ self.Base._Selected = tobool(bool)
+ else
+ return self.Base._Selected
+ end
+end
+
+function UIMenuListItem:Hovered(bool)
+ if bool ~= nil then
+ self.Base._Hovered = tobool(bool)
+ else
+ return self.Base._Hovered
+ end
+end
+
+function UIMenuListItem:Enabled(bool)
+ if bool ~= nil then
+ self.Base._Enabled = tobool(bool)
+ else
+ return self.Base._Enabled
+ end
+end
+
+function UIMenuListItem:Description(str)
+ if tostring(str) and str ~= nil then
+ self.Base._Description = tostring(str)
+ else
+ return self.Base._Description
+ end
+end
+
+function UIMenuListItem:Offset(X, Y)
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self.Base._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self.Base._Offset.Y = tonumber(Y)
+ end
+ else
+ return self.Base._Offset
+ end
+end
+
+function UIMenuListItem:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self.Base.Text:Text(tostring(Text))
+ else
+ return self.Base.Text:Text()
+ end
+end
+
+function UIMenuListItem:Index(Index)
+ if tonumber(Index) then
+ if tonumber(Index) > #self.Items then
+ self._Index = 1
+ elseif tonumber(Index) < 1 then
+ self._Index = #self.Items
+ else
+ self._Index = tonumber(Index)
+ end
+ else
+ return self._Index
+ end
+end
+
+function UIMenuListItem:ItemToIndex(Item)
+ for i = 1, #self.Items do
+ if type(Item) == type(self.Items[i]) and Item == self.Items[i] then
+ return i
+ elseif type(self.Items[i]) == "table" and (type(Item) == type(self.Items[i].Name) or type(Item) == type(self.Items[i].Value)) and (Item == self.Items[i].Name or Item == self.Items[i].Value) then
+ return i
+ end
+ end
+end
+
+function UIMenuListItem:IndexToItem(Index)
+ if tonumber(Index) then
+ if tonumber(Index) == 0 then Index = 1 end
+ if self.Items[tonumber(Index)] then
+ return self.Items[tonumber(Index)]
+ end
+ end
+end
+
+function UIMenuListItem:SetLeftBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuListItem:SetRightBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuListItem:RightLabel()
+ error("This item does not support a right label")
+end
+
+function UIMenuListItem:AddPanel(Panel)
+ if Panel() == "UIMenuPanel" then
+ table.insert(self.Panels, Panel)
+ Panel:SetParentItem(self)
+ end
+end
+
+function UIMenuListItem:RemovePanelAt(Index)
+ if tonumber(Index) then
+ if self.Panels[Index] then
+ table.remove(self.Panels, tonumber(Index))
+ end
+ end
+end
+
+function UIMenuListItem:FindPanelIndex(Panel)
+ if Panel() == "UIMenuPanel" then
+ for Index = 1, #self.Panels do
+ if self.Panels[Index] == Panel then
+ return Index
+ end
+ end
+ end
+ return nil
+end
+
+function UIMenuListItem:FindPanelItem()
+ for Index = #self.Items, 1, -1 do
+ if self.Items[Index].Panel then
+ return Index
+ end
+ end
+ return nil
+end
+
+function UIMenuListItem:Draw()
+ self.Base:Draw()
+
+ if self:Enabled() then
+ if self:Selected() then
+ self.ItemText:Colour(0, 0, 0, 255)
+ self.LeftArrow:Colour(0, 0, 0, 255)
+ self.RightArrow:Colour(0, 0, 0, 255)
+ else
+ self.ItemText:Colour(245, 245, 245, 255)
+ self.LeftArrow:Colour(245, 245, 245, 255)
+ self.RightArrow:Colour(245, 245, 245, 255)
+ end
+ else
+ self.ItemText:Colour(163, 159, 148, 255)
+ self.LeftArrow:Colour(163, 159, 148, 255)
+ self.RightArrow:Colour(163, 159, 148, 255)
+ end
+
+ local Text = (type(self.Items[self._Index]) == "table") and tostring(self.Items[self._Index].Name) or tostring(self.Items[self._Index])
+ local Offset = MeasureStringWidth(Text, 0, 0.35)
+
+ self.ItemText:Text(Text)
+ self.LeftArrow:Position(378 - Offset + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, self.LeftArrow.Y)
+
+ if self:Selected() then
+ self.LeftArrow:Draw()
+ self.RightArrow:Draw()
+ self.ItemText:Position(403 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, self.ItemText.Y)
+ else
+ self.ItemText:Position(418 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, self.ItemText.Y)
+ end
+
+ self.ItemText:Draw()
+end
+
+--[[
+ UIMenuSliderItem.lua
+ Items
+--]]
+
+function UIMenuSliderItem.New(Text, Items, Index, Description, Divider)
+ if type(Items) ~= "table" then Items = {} end
+ if Index == 0 then Index = 1 end
+ local _UIMenuSliderItem = {
+ Base = UIMenuItem.New(Text or "", Description or ""),
+ Items = Items,
+ ShowDivider = tobool(Divider),
+ LeftArrow = Sprite.New("commonmenutu", "arrowleft", 0, 105, 15, 15),
+ RightArrow = Sprite.New("commonmenutu", "arrowright", 0, 105, 15, 15),
+ Background = UIResRectangle.New(0, 0, 150, 9, 15, 24, 33, 255),
+ Slider = UIResRectangle.New(0, 0, 75, 9, 72,109,149, 255),
+ Divider = UIResRectangle.New(0, 0, 2.5, 20, 245, 245, 245, 255),
+ _Index = tonumber(Index) or 1,
+ OnSliderChanged = function(menu, item, newindex) end,
+ OnSliderSelected = function(menu, item, newindex) end,
+ }
+ return setmetatable(_UIMenuSliderItem, UIMenuSliderItem)
+end
+
+function UIMenuSliderItem:SetParentMenu(Menu)
+ if Menu() == "UIMenu" then
+ self.Base.ParentMenu = Menu
+ else
+ return self.Base.ParentMenu
+ end
+end
+
+function UIMenuSliderItem:Position(Y)
+ if tonumber(Y) then
+ self.Background:Position(250 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, Y + 158.5 + self.Base._Offset.Y)
+ self.Slider:Position(250 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, Y + 158.5 + self.Base._Offset.Y)
+ self.Divider:Position(323.5 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, Y + 153 + self.Base._Offset.Y)
+ self.LeftArrow:Position(235 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 155.5 + Y + self.Base._Offset.Y)
+ self.RightArrow:Position(400 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 155.5 + Y + self.Base._Offset.Y)
+ self.Base:Position(Y)
+ end
+end
+
+function UIMenuSliderItem:Selected(bool)
+ if bool ~= nil then
+ self.Base._Selected = tobool(bool)
+ else
+ return self.Base._Selected
+ end
+end
+
+function UIMenuSliderItem:Hovered(bool)
+ if bool ~= nil then
+ self.Base._Hovered = tobool(bool)
+ else
+ return self.Base._Hovered
+ end
+end
+
+function UIMenuSliderItem:Enabled(bool)
+ if bool ~= nil then
+ self.Base._Enabled = tobool(bool)
+ else
+ return self.Base._Enabled
+ end
+end
+
+function UIMenuSliderItem:Description(str)
+ if tostring(str) and str ~= nil then
+ self.Base._Description = tostring(str)
+ else
+ return self.Base._Description
+ end
+end
+
+function UIMenuSliderItem:Offset(X, Y)
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self.Base._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self.Base._Offset.Y = tonumber(Y)
+ end
+ else
+ return self.Base._Offset
+ end
+end
+
+function UIMenuSliderItem:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self.Base.Text:Text(tostring(Text))
+ else
+ return self.Base.Text:Text()
+ end
+end
+
+function UIMenuSliderItem:Index(Index)
+ if tonumber(Index) then
+ if tonumber(Index) > #self.Items then
+ self._Index = 1
+ elseif tonumber(Index) < 1 then
+ self._Index = #self.Items
+ else
+ self._Index = tonumber(Index)
+ end
+ else
+ return self._Index
+ end
+end
+
+function UIMenuSliderItem:ItemToIndex(Item)
+ for i = 1, #self.Items do
+ if type(Item) == type(self.Items[i]) and Item == self.Items[i] then
+ return i
+ end
+ end
+end
+
+function UIMenuSliderItem:IndexToItem(Index)
+ if tonumber(Index) then
+ if tonumber(Index) == 0 then Index = 1 end
+ if self.Items[tonumber(Index)] then
+ return self.Items[tonumber(Index)]
+ end
+ end
+end
+
+function UIMenuSliderItem:SetLeftBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuSliderItem:SetRightBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuSliderItem:RightLabel()
+ error("This item does not support a right label")
+end
+
+function UIMenuSliderItem:Draw()
+ self.Base:Draw()
+
+ if self:Enabled() then
+ if self:Selected() then
+ self.LeftArrow:Colour(0, 0, 0, 255)
+ self.RightArrow:Colour(0, 0, 0, 255)
+ else
+ self.LeftArrow:Colour(245, 245, 245, 255)
+ self.RightArrow:Colour(245, 245, 245, 255)
+ end
+ else
+ self.LeftArrow:Colour(163, 159, 148, 255)
+ self.RightArrow:Colour(163, 159, 148, 255)
+ end
+
+ local Offset = ((self.Background.Width - self.Slider.Width)/(#self.Items - 1)) * (self._Index-1)
+
+ self.Slider:Position(250 + self.Base._Offset.X + Offset + self.Base.ParentMenu.WidthOffset, self.Slider.Y)
+
+ if self:Selected() then
+ self.LeftArrow:Draw()
+ self.RightArrow:Draw()
+ end
+
+ self.Background:Draw()
+ self.Slider:Draw()
+ if self.ShowDivider then
+ self.Divider:Draw()
+ end
+end
+
+--[[
+ UIMenuColouredItem.lua
+ Items
+--]]
+
+function UIMenuColouredItem.New(Text, Description, MainColour, HighlightColour)
+ if type(Colour) ~= "table" then Colour = {R = 0, G = 0, B = 0, A = 255} end
+ if type(HighlightColour) ~= "table" then Colour = {R = 255, G = 255, B = 255, A = 255} end
+ local _UIMenuColouredItem = {
+ Base = UIMenuItem.New(Text or "", Description or ""),
+ Rectangle = UIResRectangle.New(0, 0, 431, 38, MainColour.R, MainColour.G, MainColour.B, MainColour.A),
+ MainColour = MainColour,
+ HighlightColour = HighlightColour,
+ Activated = function(menu, item) end,
+ }
+ _UIMenuColouredItem.Base.SelectedSprite:Colour(HighlightColour.R, HighlightColour.G, HighlightColour.B, HighlightColour.A)
+ return setmetatable(_UIMenuColouredItem, UIMenuColouredItem)
+end
+
+function UIMenuColouredItem:SetParentMenu(Menu)
+ if Menu() == "UIMenu" then
+ self.Base.ParentMenu = Menu
+ else
+ return self.Base.ParentMenu
+ end
+end
+
+function UIMenuColouredItem:Position(Y)
+ if tonumber(Y) then
+ self.Base:Position(Y)
+ self.Rectangle:Position(self.Base._Offset.X, Y + 144 + self.Base._Offset.Y)
+ end
+end
+
+function UIMenuColouredItem:Selected(bool)
+ if bool ~= nil then
+ self.Base._Selected = tobool(bool)
+ else
+ return self.Base._Selected
+ end
+end
+
+function UIMenuColouredItem:Hovered(bool)
+ if bool ~= nil then
+ self.Base._Hovered = tobool(bool)
+ else
+ return self.Base._Hovered
+ end
+end
+
+function UIMenuColouredItem:Enabled(bool)
+ if bool ~= nil then
+ self.Base._Enabled = tobool(bool)
+ else
+ return self.Base._Enabled
+ end
+end
+
+function UIMenuColouredItem:Description(str)
+ if tostring(str) and str ~= nil then
+ self.Base._Description = tostring(str)
+ else
+ return self.Base._Description
+ end
+end
+
+function UIMenuColouredItem:Offset(X, Y)
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self.Base._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self.Base._Offset.Y = tonumber(Y)
+ end
+ else
+ return self.Base._Offset
+ end
+end
+
+function UIMenuColouredItem:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self.Base.Text:Text(tostring(Text))
+ else
+ return self.Base.Text:Text()
+ end
+end
+
+function UIMenuColouredItem:RightLabel(Text, MainColour, HighlightColour)
+ if tostring(Text) and Text ~= nil then
+ if type(MainColour) == "table" then
+ self.Base.Label.MainColour = MainColour
+ end
+ if type(HighlightColour) == "table" then
+ self.Base.Label.HighlightColour = HighlightColour
+ end
+ self.Base.Label.Text:Text(tostring(Text))
+ else
+ return self.Base.Label.Text:Text()
+ end
+end
+
+function UIMenuColouredItem:SetLeftBadge(Badge)
+ if tonumber(Badge) then
+ self.Base.LeftBadge.Badge = tonumber(Badge)
+ end
+end
+
+function UIMenuColouredItem:SetRightBadge(Badge)
+ if tonumber(Badge) then
+ self.Base.RightBadge.Badge = tonumber(Badge)
+ end
+end
+
+function UIMenuColouredItem:Draw()
+ self.Rectangle:Size(431 + self.ParentMenu.WidthOffset, self.Rectangle.Height)
+ self.Rectangle:Draw()
+ self.Base:Draw()
+end
+
+--[[
+ UIMenuProgressItem.lua
+ Items
+--]]
+
+function UIMenuProgressItem.New(Text, Items, Index, Description, Counter)
+ if type(Items) ~= "table" then Items = {} end
+ if Index == 0 then Index = 1 end
+ local _UIMenuProgressItem = {
+ Base = UIMenuItem.New(Text or "", Description or ""),
+ Data = {
+ Items = Items,
+ Counter = tobool(Counter),
+ Max = 407.5,
+ Index = tonumber(Index) or 1,
+ },
+ Background = UIResRectangle.New(0, 0, 415, 20),
+ Bar = UIResRectangle.New(0, 0, 407.5, 12.5),
+ OnProgressChanged = function(menu, item, newindex) end,
+ OnProgressSelected = function(menu, item, newindex) end,
+ }
+
+ _UIMenuProgressItem.Base.Rectangle.Height = 60
+ _UIMenuProgressItem.Base.SelectedSprite.Height = 60
+
+ if _UIMenuProgressItem.Data.Counter then
+ _UIMenuProgressItem.Base:RightLabel(_UIMenuProgressItem.Data.Index.."/"..#_UIMenuProgressItem.Data.Items)
+ else
+ _UIMenuProgressItem.Base:RightLabel((type(_UIMenuProgressItem.Data.Items[_UIMenuProgressItem.Data.Index]) == "table") and tostring(_UIMenuProgressItem.Data.Items[_UIMenuProgressItem.Data.Index].Name) or tostring(_UIMenuProgressItem.Data.Items[_UIMenuProgressItem.Data.Index]))
+ end
+
+ _UIMenuProgressItem.Bar.Width = _UIMenuProgressItem.Data.Index/#_UIMenuProgressItem.Data.Items * _UIMenuProgressItem.Data.Max
+
+ return setmetatable(_UIMenuProgressItem, UIMenuProgressItem)
+end
+
+function UIMenuProgressItem:SetParentMenu(Menu)
+ if Menu() == "UIMenu" then
+ self.Base.ParentMenu = Menu
+ else
+ return self.Base.ParentMenu
+ end
+end
+
+function UIMenuProgressItem:Position(Y)
+ if tonumber(Y) then
+ self.Base:Position(Y)
+ self.Background:Position(8 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 177 + Y + self.Base._Offset.Y)
+ self.Bar:Position(11.75 + self.Base._Offset.X + self.Base.ParentMenu.WidthOffset, 180.75 + Y + self.Base._Offset.Y)
+ end
+end
+
+function UIMenuProgressItem:Selected(bool)
+ if bool ~= nil then
+ self.Base._Selected = tobool(bool)
+ else
+ return self.Base._Selected
+ end
+end
+
+function UIMenuProgressItem:Hovered(bool)
+ if bool ~= nil then
+ self.Base._Hovered = tobool(bool)
+ else
+ return self.Base._Hovered
+ end
+end
+
+function UIMenuProgressItem:Enabled(bool)
+ if bool ~= nil then
+ self.Base._Enabled = tobool(bool)
+ else
+ return self.Base._Enabled
+ end
+end
+
+function UIMenuProgressItem:Description(str)
+ if tostring(str) and str ~= nil then
+ self.Base._Description = tostring(str)
+ else
+ return self.Base._Description
+ end
+end
+
+function UIMenuProgressItem:Offset(X, Y)
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self.Base._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self.Base._Offset.Y = tonumber(Y)
+ end
+ else
+ return self.Base._Offset
+ end
+end
+
+function UIMenuProgressItem:Text(Text)
+ if tostring(Text) and Text ~= nil then
+ self.Base.Text:Text(tostring(Text))
+ else
+ return self.Base.Text:Text()
+ end
+end
+
+function UIMenuProgressItem:Index(Index)
+ if tonumber(Index) then
+ if tonumber(Index) > #self.Data.Items then
+ self.Data.Index = 1
+ elseif tonumber(Index) < 1 then
+ self.Data.Index = #self.Data.Items
+ else
+ self.Data.Index = tonumber(Index)
+ end
+
+ if self.Data.Counter then
+ self.Base:RightLabel(self.Data.Index.."/"..#self.Data.Items)
+ else
+ self.Base:RightLabel((type(self.Data.Items[self.Data.Index]) == "table") and tostring(self.Data.Items[self.Data.Index].Name) or tostring(self.Data.Items[self.Data.Index]))
+ end
+
+ self.Bar.Width = self.Data.Index/#self.Data.Items * self.Data.Max
+ else
+ return self.Data.Index
+ end
+end
+
+function UIMenuProgressItem:ItemToIndex(Item)
+ for i = 1, #self.Data.Items do
+ if type(Item) == type(self.Data.Items[i]) and Item == self.Data.Items[i] then
+ return i
+ elseif type(self.Data.Items[i]) == "table" and (type(Item) == type(self.Data.Items[i].Name) or type(Item) == type(self.Data.Items[i].Value)) and (Item == self.Data.Items[i].Name or Item == self.Data.Items[i].Value) then
+ return i
+ end
+ end
+end
+
+function UIMenuProgressItem:IndexToItem(Index)
+ if tonumber(Index) then
+ if tonumber(Index) == 0 then Index = 1 end
+ if self.Data.Items[tonumber(Index)] then
+ return self.Data.Items[tonumber(Index)]
+ end
+ end
+end
+
+function UIMenuProgressItem:SetLeftBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuProgressItem:SetRightBadge()
+ error("This item does not support badges")
+end
+
+function UIMenuProgressItem:RightLabel()
+ error("This item does not support a right label")
+end
+
+function UIMenuProgressItem:CalculateProgress(CursorX)
+ local Progress = CursorX - self.Bar.X
+ self:Index(math.round(#self.Data.Items * (((Progress >= 0 and Progress <= self.Data.Max) and Progress or ((Progress < 0) and 0 or self.Data.Max))/self.Data.Max)))
+end
+
+function UIMenuProgressItem:Draw()
+ self.Base:Draw()
+
+ if self.Base._Selected then
+ self.Background:Colour(table.unpack(Colours.Black))
+ self.Bar:Colour(table.unpack(Colours.White))
+ else
+ self.Background:Colour(table.unpack(Colours.White))
+ self.Bar:Colour(table.unpack(Colours.Black))
+ end
+
+ self.Background:Draw()
+ self.Bar:Draw()
+end
+
+--[[
+ UIMenuHeritageWindow.lua
+ Windows
+--]]
+
+function UIMenuHeritageWindow.New(Mum, Dad)
+ if not tonumber(Mum) then Mum = 0 end
+ if not (Mum >= 0 and Mum <= 21) then Mum = 0 end
+ if not tonumber(Dad) then Dad = 0 end
+ if not (Dad >= 0 and Dad <= 23) then Dad = 0 end
+ _UIMenuHeritageWindow = {
+ Background = Sprite.New("pause_menu_pages_char_mom_dad", "mumdadbg", 0, 0, 431, 228), -- Background is required, must be a sprite or a rectangle.
+ MumSprite = Sprite.New("char_creator_portraits", ((Mum < 21) and "female_"..Mum or "special_female_"..(tonumber(string.sub(Mum, 2, 2)) - 1)), 0, 0, 228, 228),
+ DadSprite = Sprite.New("char_creator_portraits", ((Dad < 21) and "male_"..Dad or "special_male_"..(tonumber(string.sub(Dad, 2, 2)) - 1)), 0, 0, 228, 228),
+ Mum = Mum,
+ Dad = Dad,
+ _Offset = {X = 0, Y = 0}, -- required
+ ParentMenu = nil, -- required
+ }
+ return setmetatable(_UIMenuHeritageWindow, UIMenuHeritageWindow)
+end
+
+function UIMenuHeritageWindow:SetParentMenu(Menu) -- required
+ if Menu() == "UIMenu" then
+ self.ParentMenu = Menu
+ else
+ return self.ParentMenu
+ end
+end
+
+function UIMenuHeritageWindow:Offset(X, Y) -- required
+ if tonumber(X) or tonumber(Y) then
+ if tonumber(X) then
+ self._Offset.X = tonumber(X)
+ end
+ if tonumber(Y) then
+ self._Offset.Y = tonumber(Y)
+ end
+ else
+ return self._Offset
+ end
+end
+
+function UIMenuHeritageWindow:Position(Y) -- required
+ if tonumber(Y) then
+ self.Background:Position(self._Offset.X, 144 + Y + self._Offset.Y)
+ self.MumSprite:Position(self._Offset.X + (self.ParentMenu.WidthOffset/2) + 25, 144 + Y + self._Offset.Y)
+ self.DadSprite:Position(self._Offset.X + (self.ParentMenu.WidthOffset/2) + 195, 144 + Y + self._Offset.Y)
+ end
+end
+
+function UIMenuHeritageWindow:Index(Mum, Dad)
+ if not tonumber(Mum) then Mum = self.Mum end
+ if not (Mum >= 0 and Mum <= 21) then Mum = self.Mum end
+ if not tonumber(Dad) then Dad = self.Dad end
+ if not (Dad >= 0 and Dad <= 23) then Dad = self.Dad end
+
+ self.Mum = Mum
+ self.Dad = Dad
+
+ self.MumSprite.TxtName = ((self.Mum < 21) and "female_"..self.Mum or "special_female_"..(tonumber(string.sub(Mum, 2, 2)) - 1))
+ self.DadSprite.TxtName = ((self.Dad < 21) and "male_"..self.Dad or "special_male_"..(tonumber(string.sub(Dad, 2, 2)) - 1))
+end
+
+function UIMenuHeritageWindow:Draw() -- required
+ self.Background:Size(431 + self.ParentMenu.WidthOffset, 228)
+ self.Background:Draw()
+ self.DadSprite:Draw()
+ self.MumSprite:Draw()
+end
+
+--[[
+ UIMenuGridPanel.lua
+ Panels
+--]]
+
+UIMenuGridPanel = setmetatable({}, UIMenuGridPanel)
+UIMenuGridPanel.__index = UIMenuGridPanel
+UIMenuGridPanel.__call = function() return "UIMenuPanel", "UIMenuGridPanel" end
+
+function UIMenuGridPanel.New(TopText, LeftText, RightText, BottomText)
+ _UIMenuGridPanel = {
+ Data = {
+ Enabled = true,
+ },
+ Background = Sprite.New("commonmenu", "gradient_bgd", 0, 0, 431, 275),
+ Grid = Sprite.New("pause_menu_pages_char_mom_dad", "nose_grid", 0, 0, 200, 200, 0),
+ Circle = Sprite.New("mpinventory","in_world_circle", 0, 0, 20, 20, 0),
+ Audio = {Slider = "CONTINUOUS_SLIDER", Library = "HUD_FRONTEND_DEFAULT_SOUNDSET", Id = nil},
+ ParentItem = nil,
+ Text = {
+ Top = UIResText.New(TopText or "Top", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ Left = UIResText.New(LeftText or "Left", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ Right = UIResText.New(RightText or "Right", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ Bottom = UIResText.New(BottomText or "Bottom", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ },
+ }
+ return setmetatable(_UIMenuGridPanel, UIMenuGridPanel)
+end
+
+function UIMenuGridPanel:SetParentItem(Item) -- required
+ if Item() == "UIMenuItem" then
+ self.ParentItem = Item
+ else
+ return self.ParentItem
+ end
+end
+
+function UIMenuGridPanel:Enabled(Enabled)
+ if type(Enabled) == "boolean" then
+ self.Data.Enabled = Enabled
+ else
+ return self.Data.Enabled
+ end
+end
+
+function UIMenuGridPanel:CirclePosition(X, Y)
+ if tonumber(X) and tonumber(Y) then
+ self.Circle.X = (self.Grid.X + 20) + ((self.Grid.Width - 40) * ((X >= 0.0 and X <= 1.0) and X or 0.0)) - (self.Circle.Width/2)
+ self.Circle.Y = (self.Grid.Y + 20) + ((self.Grid.Height - 40) * ((Y >= 0.0 and Y <= 1.0) and Y or 0.0)) - (self.Circle.Height/2)
+ else
+ return math.round((self.Circle.X - (self.Grid.X + 20) + (self.Circle.Width/2))/(self.Grid.Width - 40), 2), math.round((self.Circle.Y - (self.Grid.Y + 20) + (self.Circle.Height/2))/(self.Grid.Height - 40), 2)
+ end
+end
+
+function UIMenuGridPanel:Position(Y) -- required
+ if tonumber(Y) then
+ local ParentOffsetX, ParentOffsetWidth = self.ParentItem:Offset().X, self.ParentItem:SetParentMenu().WidthOffset
+
+ self.Background:Position(ParentOffsetX, Y)
+ self.Grid:Position(ParentOffsetX + 115.5 + (ParentOffsetWidth/2), 37.5 + Y)
+ self.Text.Top:Position(ParentOffsetX + 215.5 + (ParentOffsetWidth/2), 5 + Y)
+ self.Text.Left:Position(ParentOffsetX + 57.75 + (ParentOffsetWidth/2), 120 + Y)
+ self.Text.Right:Position(ParentOffsetX + 373.25 + (ParentOffsetWidth/2), 120 + Y)
+ self.Text.Bottom:Position(ParentOffsetX + 215.5 + (ParentOffsetWidth/2), 240 + Y)
+
+ if not self.CircleLocked then
+ self.CircleLocked = true
+ self:CirclePosition(0.5, 0.5)
+ end
+ end
+end
+
+function UIMenuGridPanel:UpdateParent(X, Y)
+ local _, ParentType = self.ParentItem()
+ if ParentType == "UIMenuListItem" then
+ local PanelItemIndex = self.ParentItem:FindPanelItem()
+ if PanelItemIndex then
+ self.ParentItem.Items[PanelItemIndex].Value[self.ParentItem:FindPanelIndex(self)] = {X = X, Y = Y}
+ self.ParentItem:Index(PanelItemIndex)
+ self.ParentItem.Base.ParentMenu.OnListChange(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ self.ParentItem.OnListChanged(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ else
+ local PanelIndex = self.ParentItem:FindPanelIndex(self)
+ for Index = 1, #self.ParentItem.Items do
+ if type(self.ParentItem.Items[Index]) == "table" then
+ if not self.ParentItem.Items[Index].Panels then self.ParentItem.Items[Index].Panels = {} end
+ self.ParentItem.Items[Index].Panels[PanelIndex] = {X = X, Y = Y}
+ else
+ self.ParentItem.Items[Index] = {Name = tostring(self.ParentItem.Items[Index]), Value = self.ParentItem.Items[Index], Panels = {[PanelIndex] = {X = X, Y = Y}}}
+ end
+ end
+ self.ParentItem.Base.ParentMenu.OnListChange(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ self.ParentItem.OnListChanged(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ end
+ elseif ParentType == "UIMenuItem" then
+ self.ParentItem.ActivatedPanel(self.ParentItem.ParentMenu, self.ParentItem, self, {X = X, Y = Y})
+ end
+end
+
+function UIMenuGridPanel:Functions()
+ local SafeZone = {X = 0, Y = 0}
+ if self.ParentItem:SetParentMenu().Settings.ScaleWithSafezone then
+ SafeZone = GetSafeZoneBounds()
+ end
+
+ if IsMouseInBounds(self.Grid.X + 20 + SafeZone.X, self.Grid.Y + 20 + SafeZone.Y, self.Grid.Width - 40, self.Grid.Height - 40) then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ if not self.Pressed then
+ self.Pressed = true
+ Citizen.CreateThread(function()
+ self.Audio.Id = GetSoundId()
+ PlaySoundFrontend(self.Audio.Id, self.Audio.Slider, self.Audio.Library, 1)
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(self.Grid.X + 20 + SafeZone.X, self.Grid.Y + 20 + SafeZone.Y, self.Grid.Width - 40, self.Grid.Height - 40) do
+ Citizen.Wait(0)
+ local CursorX, CursorY = math.round(GetControlNormal(0, 239) * 1920) - SafeZone.X - (self.Circle.Width/2), math.round(GetControlNormal(0, 240) * 1080) - SafeZone.Y - (self.Circle.Height/2)
+
+ self.Circle:Position(((CursorX > (self.Grid.X + 10 + self.Grid.Width - 40)) and (self.Grid.X + 10 + self.Grid.Width - 40) or ((CursorX < (self.Grid.X + 20 - (self.Circle.Width/2))) and (self.Grid.X + 20 - (self.Circle.Width/2)) or CursorX)), ((CursorY > (self.Grid.Y + 10 + self.Grid.Height - 40)) and (self.Grid.Y + 10 + self.Grid.Height - 40) or ((CursorY < (self.Grid.Y + 20 - (self.Circle.Height/2))) and (self.Grid.Y + 20 - (self.Circle.Height/2)) or CursorY)))
+ end
+ StopSound(self.Audio.Id)
+ ReleaseSoundId(self.Audio.Id)
+ self.Pressed = false
+ end)
+ Citizen.CreateThread(function()
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(self.Grid.X + 20 + SafeZone.X, self.Grid.Y + 20 + SafeZone.Y, self.Grid.Width - 40, self.Grid.Height - 40) do
+ Citizen.Wait(75)
+ local ResultX, ResultY = math.round((self.Circle.X - (self.Grid.X + 20) + (self.Circle.Width/2))/(self.Grid.Width - 40), 2), math.round((self.Circle.Y - (self.Grid.Y + 20) + (self.Circle.Height/2))/(self.Grid.Height - 40), 2)
+
+ self:UpdateParent((((ResultX >= 0.0 and ResultX <= 1.0) and ResultX or ((ResultX <= 0) and 0.0) or 1.0) * 2) - 1, (((ResultY >= 0.0 and ResultY <= 1.0) and ResultY or ((ResultY <= 0) and 0.0) or 1.0) * 2) - 1)
+ end
+ end)
+ end
+ end
+ end
+end
+
+function UIMenuGridPanel:Draw() -- required
+ if self.Data.Enabled then
+ self.Background:Size(431 + self.ParentItem:SetParentMenu().WidthOffset, 275)
+
+ self.Background:Draw()
+ self.Grid:Draw()
+ self.Circle:Draw()
+ self.Text.Top:Draw()
+ self.Text.Left:Draw()
+ self.Text.Right:Draw()
+ self.Text.Bottom:Draw()
+ self:Functions()
+ end
+end
+
+--[[
+ UIMenuColourPanel.lua
+ Panels
+--]]
+
+UIMenuColourPanel = setmetatable({}, UIMenuColourPanel)
+UIMenuColourPanel.__index = UIMenuColourPanel
+UIMenuColourPanel.__call = function() return "UIMenuPanel", "UIMenuColourPanel" end
+
+function UIMenuColourPanel.New(Title, Colours)
+ _UIMenuColourPanel = {
+ Data = {
+ Pagination = {
+ Min = 1,
+ Max = 8,
+ Total = 8,
+ },
+ Index = 1000,
+ Items = Colours,
+ Title = Title or "Title",
+ Enabled = true,
+ Value = 1,
+ },
+ Background = Sprite.New("commonmenu", "gradient_bgd", 0, 0, 431, 112),
+ Bar = {},
+ LeftArrow = Sprite.New("commonmenu", "arrowleft", 0, 0, 30, 30),
+ RightArrow = Sprite.New("commonmenu", "arrowright", 0, 0, 30, 30),
+ SelectedRectangle = UIResRectangle.New(0, 0, 44.5, 8),
+ Text = UIResText.New(Title.." (1 of "..#Colours..")" or "Title".." (1 of "..#Colours..")", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ ParentItem = nil,
+ }
+
+ for Index = 1, #Colours do
+ if Index < 10 then
+ table.insert(_UIMenuColourPanel.Bar, UIResRectangle.New(0, 0, 44.5, 44.5, table.unpack(Colours[Index])))
+ else
+ break
+ end
+ end
+
+ if #_UIMenuColourPanel.Data.Items ~= 0 then
+ _UIMenuColourPanel.Data.Index = 1000 - (1000 % #_UIMenuColourPanel.Data.Items)
+ _UIMenuColourPanel.Data.Pagination.Max = _UIMenuColourPanel.Data.Pagination.Total + 1
+ _UIMenuColourPanel.Data.Pagination.Min = 0
+ end
+ return setmetatable(_UIMenuColourPanel, UIMenuColourPanel)
+end
+
+function UIMenuColourPanel:SetParentItem(Item) -- required
+ if Item() == "UIMenuItem" then
+ self.ParentItem = Item
+ else
+ return self.ParentItem
+ end
+end
+
+function UIMenuColourPanel:Enabled(Enabled)
+ if type(Enabled) == "boolean" then
+ self.Data.Enabled = Enabled
+ else
+ return self.Data.Enabled
+ end
+end
+
+function UIMenuColourPanel:Position(Y) -- required
+ if tonumber(Y) then
+ local ParentOffsetX, ParentOffsetWidth = self.ParentItem:Offset().X, self.ParentItem:SetParentMenu().WidthOffset
+
+ self.Background:Position(ParentOffsetX, Y)
+ for Index = 1, #self.Bar do
+ self.Bar[Index]:Position(15 + (44.5 * (Index - 1)) + ParentOffsetX + (ParentOffsetWidth/2), 55 + Y)
+ end
+ self.SelectedRectangle:Position(15 + (44.5 * ((self:CurrentSelection() - self.Data.Pagination.Min) - 1)) + ParentOffsetX + (ParentOffsetWidth/2), 47 + Y)
+ self.LeftArrow:Position(7.5 + ParentOffsetX + (ParentOffsetWidth/2), 15 + Y)
+ self.RightArrow:Position(393.5 + ParentOffsetX + (ParentOffsetWidth/2), 15 + Y)
+ self.Text:Position(215.5 + ParentOffsetX + (ParentOffsetWidth/2), 15 + Y)
+ end
+end
+
+function UIMenuColourPanel:CurrentSelection(value, PreventUpdate)
+ if tonumber(value) then
+ if #self.Data.Items == 0 then
+ self.Data.Index = 0
+ end
+
+ self.Data.Index = 1000000 - (1000000 % #self.Data.Items) + tonumber(value)
+
+ if self:CurrentSelection() > self.Data.Pagination.Max then
+ self.Data.Pagination.Min = self:CurrentSelection() - (self.Data.Pagination.Total + 1)
+ self.Data.Pagination.Max = self:CurrentSelection()
+ elseif self:CurrentSelection() < self.Data.Pagination.Min then
+ self.Data.Pagination.Min = self:CurrentSelection() - 1
+ self.Data.Pagination.Max = self:CurrentSelection() + (self.Data.Pagination.Total + 1)
+ end
+
+ self:UpdateSelection(PreventUpdate)
+ else
+ if #self.Data.Items == 0 then
+ return 1
+ else
+ if self.Data.Index % #self.Data.Items == 0 then
+ return 1
+ else
+ return self.Data.Index % #self.Data.Items + 1
+ end
+ end
+ end
+end
+
+function UIMenuColourPanel:UpdateParent(Colour)
+ local _, ParentType = self.ParentItem()
+ if ParentType == "UIMenuListItem" then
+ local PanelItemIndex = self.ParentItem:FindPanelItem()
+ local PanelIndex = self.ParentItem:FindPanelIndex(self)
+ if PanelItemIndex then
+ self.ParentItem.Items[PanelItemIndex].Value[PanelIndex] = Colour
+ self.ParentItem:Index(PanelItemIndex)
+ self.ParentItem.Base.ParentMenu.OnListChange(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ self.ParentItem.OnListChanged(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ else
+ for Index = 1, #self.ParentItem.Items do
+ if type(self.ParentItem.Items[Index]) == "table" then
+ if not self.ParentItem.Items[Index].Panels then self.ParentItem.Items[Index].Panels = {} end
+ self.ParentItem.Items[Index].Panels[PanelIndex] = Colour
+ else
+ self.ParentItem.Items[Index] = {Name = tostring(self.ParentItem.Items[Index]), Value = self.ParentItem.Items[Index], Panels = {[PanelIndex] = Colour}}
+ end
+ end
+ self.ParentItem.Base.ParentMenu.OnListChange(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ self.ParentItem.OnListChanged(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ end
+ elseif ParentType == "UIMenuItem" then
+ self.ParentItem.ActivatedPanel(self.ParentItem.ParentMenu, self.ParentItem, self, Colour)
+ end
+end
+
+function UIMenuColourPanel:UpdateSelection(PreventUpdate)
+ local CurrentSelection = self:CurrentSelection()
+ if not PreventUpdate then
+ self:UpdateParent(CurrentSelection)
+ end
+ self.SelectedRectangle:Position(15 + (44.5 * ((CurrentSelection - self.Data.Pagination.Min) - 1)) + self.ParentItem:Offset().X, self.SelectedRectangle.Y)
+ for Index = 1, 9 do
+ self.Bar[Index]:Colour(table.unpack(self.Data.Items[self.Data.Pagination.Min + Index]))
+ end
+ self.Text:Text(self.Data.Title.." ("..CurrentSelection.." of "..#self.Data.Items..")")
+end
+
+function UIMenuColourPanel:Functions()
+
+ local SafeZone = {X = 0, Y = 0}
+ if self.ParentItem:SetParentMenu().Settings.ScaleWithSafezone then
+ SafeZone = GetSafeZoneBounds()
+ end
+
+
+ if IsMouseInBounds(self.LeftArrow.X + SafeZone.X, self.LeftArrow.Y + SafeZone.Y, self.LeftArrow.Width, self.LeftArrow.Height) then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ if #self.Data.Items > self.Data.Pagination.Total + 1 then
+ if self:CurrentSelection() <= self.Data.Pagination.Min + 1 then
+ if self:CurrentSelection() == 1 then
+ self.Data.Pagination.Min = #self.Data.Items - (self.Data.Pagination.Total + 1)
+ self.Data.Pagination.Max = #self.Data.Items
+ self.Data.Index = 1000 - (1000 % #self.Data.Items)
+ self.Data.Index = self.Data.Index + (#self.Data.Items - 1)
+ self:UpdateSelection()
+ else
+ self.Data.Pagination.Min = self.Data.Pagination.Min - 1
+ self.Data.Pagination.Max = self.Data.Pagination.Max - 1
+ self.Data.Index = self.Data.Index - 1
+ self:UpdateSelection()
+ end
+ else
+ self.Data.Index = self.Data.Index - 1
+ self:UpdateSelection()
+ end
+ else
+ self.Data.Index = self.Data.Index - 1
+ self:UpdateSelection()
+ end
+ end
+ end
+
+ if IsMouseInBounds(self.RightArrow.X + SafeZone.X, self.RightArrow.Y + SafeZone.Y, self.RightArrow.Width, self.RightArrow.Height) then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ if #self.Data.Items > self.Data.Pagination.Total + 1 then
+ if self:CurrentSelection() >= self.Data.Pagination.Max then
+ if self:CurrentSelection() == #self.Data.Items then
+ self.Data.Pagination.Min = 0
+ self.Data.Pagination.Max = self.Data.Pagination.Total + 1
+ self.Data.Index = 1000 - (1000 % #self.Data.Items)
+ self:UpdateSelection()
+ else
+ self.Data.Pagination.Max = self.Data.Pagination.Max + 1
+ self.Data.Pagination.Min = self.Data.Pagination.Max - (self.Data.Pagination.Total + 1)
+ self.Data.Index = self.Data.Index + 1
+ self:UpdateSelection()
+ end
+ else
+ self.Data.Index = self.Data.Index + 1
+ self:UpdateSelection()
+ end
+ else
+ self.Data.Index = self.Data.Index + 1
+ self:UpdateSelection()
+ end
+ end
+ end
+
+ for Index = 1, #self.Bar do
+ if IsMouseInBounds(self.Bar[Index].X + SafeZone.X, self.Bar[Index].Y + SafeZone.Y, self.Bar[Index].Width, self.Bar[Index].Height) then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ self:CurrentSelection(self.Data.Pagination.Min + Index - 1)
+ end
+ end
+ end
+end
+
+function UIMenuColourPanel:Draw() -- required
+ if self.Data.Enabled then
+ self.Background:Size(431 + self.ParentItem:SetParentMenu().WidthOffset, 112)
+
+ self.Background:Draw()
+ self.LeftArrow:Draw()
+ self.RightArrow:Draw()
+ self.Text:Draw()
+ self.SelectedRectangle:Draw()
+ for Index = 1, #self.Bar do
+ self.Bar[Index]:Draw()
+ end
+ self:Functions()
+ end
+end
+
+--[[
+ UIMenuPercentagePanel.lua
+ Panels
+--]]
+
+UIMenuPercentagePanel = setmetatable({}, UIMenuPercentagePanel)
+UIMenuPercentagePanel.__index = UIMenuPercentagePanel
+UIMenuPercentagePanel.__call = function() return "UIMenuPanel", "UIMenuPercentagePanel" end
+
+function UIMenuPercentagePanel.New(MinText, MaxText)
+ _UIMenuPercentagePanel = {
+ Data = {
+ Enabled = true,
+ },
+ Background = Sprite.New("commonmenu", "gradient_bgd", 0, 0, 431, 76),
+ ActiveBar = UIResRectangle.New(0, 0, 413, 10, 245, 245, 245, 255),
+ BackgroundBar = UIResRectangle.New(0, 0, 413, 10, 87, 87, 87, 255),
+ Text = {
+ Min = UIResText.New(MinText or "0%", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ Max = UIResText.New("100%", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ Title = UIResText.New(MaxText or "Opacity", 0, 0, 0.35, 255, 255, 255, 255, 0, "Centre"),
+ },
+ Audio = {Slider = "CONTINUOUS_SLIDER", Library = "HUD_FRONTEND_DEFAULT_SOUNDSET", Id = nil},
+ ParentItem = nil,
+ }
+
+ return setmetatable(_UIMenuPercentagePanel, UIMenuPercentagePanel)
+end
+
+function UIMenuPercentagePanel:SetParentItem(Item) -- required
+ if Item() == "UIMenuItem" then
+ self.ParentItem = Item
+ else
+ return self.ParentItem
+ end
+end
+
+function UIMenuPercentagePanel:Enabled(Enabled)
+ if type(Enabled) == "boolean" then
+ self.Data.Enabled = Enabled
+ else
+ return self.Data.Enabled
+ end
+end
+
+function UIMenuPercentagePanel:Position(Y) -- required
+ if tonumber(Y) then
+ local ParentOffsetX, ParentOffsetWidth = self.ParentItem:Offset().X, self.ParentItem:SetParentMenu().WidthOffset
+ self.Background:Position(ParentOffsetX, Y)
+ self.ActiveBar:Position(ParentOffsetX + (ParentOffsetWidth/2) + 9, 50 + Y)
+ self.BackgroundBar:Position(ParentOffsetX + (ParentOffsetWidth/2) + 9, 50 + Y)
+ self.Text.Min:Position(ParentOffsetX + (ParentOffsetWidth/2) + 25, 15 + Y)
+ self.Text.Max:Position(ParentOffsetX + (ParentOffsetWidth/2) + 398, 15 + Y)
+ self.Text.Title:Position(ParentOffsetX + (ParentOffsetWidth/2) + 215.5, 15 + Y)
+ end
+end
+
+function UIMenuPercentagePanel:Percentage(Value)
+ if tonumber(Value) then
+ local Percent = ((Value < 0.0) and 0.0) or ((Value > 1.0) and 1.0 or Value)
+ self.ActiveBar:Size(self.BackgroundBar.Width * Percent, self.ActiveBar.Height)
+ else
+ local SafeZone = {X = 0, Y = 0}
+ if self.ParentItem:SetParentMenu().Settings.ScaleWithSafezone then
+ SafeZone = GetSafeZoneBounds()
+ end
+
+ local Progress = (math.round(GetControlNormal(0, 239) * 1920) - SafeZone.X) - self.ActiveBar.X
+ return math.round(((Progress >= 0 and Progress <= 413) and Progress or ((Progress < 0) and 0 or 413))/self.BackgroundBar.Width, 2)
+ end
+end
+
+function UIMenuPercentagePanel:UpdateParent(Percentage)
+ local _, ParentType = self.ParentItem()
+ if ParentType == "UIMenuListItem" then
+ local PanelItemIndex = self.ParentItem:FindPanelItem()
+ if PanelItemIndex then
+ self.ParentItem.Items[PanelItemIndex].Value[self.ParentItem:FindPanelIndex(self)] = Percentage
+ self.ParentItem:Index(PanelItemIndex)
+ self.ParentItem.Base.ParentMenu.OnListChange(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ self.ParentItem.OnListChanged(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ else
+ local PanelIndex = self.ParentItem:FindPanelIndex(self)
+ for Index = 1, #self.ParentItem.Items do
+ if type(self.ParentItem.Items[Index]) == "table" then
+ if not self.ParentItem.Items[Index].Panels then self.ParentItem.Items[Index].Panels = {} end
+ self.ParentItem.Items[Index].Panels[PanelIndex] = Percentage
+ else
+ self.ParentItem.Items[Index] = {Name = tostring(self.ParentItem.Items[Index]), Value = self.ParentItem.Items[Index], Panels = {[PanelIndex] = Percentage}}
+ end
+ end
+ self.ParentItem.Base.ParentMenu.OnListChange(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ self.ParentItem.OnListChanged(self.ParentItem.Base.ParentMenu, self.ParentItem, self.ParentItem._Index)
+ end
+ elseif ParentType == "UIMenuItem" then
+ self.ParentItem.ActivatedPanel(self.ParentItem.ParentMenu, self.ParentItem, self, Percentage)
+ end
+end
+
+function UIMenuPercentagePanel:Functions()
+
+ local SafeZone = {X = 0, Y = 0}
+ if self.ParentItem:SetParentMenu().Settings.ScaleWithSafezone then
+ SafeZone = GetSafeZoneBounds()
+ end
+
+ if IsMouseInBounds(self.BackgroundBar.X + SafeZone.X, self.BackgroundBar.Y - 4 + SafeZone.Y, self.BackgroundBar.Width, self.BackgroundBar.Height + 8) then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ if not self.Pressed then
+ self.Pressed = true
+ Citizen.CreateThread(function()
+ self.Audio.Id = GetSoundId()
+ PlaySoundFrontend(self.Audio.Id, self.Audio.Slider, self.Audio.Library, 1)
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(self.BackgroundBar.X + SafeZone.X, self.BackgroundBar.Y - 4 + SafeZone.Y, self.BackgroundBar.Width, self.BackgroundBar.Height + 8) do
+ Citizen.Wait(0)
+ local Progress = (math.round(GetControlNormal(0, 239) * 1920) - SafeZone.X) - self.ActiveBar.X
+ self.ActiveBar:Size(((Progress >= 0 and Progress <= 413) and Progress or ((Progress < 0) and 0 or 413)), self.ActiveBar.Height)
+ end
+ StopSound(self.Audio.Id)
+ ReleaseSoundId(self.Audio.Id)
+ self.Pressed = false
+ end)
+ Citizen.CreateThread(function()
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(self.BackgroundBar.X + SafeZone.X, self.BackgroundBar.Y - 4 + SafeZone.Y, self.BackgroundBar.Width, self.BackgroundBar.Height + 8) do
+ Citizen.Wait(75)
+ local Progress = (math.round(GetControlNormal(0, 239) * 1920) - SafeZone.X) - self.ActiveBar.X
+ self:UpdateParent(math.round(((Progress >= 0 and Progress <= 413) and Progress or ((Progress < 0) and 0 or 413))/self.BackgroundBar.Width, 2))
+ end
+ end)
+ end
+ end
+ end
+end
+
+function UIMenuPercentagePanel:Draw() -- required
+ if self.Data.Enabled then
+ self.Background:Size(431 + self.ParentItem:SetParentMenu().WidthOffset, 76)
+ self.Background:Draw()
+ self.BackgroundBar:Draw()
+ self.ActiveBar:Draw()
+ self.Text.Min:Draw()
+ self.Text.Max:Draw()
+ self.Text.Title:Draw()
+ self:Functions()
+ end
+end
+
+--[[
+ UIMenu.lua
+ Menus
+--]]
+
+function UIMenu.New(Title, Subtitle, X, Y, TxtDictionary, TxtName, titleTxt)
+ local X, Y = tonumber(X) or 0, tonumber(Y) or 0
+ if Title ~= nil then Title = tostring(Title) or "" else Title = "" end
+ if Subtitle ~= nil then Subtitle = tostring(Subtitle) or "" else Subtitle = "" end
+ if TxtDictionary ~= nil then TxtDictionary = tostring(TxtDictionary) or "commonmenu" else TxtDictionary = "commonmenu" end
+ if TxtName ~= nil then TxtName = tostring(TxtName) or "interaction_bgd" else TxtName = "interaction_bgd" end
+ if titleTxt then
+ thisTitle = Sprite.New(TxtDictionary, titleTxt, 0 + X, 0 + Y, 431, 107)
+ else
+ thisTitle = UIResText.New(Title, 215 + X, 20 + Y, 1.15, 255, 255, 255, 255, 1, 1)
+ end
+ local _UIMenu = {
+ Logo = Sprite.New(TxtDictionary, TxtName, 0 + X, 0 + Y, 431, 107),
+ Banner = nil,
+ Title = thisTitle,
+ Subtitle = {ExtraY = 0},
+ WidthOffset = 0,
+ Position = {X = X, Y = Y},
+ Pagination = {Min = 0, Max = 9, Total = 9},
+ PageCounter = {PreText = ""},
+ Extra = {},
+ Description = {},
+ Items = {},
+ Windows = {},
+ Children = {},
+ TxtDictionary = TxtDictionary,
+ TxtName = TxtName,
+ titleTxt = titleTxt,
+ Controls = {
+ Back = {
+ Enabled = true,
+ },
+ Select = {
+ Enabled = true,
+ },
+ Left = {
+ Enabled = true,
+ },
+ Right = {
+ Enabled = true,
+ },
+ Up = {
+ Enabled = true,
+ },
+ Down = {
+ Enabled = true,
+ },
+ },
+ ParentMenu = nil,
+ ParentItem = nil,
+ _Visible = false,
+ ActiveItem = 1000,
+ Dirty = false;
+ ReDraw = true,
+ InstructionalScaleform = RequestScaleformMovie("INSTRUCTIONAL_BUTTONS"),
+ InstructionalButtons = {},
+ OnIndexChange = function(menu, newindex) end,
+ OnListChange = function(menu, list, newindex) end,
+ OnSliderChange = function(menu, slider, newindex) end,
+ OnProgressChange = function(menu, progress, newindex) end,
+ OnCheckboxChange = function(menu, item, checked) end,
+ OnListSelect = function(menu, list, index) end,
+ OnSliderSelect = function(menu, slider, index) end,
+ OnProgressSelect = function(menu, progress, index) end,
+ OnItemSelect = function(menu, item, index) end,
+ OnMenuChanged = function(menu, newmenu, forward) end,
+ OnMenuClosed = function(menu) end,
+ Settings = {
+ InstructionalButtons = true,
+ MultilineFormats = true,
+ ScaleWithSafezone = true,
+ ResetCursorOnOpen = true,
+ MouseControlsEnabled = true,
+ MouseEdgeEnabled = true,
+ ControlDisablingEnabled = true,
+ Audio = {
+ Library = "HUD_FRONTEND_DEFAULT_SOUNDSET",
+ UpDown = "NAV_UP_DOWN",
+ LeftRight = "NAV_LEFT_RIGHT",
+ Select = "SELECT",
+ Back = "BACK",
+ Error = "ERROR",
+ },
+ EnabledControls = {
+ Controller = {
+ {0, 2}, -- Look Up and Down
+ {0, 1}, -- Look Left and Right
+ {0, 25}, -- Aim
+ {0, 24}, -- Attack
+ },
+ Keyboard = {
+ {0, 201}, -- Select
+ {0, 195}, -- X axis
+ {0, 196}, -- Y axis
+ {0, 187}, -- Down
+ {0, 188}, -- Up
+ {0, 189}, -- Left
+ {0, 190}, -- Right
+ {0, 202}, -- Back
+ {0, 217}, -- Select
+ {0, 242}, -- Scroll down
+ {0, 241}, -- Scroll up
+ {0, 239}, -- Cursor X
+ {0, 240}, -- Cursor Y
+ {0, 31}, -- Move Up and Down
+ {0, 30}, -- Move Left and Right
+ {0, 21}, -- Sprint
+ {0, 22}, -- Jump
+ {0, 23}, -- Enter
+ {0, 75}, -- Exit Vehicle
+ {0, 71}, -- Accelerate Vehicle
+ {0, 72}, -- Vehicle Brake
+ {0, 59}, -- Move Vehicle Left and Right
+ {0, 89}, -- Fly Yaw Left
+ {0, 9}, -- Fly Left and Right
+ {0, 8}, -- Fly Up and Down
+ {0, 90}, -- Fly Yaw Right
+ {0, 76}, -- Vehicle Handbrake
+ },
+ }
+ }
+ }
+
+ if Subtitle ~= "" and Subtitle ~= nil then
+ _UIMenu.Subtitle.Rectangle = UIResRectangle.New(0 + _UIMenu.Position.X, 107 + _UIMenu.Position.Y, 431, 37, 0, 0, 0, 255)
+ _UIMenu.Subtitle.Text = UIResText.New(Subtitle, 8 + _UIMenu.Position.X, 110 + _UIMenu.Position.Y, 0.35, 245, 245, 245, 255, 0)
+ _UIMenu.Subtitle.BackupText = Subtitle
+ _UIMenu.Subtitle.Formatted = false
+ if string.starts(Subtitle, "~") then
+ _UIMenu.PageCounter.PreText = string.sub(Subtitle, 1, 3)
+ end
+ _UIMenu.PageCounter.Text = UIResText.New("", 425 + _UIMenu.Position.X, 110 + _UIMenu.Position.Y, 0.35, 245, 245, 245, 255, 0, "Right")
+ _UIMenu.Subtitle.ExtraY = 37
+ end
+
+ _UIMenu.ArrowSprite = Sprite.New("commonmenu", "shop_arrows_upanddown", 190 + _UIMenu.Position.X, 147 + 37 * (_UIMenu.Pagination.Total + 1) + _UIMenu.Position.Y - 37 + _UIMenu.Subtitle.ExtraY, 50, 50)
+ _UIMenu.Extra.Up = UIResRectangle.New(0 + _UIMenu.Position.X, 144 + 38 * (_UIMenu.Pagination.Total + 1) + _UIMenu.Position.Y - 37 + _UIMenu.Subtitle.ExtraY, 431, 18, 0, 0, 0, 200)
+ _UIMenu.Extra.Down = UIResRectangle.New(0 + _UIMenu.Position.X, 144 + 18 + 38 * (_UIMenu.Pagination.Total + 1) + _UIMenu.Position.Y - 37 + _UIMenu.Subtitle.ExtraY, 431, 18, 0, 0, 0, 200)
+
+ _UIMenu.Description.Bar = UIResRectangle.New(_UIMenu.Position.X, 123, 431, 4, 0, 0, 0, 255)
+ _UIMenu.Description.Rectangle = Sprite.New("commonmenu", "gradient_bgd", _UIMenu.Position.X, 127, 431, 30)
+ _UIMenu.Description.Text = UIResText.New("Description", _UIMenu.Position.X + 5, 125, 0.35)
+
+ _UIMenu.Background = Sprite.New("commonmenu", "gradient_bgd", _UIMenu.Position.X, 144 + _UIMenu.Position.Y - 37 + _UIMenu.Subtitle.ExtraY, 290, 25)
+
+ Citizen.CreateThread(function()
+ if not HasScaleformMovieLoaded(_UIMenu.InstructionalScaleform) then
+ _UIMenu.InstructionalScaleform = RequestScaleformMovie("INSTRUCTIONAL_BUTTONS")
+ while not HasScaleformMovieLoaded(_UIMenu.InstructionalScaleform) do
+ Citizen.Wait(0)
+ end
+ end
+ end)
+ return setmetatable(_UIMenu, UIMenu)
+end
+
+function UIMenu:SetMenuWidthOffset(Offset)
+ if tonumber(Offset) then
+ self.WidthOffset = math.floor(tonumber(Offset))
+ self.Logo:Size(431 + self.WidthOffset, 107)
+ if self.Title.TxtName then
+ self.Title:Position(((self.WidthOffset + 0)/2) + self.Position.X, 0 + self.Position.Y)
+ else
+ self.Title:Position(((self.WidthOffset + 431)/2) + self.Position.X, 20 + self.Position.Y)
+ end
+ if self.Subtitle.Rectangle ~= nil then
+ self.Subtitle.Rectangle:Size(431 + self.WidthOffset + 100, 37)
+ self.PageCounter.Text:Position(425 + self.Position.X + self.WidthOffset, 110 + self.Position.Y)
+ end
+ if self.Banner ~= nil then
+ self.Banner:Size(431 + self.WidthOffset, 107)
+ end
+ end
+end
+
+function UIMenu:DisEnableControls(bool)
+ if bool then
+ EnableAllControlActions(2)
+ else
+ DisableAllControlActions(2)
+ end
+
+ if bool then
+ return
+ else
+ if Controller() then
+ for Index = 1, #self.Settings.EnabledControls.Controller do
+ EnableControlAction(self.Settings.EnabledControls.Controller[Index][1], self.Settings.EnabledControls.Controller[Index][2], true)
+ end
+ else
+ for Index = 1, #self.Settings.EnabledControls.Keyboard do
+ EnableControlAction(self.Settings.EnabledControls.Keyboard[Index][1], self.Settings.EnabledControls.Keyboard[Index][2], true)
+ end
+ end
+ end
+end
+
+function UIMenu:InstructionalButtons(bool)
+ if bool ~= nil then
+ self.Settings.InstrucitonalButtons = tobool(bool)
+ end
+end
+
+function UIMenu:SetBannerSprite(Sprite, IncludeChildren)
+ if Sprite() == "Sprite" then
+ self.Logo = Sprite
+ self.Logo:Size(431 + self.WidthOffset, 107)
+ self.Logo:Position(self.Position.X, self.Position.Y)
+ self.Banner = nil
+ if IncludeChildren then
+ for Item, Menu in pairs(self.Children) do
+ Menu.Logo = Sprite
+ Menu.Logo:Size(431 + self.WidthOffset, 107)
+ Menu.Logo:Position(self.Position.X, self.Position.Y)
+ Menu.Banner = nil
+ end
+ end
+ end
+end
+
+function UIMenu:SetBannerRectangle(Rectangle, IncludeChildren)
+ if Rectangle() == "Rectangle" then
+ self.Banner = Rectangle
+ self.Banner:Size(431 + self.WidthOffset, 107)
+ self.Banner:Position(self.Position.X, self.Position.Y)
+ self.Logo = nil
+ if IncludeChildren then
+ for Item, Menu in pairs(self.Children) do
+ Menu.Banner = Rectangle
+ Menu.Banner:Size(431 + self.WidthOffset, 107)
+ Menu:Position(self.Position.X, self.Position.Y)
+ Menu.Logo = nil
+ end
+ end
+ end
+end
+
+function UIMenu:CurrentSelection(value)
+ if tonumber(value) then
+ if #self.Items == 0 then
+ self.ActiveItem = 0
+ end
+
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = 1000000 - (1000000 % #self.Items) + tonumber(value)
+
+ if self:CurrentSelection() > self.Pagination.Max then
+ self.Pagination.Min = self:CurrentSelection() - self.Pagination.Total
+ self.Pagination.Max = self:CurrentSelection()
+ elseif self:CurrentSelection() < self.Pagination.Min then
+ self.Pagination.Min = self:CurrentSelection()
+ self.Pagination.Max = self:CurrentSelection() + self.Pagination.Total
+ end
+ else
+ if #self.Items == 0 then
+ return 1
+ else
+ if self.ActiveItem % #self.Items == 0 then
+ return 1
+ else
+ return self.ActiveItem % #self.Items + 1
+ end
+ end
+ end
+end
+
+function UIMenu:CalculateWindowHeight()
+ local Height = 0
+ for i = 1, #self.Windows do
+ Height = Height + self.Windows[i].Background:Size().Height
+ end
+ return Height
+end
+
+function UIMenu:CalculateItemHeightOffset(Item)
+ if Item.Base then
+ return Item.Base.Rectangle.Height
+ else
+ return Item.Rectangle.Height
+ end
+end
+
+function UIMenu:CalculateItemHeight()
+ local ItemOffset = 0 + self.Subtitle.ExtraY - 37
+ for i = self.Pagination.Min + 1, self.Pagination.Max do
+ local Item = self.Items[i]
+ if Item ~= nil then
+ ItemOffset = ItemOffset + self:CalculateItemHeightOffset(Item)
+ end
+ end
+ return ItemOffset
+end
+
+function UIMenu:RecalculateDescriptionPosition()
+ local WindowHeight = self:CalculateWindowHeight()
+ self.Description.Bar:Position(self.Position.X, 149 + self.Position.Y + WindowHeight)
+ self.Description.Rectangle:Position(self.Position.X, 149 + self.Position.Y + WindowHeight)
+ self.Description.Text:Position(self.Position.X + 8, 155 + self.Position.Y + WindowHeight)
+
+ self.Description.Bar:Size(431 + self.WidthOffset, 4)
+ self.Description.Rectangle:Size(431 + self.WidthOffset, 30)
+
+ self.Description.Bar:Position(self.Position.X, self:CalculateItemHeight() + ((#self.Items > (self.Pagination.Total + 1)) and 37 or 0) + self.Description.Bar:Position().Y)
+ self.Description.Rectangle:Position(self.Position.X, self:CalculateItemHeight() + ((#self.Items > (self.Pagination.Total + 1)) and 37 or 0) + self.Description.Rectangle:Position().Y)
+ self.Description.Text:Position(self.Position.X + 8, self:CalculateItemHeight() + ((#self.Items > (self.Pagination.Total + 1)) and 37 or 0) + self.Description.Text:Position().Y)
+end
+
+function UIMenu:CaclulatePanelPosition(HasDescription)
+ local Height = self:CalculateWindowHeight() + 149 + self.Position.Y
+
+ if HasDescription then
+ Height = Height + self.Description.Rectangle:Size().Height + 5
+ end
+
+ return self:CalculateItemHeight() + ((#self.Items > (self.Pagination.Total + 1)) and 37 or 0) + Height
+end
+
+function UIMenu:AddWindow(Window)
+ if Window() == "UIMenuWindow" then
+ Window:SetParentMenu(self)
+ Window:Offset(self.Position.X, self.Position.Y)
+ table.insert(self.Windows, Window)
+ self.ReDraw = true
+ self:RecalculateDescriptionPosition()
+ end
+end
+
+function UIMenu:RemoveWindowAt(Index)
+ if tonumber(Index) then
+ if self.Windows[Index] then
+ table.remove(self.Windows, Index)
+ self.ReDraw = true
+ self:RecalculateDescriptionPosition()
+ end
+ end
+end
+
+function UIMenu:AddItem(Item)
+ if Item() == "UIMenuItem" then
+ local SelectedItem = self:CurrentSelection()
+ Item:SetParentMenu(self)
+ Item:Offset(self.Position.X, self.Position.Y)
+ Item:Position((#self.Items * 25) - 37 + self.Subtitle.ExtraY)
+ table.insert(self.Items, Item)
+ self:RecalculateDescriptionPosition()
+ self:CurrentSelection(SelectedItem)
+ end
+end
+
+function UIMenu:RemoveItemAt(Index)
+ if tonumber(Index) then
+ if self.Items[Index] then
+ local SelectedItem = self:CurrentSelection()
+ if #self.Items > self.Pagination.Total and self.Pagination.Max == #self.Items - 1 then
+ self.Pagination.Min = self.Pagination.Min - 1
+ self.Pagination.Max = self.Pagination.Max + 1
+ end
+ table.remove(self.Items, tonumber(Index))
+ self:RecalculateDescriptionPosition()
+ self:CurrentSelection(SelectedItem)
+ end
+ end
+end
+
+function UIMenu:RefreshIndex()
+ if #self.Items == 0 then
+ self.ActiveItem = 1000
+ self.Pagination.Max = self.Pagination.Total + 1
+ self.Pagination.Min = 0
+ return
+ end
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = 1000 - (1000 % #self.Items)
+ self.Pagination.Max = self.Pagination.Total + 1
+ self.Pagination.Min = 0
+ self.ReDraw = true
+end
+
+function UIMenu:Clear()
+ self.Items = {}
+ self.ReDraw = true
+ self:RecalculateDescriptionPosition()
+end
+
+function UIMenu:MultilineFormat(str)
+ if tostring(str) then
+
+ local PixelPerLine = 425 + self.WidthOffset
+ local AggregatePixels = 0
+ local output = ""
+ local words = string.split(tostring(str), " ")
+
+ for i = 1, #words do
+ local offset = MeasureStringWidth(words[i], 0, 0.35)
+ AggregatePixels = AggregatePixels + offset
+ if AggregatePixels > PixelPerLine then
+ output = output .. "\n" .. words[i] .. " "
+ AggregatePixels = offset + MeasureString(" ")
+ else
+ output = output .. words[i] .. " "
+ AggregatePixels = AggregatePixels + MeasureString(" ")
+ end
+ end
+ return output
+ end
+end
+
+function UIMenu:DrawCalculations()
+ local WindowHeight = self:CalculateWindowHeight()
+
+ if self.Settings.MultilineFormats then
+ if self.Subtitle.Rectangle and not self.Subtitle.Formatted then
+ self.Subtitle.Formatted = true
+ self.Subtitle.Text:Text(self:MultilineFormat(self.Subtitle.Text:Text()))
+
+ local Linecount = #string.split(self.Subtitle.Text:Text(), "\n")
+ self.Subtitle.ExtraY = ((Linecount == 1) and 37 or ((Linecount + 1) * 22))
+ self.Subtitle.Rectangle:Size(431 + self.WidthOffset, self.Subtitle.ExtraY)
+ end
+ elseif self.Subtitle.Formatted then
+ self.Subtitle.Formatted = false
+ self.Subtitle.ExtraY = 37
+ self.Subtitle.Rectangle:Size(431 + self.WidthOffset, self.Subtitle.ExtraY)
+ self.Subtitle.Text:Text(self.Subtitle.BackupText)
+ end
+
+ self.Background:Size(431 + self.WidthOffset, self:CalculateItemHeight() + WindowHeight + ((self.Subtitle.ExtraY > 0) and 0 or 37))
+
+ self.Extra.Up:Size(431 + self.WidthOffset, 18)
+ self.Extra.Down:Size(431 + self.WidthOffset, 18)
+
+ self.Extra.Up:Position(self.Position.X, 144 + self:CalculateItemHeight() + self.Position.Y + WindowHeight)
+ self.Extra.Down:Position(self.Position.X, 144 + 18 + self:CalculateItemHeight() + self.Position.Y + WindowHeight)
+
+ if self.WidthOffset > 0 then
+ self.ArrowSprite:Position(190 + self.Position.X + (self.WidthOffset / 2), 137 + self:CalculateItemHeight() + self.Position.Y + WindowHeight)
+ else
+ self.ArrowSprite:Position(190 + self.Position.X + self.WidthOffset, 137 + self:CalculateItemHeight() + self.Position.Y + WindowHeight)
+ end
+
+ self.ReDraw = false
+
+ if #self.Items ~= 0 and self.Items[self:CurrentSelection()]:Description() ~= "" then
+ self:RecalculateDescriptionPosition()
+
+ local description = self.Items[self:CurrentSelection()]:Description()
+ if self.Settings.MultilineFormats then
+ self.Description.Text:Text(self:MultilineFormat(description))
+ else
+ self.Description.Text:Text(description)
+ end
+
+ local Linecount = #string.split(self.Description.Text:Text(), "\n")
+ self.Description.Rectangle:Size(431 + self.WidthOffset, ((Linecount == 1) and 37 or ((Linecount + 1) * 22)))
+ end
+end
+
+function UIMenu:Visible(bool)
+ if bool ~= nil then
+ menuOpen = bool
+ self._Visible = tobool(bool)
+ self.JustOpened = tobool(bool)
+ self.Dirty = tobool(bool)
+ self:UpdateScaleform()
+ if self.ParentMenu ~= nil or tobool(bool) == false then
+ return
+ end
+ if self.Settings.ResetCursorOnOpen then
+ local W, H = GetScreenResolution()
+ SetCursorLocation(W / 2, H / 2)
+ SetCursorSprite(1)
+ end
+ collectgarbage()
+ else
+ return self._Visible
+ end
+end
+
+function UIMenu:ProcessControl()
+ if not self._Visible then
+ return
+ end
+
+ if self.JustOpened then
+ self.JustOpened = false
+ return
+ end
+
+ if self.Controls.Back.Enabled and (IsDisabledControlJustReleased(2, 177) or IsDisabledControlJustReleased(2, 199) ) and IsInputDisabled(0) then
+ self:GoBack()
+ end
+
+ if #self.Items == 0 then
+ return
+ end
+
+ if not self.UpPressed then
+ if self.Controls.Up.Enabled and (IsDisabledControlJustPressed(1, 172) or IsDisabledControlJustPressed(1, 241)) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ self.UpPressed = true
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoUpOverflow()
+ else
+ self:GoUp()
+ end
+ self:UpdateScaleform()
+ Citizen.Wait(175)
+ while self.Controls.Up.Enabled and (IsDisabledControlPressed(2, 172) or IsDisabledControlPressed(2, 241)) and IsInputDisabled(0) do
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoUpOverflow()
+ else
+ self:GoUp()
+ end
+ self:UpdateScaleform()
+ Citizen.Wait(125)
+ end
+ self.UpPressed = false
+ end)
+ end
+ end
+
+ if not self.DownPressed then
+ if self.Controls.Down.Enabled and (IsDisabledControlJustPressed(1, 173) or IsDisabledControlJustPressed(1, 242)) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ self.DownPressed = true
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoDownOverflow()
+ else
+ self:GoDown()
+ end
+ self:UpdateScaleform()
+ Citizen.Wait(175)
+ while self.Controls.Down.Enabled and (IsDisabledControlPressed(2, 173) or IsDisabledControlPressed(2, 242)) and IsInputDisabled(0) do
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoDownOverflow()
+ else
+ self:GoDown()
+ end
+ self:UpdateScaleform()
+ Citizen.Wait(125)
+ end
+ self.DownPressed = false
+ end)
+ end
+ end
+
+ if not self.LeftPressed then
+ if self.Controls.Left.Enabled and (IsDisabledControlPressed(2, 174)) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ if not self.LeftPressed then
+ self.LeftPressed = true
+ self:GoLeft()
+ Citizen.Wait(150)
+ while self.Controls.Left.Enabled and (IsDisabledControlPressed(2, 174)) and IsInputDisabled(0) do
+ self:GoLeft()
+ Citizen.Wait(200)
+ end
+ self.LeftPressed = false
+ end
+ end)
+ end
+ end
+
+ if not self.RightPressed then
+ if self.Controls.Right.Enabled and (IsDisabledControlPressed(2, 175)) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ if not self.RightPressed then
+ self.RightPressed = true
+ self:GoRight()
+ Citizen.Wait(150)
+ while self.Controls.Right.Enabled and (IsDisabledControlPressed(2, 175)) and IsInputDisabled(0) do
+ self:GoRight()
+ Citizen.Wait(200)
+ end
+ self.RightPressed = false
+ end
+ end)
+ end
+ end
+
+ if self.Controls.Select.Enabled and (IsDisabledControlJustPressed(1, 201) and IsInputDisabled(0)) then
+ self:SelectItem()
+ end
+end
+
+function UIMenu:GoUpOverflow()
+ if #self.Items <= self.Pagination.Total + 1 then
+ return
+ end
+
+ if self:CurrentSelection() <= self.Pagination.Min + 1 then
+ if self:CurrentSelection() == 1 then
+ self.Pagination.Min = #self.Items - (self.Pagination.Total + 1)
+ self.Pagination.Max = #self.Items
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = 1000 - (1000 % #self.Items)
+ self.ActiveItem = self.ActiveItem + (#self.Items - 1)
+ self.Items[self:CurrentSelection()]:Selected(true)
+ else
+ self.Pagination.Min = self.Pagination.Min - 1
+ self.Pagination.Max = self.Pagination.Max - 1
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = self.ActiveItem - 1
+ self.Items[self:CurrentSelection()]:Selected(true)
+ end
+ else
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = self.ActiveItem - 1
+ self.Items[self:CurrentSelection()]:Selected(true)
+ end
+ PlaySoundFrontend(-1, self.Settings.Audio.UpDown, self.Settings.Audio.Library, true)
+ self.OnIndexChange(self, self:CurrentSelection())
+ self.ReDraw = true
+end
+
+function UIMenu:GoUp()
+ if #self.Items > self.Pagination.Total + 1 then
+ return
+ end
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = self.ActiveItem - 1
+ self.Items[self:CurrentSelection()]:Selected(true)
+ PlaySoundFrontend(-1, self.Settings.Audio.UpDown, self.Settings.Audio.Library, true)
+ self.OnIndexChange(self, self:CurrentSelection())
+ self.ReDraw = true
+end
+
+function UIMenu:GoDownOverflow()
+ if #self.Items <= self.Pagination.Total + 1 then
+ return
+ end
+
+ if self:CurrentSelection() >= self.Pagination.Max then
+ if self:CurrentSelection() == #self.Items then
+ self.Pagination.Min = 0
+ self.Pagination.Max = self.Pagination.Total + 1
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = 1000 - (1000 % #self.Items)
+ self.Items[self:CurrentSelection()]:Selected(true)
+ else
+ self.Pagination.Max = self.Pagination.Max + 1
+ self.Pagination.Min = self.Pagination.Max - (self.Pagination.Total + 1)
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = self.ActiveItem + 1
+ self.Items[self:CurrentSelection()]:Selected(true)
+ end
+ else
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = self.ActiveItem + 1
+ self.Items[self:CurrentSelection()]:Selected(true)
+ end
+ PlaySoundFrontend(-1, self.Settings.Audio.UpDown, self.Settings.Audio.Library, true)
+ self.OnIndexChange(self, self:CurrentSelection())
+ self.ReDraw = true
+end
+
+function UIMenu:GoDown()
+ if #self.Items > self.Pagination.Total + 1 then
+ return
+ end
+
+ self.Items[self:CurrentSelection()]:Selected(false)
+ self.ActiveItem = self.ActiveItem + 1
+ self.Items[self:CurrentSelection()]:Selected(true)
+ PlaySoundFrontend(-1, self.Settings.Audio.UpDown, self.Settings.Audio.Library, true)
+ self.OnIndexChange(self, self:CurrentSelection())
+ self.ReDraw = true
+end
+
+function UIMenu:GoLeft()
+ local type, subtype = self.Items[self:CurrentSelection()]()
+ if subtype ~= "UIMenuListItem" and subtype ~= "UIMenuSliderItem" and subtype ~= "UIMenuProgressItem" then
+ return
+ end
+
+ if not self.Items[self:CurrentSelection()]:Enabled() then
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ return
+ end
+
+ if subtype == "UIMenuListItem" then
+ local Item = self.Items[self:CurrentSelection()]
+ Item:Index(Item._Index - 1)
+ self.OnListChange(self, Item, Item._Index)
+ Item.OnListChanged(self, Item, Item._Index)
+ PlaySoundFrontend(-1, self.Settings.Audio.LeftRight, self.Settings.Audio.Library, true)
+ elseif subtype == "UIMenuSliderItem" then
+ local Item = self.Items[self:CurrentSelection()]
+ Item:Index(Item._Index - 1)
+ self.OnSliderChange(self, Item, Item:Index())
+ Item.OnSliderChanged(self, Item, Item._Index)
+ PlaySoundFrontend(-1, self.Settings.Audio.LeftRight, self.Settings.Audio.Library, true)
+ elseif subtype == "UIMenuProgressItem" then
+ local Item = self.Items[self:CurrentSelection()]
+ Item:Index(Item.Data.Index - 1)
+ self.OnProgressChange(self, Item, Item.Data.Index)
+ Item.OnProgressChanged(self, Item, Item.Data.Index)
+ PlaySoundFrontend(-1, self.Settings.Audio.LeftRight, self.Settings.Audio.Library, true)
+ end
+end
+
+function UIMenu:GoRight()
+ local type, subtype = self.Items[self:CurrentSelection()]()
+ if subtype ~= "UIMenuListItem" and subtype ~= "UIMenuSliderItem" and subtype ~= "UIMenuProgressItem" then
+ return
+ end
+
+ if not self.Items[self:CurrentSelection()]:Enabled() then
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ return
+ end
+
+ if subtype == "UIMenuListItem" then
+ local Item = self.Items[self:CurrentSelection()]
+ Item:Index(Item._Index + 1)
+ self.OnListChange(self, Item, Item._Index)
+ Item.OnListChanged(self, Item, Item._Index)
+ PlaySoundFrontend(-1, self.Settings.Audio.LeftRight, self.Settings.Audio.Library, true)
+ elseif subtype == "UIMenuSliderItem" then
+ local Item = self.Items[self:CurrentSelection()]
+ Item:Index(Item._Index + 1)
+ self.OnSliderChange(self, Item, Item:Index())
+ Item.OnSliderChanged(self, Item, Item._Index)
+ PlaySoundFrontend(-1, self.Settings.Audio.LeftRight, self.Settings.Audio.Library, true)
+ elseif subtype == "UIMenuProgressItem" then
+ local Item = self.Items[self:CurrentSelection()]
+ Item:Index(Item.Data.Index + 1)
+ self.OnProgressChange(self, Item, Item.Data.Index)
+ Item.OnProgressChanged(self, Item, Item.Data.Index)
+ PlaySoundFrontend(-1, self.Settings.Audio.LeftRight, self.Settings.Audio.Library, true)
+ end
+end
+
+function UIMenu:SelectItem()
+ if not self.Items[self:CurrentSelection()]:Enabled() then
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ return
+ end
+ local Item = self.Items[self:CurrentSelection()]
+ local type, subtype = Item()
+ if subtype == "UIMenuCheckboxItem" then
+ Item.Checked = not Item.Checked
+ PlaySoundFrontend(-1, self.Settings.Audio.Select, self.Settings.Audio.Library, true)
+ self.OnCheckboxChange(self, Item, Item.Checked)
+ Item.CheckboxEvent(self, Item, Item.Checked)
+ elseif subtype == "UIMenuListItem" then
+ PlaySoundFrontend(-1, self.Settings.Audio.Select, self.Settings.Audio.Library, true)
+ self.OnListSelect(self, Item, Item._Index)
+ Item.OnListSelected(self, Item, Item._Index)
+ elseif subtype == "UIMenuSliderItem" then
+ PlaySoundFrontend(-1, self.Settings.Audio.Select, self.Settings.Audio.Library, true)
+ self.OnSliderSelect(self, Item, Item._Index)
+ Item.OnSliderSelected(Item._Index)
+ elseif subtype == "UIMenuProgressItem" then
+ PlaySoundFrontend(-1, self.Settings.Audio.Select, self.Settings.Audio.Library, true)
+ self.OnProgressSelect(self, Item, Item.Data.Index)
+ Item.OnProgressSelected(Item.Data.Index)
+ else
+ PlaySoundFrontend(-1, self.Settings.Audio.Select, self.Settings.Audio.Library, true)
+ self.OnItemSelect(self, Item, self:CurrentSelection())
+ Item.Activated(self, Item)
+ if not self.Children[Item] then
+ return
+ end
+ self:Visible(false)
+ self.Children[Item]:Visible(true)
+ self.OnMenuChanged(self, self.Children[self.Items[self:CurrentSelection()]], true)
+ end
+end
+
+function UIMenu:GoBack()
+ PlaySoundFrontend(-1, self.Settings.Audio.Back, self.Settings.Audio.Library, true)
+ self:Visible(false)
+ if self.ParentMenu ~= nil then
+ self.ParentMenu:Visible(true)
+ self.OnMenuChanged(self, self.ParentMenu, false)
+ if self.Settings.ResetCursorOnOpen then
+ local W, H = GetActiveScreenResolution()
+ SetCursorLocation(W / 2, H / 2)
+ end
+ end
+ self.OnMenuClosed(self)
+end
+
+function UIMenu:BindMenuToItem(Menu, Item)
+ if Menu() == "UIMenu" and Item() == "UIMenuItem" then
+ Menu.ParentMenu = self
+ Menu.ParentItem = Item
+ self.Children[Item] = Menu
+ end
+end
+
+function UIMenu:ReleaseMenuFromItem(Item)
+ if Item() == "UIMenuItem" then
+ if not self.Children[Item] then
+ return false
+ end
+ self.Children[Item].ParentMenu = nil
+ self.Children[Item].ParentItem = nil
+ self.Children[Item] = nil
+ return true
+ end
+end
+
+function UIMenu:Draw()
+ if not self._Visible then
+ return
+ end
+
+ HideHudComponentThisFrame(19)
+
+ if self.Settings.ControlDisablingEnabled then
+ self:DisEnableControls(false)
+ end
+
+ if self.Settings.InstructionalButtons then
+ DrawScaleformMovieFullscreen(self.InstructionalScaleform, 255, 255, 255, 255, 0)
+ end
+
+ if self.Settings.ScaleWithSafezone then
+ ScreenDrawPositionBegin(76, 84)
+ ScreenDrawPositionRatio(0, 0, 0, 0)
+ end
+
+ if self.ReDraw then
+ self:DrawCalculations()
+ end
+
+ if self.Logo then
+ self.Logo:Draw()
+ elseif self.Banner then
+ self.Banner:Draw()
+ end
+
+ self.Title:Draw()
+
+ if self.Subtitle.Rectangle then
+ self.Subtitle.Rectangle:Draw()
+ self.Subtitle.Text:Draw()
+ end
+
+ if #self.Items ~= 0 or #self.Windows ~= 0 then
+ self.Background:Draw()
+ end
+
+ if #self.Windows ~= 0 then
+ local WindowOffset = 0
+ for index = 1, #self.Windows do
+ if self.Windows[index - 1] then
+ WindowOffset = WindowOffset + self.Windows[index - 1].Background:Size().Height
+ end
+ local Window = self.Windows[index]
+ Window:Position(WindowOffset + self.Subtitle.ExtraY - 37)
+ Window:Draw()
+ end
+ end
+
+ if #self.Items == 0 then
+ if self.Settings.ScaleWithSafezone then
+ ScreenDrawPositionEnd()
+ end
+ return
+ end
+
+ local CurrentSelection = self:CurrentSelection()
+ self.Items[CurrentSelection]:Selected(true)
+
+ if self.Items[CurrentSelection]:Description() ~= "" then
+ self.Description.Bar:Draw()
+ self.Description.Rectangle:Draw()
+ self.Description.Text:Draw()
+ end
+
+ if self.Items[CurrentSelection].Panels ~= nil then
+ if #self.Items[CurrentSelection].Panels ~= 0 then
+ local PanelOffset = self:CaclulatePanelPosition(self.Items[CurrentSelection]:Description() ~= "")
+ for index = 1, #self.Items[CurrentSelection].Panels do
+ if self.Items[CurrentSelection].Panels[index - 1] then
+ PanelOffset = PanelOffset + self.Items[CurrentSelection].Panels[index - 1].Background:Size().Height + 5
+ end
+ self.Items[CurrentSelection].Panels[index]:Position(PanelOffset)
+ self.Items[CurrentSelection].Panels[index]:Draw()
+ end
+ end
+ end
+
+ local WindowHeight = self:CalculateWindowHeight()
+
+ if #self.Items <= self.Pagination.Total + 1 then
+ local ItemOffset = self.Subtitle.ExtraY - 37 + WindowHeight
+ for index = 1, #self.Items do
+ Item = self.Items[index]
+ Item:Position(ItemOffset)
+ Item:Draw()
+ ItemOffset = ItemOffset + self:CalculateItemHeightOffset(Item)
+ end
+ else
+ local ItemOffset = self.Subtitle.ExtraY - 37 + WindowHeight
+ for index = self.Pagination.Min + 1, self.Pagination.Max, 1 do
+ if self.Items[index] then
+ Item = self.Items[index]
+ Item:Position(ItemOffset)
+ Item:Draw()
+ ItemOffset = ItemOffset + self:CalculateItemHeightOffset(Item)
+ end
+ end
+
+ self.Extra.Up:Draw()
+ self.Extra.Down:Draw()
+ self.ArrowSprite:Draw()
+
+ if self.PageCounter.Text ~= nil then
+ local Caption = self.PageCounter.PreText .. CurrentSelection .. " / " .. #self.Items
+ self.PageCounter.Text:Text(Caption)
+ self.PageCounter.Text:Draw()
+ end
+ end
+
+ if self.Settings.ScaleWithSafezone then
+ ScreenDrawPositionEnd()
+ end
+end
+
+function UIMenu:ProcessMouse()
+ if not self._Visible or self.JustOpened or #self.Items == 0 or tobool(Controller()) or not self.Settings.MouseControlsEnabled then
+ EnableControlAction(0, 2, true)
+ EnableControlAction(0, 1, true)
+ EnableControlAction(0, 25, true)
+ EnableControlAction(0, 24, true)
+ if self.Dirty then
+ for _, Item in pairs(self.Items) do
+ if Item:Hovered() then
+ Item:Hovered(false)
+ end
+ end
+ end
+ return
+ end
+
+ local SafeZone = {X = 0, Y = 0}
+ local WindowHeight = self:CalculateWindowHeight()
+ if self.Settings.ScaleWithSafezone then
+ SafeZone = GetSafeZoneBounds()
+ end
+
+ local Limit = #self.Items
+ local ItemOffset = 0
+
+ ShowCursorThisFrame()
+
+ if #self.Items > self.Pagination.Total + 1 then
+ Limit = self.Pagination.Max
+ end
+
+ if IsMouseInBounds(0, 0, 30, 1080) and self.Settings.MouseEdgeEnabled then
+ SetGameplayCamRelativeHeading(GetGameplayCamRelativeHeading() + 5)
+ SetCursorSprite(6)
+ elseif IsMouseInBounds(1920 - 30, 0, 30, 1080) and self.Settings.MouseEdgeEnabled then
+ SetGameplayCamRelativeHeading(GetGameplayCamRelativeHeading() - 5)
+ SetCursorSprite(7)
+ elseif self.Settings.MouseEdgeEnabled then
+ SetCursorSprite(1)
+ end
+
+ for i = self.Pagination.Min + 1, Limit, 1 do
+ local X, Y = self.Position.X + SafeZone.X, self.Position.Y + 144 - 37 + self.Subtitle.ExtraY + ItemOffset + SafeZone.Y + WindowHeight
+ local Item = self.Items[i]
+ local Type, SubType = Item()
+ local Width, Height = 431 + self.WidthOffset, self:CalculateItemHeightOffset(Item)
+
+ if IsMouseInBounds(X, Y, Width, Height) then
+ Item:Hovered(true)
+ if not self.Controls.MousePressed then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ local _X, _Y, _Width, _Height = X, Y, Width, Height
+ self.Controls.MousePressed = true
+ if Item:Selected() and Item:Enabled() then
+ if SubType == "UIMenuListItem" then
+ if IsMouseInBounds(Item.LeftArrow.X + SafeZone.X, Item.LeftArrow.Y + SafeZone.Y, Item.LeftArrow.Width, Item.LeftArrow.Height) then
+ self:GoLeft()
+ elseif not IsMouseInBounds(Item.RightArrow.X + SafeZone.X, Item.RightArrow.Y + SafeZone.Y, Item.RightArrow.Width, Item.RightArrow.Height) then
+ self:SelectItem()
+ end
+ if IsMouseInBounds(Item.RightArrow.X + SafeZone.X, Item.RightArrow.Y + SafeZone.Y, Item.RightArrow.Width, Item.RightArrow.Height) then
+ self:GoRight()
+ elseif not IsMouseInBounds(Item.LeftArrow.X + SafeZone.X, Item.LeftArrow.Y + SafeZone.Y, Item.LeftArrow.Width, Item.LeftArrow.Height) then
+ self:SelectItem()
+ end
+ elseif SubType == "UIMenuSliderItem" then
+ if IsMouseInBounds(Item.LeftArrow.X + SafeZone.X, Item.LeftArrow.Y + SafeZone.Y, Item.LeftArrow.Width, Item.LeftArrow.Height) then
+ self:GoLeft()
+ elseif not IsMouseInBounds(Item.RightArrow.X + SafeZone.X, Item.RightArrow.Y + SafeZone.Y, Item.RightArrow.Width, Item.RightArrow.Height) then
+ self:SelectItem()
+ end
+ if IsMouseInBounds(Item.RightArrow.X + SafeZone.X, Item.RightArrow.Y + SafeZone.Y, Item.RightArrow.Width, Item.RightArrow.Height) then
+ self:GoRight()
+ elseif not IsMouseInBounds(Item.LeftArrow.X + SafeZone.X, Item.LeftArrow.Y + SafeZone.Y, Item.LeftArrow.Width, Item.LeftArrow.Height) then
+ self:SelectItem()
+ end
+ elseif SubType == "UIMenuProgressItem" then
+ if IsMouseInBounds(Item.Bar.X + SafeZone.X, Item.Bar.Y + SafeZone.Y - 12, Item.Data.Max, Item.Bar.Height + 24) then
+ Item:CalculateProgress(math.round(GetControlNormal(0, 239) * 1920) - SafeZone.X)
+ self.OnProgressChange(self, Item, Item.Data.Index)
+ Item.OnProgressChanged(self, Item, Item.Data.Index)
+ else
+ self:SelectItem()
+ end
+ else
+ self:SelectItem()
+ end
+ elseif not Item:Selected() then
+ self:CurrentSelection(i-1)
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ self.OnIndexChange(self, self:CurrentSelection())
+ self.ReDraw = true
+ self:UpdateScaleform()
+ elseif not Item:Enabled() and Item:Selected() then
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ end
+ Citizen.Wait(175)
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(_X, _Y, _Width, _Height) do
+ if Item:Selected() and Item:Enabled() then
+ if SubType == "UIMenuListItem" then
+ if IsMouseInBounds(Item.LeftArrow.X + SafeZone.X, Item.LeftArrow.Y + SafeZone.Y, Item.LeftArrow.Width, Item.LeftArrow.Height) then
+ self:GoLeft()
+ end
+ if IsMouseInBounds(Item.RightArrow.X + SafeZone.X, Item.RightArrow.Y + SafeZone.Y, Item.RightArrow.Width, Item.RightArrow.Height) then
+ self:GoRight()
+ end
+ elseif SubType == "UIMenuSliderItem" then
+ if IsMouseInBounds(Item.LeftArrow.X + SafeZone.X, Item.LeftArrow.Y + SafeZone.Y, Item.LeftArrow.Width, Item.LeftArrow.Height) then
+ self:GoLeft()
+ end
+ if IsMouseInBounds(Item.RightArrow.X + SafeZone.X, Item.RightArrow.Y + SafeZone.Y, Item.RightArrow.Width, Item.RightArrow.Height) then
+ self:GoRight()
+ end
+ elseif SubType == "UIMenuProgressItem" then
+ if IsMouseInBounds(Item.Bar.X + SafeZone.X, Item.Bar.Y + SafeZone.Y - 12, Item.Data.Max, Item.Bar.Height + 24) then
+ Item:CalculateProgress(math.round(GetControlNormal(0, 239) * 1920) - SafeZone.X)
+ self.OnProgressChange(self, Item, Item.Data.Index)
+ Item.OnProgressChanged(self, Item, Item.Data.Index)
+ else
+ self:SelectItem()
+ end
+ end
+ elseif not Item:Selected() then
+ self:CurrentSelection(i-1)
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ self.OnIndexChange(self, self:CurrentSelection())
+ self.ReDraw = true
+ self:UpdateScaleform()
+ elseif not Item:Enabled() and Item:Selected() then
+ PlaySoundFrontend(-1, self.Settings.Audio.Error, self.Settings.Audio.Library, true)
+ end
+ Citizen.Wait(125)
+ end
+ self.Controls.MousePressed = false
+ end)
+ end
+ end
+ else
+ Item:Hovered(false)
+ end
+ ItemOffset = ItemOffset + self:CalculateItemHeightOffset(Item)
+ end
+
+ local ExtraX, ExtraY = self.Position.X + SafeZone.X, 144 + self:CalculateItemHeight() + self.Position.Y + SafeZone.Y + WindowHeight
+
+ if #self.Items <= self.Pagination.Total + 1 then return end
+
+ if IsMouseInBounds(ExtraX, ExtraY, 431 + self.WidthOffset, 18) then
+ self.Extra.Up:Colour(30, 30, 30, 255)
+ if not self.Controls.MousePressed then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ local _ExtraX, _ExtraY = ExtraX, ExtraY
+ self.Controls.MousePressed = true
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoUpOverflow()
+ else
+ self:GoUp()
+ end
+ Citizen.Wait(175)
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(_ExtraX, _ExtraY, 431 + self.WidthOffset, 18) do
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoUpOverflow()
+ else
+ self:GoUp()
+ end
+ Citizen.Wait(125)
+ end
+ self.Controls.MousePressed = false
+ end)
+ end
+ end
+ else
+ self.Extra.Up:Colour(0, 0, 0, 200)
+ end
+
+ if IsMouseInBounds(ExtraX, ExtraY + 18, 431 + self.WidthOffset, 18) then
+ self.Extra.Down:Colour(30, 30, 30, 255)
+ if not self.Controls.MousePressed then
+ if IsDisabledControlJustPressed(1, 24) and IsInputDisabled(0) then
+ Citizen.CreateThread(function()
+ local _ExtraX, _ExtraY = ExtraX, ExtraY
+ self.Controls.MousePressed = true
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoDownOverflow()
+ else
+ self:GoDown()
+ end
+ Citizen.Wait(175)
+ while IsDisabledControlPressed(0, 24) and IsMouseInBounds(_ExtraX, _ExtraY + 18, 431 + self.WidthOffset, 18) do
+ if #self.Items > self.Pagination.Total + 1 then
+ self:GoDownOverflow()
+ else
+ self:GoDown()
+ end
+ Citizen.Wait(125)
+ end
+ self.Controls.MousePressed = false
+ end)
+ end
+ end
+ else
+ self.Extra.Down:Colour(0, 0, 0, 200)
+ end
+end
+
+function UIMenu:AddInstructionButton(button)
+ if type(button) == "table" and #button == 2 then
+ table.insert(self.InstructionalButtons, button)
+ end
+end
+
+function UIMenu:RemoveInstructionButton(button)
+ if type(button) == "table" then
+ for i = 1, #self.InstructionalButtons do
+ if button == self.InstructionalButtons[i] then
+ table.remove(self.InstructionalButtons, i)
+ break
+ end
+ end
+ else
+ if tonumber(button) then
+ if self.InstructionalButtons[tonumber(button)] then
+ table.remove(self.InstructionalButtons, tonumber(button))
+ end
+ end
+ end
+end
+
+function UIMenu:AddEnabledControl(Inputgroup, Control, Controller)
+ if tonumber(Inputgroup) and tonumber(Control) then
+ table.insert(self.Settings.EnabledControls[(Controller and "Controller" or "Keyboard")], {Inputgroup, Control})
+ end
+end
+
+function UIMenu:RemoveEnabledControl(Inputgroup, Control, Controller)
+ local Type = (Controller and "Controller" or "Keyboard")
+ for Index = 1, #self.Settings.EnabledControls[Type] do
+ if Inputgroup == self.Settings.EnabledControls[Type][Index][1] and Control == self.Settings.EnabledControls[Type][Index][2] then
+ table.remove(self.Settings.EnabledControls[Type], Index)
+ break
+ end
+ end
+end
+
+function UIMenu:UpdateScaleform()
+ if not self._Visible or not self.Settings.InstructionalButtons then
+ return
+ end
+
+ PushScaleformMovieFunction(self.InstructionalScaleform, "CLEAR_ALL")
+ PopScaleformMovieFunction()
+
+ PushScaleformMovieFunction(self.InstructionalScaleform, "TOGGLE_MOUSE_BUTTONS")
+ PushScaleformMovieFunctionParameterInt(0)
+ PopScaleformMovieFunction()
+
+ PushScaleformMovieFunction(self.InstructionalScaleform, "CREATE_CONTAINER")
+ PopScaleformMovieFunction()
+
+ PushScaleformMovieFunction(self.InstructionalScaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterString(GetControlInstructionalButton(2, 176, 0))
+ PushScaleformMovieFunctionParameterString("Select")
+ PopScaleformMovieFunction()
+
+ if self.Controls.Back.Enabled then
+ PushScaleformMovieFunction(self.InstructionalScaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(1)
+ PushScaleformMovieFunctionParameterString(GetControlInstructionalButton(2, 177, 0))
+ PushScaleformMovieFunctionParameterString("Back")
+ PopScaleformMovieFunction()
+ end
+
+ local count = 2
+
+ for i = 1, #self.InstructionalButtons do
+ if self.InstructionalButtons[i] then
+ if #self.InstructionalButtons[i] == 2 then
+ PushScaleformMovieFunction(self.InstructionalScaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(count)
+ PushScaleformMovieFunctionParameterString(self.InstructionalButtons[i][1])
+ PushScaleformMovieFunctionParameterString(self.InstructionalButtons[i][2])
+ PopScaleformMovieFunction()
+ count = count + 1
+ end
+ end
+ end
+
+ PushScaleformMovieFunction(self.InstructionalScaleform, "DRAW_INSTRUCTIONAL_BUTTONS")
+ PushScaleformMovieFunctionParameterInt(-1)
+ PopScaleformMovieFunction()
+end
+
+--[[
+ MenuPool.lua
+ Menus
+--]]
+
+function MenuPool.New()
+ local _MenuPool = {
+ Menus = {}
+ }
+ return setmetatable(_MenuPool, MenuPool)
+end
+
+function MenuPool:AddSubMenu(Menu, Text, Description, KeepPosition, KeepBanner)
+ if Menu() == "UIMenu" then
+ local Item = UIMenuItem.New(tostring(Text), Description or "")
+ Menu:AddItem(Item)
+ local SubMenu
+
+ if KeepPosition then
+ if Menu.Title.TxtName then
+ SubMenu = UIMenu.New("", Text, Menu.Position.X, Menu.Position.Y, Menu.TxtDictionary, Menu.TxtName, Menu.titleTxt)
+ else
+ SubMenu = UIMenu.New(Menu.Title._Text, Text, Menu.Position.X, Menu.Position.Y, Menu.TxtDictionary, Menu.TxtName)
+ end
+ else
+ if Menu.Title.TxtName then
+ SubMenu = UIMenu.New("", Text, 0, 0, Menu.TxtDictionary, Menu.TxtName, Menu.titleTxt)
+ else
+ SubMenu = UIMenu.New(Menu.Title._Text, Text, 0, 0, Menu.TxtDictionary, Menu.TxtName, Menu.titleTxt)
+ end
+ end
+ if KeepBanner then
+ if Menu.Logo ~= nil then
+ SubMenu.Logo = Menu.Logo
+ else
+ SubMenu.Logo = nil
+ SubMenu.Banner = Menu.Banner
+ end
+ end
+ Item:RightLabel('→→→')
+ self:Add(SubMenu)
+ Menu:BindMenuToItem(SubMenu, Item)
+ return SubMenu
+ end
+end
+
+function MenuPool:Add(Menu)
+ if Menu() == "UIMenu" then
+ table.insert(self.Menus, Menu)
+ end
+end
+
+function MenuPool:Clear()
+ self = {
+ Menus = {}
+ }
+ collectgarbage()
+end
+
+function MenuPool:Remove()
+ self = nil
+ collectgarbage()
+end
+
+function MenuPool:MouseEdgeEnabled(bool)
+ if bool ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Settings.MouseEdgeEnabled = tobool(bool)
+ end
+ end
+end
+
+function MenuPool:ControlDisablingEnabled(bool)
+ if bool ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Settings.ControlDisablingEnabled = tobool(bool)
+ end
+ end
+end
+
+function MenuPool:ResetCursorOnOpen(bool)
+ if bool ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Settings.ResetCursorOnOpen = tobool(bool)
+ end
+ end
+end
+
+function MenuPool:MultilineFormats(bool)
+ if bool ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Settings.MultilineFormats = tobool(bool)
+ end
+ end
+end
+
+function MenuPool:Audio(Attribute, Setting)
+ if Attribute ~= nil and Setting ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ if Menu.Settings.Audio[Attribute] then
+ Menu.Settings.Audio[Attribute] = Setting
+ end
+ end
+ end
+end
+
+function MenuPool:WidthOffset(offset)
+ if tonumber(offset) then
+ for _, Menu in pairs(self.Menus) do
+ Menu:SetMenuWidthOffset(tonumber(offset))
+ end
+ end
+end
+
+function MenuPool:CounterPreText(str)
+ if str ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.PageCounter.PreText = tostring(str)
+ end
+ end
+end
+
+function MenuPool:DisableInstructionalButtons(bool)
+ if bool ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Settings.InstructionalButtons = tobool(bool)
+ end
+ end
+end
+
+function MenuPool:MouseControlsEnabled(bool)
+ if bool ~= nil then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Settings.MouseControlsEnabled = tobool(bool)
+ end
+ end
+end
+
+function MenuPool:RefreshIndex()
+ for _, Menu in pairs(self.Menus) do
+ Menu:RefreshIndex()
+ end
+end
+
+function MenuPool:ProcessMenus()
+ self:ProcessControl()
+ --self:ProcessMouse()
+ self:Draw()
+end
+
+function MenuPool:ProcessControl()
+ for _, Menu in pairs(self.Menus) do
+ if Menu:Visible() then
+ Menu:ProcessControl()
+ end
+ end
+end
+
+function MenuPool:ProcessMouse()
+ for _, Menu in pairs(self.Menus) do
+ if Menu:Visible() then
+ Menu:ProcessMouse()
+ end
+ end
+end
+
+function MenuPool:Draw()
+ for _, Menu in pairs(self.Menus) do
+ if Menu:Visible() then
+ Menu:Draw()
+ end
+ end
+end
+
+function MenuPool:IsAnyMenuOpen()
+ local open = false
+ for _, Menu in pairs(self.Menus) do
+ if Menu:Visible() then
+ open = true
+ break
+ end
+ end
+ return open
+end
+
+function MenuPool:CloseAllMenus()
+ for _, Menu in pairs(self.Menus) do
+ if Menu:Visible() then
+ Menu:Visible(false)
+ Menu.OnMenuClosed(Menu)
+ end
+ end
+end
+
+function MenuPool:SetBannerSprite(Sprite)
+ if Sprite() == "Sprite" then
+ for _, Menu in pairs(self.Menus) do
+ Menu:SetBannerSprite(Sprite)
+ end
+ end
+end
+
+function MenuPool:SetBannerRectangle(Rectangle)
+ if Rectangle() == "Rectangle" then
+ for _, Menu in pairs(self.Menus) do
+ Menu:SetBannerRectangle(Rectangle)
+ end
+ end
+end
+
+function MenuPool:TotalItemsPerPage(Value)
+ if tonumber(Value) then
+ for _, Menu in pairs(self.Menus) do
+ Menu.Pagination.Total = Value - 1
+ end
+ end
+end
+--[[
+ Wrappers
+--]]
+
+function NativeUI.CreatePool()
+ return MenuPool.New()
+end
+
+function NativeUI.CreateMenu(Title, Subtitle, X, Y, TxtDictionary, TxtName, titleTxt)
+ return UIMenu.New(Title, Subtitle, X, Y, TxtDictionary, TxtName, titleTxt)
+end
+
+function NativeUI.CreateItem(Text, Description)
+ return UIMenuItem.New(Text, Description)
+end
+
+function NativeUI.CreateColouredItem(Text, Description, MainColour, HighlightColour)
+ return UIMenuColouredItem.New(Text, Description, MainColour, HighlightColour)
+end
+
+function NativeUI.CreateCheckboxItem(Text, Check, Description)
+ return UIMenuCheckboxItem.New(Text, Check, Description)
+end
+
+function NativeUI.CreateListItem(Text, Items, Index, Description)
+ return UIMenuListItem.New(Text, Items, Index, Description)
+end
+
+function NativeUI.CreateSliderItem(Text, Items, Index, Description, Divider)
+ return UIMenuSliderItem.New(Text, Items, Index, Description, Divider)
+end
+
+function NativeUI.CreateProgressItem(Text, Items, Index, Description, Counter)
+ return UIMenuProgressItem.New(Text, Items, Index, Description, Counter)
+end
+
+function NativeUI.CreateHeritageWindow(Mum, Dad)
+ return UIMenuHeritageWindow.New(Mum, Dad)
+end
+
+function NativeUI.CreateGridPanel(TopText, LeftText, RightText, BottomText)
+ return UIMenuGridPanel.New(TopText, LeftText, RightText, BottomText)
+end
+
+function NativeUI.CreateColourPanel(Title, Colours)
+ return UIMenuColourPanel.New(Title, Colours)
+end
+
+function NativeUI.CreatePercentagePanel(MinText, MaxText)
+ return UIMenuPercentagePanel.New(MinText, MaxText)
+end
+
+function NativeUI.CreateSprite(TxtDictionary, TxtName, X, Y, Width, Height, Heading, R, G, B, A)
+ return Sprite.New(TxtDictionary, TxtName, X, Y, Width, Height, Heading, R, G, B, A)
+end
+
+function NativeUI.CreateRectangle(X, Y, Width, Height, R, G, B, A)
+ return UIResRectangle.New(X, Y, Width, Height, R, G, B, A)
+end
+
+function NativeUI.CreateText(Text, X, Y, Scale, R, G, B, A, Font, Alignment, DropShadow, Outline, WordWrap)
+ return UIResText.New(Text, X, Y, Scale, R, G, B, A, Font, Alignment, DropShadow, Outline, WordWrap)
+end
diff --git a/resources/Interaction-Menu/functions.lua b/resources/Interaction-Menu/functions.lua
new file mode 100644
index 000000000..322906a2c
--- /dev/null
+++ b/resources/Interaction-Menu/functions.lua
@@ -0,0 +1,478 @@
+--[[
+─────────────────────────────────────────────────────────────────
+
+ SEM_InteractionMenu (functions.lua) - Created by Scott M
+ Current Version: v1.7.1 (Sep 2021)
+
+ Support: https://semdevelopment.com/discord
+
+ !!! Change vaules in the 'config.lua' !!!
+ DO NOT EDIT THIS IF YOU DON'T KNOW WHAT YOU ARE DOING
+
+─────────────────────────────────────────────────────────────────
+]]
+
+
+
+--General Functions
+function Notify(Text)
+ SetNotificationTextEntry('STRING')
+ AddTextComponentString(Text)
+ DrawNotification(true, true)
+end
+
+function NotifyHelp(Text)
+ SetTextComponentFormat('STRING')
+ AddTextComponentString(Text)
+ DisplayHelpTextFromStringLabel(0, 0, 1, -1)
+end
+
+function LoadAnimation(Dict)
+ while not HasAnimDictLoaded(Dict) do
+ RequestAnimDict(Dict)
+ Citizen.Wait(5)
+ end
+end
+
+function KeyboardInput(TextEntry, MaxStringLenght)
+ AddTextEntry('FMMC_KEY_TIP1', TextEntry)
+ DisplayOnscreenKeyboard(1, 'FMMC_KEY_TIP1', '', '', '', '', '', MaxStringLenght)
+ BlockInput = true
+
+ while UpdateOnscreenKeyboard() ~= 1 and UpdateOnscreenKeyboard() ~= 2 do
+ Citizen.Wait(0)
+ end
+
+ if UpdateOnscreenKeyboard() ~= 2 then
+ local Result = GetOnscreenKeyboardResult()
+ Citizen.Wait(500)
+ BlockInput = false
+ return Result
+ else
+ Citizen.Wait(500)
+ BlockInput = false
+ return nil
+ end
+end
+
+function GetClosestPlayer()
+ local Ped = PlayerPedId()
+
+ for _, Player in ipairs(GetActivePlayers()) do
+ if GetPlayerPed(Player) ~= GetPlayerPed(-1) then
+ local Ped2 = GetPlayerPed(Player)
+ local x, y, z = table.unpack(GetEntityCoords(Ped))
+ if (GetDistanceBetweenCoords(GetEntityCoords(Ped2), x, y, z) < 2) then
+ return GetPlayerServerId(Player)
+ end
+ end
+ end
+
+ Notify('~r~No Player Nearby!')
+ return false
+end
+
+function GetDistance(ID)
+ local Ped = GetPlayerPed(-1)
+ local Ped2 = GetPlayerPed(ID)
+ local x, y, z = table.unpack(GetEntityCoords(Ped))
+ return GetDistanceBetweenCoords(GetEntityCoords(Ped2), x, y, z)
+end
+
+--LEO Functions
+function ToggleRadar()
+ if Config.Radar ~= 0 then
+ if IsPedInAnyVehicle(GetPlayerPed(-1)) then
+ if GetVehicleClass(GetVehiclePedIsIn(GetPlayerPed(-1))) == 18 then
+ if GetPedInVehicleSeat(GetVehiclePedIsIn(GetPlayerPed(-1)) == -1) then
+ _MenuPool:CloseAllMenus()
+ if Config.Radar == 1 then
+ TriggerEvent('wk:openRemote')
+ elseif Config.Radar == 2 then
+ TriggerEvent('wk:radarRC')
+ end
+ else
+ Notify('~o~You need to be in the driver seat')
+ end
+ else
+ Notify('~o~You need to be in a police vehicle')
+ end
+ else
+ Notify('~o~You need to be in a vehicle')
+ end
+ end
+end
+
+function EnableShield()
+ ShieldActive = true
+ local Ped = GetPlayerPed(-1)
+ local PedPos = GetEntityCoords(Ped, false)
+
+ if IsPedInAnyVehicle(GetPlayerPed(-1), true) then
+ Notify('~r~You cannot be in a vehicle when getting your shield out!')
+ ShieldActive = false
+ return
+ end
+
+ RequestAnimDict('combat@gestures@gang@pistol_1h@beckon')
+ while not HasAnimDictLoaded('combat@gestures@gang@pistol_1h@beckon') do
+ Citizen.Wait(100)
+ end
+
+ TaskPlayAnim(Ped, 'combat@gestures@gang@pistol_1h@beckon', '0', 8.0, -8.0, -1, (2 + 16 + 32), 0.0, 0, 0, 0)
+
+ RequestModel(GetHashKey('prop_ballistic_shield'))
+ while not HasModelLoaded(GetHashKey('prop_ballistic_shield')) do
+ Citizen.Wait(100)
+ end
+
+ local shield = CreateObject(GetHashKey('prop_ballistic_shield'), PedPos.x, PedPos.y, PedPos.z, 1, 1, 1)
+ shieldEntity = shield
+ AttachEntityToEntity(shieldEntity, Ped, GetEntityBoneIndexByName(Ped, 'IK_L_Hand'), 0.0, -0.05, -0.10, -30.0, 180.0, 40.0, 0, 0, 1, 0, 0, 1)
+ SetWeaponAnimationOverride(Ped, 'Gang1H')
+
+ if HasPedGotWeapon(Ped, 'weapon_combatpistol', 0) or GetSelectedPedWeapon(Ped) == 'weapon_combatpistol' then
+ SetCurrentPedWeapon(Ped, 'weapon_combatpistol', 1)
+ HadPistol = true
+ else
+ GiveWeaponToPed(Ped, 'weapon_combatpistol', 300, 0, 1)
+ SetCurrentPedWeapon(Ped, 'weapon_combatpistol', 1)
+ HadPistol = false
+ end
+ SetEnableHandcuffs(Ped, true)
+end
+
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(1)
+
+ if ShieldActive == true then
+ DisableControlAction(1, 23, true) --F | Enter Vehicle
+ DisableControlAction(1, 75, true) --F | Exit Vehicle
+ end
+ end
+end)
+
+function DisableShield()
+ local Ped = GetPlayerPed(-1)
+ DeleteEntity(shieldEntity)
+ ClearPedTasksImmediately(Ped)
+ SetWeaponAnimationOverride(Ped, 'Default')
+ SetCurrentPedWeapon(Ped, 'weapon_unarmed', 1)
+
+ if not HadPistol then
+ RemoveWeaponFromPed(Ped, 'weapon_combatpistol')
+ end
+ SetEnableHandcuffs(Ped, false)
+ HadPistol = false
+ ShieldActive = false
+end
+
+
+
+--Civ Functions
+function Ad(Text, Name, Loc, File, ID)
+ SetNotificationTextEntry('STRING')
+ AddTextComponentString(Text)
+ EndTextCommandThefeedPostMessagetext(Loc, File, true, 1, Name, '~b~Advertisement #' .. ID)
+ DrawNotification(false, true)
+end
+
+
+
+--Vehicle Functions
+function SpawnVehicle(Veh, Name, Livery, Extras)
+ local Ped = GetPlayerPed( -1 )
+ if (DoesEntityExist(Ped) and not IsEntityDead(Ped)) then
+ local pos = GetEntityCoords(Ped)
+ if (IsPedSittingInAnyVehicle(Ped)) then
+ local Vehicle = GetVehiclePedIsIn(Ped, false)
+ if (GetPedInVehicleSeat(Vehicle, -1) == Ped) then
+ SetEntityAsMissionEntity(Vehicle, true, true )
+ DeleteVehicle(Vehicle)
+ end
+ end
+ end
+
+ local WaitTime = 0
+ local Model = GetHashKey(Veh)
+ RequestModel(Model)
+ while not HasModelLoaded(Model) do
+ CancelEvent()
+ RequestModel(Model)
+ Citizen.Wait(100)
+
+ WaitTime = WaitTime + 1
+
+ if WaitTime == 600 then
+ CancelEvent()
+ Notify('~r~Unable to load vehicle, please contact development!')
+ return
+ end
+ end
+ local x, y, z = table.unpack(GetEntityCoords(PlayerPedId(), false))
+ local Vehicle = CreateVehicle(Model, x + 2, y + 2, z + 1, GetEntityHeading(PlayerPedId()), true, false)
+ SetPedIntoVehicle(PlayerPedId(), Vehicle, -1)
+ SetVehicleDirtLevel(Vehicle, 0)
+ SetVehicleModKit(Vehicle, 0)
+ SetVehicleMod(Vehicle, 23, -1, false)
+ SetModelAsNoLongerNeeded(Model)
+ if Livery then
+ SetVehicleLivery(Vehicle, Livery)
+ end
+ if Extras then
+ for extraId = 0, 30 do
+ if DoesExtraExist(Vehicle, extraId) then
+ SetVehicleExtra(Vehicle, extraId, true)
+ end
+ end
+ for _, extra in pairs(Extras) do
+ SetVehicleExtra(Vehicle, extra, false)
+ end
+ end
+
+ if Name then
+ Notify('~b~Vehicle Spawned: ~g~' .. Name)
+ else
+ Notify('~b~Vehicle Spawned!')
+ end
+end
+
+function DeleteVehicle(entity)
+ Citizen.InvokeNative( 0xEA386986E786A54F, Citizen.PointerValueIntInitialized(entity))
+end
+
+
+
+--Ped Functions
+function LoadPed(Hash)
+ Citizen.CreateThread(function()
+ local Model = GetHashKey(Hash)
+ RequestModel(Model)
+
+ while not HasModelLoaded(Model) do
+ Wait(0)
+ end
+
+ if HasModelLoaded(Model) then
+ SetPlayerModel(PlayerId(), Model)
+ else
+ Notify('The model could not load - please contact development.')
+ end
+ end)
+end
+
+
+
+--Weapon Functions
+function GiveWeapon(Hash)
+ GiveWeaponToPed(GetPlayerPed(-1), GetHashKey(Hash), 999, false)
+end
+
+function AddWeaponComponent(WeaponHash, Component)
+ if HasPedGotWeapon(GetPlayerPed(-1), GetHashKey(WeaponHash), false) then
+ GiveWeaponComponentToPed(GetPlayerPed(-1), GetHashKey(WeaponHash), GetHashKey(Component))
+ end
+end
+
+
+
+--Prop Functions
+function SpawnProp(Object, Name)
+ local Player = PlayerPedId()
+ local Coords = GetEntityCoords(Player)
+ local Heading = GetEntityHeading(Player)
+
+ RequestModel(Object)
+ while not HasModelLoaded(Object) do
+ Citizen.Wait(0)
+ end
+
+ local OffsetCoords = GetOffsetFromEntityInWorldCoords(Player, 0.0, 0.75, 0.0)
+ local Prop = CreateObjectNoOffset(Object, OffsetCoords, false, true, false)
+ SetEntityHeading(Prop, Heading)
+ PlaceObjectOnGroundProperly(Prop)
+ SetEntityCollision(Prop, false, true)
+ SetEntityAlpha(Prop, 100)
+ FreezeEntityPosition(Prop, true)
+ SetModelAsNoLongerNeeded(Object)
+
+ Notify('Press ~g~E ~w~to place\nPress ~r~R ~w~to cancel')
+
+ Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(0)
+
+ local OffsetCoords = GetOffsetFromEntityInWorldCoords(Player, 0.0, 0.75, 0.0)
+ local Heading = GetEntityHeading(Player)
+
+ SetEntityCoordsNoOffset(Prop, OffsetCoords)
+ SetEntityHeading(Prop, Heading)
+ PlaceObjectOnGroundProperly(Prop)
+ DisableControlAction(1, 38, true) --E
+ DisableControlAction(1, 140, true) --R
+
+
+ if IsDisabledControlJustPressed(1, 38) then --E
+ local PropCoords = GetEntityCoords(Prop)
+ local PropHeading = GetEntityHeading(Prop)
+ DeleteObject(Prop)
+
+ RequestModel(Object)
+ while not HasModelLoaded(Object) do
+ Citizen.Wait(0)
+ end
+
+ local Prop = CreateObjectNoOffset(Object, PropCoords, true, true, true)
+ SetEntityHeading(Prop, PropHeading)
+ PlaceObjectOnGroundProperly(Prop)
+ FreezeEntityPosition(Prop, true)
+ SetEntityInvincible(Prop, true)
+ SetModelAsNoLongerNeeded(Object)
+ return
+ end
+
+ if IsDisabledControlJustPressed(1, 140) then --R
+ DeleteObject(Prop)
+ return
+ end
+ end
+ end)
+end
+
+function DeleteProp(Object)
+ local Hash = GetHashKey(Object)
+ local x, y, z = table.unpack(GetEntityCoords(PlayerPedId(), true))
+ if DoesObjectOfTypeExistAtCoords(x, y, z, 1.5, Hash, true) then
+ local Prop = GetClosestObjectOfType(x, y, z, 1.5, Hash, false, false, false)
+ DeleteObject(Prop)
+ Notify('~r~Prop Removed!')
+ end
+end
+
+function DeleteEntity(Entity)
+ Citizen.InvokeNative(0xAE3CBE5BF394C9C9, Citizen.PointerValueIntInitialized(Entity))
+end
+
+
+
+--Emote Functions
+function PlayEmote(Emote, Name)
+ if not DoesEntityExist(GetPlayerPed(-1)) then
+ return
+ end
+
+ if IsPedInAnyVehicle(GetPlayerPed(-1)) then
+ Notify('~r~Please exit the vehicle to use this emote!')
+ return
+ end
+
+ TaskStartScenarioInPlace(GetPlayerPed(-1), Emote, 0, true)
+ Notify('~b~Playing Emote: ~g~' .. Name)
+ EmotePlaying = true
+end
+
+function CancelEmote()
+ ClearPedTasks(GetPlayerPed(-1))
+ Notify('~r~Stopping Emote')
+ EmotePlaying = false
+end
+
+
+
+
+
+
+
+--Menu Restrictions
+function LEORestrict()
+ if Config.LEOAccess == 0 then
+ return false
+ elseif Config.LEOAccess == 1 then
+ return true
+ elseif Config.LEOAccess == 2 then
+ local Ped = GetEntityModel(GetPlayerPed(-1))
+
+ for _, LEOPeds in pairs(Config.LEOUniforms) do
+ local AllowedPed = GetHashKey(LEOPeds.spawncode)
+
+ if Ped == AllowedPed then
+ return true
+ end
+ end
+ elseif Config.LEOAccess == 3 then
+ return LEOOnduty
+ elseif Config.LEOAccess == 4 then
+ return LEOAce
+ else
+ return true
+ end
+end
+
+
+
+function FireRestrict()
+ if Config.FireAccess == 0 then
+ return false
+ elseif Config.FireAccess == 1 then
+ return true
+ elseif Config.FireAccess == 2 then
+ local Ped = GetEntityModel(GetPlayerPed(-1))
+
+ for _, FirePeds in pairs(Config.FireUniforms) do
+ local AllowedPed = GetHashKey(FirePeds.spawncode)
+
+ if Ped == AllowedPed then
+ return true
+ end
+ end
+ elseif Config.FireAccess == 3 then
+ return FireOnduty
+ elseif Config.FireAccess == 4 then
+ return FireAce
+ else
+ return true
+ end
+end
+
+
+
+function CivRestrict()
+ if Config.CivAccess == 0 then
+ return false
+ elseif Config.CivAccess == 1 then
+ return true
+ else
+ return true
+ end
+end
+
+
+
+function VehicleRestrict()
+ if Config.VehicleAccess == 0 then
+ return false
+ elseif Config.VehicleAccess == 1 then
+ return true
+ elseif Config.VehicleAccess == 2 then
+ if IsPedInAnyVehicle(GetPlayerPed(PlayerId()), false) then
+ return true
+ else
+ return false
+ end
+ else
+ return true
+ end
+end
+
+
+
+function EmoteRestrict()
+ if Config.EmoteAccess == 0 then
+ return false
+ elseif Config.EmoteAccess == 1 then
+ return true
+ else
+ return true
+ end
+end
diff --git a/resources/Interaction-Menu/fxmanifest.lua b/resources/Interaction-Menu/fxmanifest.lua
new file mode 100644
index 000000000..dcd4a8f0a
--- /dev/null
+++ b/resources/Interaction-Menu/fxmanifest.lua
@@ -0,0 +1,43 @@
+--[[
+──────────────────────────────────────────────────────────────────
+
+ SEM_InteractionMenu (fxmanifest.lua) - Created by Scott M
+ Current Version: v1.7.1 (Sep 2021)
+
+ Support: https://semdevelopment.com/discord
+
+ !!! Change vaules in the 'config.lua' !!!
+ DO NOT EDIT THIS IF YOU DON'T KNOW WHAT YOU ARE DOING
+
+──────────────────────────────────────────────────────────────────
+]]
+
+
+
+fx_version 'cerulean'
+games {'gta5'}
+
+--DO NOT REMOVE THESE
+title 'SEM_InteractionMenu'
+description 'Multi Purpose Interaction Menu'
+author 'Scott M [SEM Development]'
+version 'v1.7.1' --This is required for the version checker, DO NOT change or remove
+
+client_scripts {
+ 'dependencies/NativeUI.lua',
+ 'client.lua',
+ 'config.lua',
+ 'functions.lua',
+ 'menu.lua',
+}
+
+server_scripts {
+ 'config.lua',
+ 'server.lua',
+ 'functions.lua',
+}
+
+exports {
+ 'IsOndutyLEO',
+ 'IsOndutyFire',
+}
diff --git a/resources/Interaction-Menu/menu.lua b/resources/Interaction-Menu/menu.lua
new file mode 100644
index 000000000..2bad19a07
--- /dev/null
+++ b/resources/Interaction-Menu/menu.lua
@@ -0,0 +1,1099 @@
+--[[
+───────────────────────────────────────────────────────────────
+
+ SEM_InteractionMenu (menu.lua) - Created by Scott M
+ Current Version: v1.7.1 (Sep 2021)
+
+ Support | https://semdevelopment.com/discord
+
+ !!! Change vaules in the 'config.lua' !!!
+ DO NOT EDIT THIS IF YOU DON'T KNOW WHAT YOU ARE DOING
+
+───────────────────────────────────────────────────────────────
+]]
+
+
+
+local MenuOri = 0
+if Config.MenuOrientation == 0 then
+ MenuOri = 0
+elseif Config.MenuOrientation == 1 then
+ MenuOri = 1320
+else
+ MenuOri = 0
+end
+
+
+_MenuPool = NativeUI.CreatePool()
+MainMenu = NativeUI.CreateMenu()
+
+
+
+
+
+function Menu()
+ local MenuTitle = ''
+ if Config.MenuTitle == 0 then
+ MenuTitle = 'Interaction Menu'
+ elseif Config.MenuTitle == 1 then
+ MenuTitle = GetPlayerName(source)
+ elseif Config.MenuTitle == 2 then
+ MenuTitle = Config.MenuTitleCustom
+ else
+ MenuTitle = 'Interaction Menu'
+ end
+
+
+
+ _MenuPool:Remove()
+ _MenuPool = NativeUI.CreatePool()
+ MainMenu = NativeUI.CreateMenu(MenuTitle, GetResourceMetadata(GetCurrentResourceName(), 'title', 0) .. ' ~y~' .. GetResourceMetadata(GetCurrentResourceName(), 'version', 0), MenuOri)
+ _MenuPool:Add(MainMenu)
+ MainMenu:SetMenuWidthOffset(Config.MenuWidth)
+ collectgarbage()
+
+ MainMenu:SetMenuWidthOffset(Config.MenuWidth)
+ _MenuPool:ControlDisablingEnabled(false)
+ _MenuPool:MouseControlsEnabled(false)
+
+
+
+
+
+ if LEORestrict() then
+ local LEOMenu = _MenuPool:AddSubMenu(MainMenu, 'LEO Toolbox', 'Law Enforcement Related Menu', true)
+ LEOMenu:SetMenuWidthOffset(Config.MenuWidth)
+ local LEOActions = _MenuPool:AddSubMenu(LEOMenu, 'Actions', '', true)
+ LEOActions:SetMenuWidthOffset(Config.MenuWidth)
+ local Cuff = NativeUI.CreateItem('Cuff', 'Cuff/Uncuff the closest player')
+ local Drag = NativeUI.CreateItem('Drag', 'Drag/Undrag the closest player')
+ local Seat = NativeUI.CreateItem('Seat', 'Place a player in the closest vehicle')
+ local Unseat = NativeUI.CreateItem('Unseat', 'Remove a player from the closest vehicle')
+ local Radar = NativeUI.CreateItem('Radar', 'Toggle the radar menu')
+ local Inventory = NativeUI.CreateItem('Inventory', 'Search the closest player\'s inventory')
+ local BAC = NativeUI.CreateItem('BAC', 'Test the BAC level of the closest player')
+ local Jail = NativeUI.CreateItem('Jail', 'Jail a player')
+ local Unjail = NativeUI.CreateItem('Unjail', 'Unjail a player')
+ SpikeLengths = {1, 2, 3, 4, 5}
+ local Spikes = NativeUI.CreateListItem('Deploy Spikes', SpikeLengths, 1, 'Places spike strips on the ground')
+ local DelSpikes = NativeUI.CreateItem('Remove Spikes', 'Remove spike strips placed on the ground')
+ local Shield = NativeUI.CreateItem('Toggle Shield', 'Toggle the bulletproof shield')
+ local CarbineRifle = NativeUI.CreateItem('Toggle Carbine', 'Toggle your carbine rifle')
+ local Shotgun = NativeUI.CreateItem('Toggle Shotgun', 'Toggle your pump shotgun')
+ PropsList = {}
+ for _, Prop in pairs(Config.Props) do
+ table.insert(PropsList, Prop.name)
+ end
+ local Props = NativeUI.CreateListItem('Spawn Props', PropsList, 1, 'Spawn props on the ground')
+ local RemoveProps = NativeUI.CreateItem('Remove Props', 'Remove the closest prop')
+ LEOActions:AddItem(Cuff)
+ LEOActions:AddItem(Drag)
+ LEOActions:AddItem(Seat)
+ LEOActions:AddItem(Unseat)
+ if Config.Radar ~= 0 then
+ LEOActions:AddItem(Radar)
+ end
+ LEOActions:AddItem(Inventory)
+ LEOActions:AddItem(BAC)
+ if Config.LEOJail then
+ LEOActions:AddItem(Jail)
+ if UnjailAllowed then
+ LEOActions:AddItem(Unjail)
+ end
+ end
+ LEOActions:AddItem(Spikes)
+ LEOActions:AddItem(DelSpikes)
+ LEOActions:AddItem(Shield)
+ if Config.UnrackWeapons == 1 or Config.UnrackWeapons == 2 then
+ LEOActions:AddItem(CarbineRifle)
+ LEOActions:AddItem(Shotgun)
+ end
+ if Config.DisplayProps then
+ LEOActions:AddItem(Props)
+ LEOActions:AddItem(RemoveProps)
+ end
+ Cuff.Activated = function(ParentMenu, SelectedItem)
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:CuffNear', player)
+ end
+ end
+ Drag.Activated = function(ParentMenu, SelectedItem)
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:DragNear', player)
+ end
+ end
+ Seat.Activated = function(ParentMenu, SelectedItem)
+ local Veh = GetVehiclePedIsIn(Ped, true)
+
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:SeatNear', player, Veh)
+ end
+ end
+ Unseat.Activated = function(ParentMenu, SelectedItem)
+ if IsPedInAnyVehicle(GetPlayerPed(-1), true) then
+ Notify('~o~You need to be outside of the vehicle')
+ return
+ end
+
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:UnseatNear', player)
+ end
+ end
+ Radar.Activated = function(ParentMenu, SelectedItem)
+ ToggleRadar()
+ end
+ Inventory.Activated = function(ParentMenu, SelectedItem)
+ local player = GetClosestPlayer()
+ if player ~= false then
+ Notify('~b~Searching ...')
+ TriggerServerEvent('SEM_InteractionMenu:InventorySearch', player)
+ end
+ end
+ BAC.Activated = function(ParentMenu, SelectedItem)
+ local player = GetClosestPlayer()
+ if player ~= false then
+ Notify('~b~Testing ...')
+ TriggerServerEvent('SEM_InteractionMenu:BACTest', player)
+ end
+ end
+ Jail.Activated = function(ParentMenu, SelectedItem)
+ local PlayerID = tonumber(KeyboardInput('Player ID:', 10))
+ if PlayerID == nil then
+ Notify('~r~Please enter a player ID')
+ return
+ end
+
+ local JailTime = tonumber(KeyboardInput('Time: (Seconds) - Max Time: ' .. Config.MaxJailTime .. ' | Default Time: 30', string.len(Config.MaxJailTime)))
+ if JailTime == nil then
+ JailTime = 30
+ end
+ if JailTime > Config.MaxJailTime then
+ Notify('~y~Exceeded Max Time\nMax Time: ' .. Config.MaxJailTime .. ' seconds')
+ JailTime = Config.MaxJailTime
+ end
+
+ Notify('Player Jailed for ~b~' .. JailTime .. ' seconds')
+ TriggerServerEvent('SEM_InteractionMenu:Jail', PlayerID, JailTime)
+ end
+ Unjail.Activated = function(ParentMenu, SelectedItem)
+ local PlayerID = tonumber(KeyboardInput('Player ID:', 10))
+ if PlayerID == nil then
+ Notify('~r~Please enter a player ID')
+ return
+ end
+
+ TriggerServerEvent('SEM_InteractionMenu:Unjail', PlayerID)
+ end
+ DelSpikes.Activated = function(ParentMenu, SelectedItem)
+ TriggerEvent('SEM_InteractionMenu:Spikes-DeleteSpikes')
+ end
+ Shield.Activated = function(ParentMenu, SelectedItem)
+ if ShieldActive then
+ DisableShield()
+ else
+ EnableShield()
+ end
+ end
+ CarbineRifle.Activated = function(ParentMenu, SelectedItem)
+ if (GetVehicleClass(GetVehiclePedIsIn(GetPlayerPed(-1))) == 18) then
+ CarbineEquipped = not CarbineEquipped
+ ShotgunEquipped = false
+ elseif (GetVehicleClass(GetVehiclePedIsIn(GetPlayerPed(-1))) ~= 18) then
+ Notify('~r~You Must be in a Police Vehicle to rack/unrack your Carbine Rifle')
+ return
+ end
+
+ if CarbineEquipped then
+ Notify('~g~Carbine Rifle Equipped')
+ GiveWeapon('weapon_carbinerifle')
+ AddWeaponComponent('weapon_carbinerifle', 'component_at_ar_flsh')
+ AddWeaponComponent('weapon_carbinerifle', 'component_at_ar_afgrip')
+ else
+ Notify('~y~Carbine Rifle Unequipped')
+ RemoveWeaponFromPed(GetPlayerPed(-1), 'weapon_carbinerifle')
+ end
+ end
+ Shotgun.Activated = function(ParentMenu, SelectedItem)
+ if (GetVehicleClass(GetVehiclePedIsIn(GetPlayerPed(-1))) == 18) then
+ ShotgunEquipped = not ShotgunEquipped
+ CarbineEquipped = false
+ elseif (GetVehicleClass(GetVehiclePedIsIn(GetPlayerPed(-1))) ~= 18) then
+ Notify('~r~You Must be in a Police Vehicle to rack/unrack your Shotgun')
+ return
+ end
+
+ if ShotgunEquipped then
+ Notify('~g~Shotgun Equipped')
+ GiveWeapon('weapon_pumpshotgun')
+ AddWeaponComponent('weapon_pumpshotgun', 'component_at_ar_flsh')
+ else
+ Notify('~y~Shotgun Unequipped')
+ RemoveWeaponFromPed(GetPlayerPed(-1), 'weapon_pumpshotgun')
+ end
+ end
+ LEOActions.OnListSelect = function(sender, item, index)
+ if item == Spikes then
+ TriggerEvent('SEM_InteractionMenu:Spikes-SpawnSpikes', tonumber(index))
+ elseif item == Props then
+ for _, Prop in pairs(Config.Props) do
+ if Prop.name == item:IndexToItem(index) then
+ SpawnProp(Prop.spawncode, Prop.name)
+ end
+ end
+ end
+ end
+ RemoveProps.Activated = function(ParentMenu, SelectedItem)
+ for _, Prop in pairs(Config.Props) do
+ DeleteProp(Prop.spawncode)
+ end
+ end
+
+ if Config.DisplayBackup then
+ local LEOBackup = _MenuPool:AddSubMenu(LEOMenu, 'Backup', '', true)
+ LEOBackup:SetMenuWidthOffset(Config.MenuWidth)
+ --[[
+ Code 1 Backup | No Lights or Siren
+ Code 2 Backup | Only Lights
+ Code 3 Backup | Lights and Siren
+ Code 99 Backup | All Available Unit Responde Code 3
+ ]]
+ local BK1 = NativeUI.CreateItem('Code 1', 'Call Code 1 Backup to your location')
+ local BK2 = NativeUI.CreateItem('Code 2', 'Call Code 2 Backup to your location')
+ local BK3 = NativeUI.CreateItem('Code 3', 'Call Code 3 Backup to your location')
+ local BK99 = NativeUI.CreateItem('Code 99', 'Call Code 99 Backup to your location')
+ local PanicBTN = NativeUI.CreateItem('~r~Panic Button', 'Officer Panic Button')
+ LEOBackup:AddItem(BK1)
+ LEOBackup:AddItem(BK2)
+ LEOBackup:AddItem(BK3)
+ LEOBackup:AddItem(BK99)
+ LEOBackup:AddItem(PanicBTN)
+ BK1.Activated = function(ParentMenu, SelectedItem)
+ local Coords = GetEntityCoords(GetPlayerPed(-1))
+ local Street1, Street2 = GetStreetNameAtCoord(Coords.x, Coords.y, Coords.z)
+ local StreetName = GetStreetNameFromHashKey(Street1)
+
+ TriggerServerEvent('SEM_InteractionMenu:Backup', 1, StreetName, Coords)
+ end
+ BK2.Activated = function(ParentMenu, SelectedItem)
+ local Coords = GetEntityCoords(GetPlayerPed(-1))
+ local Street1, Street2 = GetStreetNameAtCoord(Coords.x, Coords.y, Coords.z)
+ local StreetName = GetStreetNameFromHashKey(Street1)
+
+ TriggerServerEvent('SEM_InteractionMenu:Backup', 2, StreetName, Coords)
+ end
+ BK3.Activated = function(ParentMenu, SelectedItem)
+ local Coords = GetEntityCoords(GetPlayerPed(-1))
+ local Street1, Street2 = GetStreetNameAtCoord(Coords.x, Coords.y, Coords.z)
+ local StreetName = GetStreetNameFromHashKey(Street1)
+
+ TriggerServerEvent('SEM_InteractionMenu:Backup', 3, StreetName, Coords)
+ end
+ BK99.Activated = function(ParentMenu, SelectedItem)
+ local Coords = GetEntityCoords(GetPlayerPed(-1))
+ local Street1, Street2 = GetStreetNameAtCoord(Coords.x, Coords.y, Coords.z)
+ local StreetName = GetStreetNameFromHashKey(Street1)
+
+ TriggerServerEvent('SEM_InteractionMenu:Backup', 99, StreetName, Coords)
+ end
+ PanicBTN.Activated = function(ParentMenu, SelectedItem)
+ local Coords = GetEntityCoords(GetPlayerPed(-1))
+ local Street1, Street2 = GetStreetNameAtCoord(Coords.x, Coords.y, Coords.z)
+ local StreetName = GetStreetNameFromHashKey(Street1)
+
+ TriggerServerEvent('SEM_InteractionMenu:Backup', 'panic', StreetName, Coords)
+ end
+ end
+
+ if Config.ShowStations then
+ local LEOStation = _MenuPool:AddSubMenu(LEOMenu, 'Stations', '', true)
+ LEOStation:SetMenuWidthOffset(Config.MenuWidth)
+ for _, Station in pairs(Config.LEOStations) do
+ local StationCategory = _MenuPool:AddSubMenu(LEOStation, Station.name, '', true)
+ StationCategory:SetMenuWidthOffset(Config.MenuWidth)
+ local SetWaypoint = NativeUI.CreateItem('Set Waypoint', 'Set a waypoint to the station')
+ local Teleport = NativeUI.CreateItem('Teleport', 'Teleport to the station')
+ StationCategory:AddItem(SetWaypoint)
+ if Config.AllowStationTeleport then
+ StationCategory:AddItem(Teleport)
+ end
+ SetWaypoint.Activated = function(ParentMenu, SelectedItem)
+ SetNewWaypoint(Station.coords.x, Station.coords.y)
+ end
+ Teleport.Activated = function(ParentMenu, SelectedItem)
+ SetEntityCoords(PlayerPedId(), Station.coords.x, Station.coords.y, Station.coords.z)
+ SetEntityHeading(PlayerPedId(), Station.coords.h)
+ end
+ end
+ end
+
+ if Config.DisplayLEOUniforms or Config.DisplayLEOLoadouts then
+ local LEOLoadouts = _MenuPool:AddSubMenu(LEOMenu, 'Loadouts', '', true)
+ LEOLoadouts:SetMenuWidthOffset(Config.MenuWidth)
+ UniformsList = {}
+ for _, Uniform in pairs(Config.LEOUniforms) do
+ table.insert(UniformsList, Uniform.name)
+ end
+
+ LoadoutsList = {}
+ for Name, Loadout in pairs(Config.LEOLoadouts) do
+ table.insert(LoadoutsList, Name)
+ end
+
+ local Uniforms = NativeUI.CreateListItem('Uniforms', UniformsList, 1, 'Spawn LEO uniforms')
+ local Loadouts = NativeUI.CreateListItem('Loadouts', LoadoutsList, 1, 'Spawn LEO weapon loadouts')
+ if Config.DisplayLEOUniforms then
+ LEOLoadouts:AddItem(Uniforms)
+ end
+ if Config.DisplayLEOLoadouts then
+ LEOLoadouts:AddItem(Loadouts)
+ end
+ LEOLoadouts.OnListSelect = function(sender, item, index)
+ if item == Uniforms then
+ for _, Uniform in pairs(Config.LEOUniforms) do
+ if Uniform.name == item:IndexToItem(index) then
+ LoadPed(Uniform.spawncode)
+ Notify('~b~Uniform Spawned: ~g~' .. Uniform.name)
+ end
+ end
+ end
+
+
+
+ if item == Loadouts then
+ for Name, Loadout in pairs(Config.LEOLoadouts) do
+ if Name == item:IndexToItem(index) then
+ SetEntityHealth(GetPlayerPed(-1), 200)
+ RemoveAllPedWeapons(GetPlayerPed(-1), true)
+ AddArmourToPed(GetPlayerPed(-1), 100)
+
+ for _, Weapon in pairs(Loadout) do
+ GiveWeapon(Weapon.weapon)
+
+ for _, Component in pairs(Weapon.components) do
+ AddWeaponComponent(Weapon.weapon, Component)
+ end
+ end
+
+ Notify('~b~Loadout Spawned: ~g~' .. Name)
+ end
+ end
+ end
+ end
+ end
+
+ if Config.ShowLEOVehicles then
+ local LEOVehicles = _MenuPool:AddSubMenu(LEOMenu, 'Vehicles', '', true)
+ LEOVehicles:SetMenuWidthOffset(Config.MenuWidth)
+
+ for Name, Category in pairs(Config.LEOVehiclesCategories) do
+ local LEOCategory = _MenuPool:AddSubMenu(LEOVehicles, Name, '', true)
+ LEOCategory:SetMenuWidthOffset(Config.MenuWidth)
+ for _, Vehicle in pairs(Category) do
+ local LEOVehicle = NativeUI.CreateItem(Vehicle.name, '')
+ LEOCategory:AddItem(LEOVehicle)
+ if Config.ShowLEOSpawnCode then
+ LEOVehicle:RightLabel(Vehicle.spawncode)
+ end
+ LEOVehicle.Activated = function(ParentMenu, SelectedItem)
+ SpawnVehicle(Vehicle.spawncode, Vehicle.name, Vehicle.livery, Vehicle.extras)
+ end
+ end
+ end
+ end
+
+ if Config.DisplayTrafficManager then
+ local LEOTrafficManager = _MenuPool:AddSubMenu(LEOMenu, 'Traffic Manager', '', true)
+ LEOTrafficManager:SetMenuWidthOffset(Config.MenuWidth)
+
+ TMSize = 10.0
+ TMSpeed = 0.0
+ RaduiesNames = {}
+ Raduies = {
+ {name = '10m', size = 10.0},
+ {name = '20m', size = 20.0},
+ {name = '30m', size = 30.0},
+ {name = '40m', size = 40.0},
+ {name = '50m', size = 50.0},
+ {name = '60m', size = 60.0},
+ {name = '70m', size = 70.0},
+ {name = '80m', size = 80.0},
+ {name = '90m', size = 90.0},
+ {name = '100m', size = 100.0},
+ }
+ SpeedsNames = {}
+ Speeds = {
+ {name = '0 mph', speed = 0.0},
+ {name = '5 mph', speed = 5.0},
+ {name = '10 mph', speed = 10.0},
+ {name = '15 mph', speed = 15.0},
+ {name = '20 mph', speed = 20.0},
+ {name = '25 mph', speed = 25.0},
+ {name = '30 mph', speed = 30.0},
+ {name = '40 mph', speed = 40.0},
+ {name = '50 mph', speed = 50.0},
+ }
+
+ for _, RaduisInfo in pairs(Raduies) do
+ table.insert(RaduiesNames, RaduisInfo.name)
+ end
+ for _, SpeedsInfo in pairs(Speeds) do
+ table.insert(SpeedsNames, SpeedsInfo.name)
+ end
+
+ local Radius = NativeUI.CreateListItem('Radius', RaduiesNames, 1, '')
+ local Speed = NativeUI.CreateListItem('Speed', SpeedsNames, 1, '')
+ local TMCreate = NativeUI.CreateItem('Create Speed Zone', '')
+ local TMDelete = NativeUI.CreateItem('Delete Speed Zone', '')
+ LEOTrafficManager:AddItem(Radius)
+ LEOTrafficManager:AddItem(Speed)
+ LEOTrafficManager:AddItem(TMCreate)
+ LEOTrafficManager:AddItem(TMDelete)
+ Radius.OnListChanged = function(sender, item, index)
+ TMSize = Raduies[index].size
+ end
+ Speed.OnListChanged = function(sender, item, index)
+ TMSpeed = Speeds[index].speed
+ end
+ TMCreate.Activated = function(ParentMenu, SelectedItem)
+ if Zone == nil then
+ Zone = AddSpeedZoneForCoord(GetEntityCoords(PlayerPedId()), TMSize, TMSpeed, false)
+ Area = AddBlipForRadius(GetEntityCoords(PlayerPedId()), TMSize)
+ SetBlipAlpha(Area, 100)
+ Notify('~g~Speed Zone Created')
+ else
+ Notify('~y~You already have a Speed Zone created')
+ end
+ end
+ TMDelete.Activated = function(ParentMenu, SelectedItem)
+ if Zone ~= nil then
+ RemoveSpeedZone(Zone)
+ RemoveBlip(Area)
+ Zone = nil
+ Notify('~r~Speed Zone Deleted')
+ else
+ Notify('~y~You don\'t have a Speed Zone')
+ end
+ end
+ end
+ end
+
+
+
+
+ if FireRestrict() then
+ local FireMenu = _MenuPool:AddSubMenu(MainMenu, 'Fire Toolbox', 'Fire Related Menu', true)
+ FireMenu:SetMenuWidthOffset(Config.MenuWidth)
+ local FireActions = _MenuPool:AddSubMenu(FireMenu, 'Actions', '', true)
+ FireActions:SetMenuWidthOffset(Config.MenuWidth)
+ local Drag = NativeUI.CreateItem('Drag', 'Drag/Undrag the closest player')
+ local Seat = NativeUI.CreateItem('Seat', 'Place a player in the closest vehicle')
+ local Unseat = NativeUI.CreateItem('Unseat', 'Remove a player from the closest vehicle')
+ FireActions:AddItem(Drag)
+ FireActions:AddItem(Seat)
+ FireActions:AddItem(Unseat)
+ Drag.Activated = function(ParentMenu, SelectedItem)
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:DragNear', player)
+ end
+ end
+ Seat.Activated = function(ParentMenu, SelectedItem)
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:SeatNear', player, Veh)
+ end
+ end
+ Unseat.Activated = function(ParentMenu, SelectedItem)
+ if IsPedInAnyVehicle(GetPlayerPed(-1), true) then
+ Notify('~o~You need to be outside of the vehicle')
+ return
+ end
+
+ local player = GetClosestPlayer()
+ if player ~= false then
+ TriggerServerEvent('SEM_InteractionMenu:UnseatNear', player)
+ end
+ end
+ if Config.FireHospital then
+ local HospitalLocations = _MenuPool:AddSubMenu(FireActions, 'Hospitalize', '', true)
+ HospitalLocations:SetMenuWidthOffset(Config.MenuWidth)
+ for HospitalName, HospitalInfo in pairs(Config.HospitalLocation) do
+ local Hospitalize = NativeUI.CreateItem(HospitalName, 'Hospitalize a player')
+ HospitalLocations:AddItem(Hospitalize)
+ Hospitalize.Activated = function(ParentMenu, SelectedItem)
+ local PlayerID = tonumber(KeyboardInput('Player ID:', 10))
+ if PlayerID == nil then
+ Notify('~r~Please enter a player ID')
+ return
+ end
+
+ local HospitalTime = tonumber(KeyboardInput('Time: (Seconds) - Max Time: ' .. Config.MaxHospitalTime .. ' | Default Time: 30', 3))
+ if HospitalTime == nil then
+ HospitalTime = 30
+ end
+ if HospitalTime > Config.MaxHospitalTime then
+ Notify('~y~Exceeded Max Time\nMax Time: ' .. Config.MaxHospitalTime .. ' seconds')
+ HospitalTime = Config.MaxHospitalTime
+ end
+
+ Notify('Player Hospitalized for ~b~' .. HospitalTime .. ' seconds')
+ TriggerServerEvent('SEM_InteractionMenu:Hospitalize', PlayerID, HospitalTime, HospitalInfo)
+ end
+ end
+ local Unhospitalize = NativeUI.CreateItem('Unhospitalize', 'Unhospitalize a player')
+ if UnhospitalAllowed then
+ FireActions:AddItem(Unhospitalize)
+ end
+ Unhospitalize.Activated = function(ParentMenu, SelectedItem)
+ local PlayerID = tonumber(KeyboardInput('Player ID:', 10))
+ if PlayerID == nil then
+ Notify('~r~Please enter a player ID')
+ return
+ end
+
+ TriggerServerEvent('SEM_InteractionMenu:Unhospitalize', PlayerID)
+ end
+ end
+ PropsList = {}
+ for _, Prop in pairs(Config.Props) do
+ table.insert(PropsList, Prop.name)
+ end
+ local Props = NativeUI.CreateListItem('Spawn Props', PropsList, 1, 'Spawn props on the ground')
+ local RemoveProps = NativeUI.CreateItem('Remove Props', 'Remove the closest prop')
+ FireActions:AddItem(Props)
+ FireActions:AddItem(RemoveProps)
+ FireActions.OnListSelect = function(sender, item, index)
+ if item == Props then
+ for _, Prop in pairs(Config.Props) do
+ if Prop.name == item:IndexToItem(index) then
+ SpawnProp(Prop.spawncode, Prop.name)
+ end
+ end
+ end
+ end
+ RemoveProps.Activated = function(ParentMenu, SelectedItem)
+ for _, Prop in pairs(Config.Props) do
+ DeleteProp(Prop.spawncode)
+ end
+ end
+
+ if Config.ShowStations then
+ local FireEMSStation = _MenuPool:AddSubMenu(FireMenu, 'Stations', '', true)
+ FireEMSStation:SetMenuWidthOffset(Config.MenuWidth)
+ local FireStation = _MenuPool:AddSubMenu(FireEMSStation, 'Fire Stations', '', true)
+ FireStation:SetMenuWidthOffset(Config.MenuWidth)
+ for _, Station in pairs(Config.FireStations) do
+ local StationCategory = _MenuPool:AddSubMenu(FireStation, Station.name, '', true)
+ StationCategory:SetMenuWidthOffset(Config.MenuWidth)
+ local SetWaypoint = NativeUI.CreateItem('Set Waypoint', 'Set a waypoint to the station')
+ local Teleport = NativeUI.CreateItem('Teleport', 'Teleport to the station')
+ StationCategory:AddItem(SetWaypoint)
+ if Config.AllowStationTeleport then
+ StationCategory:AddItem(Teleport)
+ end
+ SetWaypoint.Activated = function(ParentMenu, SelectedItem)
+ SetNewWaypoint(Station.coords.x, Station.coords.y)
+ end
+ Teleport.Activated = function(ParentMenu, SelectedItem)
+ SetEntityCoords(PlayerPedId(), Station.coords.x, Station.coords.y, Station.coords.z)
+ SetEntityHeading(PlayerPedId(), Station.coords.h)
+ end
+ end
+
+ local EMSStation = _MenuPool:AddSubMenu(FireEMSStation, 'Hospitals', '', true)
+ EMSStation:SetMenuWidthOffset(Config.MenuWidth)
+ for _, Station in pairs(Config.HospitalStations) do
+ local StationCategory = _MenuPool:AddSubMenu(EMSStation, Station.name, '', true)
+ StationCategory:SetMenuWidthOffset(Config.MenuWidth)
+ local SetWaypoint = NativeUI.CreateItem('Set Waypoint', 'Set a waypoint to the hospital')
+ local Teleport = NativeUI.CreateItem('Teleport', 'Teleport to the hospital')
+ StationCategory:AddItem(SetWaypoint)
+ if Config.AllowStationTeleport then
+ StationCategory:AddItem(Teleport)
+ end
+ SetWaypoint.Activated = function(ParentMenu, SelectedItem)
+ SetNewWaypoint(Station.coords.x, Station.coords.y)
+ end
+ Teleport.Activated = function(ParentMenu, SelectedItem)
+ SetEntityCoords(PlayerPedId(), Station.coords.x, Station.coords.y, Station.coords.z)
+ SetEntityHeading(PlayerPedId(), Station.coords.h)
+ end
+ end
+ end
+
+ if Config.DisplayFireUniforms or Config.DisplayFireLoadouts then
+ local FireLoadouts = _MenuPool:AddSubMenu(FireMenu, 'Loadouts', '', true)
+ FireLoadouts:SetMenuWidthOffset(Config.MenuWidth)
+ UniformsList = {}
+ for _, Uniform in pairs(Config.FireUniforms) do
+ table.insert(UniformsList, Uniform.name)
+ end
+
+ LoadoutsList = {
+ 'Clear',
+ 'Standard',
+ }
+ local Uniforms = NativeUI.CreateListItem('Uniforms', UniformsList, 1, 'Spawn Fire uniforms')
+ local Loadouts = NativeUI.CreateListItem('Loadouts', LoadoutsList, 1, 'Spawns Fire weapon loadouts')
+ if Config.DisplayFireUniforms then
+ FireLoadouts:AddItem(Uniforms)
+ end
+ if Config.DisplayFireLoadouts then
+ FireLoadouts:AddItem(Loadouts)
+ end
+ FireLoadouts.OnListSelect = function(sender, item, index)
+ if item == Uniforms then
+ for _, Uniform in pairs(Config.FireUniforms) do
+ if Uniform.name == item:IndexToItem(index) then
+ LoadPed(Uniform.spawncode)
+ Notify('~b~Uniform Spawned: ~g~' .. Uniform.name)
+ end
+ end
+ end
+
+
+
+ if item == Loadouts then
+ local SelectedLoadout = item:IndexToItem(index)
+ if SelectedLoadout == 'Clear' then
+ SetEntityHealth(GetPlayerPed(-1), 200)
+ RemoveAllPedWeapons(GetPlayerPed(-1), true)
+ Notify('~r~All Weapons Cleared!')
+ elseif SelectedLoadout == 'Standard' then
+ SetEntityHealth(GetPlayerPed(-1), 200)
+ RemoveAllPedWeapons(GetPlayerPed(-1), true)
+ AddArmourToPed(GetPlayerPed(-1), 100)
+ GiveWeapon('weapon_flashlight')
+ GiveWeapon('weapon_fireextinguisher')
+ GiveWeapon('weapon_flare')
+ GiveWeapon('weapon_stungun')
+ Notify('~b~Loadout Spawned: ~g~' .. SelectedLoadout)
+ end
+ end
+ end
+ end
+
+ if Config.ShowFireVehicles then
+ local FireVehicles = _MenuPool:AddSubMenu(FireMenu, 'Vehicles', '', true)
+ FireVehicles:SetMenuWidthOffset(Config.MenuWidth)
+
+ for _, Vehicle in pairs(Config.FireVehicles) do
+ local FireVehicle = NativeUI.CreateItem(Vehicle.name, '')
+ FireVehicles:AddItem(FireVehicle)
+ if Config.ShowFireSpawnCode then
+ FireVehicle:RightLabel(Vehicle.spawncode)
+ end
+ FireVehicle.Activated = function(ParentMenu, SelectedItem)
+ SpawnVehicle(Vehicle.spawncode, Vehicle.name, Vehicle.livery, Vehicle.extras)
+ end
+ end
+ end
+ end
+
+
+
+
+ if CivRestrict() then
+ local CivMenu = _MenuPool:AddSubMenu(MainMenu, 'Civ Toolbox', 'Civilian Related Menu', true)
+ CivMenu:SetMenuWidthOffset(Config.MenuWidth)
+ local CivActions = _MenuPool:AddSubMenu(CivMenu, 'Actions', '', true)
+ CivActions:SetMenuWidthOffset(Config.MenuWidth)
+ local HU = NativeUI.CreateItem('Hands Up', 'Put your hands up')
+ local HUK = NativeUI.CreateItem('Hand Up & Kneel', 'Put your hands up and kneel on the ground')
+ local Inventory = NativeUI.CreateItem('Inventory', 'Set your inventory')
+ local BAC = NativeUI.CreateItem('BAC', 'Set your BAC level')
+ local DropWeapon = NativeUI.CreateItem('Drop Weapon', 'Drop your current weapon on the ground')
+ CivActions:AddItem(HU)
+ CivActions:AddItem(HUK)
+ CivActions:AddItem(Inventory)
+ CivActions:AddItem(BAC)
+ CivActions:AddItem(DropWeapon)
+ HU.Activated = function(ParentMenu, SelectedItem)
+ local Ped = PlayerPedId()
+ if DoesEntityExist(Ped) and not HandCuffed then
+ Citizen.CreateThread(function()
+ LoadAnimation('random@mugging3')
+ if IsEntityPlayingAnim(Ped, 'random@mugging3', 'handsup_standing_base', 3) or HandCuffed then
+ ClearPedSecondaryTask(Ped)
+ SetEnableHandcuffs(Ped, false)
+ elseif not IsEntityPlayingAnim(Ped, 'random@mugging3', 'handsup_standing_base', 3) or not HandCuffed then
+ TaskPlayAnim(Ped, 'random@mugging3', 'handsup_standing_base', 8.0, -8, -1, 49, 0, 0, 0, 0)
+ SetEnableHandcuffs(Ped, true)
+ end
+ end)
+ end
+ end
+ HUK.Activated = function(ParentMenu, SelectedItem)
+ local Ped = PlayerPedId()
+ if (DoesEntityExist(Ped) and not IsEntityDead(Ped)) and not HandCuffed then
+ Citizen.CreateThread(function()
+ LoadAnimation('random@arrests')
+ if (IsEntityPlayingAnim(Ped, 'random@arrests', 'kneeling_arrest_idle', 3)) then
+ TaskPlayAnim(Ped, 'random@arrests', 'kneeling_arrest_get_up', 8.0, 1.0, -1, 128, 0, 0, 0, 0)
+ else
+ TaskPlayAnim(Ped, 'random@arrests', 'idle_2_hands_up', 8.0, 1.0, -1, 2, 0, 0, 0, 0)
+ Wait (4000)
+ TaskPlayAnim(Ped, 'random@arrests', 'kneeling_arrest_idle', 8.0, 1.0, -1, 2, 0, 0, 0, 0)
+ end
+ end)
+ end
+ end
+ Inventory.Activated = function(ParentMenu, SelectedItem)
+ local Items = KeyboardInput('Items:', 75)
+ if Items == nil or Items == '' then
+ Notify('~r~No Items Provided!')
+ return
+ end
+
+ TriggerServerEvent('SEM_InteractionMenu:InventorySet', Items)
+ Notify('~g~Inventory Set!')
+ end
+ BAC.Activated = function(ParentMenu, SelectedItem)
+ local BACLevel = KeyboardInput('BAC Level - Legal Limit: 0.08', 5)
+ if BACLevel == nil or BACLevel == '' then
+ Notify('~r~No BAC Level Provided!')
+ return
+ end
+
+ TriggerServerEvent('SEM_InteractionMenu:BACSet', tonumber(BACLevel))
+ if tonumber(BACLevel) < 0.08 then
+ Notify('~b~BAC Level Set: ~g~' .. tostring(BACLevel))
+ else
+ Notify('~b~BAC Level Set: ~r~' .. tostring(BACLevel))
+ end
+ end
+ DropWeapon.Activated = function(ParentMenu, SelectedItem)
+ local CurrentWeapon = GetSelectedPedWeapon(PlayerPedId())
+ SetCurrentPedWeapon(PlayerPedId(), 'weapon_unarmed', true)
+ SetPedDropsInventoryWeapon(GetPlayerPed(-1), CurrentWeapon, -2.0, 0.0, 0.5, 30)
+ Notify('~r~Weapon Dropped!')
+ end
+ if Config.ShowCivAdverts then
+ local CivAdverts = _MenuPool:AddSubMenu(CivMenu, 'Adverts', '', true)
+ CivAdverts:SetMenuWidthOffset(Config.MenuWidth)
+ for _, Ad in pairs(Config.CivAdverts) do
+ local Advert = NativeUI.CreateItem(Ad.name, 'Send an advert for ' .. Ad.name)
+ CivAdverts:AddItem(Advert)
+ Advert.Activated = function(ParentMenu, SelectedItem)
+ local Message = KeyboardInput('Message:', 128)
+ if Message == nil or Message == '' then
+ Notify('~r~No Advert Message Provided!')
+ return
+ end
+
+ TriggerServerEvent('SEM_InteractionMenu:Ads', Message, Ad.name, Ad.loc, Ad.file)
+ end
+ end
+ end
+ if Config.ShowCivVehicles then
+ local CivVehicles = _MenuPool:AddSubMenu(CivMenu, 'Vehicles', '', true)
+ CivVehicles:SetMenuWidthOffset(Config.MenuWidth)
+
+ for _, Vehicle in pairs(Config.CivVehicles) do
+ local CivVehicle = NativeUI.CreateItem(Vehicle.name, '')
+ CivVehicles:AddItem(CivVehicle)
+ if Config.ShowCivSpawnCode then
+ CivVehicle:RightLabel(Vehicle.spawncode)
+ end
+ CivVehicle.Activated = function(ParentMenu, SelectedItem)
+ SpawnVehicle(Vehicle.spawncode, Vehicle.name)
+ end
+ end
+ end
+ end
+
+
+
+
+
+ if VehicleRestrict() then
+ local VehicleMenu = _MenuPool:AddSubMenu(MainMenu, 'Vehicle', 'Vehicle Related Menu', true)
+ VehicleMenu:SetMenuWidthOffset(Config.MenuWidth)
+ local Seats = {-1, 0, 1, 2}
+ local Windows = {'Front', 'Rear', 'All'}
+ local Doors = {'Driver', 'Passenger', 'Rear Right', 'Rear Left', 'Hood', 'Trunk', 'All'}
+ local Engine = NativeUI.CreateItem('Toggle Engine', 'Toggle your vehicle\'s engine')
+ local ILights = NativeUI.CreateItem('Toggle Interior Light', 'Toggle your vehicle\'s interior light')
+ local Seat = NativeUI.CreateSliderItem('Change Seats', Seats, 1, 'Switch to a different seat')
+ local Window = NativeUI.CreateListItem('Windows', Windows, 1, 'Open/Close your vehicle\'s windows')
+ local Door = NativeUI.CreateListItem('Doors', Doors, 1, 'Open/Close your vehicle\'s doors')
+ local FixVeh = NativeUI.CreateItem('Repair Vehicle', 'Repair your current vehicle')
+ local CleanVeh = NativeUI.CreateItem('Clean Vehicle', 'Clean your current vehicle')
+ local DelVeh = NativeUI.CreateItem('~r~Delete Vehicle', 'Delete your current vehicle')
+ VehicleMenu:AddItem(Engine)
+ VehicleMenu:AddItem(ILights)
+ VehicleMenu:AddItem(Seat)
+ VehicleMenu:AddItem(Window)
+ VehicleMenu:AddItem(Door)
+ if Config.VehicleOptions then
+ VehicleMenu:AddItem(FixVeh)
+ VehicleMenu:AddItem(CleanVeh)
+ VehicleMenu:AddItem(DelVeh)
+ end
+ Engine.Activated = function(ParentMenu, SelectedItem)
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
+ if Vehicle ~= nil and Vehicle ~= 0 and GetPedInVehicleSeat(Vehicle, 0) then
+ SetVehicleEngineOn(Vehicle, (not GetIsVehicleEngineRunning(Vehicle)), false, true)
+ Notify('~g~Engine Toggled!')
+ else
+ Notify('~r~You\'re not in a Vehicle!')
+ end
+ end
+ ILights.Activated = function(ParentMenu, SelectedItem)
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
+
+ if IsPedInVehicle(PlayerPedId(), Vehicle, false) then
+ if IsVehicleInteriorLightOn(Vehicle) then
+ SetVehicleInteriorlight(Vehicle, false)
+ else
+ SetVehicleInteriorlight(Vehicle, true)
+ end
+ else
+ Notify('~r~You\'re not in a Vehicle!')
+ end
+ end
+ VehicleMenu.OnSliderChange = function(sender, item, index)
+ if item == Seat then
+ VehicleSeat = item:IndexToItem(index)
+ local Veh = GetVehiclePedIsIn(GetPlayerPed(-1),false)
+ SetPedIntoVehicle(PlayerPedId(), Veh, VehicleSeat)
+ end
+ end
+ VehicleMenu.OnListSelect = function(sender, item, index)
+ local Ped = GetPlayerPed(-1)
+ local Veh = GetVehiclePedIsIn(Ped, false)
+
+ if item == Window then
+ VehicleWindow = item:IndexToItem(index)
+ if VehicleWindow == 'Front' then
+ if IsPedInAnyVehicle(Ped, false) then
+ if (GetPedInVehicleSeat(Veh, -1) == Ped) then
+ SetEntityAsMissionEntity(Veh, true, true)
+ if (WindowFrontRolled) then
+ RollDownWindow(Veh, 0)
+ RollDownWindow(Veh, 1)
+ WindowFrontRolled = false
+ else
+ RollUpWindow(Veh, 0)
+ RollUpWindow(Veh, 1)
+ WindowFrontRolled = true
+ end
+ end
+ end
+ elseif VehicleWindow == 'Rear' then
+ if IsPedInAnyVehicle(Ped, false) then
+ if (GetPedInVehicleSeat(Veh, -1) == Ped) then
+ SetEntityAsMissionEntity(Veh, true, true)
+ if (WindowFrontRolled) then
+ RollDownWindow(Veh, 2)
+ RollDownWindow(Veh, 3)
+ WindowFrontRolled = false
+ else
+ RollUpWindow(Veh, 2)
+ RollUpWindow(Veh, 3)
+ WindowFrontRolled = true
+ end
+ end
+ end
+ elseif VehicleWindow == 'All' then
+ if IsPedInAnyVehicle(Ped, false) then
+ if (GetPedInVehicleSeat(Veh, -1) == Ped) then
+ SetEntityAsMissionEntity(Veh, true, true)
+ if (WindowFrontRolled) then
+ RollDownWindow(Veh, 0)
+ RollDownWindow(Veh, 1)
+ RollDownWindow(Veh, 2)
+ RollDownWindow(Veh, 3)
+ WindowFrontRolled = false
+ else
+ RollUpWindow(Veh, 0)
+ RollUpWindow(Veh, 1)
+ RollUpWindow(Veh, 2)
+ RollUpWindow(Veh, 3)
+ WindowFrontRolled = true
+ end
+ end
+ end
+ end
+ elseif item == Door then
+ local Doors = {'Driver', 'Passenger', 'Rear Left', 'Rear Right', 'Hood', 'Trunk', 'All'}
+ VehicleDoor = item:IndexToItem(index)
+ if VehicleDoor == 'Driver' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 0) > 0 then
+ SetVehicleDoorShut(Veh, 0, false)
+ else
+ SetVehicleDoorOpen(Veh, 0, false, false)
+ end
+ end
+ elseif VehicleDoor == 'Passenger' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 1) > 0 then
+ SetVehicleDoorShut(Veh, 1, false)
+ else
+ SetVehicleDoorOpen(Veh, 1, false, false)
+ end
+ end
+ elseif VehicleDoor == 'Rear Left' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 2) > 0 then
+ SetVehicleDoorShut(Veh, 2, false)
+ else
+ SetVehicleDoorOpen(Veh, 2, false, false)
+ end
+ end
+ elseif VehicleDoor == 'Rear Right' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 3) > 0 then
+ SetVehicleDoorShut(Veh, 3, false)
+ else
+ SetVehicleDoorOpen(Veh, 3, false, false)
+ end
+ end
+ elseif VehicleDoor == 'Hood' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 4) > 0 then
+ SetVehicleDoorShut(Veh, 4, false)
+ else
+ SetVehicleDoorOpen(Veh, 4, false, false)
+ end
+ end
+ elseif VehicleDoor == 'Trunk' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 5) > 0 then
+ SetVehicleDoorShut(Veh, 5, false)
+ else
+ SetVehicleDoorOpen(Veh, 5, false, false)
+ end
+ end
+ elseif VehicleDoor == 'All' then
+ if Veh ~= nil and Veh ~= 0 and Veh ~= 1 then
+ if GetVehicleDoorAngleRatio(Veh, 0) > 0 then
+ SetVehicleDoorShut(Veh, 0, false)
+ SetVehicleDoorShut(Veh, 1, false)
+ SetVehicleDoorShut(Veh, 2, false)
+ SetVehicleDoorShut(Veh, 3, false)
+ SetVehicleDoorShut(Veh, 4, false)
+ SetVehicleDoorShut(Veh, 5, false)
+ else
+ SetVehicleDoorOpen(Veh, 0, false, false)
+ SetVehicleDoorOpen(Veh, 1, false, false)
+ SetVehicleDoorOpen(Veh, 2, false, false)
+ SetVehicleDoorOpen(Veh, 3, false, false)
+ SetVehicleDoorOpen(Veh, 4, false, false)
+ SetVehicleDoorOpen(Veh, 5, false, false)
+ end
+ end
+ end
+ end
+ end
+ FixVeh.Activated = function(ParentMenu, SelectedItem)
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
+ if Vehicle ~= nil and Vehicle ~= 0 then
+ SetVehicleEngineHealth(Vehicle, 100)
+ SetVehicleFixed(Vehicle)
+ Notify('~g~Vehicle Repaired!')
+ else
+ Notify('~r~You\'re not in a Vehicle!')
+ end
+
+ end
+ CleanVeh.Activated = function(ParentMenu, SelectedItem)
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
+ if Vehicle ~= nil and Vehicle ~= 0 then
+ SetVehicleDirtLevel(Vehicle, 0)
+ Notify('~g~Vehicle Cleaned!')
+ else
+ Notify('~r~You\'re not in a Vehicle!')
+ end
+ end
+ DelVeh.Activated = function(ParentMenu, SelectedItem)
+ if (IsPedSittingInAnyVehicle(PlayerPedId())) then
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
+
+ if (GetPedInVehicleSeat(Vehicle, -1) == PlayerPedId()) then
+ SetEntityAsMissionEntity(Vehicle, true, true)
+ DeleteVehicle(Vehicle)
+
+ if (DoesEntityExist(Vehicle)) then
+ Notify('~o~Unable to delete vehicle, try again.')
+ else
+ Notify('~r~Vehicle Deleted!')
+ end
+ else
+ Notify('~r~You must be in the driver\'s seat!')
+ end
+ else
+ Notify('~r~You\'re not in a Vehicle!')
+ end
+ end
+ end
+
+
+
+
+
+ if EmoteRestrict() then
+ local EmotesList = {}
+ for _, Emote in pairs(Config.EmotesList) do
+ table.insert(EmotesList, Emote.name)
+ end
+
+ local EmotesMenu = NativeUI.CreateListItem('Emotes', EmotesList, 1, 'General RP Emotes')
+ MainMenu:AddItem(EmotesMenu)
+
+ MainMenu.OnListSelect = function(sender, item, index)
+ if item == EmotesMenu then
+ for _, Emote in pairs(Config.EmotesList) do
+ if Emote.name == item:IndexToItem(index) then
+ PlayEmote(Emote.emote, Emote.name)
+ end
+ end
+ end
+ end
+ end
+
+
+
+ _MenuPool:RefreshIndex()
+end
+
+
+
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(0)
+
+ _MenuPool:ProcessMenus()
+ _MenuPool:ControlDisablingEnabled(false)
+ _MenuPool:MouseControlsEnabled(false)
+
+ if IsControlJustPressed(1, Config.MenuButton) and GetLastInputMethod(2) then
+ if not menuOpen then
+ Menu()
+ MainMenu:Visible(true)
+ else
+ _MenuPool:CloseAllMenus()
+ end
+ end
+ end
+end)
+
+
+
+RegisterCommand(Config.Command, function(source, args, rawCommands)
+ if Config.OpenMenu == 1 then
+ Menu()
+ MainMenu:Visible(true)
+ end
+end)
+
+Citizen.CreateThread(function()
+ if Config.OpenMenu == 1 then
+ TriggerEvent('chat:addSuggestion', '/' .. Config.Command, 'Used to open SEM_InteractionMenu')
+ end
+end)
diff --git a/resources/Interaction-Menu/server.lua b/resources/Interaction-Menu/server.lua
new file mode 100644
index 000000000..abb055a30
--- /dev/null
+++ b/resources/Interaction-Menu/server.lua
@@ -0,0 +1,246 @@
+--[[
+───────────────────────────────────────────────────────────────
+
+ SEM_InteractionMenu (server.lua) - Created by Scott M
+ Current Version: v1.7.1 (Sep 2021)
+
+ Support: https://semdevelopment.com/discord
+
+ !!! Change vaules in the 'config.lua' !!!
+ DO NOT EDIT THIS IF YOU DON'T KNOW WHAT YOU ARE DOING
+
+───────────────────────────────────────────────────────────────
+]]
+
+
+
+RegisterServerEvent('SEM_InteractionMenu:GlobalChat')
+AddEventHandler('SEM_InteractionMenu:GlobalChat', function(Color, Prefix, Message)
+ TriggerClientEvent('chatMessage', -1, Prefix, Color, Message)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:CuffNear')
+AddEventHandler('SEM_InteractionMenu:CuffNear', function(ID)
+ if ID == -1 or ID == '-1' then
+ if source ~= '' then
+ print('^1[#' .. source .. '] ' .. GetPlayerName(source) .. ' - attempted to cuff all players^7')
+ DropPlayer(source, '\n[SEM_InteractionMenu] Attempting to cuff all players')
+ else
+ print('^1Someone attempted to cuff all players^7')
+ end
+
+ return
+ end
+
+ if ID ~= false then
+ TriggerClientEvent('SEM_InteractionMenu:Cuff', ID)
+ end
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:DragNear')
+AddEventHandler('SEM_InteractionMenu:DragNear', function(ID)
+ if ID == -1 or ID == '-1' then
+ if source ~= '' then
+ print('^1[#' .. source .. '] ' .. GetPlayerName(source) .. ' - attempted to drag all players^7')
+ DropPlayer(source, '\n[SEM_InteractionMenu] Attempting to drag all players')
+ else
+ print('^1Someone attempted to drag all players^7')
+ end
+
+ return
+ end
+
+ if ID ~= false and ID ~= source then
+ TriggerClientEvent('SEM_InteractionMenu:Drag', ID, source)
+ end
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:SeatNear')
+AddEventHandler('SEM_InteractionMenu:SeatNear', function(ID, Vehicle)
+ TriggerClientEvent('SEM_InteractionMenu:Seat', ID, Vehicle)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:UnseatNear')
+AddEventHandler('SEM_InteractionMenu:UnseatNear', function(ID, Vehicle)
+ TriggerClientEvent('SEM_InteractionMenu:Unseat', ID, Vehicle)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:Jail')
+AddEventHandler('SEM_InteractionMenu:Jail', function(ID, Time)
+ if ID == -1 or ID == '-1' then
+ if source ~= '' then
+ print('^1[#' .. source .. '] ' .. GetPlayerName(source) .. ' - attempted to jail all players^7')
+ DropPlayer(source, '\n[SEM_InteractionMenu] Attempting to jail all players')
+ else
+ print('^1Someone attempted to jail all players^7')
+ end
+
+ return
+ end
+
+ TriggerClientEvent('SEM_InteractionMenu:JailPlayer', ID, Time)
+ TriggerClientEvent('chatMessage', -1, 'Judge', {86, 96, 252}, GetPlayerName(ID) .. ' has been Jailed for ' .. Time .. ' months(s)')
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:Unjail')
+AddEventHandler('SEM_InteractionMenu:Unjail', function(ID)
+ TriggerClientEvent('SEM_InteractionMenu:UnjailPlayer', ID)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:Backup')
+AddEventHandler('SEM_InteractionMenu:Backup', function(Code, StreetName, Coords)
+ TriggerClientEvent('SEM_InteractionMenu:CallBackup', -1, Code, StreetName, Coords)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:Ads')
+AddEventHandler('SEM_InteractionMenu:Ads', function(Text, Name, Loc, File)
+ TriggerClientEvent('SEM_InteractionMenu:SyncAds', -1, Text, Name, Loc, File, source)
+end)
+
+BACList = {}
+RegisterServerEvent('SEM_InteractionMenu:BACSet')
+AddEventHandler('SEM_InteractionMenu:BACSet', function(BACLevel)
+ BACList[source] = BACLevel
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:BACTest')
+AddEventHandler('SEM_InteractionMenu:BACTest', function(ID)
+ local BACLevel = BACList[ID]
+ TriggerClientEvent('SEM_InteractionMenu:BACResult', source, BACLevel)
+end)
+
+Inventories = {}
+RegisterServerEvent('SEM_InteractionMenu:InventorySet')
+AddEventHandler('SEM_InteractionMenu:InventorySet', function(Items)
+ Inventories[source] = Items
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:InventorySearch')
+AddEventHandler('SEM_InteractionMenu:InventorySearch', function(ID)
+ local Inventory = Inventories[ID]
+
+ TriggerClientEvent('SEM_InteractionMenu:InventoryResult', source, Inventory)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:Hospitalize')
+AddEventHandler('SEM_InteractionMenu:Hospitalize', function(ID, Time, Location)
+ if ID == -1 or ID == '-1' then
+ if source ~= '' then
+ print('^1[#' .. source .. '] ' .. GetPlayerName(source) .. ' - attempted to hospitalize all players^7')
+ DropPlayer(source, '\n[SEM_InteractionMenu] Attempting to hospitalize all players')
+ else
+ print('^1Someone attempted to hospitalize all players^7')
+ end
+
+ return
+ end
+
+ TriggerClientEvent('SEM_InteractionMenu:HospitalizePlayer', ID, Time, Location)
+ TriggerClientEvent('chatMessage', -1, 'Doctor', {86, 96, 252}, GetPlayerName(ID) .. ' has been Hospitalized for ' .. Time .. ' months(s)')
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:Unhospitalize')
+AddEventHandler('SEM_InteractionMenu:Unhospitalize', function(ID)
+ TriggerClientEvent('SEM_InteractionMenu:UnhospitalizePlayer', ID)
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:LEOPerms')
+AddEventHandler('SEM_InteractionMenu:LEOPerms', function()
+ if IsPlayerAceAllowed(source, 'sem_intmenu.leo') then
+ TriggerClientEvent('SEM_InteractionMenu:LEOPermsResult', source, true)
+ else
+ TriggerClientEvent('SEM_InteractionMenu:LEOPermsResult', source, false)
+ end
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:FirePerms')
+AddEventHandler('SEM_InteractionMenu:FirePerms', function()
+ if IsPlayerAceAllowed(source, 'sem_intmenu.fire') then
+ TriggerClientEvent('SEM_InteractionMenu:FirePermsResult', source, true)
+ else
+ TriggerClientEvent('SEM_InteractionMenu:FirePermsResult', source, false)
+ end
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:UnjailPerms')
+AddEventHandler('SEM_InteractionMenu:UnjailPerms', function()
+ if IsPlayerAceAllowed(source, 'sem_intmenu.unjail') then
+ TriggerClientEvent('SEM_InteractionMenu:UnjailPermsResult', source, true)
+ else
+ TriggerClientEvent('SEM_InteractionMenu:UnjailPermsResult', source, false)
+ end
+end)
+
+RegisterServerEvent('SEM_InteractionMenu:UnhospitalPerms')
+AddEventHandler('SEM_InteractionMenu:UnhospitalPerms', function()
+ if IsPlayerAceAllowed(source, 'sem_intmenu.unhospital') then
+ TriggerClientEvent('SEM_InteractionMenu:UnhospitalPermsResult', source, true)
+ else
+ TriggerClientEvent('SEM_InteractionMenu:UnhospitalPermsResult', source, false)
+ end
+end)
+
+
+local resourceName =
+[[^3
+ _____ ______ _____ _____
+ / ____| | ___| | \ / |
+ | (___ | |___ | |\ \ / /| |
+ \___ \ | ___| | | \ \/ / | |
+ ____) | | |___ | | \ / | |
+ \_____/ |______| |__| \__/ |__|^7
+ InteractionMenu
+ Created By Scott M
+]]
+
+Citizen.CreateThread(function()
+ local currentVersion = GetResourceMetadata(GetCurrentResourceName(), 'version', 0)
+
+ function VersionCheckHTTPRequest()
+ PerformHttpRequest('https://semdevelopment.com/releases/interactionmenu/info/version.json', VersionCheck, 'GET')
+ end
+
+ function VersionCheck(err, response, headers)
+ Citizen.Wait(3000)
+ if err == 200 then
+ local data = json.decode(response)
+
+ if Config.VersionChecker == 0 then
+ print(resourceName)
+ end
+
+ if currentVersion ~= data.NewestVersion then
+ if Config.VersionChecker == 0 then
+ print('\n ^1SEM_InteractionMenu is outdated!^7')
+ print(' Latest Version: ^2' .. data.NewestVersion .. '^7')
+ print(' Your Version: ^1' .. currentVersion .. '^7')
+ print(' Please download the leastest version from ^5' .. data.DownloadLocation .. '^7')
+
+ if data.Changes ~= '' then
+ print('\n ^5Changes: ^7' .. data.Changes)
+ end
+ elseif Config.VersionChecker == 1 then
+ print('\n^1SEM_InteractionMenu is outdated!^7')
+ print('Latest Version: ^2' .. data.NewestVersion .. '^7')
+ print('Please download the leastest version from ^5' .. data.DownloadLocation .. '^7\n')
+ end
+ else
+ print('\n ^2SEM_InteractionMenu is up to date!^7')
+ end
+
+ print('\n')
+ else
+ print('^1SEM_InteractionMenu Version Check Failed!^7')
+ end
+
+ SetTimeout(60000000, VersionCheckHTTPRequest)
+ end
+
+ if Config.VersionChecker ~= 2 then
+ if currentVersion then
+ VersionCheckHTTPRequest()
+ else
+ print('^1SEM_InteractionMenu Version Check Failed!^7')
+ end
+ end
+end)
diff --git a/resources/Interaction-Menu/stream/char_floyd.ytd b/resources/Interaction-Menu/stream/char_floyd.ytd
new file mode 100644
index 000000000..68efafd77
--- /dev/null
+++ b/resources/Interaction-Menu/stream/char_floyd.ytd
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f01c0e21d2134a67772d132e247918a3e494810ea64f939e108b7067eb7b165
+size 260166
diff --git a/resources/[EGRP-Discord-Integration]/Discord-VehRestriction/config.lua b/resources/[EGRP-Discord-Integration]/Discord-VehRestriction/config.lua
index 5df416dd0..672fb2ab0 100644
--- a/resources/[EGRP-Discord-Integration]/Discord-VehRestriction/config.lua
+++ b/resources/[EGRP-Discord-Integration]/Discord-VehRestriction/config.lua
@@ -308,7 +308,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -646,7 +645,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -690,7 +688,6 @@ Config.VehicleRestrictions = {
"23ssjam",
"23tahoeppvrb",
"23tahoessvrb",
- "24sodur_ht",
"24tactrd",
"903um",
"2019GMCSlick",
@@ -863,7 +860,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -988,7 +984,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -1030,7 +1025,6 @@ Config.VehicleRestrictions = {
"23ssjam",
"23tahoeppvrb",
"23tahoessvrb",
- "24sodur_ht",
"24tactrd",
"903um",
"2019GMCSlick",
@@ -1211,7 +1205,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -1336,7 +1329,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -1542,7 +1534,6 @@ Config.VehicleRestrictions = {
"19mustgtrw",
"23camsSs",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"233ppv",
"BCSO21ppv",
@@ -1912,9 +1903,6 @@ Config.VehicleRestrictions = {
"nxsmods21yukon",
"nxsmods21tah",
"nxsmods21sierra",
-
- "24sodur_ht",
-
"18chargerb",
"20Maxfdramsquad",
"21f150",
@@ -2051,7 +2039,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -2094,7 +2081,6 @@ Config.VehicleRestrictions = {
"23ssjam",
"23tahoeppvrb",
"23tahoessvrb",
- "24sodur_ht",
"24tactrd",
"903um",
"2019GMCSlick",
@@ -2276,7 +2262,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -2383,7 +2368,6 @@ Config.VehicleRestrictions = {
"19mustgtrw",
"23camsSs",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"233ppv",
"BCSO21ppv",
@@ -2775,7 +2759,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -2818,7 +2801,6 @@ Config.VehicleRestrictions = {
"23ssjam",
"23tahoeppvrb",
"23tahoessvrb",
- "24sodur_ht",
"24tactrd",
"903um",
"2019GMCSlick",
@@ -3000,7 +2982,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -3023,7 +3004,6 @@ Config.VehicleRestrictions = {
"19mustgtrw",
"23camsSs",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"233ppv",
"BCSO21ppv",
@@ -3499,7 +3479,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -3757,7 +3736,6 @@ Config.VehicleRestrictions = {
"23ssjam",
"23tahoeppvrb",
"23tahoessvrb",
- "24sodur_ht",
"24tactrd",
"903um",
"2019GMCSlick",
@@ -3924,7 +3902,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -4049,7 +4026,9 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
+
"montereyparkpd25dura_180",
+
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -4100,8 +4079,7 @@ Config.VehicleRestrictions = {
"19mustgtrw",
"23camsSs",
"24mustgt",
- "24sodur_ht",
- "25darkhorse",
+ "25darkhorse",
"233ppv",
"BCSO21ppv",
"IDHP_25stang",
@@ -4651,7 +4629,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -4778,7 +4755,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
@@ -5042,7 +5018,6 @@ Config.VehicleRestrictions = {
"23ssjam",
"23tahoeppvrb",
"23tahoessvrb",
- "24sodur_ht",
"24tactrd",
"903um",
"2019GMCSlick",
@@ -5217,7 +5192,6 @@ Config.VehicleRestrictions = {
"19mustgtrb",
"19mustgtrw",
"24mustgt",
- "24sodur_ht",
"25darkhorse",
"615",
"704durango",
@@ -5717,7 +5691,6 @@ Config.VehicleRestrictions = {
"fs23hoe",
"IDHP_25stang",
"mbankssRTCharger",
- "montereyparkpd25dura_180",
"reevesaceunit",
"spec20legcyfpiu",
"firehawk",
diff --git a/resources/vMenu/config/addons.json b/resources/vMenu/config/addons.json
index b6ab4ed4c..0afe4f027 100644
--- a/resources/vMenu/config/addons.json
+++ b/resources/vMenu/config/addons.json
@@ -2,6 +2,7 @@
"vehicles": [
"rrst",
"24sodur_ht",
+ "montereyparkpd25dura_180",
"sbcsd21durango",
"LAPD",
"lapd09charger1",