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 @@ +[![Discord](https://semdevelopment.com/img/discord.png)](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",