From 6637a9b7fc9e558cc778080f0d1cf2d8a15deeb3 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Fri, 8 Sep 2023 21:20:10 +0200 Subject: [PATCH] delete: remove EasyAdmin - Removing EasyAdmin will be replaced with a new custom admin menu --- .../resources/[esx]/EasyAdmin/.eslintrc.json | 47 - .../resources/[esx]/EasyAdmin/LICENSE.md | 651 --- .../resources/[esx]/EasyAdmin/README.md | 48 - .../[esx]/EasyAdmin/backups/.gitkeep | 0 .../[esx]/EasyAdmin/client/admin_client.lua | 420 -- .../[esx]/EasyAdmin/client/gui_c.lua | 1932 -------- .../[esx]/EasyAdmin/client/plugins.lua | 10 - .../[esx]/EasyAdmin/dependencies/Controls.lua | 776 ---- .../[esx]/EasyAdmin/dependencies/NativeUI.lua | 3938 ----------------- .../dependencies/images/banner-eoa.png | Bin 67202 -> 0 bytes .../dependencies/images/banner-gradient.png | Bin 8920 -> 0 bytes .../dependencies/images/banner-hardadmin.png | Bin 15446 -> 0 bytes .../dependencies/images/banner-logo.png | Bin 8269 -> 0 bytes .../dependencies/images/logo-eoa.png | Bin 6837 -> 0 bytes .../dependencies/images/logo-hardadmin.png | Bin 10967 -> 0 bytes .../dependencies/images/pl_badge_contr.png | Bin 1942 -> 0 bytes .../dependencies/images/pl_badge_dev.png | Bin 1914 -> 0 bytes .../EasyAdmin/dependencies/images/pride.png | Bin 16476 -> 0 bytes .../dependencies/images/small-logo-bg.png | Bin 23213 -> 0 bytes .../dependencies/images/small-logo.png | Bin 19137 -> 0 bytes .../EasyAdmin/dependencies/images/ukraine.png | Bin 18389 -> 0 bytes .../EasyAdmin/dependencies/nui/index.html | 123 - .../dependencies/nui/input/shadow.js | 74 - .../dependencies/nui/input/style.css | 129 - .../resources/[esx]/EasyAdmin/fxmanifest.lua | 86 - .../[esx]/EasyAdmin/language/de.json | 239 - .../[esx]/EasyAdmin/language/en.json | 240 - .../[esx]/EasyAdmin/language/es.json | 240 - .../[esx]/EasyAdmin/language/fr.json | 243 - .../[esx]/EasyAdmin/language/it.json | 242 - .../[esx]/EasyAdmin/language/nl.json | 242 - .../[esx]/EasyAdmin/language/pl.json | 241 - .../resources/[esx]/EasyAdmin/package.json | 31 - .../[esx]/EasyAdmin/plugins/.gitkeep | 0 .../plugins/eadiag/eadiag_server.lua | 155 - .../EasyAdmin/plugins/example_client.lua | 58 - .../EasyAdmin/plugins/example_server.lua | 8 - .../EasyAdmin/plugins/example_shared.lua | 8 - .../notifications/mythic-notify_client.lua | 20 - .../plugins/notifications/pNotify_client.lua | 20 - .../plugins/notifications/t-notify_client.lua | 20 - .../[esx]/EasyAdmin/server/admin_server.lua | 976 ---- .../[esx]/EasyAdmin/server/backups.lua | 134 - .../[esx]/EasyAdmin/server/banlist.lua | 500 --- .../[esx]/EasyAdmin/server/bot/bot.js | 116 - .../[esx]/EasyAdmin/server/bot/chat_bridge.js | 84 - .../EasyAdmin/server/bot/commands/add_ace.js | 53 - .../server/bot/commands/add_group.js | 27 - .../EasyAdmin/server/bot/commands/announce.js | 23 - .../EasyAdmin/server/bot/commands/ban.js | 66 - .../EasyAdmin/server/bot/commands/baninfo.js | 49 - .../EasyAdmin/server/bot/commands/cleanup.js | 31 - .../server/bot/commands/configure.js | 122 - .../EasyAdmin/server/bot/commands/freeze.js | 33 - .../EasyAdmin/server/bot/commands/kick.js | 34 - .../EasyAdmin/server/bot/commands/mute.js | 33 - .../server/bot/commands/playerinfo.js | 79 - .../server/bot/commands/playerlist.js | 143 - .../server/bot/commands/refreshperms.js | 30 - .../server/bot/commands/remove_ace.js | 52 - .../server/bot/commands/remove_group.js | 27 - .../server/bot/commands/screenshot.js | 52 - .../EasyAdmin/server/bot/commands/slap.js | 38 - .../EasyAdmin/server/bot/commands/unban.js | 27 - .../EasyAdmin/server/bot/commands/unfreeze.js | 33 - .../EasyAdmin/server/bot/commands/unmute.js | 33 - .../EasyAdmin/server/bot/commands/warn.js | 37 - .../[esx]/EasyAdmin/server/bot/functions.js | 131 - .../[esx]/EasyAdmin/server/bot/logging.js | 48 - .../EasyAdmin/server/bot/player_events.js | 35 - .../[esx]/EasyAdmin/server/bot/reports.js | 69 - .../[esx]/EasyAdmin/server/bot/roles.js | 48 - .../EasyAdmin/server/bot/server_status.js | 142 - .../[esx]/EasyAdmin/server/commands.lua | 91 - .../EasyAdmin/server/permission_editor.lua | 342 -- .../[esx]/EasyAdmin/server/playercache.lua | 67 - .../[esx]/EasyAdmin/server/reports.lua | 248 -- .../[esx]/EasyAdmin/server/webhook.lua | 63 - .../[esx]/EasyAdmin/shared/util_shared.lua | 352 -- 79 files changed, 14709 deletions(-) delete mode 100644 server-data/resources/[esx]/EasyAdmin/.eslintrc.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/LICENSE.md delete mode 100644 server-data/resources/[esx]/EasyAdmin/README.md delete mode 100644 server-data/resources/[esx]/EasyAdmin/backups/.gitkeep delete mode 100644 server-data/resources/[esx]/EasyAdmin/client/admin_client.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/client/gui_c.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/client/plugins.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/Controls.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/NativeUI.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-eoa.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-gradient.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-hardadmin.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-logo.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/logo-eoa.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/logo-hardadmin.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/pl_badge_contr.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/pl_badge_dev.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/pride.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/small-logo-bg.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/small-logo.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/images/ukraine.png delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/nui/index.html delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/shadow.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/style.css delete mode 100644 server-data/resources/[esx]/EasyAdmin/fxmanifest.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/de.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/en.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/es.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/fr.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/it.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/nl.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/language/pl.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/package.json delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/.gitkeep delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/eadiag/eadiag_server.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/example_client.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/example_server.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/example_shared.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/notifications/mythic-notify_client.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/notifications/pNotify_client.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/plugins/notifications/t-notify_client.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/admin_server.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/backups.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/banlist.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/bot.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/chat_bridge.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_ace.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_group.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/announce.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/ban.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/baninfo.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/cleanup.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/configure.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/freeze.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/kick.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/mute.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerinfo.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerlist.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/refreshperms.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_ace.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_group.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/screenshot.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/slap.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/unban.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/unfreeze.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/unmute.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/commands/warn.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/functions.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/logging.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/player_events.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/reports.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/roles.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/bot/server_status.js delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/commands.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/permission_editor.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/playercache.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/reports.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/server/webhook.lua delete mode 100644 server-data/resources/[esx]/EasyAdmin/shared/util_shared.lua diff --git a/server-data/resources/[esx]/EasyAdmin/.eslintrc.json b/server-data/resources/[esx]/EasyAdmin/.eslintrc.json deleted file mode 100644 index 2eae331a3..000000000 --- a/server-data/resources/[esx]/EasyAdmin/.eslintrc.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "env": { - "browser": true, - "commonjs": true, - "es2021": true - }, - "extends": "eslint:recommended", - "overrides": [], - "parserOptions": { - "ecmaVersion": "latest" - }, - "rules": { - "indent": [ - "error", - "tab" - ], - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "never" - ], - "no-undef": "off", - "no-else-return": [ - "warn" - ], - "no-empty-function": [ - "error", - {"allow": ["arrowFunctions"]} - ], - "no-implicit-coercion": [ - "warn" - ], - "no-useless-return": [ - "warn" - ], - "no-undef-init": [ - "warn" - ] - } -} \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/LICENSE.md b/server-data/resources/[esx]/EasyAdmin/LICENSE.md deleted file mode 100644 index 4ef32f083..000000000 --- a/server-data/resources/[esx]/EasyAdmin/LICENSE.md +++ /dev/null @@ -1,651 +0,0 @@ -GNU Affero General Public License -================================= - -_Version 3, 19 November 2007_ -_Copyright © 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 Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are 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. - -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. - -Developers that use our General Public Licenses protect your rights -with two steps: **(1)** assert copyright on the software, and **(2)** offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - -A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - -The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - -An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - -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 Affero 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. Remote Network Interaction; Use with the GNU General Public License - -Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - -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 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 work with which it is combined will remain governed by version -3 of the GNU General Public License. - -### 14. Revised Versions of this License - -The Free Software Foundation may publish revised and/or new versions of -the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a “Source” link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - -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 AGPL, see -<>. diff --git a/server-data/resources/[esx]/EasyAdmin/README.md b/server-data/resources/[esx]/EasyAdmin/README.md deleted file mode 100644 index 736afe8f1..000000000 --- a/server-data/resources/[esx]/EasyAdmin/README.md +++ /dev/null @@ -1,48 +0,0 @@ -![image](https://user-images.githubusercontent.com/13604413/129143671-3ab6a643-faf0-479d-8b97-af71ffb5193c.png) - - -EasyAdmin is an Administration Suite for FiveM Servers, EasyAdmin is feature-rich and highly customisable, it includes features such as - -- Basic Administration (Kicking,Temp/PermaBanning,Mute,Teleport To/From,Slapping,Freeze Player,Warnings) -- Ability to Screenshot other Players' Game (requires screenshot-basic) -- Extensive Banlist System which is highly resilient to ban evasion -- Basic Ability to modify Server Settings from a GUI. -- Report / Admin Call System with GUI to view and handle them -- Extensive Permission system utilising FiveM's inbuilt ACE System. -- A Permission Editor allowing for realtime modification of Server Permissions, saves to a config! -- Various Server Admin Tools such as Cleaning up spawned Cars/Peds/Props -- API to allow Developers to Communicate with EasyAdmin -- Translation in 7 Languages (Community Driven) -- Actively Supported & Updated since 2017 -- Plugin Support -- Fully integrated Discord Bot, including Discord ACE Permissions, Chat Bridge, Commands and Logs - -![image](https://user-images.githubusercontent.com/13604413/126916981-1680e5ac-e024-467b-aad3-a5a9658449e0.png) - -![image](https://user-images.githubusercontent.com/13604413/126916983-0e62e13f-aa66-49ea-b7ef-4f8449601c53.png) - -![image](https://user-images.githubusercontent.com/13604413/126916995-213fca15-d356-47b6-8b80-8745b4a37eb9.png) - -![image](https://user-images.githubusercontent.com/13604413/126916989-f78d7b16-d20a-49ba-a559-6c3b56e98de5.png) - -### Dependencies - -EasyAdmin requires FiveM's default `yarn` resource, this is included in [cfx-server-data](https://github.com/citizenfx/cfx-server-data) when installing the server. - - -### Installation - -Take a look at our Documentation [here](https://easyadmin.readthedocs.io/), we Explain how to Install and Configure EasyAdmin on the Wiki. - -## [Support Discord](https://discord.gg/GugyRU8) - - - -Graphic Design by [Team Snaily](https://teamsnaily.com) - - - -Supported by: - -ZAP-Hosting Gameserver and Webhosting -10% Discount Code: blumlaut-a-1757 diff --git a/server-data/resources/[esx]/EasyAdmin/backups/.gitkeep b/server-data/resources/[esx]/EasyAdmin/backups/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/server-data/resources/[esx]/EasyAdmin/client/admin_client.lua b/server-data/resources/[esx]/EasyAdmin/client/admin_client.lua deleted file mode 100644 index d6dc827ba..000000000 --- a/server-data/resources/[esx]/EasyAdmin/client/admin_client.lua +++ /dev/null @@ -1,420 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -players = {} -banlist = {} -cachedplayers = {} -reports = {} -add_aces, add_principals = {}, {} -MessageShortcuts = {} -FrozenPlayers = {} -MutedPlayers = {} - -local vehicleInfo = { - netId = nil, - seat = nil, -} - -RegisterNetEvent("EasyAdmin:adminresponse", function(perms) - permissions = perms - - for _, val in pairs(perms) do - if val == true then - isAdmin = true - end - end -end) - -RegisterNetEvent("EasyAdmin:SetSetting", function(setting,state) - settings[setting] = state -end) - -AddEventHandler('EasyAdmin:SetLanguage', function(newstrings) - strings = newstrings -end) - -RegisterNetEvent("EasyAdmin:fillBanlist", function(thebanlist) - banlist = thebanlist -end) - -RegisterNetEvent("EasyAdmin:fillCachedPlayers", function(thecached) - if permissions["player.ban.temporary"] or permissions["player.ban.permanent"] then - cachedplayers = thecached - end -end) - - -RegisterNetEvent("EasyAdmin:GetInfinityPlayerList", function(players) - playerlist = players -end) - -RegisterNetEvent("EasyAdmin:getServerAces", function(aces,principals) - add_aces = aces - add_principals = principals - PrintDebugMessage("Recieved ACE Permissions list", 4) -end) - -RegisterNetEvent("EasyAdmin:SetLanguage", function() - if permissions["server.permissions.read"] then - TriggerServerEvent("EasyAdmin:getServerAces") - end -end) - -RegisterNetEvent("EasyAdmin:NewReport", function(reportData) - reports[reportData.id] = reportData -end) - -RegisterNetEvent("EasyAdmin:ClaimedReport", function(reportData) - reports[reportData.id] = reportData - if _menuPool and _menuPool:IsAnyMenuOpen() then - for _, menu in pairs(reportMenus) do - for _,item in pairs(menu.Items) do - if getMenuItemTitle(item) == GetLocalisedText("claimreport") then - setMenuItemTitle(item, GetLocalisedText("claimedby")) - item:RightLabel(reportData.claimedName) - end - end - end - end -end) - -RegisterNetEvent("EasyAdmin:RemoveReport", function(reportData) - reports[reportData.id] = nil -end) - -RegisterNetEvent("EasyAdmin:fillShortcuts", function (shortcuts) - MessageShortcuts = shortcuts -end) - -RegisterNetEvent('EasyAdmin:SetPlayerFrozen', function(player,state) - FrozenPlayers[player] = state - if _menuPool and _menuPool:IsAnyMenuOpen() then - if playerMenus[tostring(player)].menu then - for _,item in pairs(playerMenus[tostring(player)].menu.Items) do - if getMenuItemTitle(item) == GetLocalisedText("setplayerfrozen") then - item.Checked = state - end - end - end - end -end) - -RegisterNetEvent('EasyAdmin:SetPlayerMuted', function(player,state) - MutedPlayers[player] = state - if _menuPool and _menuPool:IsAnyMenuOpen() then - if playerMenus[tostring(player)].menu then - for _,item in pairs(playerMenus[tostring(player)].menu.Items) do - if getMenuItemTitle(item) == GetLocalisedText("mute") then - item.Checked = state - end - end - end - end -end) - -Citizen.CreateThread( function() - while true do - Citizen.Wait(0) - if frozen then - local localPlayerPedId = PlayerPedId() - FreezeEntityPosition(localPlayerPedId, frozen) - if IsPedInAnyVehicle(localPlayerPedId, true) then - FreezeEntityPosition(GetVehiclePedIsIn(localPlayerPedId, false), frozen) - end - else - Citizen.Wait(200) - end - end -end) - -RegisterNetEvent("EasyAdmin:requestSpectate", function(playerServerId, tgtCoords) - local localPlayerPed = PlayerPedId() - - if IsPedInAnyVehicle(localPlayerPed) then - local vehicle = GetVehiclePedIsIn(localPlayerPed, false) - local numVehSeats = GetVehicleModelNumberOfSeats(GetEntityModel(vehicle)) - vehicleInfo.netId = VehToNet(vehicle) - for i = -1, numVehSeats do - if GetPedInVehicleSeat(vehicle, i) == localPlayerPed then - vehicleInfo.seat = i - break - end - end - end - - if ((not tgtCoords) or (tgtCoords.z == 0.0)) then tgtCoords = GetEntityCoords(GetPlayerPed(GetPlayerFromServerId(playerServerId))) end - if playerServerId == GetPlayerServerId(PlayerId()) then - local oldCoords - if oldCoords then - RequestCollisionAtCoord(oldCoords.x, oldCoords.y, oldCoords.z) - Wait(500) - SetEntityCoords(playerPed, oldCoords.x, oldCoords.y, oldCoords.z, 0, 0, 0, false) - oldCoords=nil - end - spectatePlayer(localPlayerPed,GetPlayerFromServerId(PlayerId()),GetPlayerName(PlayerId())) - frozen = false - return - else - if not oldCoords then - oldCoords = GetEntityCoords(localPlayerPed) - end - end - SetEntityCoords(localPlayerPed, tgtCoords.x, tgtCoords.y, tgtCoords.z - 10.0, 0, 0, 0, false) - frozen = true - stopSpectateUpdate = true - - local playerId = GetPlayerFromServerId(playerServerId) - repeat - Wait(200) - playerId = GetPlayerFromServerId(playerServerId) - until ((GetPlayerPed(playerId) > 0) and (playerId ~= -1)) - spectatePlayer(GetPlayerPed(playerId),playerId,GetPlayerName(playerId)) - stopSpectateUpdate = false -end) - -Citizen.CreateThread(function() - RegisterNetEvent("EasyAdmin:requestCleanup", function(type, radius) - - local toDelete = {} - local deletionText = "" - if type == "cars" then - toDelete = GetGamePool("CVehicle") - deletionText = GetLocalisedText("cleaningcar") - elseif type == "peds" then - toDelete = GetGamePool("CPed") - deletionText = GetLocalisedText("cleaningped") - elseif type == "props" then - toDelete = mergeTables(GetGamePool("CObject"), GetGamePool("CPickup")) - deletionText = GetLocalisedText("cleaningprop") - end - - for _,entity in pairs(toDelete) do - PrintDebugMessage("starting deletion for entity "..entity, 4) - if DoesEntityExist(entity) then - if (type == "cars" and not IsPedAPlayer(GetPedInVehicleSeat(entity, -1))) then - if not NetworkHasControlOfEntity(entity) then - local i=0 - repeat - NetworkRequestControlOfEntity(entity) - i=i+1 - Wait(150) - until (NetworkHasControlOfEntity(entity) or i==500) - end - - -- draw text - SetTextFont(2) - SetTextColour(255, 255, 255, 200) - SetTextProportional(1) - SetTextScale(0.0, 0.6) - SetTextDropshadow(0, 0, 0, 0, 255) - SetTextEdge(1, 0, 0, 0, 255) - SetTextDropShadow() - SetTextOutline() - SetTextEntry("STRING") - AddTextComponentString(string.format(deletionText, entity)) - EndTextCommandDisplayText(0.45, 0.95) - - -- delete entity - if radius == "global" then - PrintDebugMessage("deleting entity "..entity, 3) - SetEntityAsNoLongerNeeded(entity) - DeleteEntity(entity) - else - local entityCoords = GetEntityCoords(entity) - local playerCoords = GetEntityCoords(PlayerPedId()) - if #(playerCoords - entityCoords) < radius then - PrintDebugMessage("deleting entity "..entity, 3) - SetEntityAsNoLongerNeeded(entity) - DeleteEntity(entity) - end - end - Wait(1) - end - toDelete[i] = nil - end - end - end) -end) - -Citizen.CreateThread( function() - while true do - Citizen.Wait(500) - if drawInfo and not stopSpectateUpdate then - local localPlayerPed = PlayerPedId() - local targetPed = GetPlayerPed(drawTarget) - - local tgtCoords = GetEntityCoords(targetPed) - if tgtCoords and tgtCoords.x ~= 0 then - SetEntityCoords(localPlayerPed, tgtCoords.x, tgtCoords.y, tgtCoords.z - 10.0, 0, 0, 0, false) - end - else - Citizen.Wait(1000) - end - end -end) - - -RegisterNetEvent("EasyAdmin:TeleportPlayerBack", function() - if lastLocation then - SetEntityCoords(PlayerPedId(), lastLocation,0,0,0, false) - lastLocation=nil - end -end) - -RegisterNetEvent("EasyAdmin:TeleportRequest", function(id, tgtCoords) - if id then - if (tgtCoords.x == 0.0 and tgtCoords.y == 0.0 and tgtCoords.z == 0.0) then - local tgtPed = GetPlayerPed(GetPlayerFromServerId(id)) - tgtCoords = GetEntityCoords(tgtPed) - end - lastLocation = tgtCoords - SetEntityCoords(PlayerPedId(), tgtCoords,0,0,0, false) - else - lastLocation = tgtCoords - SetEntityCoords(PlayerPedId(), tgtCoords,0,0,0, false) - end -end) - -RegisterNetEvent("EasyAdmin:SlapPlayer", function(slapAmount) - local ped = PlayerPedId() - if slapAmount > GetEntityHealth(ped) then - ApplyDamageToPed(ped, 5000, false, true,true) - else - ApplyDamageToPed(ped, slapAmount, false, true,true) - end -end) - - -RegisterCommand("kick", function(_, args) - local reason = "" - for i,theArg in pairs(args) do - if i ~= 1 then -- make sure we are not adding the kicked player as a reason - reason = reason.." "..theArg - end - end - if args[1] and tonumber(args[1]) then - TriggerServerEvent("EasyAdmin:kickPlayer", tonumber(args[1]), reason) - end -end, false) - -RegisterCommand("ban", function(_, args) - if args[1] and tonumber(args[1]) then - local reason = "" - for i,theArg in pairs(args) do - if i ~= 1 then - reason = reason.." "..theArg - end - end - if args[1] and tonumber(args[1]) then - TriggerServerEvent("EasyAdmin:banPlayer", tonumber(args[1]), reason, false, GetPlayerName(args[1])) - end - end -end, false) - -RegisterNetEvent("EasyAdmin:FreezePlayer", function(toggle) - frozen = toggle - local playerPed = PlayerPedId() - FreezeEntityPosition(playerPed, frozen) - if IsPedInAnyVehicle(playerPed, false) then - FreezeEntityPosition(GetVehiclePedIsIn(playerPed, false), frozen) - end -end) - - -RegisterNetEvent("EasyAdmin:CaptureScreenshot", function() - exports['screenshot-basic']:requestScreenshotUpload(GetConvar("ea_screenshoturl", 'https://wew.wtf/upload.php'), GetConvar("ea_screenshotfield", 'files[]'), function(data) - TriggerLatentServerEvent("EasyAdmin:TookScreenshot", 100000, data) - end) -end) - -function spectatePlayer(targetPed, target, name) - local playerPed = PlayerPedId() -- yourself - enable = true - if (target == PlayerId() or target == -1) then - enable = false - end - - if(enable)then - SetEntityVisible(playerPed, false, 0) - SetEntityCollision(playerPed, false, false) - SetEntityInvincible(playerPed, true) - NetworkSetEntityInvisibleToNetwork(playerPed, true) - Citizen.Wait(200) -- to prevent target player seeing you - if targetPed == playerPed then - Wait(500) - targetPed = GetPlayerPed(target) - end - local targetx, targety, targetz = table.unpack(GetEntityCoords(targetPed, false)) - RequestCollisionAtCoord(targetx,targety,targetz) - NetworkSetInSpectatorMode(true, targetPed) - - DrawPlayerInfo(target) - TriggerEvent("EasyAdmin:showNotification", string.format(GetLocalisedText("spectatingUser"), name)) - else - local oldCoords - if oldCoords then - RequestCollisionAtCoord(oldCoords.x, oldCoords.y, oldCoords.z) - Wait(500) - SetEntityCoords(playerPed, oldCoords.x, oldCoords.y, oldCoords.z, 0, 0, 0, false) - end - NetworkSetInSpectatorMode(false, targetPed) - StopDrawPlayerInfo() - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("stoppedSpectating")) - frozen = false - Citizen.Wait(200) -- to prevent staying invisible - SetEntityVisible(playerPed, true, 0) - SetEntityCollision(playerPed, true, true) - SetEntityInvincible(playerPed, false) - NetworkSetEntityInvisibleToNetwork(playerPed, false) - if vehicleInfo.netId and vehicleInfo.seat then - local vehicle = NetToVeh(vehicleInfo.netId) - if DoesEntityExist(vehicle) then - if IsVehicleSeatFree(vehicle, vehicleInfo.seat) then - SetPedIntoVehicle(playerPed, vehicle, vehicleInfo.seat) - else - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("spectatevehicleseatoccupied")) - end - else - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("spectatenovehiclefound")) - end - - vehicleInfo.netId = nil - vehicleInfo.seat = nil - end - end -end - -function ShowNotification(text) - if not RedM then - local notificationTxd = CreateRuntimeTxd("easyadmin_notification") - CreateRuntimeTextureFromImage(notificationTxd, 'small_logo', 'dependencies/images/small-logo-bg.png') - BeginTextCommandThefeedPost("STRING") - AddTextComponentSubstringPlayerName(text) - - local title = "~bold~EasyAdmin" - local subtitle = GetLocalisedText("notification") - local iconType = 0 - local flash = false - - EndTextCommandThefeedPostMessagetext("easyadmin_notification", "small_logo", flash, iconType, title, subtitle) - local showInBrief = false - local blink = false - EndTextCommandThefeedPostTicker(blink, showInBrief) - end -end - -RegisterNetEvent("EasyAdmin:showNotification", function(text) - TriggerEvent("EasyAdmin:receivedNotification") - if not WasEventCanceled() then - ShowNotification(text) - end -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/client/gui_c.lua b/server-data/resources/[esx]/EasyAdmin/client/gui_c.lua deleted file mode 100644 index b057a0e73..000000000 --- a/server-data/resources/[esx]/EasyAdmin/client/gui_c.lua +++ /dev/null @@ -1,1932 +0,0 @@ ------------------------------------- ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -isAdmin = false -showLicenses = false -RedM = false - -settings = { - button = "none", - forceShowGUIButtons = false, -} - - --- generate "slap" table once -local SlapAmount = {} -for i=1,20 do - table.insert(SlapAmount,i) -end - -function handleOrientation(orientation) - if orientation == "right" then - return 1300-menuWidth - elseif orientation == "middle" then - return 730 - elseif orientation == "left" then - return 0 - end -end - -playlist = nil - - -RegisterCommand('easyadmin', function(source, args) - CreateThread(function() - if not isAdmin == true then - TriggerServerEvent("EasyAdmin:amiadmin") - local waitTime = 0 - - repeat - Wait(10) - waitTime=waitTime+1 - until (isAdmin or waitTime==1000) - if not isAdmin then - return - end - end - - if not mainMenu or not mainMenu:Visible() then - if ((RedM and settings.infinity) or not RedM) and isAdmin then - playerlist = nil - if DoesPlayerHavePermissionForCategory(-1, "player") then - TriggerServerEvent("EasyAdmin:GetInfinityPlayerList") - repeat - Wait(10) - until playerlist - else - playerlist = {} - end - end - - if strings and isAdmin then - banLength = {} - - if permissions["player.ban.permanent"] then - table.insert(banLength, {label = GetLocalisedText("permanent"), time = 10444633200}) - end - - if permissions["player.ban.temporary"] then - table.insert(banLength, {label = "6 "..GetLocalisedText("hours"), time = 21600}) - table.insert(banLength, {label = "12 "..GetLocalisedText("hours"), time = 43200}) - table.insert(banLength, {label = "1 "..GetLocalisedText("day"), time = 86400}) - table.insert(banLength, {label = "3 "..GetLocalisedText("days"), time = 259200}) - table.insert(banLength, {label = "1 "..GetLocalisedText("week"), time = 518400}) - table.insert(banLength, {label = "2 "..GetLocalisedText("weeks"), time = 1123200}) - table.insert(banLength, {label = "1 "..GetLocalisedText("month"), time = 2678400}) - table.insert(banLength, {label = "1 "..GetLocalisedText("year"), time = 31536000}) - table.insert(banLength, {label = GetLocalisedText("customtime"), time = -1}) - end - GenerateMenu() - - if (args[1]) then - local id = tonumber(args[1]) - if (reportMenus[id]) then - reportMenus[id]:Visible(true) - return - elseif playerMenus[args[1]] then - local menu = playerMenus[args[1]] - menu.generate(menu.menu) - menu.menu:Visible(true) - return - end - end - SendNUIMessage({action= "speak", text="EasyAdmin"}) - mainMenu:Visible(true) - else - TriggerServerEvent("EasyAdmin:amiadmin") - end - else - mainMenu:Visible(false) - _menuPool:Remove() - TriggerEvent("EasyAdmin:MenuRemoved") - collectgarbage() - end - end) -end) - - -RegisterCommand('ea', function(source,args) - ExecuteCommand('easyadmin '..table.concat(args, " ")) -end) - -Citizen.CreateThread(function() - if CompendiumHorseObserved then -- https://www.youtube.com/watch?v=r7qovpFAGrQ - RedM = true - settings.button = "PhotoModePc" - end - repeat - Wait(100) - until NativeUI - - TriggerServerEvent("EasyAdmin:amiadmin") - TriggerServerEvent("EasyAdmin:requestBanlist") - TriggerServerEvent("EasyAdmin:requestCachedPlayers") - - if not GetResourceKvpString("ea_menuorientation") then - SetResourceKvp("ea_menuorientation", "middle") - SetResourceKvpInt("ea_menuwidth", 0) - menuWidth = 0 - menuOrientation = handleOrientation("middle") - else - menuWidth = GetResourceKvpInt("ea_menuwidth") - menuOrientation = handleOrientation(GetResourceKvpString("ea_menuorientation")) - end - if not GetResourceKvpInt("ea_tts") then - SetResourceKvpInt("ea_tts", 0) - else - if GetResourceKvpInt("ea_tts") == 1 then - SendNUIMessage({ - action = "toggle_speak", - enabled = true - }) - end - end - - local subtitle = "~b~Admin Menu" - if settings.updateAvailable then - subtitle = "~g~UPDATE "..settings.updateAvailable.." AVAILABLE!" - elseif settings.alternativeTitle then - -- if you remove this code then you're a killjoy, can't we have nice things? just once? it's not like this changes the whole admin menu or how it behaves, its a single subtitle. - subtitle = settings.alternativeTitle - end - - while true do - if _menuPool then - if not _menuPool:IsAnyMenuOpen() then - _menuPool:Remove() - TriggerEvent("EasyAdmin:MenuRemoved") - _menuPool = nil - collectgarbage() - elseif _menuPool:IsAnyMenuOpen() then - _menuPool:ProcessMenus() - end - end - - if RedM then -- since RedM doesn't have the new key bindings yet, watch for button press actively. - if (RedM and IsControlJustReleased(0, Controls[settings.button]) ) then - ExecuteCommand("easyadmin") - end - end - - Citizen.Wait(1) - end -end) - -function DrawPlayerInfo(target) - drawTarget = target - drawInfo = true -end - -function StopDrawPlayerInfo() - drawInfo = false - drawTarget = 0 -end - -local banlistPage = 1 -playerMenus = {} -cachedMenus = {} -reportMenus = {} -local easterChance = math.random(0,101) -local overrideEgg, currentEgg - - - --- note: we dont support dui banner and dui logo at the same time yet. -local eastereggs = { - pipes = { - duibanner = "http://legacy.furfag.de/eggs/pipes", - banner = false, - logo = "dependencies/images/banner-logo.png", - }, - nom = { - duilogo = "http://legacy.furfag.de/eggs/nom", - banner = "dependencies/images/banner-gradient.png", - logo = false, - }, - pride = { - banner = "dependencies/images/banner-gradient.png", - logo = "dependencies/images/pride.png", - }, - ukraine = { - banner = "dependencies/images/banner-gradient.png", - logo = "dependencies/images/ukraine.png" - }, - EOA = { - banner = "dependencies/images/banner-eoa.png", - logo = "dependencies/images/logo-eoa.png" - }, - HardAdmin = { - banner = "dependencies/images/banner-hardadmin.png", - logo = "dependencies/images/logo-hardadmin.png" - } -} - -function generateTextures() - if not RedM and not txd or (overrideEgg ~= currentEgg) then - if dui then - DestroyDui(dui) - dui = nil - end - txd = CreateRuntimeTxd("easyadmin") - CreateRuntimeTextureFromImage(txd, 'badge_dev', 'dependencies/images/pl_badge_dev.png') - CreateRuntimeTextureFromImage(txd, 'badge_contrib', 'dependencies/images/pl_badge_contr.png') - - if ((overrideEgg == nil) and easterChance == 100) or (overrideEgg or overrideEgg == false) then - local chance = overrideEgg - if ((overrideEgg == nil) and easterChance == 100) then - -- dirty function to select random easter egg - local tbl = {} - for k,v in pairs(eastereggs) do - table.insert(tbl, k) - end - - chance = tbl[math.random( #tbl )] - end - - local egg = eastereggs[chance] - if egg then - if egg.duibanner then - dui = CreateDui(egg.duibanner, 512,128) - local duihandle = GetDuiHandle(dui) - CreateRuntimeTextureFromDuiHandle(txd, 'banner-gradient', duihandle) - Wait(800) - elseif egg.duilogo then - dui = CreateDui(egg.duilogo, 512,128) - local duihandle = GetDuiHandle(dui) - CreateRuntimeTextureFromDuiHandle(txd, 'logo', duihandle) - Wait(800) - end - if egg.logo then - CreateRuntimeTextureFromImage(txd, 'logo', egg.logo) - end - if egg.banner then - CreateRuntimeTextureFromImage(txd, 'banner-gradient', egg.banner) - end - currentEgg = chance - else - CreateRuntimeTextureFromImage(txd, 'logo', 'dependencies/images/banner-logo.png') - CreateRuntimeTextureFromImage(txd, 'banner-gradient', 'dependencies/images/banner-gradient.png') - currentEgg = false - end - else - if settings.alternativeLogo then - CreateRuntimeTextureFromImage(txd, 'logo', 'dependencies/images/'..settings.alternativeLogo..'.png') - else - CreateRuntimeTextureFromImage(txd, 'logo', 'dependencies/images/banner-logo.png') - end - if settings.alternativeBanner then - CreateRuntimeTextureFromImage(txd, 'banner-gradient', 'dependencies/images/'..settings.alternativeBanner..'.png') - else - CreateRuntimeTextureFromImage(txd, 'banner-gradient', 'dependencies/images/banner-gradient.png') - end - currentEgg=nil - end - end -end - -function GenerateMenu() -- this is a big ass function - - generateTextures() - TriggerServerEvent("EasyAdmin:requestCachedPlayers") - if _menuPool then - _menuPool:Remove() - TriggerEvent("EasyAdmin:MenuRemoved") - collectgarbage() - end - _menuPool = NativeUI.CreatePool() - collectgarbage() - if not GetResourceKvpString("ea_menuorientation") then - SetResourceKvp("ea_menuorientation", "middle") - SetResourceKvpInt("ea_menuwidth", 0) - menuWidth = 0 - menuOrientation = handleOrientation("middle") - else - menuWidth = GetResourceKvpInt("ea_menuwidth") - menuOrientation = handleOrientation(GetResourceKvpString("ea_menuorientation")) - end - maxRightTextWidth = math.floor((24+(menuWidth*0.12))) - local subtitle = "Admin Menu" - if settings.updateAvailable then - subtitle = "~g~UPDATE "..settings.updateAvailable.." AVAILABLE!" elseif settings.alternativeTitle then subtitle = settings.alternativeTitle - end - mainMenu = NativeUI.CreateMenu(RedM and "EasyAdmin" or "", subtitle, menuOrientation, 0, "easyadmin", "banner-gradient", "logo") - _menuPool:Add(mainMenu) - - _menuPool:ControlDisablingEnabled(false) - _menuPool:MouseControlsEnabled(false) - - if DoesPlayerHavePermissionForCategory(-1, "player") then - playermanagement = _menuPool:AddSubMenu(mainMenu, GetLocalisedText("playermanagement"),"",true, true) - playermanagement:SetMenuWidthOffset(menuWidth) - end - - if DoesPlayerHavePermissionForCategory(-1, "server") or permissions["player.ban.view"] then - servermanagement = _menuPool:AddSubMenu(mainMenu, GetLocalisedText("servermanagement"),"",true, true) - servermanagement:SetMenuWidthOffset(menuWidth) - end - - settingsMenu = _menuPool:AddSubMenu(mainMenu, GetLocalisedText("settings"),"",true, true) - - mainMenu:SetMenuWidthOffset(menuWidth) - settingsMenu:SetMenuWidthOffset(menuWidth) - - -- util stuff - players = {} - local localplayers = {} - - if (RedM and settings.infinity) or not RedM then - local localplayers = playerlist - local temp = {} - --table.sort(localplayers) - for i,thePlayer in pairs(localplayers) do - table.insert(temp, thePlayer.id) - end - table.sort(temp) - for i, thePlayerId in pairs(temp) do - for _, thePlayer in pairs(localplayers) do - if thePlayerId == thePlayer.id then - players[i] = thePlayer - end - end - end - temp=nil - else - for i = 0, 128 do - if NetworkIsPlayerActive( i ) then - table.insert( localplayers, GetPlayerServerId(i) ) - end - end - table.sort(localplayers) - for i,thePlayer in ipairs(localplayers) do - table.insert(players,GetPlayerFromServerId(thePlayer)) - end - end - - TriggerEvent("EasyAdmin:BuildMainMenuOptions") - for i, plugin in pairs(plugins) do - if plugin.functions.mainMenu then - PrintDebugMessage("Processing Plugin: "..plugin.name, 4) - local ran, errorMsg = pcall(plugin.functions.mainMenu) - if not ran then - PrintDebugMessage("Error in plugin "..plugin.name..": \n"..errorMsg, 1) - end - end - end - - if DoesPlayerHavePermissionForCategory(-1, "player") then - - local userSearch = NativeUI.CreateItem(GetLocalisedText("searchuser"), GetLocalisedText("searchuserguide")) - playermanagement:AddItem(userSearch) - userSearch.Activated = function(ParentMenu, SelectedItem) - - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 60) - - if result and result ~= "" then - local found = false - local foundbyid = playerMenus[result] or false - local temp = {} - if foundbyid then - found = true - table.insert(temp, {id = foundbyid.id, name = foundbyid.name, menu = foundbyid.menu}) - end - for k,v in pairs(playerMenus) do - if string.find(string.lower(v.name), string.lower(result)) then - found = true - table.insert(temp, {id = v.id, name = v.name, menu = v.menu}) - end - end - for k,v in pairs(cachedMenus) do - if string.find(string.lower(v.name), string.lower(result)) then - found = true - table.insert(temp, {id = v.id, name = v.name, menu = v.menu, cached = true}) - end - end - - if found and (#temp > 1) then - local searchsubtitle = "Found "..tostring(#temp).." results!" - ttsSpeechText(searchsubtitle) - local resultMenu = NativeUI.CreateMenu("Search Results", searchsubtitle, menuOrientation, 0, "easyadmin", "banner-gradient", "logo") - _menuPool:Add(resultMenu) - _menuPool:ControlDisablingEnabled(false) - _menuPool:MouseControlsEnabled(false) - - for i,thePlayer in ipairs(temp) do - local title = "["..thePlayer.id.."] "..thePlayer.name, "" - if thePlayer.cached then - title = thePlayer.name - end - local thisItem = NativeUI.CreateItem(title) - resultMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu, SelectedItem) - _menuPool:CloseAllMenus() - Citizen.Wait(300) - local thisMenu = thePlayer.menu - playerMenus[tostring(thePlayer.id)].generate(thisMenu) - thisMenu:Visible(true) - end - end - _menuPool:CloseAllMenus() - Citizen.Wait(300) - resultMenu:Visible(true) - return - end - if found and (#temp == 1) then - local thisMenu = temp[1].menu - _menuPool:CloseAllMenus() - Citizen.Wait(300) - ttsSpeechText("Found User.") - playerMenus[tostring(temp[1].id)].generate(thisMenu) - thisMenu:Visible(true) - return - end - TriggerEvent("EasyAdmin:showNotification", "~r~No results found!") - end - end - - playerMenus = {} - cachedMenus = {} - reportMenus = {} - for i,thePlayer in pairs(players) do - if RedM and not settings.infinity then - thePlayer = { - id = GetPlayerServerId(thePlayer), - name = GetPlayerName(thePlayer) - } - end - local thisPlayerMenu = _menuPool:AddSubMenu(playermanagement,"["..thePlayer.id.."] "..thePlayer.name,"",true) - if not RedM and thePlayer.developer then - thisPlayerMenu.ParentItem:SetRightBadge(23) - elseif not RedM and thePlayer.contributor then - thisPlayerMenu.ParentItem:SetRightBadge(24) - end - playerMenus[tostring(thePlayer.id)] = {menu = thisPlayerMenu, name = thePlayer.name, id = thePlayer.id } - - thisPlayerMenu:SetMenuWidthOffset(menuWidth) - - playerMenus[tostring(thePlayer.id)].generate = function(menu) - thisPlayer = menu - - - if not playerMenus[tostring(thePlayer.id)].generated then - - if permissions["player.kick"] then - local thisKickMenu = _menuPool:AddSubMenu(thisPlayer,GetLocalisedText("kickplayer"),"",true) - thisKickMenu:SetMenuWidthOffset(menuWidth) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reason"),GetLocalisedText("kickreasonguide")) - thisKickMenu:AddItem(thisItem) - KickReason = GetLocalisedText("noreason") - thisItem:RightLabel(KickReason) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 128) - local formattedResult = formatRightString(formatShortcuts(result)) - - if result and result ~= "" then - KickReason = result - thisItem:RightLabel(formattedResult) - else - KickReason = GetLocalisedText("noreason") - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("confirmkick"),GetLocalisedText("confirmkickguide")) - thisKickMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if KickReason == "" then - KickReason = GetLocalisedText("noreason") - end - TriggerServerEvent("EasyAdmin:kickPlayer", thePlayer.id, KickReason) - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - playermanagement:Visible(true) - end - end - - if permissions["player.ban.temporary"] or permissions["player.ban.permanent"] then - local thisBanMenu = _menuPool:AddSubMenu(thisPlayer,GetLocalisedText("banplayer"),"",true) - thisBanMenu:SetMenuWidthOffset(menuWidth) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reason"),GetLocalisedText("banreasonguide")) - thisBanMenu:AddItem(thisItem) - BanReason = GetLocalisedText("noreason") - thisItem:RightLabel(BanReason) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 128) - local formattedResult = formatRightString(formatShortcuts(result)) - - if result and result ~= "" then - BanReason = result - thisItem:RightLabel(formattedResult) - else - BanReason = GetLocalisedText("noreason") - end - end - local bt = {} - for i,a in ipairs(banLength) do - table.insert(bt, a.label) - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("banlength"),bt, 1,GetLocalisedText("banlengthguide") ) - thisBanMenu:AddItem(thisItem) - local BanTime = banLength[1].time - thisItem.OnListChanged = function(sender,item,index) - BanTime = banLength[index].time - end - - thisItem.OnListSelected = function (sender,item,index) - if banLength[index].time == -1 then - - local thisBanTimeMenu = _menuPool:AddSubMenu(thisBanMenu, GetLocalisedText("banlength"), "",true) - thisBanTimeMenu:SetMenuWidthOffset(menuWidth) - - local hours, days, weeks, months = 0, 0, 0, 0 - -- generate our ban lengths - local hourArray = {} - for i=0, 24 do - table.insert(hourArray,i) - end - - local dayArray = {} - for i=0, 31 do - table.insert(dayArray,i) - end - - local weekArray = {} - for i=0, 4 do - table.insert(weekArray,i) - end - - local monthArray = {} - for i=0, 12 do - table.insert(monthArray,i) - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("hours"),hourArray, 1,"") - thisBanTimeMenu:AddItem(thisItem) - thisItem.OnListChanged = function(sender,item,index) - hours = item:IndexToItem(index) - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("days"),dayArray, 1,"") - thisBanTimeMenu:AddItem(thisItem) - thisItem.OnListChanged = function(sender,item,index) - days = item:IndexToItem(index) - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("weeks"),weekArray, 1,"") - thisBanTimeMenu:AddItem(thisItem) - thisItem.OnListChanged = function(sender,item,index) - weeks = item:IndexToItem(index) - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("months"),monthArray, 1,"") - thisBanTimeMenu:AddItem(thisItem) - thisItem.OnListChanged = function(sender,item,index) - months = item:IndexToItem(index) - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("confirm"),"") - thisBanTimeMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - hours=hours*3600 - days=days*86400 - weeks=weeks*518400 - months=months*2678400 - BanTime = hours+days+weeks+months - thisBanMenu:Visible(true) - end - - thisBanTimeMenu:RefreshIndexRecursively() - -- evil NativeUI hack to force it to select and open our submenu - thisBanMenu:CurrentSelection(#thisBanMenu.Items-1) - for i, item in pairs(thisBanMenu.Items) do - item:Selected(false) - end - - thisBanMenu:SelectItem() - -- woosh - thisBanMenu:RemoveItemAt(#thisBanMenu.Items) - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("confirmban"),GetLocalisedText("confirmbanguide")) - thisBanMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if BanTime == -1 then - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("nocustombantime")) - return - end - if BanReason == "" then - BanReason = GetLocalisedText("noreason") - end - TriggerServerEvent("EasyAdmin:banPlayer", thePlayer.id, BanReason, BanTime, thePlayer.name ) - BanTime = 1 - BanReason = "" - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - playermanagement:Visible(true) - end - - end - - if permissions["player.mute"] then - local thisItem = NativeUI.CreateCheckboxItem(GetLocalisedText("mute"), MutedPlayers[thePlayer.id], GetLocalisedText("muteguide")) - thisPlayer:AddItem(thisItem) - thisItem.CheckboxEvent = function(sender, item, checked_) - TriggerServerEvent("EasyAdmin:mutePlayer", thePlayer.id) - end - end - - if permissions["player.spectate"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("spectateplayer"), "") - thisPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:requestSpectate",thePlayer.id) - end - end - - if permissions["player.teleport.single"] then - local sl = {GetLocalisedText("teleporttoplayer"), GetLocalisedText("teleportplayertome"), GetLocalisedText("teleportmeback"), GetLocalisedText("teleportplayerback"), GetLocalisedText("teleportintoclosestvehicle")} - local thisItem = NativeUI.CreateListItem(GetLocalisedText("teleportplayer"), sl, 1, "") - thisPlayer:AddItem(thisItem) - thisItem.OnListSelected = function(sender, item, index) - if item == thisItem then - i = item:IndexToItem(index) - local playerPed = PlayerPedId() - if i == GetLocalisedText("teleporttoplayer") then - if settings.infinity then - TriggerServerEvent('EasyAdmin:TeleportAdminToPlayer', thePlayer.id) - else - lastLocation = GetEntityCoords(playerPed) - local x,y,z = table.unpack(GetEntityCoords(GetPlayerPed(GetPlayerFromServerId(thePlayer.id)),true)) - local heading = GetEntityHeading(GetPlayerPed(player)) - SetEntityCoords(playerPed, x,y,z,0,0,heading, false) - end - elseif i == GetLocalisedText("teleportplayertome") then - local coords = GetEntityCoords(playerPed,true) - TriggerServerEvent("EasyAdmin:TeleportPlayerToCoords", thePlayer.id, coords) - elseif i == GetLocalisedText("teleportmeback") and lastLocation then - local heading = GetEntityHeading(playerPed) - SetEntityCoords(playerPed, lastLocation,0,0,heading, false) - lastLocation = nil - elseif i == GetLocalisedText("teleportplayerback") then - TriggerServerEvent("EasyAdmin:TeleportPlayerBack", thePlayer.id) - elseif i == GetLocalisedText("teleportintoclosestvehicle") then - local coords = GetEntityCoords(playerPed,true) - local vehicles = GetGamePool("CVehicle") - local closestDistance = -1 - local closestVehicle = -1 - for _,vehicle in pairs(vehicles) do - local vehcoords = GetEntityCoords(vehicle,true) - local distance = #(coords - vehcoords) - if closestDistance == -1 or closestDistance > distance then - closestDistance = distance - closestVehicle = vehicle - end - end - if closestVehicle ~= -1 then - for i=-1, GetVehicleMaxNumberOfPassengers(closestVehicle) do - if IsVehicleSeatFree(closestVehicle, i) then - SetPedIntoVehicle(playerPed, closestVehicle, i) - break - end - end - else - TriggerEvent("EasyAdmin:showNotification", "No Vehicles found nearby.") - end - end - end - end - end - - if permissions["player.slap"] then - local thisItem = NativeUI.CreateSliderItem(GetLocalisedText("slapplayer"), SlapAmount, 20, false, false) - thisPlayer:AddItem(thisItem) - thisItem.OnSliderSelected = function(index) - TriggerServerEvent("EasyAdmin:SlapPlayer", thePlayer.id, index*10) - end - end - - if permissions["player.freeze"] and not RedM then - local thisItem = NativeUI.CreateCheckboxItem(GetLocalisedText("setplayerfrozen"), FrozenPlayers[thePlayer.id]) - thisPlayer:AddItem(thisItem) - thisItem.CheckboxEvent = function(sender, item, checked_) - TriggerServerEvent("EasyAdmin:FreezePlayer", thePlayer.id, checked_) - end - end - - if permissions["player.screenshot"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("takescreenshot"),"") - thisPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:TakeScreenshot", thePlayer.id) - end - end - - if permissions["player.warn"] then - local thisWarnMenu = _menuPool:AddSubMenu(thisPlayer,GetLocalisedText("warnplayer"),"",true) - thisWarnMenu:SetMenuWidthOffset(menuWidth) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reason"),GetLocalisedText("warnreasonguide")) - thisWarnMenu:AddItem(thisItem) - WarnReason = GetLocalisedText("noreason") - thisItem:RightLabel(WarnReason) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 128) - local formattedResult = formatRightString(formatShortcuts(result)) - - if result and result ~= "" then - WarnReason = result - thisItem:RightLabel(formattedResult) - else - WarnReason = GetLocalisedText("noreason") - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("confirmwarn"),GetLocalisedText("confirmwarnguide")) - thisWarnMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if WarnReason == "" then - WarnReason = GetLocalisedText("noreason") - end - TriggerServerEvent("EasyAdmin:warnPlayer", thePlayer.id, WarnReason) - BanTime = 1 - BanReason = "" - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - playermanagement:Visible(true) - end - end - - TriggerEvent("EasyAdmin:BuildPlayerOptions", thePlayer.id) - for i, plugin in pairs(plugins) do - if plugin.functions.playerMenu then - PrintDebugMessage("Processing Plugin: "..plugin.name, 4) - local ran, errorMsg = pcall(plugin.functions.playerMenu, thePlayer.id ) - if not ran then - PrintDebugMessage("Error in plugin "..plugin.name..": \n"..errorMsg, 1) - end - end - end - - if GetResourceState("es_extended") == "started" and not ESX then - local thisItem = NativeUI.CreateItem("~y~[ESX]~s~ Options","You can buy the ESX Plugin from https://blumlaut.tebex.io to use this Feature.") - thisPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu, SelectedItem) - copyToClipboard("https://blumlaut.tebex.io/package/4455820") - end - end - if GetResourceState("qb-core") == "started" and not QBCore then - local thisItem = NativeUI.CreateItem("~b~[QBCore]~s~ Options","You can buy the QBCore Plugin from https://blumlaut.tebex.io to use this Feature.") - thisPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu, SelectedItem) - copyToClipboard("https://blumlaut.tebex.io/package/4842353") - end - end - - _menuPool:ControlDisablingEnabled(false) - _menuPool:MouseControlsEnabled(false) - - thisPlayer:RefreshIndexRecursively() - playerMenus[tostring(thePlayer.id)].generated = true - end - end - - thisPlayerMenu.ParentItem.Activated = function(ParentMenu, SelectedItem) - thisPlayer = thisPlayerMenu - playerMenus[tostring(thePlayer.id)].generate(thisPlayer) - - for i, menu in pairs(playerMenus) do - menu.menu.ParentMenu = playermanagement - end - end - - end - - playermanagement.ParentItem.Activated = function(ParentMenu, SelectedItem) - for i, menu in pairs(playerMenus) do - menu.menu.ParentMenu = playermanagement - end - end - - if permissions["player.reports.view"] then - reportViewer = _menuPool:AddSubMenu(playermanagement, GetLocalisedText("reportviewer"),"",true) - local thisMenuWidth = menuWidth - if menuWidth < 150 then - thisMenuWidth = 150 - else - thisMenuWidth = menuWidth - end - reportViewer:SetMenuWidthOffset(thisMenuWidth) - reportViewer.ParentItem:RightLabel(tostring(#reports).." "..GetLocalisedText("open")) - - for i, report in pairs(reports) do - local reportColour = (report.type == 0 and "~y~" or "~r~") - if report.claimed then - reportColour = "~g~" - end - local thisMenu = _menuPool:AddSubMenu(reportViewer, reportColour.. "#"..report.id.." "..string.sub((report.reportedName or report.reporterName), 1, 12).."~w~", "", true) - thisMenu:SetMenuWidthOffset(thisMenuWidth) - thisMenu.ParentItem:RightLabel(formatRightString(report.reason, 32)) - reportMenus[report.id] = thisMenu - - if permissions["player.reports.claim"] then - local claimText = GetLocalisedText("claimreport") - local rightLabel = "" - if report.claimed then - claimText = GetLocalisedText("claimedby") - rightLabel = formatRightString(report.claimedName) - end - - local thisItem = NativeUI.CreateItem(claimText, "") - thisItem:RightLabel(rightLabel) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if not report.claimed then - TriggerServerEvent("EasyAdmin:ClaimReport", i) - else - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("reportalreadyclaimed")) - end - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reporter"), GetLocalisedText("entertoopen")) - thisItem:RightLabel(formatRightString(report.reporterName)) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - _menuPool:CloseAllMenus() - Citizen.Wait(50) - GenerateMenu() - Wait(100) - if not playerMenus[tostring(report.reporter)] then - TriggerEvent("EasyAdmin:showNotification", "~r~Reporting player not found.") - reportViewer:Visible(true) - else - local ourMenu = playerMenus[tostring(report.reporter)].menu - playerMenus[tostring(report.reporter)].generate(ourMenu) - ourMenu.ParentMenu=reportMenus[report.id] - ourMenu:Visible(true) - end - end - - if report.type == 1 then - local thisItem = NativeUI.CreateItem(GetLocalisedText("reported"), GetLocalisedText("entertoopen")) - thisItem:RightLabel(formatRightString(report.reportedName)) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - _menuPool:CloseAllMenus() - Citizen.Wait(50) - GenerateMenu() - Wait(100) - if not playerMenus[tostring(report.reported)] then - TriggerEvent("EasyAdmin:showNotification", "~r~Reported player not found.") - reportViewer:Visible(true) - else - local ourMenu = playerMenus[tostring(report.reported)].menu - playerMenus[tostring(report.reported)].generate(ourMenu) - ourMenu.ParentMenu=reportMenus[report.id] - ourMenu:Visible(true) - end - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reason"), "") - thisItem:RightLabel(formatRightString(report.reason, 48)) - thisMenu:AddItem(thisItem) - - if permissions["player.reports.process"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("closereport"), "") - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:RemoveReport", report) - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - reportViewer:Visible(true) - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("closesimilarreports"), GetLocalisedText("closesimilarreportsguide")) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:RemoveSimilarReports", report) - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - reportViewer:Visible(true) - end - end - end - local thisItem = NativeUI.CreateItem(GetLocalisedText("refreshreports"), GetLocalisedText("refreshreportsguide")) - reportViewer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - _menuPool:CloseAllMenus() - repeat - Wait(100) - until reportViewer - GenerateMenu() - reportViewer:Visible(true) - end - end - - allPlayers = _menuPool:AddSubMenu(playermanagement,GetLocalisedText("allplayers"),"",true) - allPlayers:SetMenuWidthOffset(menuWidth) - if permissions["player.teleport.everyone"] then - -- "all players" function - local thisItem = NativeUI.CreateItem(GetLocalisedText("teleporttome"), GetLocalisedText("teleporttomeguide")) - allPlayers:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - local pCoords = GetEntityCoords(PlayerPedId(),true) - TriggerServerEvent("EasyAdmin:TeleportPlayerToCoords", -1, pCoords) - end - end - - - cachedListGenerated = false - - CachedList = _menuPool:AddSubMenu(playermanagement,GetLocalisedText("cachedplayers"),"",true) - CachedList:SetMenuWidthOffset(menuWidth) - - CachedList.ParentItem.Activated = function(ParentMenu, SelectedItem) - if not cachedListGenerated then - if permissions["player.ban.temporary"] or permissions["player.ban.permanent"] then - for i, cachedplayer in pairs(cachedplayers) do - if cachedplayer.droppedTime and not cachedplayer.immune then - local thisCachedPlayerMenu = _menuPool:AddSubMenu(CachedList,"["..cachedplayer.id.."] "..cachedplayer.name,"",true) - cachedMenus[tostring(cachedplayer.id)] = {menu = thisCachedPlayerMenu, name = cachedplayer.name, id = cachedplayer.id } - thisCachedPlayerMenu:SetMenuWidthOffset(menuWidth) - - thisCachedPlayerMenu.ParentItem.Activated = function(ParentMenu, SelectedItem) - thisPlayer = thisCachedPlayerMenu - if not cachedMenus[tostring(cachedplayer.id)].generated then - local thisBanMenu = _menuPool:AddSubMenu(thisPlayer,GetLocalisedText("banplayer"),"",true) - thisBanMenu:SetMenuWidthOffset(menuWidth) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reason"),GetLocalisedText("banreasonguide")) - thisBanMenu:AddItem(thisItem) - BanReason = GetLocalisedText("noreason") - thisItem:RightLabel(BanReason) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 128) - local formattedResult = formatRightString(formatShortcuts(result)) - - if result and result ~= "" then - BanReason = result - thisItem:RightLabel(formattedResult) - else - BanReason = GetLocalisedText("noreason") - end - end - local bt = {} - for i,a in ipairs(banLength) do - table.insert(bt, a.label) - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("banlength"),bt, 1,GetLocalisedText("banlengthguide") ) - thisBanMenu:AddItem(thisItem) - local BanTime = 1 - thisItem.OnListChanged = function(sender,item,index) - BanTime = index - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("confirmban"),GetLocalisedText("confirmbanguide")) - thisBanMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if BanReason == "" then - BanReason = GetLocalisedText("noreason") - end - TriggerServerEvent("EasyAdmin:offlinebanPlayer", cachedplayer.id, BanReason, banLength[BanTime].time, cachedplayer.name) - BanTime = 1 - BanReason = "" - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - playermanagement:Visible(true) - end - TriggerEvent("EasyAdmin:BuildCachedOptions", cachedplayer.id) - for i, plugin in pairs(plugins) do - if plugin.functions.cachedMenu then - PrintDebugMessage("Processing Plugin: "..plugin.name, 4) - local ran, errorMsg = pcall(plugin.functions.cachedMenu, cachedplayer.id ) - if not ran then - PrintDebugMessage("Error in plugin "..plugin.name..": \n"..errorMsg, 1) - end - end - end - thisPlayer:RefreshIndexRecursively() - end - end - - end - end - end - cachedListGenerated=true - CachedList:RefreshIndexRecursively() - end - end - - end - - - if DoesPlayerHavePermissionForCategory(-1, "server") then - if permissions["server.announce"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("announcement"), GetLocalisedText("announcementguide")) - servermanagement:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 128) - - if result then - TriggerServerEvent("EasyAdmin:Announce", result) - end - end - end - - if permissions["server.convars"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("setgametype"), GetLocalisedText("setgametypeguide")) - servermanagement:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 32) - - if result then - TriggerServerEvent("EasyAdmin:SetGameType", result) - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("setmapname"), GetLocalisedText("setmapnameguide")) - servermanagement:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 32) - - if result then - TriggerServerEvent("EasyAdmin:SetMapName", result) - end - end - end - - if permissions["server.resources.start"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("startresourcebyname"), GetLocalisedText("startresourcebynameguide")) - servermanagement:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 32) - - if result then - TriggerServerEvent("EasyAdmin:StartResource", result) - end - end - end - - if permissions["server.resources.stop"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("stopresourcebyname"), GetLocalisedText("stopresourcebynameguide")) - servermanagement:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 32) - - if result then - if result ~= GetCurrentResourceName() then - TriggerServerEvent("EasyAdmin:StopResource", result) - else - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("badidea")) - end - end - end - end - - if permissions["server.convars"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("setconvar"), GetLocalisedText("setconvarguide")) - servermanagement:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("EA_SETCONVAR_1", GetLocalisedText("convarname")) - AddTextEntry("EA_SETCONVAR_2", GetLocalisedText("convarvalue")) - local result = displayKeyboardInput("EA_SETCONVAR_1", "", 64) - - if result then - local result2 = displayKeyboardInput("EA_SETCONVAR_2", "", 64) - - if result2 then - TriggerServerEvent("EasyAdmin:SetConvar", result, result2) - end - end - end - end - end - - if permissions["player.ban.view"] then - unbanPlayer = _menuPool:AddSubMenu(servermanagement,GetLocalisedText("viewbanlist"),"",true) - local thisMenuWidth = menuWidth - if menuWidth < 150 then - thisMenuWidth = 150 - else - thisMenuWidth = menuWidth - end - unbanPlayer:SetMenuWidthOffset(thisMenuWidth) - local reason = "" - local identifier = "" - - local function generateBanOverview(banId) - _menuPool:Remove() - TriggerEvent("EasyAdmin:MenuRemoved") - _menuPool = NativeUI.CreatePool() - collectgarbage() - if not GetResourceKvpString("ea_menuorientation") then - SetResourceKvp("ea_menuorientation", "middle") - SetResourceKvpInt("ea_menuwidth", 0) - menuWidth = 0 - menuOrientation = handleOrientation("middle") - else - menuWidth = GetResourceKvpInt("ea_menuwidth") - menuOrientation = handleOrientation(GetResourceKvpString("ea_menuorientation")) - end - - local mainMenu = NativeUI.CreateMenu("", "Ban Infos", menuOrientation, 0, "easyadmin", "banner-gradient", "logo") - _menuPool:Add(mainMenu) - - local thisMenuWidth = menuWidth - if menuWidth < 150 then - thisMenuWidth = 150 - else - thisMenuWidth = menuWidth - end - mainMenu:SetMenuWidthOffset(thisMenuWidth) - _menuPool:ControlDisablingEnabled(false) - _menuPool:MouseControlsEnabled(false) - - if banlist[banId].banid then - local thisItem = NativeUI.CreateItem("Ban ID: "..banlist[banId].banid, "") - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - copyToClipboard(banlist[banId].banid) - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("reason"),banlist[banId].reason) - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - --nothing - end - - - if banlist[banId].name then - local thisItem = NativeUI.CreateItem("Name: "..banlist[banId].name, "") - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if permissions["player.ban.edit"] then - local result = displayKeyboardInput("", banlist[banId].name, 64) - - if result then - banlist[banId].name = result - end - end - end - end - - if banlist[banId].banner then - local thisItem = NativeUI.CreateItem("Banner: "..banlist[banId].banner, "") - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if permissions["player.ban.edit"] then - local result = displayKeyboardInput("", banlist[banId].banner, 64) - - if result then - banlist[banId].banner = result - end - end - end - end - - - if banlist[banId].expireString then - local thisItem = NativeUI.CreateItem("Expires: "..banlist[banId].expireString, "") - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if permissions["player.ban.edit"] then - AddTextEntry("EA_ENTERTIME", "Enter Unix Timestamp") - local result = displayKeyboardInput("EA_ENTERTIME", banlist[banId].expire, 64) - - if result then - banlist[banId].expire = tonumber(result) - end - end - end - end - - for _, identifier in pairs(banlist[banId].identifiers) do - if not (GetConvar("ea_IpPrivacy", "true") == "true" and string.split(identifier, ":")[1] == "ip") then - local thisItem = NativeUI.CreateItem(string.format(GetLocalisedText("identifier"), string.split(identifier, ":")[1]),identifier) - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - copyToClipboard(identifier) - end - end - end - - if permissions["player.ban.edit"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("savebanchanges"),GetLocalisedText("savebanguide")) - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:editBan", banlist[banId]) - end - end - - if permissions["player.ban.remove"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("unbanplayer"), GetLocalisedText("unbanplayerguide")) - mainMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:unbanPlayer", banlist[banId].banid) - TriggerServerEvent("EasyAdmin:requestBanlist") - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - unbanPlayer:Visible(true) - end - mainMenu:Visible(true) - end - end - - - local thisItem = NativeUI.CreateItem(GetLocalisedText("searchbans"), "") - unbanPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - local result = displayKeyboardInput("FMMC_KEY_TIP8", "", 128) - - local foundBan = false - if result then - for i,theBanned in ipairs(banlist) do - if foundBan then - break - end - if tostring(theBanned.banid) == result then - foundBan=true - foundBanid=i - break - end - end - if not foundBan then - for i,theBanned in ipairs(banlist) do - if theBanned.name then - if string.find(string.lower(theBanned.name), string.lower(result)) then - foundBan=true - foundBanid=i - break - end - end - if string.find((string.lower(theBanned.reason) or "No Reason"), string.lower(result)) then - foundBan=true - foundBanid=i - break - end - for _, identifier in pairs(theBanned.identifiers) do - if string.find(identifier, result) then - foundBan=true - foundBanid=i - break - end - end - end - end - end - _menuPool:CloseAllMenus() - Citizen.Wait(300) - if foundBan then - generateBanOverview(foundBanid) - else - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("searchbansfail")) - GenerateMenu() - unbanPlayer:Visible(true) - end - - end - - for i,theBanned in ipairs(banlist) do - if i<(banlistPage*10)+1 and i>(banlistPage*10)-10 then - if theBanned then - reason = theBanned.reason or "No Reason" - local thisItem = NativeUI.CreateItem(string.sub(reason, 1,50), "") - unbanPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - generateBanOverview(i) - end - end - end - end - - - if #banlist > (banlistPage*10) then - local thisItem = NativeUI.CreateItem(GetLocalisedText("lastpage"), "") - unbanPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - banlistPage = math.ceil(#banlist/10) - _menuPool:CloseAllMenus() - Citizen.Wait(300) - GenerateMenu() - unbanPlayer:Visible(true) - end - end - - if banlistPage>1 then - local thisItem = NativeUI.CreateItem(GetLocalisedText("firstpage"), "") - unbanPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - banlistPage = 1 - _menuPool:CloseAllMenus() - Citizen.Wait(300) - GenerateMenu() - unbanPlayer:Visible(true) - end - local thisItem = NativeUI.CreateItem(GetLocalisedText("previouspage"), "") - unbanPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - banlistPage=banlistPage-1 - _menuPool:CloseAllMenus() - Citizen.Wait(300) - GenerateMenu() - unbanPlayer:Visible(true) - end - end - if #banlist > (banlistPage*10) then - local thisItem = NativeUI.CreateItem(GetLocalisedText("nextpage"), "") - unbanPlayer:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - banlistPage=banlistPage+1 - _menuPool:CloseAllMenus() - Citizen.Wait(300) - GenerateMenu() - unbanPlayer:Visible(true) - end - end - end - - if DoesPlayerHavePermissionForCategory(-1, "server") then - - local sl = {} - if permissions["server.cleanup.cars"] then - table.insert(sl, GetLocalisedText('cars')) - end - if permissions["server.cleanup.peds"] then - table.insert(sl, GetLocalisedText('peds')) - end - if permissions["server.cleanup.props"] then - table.insert(sl, GetLocalisedText('props')) - end - - local radi = {10,20,50,100,"global"} - - if #sl > 0 and not RedM then - cleanType = sl[1] - cleanRadius = radi[1] - deepClean = true - - cleanupMenu = _menuPool:AddSubMenu(servermanagement, GetLocalisedText("cleanarea"), GetLocalisedText("cleanareaguide"),true) - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("type"), sl, 1, GetLocalisedText("cleanareaguide")) - cleanupMenu:AddItem(thisItem) - thisItem.OnListChanged = function(sender, item, index) - if item == thisItem then - i = item:IndexToItem(index) - cleanType = i - end - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("radius"), radi, 1, "") - cleanupMenu:AddItem(thisItem) - thisItem.OnListChanged = function(sender, item, index) - if item == thisItem then - i = item:IndexToItem(index) - cleanRadius = i - end - end - - local thisItem = NativeUI.CreateCheckboxItem(GetLocalisedText("deepclean"), deepClean, GetLocalisedText("deepcleanguide")) - cleanupMenu:AddItem(thisItem) - thisItem.CheckboxEvent = function(sender, item, checked_) - deepClean = checked_ - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("cleanarea"), GetLocalisedText("cleanareaguide")) - cleanupMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - if cleanType == GetLocalisedText('cars') then - cleanType = "cars" - elseif cleanType == GetLocalisedText('peds') then - cleanType = "peds" - elseif cleanType == GetLocalisedText('props') then - cleanType = "props" - end - TriggerServerEvent("EasyAdmin:requestCleanup", cleanType, cleanRadius, deepClean) - end - end - - if permissions["server.permissions.read"] then - permissionEditor = _menuPool:AddSubMenu(servermanagement, GetLocalisedText("permissioneditor"),GetLocalisedText("permissioneditorguide"),true) - local thisMenuWidth = menuWidth - if menuWidth < 150 then - thisMenuWidth = 150 - else - thisMenuWidth = menuWidth - end - permissionEditor:SetMenuWidthOffset(thisMenuWidth) - - editAces = _menuPool:AddSubMenu(permissionEditor, GetLocalisedText("aces"),"",true) - editAces:SetMenuWidthOffset(thisMenuWidth) - - - if permissions["server.permissions.read"] and permissions["server.permissions.write"] then - local thisMenu = _menuPool:AddSubMenu(editAces, GetLocalisedText("addace"), "", true) - thisMenu:SetMenuWidthOffset(thisMenuWidth) - local tempAce = {} - local thisItem = NativeUI.CreateItem(GetLocalisedText("group"), "") - thisItem:RightLabel(tempAce[1] or "") - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERGROUP", GetLocalisedText("entergroup")) - local result = displayKeyboardInput("ENTERGROUP", "group.", 64) - - if result and result ~= "" then - tempAce[1] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem.Text._Text = result - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("permission"), "") - thisItem:RightLabel(formatRightString(tempAce[2])) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERPERM", GetLocalisedText("enterperm")) - local result = displayKeyboardInput("ENTERPERM", "", 64) - - if result and result ~= "" then - tempAce[2] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem:RightLabel(formatRightString(result)) - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("state"), GetLocalisedText("stateguide")) - thisItem:RightLabel("allow") - tempAce[3] = "allow" - thisMenu:AddItem(thisItem) - thisItem:Enabled(false) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("addace"), GetLocalisedText("addaceguide")) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - table.insert(add_aces, tempAce) - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - editAces:Visible(true) - collectgarbage() - end - end - - for i, ace in pairs(add_aces) do - local thisMenu = _menuPool:AddSubMenu(editAces, ace[1].." "..ace[2], "", true) - thisMenu:SetMenuWidthOffset(thisMenuWidth) - thisMenu.ParentItem:RightLabel(ace[3]) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("group"), "") - thisItem:RightLabel(formatRightString(ace[1])) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERGROUP", GetLocalisedText("entergroup")) - local result = displayKeyboardInput("ENTERGROUP", ace[1], 64) - - if result and result ~= "" then - add_aces[i][1] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem.Text._Text = add_aces[i][1].." "..add_aces[i][2] - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("permission"), "") - thisItem:RightLabel(formatRightString(ace[2])) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERPERM", GetLocalisedText("enterperm")) - local result = displayKeyboardInput("ENTERPERM", ace[2], 64) - - if result and result ~= "" then - add_aces[i][2] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem.Text._Text = add_aces[i][1].." "..add_aces[i][2] - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("state"), GetLocalisedText("stateguide")) - thisItem:RightLabel(formatRightString(ace[3])) - thisMenu:AddItem(thisItem) - thisItem:Enabled(false) - - if (ace.file) then - local thisItem = NativeUI.CreateItem(GetLocalisedText("location"), GetLocalisedText("locationguide")) - thisItem:RightLabel(formatRightString(ace.file)) - thisMenu:AddItem(thisItem) - thisItem:Enabled(false) - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("deletepermission"), GetLocalisedText("deletepermissionguide")) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - thisMenu.ParentItem:Enabled(false) - thisMenu.ParentItem._Description = GetLocalisedText("itemdeleted") - add_aces[i] = nil - thisMenu:GoBack() - end - end - - - editPrincipals = _menuPool:AddSubMenu(permissionEditor, GetLocalisedText("principals"),"",true) - editPrincipals:SetMenuWidthOffset(thisMenuWidth) - - if permissions["server.permissions.read"] and permissions["server.permissions.write"] then - local thisMenu = _menuPool:AddSubMenu(editPrincipals, GetLocalisedText("addprincipal"), "", true) - thisMenu:SetMenuWidthOffset(thisMenuWidth) - local tempPrincipal = {} - local thisItem = NativeUI.CreateItem(GetLocalisedText("principal"), "") - thisItem:RightLabel(tempPrincipal[1] or "") - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERPRINCIPAL", GetLocalisedText("enterprincipal")) - local result = displayKeyboardInput("ENTERPRINCIPAL", "identifier.", 64) - - if result and result ~= "" then - tempPrincipal[1] = result - thisItem:RightLabel(formatRightString(result)) - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("group"), "") - thisItem:RightLabel(tempPrincipal[2] or "") - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERGROUP", GetLocalisedText("entergroup")) - local result = displayKeyboardInput("ENTERGROUP", "group.", 64) - - if result and result ~= "" then - tempPrincipal[2] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem:RightLabel(formatRightString(result)) - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("addprincipal"), GetLocalisedText("addaceguide")) -- the use of addaceguide is intentional. - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - table.insert(add_principals, tempPrincipal) - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - editPrincipals:Visible(true) - collectgarbage() - end - end - - for i, principal in pairs(add_principals) do - - local thisMenu = _menuPool:AddSubMenu(editPrincipals, principal[1], "", true) - thisMenu:SetMenuWidthOffset(thisMenuWidth) - thisMenu.ParentItem:RightLabel(principal[2]) - - local thisItem = NativeUI.CreateItem(GetLocalisedText("principal"), "") - thisItem:RightLabel(formatRightString(principal[1])) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERPRINCIPAL", GetLocalisedText("enterprincipal")) - local result = displayKeyboardInput("ENTERPRINCIPAL", principal[1], 64) - - if result and result ~= "" then - add_principals[i][1] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem.Text._Text = add_principals[i][1] - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("group"), "") - thisItem:RightLabel(formatRightString(principal[2])) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - AddTextEntry("ENTERGROUP", GetLocalisedText("entergroup")) - local result = displayKeyboardInput("ENTERGROUP", principal[2], 64) - - if result and result ~= "" then - add_principals[i][2] = result - thisItem:RightLabel(formatRightString(result)) - thisMenu.ParentItem:RightLabel(formatRightString(result)) - end - end - - if (principal.file) then - local thisItem = NativeUI.CreateItem(GetLocalisedText("location"), GetLocalisedText("locationguide")) - thisItem:RightLabel(formatRightString(principal.file)) - thisMenu:AddItem(thisItem) - thisItem:Enabled(false) - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("deleteprincipal"), GetLocalisedText("deleteprincipalguide")) - thisMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - thisMenu.ParentItem:Enabled(false) - thisMenu.ParentItem._Description = GetLocalisedText("itemdeleted") - add_principals[i] = nil - thisMenu:GoBack() - end - - end - end - if permissions["server.permissions.read"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("refreshpermissions"), "") - permissionEditor:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - add_aces, add_principals = nil, nil - TriggerServerEvent("EasyAdmin:getServerAces") - _menuPool:CloseAllMenus() - repeat - Wait(500) - until add_aces - GenerateMenu() - permissionEditor:Visible(true) - collectgarbage() - end - end - - if permissions["server.permissions.read"] and permissions["server.permissions.write"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("savechanges"), GetLocalisedText("savechangesguide")) - permissionEditor:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerLatentServerEvent("EasyAdmin:setServerAces", 200000, add_aces, add_principals) - _menuPool:CloseAllMenus() - Citizen.Wait(800) - GenerateMenu() - permissionEditor:Visible(true) - collectgarbage() - end - end - - - TriggerEvent("EasyAdmin:BuildServerManagementOptions") - for i, plugin in pairs(plugins) do - if plugin.functions.serverMenu then - PrintDebugMessage("Processing Plugin: "..plugin.name, 4) - local ran, errorMsg = pcall(plugin.functions.serverMenu) - if not ran then - PrintDebugMessage("Error in plugin "..plugin.name..": \n"..errorMsg, 1) - end - end - end - end - - if permissions["player.ban.view"] then - local sl = {GetLocalisedText("unbanreasons"), GetLocalisedText("unbanlicenses")} - local thisItem = NativeUI.CreateListItem(GetLocalisedText("banlistshowtype"), sl, 1,GetLocalisedText("banlistshowtypeguide")) - settingsMenu:AddItem(thisItem) - thisItem.OnListSelected = function(sender, item, index) - if item == thisItem then - i = item:IndexToItem(index) - if i == GetLocalisedText(unbanreasons) then - showLicenses = false - else - showLicenses = true - end - end - end - end - - if permissions["player.ban.view"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("refreshbanlist"), GetLocalisedText("refreshbanlistguide")) - settingsMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - banlist=nil - TriggerServerEvent("EasyAdmin:updateBanlist") - repeat - Wait(500) - until banlist - GenerateMenu() - settingsMenu:Visible(true) - collectgarbage() - end - end - - if permissions["player.ban.temporary"] or permissions["player.ban.permanent"] then - local thisItem = NativeUI.CreateItem(GetLocalisedText("refreshcachedplayers"), GetLocalisedText("refreshcachedplayersguide")) - settingsMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - cachedplayers=nil - TriggerServerEvent("EasyAdmin:requestCachedPlayers") - repeat - Wait(500) - until cachedplayers - GenerateMenu() - settingsMenu:Visible(true) - collectgarbage() - end - end - - local thisItem = NativeUI.CreateItem(GetLocalisedText("refreshpermissions"), GetLocalisedText("refreshpermissionsguide")) - settingsMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - TriggerServerEvent("EasyAdmin:amiadmin") - end - - local orientationIndex = 1 - if GetResourceKvpString("ea_menuorientation") == "middle" then - orientationIndex = 2 - elseif GetResourceKvpString("ea_menuorientation") == "right" then - orientationIndex = 3 - end - - local sl = {GetLocalisedText("left"), GetLocalisedText("middle"), GetLocalisedText("right")} - local thisItem = NativeUI.CreateListItem(GetLocalisedText("menuOrientation"), sl, orientationIndex, GetLocalisedText("menuOrientationguide")) - settingsMenu:AddItem(thisItem) - thisItem.OnListSelected = function(sender, item, index) - if item == thisItem then - i = item:IndexToItem(index) - if i == GetLocalisedText("left") then - SetResourceKvp("ea_menuorientation", "left") - elseif i == GetLocalisedText("middle") then - SetResourceKvp("ea_menuorientation", "middle") - else - SetResourceKvp("ea_menuorientation", "right") - end - end - end - - local sl = {} - for i=0,250,10 do - table.insert(sl,i) - end - local thisi = 0 - for i,a in ipairs(sl) do - if menuWidth == a then - thisi = i - end - end - local thisItem = NativeUI.CreateSliderItem(GetLocalisedText("menuOffset"), sl, thisi, GetLocalisedText("menuOffsetguide"), false) - settingsMenu:AddItem(thisItem) - thisItem.OnSliderSelected = function(index) - i = thisItem:IndexToItem(index) - SetResourceKvpInt("ea_menuwidth", i) - menuWidth = i - end - thisi = nil - sl = nil - - local thisItem = NativeUI.CreateItem(GetLocalisedText("resetmenuOffset"), "") - settingsMenu:AddItem(thisItem) - thisItem.Activated = function(ParentMenu,SelectedItem) - SetResourceKvpInt("ea_menuwidth", 0) - menuWidth = 0 - end - - if permissions["anon"] then - local thisItem = NativeUI.CreateCheckboxItem(GetLocalisedText("anonymous"), anonymous or false, GetLocalisedText("anonymousguide")) - settingsMenu:AddItem(thisItem) - thisItem.CheckboxEvent = function(sender, item, checked_) - anonymous = checked_ - TriggerServerEvent("EasyAdmin:SetAnonymous", checked_) - end - end - - local thisItem = NativeUI.CreateCheckboxItem(GetLocalisedText("screenreader"), GetResourceKvpInt('ea_tts') == 1 and true or false, GetLocalisedText("screenreaderguide")) - settingsMenu:AddItem(thisItem) - thisItem.CheckboxEvent = function(sender, item, checked_) - SendNUIMessage({ - action = "toggle_speak", - enabled = checked_ - }) - SetResourceKvpInt("ea_tts", checked_ and 1 or 0) - SendNUIMessage({action= "speak", text="Text to Speech"}) - end - - if not RedM then - local sl = {"none"} - local currentEggIndex = 1 - for k,v in pairs(eastereggs) do - table.insert(sl, k) - end - for k,v in pairs(sl) do - if v == overrideEgg or v == currentEgg then - currentEggIndex = k - end - end - - local thisItem = NativeUI.CreateListItem(GetLocalisedText("forceeasteregg"), sl, currentEggIndex, "") - settingsMenu:AddItem(thisItem) - thisItem.OnListSelected = function(sender, item, index) - if item == thisItem then - i = item:IndexToItem(index) - if i == "none" then - overrideEgg = false - else - overrideEgg = i - end - end - end - end - - TriggerEvent("EasyAdmin:BuildSettingsOptions") - for i, plugin in pairs(plugins) do - if plugin.functions.settingsMenu then - PrintDebugMessage("Processing Plugin: "..plugin.name, 4) - local ran, errorMsg = pcall(plugin.functions.settingsMenu) - if not ran then - PrintDebugMessage("Error in plugin "..plugin.name..": \n"..errorMsg, 1) - end - end - end - _menuPool:ControlDisablingEnabled(false) - _menuPool:MouseControlsEnabled(false) - _menuPool:RefreshIndex() -- refresh indexes -end - - -Citizen.CreateThread( function() - while true do - Citizen.Wait(0) - if drawInfo then - local text = {} - -- cheat checks - local targetPed = GetPlayerPed(drawTarget) - if (not RedM) then - local targetGod = GetPlayerInvincible(drawTarget) - if targetGod then - table.insert(text,GetLocalisedText("godmodedetected")) - else - table.insert(text,GetLocalisedText("godmodenotdetected")) - end - if not CanPedRagdoll(targetPed) and not IsPedInAnyVehicle(targetPed, false) and (GetPedParachuteState(targetPed) == -1 or GetPedParachuteState(targetPed) == 0) and not IsPedInParachuteFreeFall(targetPed) then - table.insert(text,GetLocalisedText("antiragdoll")) - end - -- health info - table.insert(text,GetLocalisedText("health")..": "..GetEntityHealth(targetPed).."/"..GetEntityMaxHealth(targetPed)) - - table.insert(text,GetLocalisedText("armor")..": "..GetPedArmour(targetPed)) - - -- misc info - table.insert(text,GetLocalisedText("wantedlevel")..": "..GetPlayerWantedLevel(drawTarget)) - table.insert(text,GetLocalisedText("exitspectator")) - - for i,theText in pairs(text) do - SetTextFont(0) - SetTextProportional(1) - SetTextScale(0.0, 0.30) - SetTextDropshadow(0, 0, 0, 0, 255) - SetTextEdge(1, 0, 0, 0, 255) - SetTextDropShadow() - SetTextOutline() - SetTextEntry("STRING") - AddTextComponentString(theText) - EndTextCommandDisplayText(0.3, 0.7+(i/30)) - end - elseif (RedM) then - local targetGod = GetPlayerInvincible(drawTarget) - if targetGod then - table.insert(text,GetLocalisedText("godmodedetected")) - else - table.insert(text,GetLocalisedText("godmodenotdetected")) - end - - table.insert(text,GetLocalisedText("health")..": "..GetEntityHealth(targetPed).."/"..GetEntityMaxHealth(targetPed)) - table.insert(text,GetLocalisedText("exitspectator")) - - for i,theText in pairs(text) do - Citizen.InvokeNative(0xADA9255D,0) - SetTextScale(0.0, 0.30) - SetTextDropshadow(0, 0, 0, 0, 255) - - local str = CreateVarString(10, "LITERAL_STRING",Text) - DisplayText(theText, 0.3, 0.7+(i/30)) - end - end - - if (not RedM and IsControlJustPressed(0,103) or (RedM and IsControlJustReleased(0, Controls["Enter"]))) then - local targetPed = PlayerPedId() - local targetPlayer = -1 - local targetx,targety,targetz = table.unpack(GetEntityCoords(targetPed, false)) - spectatePlayer(targetPed,targetPlayer,GetPlayerName(targetPlayer)) - TriggerEvent('EasyAdmin:FreezePlayer', false) - --SetEntityCoords(PlayerPedId(), oldCoords.x, oldCoords.y, oldCoords.z, 0, 0, 0, false) - if not RedM then - TriggerEvent('EasyAdmin:FreezePlayer', false) - end - - StopDrawPlayerInfo() - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("stoppedSpectating")) - end - else - Citizen.Wait(1000) - end - end -end) diff --git a/server-data/resources/[esx]/EasyAdmin/client/plugins.lua b/server-data/resources/[esx]/EasyAdmin/client/plugins.lua deleted file mode 100644 index 35837315c..000000000 --- a/server-data/resources/[esx]/EasyAdmin/client/plugins.lua +++ /dev/null @@ -1,10 +0,0 @@ -plugins = {} - -function addPlugin(data) - table.insert(plugins, data) - -- sort plugins table by name (alphabetically) - table.sort(plugins, function(a, b) return a.name < b.name end) - - TriggerEvent('EasyAdmin:pluginAdded', data.name) - PrintDebugMessage("Added Plugin "..data.name, 4) -end \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/Controls.lua b/server-data/resources/[esx]/EasyAdmin/dependencies/Controls.lua deleted file mode 100644 index 4505864ae..000000000 --- a/server-data/resources/[esx]/EasyAdmin/dependencies/Controls.lua +++ /dev/null @@ -1,776 +0,0 @@ --- RedM Only - -Controls = {} -Controls["NextCamera"] = 0x7F8D09B8 -Controls["LookLr"] = 0xA987235F -Controls["LookUd"] = 0xD2047988 -Controls["LookUpOnly"] = 0xC0651D40 -Controls["LookDownOnly"] = 0x8ED92E16 -Controls["LookLeftOnly"] = 0x08F8BC6D -Controls["LookRightOnly"] = 0xA1EB1353 -Controls["CinematicSlowmo"] = 0x7A9093DE -Controls["RadialMenuNavUd"] = 0xBA60039F -Controls["RadialMenuNavLr"] = 0x390948DC -Controls["RadialMenuSlotNavNext"] = 0xE71F89B8 -Controls["RadialMenuSlotNavPrev"] = 0x93D6723F -Controls["SelectNextWheel"] = 0x77E56FB3 -Controls["SelectItemWheel"] = 0x1F6D95E5 -Controls["QuickSelectInspect"] = 0xF09866F3 -Controls["QuickSelectSetForSwap"] = 0xD45EC04F -Controls["QuickShortcutAbilitiesMenu"] = 0x9CC7A1A4 -Controls["QuickSelectSecondaryNavNext"] = 0xF1421CF5 -Controls["QuickSelectSecondaryNavPrev"] = 0xD9F9F017 -Controls["QuickSelectToggleShortcutItem"] = 0xFA0B29CD -Controls["QuickSelectPutAwayRod"] = 0x253FEC09 -Controls["EmotesFavorite"] = 0xA835261B -Controls["EmotesManage"] = 0x7E75F4DC -Controls["EmotesSlotNavNext"] = 0xCBB12F87 -Controls["SelectNextWeapon"] = 0xD0842EDF -Controls["SelectPrevWeapon"] = 0xF78D7337 -Controls["SkipCutscene"] = 0xCDC4E4E9 -Controls["CharacterWheel"] = 0x972F8D1E -Controls["MultiplayerInfo"] = 0xE8342FF2 -Controls["Sprint"] = 0x8FFC75D6 -Controls["Jump"] = 0xD9D0E1C0 -Controls["Enter"] = 0xCEFD9220 -Controls["Attack"] = 0x07CE1E61 -Controls["Aim"] = 0xF84FA74F -Controls["LookBehind"] = 0x9959A6F0 -Controls["Phone"] = 0x4CF871D0 -Controls["SpecialAbility"] = 0xCEE12B50 -Controls["SpecialAbilitySecondary"] = 0x6328239B -Controls["SecondarySpecialAbilitySecondary"] = 0x811F4A1A -Controls["SpecialAbilityAction"] = 0x1ECA87D4 -Controls["MoveLr"] = 0x4D8FB4C1 -Controls["MoveUd"] = 0xFDA83190 -Controls["MoveUpOnly"] = 0x8FD015D8 -Controls["MoveDownOnly"] = 0xD27782E3 -Controls["MoveLeftOnly"] = 0x7065027D -Controls["MoveRightOnly"] = 0xB4E465B4 -Controls["Duck"] = 0xDB096B85 -Controls["TwirlPistol"] = 0x938D4071 -Controls["ToggleHolster"] = 0xB238FE0B -Controls["OpenWheelMenu"] = 0xAC4BD4F1 -Controls["OpenSatchelMenu"] = 0x4CC0E2FE -Controls["OpenSatchelHorseMenu"] = 0x5966D52A -Controls["OpenCraftingMenu"] = 0x734C6E39 -Controls["OpenJournal"] = 0xF3830D8E -Controls["Pickup"] = 0xE6360A8E -Controls["Ignite"] = 0xC75C27B0 -Controls["SniperZoom"] = 0x7ABC6A66 -Controls["SniperZoomInOnly"] = 0xA5BDCD3C -Controls["SniperZoomOutOnly"] = 0x430593AA -Controls["SniperZoomInSecondary"] = 0x6BE9C207 -Controls["SniperZoomOutSecondary"] = 0x8A7B8833 -Controls["ToggleWeaponScope"] = 0x3076E97C -Controls["Cover"] = 0xDE794E3E -Controls["CoverTransition"] = 0x750C8010 -Controls["Reload"] = 0xE30CD707 -Controls["Talk"] = 0x7DCA9C75 -Controls["Detonate"] = 0x73846677 -Controls["HudSpecial"] = 0x580C4473 -Controls["Arrest"] = 0xA4F1006B -Controls["AccurateAim"] = 0x406ADFAE -Controls["SwitchShoulder"] = 0x827E9EE8 -Controls["IronSight"] = 0x841240A9 -Controls["AimInAir"] = 0xD8F73058 -Controls["SwitchFiringMode"] = 0xEED15F18 -Controls["Context"] = 0xB73BCA77 -Controls["ContextSecondary"] = 0xF19BE385 -Controls["WeaponSpecial"] = 0x733901F3 -Controls["WeaponSpecialTwo"] = 0x50BA1A77 -Controls["Dive"] = 0x06052D11 -Controls["DropWeapon"] = 0x7DBCD016 -Controls["DropAmmo"] = 0x4E42696E -Controls["ThrowGrenade"] = 0x0AF99998 -Controls["FocusCam"] = 0xE72B43F4 -Controls["Inspect"] = 0xA61DC630 -Controls["InspectZoom"] = 0x53296B75 -Controls["InspectLr"] = 0x1788C283 -Controls["InspectUd"] = 0xF9781997 -Controls["InspectOpenSatchel"] = 0x9B1CA8DA -Controls["DynamicScenario"] = 0x2EAB0795 -Controls["PlayerMenu"] = 0x80F28E95 -Controls["OpenEmoteWheel"] = 0xE2B557A3 -Controls["OpenEmoteWheelHorse"] = 0x8B3FA65E -Controls["EmoteGroupLink"] = 0x1C826362 -Controls["EmoteGroupLinkHorse"] = 0x4FD1C57B -Controls["RevealHud"] = 0xCF8A4ECA -Controls["SelectRadarMode"] = 0x0F39B3D4 -Controls["SimpleRadar"] = 0x5FEF1B6D -Controls["ExpandRadar"] = 0xCF0B11DE -Controls["RegularRadar"] = 0x51AA7A35 -Controls["DisableRadar"] = 0x70CBD78D -Controls["Surrender"] = 0xDB8D69B8 -Controls["Whistle"] = 0x24978A28 -Controls["WhistleHorseback"] = 0xE7EB9185 -Controls["StopLeadingAnimal"] = 0x7914A3DD -Controls["CinematicCam"] = 0x620A6C5E -Controls["CinematicCamHold"] = 0xD7E7B375 -Controls["CinematicCamChangeShot"] = 0xA6C67243 -Controls["CinematicCamUd"] = 0x84574AE8 -Controls["CinematicCamUpOnly"] = 0xEFCFE6B7 -Controls["CinematicCamDownOnly"] = 0x23AE34A2 -Controls["CinematicCamLr"] = 0x6BC904FC -Controls["ContextA"] = 0x5181713D -Controls["ContextB"] = 0x3B24C470 -Controls["ContextX"] = 0xE3BF959B -Controls["ContextY"] = 0xD51B784F -Controls["ContextLt"] = 0xC13A6564 -Controls["ContextRt"] = 0x07B8BEAF -Controls["ContextAction"] = 0xB28318C0 -Controls["VehMoveLr"] = 0xF1E2852C -Controls["VehMoveUd"] = 0x8A81C00C -Controls["VehMoveUpOnly"] = 0xDEBD7EF6 -Controls["VehMoveDownOnly"] = 0x16D73E1D -Controls["VehMoveLeftOnly"] = 0x9DF54706 -Controls["VehMoveRightOnly"] = 0x97A8FD98 -Controls["VehSpecial"] = 0x493919DB -Controls["VehGunLr"] = 0xB6F3E4FE -Controls["VehGunUd"] = 0x482560EE -Controls["VehAim"] = 0xD7CAFCEF -Controls["VehAttack"] = 0xF4330038 -Controls["VehAttack2"] = 0xF1C341BA -Controls["VehAccelerate"] = 0x5B9FD4E2 -Controls["VehBrake"] = 0x6E1F639B -Controls["VehDuck"] = 0x5B3690F2 -Controls["VehHeadlight"] = 0xF1301666 -Controls["VehExit"] = 0xFEFAB9B4 -Controls["VehHandbrake"] = 0x65D24C98 -Controls["VehLookBehind"] = 0xCAE9B017 -Controls["VehNextRadio"] = 0x22E0F7E7 -Controls["VehPrevRadio"] = 0x9785CE13 -Controls["VehNextRadioTrack"] = 0xF7FA2DDC -Controls["VehPrevRadioTrack"] = 0x0A94C4FF -Controls["VehRadioWheel"] = 0x4915AC0A -Controls["VehHorn"] = 0x63A0D258 -Controls["VehFlyThrottleUp"] = 0x7232BAB3 -Controls["VehFlyThrottleDown"] = 0x084DFF95 -Controls["VehFlyYawLeft"] = 0x31589AD1 -Controls["VehFlyYawRight"] = 0xBD143FC6 -Controls["VehPassengerAim"] = 0xEE2804D0 -Controls["VehPassengerAttack"] = 0x27AD4433 -Controls["VehSpecialAbilityFranklin"] = 0x5EC33578 -Controls["VehStuntUd"] = 0x4AA1560E -Controls["VehSelectNextWeapon"] = 0x889A626F -Controls["VehSelectPrevWeapon"] = 0x0C97BAC7 -Controls["VehRoof"] = 0x3E7CF9A4 -Controls["VehJump"] = 0xAA56B926 -Controls["VehGrapplingHook"] = 0xB985AA5E -Controls["VehShuffle"] = 0xC7083798 -Controls["VehTraversal"] = 0x739D6261 -Controls["VehDropProjectile"] = 0xC61611E6 -Controls["VehMouseControlOverride"] = 0x39CCABD5 -Controls["VehFlyRollLr"] = 0x3C8AB570 -Controls["VehFlyRollLeftOnly"] = 0x56F84EA0 -Controls["VehFlyRollRightOnly"] = 0x876B3361 -Controls["VehFlyPitchUd"] = 0xE67E1E57 -Controls["VehFlyPitchUpOnly"] = 0x6280BA1A -Controls["VehFlyPitchDownOnly"] = 0x0F4E369F -Controls["VehFlyUndercarriage"] = 0xFE0FE518 -Controls["VehFlyAttack"] = 0x1D71D7AA -Controls["VehFlySelectNextWeapon"] = 0x24E94299 -Controls["VehFlySelectPrevWeapon"] = 0xC0D874E5 -Controls["VehFlySelectTargetLeft"] = 0x307FC4C1 -Controls["VehFlySelectTargetRight"] = 0x52F25C96 -Controls["VehFlyVerticalFlightMode"] = 0xE3238029 -Controls["VehFlyDuck"] = 0x378A10F7 -Controls["VehFlyAttackCamera"] = 0x2FBA3F0B -Controls["VehFlyMouseControlOverride"] = 0x6C9810A5 -Controls["VehSubMouseControlOverride"] = 0x2CAF327E -Controls["VehSubTurnLr"] = 0x627C4619 -Controls["VehSubTurnLeftOnly"] = 0x44E7E093 -Controls["VehSubTurnRightOnly"] = 0xE78A5A3C -Controls["VehSubPitchUd"] = 0x469CE271 -Controls["VehSubPitchUpOnly"] = 0xF9EF072A -Controls["VehSubPitchDownOnly"] = 0xBA2D22AA -Controls["VehSubThrottleUp"] = 0xD28C446F -Controls["VehSubThrottleDown"] = 0xF5B2CEFB -Controls["VehSubAscend"] = 0xD7991F74 -Controls["VehSubDescend"] = 0x7D51DE24 -Controls["VehSubTurnHardLeft"] = 0x64214D49 -Controls["VehSubTurnHardRight"] = 0xA44C0F83 -Controls["VehPushbikePedal"] = 0xFD8D64A7 -Controls["VehPushbikeSprint"] = 0xF03EE151 -Controls["VehPushbikeFrontBrake"] = 0x585E942D -Controls["VehPushbikeRearBrake"] = 0xF8CBAFB5 -Controls["VehDraftMoveUd"] = 0x23595CEA -Controls["VehDraftTurnLr"] = 0xA7DFAE8A -Controls["VehDraftMoveUpOnly"] = 0x29A5E51E -Controls["VehDraftMoveDownOnly"] = 0x25493EB3 -Controls["VehDraftTurnLeftOnly"] = 0x198AFC64 -Controls["VehDraftTurnRightOnly"] = 0x5E371EA7 -Controls["VehDraftAccelerate"] = 0xE99D2B05 -Controls["VehDraftBrake"] = 0xD648E48D -Controls["VehDraftAim"] = 0xBDD5830D -Controls["VehDraftAttack"] = 0xF40AB198 -Controls["VehDraftAttack2"] = 0x886F12DD -Controls["VehDraftSwitchDrivers"] = 0x70B87844 -Controls["VehBoatTurnLr"] = 0xD8DFCAB3 -Controls["VehBoatTurnLeftOnly"] = 0x5BED7C91 -Controls["VehBoatTurnRightOnly"] = 0xF9780DFB -Controls["VehBoatAccelerate"] = 0xB341E812 -Controls["VehBoatBrake"] = 0x428D5F39 -Controls["VehBoatAim"] = 0x92F5F01E -Controls["VehBoatAttack"] = 0x6866FA3A -Controls["VehBoatAttack2"] = 0x876096E9 -Controls["VehCarTurnLr"] = 0x3BD38D43 -Controls["VehCarTurnLeftOnly"] = 0x07D1654C -Controls["VehCarTurnRightOnly"] = 0x6E3C3649 -Controls["VehCarAccelerate"] = 0xB9F544B0 -Controls["VehCarBrake"] = 0xD1887B3F -Controls["VehCarAim"] = 0x6777B840 -Controls["VehCarAttack"] = 0x5572F386 -Controls["VehCarAttack2"] = 0x5B763AD7 -Controls["VehHandcartAccelerate"] = 0xFF3626FC -Controls["VehHandcartBrake"] = 0x2D79D80A -Controls["HorseMoveLr"] = 0x126796EB -Controls["HorseMoveUd"] = 0x3BBDEFEF -Controls["HorseMoveUpOnly"] = 0x699487BB -Controls["HorseMoveDownOnly"] = 0x56F82045 -Controls["HorseMoveLeftOnly"] = 0x86D773F6 -Controls["HorseMoveRightOnly"] = 0x7E6B8612 -Controls["HorseSpecial"] = 0x70089459 -Controls["HorseGunLr"] = 0x3D99EEC6 -Controls["HorseGunUd"] = 0xBFF476F9 -Controls["HorseAttack"] = 0x60C81CDE -Controls["HorseAttack2"] = 0xC904196D -Controls["HorseSprint"] = 0x5AA007D7 -Controls["HorseStop"] = 0xE16B9AAD -Controls["HorseExit"] = 0xCBDB82A8 -Controls["HorseLookBehind"] = 0x81280569 -Controls["HorseJump"] = 0xE4D2CE1D -Controls["HorseAim"] = 0x61470051 -Controls["HorseCollect"] = 0x7D5B3717 -Controls["HitchAnimal"] = 0xA95E1468 -Controls["HorseCommandFlee"] = 0x4216AF06 -Controls["HorseCommandStay"] = 0xAE5DFDED -Controls["HorseCommandFollow"] = 0x763E4D27 -Controls["HorseMelee"] = 0x1A3EABBB -Controls["MeleeHorseAttackPrimary"] = 0x78ED2132 -Controls["MeleeHorseAttackSecondary"] = 0x162AFEB8 -Controls["HorseCoverTransition"] = 0x2996DD15 -Controls["MeleeAttack"] = 0xB2F377E8 -Controls["MeleeModifier"] = 0x1E7D7275 -Controls["MeleeBlock"] = 0xB5EEEFB7 -Controls["MeleeGrapple"] = 0x2277FAE9 -Controls["MeleeGrappleAttack"] = 0xADEAF48C -Controls["MeleeGrappleChoke"] = 0x018C47CF -Controls["MeleeGrappleReversal"] = 0x91C9A817 -Controls["MeleeGrappleBreakout"] = 0xD0C1FEFF -Controls["MeleeGrappleStandSwitch"] = 0xBE1F4699 -Controls["MeleeGrappleMountSwitch"] = 0x67ED272E -Controls["ParachuteDeploy"] = 0xEBF53058 -Controls["ParachuteDetach"] = 0xFFBFF139 -Controls["ParachuteTurnLr"] = 0x8EC920BF -Controls["ParachuteTurnLeftOnly"] = 0xC4CF3322 -Controls["ParachuteTurnRightOnly"] = 0x2BDBA378 -Controls["ParachutePitchUd"] = 0xF0526228 -Controls["ParachutePitchUpOnly"] = 0x08BFEA69 -Controls["ParachutePitchDownOnly"] = 0x7C3A4352 -Controls["ParachuteBrakeLeft"] = 0x272BD8BA -Controls["ParachuteBrakeRight"] = 0x948B3EA7 -Controls["ParachuteSmoke"] = 0x2574FAB0 -Controls["ParachutePrecisionLanding"] = 0xC675B8BD -Controls["Map"] = 0xE31C6A41 -Controls["SelectWeaponUnarmed"] = 0x1F6EEB0F -Controls["SelectWeaponMelee"] = 0x109E6852 -Controls["SelectWeaponHandgun"] = 0x184960E3 -Controls["SelectWeaponShotgun"] = 0x76D3EA05 -Controls["SelectWeaponSmg"] = 0xCEF1BB48 -Controls["SelectWeaponAutoRifle"] = 0x05EEA9D0 -Controls["SelectWeaponSniper"] = 0x96C61FDF -Controls["SelectWeaponHeavy"] = 0x3D1675C3 -Controls["SelectWeaponSpecial"] = 0xC41ECEF8 -Controls["SelectCharacterMichael"] = 0xEA9256B8 -Controls["SelectCharacterFranklin"] = 0x8E8B08CB -Controls["SelectCharacterTrevor"] = 0xB00CC093 -Controls["SelectCharacterMultiplayer"] = 0xDFB2B3B8 -Controls["SaveReplayClip"] = 0x5B3AF9E3 -Controls["SpecialAbilityPc"] = 0x52E60A8B -Controls["SelectQuickselectSidearmsLeft"] = 0xE6F612E4 -Controls["SelectQuickselectDualwield"] = 0x1CE6D9EB -Controls["SelectQuickselectSidearmsRight"] = 0x4F49CC4C -Controls["SelectQuickselectUnarmed"] = 0x8F9F9E58 -Controls["SelectQuickselectMeleeNoUnarmed"] = 0xAB62E997 -Controls["SelectQuickselectSecondaryLongarm"] = 0xA1FDE2A6 -Controls["SelectQuickselectThrown"] = 0xB03A913B -Controls["SelectQuickselectPrimaryLongarm"] = 0x42385422 -Controls["CellphoneUp"] = 0xD2EE3B1E -Controls["CellphoneDown"] = 0x82196002 -Controls["CellphoneLeft"] = 0x3ABBE990 -Controls["CellphoneRight"] = 0xD25EFDCD -Controls["CellphoneSelect"] = 0xDC264018 -Controls["CellphoneCancel"] = 0xDD833287 -Controls["CellphoneOption"] = 0xD2C28BB4 -Controls["CellphoneExtraOption"] = 0xBE354011 -Controls["CellphoneScrollForward"] = 0xCB4E1798 -Controls["CellphoneScrollBackward"] = 0x47CD0F3B -Controls["CellphoneCameraFocusLock"] = 0x5AC1805E -Controls["CellphoneCameraGrid"] = 0xE18CC57A -Controls["CellphoneCameraSelfie"] = 0x6A440BFE -Controls["CellphoneCameraDof"] = 0x593DB489 -Controls["CellphoneCameraExpression"] = 0xD7E274E7 -Controls["FrontendDown"] = 0x05CA7C52 -Controls["FrontendUp"] = 0x6319DB71 -Controls["FrontendLeft"] = 0xA65EBAB4 -Controls["FrontendRight"] = 0xDEB34313 -Controls["FrontendRdown"] = 0x5734A944 -Controls["FrontendRup"] = 0xD7DE6B1E -Controls["FrontendRleft"] = 0x39336A4F -Controls["FrontendRright"] = 0x5B48F938 -Controls["FrontendAxisX"] = 0xFB56DD5B -Controls["FrontendAxisY"] = 0x091178D0 -Controls["FrontendScrollAxisX"] = 0x3224BC55 -Controls["FrontendScrollAxisY"] = 0x21651AD6 -Controls["FrontendRightAxisX"] = 0x3D23549A -Controls["FrontendRightAxisY"] = 0xEB4130DF -Controls["FrontendPause"] = 0xD82E0BD2 -Controls["FrontendPauseAlternate"] = 0x4A903C11 -Controls["FrontendAccept"] = 0xC7B5340A -Controls["FrontendCancel"] = 0x156F7119 -Controls["FrontendX"] = 0x6DB8C62F -Controls["FrontendY"] = 0x7C0162C0 -Controls["FrontendLb"] = 0xE885EF16 -Controls["FrontendRb"] = 0x17BEC168 -Controls["FrontendLt"] = 0x51104035 -Controls["FrontendRt"] = 0x6FED71BC -Controls["FrontendLs"] = 0x43CDA5B0 -Controls["FrontendRs"] = 0x7DA48D2A -Controls["FrontendLeaderboard"] = 0x9EDC8D65 -Controls["FrontendSocialClub"] = 0x064D1698 -Controls["FrontendSocialClubSecondary"] = 0xBDB8D6F3 -Controls["FrontendDelete"] = 0x4AF4D473 -Controls["FrontendEndscreenAccept"] = 0x3E32FCEE -Controls["FrontendEndscreenExpand"] = 0xC79BDE9F -Controls["FrontendSelect"] = 0x171910DC -Controls["FrontendPhotoMode"] = 0x44CD301B -Controls["FrontendNavUp"] = 0x8CFFE0A1 -Controls["FrontendNavDown"] = 0x78114AB3 -Controls["FrontendNavLeft"] = 0x877F1027 -Controls["FrontendNavRight"] = 0x08BD758C -Controls["FrontendMapNavUp"] = 0x125A70E5 -Controls["FrontendMapNavDown"] = 0xF8480EED -Controls["FrontendMapNavLeft"] = 0xE0D75B00 -Controls["FrontendMapNavRight"] = 0x28725E5D -Controls["FrontendMapZoom"] = 0x6B359A27 -Controls["GameMenuAccept"] = 0x43DBF61F -Controls["GameMenuCancel"] = 0x308588E6 -Controls["GameMenuOption"] = 0xFBD7B3E6 -Controls["GameMenuExtraOption"] = 0xD596CFB0 -Controls["GameMenuUp"] = 0x911CB09E -Controls["GameMenuDown"] = 0x4403F97F -Controls["GameMenuLeft"] = 0xAD7FCC5B -Controls["GameMenuRight"] = 0x65F9EC5B -Controls["GameMenuTabLeft"] = 0xCBD5B26E -Controls["GameMenuTabRight"] = 0x110AD1D2 -Controls["GameMenuTabLeftSecondary"] = 0x26E9DC00 -Controls["GameMenuTabRightSecondary"] = 0x8CC9CD42 -Controls["GameMenuScrollForward"] = 0x81457A1A -Controls["GameMenuScrollBackward"] = 0x9DA42644 -Controls["GameMenuStickUp"] = 0x9CA97399 -Controls["GameMenuStickDown"] = 0x63898D36 -Controls["GameMenuStickLeft"] = 0x06C089D4 -Controls["GameMenuStickRight"] = 0x5BDBE841 -Controls["GameMenuRightStickUp"] = 0xF0232A03 -Controls["GameMenuRightStickDown"] = 0xADB78673 -Controls["GameMenuRightStickLeft"] = 0x71E38966 -Controls["GameMenuRightStickRight"] = 0xE1CECE4B -Controls["GameMenuLs"] = 0xA8F6DE66 -Controls["GameMenuRs"] = 0x89EA3FA5 -Controls["GameMenuRightAxisX"] = 0x4685AA33 -Controls["GameMenuRightAxisY"] = 0x60C65EB4 -Controls["GameMenuLeftAxisX"] = 0xF431D57A -Controls["GameMenuLeftAxisY"] = 0x226EB1EF -Controls["Quit"] = 0x8E90C7BB -Controls["DocumentPageNext"] = 0xC97792B7 -Controls["DocumentPagePrev"] = 0x20190AB4 -Controls["DocumentScroll"] = 0xAC70F311 -Controls["DocumentScrollUpOnly"] = 0x3D0C19EC -Controls["DocumentScrollDownOnly"] = 0xD72F3E29 -Controls["Attack2"] = 0x0283C582 -Controls["PrevWeapon"] = 0xCC1075A7 -Controls["NextWeapon"] = 0xFD0F0C2C -Controls["SniperZoomIn"] = 0xE4568AA1 -Controls["SniperZoomOut"] = 0xE40CE39E -Controls["SniperZoomInAlternate"] = 0x3A9897C1 -Controls["SniperZoomOutAlternate"] = 0xBC820489 -Controls["ReplayStartStopRecording"] = 0xDCA6978E -Controls["ReplayStartStopRecordingSecondary"] = 0x8991A70B -Controls["ReplayMarkerDelete"] = 0xC7D2C51B -Controls["ReplayClipDelete"] = 0xF6734E42 -Controls["ReplayPause"] = 0x083137B2 -Controls["ReplayRewind"] = 0xC1339A31 -Controls["ReplayFfwd"] = 0x609A27E8 -Controls["ReplayNewmarker"] = 0xF7C6DA28 -Controls["ReplayRecord"] = 0xAD9A9C7C -Controls["ReplayScreenshot"] = 0x567FAF34 -Controls["ReplayHidehud"] = 0x7E479C7B -Controls["ReplayStartpoint"] = 0x5DAFACCF -Controls["ReplayEndpoint"] = 0x4EF75BBD -Controls["ReplayAdvance"] = 0x323AA450 -Controls["ReplayBack"] = 0x088C7CD4 -Controls["ReplayTools"] = 0x561A3387 -Controls["ReplayRestart"] = 0x81B8BC9D -Controls["ReplayShowhotkey"] = 0xEBA2A41E -Controls["ReplayCyclemarkerleft"] = 0x5C220959 -Controls["ReplayCyclemarkerright"] = 0xC69AE799 -Controls["ReplayFovincrease"] = 0x5925A10D -Controls["ReplayFovdecrease"] = 0x2B88D701 -Controls["ReplayCameraup"] = 0x749EFF0C -Controls["ReplayCameradown"] = 0xA1FE9E2A -Controls["ReplaySave"] = 0xEBC60685 -Controls["ReplayToggletime"] = 0xE3FB91B3 -Controls["ReplayToggletips"] = 0xC8A1DE20 -Controls["ReplayPreview"] = 0x58AC1355 -Controls["ReplayToggleTimeline"] = 0xF8629909 -Controls["ReplayTimelinePickupClip"] = 0xD2454F90 -Controls["ReplayTimelineDuplicateClip"] = 0x4146A033 -Controls["ReplayTimelinePlaceClip"] = 0x60726F50 -Controls["ReplayCtrl"] = 0xD88B47E7 -Controls["ReplayTimelineSave"] = 0x65D70E9D -Controls["ReplayPreviewAudio"] = 0x79022218 -Controls["ReplayActionReplayStart"] = 0xD9961107 -Controls["ReplayActionReplayCancel"] = 0x93776CAE -Controls["ReplayRecordingStart"] = 0xFD28D0F4 -Controls["ReplayRecordingStop"] = 0xDB16E702 -Controls["ReplaySaveSnapshot"] = 0xEFEC8FDE -Controls["VehDriveLook"] = 0xA2117C9A -Controls["VehDriveLook2"] = 0x55AC04E5 -Controls["VehFlyAttack2"] = 0x4D83147C -Controls["RadioWheelUd"] = 0x14C7291D -Controls["RadioWheelLr"] = 0xF9FA6BC8 -Controls["VehSlowmoUd"] = 0xF1F9CD26 -Controls["VehSlowmoUpOnly"] = 0x2B981F4F -Controls["VehSlowmoDownOnly"] = 0x642DE054 -Controls["MapPoi"] = 0x9BEE9213 -Controls["InteractLockon"] = 0xF8982F00 -Controls["InteractLockonNeg"] = 0x26A18F47 -Controls["InteractLockonPos"] = 0xF63A17F9 -Controls["InteractLockonRob"] = 0x9FA5AD07 -Controls["InteractLockonY"] = 0x09A92B8B -Controls["InteractLockonA"] = 0xD10A3A36 -Controls["InteractNeg"] = 0x424BD2D2 -Controls["InteractPos"] = 0xF6BB7378 -Controls["InteractOption1"] = 0x760A9C6F -Controls["InteractOption2"] = 0x84543902 -Controls["InteractAnimal"] = 0xA1ABB953 -Controls["InteractLockonAnimal"] = 0x5415BE48 -Controls["InteractLeadAnimal"] = 0x17D3BFF5 -Controls["InteractLockonDetachHorse"] = 0xF5C4701B -Controls["InteractHorseCare"] = 0xB0BCE5D6 -Controls["InteractLockonCallAnimal"] = 0x71F89BBC -Controls["InteractLockonTrackAnimal"] = 0xE2473BF0 -Controls["InteractLockonTargetInfo"] = 0x31219490 -Controls["InteractLockonStudyBinoculars"] = 0xB3F388BC -Controls["InteractWildAnimal"] = 0x89F3D2E0 -Controls["InteractHorseFeed"] = 0x0D55A0F0 -Controls["InteractHorseBrush"] = 0x63A38F2C -Controls["EmoteAction"] = 0x13C42BB2 -Controls["EmoteTaunt"] = 0x470DC190 -Controls["EmoteGreet"] = 0x72BAD5AA -Controls["EmoteComm"] = 0x661857B3 -Controls["EmoteDance"] = 0xF311100C -Controls["EmoteTwirlGunHold"] = 0x04FB8191 -Controls["EmoteTwirlGunVarA"] = 0x6990BDDF -Controls["EmoteTwirlGunVarB"] = 0x52D29063 -Controls["EmoteTwirlGunVarC"] = 0xBC2AE312 -Controls["EmoteTwirlGunVarD"] = 0xAE69478F -Controls["QuickEquipItem"] = 0x6070D032 -Controls["MinigameBuildingCameraNext"] = 0x16B0EEF8 -Controls["MinigameBuildingCameraPrev"] = 0x5F97B231 -Controls["MinigameBuildingHammer"] = 0xFA91AECD -Controls["CursorAcceptDoubleClick"] = 0x1C559F2E -Controls["CursorAcceptHold"] = 0xE474F150 -Controls["CursorAccept"] = 0x9D2AEA88 -Controls["CursorCancel"] = 0x27568539 -Controls["CursorCancelDoubleClick"] = 0x9CB4ECCE -Controls["CursorCancelHold"] = 0xD7F70F36 -Controls["CursorX"] = 0xD6C4ECDC -Controls["CursorY"] = 0xE4130778 -Controls["CursorScrollUp"] = 0x62800C92 -Controls["CursorScrollDown"] = 0x8BDE7443 -Controls["CursorScrollClick"] = 0x6AA8A71B -Controls["CursorScrollDoubleClick"] = 0xE1B6ED6D -Controls["CursorScrollHold"] = 0x5484DBDD -Controls["CursorForwardClick"] = 0x11DBBAB9 -Controls["CursorForwardDoubleClick"] = 0x9805D715 -Controls["CursorForwardHold"] = 0x7630C9A1 -Controls["CursorBackwardClick"] = 0x9AF38793 -Controls["CursorBackwardDoubleClick"] = 0xA14BA1FC -Controls["CursorBackwardHold"] = 0x01AA9FA1 -Controls["EnterCheatCode"] = 0x7BF65AC8 -Controls["InteractionMenu"] = 0xCC510E59 -Controls["MpTextChatAll"] = 0x9720FCEE -Controls["MpTextChatTeam"] = 0x9098AD9D -Controls["MpTextChatFriends"] = 0x7098AC73 -Controls["MpTextChatCrew"] = 0x8142FA92 -Controls["PushToTalk"] = 0x4BC9DABB -Controls["CreatorLs"] = 0x339F3730 -Controls["CreatorRs"] = 0xD8CF0C95 -Controls["CreatorLt"] = 0x446258B6 -Controls["CreatorRt"] = 0x3C3DD371 -Controls["CreatorMenuToggle"] = 0x85D24405 -Controls["CreatorAccept"] = 0x2CD5343E -Controls["CreatorMenuUp"] = 0xBCD1444B -Controls["CreatorMenuDown"] = 0x97410755 -Controls["CreatorMenuLeft"] = 0xEC6A30AA -Controls["CreatorMenuRight"] = 0x19D8334C -Controls["CreatorMenuAccept"] = 0xFB9C3231 -Controls["CreatorMenuCancel"] = 0xBB3FC460 -Controls["CreatorMenuFunction"] = 0x5A03B3F3 -Controls["CreatorMenuExtraFunction"] = 0xE6B8F103 -Controls["CreatorMenuSelect"] = 0x0984E40A -Controls["CreatorPlace"] = 0xD74CACAD -Controls["CreatorDelete"] = 0x3F4DC0EF -Controls["CreatorDrop"] = 0x414034D5 -Controls["CreatorFunction"] = 0xB05FDA25 -Controls["CreatorRotateRight"] = 0x9D75674E -Controls["CreatorRotateLeft"] = 0xD41E9C2A -Controls["CreatorGrab"] = 0x338A0D45 -Controls["CreatorSwitchCam"] = 0x16CCFEC6 -Controls["CreatorZoomIn"] = 0x335D8D76 -Controls["CreatorZoomOut"] = 0x24A42F93 -Controls["CreatorRaise"] = 0x0D0FB9B1 -Controls["CreatorLower"] = 0x1BDE2EB3 -Controls["CreatorSearch"] = 0xF55864CD -Controls["CreatorMoveUd"] = 0x82428676 -Controls["CreatorMoveLr"] = 0x59753EDC -Controls["CreatorLookUd"] = 0x55EA24F3 -Controls["CreatorLookLr"] = 0xAEB2A9C7 -Controls["CutFree"] = 0xD2CC4644 -Controls["Drop"] = 0xD2928083 -Controls["PickupCarriable"] = 0xEB2AC491 -Controls["PickupCarriable2"] = 0xBE8593AF -Controls["PlaceCarriableOntoParent"] = 0x7D326951 -Controls["PickupCarriableFromParent"] = 0xA1202C7B -Controls["MercyKill"] = 0x956C2A0E -Controls["Revive"] = 0x43F2959C -Controls["Hogtie"] = 0xD9C50532 -Controls["CarriableSuicide"] = 0x6E9734E8 -Controls["CarriableBreakFree"] = 0x295175BF -Controls["InteractHitCarriable"] = 0x0522B243 -Controls["Loot"] = 0x41AC83D1 -Controls["Loot2"] = 0x399C6619 -Controls["Loot3"] = 0x27D1C284 -Controls["LootVehicle"] = 0x14DB6C5E -Controls["LootAmmo"] = 0xC23D7B9E -Controls["BreakVehicleLock"] = 0x97C71B28 -Controls["LootAliveComponent"] = 0xFF8109D8 -Controls["FeedInteract"] = 0xA8E3F467 -Controls["SaddleTransfer"] = 0x73A8FD83 -Controls["ShopBuy"] = 0xDFF812F9 -Controls["ShopSell"] = 0x6D1319BE -Controls["ShopSpecial"] = 0xEA150E72 -Controls["ShopBounty"] = 0xD3ECF82F -Controls["ShopInspect"] = 0x5E723D8C -Controls["ShopChangeCurrency"] = 0x90FA19AB -Controls["QuickUseItem"] = 0xC1989F95 -Controls["PromptPageNext"] = 0x8CF90A9D -Controls["FrontendTouchZoomFactor"] = 0xE7F89C38 -Controls["FrontendTouchZoomX"] = 0x16661AD0 -Controls["FrontendTouchZoomY"] = 0x253DB87F -Controls["FrontendTouchDragX"] = 0xEC93548E -Controls["FrontendTouchDragY"] = 0x9AC130EB -Controls["FrontendTouchTapX"] = 0xC10E180A -Controls["FrontendTouchTapY"] = 0xCF4B3484 -Controls["FrontendTouchDoubleTapX"] = 0x1661FAB0 -Controls["FrontendTouchDoubleTapY"] = 0x96E87BBF -Controls["FrontendTouchHoldX"] = 0x0FF17F1D -Controls["FrontendTouchHoldY"] = 0x398ED257 -Controls["FrontendTouchSwipeUpX"] = 0x0B71D439 -Controls["FrontendTouchSwipeUpY"] = 0x19CA70EA -Controls["FrontendTouchSwipeDownX"] = 0xE3B30955 -Controls["FrontendTouchSwipeDownY"] = 0xBDFF3DEA -Controls["FrontendTouchSwipeLeftX"] = 0x2545B0DE -Controls["FrontendTouchSwipeLeftY"] = 0xD43D0ECE -Controls["FrontendTouchSwipeRightX"] = 0xEAB68397 -Controls["FrontendTouchSwipeRightY"] = 0x675B7CE3 -Controls["MultiplayerInfoPlayers"] = 0x9C68CE34 -Controls["MultiplayerDeadSwitchRespawn"] = 0xB4F298BA -Controls["MultiplayerDeadInformLaw"] = 0x6816A38E -Controls["MultiplayerDeadRespawn"] = 0x18987353 -Controls["MultiplayerDeadDuel"] = 0xF875FC78 -Controls["MultiplayerDeadParley"] = 0x4D11FE01 -Controls["MultiplayerDeadFeud"] = 0xB4A11066 -Controls["MultiplayerDeadLeaderFeud"] = 0xCC18F960 -Controls["MultiplayerDeadPressCharges"] = 0xE50DCA13 -Controls["MultiplayerRaceRespawn"] = 0x014CA044 -Controls["MultiplayerPredatorAbility"] = 0xC5CF41B2 -Controls["MultiplayerSpectatePlayerNext"] = 0xBA065692 -Controls["MultiplayerSpectatePlayerPrev"] = 0x5092BF47 -Controls["MultiplayerSpectateHideHud"] = 0x7DBA5D49 -Controls["MultiplayerSpectatePlayerOptions"] = 0x4E074EE6 -Controls["MultiplayerLeaderboardScrollUd"] = 0xA917D24B -Controls["MinigameQuit"] = 0xE9094BA0 -Controls["MinigameIncreaseBet"] = 0xC7CB8D5F -Controls["MinigameDecreaseBet"] = 0xD3EBF425 -Controls["MinigameChangeBetAxisY"] = 0xBDC733EE -Controls["MinigamePlaceBet"] = 0x410B0B2E -Controls["MinigameClearBet"] = 0x4A21C66B -Controls["MinigameHelp"] = 0x9384E0A8 -Controls["MinigameHelpPrev"] = 0xC5F53156 -Controls["MinigameHelpNext"] = 0x83608AC0 -Controls["MinigameReplay"] = 0x985243B7 -Controls["MinigameNewGame"] = 0x5D1788FF -Controls["MinigamePokerSkip"] = 0x646A7792 -Controls["MinigamePokerCall"] = 0xDAB9EE72 -Controls["MinigamePokerFold"] = 0x49B4AD1E -Controls["MinigamePokerCheck"] = 0x206B2087 -Controls["MinigamePokerCheckFold"] = 0x72A9D1F7 -Controls["MinigamePokerBet"] = 0xA9883369 -Controls["MinigamePokerHoleCards"] = 0xC2B1193A -Controls["MinigamePokerBoardCards"] = 0x03753498 -Controls["MinigamePokerSkipTutorial"] = 0xB568BCD0 -Controls["MinigamePokerShowPossibleHands"] = 0x7765B9D4 -Controls["MinigamePokerYourCards"] = 0xF923B337 -Controls["MinigamePokerCommunityCards"] = 0xE402B898 -Controls["MinigamePokerCheatLr"] = 0x2330F517 -Controls["MinigameFishingResetCast"] = 0xB40A9BDB -Controls["MinigameFishingReleaseFish"] = 0xF14FD435 -Controls["MinigameFishingKeepFish"] = 0x52C5C34A -Controls["MinigameFishingHook"] = 0xA1CD103A -Controls["MinigameFishingLeftAxisX"] = 0x69B10623 -Controls["MinigameFishingLeftAxisY"] = 0x09BF4645 -Controls["MinigameFishingRightAxisX"] = 0x4FD4E558 -Controls["MinigameFishingRightAxisY"] = 0x95F2F193 -Controls["MinigameFishingLeanLeft"] = 0x0D4C3ABA -Controls["MinigameFishingLeanRight"] = 0x05074A9B -Controls["MinigameFishingQuickEquip"] = 0x25F525CD -Controls["MinigameFishingReelSpeedUp"] = 0x2FA915F5 -Controls["MinigameFishingReelSpeedDown"] = 0xD7AF56A0 -Controls["MinigameFishingReelSpeedAxis"] = 0x49C73CB2 -Controls["MinigameFishingManualReelIn"] = 0xA303F462 -Controls["MinigameFishingManualReelOutModifier"] = 0x4556642C -Controls["MinigameCrackpotBoatShowControls"] = 0x524C3787 -Controls["MinigameDominoesViewDominoes"] = 0x88F8B6B1 -Controls["MinigameDominoesViewMoves"] = 0x7733CF2C -Controls["MinigameDominoesPlayTile"] = 0x95F5BB7C -Controls["MinigameDominoesSkipDeal"] = 0xC5E622D7 -Controls["MinigameDominoesMoveLeftOnly"] = 0xFDDD89D4 -Controls["MinigameDominoesMoveRightOnly"] = 0x7D5187C9 -Controls["MinigameDominoesMoveUpOnly"] = 0xC6AB8CB3 -Controls["MinigameDominoesMoveDownOnly"] = 0xFD9FC86D -Controls["MinigameBlackjackHandView"] = 0x03F1E7CB -Controls["MinigameBlackjackTableView"] = 0xADE09435 -Controls["MinigameBlackjackBetAxisY"] = 0x3D2EA092 -Controls["MinigameBlackjackBet"] = 0x661D8A31 -Controls["MinigameBlackjackDecline"] = 0xCD7DDF9B -Controls["MinigameBlackjackStand"] = 0x31260507 -Controls["MinigameBlackjackHit"] = 0xA8142713 -Controls["MinigameBlackjackDouble"] = 0x74486CA4 -Controls["MinigameBlackjackSplit"] = 0x432B111F -Controls["MinigameFffA"] = 0x0E717DC6 -Controls["MinigameFffB"] = 0x1BC81873 -Controls["MinigameFffX"] = 0x65F0ACDF -Controls["MinigameFffY"] = 0x73AD4858 -Controls["MinigameFffZoom"] = 0x61E4CACC -Controls["MinigameFffSkipTurn"] = 0x3073681B -Controls["MinigameFffCycleSequenceLeft"] = 0x29A3550E -Controls["MinigameFffCycleSequenceRight"] = 0x7B5B896D -Controls["MinigameFffFlourishContinue"] = 0x6FC9DE68 -Controls["MinigameFffFlourishEnd"] = 0xF7750B25 -Controls["MinigameFffPractice"] = 0xCA379F82 -Controls["MinigameMilkingLeftAction"] = 0xFF4B2ADA -Controls["MinigameMilkingRightAction"] = 0x30BE7CF2 -Controls["MinigameLeftTrigger"] = 0x7EC33553 -Controls["MinigameRightTrigger"] = 0xBE78B715 -Controls["MinigameActionLeft"] = 0x0A1EFC09 -Controls["MinigameActionRight"] = 0x16D70379 -Controls["MinigameActionUp"] = 0xF5A13A0D -Controls["MinigameActionDown"] = 0xF601BCFC -Controls["StickyFeedAccept"] = 0xF4DD4C67 -Controls["StickyFeedCancel"] = 0x0CFB963F -Controls["StickyFeedX"] = 0xBD1D94A1 -Controls["StickyFeedY"] = 0xC85BAB1D -Controls["CameraPutAway"] = 0x5FC770EA -Controls["CameraBack"] = 0xA4BD74A5 -Controls["CameraTakePhoto"] = 0x44FA14C2 -Controls["CameraContextGallery"] = 0xE8337356 -Controls["CameraHandheldUse"] = 0x776F65E9 -Controls["CameraDof"] = 0x3003F9DC -Controls["CameraSelfie"] = 0xAC5922EA -Controls["CameraZoom"] = 0x47EC4C22 -Controls["CameraPoseNext"] = 0xF810FB35 -Controls["CameraPosePrev"] = 0x8D5BE9D1 -Controls["CameraExpressionNext"] = 0xCFA703D3 -Controls["CameraExpressionPrev"] = 0x07B6435D -Controls["TithingIncreaseAmount"] = 0x24F37AB5 -Controls["TithingDecreaseAmount"] = 0xCEFF5C13 -Controls["BreakDoorLock"] = 0x77110B0A -Controls["InterrogateQuestion"] = 0xA1AA2D8D -Controls["InterrogateBeat"] = 0x6E1E0D62 -Controls["InterrogateKill"] = 0x81B2E311 -Controls["InterrogateRelease"] = 0x3C22EF0E -Controls["CampBedInspect"] = 0xC67E13BB -Controls["PcFreeLook"] = 0x8AAA0AD4 -Controls["MinigameBartenderRaiseGlass"] = 0xA13460F5 -Controls["MinigameBartenderRaiseBottle"] = 0xF0A25112 -Controls["MinigameBartenderPour"] = 0xCABC2460 -Controls["MinigameBartenderServe"] = 0xDC03B043 -Controls["PhotoMode"] = 0x3C0A40F2 -Controls["PhotoModePc"] = 0x35957F6C -Controls["PhotoModeChangeCamera"] = 0x9F06B29C -Controls["PhotoModeMoveLr"] = 0x4F136512 -Controls["PhotoModeMoveLeftOnly"] = 0x311353EB -Controls["PhotoModeMoveRightOnly"] = 0x5357A7F5 -Controls["PhotoModeMoveUd"] = 0xEC001315 -Controls["PhotoModeMoveUpOnly"] = 0x315D57E6 -Controls["PhotoModeMoveDownOnly"] = 0x4EBCC409 -Controls["PhotoModeReset"] = 0xA209BD57 -Controls["PhotoModeLenseNext"] = 0xB138D899 -Controls["PhotoModeLensePrev"] = 0x06A057F8 -Controls["PhotoModeRotateLeft"] = 0x2EEA1D2A -Controls["PhotoModeRotateRight"] = 0x96E70854 -Controls["PhotoModeToggleHud"] = 0x7F9055F5 -Controls["PhotoModeViewPhotos"] = 0xDCE96D67 -Controls["PhotoModeTakePhoto"] = 0xA190AAC7 -Controls["PhotoModeBack"] = 0x2F13EC9A -Controls["PhotoModeSwitchMode"] = 0x8F32E2EB -Controls["PhotoModeFilterIntensity"] = 0xFE6DD360 -Controls["PhotoModeFilterIntensityUp"] = 0x2286D46B -Controls["PhotoModeFilterIntensityDown"] = 0xB341F407 -Controls["PhotoModeFocalLength"] = 0x886ABA4E -Controls["PhotoModeFocalLengthUpOnly"] = 0xFAFBD66A -Controls["PhotoModeFocalLengthDownOnly"] = 0x01EBFABD -Controls["PhotoModeFilterNext"] = 0x699F8D08 -Controls["PhotoModeFilterPrev"] = 0x4F640885 -Controls["PhotoModeZoomIn"] = 0x5B843BC9 -Controls["PhotoModeZoomOut"] = 0x2354D2E6 -Controls["PhotoModeDof"] = 0x26B9AE6A -Controls["PhotoModeDofUpOnly"] = 0x87B07940 -Controls["PhotoModeDofDownOnly"] = 0x047099F1 -Controls["PhotoModeExposureUp"] = 0xC64E2284 -Controls["PhotoModeExposureDown"] = 0xAD07A5A5 -Controls["PhotoModeExposureLock"] = 0x9DE08D71 -Controls["PhotoModeContrast"] = 0x483F707F -Controls["PhotoModeContrastUpOnly"] = 0x5D2DD717 -Controls["PhotoModeContrastDownOnly"] = 0x30811620 -Controls["CraftingEat"] = 0xB99A9CAD -Controls["CampSetupTent"] = 0x0B1BE2E8 -Controls["MinigameActionX"] = 0x1D927DF2 -Controls["DeprecatedAbove"] = 0xC1D24F92 -Controls["ScriptLeftAxisX"] = 0x1F8EEF84 -Controls["ScriptLeftAxisY"] = 0x5418D8AB -Controls["ScriptRightAxisX"] = 0xA6B769E9 -Controls["ScriptRightAxisY"] = 0x27A5EBC0 -Controls["ScriptRup"] = 0x771D6E13 -Controls["ScriptRdown"] = 0x37933367 -Controls["ScriptRleft"] = 0xA4DB0458 -Controls["ScriptRright"] = 0x22A3B800 -Controls["ScriptLb"] = 0xE624C062 -Controls["ScriptRb"] = 0x91E9231C -Controls["ScriptLt"] = 0x2B314A1E -Controls["ScriptRt"] = 0x26E9CD17 -Controls["ScriptLs"] = 0xAADDC975 -Controls["ScriptRs"] = 0xD04E9FE2 -Controls["ScriptPadUp"] = 0x0DC15ADD -Controls["ScriptPadDown"] = 0xB1DA5574 -Controls["ScriptPadLeft"] = 0x1AF81D9E -Controls["ScriptPadRight"] = 0x82A9B758 -Controls["ScriptSelect"] = 0xC8722109 -Controls["ScriptedFlyUd"] = 0xAEB4B1DE -Controls["ScriptedFlyLr"] = 0xF1111E4A -Controls["ScriptedFlyZup"] = 0x639B9FC9 -Controls["ScriptedFlyZdown"] = 0x9C5E030C -Controls["Count"] = 0x8EDFFB30 \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/NativeUI.lua b/server-data/resources/[esx]/EasyAdmin/dependencies/NativeUI.lua deleted file mode 100644 index e5f203af9..000000000 --- a/server-data/resources/[esx]/EasyAdmin/dependencies/NativeUI.lua +++ /dev/null @@ -1,3938 +0,0 @@ -Citizen.CreateThread(function() - - if CompendiumHorseObserved then - return - end - - -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, - [23] = function() return "badge_dev" end, - [24] = function() return "badge_contrib" end, -} - -BadgeDictionary = { - [0] = function(Selected) - if Selected then - return "commonmenu" - else - return "commonmenu" - end - end, - [23] = function(Selected) - return "easyadmin" - end, - [24] = function(Selected) - return "easyadmin" - 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, - [23] = function(Selected) if Selected then return 0,0,0, 255 else return 255,255,255,255 end end, - [24] = 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) - local X, Y = FormatXWYH(X, Y) - local 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) - local 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) - local Text = tostring(Text) - local 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 - local 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 - local thisTitle - 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:RefreshIndexRecursively() - self:RefreshIndex() - - for _, Item in pairs(self.Children) do - if Item.RefreshIndexRecursively then - Item:RefreshIndexRecursively() - end - end -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 - self._Visible = tobool(bool) - self.JustOpened = tobool(bool) - self.Dirty = tobool(bool) - self:UpdateScaleform() - if tobool(bool) == true then - ttsSpeechItem(self.Items[self:CurrentSelection()]) - end - 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 - - local isInputZeroDisabled = IsInputDisabled(0) - - if self.Controls.Back.Enabled and (IsDisabledControlJustReleased(2, 177) or IsDisabledControlJustReleased(2, 199) ) and isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled 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 isInputZeroDisabled do - self:GoRight() - Citizen.Wait(200) - end - self.RightPressed = false - end - end) - end - end - - if self.Controls.Select.Enabled and (IsDisabledControlJustPressed(1, 201) and isInputZeroDisabled) 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) - - ttsSpeechItem(self.Items[self:CurrentSelection()]) - - 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) - - ttsSpeechItem(self.Items[self:CurrentSelection()]) - - 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) - ttsSpeechText(Item.Items[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) - ttsSpeechText(Item.Items[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 - ttsSpeechText(Item.Checked and "Checked" or "Unchecked") - 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) - ttsSpeechText("Selected") - 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) - ttsSpeechText("Selected") - 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) - ttsSpeechText("Selected") - 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()) - ttsSpeechItem(Item) - 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 - local 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 - local 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 - 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() - if false then - self:ProcessMouse() - end - self:Draw() -end - -function MenuPool:ProcessControl() - for _, Menu in pairs(self.Menus) do - if Menu:Visible() then - Menu:ProcessControl() - break - end - end -end - -function MenuPool:ProcessMouse() - for _, Menu in pairs(self.Menus) do - if Menu:Visible() then - Menu:ProcessMouse() - break - end - end -end - -function MenuPool:Draw() - for _, Menu in pairs(self.Menus) do - if Menu:Visible() then - Menu:Draw() - break - 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) - break - 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 - -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-eoa.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-eoa.png deleted file mode 100644 index 6046aa80e25f01fcc1434ab9927bbfc1bdcb96cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67202 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

ow?iBaGYM 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 diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-gradient.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-gradient.png deleted file mode 100644 index 3ef7346f1cfa9f5a17c76de1a121411efdae0ef9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8920 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 XJ9VKddKvu221G^S&TX8WMd<$nZlmUd diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-logo.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/banner-logo.png deleted file mode 100644 index 616920e8a40af5767d62c9da9332800471e385a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8269 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 OcNwIjtz4~W9sVECoYf`( diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/logo-eoa.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/logo-eoa.png deleted file mode 100644 index e4bbc1affc71b29d72fe8275c5b9c63cf90fbc82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6837 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 diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/pl_badge_contr.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/pl_badge_contr.png deleted file mode 100644 index f7b9378b0407d24c02780d749dd2cbc501565adc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1942 zcmV;H2Wj|;P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T5?f08^?(^Pr&wX#sd-@PMfA}$T=FT~1&Ua?M`DV_oD^#dZp+bcU z6)IGyP@zJF3Kc5!KT~~@wn!T`Z1DVcuvI8%8*PAf_4W0ptyXRW@X#jPLU%X;_JLSQwxi%!4d_0-eBmt>F;xTI(ULSoDBBVT#7Y-!G;aVlD)$Qv_=~E zc<3CCD)lF5faUNu*xl38Xa;88X-g=GRaBSZqPvj12Sv&bac^M^tcA4n=!24~ znd0}JFw@EznhjA=VQj;-c&*-Ih?>jNG|psiF!asx*uR6bPE+~;bdc-9QKg&-4sXad ztylxt4YO{f`oOL{RDCSe_r5)0PslRt2jFzqlpK1~z*!+J1GkHldo`(8AK+4T3LF~> zD&_v%SkNiOE{A>VXFz{&9Z)W>F^KUyuHmq@XqRt#GC2RI<E!4Uovqh+E+~a9t3+?v9Yx`Ud>k6AEHPU8mp)@J}eH zlp8G0QZ*Mx@pnpuH9co{aC5|k#{rPFQK@R1zFPLG*I{zf+oDCATm;<-dxe6HGDM#U z6B0ueDpaUaRX%|6bUMjdU?}=9W9LuGj0A50$JlhrTmYRyegJt_WuEsqiPMo|Exmc_ zhNmk(Wu*5x=e^1~XObU6{zCFbey#y*(|pPvqwHMr1C!rf+~|BihxDrSMfe^Df-Bdl zoHLnot|Wg1C})fDlsn5bs$v%41Q#eyDZ7Hp)Hgu6Fu57r7kGhs8psfKF!>Rr%IHl4 z>G(8)`}FShmz>W~a8C9^rqP?<^mLvj!?=-g8rY_X`2B8D>z003<~5+#><5PC=DnwF z)bP8(B+j=z?q>VhOyeC1PT{r1Ndv%DmU$z;h0q05kEDDe+zb6JL}4lvSLBz% z?MdE^OclH|=^Svg^Lz`FvY+6ABtHr?(q&Nky`y;)w$4eXfS>EkALb9pp6apJ9|X5R z2&zb3(yNLvI~@o3Pmd7f&{aMIS8Js=Sc=Y!*2>q~TXE4*q*}WW9_hmq*vm9lF{U56;WeOjSC)N^I%o7T{cZR8p3MSe9ZIqcfv0#AW* z`nx14(>eN;Mg>nw8l0ecMRogwwL1N{aZ)N8YAJOBUy07*qoM6N<$g5p%NZ~y=R diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/pl_badge_dev.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/pl_badge_dev.png deleted file mode 100644 index 79fdbcf4b99402d6efd2e2122cae785e950c88f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1914 zcmV-=2Zi{FP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2M{+1pdAR;^mKYSpS$ zt5&UAwQAL>RqKDI-k5f!zP>)s9|9|dg27QA{MFmrYdT<6HUKwmvI>lV17JH?9R_5~ zM0xQ);9YnbzQhoJlXk62osKuZE}R2r!X_{bY-g|xkQw1ixB(_%ghiywUl?E*Tn~$3 zMbJDr5@HXRqqxWrI2RVdilpf_MUC1`pf|KDuDE7-BXViDp zSLcJq%iyC-;3%RB8UH=#~PEmTnKCp4fB`DPc3^>Qasv*}ZXKF^_s`wP! zJt}W}D(>I(dwdYnJi2VaN>h!12zySGI$TiE6VAsZB|4KE0 zZdi69wKuHKP4%WieXqL*YzAe9eGwe)`jfBTtKg_mloPiT*!JXB>IhlS*W4DcirOE*g@(VK6pBCxDBij-sBNz|9dC9@|6NMx|wV5WfyDRL zZMKK_{aeahhWkyx{`gd9Q9gKo7$fU z2ZG9f%eX0Crn9J=86eEaNT-0C6PW>=`YsXqT|u1@^UlKTNM-<+u1iF|odNvt;RxgL zXe%JU`(*}jS?+I-!xNAh;6%$ak8p%AsHM{Hg z*TLzpzv%&%XIio&!|`1f#H{)3DgGW)l^+Hd##H$Q$eksJKJ_b%i|ussfPDM;VK5s7 z?lTik`Y(vSI=3H?Xd|1X1r_tPYSpS$tEZ>uUp-bW9zBu%-T(jq07*qoM6N<$f;IP( A6#xJL diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/pride.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/pride.png deleted file mode 100644 index c7af2693650b3602904994d35c2365ae1bb6522a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16476 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 diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/small-logo-bg.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/small-logo-bg.png deleted file mode 100644 index 4321bfd515c30ac05d6b5b40538d7e1e9ab0ce58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23213 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 ZLK+y09Oq=_Tn2F!(7K_kR<2?Z_CI4ve;NP) diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/small-logo.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/small-logo.png deleted file mode 100644 index 5c2002a26543f88cfe324cd4efff031284bc9514..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19137 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 diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/images/ukraine.png b/server-data/resources/[esx]/EasyAdmin/dependencies/images/ukraine.png deleted file mode 100644 index f544d4ebedfc1b73cc7ee86f07160eff569d4add..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18389 zcmb??19v6Q*Y%BU+nm_8ZQJ(5wkDZOY)x$2nphJ%H@03=UN;nyhdQOLYKEQWBEjPeC+9LPBsnmMQ~cEc`0W0a;N(SaEdh z1ZdB%*nYYD`DpsHWku+?`aZX57N#E-F-?YDl`|NzLW~$~H*9Qpbnk#kBm|z)1%L|M zWI^WsK>-GM2^0|cM&1w83jp(8M1TeKD`xkz5Jr8WoQY)`fCYzx^?M}o$ioI>0z`Zh z#7Y4oQeeTk8Pw{40!V<#q`4Uopuz|+p$R%#1O(^4Wcq;t^i#;NzzUK8q!^ab5&#>1 zK=rIvtRz5(1%PQKKge~SQGpGoS09Zr-i)wTP9RSh~VDg88!W$5t z3BZ)P(-ye>R*QN}{|~9mMu84WUda%BSVmVEZEYrE+8J4#Z4bK`r}dz5 zzxId2YoNgU^W$#sK4l1-VTd&3%kJRRgF*@6>@T=*v(2MK>Gw8-uXmzF@?lxC4jp<- zpenj+f<#vQJzEKRjMy)_5vqkRi~Su25QNYUC!pR=2+S{uvE0xc;z^zcRmY=${}}+d z>vA6WOA8ATY!kLO>-BXl{2^UH1qim1{pAV(7)ekvX-qT;kHG=}5(Odj^`Zn11Go(R z(1Zg}n*#{%CS2iSl*5B!sABLI!Gvxmbk(6^w2{;GMD!-ie?^Em`c-WrlUQE%W;$%j=kPXN{qQ_B^jz?pvlFKJ?%CM@% zYLIHm5Zwv7f%8TgNOdOh{{gUtzDIUTv!;YJstW!^Y!UA$=f)40DngjE@LR~@7wPcD~lrtU<09mX|d zV~x_0VW3z>PsCtG^@|oC=B7lC5oe?6_z5oC@LOX6eL-r$NsZ!^p(2|@2A?8%eBHvn z5r#K~f%My-h=bgNp#$~ie{66LxZVhp2}fhtQT8Y@(XQS!!G5 zg&Js_iF#r+g{^9Js!`<_9N)8er^B#irOm6(Y8{FmD1<57bZ`o zPts2y9=s5dBC*28qOEE1vTt?|5Y>oOEd@fPFo#xz*CSHV>i zCg4-$QmIo3=$SPtDoZQ3D-ksBH77L^D~!|wYNa&U)qxdof77ZoD|1!O)d|$CEBMQ! zDy_?%HH3c$>VCvP>kcUlDU1JBEod+Xc8`)GnBz5hGmzOMRYiUe;T)!Jm|pYur0>f3 zAnJ+}tgI;H6=#=Y*KSg~Pq7k)0cN4HL#MaSf53}~dS6K9}ukJZ=Ix60tluoVUQ9&4E@ z?Mny>yCl5&oXV~;@_LPe^Aw!Rwtaez-v|SkKQulZ-^w1P5p|%O5GkQI5NB`&MmSxa z1&El(Hf~kL4TnU;OH5Nu)Az!YI_3q6m!y73p~Tyc8UQJQj-!sF#njN$WYqGCXNp%@ zYKk4Dbfx?X{0d{!Q`1G$%bA?nf~+fS$Jxi(E7?13CVJj_46Ws@?Uu5Q2YRKtSFPPn z5qiA3B|4gVhD}m6xiwVFT{T^0Xk{Hr$x2mu%hp5nx2@x?6>WI+hpW&VwXKe=0jAbw zIM!_=wL@3QJ;?!`0Y5$jVUVKMkgJIFhyvZ4-7xvI__niVa;}5}Mjp~mnXPqZQD;}# zfvp&mbi=l}>e-9Z);QK3?qT=QaWV3tlT7JCOxXmZTYq{EbA*R0gH zm3UP?n_lAxJqfwx1BHMB+}mv1y2ex7{cV5RCY6Vr^Nb^Q#DgflEWUid<^g5FRAAN+ zF(INM`rx1!Uw^W`1Yy1n%csV*OVRj(4pA)8#L$}1!+{zyn?DynYsj9DGiH)zY>^{W z0#BCbU`&rpvE!-kFubS|;C%*(2Cd1N=eg#iSH%tJiIwhT=oNi` zT_kmL`cRIrmoOZ5gws^~>{dWh*vjV1!_M*~bz~Fd>tPN1mGsk9tddMIxz+m2`pC+2 zvf47D@pHr}u8o;E6=vMN>1Q>RE+c%aSm}v|9_wpHJB%mi;*Ro=Jh%c-V` zji1~(*ZE(U=!58czgkNo<+T}!bdGwe?RXmnTzxj+fml%ABDJ8a1=?d<=^dApt1h+E zYXf_zeHHCPj0Pq`2U@`F&k;1?4`GqxpYdmPYX>eUFY_*uKyg1EY5%_gKaa=5I=yyMV2#xn{ zXo6`fE?F&Ef@DEW&%(7am8UOhAVnrQ@BX@9GpJG?K~4B@vVEt`&IA3ERuK|H%&H*TK$gA zXLjsRoUc1kJ@ytPzv7PhyZL7f?%F->8aECxwA;0_>iCQu&pgfqK9d)f$GUR5vT8dF z>^rV}ds_Ugo=*w#xM#K=dVFrNzm}Kscm=n5?tPt~N1h*du(tx;Xg+2pyVksDu4=lK zyNw@2E=AYGx1yGW^*=XPuQ!A2`n?M7=QpLFp(Da(Ka(C4QN*DVGZQBxks{CYc!YR) z_l4a)PT$MKXDw!_^Kd?kJ|}B&u$cp2md;mZ<}>CC)9=!g`yBmCKF<2j@#;242Smjhm#UPRZ$J1EKr{-i;)UpB7|kLq(}pk%z{~`q<~~)bCuYQ}cKkBzWz!^Gs{c z7)lBoO8Op9c!>Vb!iK^EL{T9{Q6&KXd8FVX|MMWx0RNp94fxL!g$({*BUp04|Bi{G zg8k?HA2j_x>j)d~Ip!%s=rj2{E`$>}^VVpSJjEs^CZ3xBYyb-wE(y9~wKIRX{I}vw zuEx|hvj)APUoBwcIYl~d^;&zh2`DFRX3JFwL z;WZWW;`cRF?E726Lo^@S`PzrXKiCi-Tl=fnj%aj$mAhSqBo?G>U3MAVbzpQ{O$_M< zii9Yjt2sp*KeBMi)^><-?Xe52OK=%doksHLCCf!VIO+|m8@a&D`c9JoR$Ck5X1_M< zZS0tT7KYz3rW(@C_yg;7z0QU>rJL{K{=@vHEyhh0>gUz2zT%{JYVtwIKosv(c!8md zIx77i=0o-v1{%944DozrZ3H`YHmI0pGQ^yiEvjJihBFgVxCgN3(5>b%SFn_k@a=DK zOMd?Pe9O2bnC;O^8)UNz{}*6z6J}Qd75q)gC@3tMBVLZR zDfLP5cOxp`?7}W3FITm|5gKijkVPj1p){Jt5`1-lu?Yfm1%6Pb81NKP&j&ZSzYDF3 zNpx+pxskF3tfcAi=cHF^TW%rC0lNG+50Q=-yeP?tNpQ-d+>9aoZsbI-((86r&HVKo z9O$s3Cn&lbNou|P|6hpbJ9;=)`B6Jl5>N-~_g!}@+u$`;(!ldVwWk4J^tLd^dh&&> zpaTITjE+2j@C{Lt#*YZx?ZT!z0f|AQ(TkdaG@4JZ+<{lm0JL3`uYZP1lt4PMQ;OUYx@6&xz3 zx|1uX$z$di;%I)>vk{l*?`8;c=c#-oOe@KkShp4DC4)!>by%O+UEy}ehhCO3y24IC zDwEV4T)mAXI7W2)-DDjzei7Pd<+Y9R9%4-DBngJlc=aalJbw^yKNu;Ddj53fm+1nN z+yV7_aS=D_n)h0zLP2F3^dIFaHvg_#kc0Z~t!~<*6!AmVXs^@T9k9r2G0HlUNI1rZ z>DYE1kaZKluU8>l@?tX9>L+QNz1T?lJkW&+2&HN1vrYV9QqG;av!uEIYw5h#2xF`i zbQsvF7Bz3Y?X9yd7{5~Y$?M~c7cN35R&PFr{ONx`9Uq@Czpy#XuEd*L#`Fr1Z?u2? z9I6}Clog-}8&RXRW`Yy@=+p7_&==W5OW*(C-J~|wtN1V61!!Qwp=UfAV=qpPF>zQw z|F}E3;;ejw*`ypZABfyt-(7drZtB@Fcx=?%*^ORiFE;Up8lo8T2kP(aUHM%P&Bqq6 zkc>b%d$T+1`tg5VhlrmrI#TYnzYm3ICqJj5L2D9ngY^dm?8zHE<$e|R^^sIz>YSXt zVLJN^5%69s9=k1z1{l2lf%33kF0TK*2{^yQ!#L9}(UXfe?!ja~5VQ7Y`!79$oA|z? zCt5aXj&@LUg@8!6LywfB2q`5|A<* zbo;GZHvqOuo8)d)2nHsq{if$@>ZN{G^4jN{SF5qo^(utL7S%jl)LD*#*IrFT-%(^N zHwW;$hHCqNX*(S17)~43rW+=*O++QKK+M@PezFBZ>6b+1k94X=!edGq2o3o&tra|T z+ge~z@;XzjC+hS2EdQ6|#pc>%WO1U|VeDbGo)3T^LFiX*Ku`A_5;Fy%;fy5OPr67??wQ}+>?886j!Z+a(a5&|K2bC z_EghKOnfg;%8YaoOm6L~YBjjqXhZAV zvhT5#-`3u7B*P`~14dLvTbjGC=aojl@1dmHX~IM-%#&lo%K@0An7FVu-w<_MhXt0K zOC->K>h$(j$zX3d)Y=>6k^@N$MwM&Ov&CdA$ibITB|^tybHUO+v5>t}QU^epEI|XSFSp?H=8dI_2n<|lj8*>; zvOs!}7UD^Y*iRC*PrA%}Q*~e$cTDNIA>E-iAn(0u%TRgTp;0AND6GoLqUUO1f!TMK zEJtPiq-GQ4Rw7e^`paR`BsyHmMr?sMNnoS46Qv7VM$ZI`}AU*GC zOyeeVNpUD$&~l_WUxf!&ZWinsazeyzKMCFd@*T&e(4goeipBs@ zWHRKvz!J-tLuf4AvOGbwlc;l9^j^+J7;+ss@|~?Z{b=aWFe~(QOQ$Q*Qa+uHjV*TB z&xp8&6GA+oTGxl;+qyP*!Tw~s!>QU`2Se(_R0k+kE+Vbe^Ujgjd<2Z}&=l;4a>HEg zBj5a~l}9I(+H_(Erw6$_b!H)F?r%W^%~XYV%aeZYfR7|~;%>Kl*ZiAJ5oXyRX4Q~L z@S*jyuYId8edsa}D5up1=;l4Ze<%agB|w=Q18p`>9Yg=DPJeQAgvEXDCvcK zFF=6E2C6nXy=62AARKKhENG;S3RZuED9}$u9c}P=XiA$X(n!blR&it_)nBNnf(KDk z*iv(uGcDfsaZ-3|FB-xbStfKy*9~<18ou<}9;r19!!>6aWvREv@!IskA?Z9Z&$&6E z=-3}XSupM(!3;MA^k?rpBvJ|__V#`XN(7(tT&&KE%lD=R`7U+{92hH)lSuCUa-?7A zGlxsu3_$HT0@vy)KyT-Z0hYRKA|@nD`rvtxV|;im>ZDR9s}TOxFmZPsG0pe6k@VVR zYkb4{=Jt_D#GP4VHu8-Wc5uHQydB^T$70a5IUIz6ICgG;>P#UpBXD~LNO?W{gZ;-- ziubE{V?bUMgfDYIwu6-Y1&A<`;f7c?%ztcb+njqI$VO4haqZ5IW*U#${+i|B=5&3< z(q7ouh=Sr<-+0+96B^Rre%Eut@63pb;&Sskb6e&TCXuFLp*GnKQBQl91;PykFY34lS#HzzN!I*8Uk=C<=b zG&fA-`8z=XKEssA4pzy7O~u2PGWYsENxrNOD4!EkV!p$SO|Io9)_pBG#%i9MIG^9uN!@E}G^KZ9No6yp*{gU+IfiD@irT7UP$V zPZr^2nz*f(E7r7lV=TZzeLhP~Sb$1_mhOlT=ZzJOMInC3UChgQJ+Hfd)NZWkOmQex zU@O0?RSvlS=#PpXJ^Iu-1>1)4_ah-ZCjy<2y(8~_jhox5^5VS!Q!Jw(p08<+pZ#}f z)YEdI-+hI9OWQ_fe+`u}^wE8O9nei)$Blp*76?Zm>I*bR3w)r2o%u7kH4&1|zgSy} zi#{DY8MCld>|q1^o=+%s1i5n@eixJrQRp0wqLQ7bmX3j-8O=bKEF39i2!o#DWJ;uhD zCNX6C)pa?&*>N7_L7hOkBP2HX&25AxZ{UV2-LlFH^^Hzr*YrEM2l^(ZR2S`GFwitb ziVkUAlph}hGCUO(2-2yN?WBl*lYe;-WbXJppWRqp(j~@QZ?DM?dVQ`d4bBHs_5F)Iw>_hRSuq-P+`uWY(7mk_lBLD2lw^}GmV0-W9Q?f%YM zNta#30IPptLNe)2uRBb;qqg^{t_`|`H^oXp2#gspOw3TmBv!<5kZZVry&eI>lC`R+YyK`cr7>PYoA@0{1-4v@JK%o_^QlYMTdXOmq}2u9aBjYhQRuX`!pH#W zg5eWtOcb#~$rJ7XI3%N*AN@G)-of=7$cTw9KE=_?f<@jGm59Cv#hdFqHtzV>o848g z1H$9Opx!S8ob7AFub81uWqBq=C(`p4Q^!9p-@oa{ayPoZrx#3C8_p~8bwwyXlgwzQ z_a^W=^JUe3|FFrWf~BX`m&!(UtfB=cBq*xa5o;#!VVW?9q91}%Ni&cq=FT3xC7eTE zmNJUgtW1aTV&MPW+qUG+Z|)TE9=djR^m|iJ^doNt&N4ItGG+)!-yLta@r3*+ZpJ@$ z3C~A%FRp>j`Ow?Q0&q47Kfc)QyXmz7ePF~;Qg*Fq#Mj987>v-nMa*>+)uBMq=Z2ho z{fTs90_gJB%H>ec@^-oeRpjRgOGzrko4NK070JRMqqK`=KlCbU?oFG0Dq7%YABAmG ze2h^k`|aRmNA&a$*0pOb7bBDI*7CbqdD}Tf<)g?`A6an;NsBvg;(UBtJzQs}XM~PJ=Kw0Yb8veH76!CZ@V> z5*E)X;LJnmiv6eky2-z2KFj*`eg(!%h9K#xfr2JDnQmWt#XmFjTF{4s7k6#1# z5lKzOpTSEm2H>^mEcx8X0_TROfzmiD)q>m9*_V2HT5s=9ep>=A@-&mkNWqj5w*nA7 zlWT7$zN4dB^V^!g-A9m9l=kgllzmiH$x7hnbo%k-ZLqX#=h6meOyv#*Dx4Azei0Tn zV1@wi!f6QrWUDrrt!Y+m5^;DyFE8% zcx&Q?*R{r=(&tvFi1n)sP4g21xBAuJ3>VmD_AUuvz;}!iu-b(CW&%DVJpx`3NdG?H zf3EV>_=mlK7B8w9qp`3UW%jlcCBN3QI!`Y$lV1>!#10oGdz;YBw7FWE* zjP63tbqi;C4kW|A#?>#o&qKEvFJ5{my3UrZGJ*(PveIA+OP<>8ur<&w?4e9h+NVXR z4f8Z0oVE0?4_DV}_F|^#D?-}A54D5ULepcRjyHo=h;9K})=(`>WE0AOPk6GO)xx_s zR05rlwt0Q!d_T{>m*{w$g=!QL4O|u@+TPgZBL^JKE`|hA#eUW?LK(yw$2}`R_V4fG zlrgAWkWSnS21QOYfd#^1MtwlSSdV!hiY5YBoUnrMb=>rpEBsN~HbsAmR@h|B=t1#` ztWi!7iGy_gfpgouGg$e7)cl~t-Jj~7p(Nl`gzz>{>*$t$B8e;i^=>FeBR?{(?xs%5 z9Ou?VOB10)dAlHcQC~R69-FN2^HiZg6N)+9g52$upajmfo?6m|qwp3^Jgx!S65K(h z(5}ixohp}h@i2n${z$9U*oQxNG9j}axeY9c9RJwp&xEM!ClrQhCQ!m@x)r*W=m%2Q zVn6GQ<(F3~JSa~+27K1;RhI$VSg+~AqR2FO+HL%38YU+6(}Il@4FE#I0sqn|mMQy1 zIzbbZ+4}RQ(&5RFp4Jnwtp`@KXSBd|5R#fTww5c5O^5l+fotcMIP|?36S)&QqC_wJ zFbZ3SQD0KEEyX3b9KZb!k%x9rS4~?65x;3(v%y*dbq(IS74W|st+s174TmvKKrebfS`n4BVfQB zk}g*iKbjDZE8HK6T}9OFId@7KW02Rc+{B=Iusr~LxmxNw7xcXeBiXu16TYqj-Bz?~ z3lpScNFo`p6##hfb&wr`z}pVBitu5lZ$U{fpah1&2EyI`RrO?2x5Fz)PmcBvGo*2g zV6(7uCaOwr-bWu6)!$aQHWjZIEw0z!$(jTk`_cR3X1mhgH1DwAwrk@1^f`N--DTY! z66y@8d#?rK{*K}o(g~$AsE}0-%`+eAL-rfIC~ab7(4l>u|D`YVBMFYJ_9E zrm|)!iA$;>q}`Ya$9vDZ?+t*y>UwV;Bm7GVO-Lec$_>P|87+^Zn8O;dufUJ9+Gk=H zgUQIPb8lv9W=EHvD&SXFurLG-SN-&w#G!h=O!W^zomtAt*0k)qS6mmA8~I%ViBm1V zp*VAKZ*bKse@3$UB-&??y@a^bW8SwN>e$H_T7>NT0E~L-PsX6OkjEWE>$MWjx-a|K z<4Gu^S&QW{e_ozm>sfGQOE)z_420mWn0=&PPL!mSXsFv{^C5pQ7w7l2rr@^We8#%9 zQe2XI`rZ2mvRpYohCm|`ji*lASneyOB99FzIhsD^D-YcwjxFDf?hsmclR*d&8nt^% zC-5>KqwV!5>yp0V`KYwLXNR`bKTu$A0nqo*-?R@QT7~ z1ZU1D&z-OAd7v{2&6rx-x7)U@d|&Ebe~8%w69o4CfLUb-Mgj^NfMLg%etj_Sr+puR z5*V#vlr;TjlPf}pa!Aj?RH&$0eT0W{2U!^9YzS`73LUTYGGq?f(^KB8>?9jn%`P>u zCMlLa$0cH_qE*-okubFC5^*P!1o)Azlx#bmH048?j0`txMh_OqOS-d=u+f@jXzsYV zwKJGG-Pf_ZE}z%Iym>4=g7RyPc2xL{H1I1?0UJFt24>a0+soOfdjWt#R;l)U#_M1n z;yfzlcutt;MLCZ;p?+LHt=Ry!uG_?5xG0W-Q(x>#oh<6B4aRJ5l0ASAD$T2Z&$|1VH;7&M~!>aOMr4pgc^csV6h254|(Q(F+lrN5(Eqj zrYmC{t}lAxF@!$ZIs)>#oBJX}v2p6^nQ4adIk1VF)56OG|YQcbDAXVp7}rWqt15@qYG zuS?6@Z~SvO!mpdrfoX>X?BlNgtUAN} z(zlNnzWx6G-TxD#nl#p?&otrK3;l+II&25uRQBC44};{s7P{4EBITK2e>Kr zq5ODk^q#MX5C2Ca(^Wt|o8mQSe37Bb@cD)BygT{N^w$Ef(ev?0kTr%PKf}Lyg|z2U zloiXx=fb~1Cvcawh)ykGYIYPstM5d}JCE2|?!51;lCGP_e~@4n*<6qZM*UpUE~1J1 zJl)a`lM1zz{N|b4Pv3@oZt}uT^n?_oC#ks@b|wlZxf@DBWXQUAv|EhD9M+`gA;+Q< zanwxf9I#L%2|Dmfc{Uu(@zh-K{^c6QF(Gfzg?0s;2-A2x1w`-km~kfWkjpb3d4$zeP;UN@{2#dcYCSh<=^!hSm&->SrT-RW9P5L*y4b(WPiIolG18kX5&*k zcwpz+tr3E-K9-X^t

  • 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 diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/nui/index.html b/server-data/resources/[esx]/EasyAdmin/dependencies/nui/index.html deleted file mode 100644 index 2b2cfedd6..000000000 --- a/server-data/resources/[esx]/EasyAdmin/dependencies/nui/index.html +++ /dev/null @@ -1,123 +0,0 @@ - - - - - Keyboard Input - - - - - - - -
    - - -
    - -
    -

    - - -
    - - -
    - -
    - \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/shadow.js b/server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/shadow.js deleted file mode 100644 index 1801933ce..000000000 --- a/server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/shadow.js +++ /dev/null @@ -1,74 +0,0 @@ -(function() { - var Filters = {} - - var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') - svg.setAttribute('style', 'display:block;width:0px;height:0px') - var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs') - - var blurFilter = document.createElementNS('http://www.w3.org/2000/svg', 'filter') - blurFilter.setAttribute('id', 'svgBlurFilter') - var feGaussianFilter = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur') - feGaussianFilter.setAttribute('stdDeviation', '0 0') - blurFilter.appendChild(feGaussianFilter) - defs.appendChild(blurFilter) - Filters._svgBlurFilter = feGaussianFilter - - // Drop Shadow Filter - var dropShadowFilter = document.createElementNS('http://www.w3.org/2000/svg', 'filter') - dropShadowFilter.setAttribute('id', 'svgDropShadowFilter') - var feGaussianFilter1 = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur') - feGaussianFilter1.setAttribute('in', 'SourceAlpha') - feGaussianFilter1.setAttribute('stdDeviation', '3') - dropShadowFilter.appendChild(feGaussianFilter1) - Filters._svgDropshadowFilterBlur = feGaussianFilter1 - - var feOffset = document.createElementNS('http://www.w3.org/2000/svg', 'feOffset') - feOffset.setAttribute('dx', '0') - feOffset.setAttribute('dy', '0') - feOffset.setAttribute('result', 'offsetblur') - dropShadowFilter.appendChild(feOffset) - Filters._svgDropshadowFilterOffset = feOffset - - var feFlood = document.createElementNS('http://www.w3.org/2000/svg', 'feFlood') - feFlood.setAttribute('flood-color', 'rgba(0,0,0,1)') - dropShadowFilter.appendChild(feFlood) - Filters._svgDropshadowFilterFlood = feFlood - - var feComposite = document.createElementNS('http://www.w3.org/2000/svg', 'feComposite') - feComposite.setAttribute('in2', 'offsetblur') - feComposite.setAttribute('operator', 'in') - dropShadowFilter.appendChild(feComposite) - var feComposite1 = document.createElementNS('http://www.w3.org/2000/svg', 'feComposite') - feComposite1.setAttribute('in2', 'SourceAlpha') - feComposite1.setAttribute('operator', 'out') - feComposite1.setAttribute('result', 'outer') - dropShadowFilter.appendChild(feComposite1) - - var feMerge = document.createElementNS('http://www.w3.org/2000/svg', 'feMerge') - var feMergeNode = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode') - feMerge.appendChild(feMergeNode) - var feMergeNode1 = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode') - feMerge.appendChild(feMergeNode1) - Filters._svgDropshadowMergeNode = feMergeNode1 - dropShadowFilter.appendChild(feMerge) - defs.appendChild(dropShadowFilter) - svg.appendChild(defs) - document.documentElement.appendChild(svg) - - const blurScale = 1 - const scale = (document.body.clientWidth / 1280) - - Filters._svgDropshadowFilterBlur.setAttribute('stdDeviation', - Number(blurScale) + ' ' + - Number(blurScale) - ) - Filters._svgDropshadowFilterOffset.setAttribute('dx', - String(Number(Math.cos(45 * Math.PI / 180)) * scale)) - Filters._svgDropshadowFilterOffset.setAttribute('dy', - String(Number(Math.sin(45 * Math.PI / 180)) * scale)) - Filters._svgDropshadowFilterFlood.setAttribute('flood-color', - 'rgba(0, 0, 0, 1)') - Filters._svgDropshadowMergeNode.setAttribute('in', - 'SourceGraphic') - -})() \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/style.css b/server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/style.css deleted file mode 100644 index 1396a00fa..000000000 --- a/server-data/resources/[esx]/EasyAdmin/dependencies/nui/input/style.css +++ /dev/null @@ -1,129 +0,0 @@ -/* ==================================================== -Recreating the email field from https://webflow.com/cms. Just an experiment - not as cross-browser friendly as the original. -Changed: -- animated gradient bar to :after element -- flexbox for layout -==================================================== */ -html { - box-sizing: border-box; - font-size: 10px; - } - - *, *:before, *:after { - box-sizing: inherit; - } - - body, ul, li { - margin: 0; - padding: 0; - } - - li { - list-style: none; - } - - p, h1, h2, h3, h4, h5, h6 { - margin-top: 0; - } - - a { - text-decoration: none; - } - - input { - border-style: none; - background: transparent; - outline: none; - } - - button { - padding: 0; - background: none; - border: none; - outline: none; - } - - body { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - background: transparent; - top: 50%; - left: 50%; - } - - .demo-flex-spacer { - flex-grow: 1; - } - - .container { - display: flex; - flex-direction: column; - height: 100vh; - max-width: 1600px; - padding: 0 15px; - margin: 0 auto; - } - - .webflow-style-input { - position: relative; - display: none; - flex-direction: row; - width: 100%; - max-width: 450px; - margin: 0 auto; - border-radius: 2px; - padding: 1.4rem 2rem 1.6rem; - background: rgba(57, 63, 84, 0.6); - filter: url(#svgDropShadowFilter); - } - - .webflow-style-button { - position: absolute; - right: 5px; - top: 50% - } - - .webflow-style-input:after { - content: ""; - position: absolute; - left: 0px; - right: 0px; - bottom: 0px; - z-index: 999; - height: 2px; - border-bottom-left-radius: 2px; - border-bottom-right-radius: 2px; - background: linear-gradient(to right, #3C4E63, #526B86); - } - - .webflow-style-input input { - flex-grow: 1; - color: #BFD2FF; - font-size: 1.8rem; - line-height: 2.4rem; - vertical-align: middle; - width:100%; - } - - .webflow-style-text { - color: #b5cafc; - font-size: 1.6rem; - margin-bottom: 0.3rem; - vertical-align: middle; - } - - .webflow-style-input input::-webkit-input-placeholder { - color: #8B95B7; - } - - .webflow-style-input button { - color: #8B95B7; - font-size: 2.4rem; - line-height: 2.4rem; - vertical-align: middle; - transition: color 0.25s; - } - .webflow-style-input button:hover { - color: #BFD2FF; - } \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/fxmanifest.lua b/server-data/resources/[esx]/EasyAdmin/fxmanifest.lua deleted file mode 100644 index 34093b4c4..000000000 --- a/server-data/resources/[esx]/EasyAdmin/fxmanifest.lua +++ /dev/null @@ -1,86 +0,0 @@ -fx_version "cerulean" - -games {"gta5"} - -author 'Blumlaut ' -description 'EasyAdmin - Admin Menu for FiveM' -version '7.02' - - -lua54 'yes' - -shared_script 'shared/util_shared.lua' - -server_scripts { - "server/*.lua", - "server/bot/*.js", - "plugins/**/*_shared.lua", - "plugins/**/*_server.lua" -} - -client_scripts { - "dependencies/NativeUI.lua", - "dependencies/Controls.lua", - "client/*.lua", - "plugins/**/*_shared.lua", - "plugins/**/*_client.lua" -} - -ui_page "dependencies/nui/index.html" - -files { - "dependencies/images/*.png", - "dependencies/nui/**/*" -} - -dependencies { - 'yarn' -} - -provide 'EasyAdmin' - - -convar_category 'EasyAdmin' { - "Configuration Options", - { - { "EasyAdmin language", "$ea_LanguageName", "CV_STRING", "en" }, - { "Default key to open the menu", "$ea_defaultKey", "CV_STRING", "none" }, - { "The Minimum Amount of Identifiers", "$ea_minIdentifierMatches", "CV_INT", "2" }, - { "Display banlist checking progress", "$ea_presentDeferral", "CV_BOOL", "true" }, - { "Moderation Actions Webhook", "$ea_moderationNotification", "CV_STRING", "false" }, - { "Report Notifications Webhook", "$ea_reportNotification", "CV_STRING", "false" }, - { "Detail Notifications Webhook", "$ea_detailNotification", "CV_STRING", "false" }, - { "Set a custom DateTime format", "$ea_dateFormat", "CV_STRING", "%d/%m/%Y %H:%M:%S" }, - { "Image Uploader", "$ea_screenshoturl", "CV_STRING", "none" }, - { "Screenshot Field Name", "$ea_screenshotfield", "CV_STRING", "files[]" }, - { "JSON String arguments", "$ea_screenshotOptions", "CV_STRING", "{}" }, - { "Screenshot on Report", "$ea_enableReportScreenshots", "CV_BOOL", "true" }, - { "Webhook Identifier", "$ea_logIdentifier", "CV_STRING", "steam" }, - { "Enable calladmin Command", "$ea_enableCallAdminCommand", "CV_BOOL", "true" }, - { "Enable report Command", "$ea_enableReportCommand", "CV_BOOL", "true" }, - { "calladmin Command Name", "$ea_callAdminCommandName", "CV_STRING", "calladmin" }, - { "report Command Name", "$ea_reportCommandName", "CV_STRING", "report" }, - { "calladmin Cooldown (seconds)", "$ea_callAdminCooldown", "CV_INT", "60" }, - { "Minimum Reports to Ban Someone", "$ea_defaultMinReports", "CV_INT", "3" }, - { "Report Ban Time (unix time)", "$ea_ReportBanTime", "CV_INT", "86400" }, - { "Allow Minimum Report Count", "$ea_MinReportModifierEnabled", "CV_BOOL", "true" }, - { "Minimum Amount of Players to enable Report Modifier", "$ea_MinReportPlayers", "CV_INT", "12" }, - { "Divisor of Player Count to get minimum reports needed count", "$ea_MinReportModifier", "CV_BOOL", "true" }, - { "Amount of Warns before Actions", "$ea_maxWarnings", "CV_INT", "3" }, - { "Maximum Warn Action", "$ea_warnAction", "CV_STRING", "kick" }, - { "Maximum Warn Ban Time (unix time)", "$ea_warningBanTime", "CV_INT", "604800" }, - { "Hide IP in the GUI", "$ea_IpPrivacy", "CV_BOOL", "true" }, - { "Banlist Backup Time (hours)", "$ea_backupFrequency", "CV_INT", "72" }, - { "Maximum Backup Count", "$ea_maxBackupCount", "CV_INT", "10" }, - { "Chat Reminder Time (minutes, disabled if 0)", "$ea_chatReminderTime", "CV_INT", "0" }, - { "Time before Cached Player Expires", "$ea_playerCacheExpiryTime", "CV_INT", "900" }, - { "Set Debug Level", "$ea_logLevel", "CV_INT", "1" }, - { "Enable Custom Banlist", "$ea_custombanlist", "CV_BOOL", "false" }, - { "Use Tokens as Identifiers", "$ea_useTokenIdentifiers", "CV_BOOL", "true" }, - { "Enable Ascii Art on Start", "$ea_enableSplash", "CV_BOOL", "true" }, - { "Token for Discord bot", "$ea_botToken", "CV_STRING", "none" }, - { "Channel for Discord bot to log", "$ea_botLogChannel", "CV_STRING", "none" }, - { "Channel for Discord bot to enable live status", "$ea_botStatusChannel", "CV_STRING", "true" }, - { "Enable Allowlist", "$ea_enableAllowlist", "CV_BOOL", "false" } - } -} diff --git a/server-data/resources/[esx]/EasyAdmin/language/de.json b/server-data/resources/[esx]/EasyAdmin/language/de.json deleted file mode 100644 index 55e78aa11..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/de.json +++ /dev/null @@ -1,239 +0,0 @@ -[{ - "translator": "EasyAdmin, zRxnx", - "language": "de", - "on": "An", - "off": "Aus", - - "spectatingUser": "Schaue ~b~%s ~w~zu.", - "stoppedSpectating": "Zuschauermodus verlassen.", - "hour": "Stunde", - "hours": "Stunden", - "day": "Tag", - "days": "Tage", - "week": "Woche", - "weeks": "Wochen", - "month": "Monat", - "months": "Monate", - "year": "Jahr", - "years": "Jahre", - "customtime": "Eigener Zeitraum", - "nocustombantime": "Du musst erst einen Zeitraum setzen!", - "permanent": "Permanent", - "confirm": "Bestätigen", - "playermanagement": "Spielermanagement", - "servermanagement": "Servermanagement", - "settings": "Einstellungen", - "kickplayer": "Spieler Kicken", - "banplayer": "Spieler Bannen", - "reason": "Grund", - "kickreasonguide": "Kick Grund angeben.", - "noreason": "Keinen Grund angegeben.", - "confirmkick": "Kick bestätigen", - "confirmkickguide": "~r~~h~ACHTUNG:~h~~w~ Durch das drücken von bestätigen wird der Spieler mit dem angegebenen Grund gekickt.", - "banreasonguide": "Bann Grund angeben.", - "banlength": "Bann Länge", - "banlengthguide": "Wie lange soll der Spieler gebannt werden?", - "confirmban": "Bann bestätigen", - "confirmbanguide": "~r~~h~ACHTUNG:~h~~w~ Durch das drücken von bestätigen wird der Spieler mit dem angegebenen Grund gebannt.", - "mute": "Spieler stummschalten", - "muteguide": "~r~~h~ACHTUNG:~h~~w~ Dadurch wird verhindert, dass der Spieler den Text-Chat verwenden kann.", - "adminmutedplayer": "**%s** hat **%s** gestummt", - "adminunmutedplayer": "**%s** hat **%s** entstummt", - "playermuted": "wurde stummgeschaltet!", - "playerunmuted": "wurde entstummt!", - "playermute": "Du bist stummgeschaltet!", - "spectateplayer": "Spieler zuschauen", - "teleportplayer": "Teleportieren", - "teleporttoplayer": "Zum Spieler", - "teleportplayertome": "Spieler zu mir", - "slapplayer": "Spieler schlagen", - "setplayerfrozen": "Spieler einfrieren", - "allplayers": "Alle Spieler", - "teleporttome": "Zu mir Teleportieren", - "teleporttomeguide": "~r~~h~ACHTUNG:~h~~w~ Das wird ~h~alle~h~ Spieler zu dir Teleportieren.", - "setgametype": "Spiel-Typ setzen", - "setgametypeguide": "~r~~h~ACHTUNG:~h~~w~ Das wird den Spiel-Typ wie er auf der Serverliste angezeigt wird setzen.", - "setmapname": "Karten Namen setzen", - "setmapnameguide": "~r~~h~ACHTUNG:~h~~w~ Das wird den Karten Namen wie er auf der Serverliste angezeigt wird setzen.", - "startresourcebyname": "Ressource mit Namen starten", - "startresourcebynameguide": "~r~~h~ACHTUNG:~h~~w~ Das wird eine Ressource die auf dem server installiert ist starten.", - "stopresourcebyname": "Ressource mit Namen stoppen", - "stopresourcebynameguide": "~r~~h~ACHTUNG:~h~~w~ Das wird eine Ressource die auf dem server installiert ist stoppen.", - "badidea": "Das würde ich lieber nicht tuhen.", - "viewbanlist": "Bannliste ansehen", - "unbanplayer": "Spieler entbannen", - "unbanplayerguide": "~r~~h~ACHTUNG:~h~~w~ Durch das drücken von bestätigen wird der Spieler entbannt.", - "banlistshowtype": "~h~Bannliste:~h~ Typ Anzeigen", - "unbanreasons": "Gründe", - "unbanlicenses": "Lizenzen", - "banlistshowtypeguide": "Zwischen Gründen und Lizenzen wechseln .\nErfordert erneutes öffnen des Adminpanels.", - "refreshbanlist": "Bannliste neu laden", - "refreshbanlistguide": "Das wird die Bannliste neu laden.\nErfordert erneutes öffnen des Adminpanels.", - "refreshpermissions": "Berechtigungen neu Laden", - "refreshpermissionsguide": "Das wird deine jetzigen Berechtigungen neu laden.\nErfordert erneutes öffnen des Adminpanels.", - "godmodedetected": "Godmode: ~r~Erkannt~w~", - "godmodenotdetected": "Godmode: ~g~Nicht Erkannt~w~", - "antiragdoll": "~r~Anti-Ragdoll~w~", - "health": "Leben", - "armor": "Rüstung", - "wantedlevel": "Fahndungslevel", - "exitspectator": "Drücke E um den Zuschauermodus zu verlassen.", - - "allowlist": "Du hast keine Berechtigung diesen Server zu betreten", - "checkingallowlist": "Prüfe Allowlist..", - "bannedjoin": "\nDu wurdest von diesem server gebannt, \nGrund: %s, \nBann Läuft am %s ab\nDeine Bann-ID lautet #%s.", - "kicked": "Gekickt von %s, \nGrund: %s", - "banned": "Du wurdest von diesem server gebannt, \nGrund: %s, \nBann Läuft am %s ab.", - "reasonadd": " ( Spielername: %s ), \nGebannt von: %s", - "nongiven": "Keinen gegeben", - "announcement": "Announcement", - "announcementguide": "Send an announcement to all players", - "adminannouncement": "**%s** sent an announcement: **%s**", - "playernotfound": "Spieler wurde nicht gefunden.", - "done": "Fertig!", - "adminkickedplayer": "**%s** hat **%s** gekickt, Grund: %s", - "adminbannedplayer": "**%s** hat **%s** gebannt, Grund: %s, Bann entfällt: %s, ID: %s", - "adminunbannedplayer": "**%s** hat **%s** entbannt. [ %s ]", - "adminslappedplayer": "**%s** hat **%s** geschlagen **(%s Leben)**.", - - "adminfrozeplayer": "**%s** hat **%s** eingefroren.", - "adminunfrozeplayer": "**%s** hat **%s** entfroren.", - - "left": "Links", - "middle": "Mitte", - "right": "Rechts", - "menuOrientation": "Menüorientation", - "menuOrientationguide": "Menü Orientation setzen ( Links, Rechts oder Mitte ) \nErfordert Spielneustart.", - "menuOffset": "Menüoffset", - "menuOffsetguide": "Menü länger machen.\nErfordert erneutes öffnen des Adminpanels.", - "resetmenuOffset": "Menüoffset Zurücksetzen", - "takescreenshot": "Screenshot erstellen", - "admintookscreenshot": "**%s** hat ein Screenshot von **%s**'s Bildschirm Erstellt: %s", - "screenshotinprogress": "Ein Screenshot wird bereits erstellt, bitte warten!", - "screenshotlink": "Der Screenshot ist hier verfügbar: %s", - "anonymous": "Anonymer Admin", - "anonymousguide": "Blendet dein Namen aus, wenn du die EasyAdmin-Funktionen verwendest.", - "adminofflinebannedplayer": "**%s** hat Offline-Spieler **%s** gebannt, Grund: %s, Bann Läuft am %s ab.", - "cachedplayers": "Frühere Spieler", - "refreshcachedplayers": "Frühere Spielerliste aktualisieren", - "refreshcachedplayersguide": "Aktualisiert die Liste der früheren Spieler. Erfordert erneutes öffnen des Adminpanels.", - "adminrequestedofflineinfo": "**%s** hat die Nutzerdaten des Spielers **%s** angefordert", - "nextpage": "Nächste Seite»", - "firstpage": "««Erste Seite", - "lastpage": "Letzte Seite»»", - "previouspage": "«Vorherige Seite", - "playercalledforadmin": "Spieler %s ruft nach einem Admin!\nGrund: %s\nID: %s\n", - "playerreportedplayer": "Spieler %s hat einen Spieler gemeldet!\n%s, Grund: %s\nReport %s/%s\nID: %s\n", - "successfullyreported": "Spieler erfolgreich gemeldet!", - "reportbantext": "Von %s Spielern in kurzer Zeit gemeldet.", - "alreadyreported": "Du hast diesen Spieler schon gemeldet!", - "reportedusageerror": "Fehler! Nutzung: /report Name Grund", - "admincalled": "Ein Admin wurde erfolgreich gerufen!", - "spectatedplayer": "**%s** hat **%s** zugesehen", - "teleportedtoplayer": "**%s** teleportiert nach **%s**", - "searchbans": "Bann suchen", - "searchbansfail": "Es wurde kein Bann gefunden.", - "identifier": "%s Identifier", - "warnplayer": "Spieler warnen", - "warnreasonguide": "Einen Grund zur Warnung hinzufügen.", - "confirmwarn": "Warnung bestätigen", - "confirmwarnguide": "~r~~h~ACHTUNG:~h~~w~ Durch das drücken von bestätigen wird der Spieler mit dem angegebenen Grund gewarnt.", - "warnedtitle": "Warnung", - "warnedby": "verwarnt durch", - "warndismiss": "Halte die Leertaste für 10 Sekunden gedrückt, um die Warnung zu schließen.", - "warned": "Du wurdest verwarnt, Grund: %s, Warnung %s/%s", - "warnkicked": "Du wurdest gekickt, weil du zu oft verwarnt wurdest.", - "warnbanned": "Du wurdest gebannt, weil du zu oft verwarnt wurdest.", - "adminwarnedplayer": "**%s** hat **%s** gewarnt, Grund: %s, Warnung %s/%s", - - "searchuser": "Spieler suchen", - "searchuserguide": "Spieler mit Spielernamen oder Server ID finden.", - - "setconvar": "Convar Setzen", - "setconvarguide": "~r~~h~ACHTUNG:~h~~w~ Nur mit wissen anfassen!\nÄndert eine Convar.", - "adminchangedconvar": "**%s** hat die Convar **%s** zu **%s** geändert.", - "adminstartedresource": "**%s** hat die Ressource **%s** gestartet", - "adminstoppedresource": "**%s** hat die Ressource **%s** gestoppt", - - "convarname": "Convar Name", - "convarvalue": "Convar Inhalt", - - "cleanarea": "Gebiet aufräumen", - "cleanareaguide": "Entfernt alle Gegenstände in der nähe.", - "cars": "Fahrzeuge", - "peds": "NPC's", - "props": "Objekte", - "cleaningcar": "Loesche Fahrzeug %s", - "cleaningped": "Loesche NPC %s", - "cleaningprop": "Loesche Objekt %s", - "finishedcleaning": "Löschen von %s beendet.", - "admincleanedup": "**%s** hat alle **%s** in einem **%s** radius gelöscht.", - "radius": "Radius", - "type": "Art", - "deepclean":"Tiefe Reinigung", - "deepcleanguide":"Löscht auch clientseitige Gegenstände.", - - "forceeasteregg": "Easter Egg aktivieren", - - "permissioneditor": "Permissions anpassen", - "permissioneditorguide": "Permissions auf dem Server editieren, speichert nach ~g~easyadmin_permissions.cfg~w~.", - - "aces": "ACEs", - "permission": "Permission", - "principals": "Principals", - "addace": "~g~ACE hinzufügen", - "addaceguide": "Du kannst das ganze vor dem abspeichern nochmal editieren.", - "addprincipal": "~g~Principal hinzufügen", - "group": "Gruppe", - "entergroup": "Gruppennamen einfügen (z.b. group.admin)", - "enterperm": "Permissionnamen einfügen (z.b. easyadmin.player.kick)", - "state": "Status", - "stateguide": "Das verweigern von Permissions funktioniert nicht richtig. Erlaube nur die Permissions, die der Spieler haben soll.", - "location": "Ort", - "locationguide": "Dieser Wert kann nicht bearbeitet werden.", - "principal": "Principal", - "enterprincipal": "Principal einfügen (e.g. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Permission löschen", - "deletepermissionguide": "~r~~h~ACHTUNG:~h~~w~ Dadurch wird die Permission vollständig widerrufen und gelöscht!", - "deleteprincipal": "~r~Principal löschen", - "deleteprincipalguide": "~r~~h~ACHTUNG:~h~~w~ Dadurch wird der Principal vollständig widerrufen und gelöscht!", - - "itemdeleted": "~r~Dieser Eintrag wurde gelöscht.", - "savechanges": "~g~Änderungen speichern", - "savechangesguide": "~r~~h~ACHTUNG:~h~~w~ Das kann nicht rückgängig gemacht werden!", - "admineditedpermissions": "**%s** hat die Berechtigungen des Servers bearbeitet.", - - "reportviewer": "Report Viewer", - "call": "Ruf", - "report": "Report", - "open": "Offen", - "reporter": "Melder", - "reported": "Gemelderter", - "closereport": "Report schließen", - "closesimilarreports": "~r~Ähnliche Reports schließen~w~", - "closesimilarreportsguide": "Versucht ähnliche Reports zu finden und zu löschen.", - "adminclosedreport": "**%s** hat Report #**%s** geschlossen.", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "refreshreports": "Reports aktualisieren", - "refreshreportsguide": "Zeigt neue Reports an, falls es neue gibt seitdem das Menü geöffnet wurde.", - "waitbeforeusingagain": "Du musst warten bevor du das benutzen kannst!", - "invalidreport": "Du musst einen Grund angeben!", - "claimreport": "Report übernehmen", - "claimedby": "Übernommen von", - "reportalreadyclaimed": "Dieser Report wurde bereits übernommen", - "notification": "Benachrichtigung", - "deferral": "Prüfe Bannliste, bitte warten... (%s%%)", - "savebanchanges": "Änderungen speichern", - "savebanguide": "~r~~h~ACHTUNG:~h~~w~ Durch das drücken von bestätigen wird der Bann überschrieben", - "adminimmune": "Dazu hast du keine Rechte, der ausgewählte Spieler ist Immun", - "teleportmeback": "Teleportiere mich zurück", - "teleportplayerback": "Teleportiere Spieler zurück", - "teleportintoclosestvehicle":"zum nächstliegenden Auto", - - "copiedtoclipboard": "Text wurde in die Zwischenablage kopiert!", - - "spectatevehicleseatoccupied": "Der Fahrzeugsitz auf dem du gesessen hast, ist jetzt besetzt", - "spectatenovehiclefound": "Das Fahrzeug in dem du gesessen hast, wurde nicht mehr gefunden", - "screenreader":"Screen Reader (TTS)", - "screenreaderguide":"Aktiviert Text to Speech für die GUI" -}] diff --git a/server-data/resources/[esx]/EasyAdmin/language/en.json b/server-data/resources/[esx]/EasyAdmin/language/en.json deleted file mode 100644 index 91035fcce..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/en.json +++ /dev/null @@ -1,240 +0,0 @@ -[{ - "translator": "EasyAdmin", - "language": "en", - "on": "On", - "off": "Off", - "spectatingUser": "Spectating ~b~%s.", - "stoppedSpectating": "Stopped Spectating.", - "hour": "Hour", - "hours": "Hours", - "day": "Day", - "days": "Days", - "week": "Week", - "weeks": "Weeks", - "month": "Month", - "months": "Months", - "year": "Year", - "years": "Years", - "customtime": "Custom Time", - "nocustombantime": "You need to set a custom ban length first!", - "permanent": "Permanent", - "confirm": "Confirm", - "playermanagement": "Player Management", - "servermanagement": "Server Management", - "settings": "Settings", - "kickplayer": "Kick Player", - "banplayer": "Ban Player", - "reason": "Reason", - "kickreasonguide": "Add a reason to the kick.", - "noreason": "No Reason Specified", - "confirmkick": "Confirm Kick", - "confirmkickguide": "~r~~h~NOTE:~h~~w~ Pressing Confirm will kick this Player with the specified settings.", - "banreasonguide": "Add a reason to the ban.", - "banlength": "Ban Length", - "banlengthguide": "Until when should the Player be banned?", - "confirmban": "Confirm Ban", - "confirmbanguide": "~r~~h~NOTE:~h~~w~ Pressing Confirm will ban this Player with the specified settings.", - "mute": "Mute Player", - "muteguide": "~r~~h~NOTE:~h~~w~ This will prevent the player from using the text chat.", - "adminmutedplayer": "**%s** muted **%s**", - "adminunmutedplayer": "**%s** unmuted **%s**", - "playermuted": "has been muted!", - "playerunmuted": "has been un-muted!", - "playermute": "You are muted!", - "spectateplayer": "Spectate Player", - "teleportplayer": "Teleport", - "teleporttoplayer": "Me to Player", - "teleportplayertome": "Player to Me", - "slapplayer": "Slap Player", - "setplayerfrozen": "Set Player Frozen", - "allplayers": "All Players", - "teleporttome": "Teleport To Me", - "teleporttomeguide": "~r~~h~NOTE:~h~~w~ This will teleport ~h~all~h~ players to you.", - "setgametype": "Set Game Type", - "setgametypeguide": "~r~~h~NOTE:~h~~w~ This will set the Game Type as listed on the Serverlist.", - "setmapname": "Set Map Name", - "setmapnameguide": "~r~~h~NOTE:~h~~w~ This will set the Map Name as listed on the Serverlist.", - "startresourcebyname": "Start Resource by Name", - "startresourcebynameguide": "~r~~h~NOTE:~h~~w~ This will start a resource installed on the server.", - "stopresourcebyname": "Stop Resource by Name", - "stopresourcebynameguide": "~r~~h~NOTE:~h~~w~ This will stop a resource installed on the server.", - "badidea": "Don't do that, please.", - "viewbanlist": "View Banlist", - "unbanplayer": "Unban Player", - "unbanplayerguide": "~r~~h~NOTE:~h~~w~ Pressing Confirm will unban this Player.", - "banlistshowtype": "~h~Banlist:~h~ Show Type", - "unbanreasons": "Reasons", - "unbanlicenses": "Licenses", - "banlistshowtypeguide": "Toggle Between Ban Reasons or Identifiers in the 'Unban Player' Menu.\nRequires Reopening.", - "refreshbanlist": "Refresh Banlist", - "refreshbanlistguide": "This Refreshes the Banlist in the 'Unban Player' Menu.\nRequires Reopening.", - "refreshpermissions": "Refresh Permissions", - "refreshpermissionsguide": "This Refreshes your current Permissions.\nRequires Reopening.", - "godmodedetected": "Godmode: ~r~Detected~w~", - "godmodenotdetected": "Godmode: ~g~None Detected~w~", - "antiragdoll": "~r~Anti-Ragdoll~w~", - "health": "Health", - "armor": "Armor", - "wantedlevel": "Wanted Level", - "exitspectator": "Press E to exit spectator mode", - - "allowlist": "You are not allowlisted on this server", - "checkingallowlist": "Checking allowlist..", - "bannedjoin": "\nYou have been banned from this Server\n\nReason: %s\n\nBan Expires: %s\nYour Ban ID is #%s.", - "kicked": "Kicked by %s, Reason: %s", - "banned": "You have been banned from this Server, Reason: %s, Ban Expires: %s", - "reasonadd": " ( Nickname: %s ), Banned by: %s", - "nongiven": "None Provided", - "announcement": "Announcement", - "announcementguide": "Send an announcement to all players", - "adminannouncement": "**%s** sent an announcement: **%s**", - "playernotfound": "Player could not be found.", - "done": "Done!", - "adminkickedplayer": "**%s** kicked **%s**, Reason: %s", - "adminbannedplayer": "**%s** banned **%s**, Reason: %s, Ban Expires: %s, ID: %s", - "adminunbannedplayer": "**%s** unbanned **%s** [ %s ]", - "adminslappedplayer": "**%s** slapped **%s** for **%s HP**", - - "adminfrozeplayer": "**%s** froze **%s**.", - "adminunfrozeplayer": "**%s** unfroze **%s**.", - - "left": "Left", - "middle": "Middle", - "right": "Right", - "menuOrientation": "Menu Orientation", - "menuOrientationguide": "Set Menu Orientation ( Left, Right or Center ) \nRequires Game Restart.", - "menuOffset": "Menu Offset", - "menuOffsetguide": "Set Menu Size Offset\nRequires Re-opening of Menu.", - "resetmenuOffset": "Reset Menu Offset", - "takescreenshot": "Take Screenshot", - "admintookscreenshot": "**%s** took a Screenshot of **%s**'s Screen: %s", - "screenshotinprogress": "a Screenshot is already in progress, please wait!", - "screenshotlink": "The Screenshot is available here: %s", - "anonymous": "Anonymous Admin", - "anonymousguide": "Hides your Name when using EasyAdmin Features.", - "adminofflinebannedplayer": "**%s** offline banned **%s**, Reason: %s, Ban Expires: %s", - "cachedplayers": "Cached Players", - "refreshcachedplayers": "Refresh Cached Players", - "refreshcachedplayersguide": "Refreshes List of Cached Players, requires reopening.", - "adminrequestedofflineinfo": "**%s** requested User Data of Offline User **%s**", - "nextpage": "Next Page»", - "firstpage": "««First Page", - "lastpage": "Last Page»»", - "previouspage": "«Previous Page", - "playercalledforadmin": "User %s calls for an admin!\nReason: %s\nID: %s\n", - "playerreportedplayer": "User %s reported a player!\n%s, Reason: %s\nReport %s/%s\nID: %s\n", - "successfullyreported": "Successfully reported the player!", - "reportbantext": "Reported by %s Players in short time.", - "alreadyreported": "You already reported this player!", - "reportedusageerror": "Error! Usage: /report name reason", - "admincalled": "Successfully called an Admin!", - "spectatedplayer": "**%s** has Spectated **%s**", - "teleportedtoplayer": "**%s** has Teleported to **%s**", - "searchbans": "Search Bans", - "searchbansfail": "No Ban with the Search Criteria was found.", - "identifier": "%s Identifier", - "warnplayer": "Warn Player", - "warnreasonguide": "Add a reason to the Warn.", - "confirmwarn": "Confirm Warn", - "confirmwarnguide": "~r~~h~NOTE:~h~~w~ Pressing Confirm will Warn this Player with the specified settings.", - "warnedtitle": "Warning", - "warnedby": "warned by", - "warndismiss": "Hold Spacebar for 10 Seconds to dismiss.", - "warned": "You have been warned, Reason: %s, Warning %s/%s", - "warnkicked": "You have been kicked due to getting Warned too many times.", - "warnbanned": "Warned too many times.", - "adminwarnedplayer": "**%s** warned **%s**, Reason: %s, Warning %s/%s", - - "searchuser": "Search for User", - "searchuserguide": "Find a Player using their Username or ID", - - "setconvar": "Set Convar", - "setconvarguide": "~r~NOTE: If you don't know what you are doing, don't touch this!~w~\nChanges a Convar", - "adminchangedconvar": "**%s** changed convar **%s** to **%s**", - "adminstartedresource": "**%s** started resource **%s**", - "adminstoppedresource": "**%s** stopped resource **%s**", - - "convarname": "Convar Name", - "convarvalue": "Convar Value", - - "cleanarea": "Cleanup Area", - "cleanareaguide": "Cleans up all nearby entities of selected type", - "cars": "Cars", - "peds": "Peds", - "props": "Props", - "cleaningcar": "Deleting Vehicle %s", - "cleaningped": "Deleting Ped %s", - "cleaningprop": "Deleting Object %s", - "finishedcleaning": "Finished cleaning %s", - "admincleanedup": "**%s** Cleaned up all **%s** in a **%s** radius.", - "radius": "Radius", - "type": "Type", - "deepclean":"Deep Clean", - "deepcleanguide":"Also deletes clientside entities.", - - "forceeasteregg": "Force Easter Egg", - - "permissioneditor": "Permission Editor", - "permissioneditorguide": "Edit Permissions on the Server, saves into ~g~easyadmin_permissions.cfg~w~", - - "aces": "ACEs", - "permission": "Permission", - "principals": "Principals", - "addace": "~g~Add ACE", - "addaceguide": "You have the opportunity to edit this before saving.", - "addprincipal": "~g~Add Principal", - "group": "Group", - "entergroup": "Enter group name (e.g. group.admin)", - "enterperm": "Enter Permission name (e.g. easyadmin.player.kick)", - "state": "State", - "stateguide": "Denying permissions does not work properly, only allow permissions you want the player to have.", - "location": "Location", - "locationguide": "This value cannot be edited.", - "principal": "Principal", - "enterprincipal": "Enter principal (e.g. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Delete Permission", - "deletepermissionguide": "~r~Warning! This will completely revoke and delete the Permission!", - "deleteprincipal": "~r~Delete Principal", - "deleteprincipalguide": "~r~Warning! This will completely revoke and delete the Principal!", - - "itemdeleted": "~r~This Item has been deleted.", - "savechanges": "~g~Save Changes", - "savechangesguide": "~r~Warning! ~w~This cannot be undone!", - "admineditedpermissions": "**%s** edited the server's permissions.", - - "reportviewer": "Report Viewer", - "call": "Call", - "report": "Report", - "open": "Open", - "reporter": "Reporter", - "reported": "Reported", - "closereport": "Close Report", - "closesimilarreports": "~r~Close all similar reports~w~", - "closesimilarreportsguide": "Will attempt to find and delete reports made by the same user.", - "adminclosedreport": "**%s** closed Report #**%s**", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "entertoopen": "Select to display options for this player.", - "refreshreports": "Refresh Reports", - "refreshreportsguide": "Shows new reports in case there are any since last time the menu was opened.", - "waitbeforeusingagain": "You must wait before using this again!", - "invalidreport": "You need to specify a reason!", - "claimreport":"Claim Report", - "claimedby":"Claimed By", - "reportalreadyclaimed": "This report has already been claimed!", - "notification":"Notification", - "deferral":"Checking Banlist, please wait.. (%s%%)", - "savebanchanges":"Save Changes", - "savebanguide":"~r~~h~NOTE:~h~~w~ Pressing Confirm will update this Ban on the Server.", - "adminimmune":"You do not have permission to perform this action, target player is immune.", - "teleportmeback":"Teleport me back", - "teleportplayerback":"Teleport Player back", - "teleportintoclosestvehicle":"into closest vehicle", - - "copiedtoclipboard":"Text copied to Clipboard!", - - "spectatevehicleseatoccupied":"The vehicle seat you were in is now occupied.", - "spectatenovehiclefound":"The vehicle you were in can no longer be found.", - "screenreader":"Screen Reader (TTS)", - "screenreaderguide":"Enables Text to Speech for the GUI" - -}] diff --git a/server-data/resources/[esx]/EasyAdmin/language/es.json b/server-data/resources/[esx]/EasyAdmin/language/es.json deleted file mode 100644 index 6b9891649..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/es.json +++ /dev/null @@ -1,240 +0,0 @@ -[{ - "translator": "Antho#0707, Javiito32, Jenoxen#2894", - "language": "es", - "on": "Encendido", - "off": "Apagado", - "spectatingUser": "Especteando ~b~%s.", - "stoppedSpectating": "Has parado de Espectear.", - "hour": "Hora", - "hours": "Horas", - "day": "Dia", - "days": "Dias", - "week": "Semana", - "weeks": "Semanas", - "month": "Mes", - "months": "Meses", - "year": "Año", - "years": "Años", - "customtime": "Tiempo personalizado", - "nocustombantime": "Primero tienes que definir un tiempo de Baneo!", - "permanent": "Permanente", - "confirm": "Confirmar", - "playermanagement": "Gestionar Jugadores", - "servermanagement": "Gestionar Servidor", - "settings": "Ajustes", - "kickplayer": "Kickear jugador", - "banplayer": "Banear jugador", - "reason": "Razon", - "kickreasonguide": "Añade una razon.", - "noreason": "No se ha especificado una razón", - "confirmkick": "Confirmar Kickeo", - "confirmkickguide": "~r~~h~NOTA:~h~~w~ Presionando confirmar kickearas a este jugador con la configuración especificada.", - "banreasonguide": "Añadir una razon de baneo.", - "banlength": "Duración de baneo", - "banlengthguide": "¿Cuanto tiempo deseas banear al jugador?", - "confirmban": "Confirmar baneo", - "confirmbanguide": "~r~~h~NOTA:~h~~w~ Presionando confirmar banearas a este jugador con la configuración especificada.", - "mute": "Mutear jugador", - "muteguide": "~r~~h~NOTA:~h~~w~ Le prohibe el uso del chat al jugador.", - "adminmutedplayer": "**%s** ha muteado a **%s**", - "adminunmutedplayer": "**%s** ha desmuteado a **%s**", - "playermuted": "ha sido muteado", - "playerunmuted": "ha sido desmuteado", - "playermute": "Ha sido muteado", - "spectateplayer": "Espectear jugador", - "teleportplayer": "Teletransportar", - "teleporttoplayer": "hacia jugador", - "teleportplayertome": "jugador hacia mi", - "slapplayer": "Pegarle al jugador", - "setplayerfrozen": "Congelar al jugador", - "allplayers": "Todos los jugadores", - "teleporttome": "Teleportar hacia mi", - "teleporttomeguide": "~r~~h~NOTA:~h~~w~ Teletransportaras hacia ti a ~h~TODOS~h~ los jugadores.", - "setgametype": "Cambiar nombre de modo de juego", - "setgametypeguide": "~r~~h~NOTA:~h~~w~ Esto establecera el nombre del modo de juego.", - "setmapname": "Cambia el nombre de mapa", - "setmapnameguide": "~r~~h~NOTA:~h~~w~ Esto ajustara el nombre del mapa.", - "startresourcebyname": "Inicia un recurso o script por nombre", - "startresourcebynameguide": "~r~~h~NOTA:~h~~w~ Esto iniciara un recurso o script instalado en el servidor.", - "stopresourcebyname": "Detiene un recurso o script por nombre", - "stopresourcebynameguide": "~r~~h~NOTA:~h~~w~ Esto detendra a un recurso o script instalado en el servidor.", - "badidea": "NO hagas eso por favor.", - "viewbanlist": "Ver lista de baneos", - "unbanplayer": "Desbanear jugador", - "unbanplayerguide": "~r~~h~NOTA:~h~~w~ Presiona confirmar para desbanear al jugador.", - "banlistshowtype": "~h~Lista de Bans:~h~ Mostrar tipo de ban", - "unbanreasons": "Razones", - "unbanlicenses": "Licencias", - "banlistshowtypeguide": "Alterna entre razones de baneo o identificadores en el menú 'Desbanear jugador'.\nRequiere volver a entrar.", - "refreshbanlist": "Actualizar lista de baneos", - "refreshbanlistguide": "Esto actualiza la lista de baneos en el menú 'Desbanear jugador'.\nRequiere volver a entrar.", - "refreshpermissions": "Actualizar permisos", - "refreshpermissionsguide": "Esto actualiza sus permisos actuales.\nRequiere volver a entrar.", - "godmodedetected": "Godmode: ~r~Detectado~w~", - "godmodenotdetected": "Godmode: ~r~No detectado~w~", - "antiragdoll": "~r~Anti-Ragdoll~w~", - "health": "Vida", - "armor": "Armadura", - "wantedlevel": "Nivel de búsqueda", - "exitspectator": "Presiona E para salir del modo Espectador", - - "allowlist": "You are not allowlisted on this server", - "checkingallowlist": "Checking allowlist..", - "bannedjoin": "Has sido baneado del servidor, \nRazon: %s, Expira: %s, Ban ID: %s", - "kicked": "Has sido kickeado por %s, Razon: %s", - "banned": "Has sido baneado del servidor, Razon: %s, Expira: %s", - "reasonadd": " ( El jugador: %s ), ha sido baneado por: %s", - "nongiven": "No se ha proporcionado", - "announcement": "Anuncio", - "announcementguide": "Send an announcement to all players", - "adminannouncement": "**%s** sent an announcement: **%s**", - "playernotfound": "No se ha encontrado al jugador.", - "done": "Hecho", - "adminkickedplayer": "**%s** a kickeado a **%s**, Razon: %s", - "adminbannedplayer": "**%s** a baneado a **%s**, Razon: %s, Expira: %s, ID: %s", - "adminunbannedplayer": "**%s** a desbaneado a **%s** [ %s ]", - "adminslappedplayer": "**%s** le ha pegado a **%s** por **%s HP**", - - "adminfrozeplayer": "**%s** a congelado a **%s**.", - "adminunfrozeplayer": "**%s** a descongelado a **%s**.", - - "left": "Izquierda", - "middle": "Central", - "right": "Derecha", - "menuOrientation": "Posicion del menu", - "menuOrientationguide": "Ajusta la posición del menu ( Izquierda, Derecha o Central ) \nRequiere reiniciar el juego.", - "menuOffset": "Desplazamiento del menu", - "menuOffsetguide": "Establecer tamaño del menu\nRequiere reabrir el menú.", - "resetmenuOffset": "Reestablecer tamaño del menu", - "takescreenshot": "Sacar una captura", - "admintookscreenshot": "**%s** saco una captura de pantalla de **%s**'s Pantalla: %s", - "screenshotinprogress": "una captura de pantalla ya esta en progreso, por favor espere", - "screenshotlink": "La captura de pantalla estara disponible aqui: %s", - "anonymous": "Admin anonimo", - "anonymousguide": "Oculta su nombre cuando usa las funciones de EasyAdmin.", - "adminofflinebannedplayer": "**%s** baneado desconectado **%s**, Razon: %s, Expira: %s", - "cachedplayers": "Jugadores en cache", - "refreshcachedplayers": "Actualizar jugadores en cache", - "refreshcachedplayersguide": "Refresca la lista de jugadores en cache, Requiere volver a entrar.", - "adminrequestedofflineinfo": "**%s** datos solicitados del usuario sin conexion **%s**", - "nextpage": "Siguiente pagina»", - "firstpage": "««Primera pagina", - "lastpage": "Ultima pagina»»", - "previouspage": "«Pagina anterior", - "playercalledforadmin": "Usuario %s pide un administrador\nRazon: %s\nID: %s\n", - "playerreportedplayer": "Usuario %s reporto a un jugador\n%s, Razon: %s\nReporta %s/%s\nID: %s\n", - "successfullyreported": "Se ha reportado correctamente", - "reportbantext": "Reportado por %s jugadores en poco tiempo.", - "alreadyreported": "Ya has reportado a este jugador", - "reportedusageerror": "Error, Usa: /report nombre razon", - "admincalled": "Se ha llamado a un administrador exitosamente", - "spectatedplayer": "**%s** ha especteado **%s**", - "teleportedtoplayer": "**%s** se ha teletransportado a **%s**", - "searchbans": "Busqueda de baneos", - "searchbansfail": "No se ha encontrado ningun baneo con los criterios de busqueda.", - "identifier": "Identificador %s", - "warnplayer": "Advertir al jugador", - "warnreasonguide": "Añade un motivo a la advertencia.", - "confirmwarn": "Confirmar Advertencia", - "confirmwarnguide": "~r~~h~NOTA:~h~~w~ Al pulsar Confirmar, se advertirá a este usuario con los ajustes especificados.", - "warnedtitle": "Advertencia", - "warnedby": "advertido por", - "warndismiss": "Mantén la Barra Espaciadora durante 10 segundos para cancelar.", - "warned": "Has recibido una advertencia, Razon: %s, Aviso: %s/%s", - "warnkicked": "Has sido kickeado por recibir demasiadas advertencias.", - "warnbanned": "Has sido baneado por recibir demasiadas advertencias.", - "adminwarnedplayer": "**%s** advirtio a **%s**, Razón: %s, Advertencia %s/%s", - - "searchuser": "Buscar usuario", - "searchuserguide": "Encuentra un jugador usando su nombre de usuario o ID", - - "setconvar": "Definir Convar", - "setconvarguide": "~r~NOTA: NO TOQUES ESTO si no sabes lo que haces ~w~\nCambia una Convar", - "adminchangedconvar": "**%s** ha cambiado la convar **%s** a **%s**", - "adminstartedresource": "**%s** ha iniciado el script **%s**", - "adminstoppedresource": "**%s** ha detenido el script **%s**", - - "convarname": "Nombre de Convar", - "convarvalue": "Valor de Convar", - - "cleanarea": "Limpiar Area", - "cleanareaguide": "Limpia el area de las entidades seleccionadas", - "cars": "Vehículos", - "peds": "Peatones", - "props": "Objetos", - "cleaningcar": "Borrando Vehículos %s", - "cleaningped": "Borrando Peatones %s", - "cleaningprop": "Borrando Objetos %s", - "finishedcleaning": "Limpieza completada con exito %s", - "admincleanedup": "**%s** Ha realizado una limpieza de todos los **%s** en un radio de **%s**.", - "radius": "Radio", - "type": "Tipo", - "deepclean":"Limpieza Profunda", - "deepcleanguide":"También borra las entidades del cliente.", - - "forceeasteregg": "Forzar Easter Egg", - - "permissioneditor": "Editor de permisos", - "permissioneditorguide": "Edita los permisos del servidor, se guardan en ~g~easyadmin_permissions.cfg~w~", - - "aces": "ACEs", - "permission": "Permisos", - "principals": "Principal", - "addace": "~g~Añadir ACE", - "addaceguide": "Puedes editar esto antes de guardar.", - "addprincipal": "~g~Añadir Principal", - "group": "Grupo", - "entergroup": "Introduce un nombre de grupo (ej. group.admin)", - "enterperm": "Introduce nombre de permiso (ej. easyadmin.player.kick)", - "state": "Estado", - "stateguide": "La denegacion de permisos no funciona correctamente, Solo dale permisos que quieras que el jugador tenga.", - "location": "Ubicacion", - "locationguide": "Este valor no se puede editar.", - "principal": "Principal", - "enterprincipal": "Añadir principal (ej. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Borrar Permisos", - "deletepermissionguide": "~r~CUIDADO, Esto le borrara el permiso a este jugador", - "deleteprincipal": "~r~Borrar Principal", - "deleteprincipalguide": "~r~CUIDADO, Esto le borrará el permiso a este jugador", - - "itemdeleted": "~r~Item borrado.", - "savechanges": "~g~Guardar cambios", - "savechangesguide": "~r~CUIDADO ~w~ Esto no se puede restaurar", - "admineditedpermissions": "**%s** ha editado los permisos del servidor.", - - "reportviewer": "Reportes activos", - "call": "Llamada de Admin", - "report": "Reporte de Admin", - "open": "Abrir", - "reporter": "Jugador que Reporta", - "reported": "Jugador Reportado", - "closereport": "Cerrar Reporte", - "closesimilarreports": "~r~Cerrar todos los reportes similares~w~", - "closesimilarreportsguide": "Intentara encontrar y borrar todos los reportes realizados por el mismo usuario.", - "adminclosedreport": "**%s** ha cerrado el reporte #**%s**", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "entertoopen": "Selecciona para revisar las opciones para este jugador.", - "refreshreports": "Refrescar Reportes", - "refreshreportsguide": "Muestra nuevos reportes desde la última vez que se haya abierto el menu.", - "waitbeforeusingagain": "¡Debes esperar antes de poder hacer eso otra vez!", - "invalidreport": "¡Tienes que especificar un motivo!", - "claimreport":"Aceptar reporte", - "claimedby":"Reporte atendido por", - "reportalreadyclaimed": "Este reporte ya ha sido aceptado", - "notification":"Notificacion", - "deferral":"Comprobando lista de baneos, por favor espere... (%s%%)", - "savebanchanges":"Guardar Cambios", - "savebanguide":"~r~~h~NOTA:~h~~w~ Pulsando Confirmar actualizaras este Baneo en el servidor.", - "adminimmune":"No tienes permiso para hacer esto, el jugador que has seleccionado es Administrador.", - "teleportmeback":"Teletransportarme a donde estaba", - "teleportplayerback":"Teletransportar al jugador a donde estaba", - "teleportintoclosestvehicle":"Al vehiculo mas cercano", - - "copiedtoclipboard":"Texto copiado al portapapeles", - - "spectatevehicleseatoccupied":"El asiento en el vehiculo en el que estabas se encuentra ocupado.", - "spectatenovehiclefound":"El vehiculo en el que estabas ya no puede ser encontrado.", - "screenreader":"Texto a Voz (TTS)", - "screenreaderguide":"Activa el Texto a Voz de la interfaz (GUI)" - -}] diff --git a/server-data/resources/[esx]/EasyAdmin/language/fr.json b/server-data/resources/[esx]/EasyAdmin/language/fr.json deleted file mode 100644 index 347054205..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/fr.json +++ /dev/null @@ -1,243 +0,0 @@ -[{ - "translator": "Feared, Robi321", - "language": "fr", - "on": "Activer", - "off": "Éteins", - - "spectatingUser": "Vous regardez ~b~%s.", - "stoppedSpectating": "Vous avez arrêté de regarder un joueur.", - "hour": "Heure", - "hours": "Les heures", - "day": "Jour", - "days": "Jours", - "week": "La semaine", - "weeks": "Semaines", - "month": "Mois", - "months": "Mois", - "year": "An", - "years": "Ans", - "customtime": "Heure personnalisée", - "nocustombantime": "Vous devez d'abord définir une durée d'interdiction personnalisée !", - "permanent": "Permanent", - "confirm": "Confirmer", - "playermanagement": "Gestion des joueurs", - "servermanagement": "Gestion du serveur", - "settings": "Paramètres", - "kickplayer": "Expulser le joueur", - "banplayer": "Bannir le joueur", - "reason": "Raison", - "kickreasonguide": "Ajouter une raison pour le kick.", - "noreason": "Aucune raison spécifiée", - "confirmkick": "Confirmer le kick", - "confirmkickguide": "~r~~h~NOTE :~h~~w~ Appuyer sur confirmer va ~r~~h~expulser~h~~w~ le joueur avec les paramètres spécifiés.", - "banreasonguide": "Ajouter une raison pour le bannissement.", - "banlength": "Temps du bannissement", - "banlengthguide": "Jusqu'à quand le joueur doit être banni?", - "confirmban": "Confirmer le bannissement", - "mute": "Joueur muet", - "muteguide": "~r~~h~NOTE:~h~~w~ Cela empêchera le joueur d’utiliser le chat textuel.", - "adminmutedplayer": "**%s** muted **%s**", - "adminunmutedplayer": "**%s** unmuted **%s**", - "playermuted": "a été mis en sourdine!", - "playerunmuted": "a été mis en sans sourdine!", - "playermute": "Vous êtes muet!", - "confirmbanguide": "~r~~h~NOTE:~h~~w~ Appuyer sur confirmer va ~r~~h~bannir~h~~w~ le joueur avec les paramètres specifiés.", - "spectateplayer": "Regarder le joueur", - "teleportplayer": "Téléportation", - "teleporttoplayer": "au joueur", - "teleportplayertome": "du joueur à moi", - "slapplayer": "Pousser le joueur", - "setplayerfrozen": "Geler le joueur", - "allplayers": "Tout les joueurs", - "teleporttome": "Téléporter à moi", - "teleporttomeguide": "~r~~h~NOTE :~h~~w~ Cela va téléporter ~r~~h~tout~h~~w~ les joueurs à vous.", - "setgametype": "Changer le type de jeu", - "setgametypeguide": "~r~~h~NOTE:~h~~w~ Cela va changer de type de jeu dans la liste des serveurs.", - "setmapname": "Changer de carte", - "setmapnameguide": "~r~~h~NOTE :~h~~w~ Cela va changer de carte dans la liste des serveurs.", - "startresourcebyname": "Lancer la ressource par son nom", - "startresourcebynameguide": "~r~~h~NOTE :~h~~w~ Cela va lancer une ressource sur votre serveur.", - "stopresourcebyname": "Arrêter la ressource par son nom", - "stopresourcebynameguide": "~r~~h~NOTE :~h~~w~ Cela va arrêter une ressource sur votre serveur.", - "badidea": "Ne faites pas cela, s'il vous plaît.", - "viewbanlist": "Voir la liste d'interdiction", - "unbanplayer": "Débannir un joueur", - "unbanplayerguide": "~r~~h~NOTE :~h~~w~ Confirmer le débannissement du joueur.", - "banlistshowtype": "~h~Liste des bans :~h~ voir le type", - "unbanreasons": "Raison", - "unbanlicenses": "ID", - "banlistshowtypeguide": "Choisir l'affichage dans le menu 'Débannir joueur'.\nRéouverture requise.", - "refreshbanlist": "Rafraîchir la liste des bans", - "refreshbanlistguide": "Rafraîchir votre liste des bans au menu 'Débannir joueur'. Réouverture requise.", - "refreshpermissions": "Rafraîchir les permissions", - "refreshpermissionsguide": "Rafraîchir vos permissions sur le serveur actuel.\nRéouverture requise.", - "godmodedetected": "Invincibilité : ~r~Détecté~w~", - "godmodenotdetected": "Invincibilité : ~g~Non détecté~w~", - "antiragdoll": "~r~Anti-Gamelle~w~", - "health": "Vie", - "armor": "Armure", - "wantedlevel": "Niveau de recherche", - "exitspectator": "Appuyer sur E pour quitter le mode spectateur", - - "allowlist": "You are not allowlisted on this server", - "checkingallowlist": "Checking allowlist..", - "bannedjoin": "Vous avez été banni du serveur, \nRaison : %s, Expiration du bannissement : %s, Ban ID: %s", - "kicked": "Éjecté par %s, Raison : %s", - "banned": "Vous avez été banni du serveur, \nRaison : %s, Expiration du bannissement : %s", - "reasonadd": " ( Pseudo : %s ), banni par : %s", - "nongiven": "Non autorisé", - "announcement": "Announcement", - "announcementguide": "Send an announcement to all players", - "adminannouncement": "**%s** sent an announcement: **%s**", - "playernotfound": "Le joueur n'a pas été trouvé.", - "done": "Fait!", - "adminkickedplayer": "**%s** a expulser **%s**, Raison: %s", - "adminbannedplayer": "**%s** a banni **%s**, Raison: %s, Expire: %s, ID: %s", - "adminunbannedplayer": "**%s** a débanni **%s** [ %s ]", - "adminslappedplayer": "**%s** à pousser **%s** pour **%s HP**", - - "adminfrozeplayer": "**%s** à geler **%s**.", - "adminunfrozeplayer": "**%s** à dégeler **%s**.", - - "left": "Gauche", - "middle": "Milieu", - "right": "Droite", - "menuOrientation": "Orientation du menu", - "menuOrientationguide": "Réglage de l'orientation du menu ( Gauche, milieu ou droite ) \nNécessite un redémarrage du jeu.", - "menuOffset": "Définir la largeur du menu", - "menuOffsetguide": "Définir la taille du menu\nNécessite la réouverture du menu.", - "resetmenuOffset": "Réinitialiser la largeur du menu", - "takescreenshot": "Faire une capture d'écran", - "admintookscreenshot": "**%s** a fait une capture d'écran de l'écran de **%s** : %s", - "screenshotinprogress": "Une capture d'écran est déjà en cours, veuillez patienter !", - "screenshotlink": "La capture d'écran est disponible ici : %s", - "anonymous": "Administration anonyme", - "anonymousguide": "Masque votre nom lorsque vous utilisez les fonctions EasyAdmin.", - "adminofflinebannedplayer": "**%s** offline banned **%s**, Reason: %s, Ban Expires: %s", - "cachedplayers": "Joueurs en cache", - "refreshcachedplayers": "Rafraîchir les joueurs en cache", - "refreshcachedplayersguide": "Refreshes List of Cached Players, requires reopening.", - "adminrequestedofflineinfo": "**%s** Données demandées à l'utilisateur de l'utilisateur hors ligne **%s**", - "nextpage": "Page suivante»", - "firstpage": "««Première page", - "lastpage": "Dernière page»»", - "previouspage": "«Page précédente", - "playercalledforadmin": "Utilisateur %s appelle un administrateur!\nRaison: %s\nID: %s\n", - "playerreportedplayer": "Utilisateur %s a signalé un joueur!\n%s, Raison: %s\nReportage %s/%s\nID: %s\n", - "successfullyreported": "A signalé le joueur avec succès!", - "reportbantext": "Rapporté par %s Joueurs en peu de temps.", - "alreadyreported": "Vous avez déjà signalé ce joueur!", - "reportedusageerror": "Erreur! Usage: /report nom raison", - "admincalled": "Appelé avec succès un administrateur!", - - "spectatedplayer": "**%s** a regardé **%s**", - "teleportedtoplayer": "**%s** téléporté vers **%s**", - "searchbans": "Interdictions de recherche", - "searchbansfail": "Aucune interdiction avec les critères de recherche n'a été trouvée.", - "identifier": "Identifier %s", - "warnplayer": "Avertir le joueur", - "warnreasonguide": "Ajouter une raison à l'avertissement.", - "confirmwarn": "Confirmer Avertir", - "confirmwarnguide": "~r~~h~NOTE:~h~~w~ Appuyer sur Confirmer avertira ce lecteur avec les paramètres spécifiés.", - "warned": "Vous avez été averti, Raison : %s, Avertissement %s/%s", - "warnedtitle": "Avertissement", - "warnedby": "averti par", - "warndismiss": "Maintenez la barre d'espacement pendant 10 secondes pour fermer.", - "warnkicked": "Vous avez reçu un coup de pied parce que vous avez été averti trop souvent.", - "warnbanned": "Averti trop souvent.", - "adminwarnedplayer": "**%s** averti **%s**, Raison : %s, Avertissement %s/%s", - - "searchuser": "Rechercher un utilisateur", - "searchuserguide": "Trouver un joueur à l'aide de son nom d'utilisateur ou de son identifiant", - - "setconvar": "Set Convar", - "setconvarguide": "~r~NOTE: Si vous ne savez pas ce que vous faites, n'y touchez pas!~w~\nModifications apportées à Convar", - "adminchangedconvar": "**%s** changed convar **%s** to **%s**", - "adminstartedresource": "**%s** started resource **%s**", - "adminstoppedresource": "**%s** stopped resource **%s**", - - "convarname": "Convar Nom", - "convarvalue": "Convar Valeur", - - "cleanarea": "Zone de nettoyage", - "cleanareaguide": "Nettoie toutes les entités à proximité du type sélectionné", - "cars": "Voitures", - "peds": "Péd", - "props": "Accessoires", - "cleaningcar": "Suppression d'un véhicule %s", - "cleaningped": "Suppression Ped %s", - "cleaningprop": "Suppression d'un objet %s", - "finishedcleaning": "Nettoyage terminé %s", - "admincleanedup": "**%s** Nettoyé tous les **%s** dans un rayon de **%s**.", - "radius": "Radius", - "type": "Type", - "deepclean":"Deep Clean", - "deepcleanguide":"Also deletes clientside entities.", - - "forceeasteregg": "Forcer Easter Egg", - - "permissioneditor": "Permission Editor", - "permissioneditorguide": "Modifier les autorisations sur le serveur, enregistre dans ~g~easyadmin_permissions.cfg~w~", - - "aces": "ACEs", - "permission": "Autorisation", - "principals": "Principals", - "addace": "~g~Ajouter ACE", - "addaceguide": "Vous avez la possibilité de le modifier avant d'enregistrer.", - "addprincipal": "~g~Ajouter Principal", - "group": "Grouper", - "entergroup": "Entrez le nom du groupe (e.g. group.admin)", - "enterperm": "Entrez le nom de l'autorisation (e.g. easyadmin.player.kick)", - "state": "État", - "stateguide": "Refuser les autorisations ne fonctionne pas correctement, n'autorisez que les autorisations que vous souhaitez que le joueur ait.", - "location": "Emplacement", - "locationguide": "Cette valeur ne peut pas être modifiée.", - "principal": "Principal", - "enterprincipal": "Entrer principal (e.g. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Supprimer l'autorisation", - "deletepermissionguide": "~r~Avertissement! Cela révoquera et supprimera complètement l'autorisation!", - "deleteprincipal": "~r~Supprimer Principal", - "deleteprincipalguide": "~r~Avertissement! Cela révoquera et supprimera complètement le principal!", - - "itemdeleted": "~r~Cet élément a été supprimé.", - "savechanges": "~g~Sauvegarder les modifications", - "savechangesguide": "~r~Avertissement! ~w~Ça ne peut pas être annulé!", - "admineditedpermissions": "**%s** modifié les autorisations du serveur.", - - "reportviewer": "Visionneuse de rapports", - "call": "Appeler", - "report": "Reportage", - "open": "Ouvert", - "reporter": "Reporter", - "reported": "Signalé", - "closereport": "Fermer le rapport", - "closesimilarreports": "~r~Fermer tous les rapports similaires~w~", - "closesimilarreportsguide": "Tentera de trouver et de supprimer les rapports créés par le même utilisateur.", - "adminclosedreport": "**%s** Rapport fermé #**%s**", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "entertoopen": "Sélectionnez pour afficher les options pour ce lecteur.", - "refreshreports": "Actualiser les rapports", - "refreshreportsguide": "Affiche de nouveaux rapports au cas où il y en aurait depuis la dernière fois que le menu a été ouvert.", - "waitbeforeusingagain": "Vous devez attendre avant de l'utiliser à nouveau!", - "invalidreport": "Vous devez spécifier une raison!", - "claimreport":"Rapport de réclamation", - "claimedby":"Revendiquée par", - "reportalreadyclaimed": "Ce rapport a déjà été réclamé!", - "notification":"Notification", - "deferral":"Vérification de la liste de bannissement, veuillez patienter.. (%s%%)", - "savebanchanges":"Sauvegarder les modifications", - "savebanguide":"~r~~h~NOTE:~h~~w~ Appuyez sur Confirmer pour mettre à jour cette interdiction sur le serveur.", - "adminimmune":"Vous n'avez pas la permission d'effectuer cette action, le joueur ciblé est immunisé.", - "teleportmeback":"Téléporter moi dos", - "teleportplayerback":"Téléporter le joueur dos", - "teleportintoclosestvehicle":"into closest vehicle", - - "copiedtoclipboard":"Texte copié dans le presse-papiers!", - - "spectatevehicleseatoccupied":"Le siège du véhicule dans lequel vous étiez est maintenant occupé.", - "spectatenovehiclefound":"Le véhicule dans lequel vous étiez est introuvable.", - "screenreader":"Screen Reader (TTS)", - "screenreaderguide":"Enables Text to Speech for the GUI" - - -}] diff --git a/server-data/resources/[esx]/EasyAdmin/language/it.json b/server-data/resources/[esx]/EasyAdmin/language/it.json deleted file mode 100644 index ff1426bc1..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/it.json +++ /dev/null @@ -1,242 +0,0 @@ -[{ - "translator": "IceHax, ITKewai", - "language": "it", - "on": "Attivo", - "off": "Disattivo", - "spectatingUser": "Spectando ~b~%s.", - "stoppedSpectating": "Non Stai Più Spectando.", - "hour": "Hour", - "hours": "Hours", - "day": "Day", - "days": "Days", - "week": "Week", - "weeks": "Weeks", - "month": "Month", - "months": "Months", - "year": "Year", - "years": "Years", - "customtime": "Custom Time", - "nocustombantime": "You need to set a custom ban length first!", - "permanent": "Permanente", - "confirm": "Confirm", - "playermanagement": "Gestione Giocatori", - "servermanagement": "Gestione Server", - "settings": "Impostazioni", - "kickplayer": "Espelli Giocatore", - "banplayer": "Bandisci Giocatore", - "reason": "Ragione", - "kickreasonguide": "Aggiungi una ragione per l'espulsione", - "noreason": "Nessuna Ragione Specificata", - "confirmkick": "Conferma espulsione", - "confirmkickguide": "~r~~h~NOTA:~h~~w~ Premendo Conferma si espellera il giocatore con le impostazioni correnti.", - "banreasonguide": "Aggiugi una ragione per il Ban.", - "banlength": "Durata del Ban", - "banlengthguide": "Fino a quando dovrà essere bandito il giocatore?", - "confirmban": "Conferma il Ban", - "confirmbanguide": "~r~~h~NOTA:~h~~w~ Premendo conferma bandirai il giocatore con le impostazioni correnti.", - "mute": "Muta il Giocatore", - "muteguide": "~r~~h~ATTENZIONE:~h~~w~ Ciò impedirà al giocatore di utilizzare la chat di testo.", - "adminmutedplayer": "**%s** ha mutato **%s**", - "adminunmutedplayer": "**%s** ha smutato **%s**", - "playermuted": "è stato mutato!", - "playerunmuted": "è stato smutato!", - "playermute": "Sei mutato!", - "spectateplayer": "Spetta Giocatore", - "teleportplayer": "Teletrasporta", - "teleporttoplayer": "Te dal Giocatore", - "teleportplayertome": "il Giocatore da Me", - "slapplayer": "Schiaffeggia il Giocatore", - "setplayerfrozen": "Blocca Il Giocatore", - "allplayers": "Tutti i Giocatori", - "teleporttome": "Teletrasporta tutti", - "teleporttomeguide": "~r~~h~NOTA:~h~~w~ Questo Teletrasporterà ~h~TUTTI~h~ i giocatori da te.", - "setgametype": "Imposta Game Type", - "setgametypeguide": "~r~~h~NOTA:~h~~w~ Questo imposterà il Game Type mostrato nella ServerList.", - "setmapname": "Imposta Map Name", - "setmapnameguide": "~r~~h~NOTA:~h~~w~ Questo modificherà La Map Name, mostrata nella ServerList.", - "startresourcebyname": "Esegui Risorsa per nome", - "startresourcebynameguide": "~r~~h~NOTA:~h~~w~ Questo Starterà una Risorsa installata sul server.", - "stopresourcebyname": "Ferma Risorsa per nome", - "stopresourcebynameguide": "~r~~h~NOTA:~h~~w~ Questo Stopperà una Risorsa installata sul server.", - "badidea": "Non farlo, per piacere.", - "viewbanlist": "Visualizza Banlist", - "unbanplayer": "Rimuovi ban per il giocatore", - "unbanplayerguide": "~r~~h~NOTA:~h~~w~ Premendo Conferma Rimuoverai il Ban di questo giocatore.", - "banlistshowtype": "~h~Banlist:~h~ Mostra tipo", - "unbanreasons": "Ragioni", - "unbanlicenses": "Licenze", - "banlistshowtypeguide": "Cambia tra Identificatore e Ragioni per il ban Nel menù 'Rimuovi Ban per il giocatore'.\nRichiede una riapertura del menù.", - "refreshbanlist": "Aggiorna La lista dei ban", - "refreshbanlistguide": "Questo aggiorna la lista dei ban per il menù 'Rimuovi ban per il giocatore'.\nRichiede una riapertura del menù.", - "refreshpermissions": "Aggiorna Permessi", - "refreshpermissionsguide": "Questo aggiorna i tuoi permessi correnti.\nRichiede una riapertura del menù.", - "godmodedetected": "Godmode: ~r~Trovata~w~", - "godmodenotdetected": "Godmode: ~g~Non Trovata~w~", - "antiragdoll": "~r~Anti-Ragdoll~w~", - "health": "Salute", - "armor": "Armatura", - "wantedlevel": "Livello Ricercato", - "exitspectator": "Premi E per uscire dalla modalità spettatore.", - - "allowlist": "You are not allowlisted on this server", - "checkingallowlist": "Checking allowlist..", - "bannedjoin": "Sei stato bandito da questo server, \nMotivo: %s, Il Ban Scade: %s, Ban ID: %s", - "kicked": "Espulso da %s, Motivo: %s", - "banned": "Sei stato bandito da questo server, Motivo: %s, Il Ban Scade: %s", - "reasonadd": " ( Nickname: %s ), Bandito da: %s", - "nongiven": "Non fornito", - "announcement": "Announcement", - "announcementguide": "Send an announcement to all players", - "adminannouncement": "**%s** sent an announcement: **%s**", - "playernotfound": "Giocatore non trovato.", - "done": "Fatto!", - "adminkickedplayer": "**%s** Ha espulso **%s**, Motivo: %s", - "adminbannedplayer": "**%s** Ha bandito **%s**, Motivo: %s, Scadenza: %s, ID: %s", - "adminunbannedplayer": "**%s** ha revocato il ban di **%s** [ %s ]", - "adminslappedplayer": "**%s** ha schiaffeggiato **%s** togliendogli **%s HP**", - - "adminfrozeplayer": "**%s** ha bloccato **%s**.", - "adminunfrozeplayer": "**%s** ha sbloccato **%s**.", - - "left": "Sinistra", - "middle": "Centrale", - "right": "Destra", - "menuOrientation": "Orientamento del menù", - "menuOrientationguide": "Imposta l'orientamento del menù ( Sinistra, Destra o Centro ) \nRichiede la riapertura del menù.", - "menuOffset": "Larghezza del menù", - "menuOffsetguide": "Imposta l'offset della dimensione del menù\nRichiede la riapertura del menù.", - "resetmenuOffset": "Resetta larghezza del menù", - "takescreenshot": "Prendi uno Screenshot", - "admintookscreenshot": "**%s** ha preso uno Screenshot dallo schermo di **%s** : %s", - "screenshotinprogress": "si sta già catturando lo Screenshot, attendi!", - "screenshotlink": "Lo screenshot è disponibile qui: %s", - "anonymous": "Admin anonimo", - "anonymousguide": "Nasconde il tuo nome quando usi le funzioni dell' EasyAdmin.", - "adminofflinebannedplayer": "**%s** ha bannato offline **%s**, Motivo: %s, Scadenza del Ban: %s", - "cachedplayers": "Giocatori nella Cache", - "refreshcachedplayers": "Aggiorna giocatori nella Cache", - "refreshcachedplayersguide": "Aggiornare giocatori nella Cache, Richiede la riapertura del menù.", - "adminrequestedofflineinfo": "**%s** ha richiesto i Dati Offline di **%s**", - "nextpage": "Pagina Sucessiva»", - "firstpage": "««Prima Pagina", - "lastpage": "Ultima Pagina»»", - "previouspage": "«Pagina Precedente", - "playercalledforadmin": "L'utente %s sta chiamando un admin!\nMotivo: %s\nID: %s\n", - "playerreportedplayer": "L'utente %s sta segnalando un giocatore!\n%s, Motivo: %s\nSegnalazione %s/%s\nID: %s\n", - "successfullyreported": "Player segnalato con successo!", - "reportbantext": "Sei stato segnalato da %s giocatori in breve periodo.", - "alreadyreported": "Hai già segnalato questo player!", - "reportedusageerror": "Errore! Esempio di utilizzo : /report nome motivo", - "admincalled": "Admin contattato correttamente!", - - "spectatedplayer": "**%s** sta guardando **%s**", - "teleportedtoplayer": "**%s** teletrasportato a **%s**", - "searchbans": "Cerca Ban", - "searchbansfail": "Non è stato trovato nessun Ban con questo criterio di ricerca.", - "identifier": "Identificatore %s", - "warnplayer": "Warna un giocatore", - "warnreasonguide": "Aggiungi il motivo dell' Warn.", - "confirmwarn": "Conferma Warn", - "confirmwarnguide": "~r~~h~ATTENZIONE:~h~~w~ Premendo conferma si Warnerà questo giocatore con le impostazioni attuali.", - "warnedtitle": "Attenzione", - "warnedby": "warnato da", - "warndismiss": "Tieni premuto spazio per 10 Secondi per chiudere questa schermata.", - "warned": "Sei stato warnato, Motivo: %s, Warn %s di %s", - "warnkicked": "Sei stato espulso per aver preso troppi Warn. Rientra", - "warnbanned": "Warnato troppe volte.", - "adminwarnedplayer": "**%s** ha warnato **%s**, Motivo: %s, Warn %s di %s", - - "searchuser": "Cerca utente ", - "searchuserguide": "Trova un giocatore utilizzando il suo nome utente o ID", - - "setconvar": "Set Convar", - "setconvarguide": "~r~ATTENZIONE: Se non sai cosa stai facendo, non modificare niente!~w~\nChanges a Convar", - "adminchangedconvar": "**%s** changed convar **%s** to **%s**", - "adminstartedresource": "**%s** ha avviato la Risorsa **%s**", - "adminstoppedresource": "**%s** ha fermato la Risorsa **%s**", - - "convarname": "Convar Name", - "convarvalue": "Convar Value", - - "cleanarea": "Pulisci l'Area", - "cleanareaguide": "Cleans up all nearby entities of selected type", - "cars": "Veicoli", - "peds": "Ped", - "props": "Oggetti", - "cleaningcar": "Cancellando Veicoli %s", - "cleaningped": "Cancellando Ped %s", - "cleaningprop": "Cancellando Oggetti %s", - "finishedcleaning": "%s cancellatti correttamente", - "admincleanedup": "**%s** Ripulito tutto **%s** in un raggio **%s**.", - "radius": "Radius", - "type": "Type", - "deepclean":"Deep Clean", - "deepcleanguide":"Also deletes clientside entities.", - - "forceeasteregg": "Force Easter Egg", - - "permissioneditor": "Editor permessi", - "permissioneditorguide": "Modifica i permessi nel server, e li salva in ~g~easyadmin_permissions.cfg~w~", - - "aces": "ACEs", - "permission": "Permission", - "principals": "Principals", - "addace": "~g~Add ACE", - "addaceguide": "Puoi modificare questo file prima di salvarlo.", - "addprincipal": "~g~Add Principal", - "group": "Group", - "entergroup": "Enter group name (e.g. group.admin)", - "enterperm": "Enter Permission name (e.g. easyadmin.player.kick)", - "state": "State", - "stateguide": "Denying permissions does not work properly, only allow permissions you want the player to have.", - "location": "Location", - "locationguide": "This value cannot be edited.", - "principal": "Principal", - "enterprincipal": "Enter principal (e.g. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Delete Permission", - "deletepermissionguide": "~r~Warning! This will completely revoke and delete the Permission!", - "deleteprincipal": "~r~Delete Principal", - "deleteprincipalguide": "~r~Warning! This will completely revoke and delete the Principal!", - - "itemdeleted": "~r~This Item has been deleted.", - "savechanges": "~g~Salve Modifiche", - "savechangesguide": "~r~Attenzione! ~w~Non può essere fatto!", - "admineditedpermissions": "**%s** ha modificato i permessi del server.", - - "reportviewer": "Lista Segnalazioni", - "call": "Call", - "report": "Segnalazioni", - "open": "Apri", - "reporter": "Utente Segnalatore", - "reported": "Utente Segnalato", - "closereport": "Chiudi segnalazione", - "closesimilarreports": "~r~Chiudi le segnalazioni simili~w~", - "closesimilarreportsguide": "Prova a cercare e cancellare segnalazioni fatte dallo stesso utente.", - "adminclosedreport": "**%s** ha chiuso il report #**%s**", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "entertoopen": "Seleziona per vedere opzioni per questo giocatore.", - "refreshreports": "Aggiorna Segnalazioni", - "refreshreportsguide": "Mostra le nuove segnalazioni rispetto all'ultima volta che hai aperto il menù.", - "waitbeforeusingagain": "You must wait before using this again!", - "invalidreport": "You need to specify a reason!", - "claimreport":"Claim Report", - "claimedby":"Claimed By", - "reportalreadyclaimed": "This report has already been claimed!", - "notification":"Notification", - "deferral":"Checking Banlist, please wait.. (%s%%)", - "savebanchanges":"Save Changes", - "savebanguide":"~r~~h~NOTE:~h~~w~ Pressing Confirm will update this Ban on the Server.", - "adminimmune":"You do not have permission to perform this action, target player is immune.", - "teleportmeback":"Teleport me back", - "teleportplayerback":"Teleport Player back", - "teleportintoclosestvehicle":"into closest vehicle", - - "copiedtoclipboard":"Text copied to Clipboard!", - - "spectatevehicleseatoccupied":"The vehicle seat you were in is now occupied.", - "spectatenovehiclefound":"The vehicle you were in can no longer be found.", - "screenreader":"Screen Reader (TTS)", - "screenreaderguide":"Enables Text to Speech for the GUI" - - -}] diff --git a/server-data/resources/[esx]/EasyAdmin/language/nl.json b/server-data/resources/[esx]/EasyAdmin/language/nl.json deleted file mode 100644 index d187605ff..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/nl.json +++ /dev/null @@ -1,242 +0,0 @@ -[{ - "translator": "TheIndra, Jaccosf", - "language": "nl", - - "on": "Ingeschakeld", - "off": "Uitgeschakeld", - "spectatingUser": "~b~%s aan het Spectaten.", - "stoppedSpectating": "Gestopt met Spectaten.", - "hour": "Uur", - "hours": "Uren", - "day": "Dag", - "days": "Dagen", - "week": "Week", - "weeks": "Weken", - "month": "Maand", - "months": "Maanden", - "year": "Jaar", - "years": "Jaren", - "customtime": "Custom Tijd", - "nocustombantime": "Je moet eerst de lengte van de ban invullen!", - "permanent": "Permanent", - "confirm": "Bevestig", - "playermanagement": "Speler Beheer", - "servermanagement": "Server Management", - "settings": "Instellingen", - "kickplayer": "Kick Speler", - "banplayer": "Ban Speler", - "reason": "Reden", - "kickreasonguide": "Voeg een reden toe aan de kick.", - "noreason": "Geen reden opgegeven", - "confirmkick": "Bevestig Kick", - "confirmkickguide": "~r~~h~NOTITIE:~h~~w~ Als je op bevestigen drukt wordt deze Speler gekickt.", - "mute": "Demp speler", - "muteguide": "~r~~h~NOTITIE:~h~~w~ Dit voorkomt dat de speler de tekstchat gebruikt.", - "adminmutedplayer": "**%s** muted **%s**", - "adminunmutedplayer": "**%s** unmuted **%s**", - "playermuted": "is gedempt!", - "playerunmuted": "is niet-gedempt!", - "playermute": "Je bent gedempt!", - "banreasonguide": "Voeg een reden toe aan de ban.", - "banlength": "Ban Lengte", - "banlengthguide": "Hoelang moet de Speler verbannen worden?", - "confirmban": "Bevestig Ban", - "confirmbanguide": "~r~~h~NOTITIE:~h~~w~ Als je op bevestigen drukt wordt deze Speler verbannen.", - "spectateplayer": "Spectate Speler", - "teleportplayer": "Teleporteer", - "teleporttoplayer": "naar Speler", - "teleportplayertome": "Speler naar mij", - "slapplayer": "Speler klap geven", - "setplayerfrozen": "Speler vastzetten", - "allplayers": "Alle spelers", - "teleporttome": "Teleporteer naar mij", - "teleporttomeguide": "~r~~h~NOTITIE:~h~~w~ Dit teleporteert ~h~alle~h~ spelers naar jou.", - "setgametype": "Stel Game Type in", - "setgametypeguide": "~r~~h~NOTITIE:~h~~w~ Dit stelt het game type in zoals weergegeven op de Serverlijst.", - "setmapname": "Stel Map Naam in", - "setmapnameguide": "~r~~h~NOTITIE:~h~~w~ Dit stelt de map naam in zoals weergegeven in de Serverlijst.", - "startresourcebyname": "Start Resource met Naam", - "startresourcebynameguide": "~r~~h~NOTITIE:~h~~w~ Dit zal een resource starten geinstalleerd op de server.", - "stopresourcebyname": "Stop Resource met Naam", - "stopresourcebynameguide": "~r~~h~NOTITIE:~h~~w~ Dit zal een resource stoppen geinstalleerd op de server.", - "badidea": "Doe dat alsjeblieft niet.", - "viewbanlist": "Bekijk de Banlijst", - "unbanplayer": "Unban Speler", - "unbanplayerguide": "~r~~h~NOTITIE:~h~~w~ Als je op bevestigen drukt wordt deze Speler zijn ban ongedaan gemaakt.", - "banlistshowtype": "~h~Banlijst:~h~ Show Type", - "unbanreasons": "Redenen", - "unbanlicenses": "Licenses", - "banlistshowtypeguide": "Wissel tussen Ban redenen of Identifiers in het 'Unban Speler' Menu.\nVereist Heropenen.", - "refreshbanlist": "Herlaad Banlijst", - "refreshbanlistguide": "Dit herlaad de Banlijst in het 'Unban Speler' Menu.\nVereist Heropenen.", - "refreshpermissions": "Herlaad Permissies", - "refreshpermissionsguide": "Dit herlaad je huidige permissies.\nVereist Heropenen.", - "godmodedetected": "Godmode: ~r~Gevonden~w~", - "godmodenotdetected": "Godmode: ~g~Niet Gevonden~w~", - "antiragdoll": "~r~Anti-Ragdoll~w~", - "health": "Health", - "armor": "Armor", - "wantedlevel": "Wanted Level", - "exitspectator": "Druk op E om spectator mode te verlaten", - - "allowlist": "Je staat niet op de allowlist voor deze server", - "checkingallowlist": "Checking allowlist..", - "bannedjoin": "Je bent verbannen van deze Server, \nReden: %s, Ban Vervalt: %s, Ban ID: %s", - "kicked": "Gekickt door %s, Reden: %s", - "banned": "Je bent verbannen van deze Server, Reden: %s, Ban Vervalt: %s", - "reasonadd": " ( Gebruikersnaam: %s ), Verbannen door: %s", - "nongiven": "Geen reden opgegeven", - "announcement": "Mededeling", - "announcementguide": "Stuur een mededeling naar alle spelers", - "adminannouncement": "**%s** stuurde een mededeling: **%s**", - "playernotfound": "Speler kan niet gevonden worden.", - "done": "Afgerond!", - - "adminkickedplayer": "**%s** Gekickt **%s**, Reden: %s", - "adminbannedplayer": "**%s** banned **%s**, Reden: %s, Ban Vervalt: %s, ID: %s", - "adminunbannedplayer": "**%s** Ban ongedaan gemaakt **%s** [ %s ]", - "adminslappedplayer": "**%s** sloeg **%s** for **%s HP**", - "adminfrozeplayer": "**%s** bevroor **%s**.", - "adminunfrozeplayer": "**%s** gestopt met bevriezen **%s**.", - - "left": "Links", - "middle": "Midden", - "right": "Rechts", - "menuOrientation": "Menu Orientatie", - "menuOrientationguide": "Stel Menu Orientatie in ( Links, Rechts of Midden ) \nVereist Game restart.", - "menuOffset": "Menu Grootte", - "menuOffsetguide": "Stel Menu Grootte in\nVereist menu Heropenen", - "resetmenuOffset": "Reset Menu Grootte", - "takescreenshot": "Neem Schermafbeelding", - "admintookscreenshot": "**%s** Nam een Schermafbeelding van **%s** zijn Scherm: %s", - "screenshotinprogress": "een Schermafbeelding is is al aan de gang, even geduld aub!", - "screenshotlink": "De schermafbeelding is beschikbaar hier: %s", - "anonymous": "Anonieme Admin", - "anonymousguide": "Verberg je naam bij het gebruik van EasyAdmin.", - "adminofflinebannedplayer": "**%s** offline is gebanned **%s**, Reden: %s, Ban verloopt op: %s", - "cachedplayers": "Opgeslagen Spelers", - "refreshcachedplayers": "Herlaad Opgeslage Spelers", - "refreshcachedplayersguide": "Herlaad de opgeslagen spelers, je moet wel het menu heropenen", - "adminrequestedofflineinfo": "**%s** heeft gebruikersgegevens aangevraagd van een offline speler **%s**", - "nextpage": "Volgende Pagina»", - "firstpage": "««Eerste Pagina", - "lastpage": "Laatste Pagina»»", - "previouspage": "«Vorige Pagina", - "playercalledforadmin": "Speler %s wil hulp van een admin!\nReden: %s\nID: %s\n", - "playerreportedplayer": "Speler %s heeft een speler gerapporteerd!\n%s, Reden: %s\nReport %s/%s\nID: %s\n", - "successfullyreported": "De speler is succesvol gerapporteerd!", - "reportbantext": "Gerapporteerd door %s Spelers in een zeer korte tijd.", - "alreadyreported": "Je hebt deze speler al gerapporteerd!", - "reportedusageerror": "Error! Gebruik: /report naam reden", - "admincalled": "Succesvol hulp gevraagd aan een admin!", - - "spectatedplayer": "**%s** is aan het kijken naar **%s**", - "teleportedtoplayer": "**%s** geteleporteerd naar **%s**", - "searchbans": "Zoek Bans", - "searchbansfail": "Geen vergelijkbare ban gevonden.", - "identifier": "Identifier %s", - "warnplayer": "Waarschuw Speler", - "warnreasonguide": "Geef een reden voor de waarschuwing.", - "confirmwarn": "Bevestig Waarschuwing", - "confirmwarnguide": "~r~~h~NOTITIE:~h~~w~ Als je het bevestigt, krijgt de speler de waarschuwing te zien.", - "warnedtitle": "Waarschuwing", - "warnedby": "gewaarschuwd door", - "warndismiss": "Houd spatiebalk 10 seconden ingedrukt, om de waarschuwing te accepteren.", - "warned": "Je bent gewaarschuwd, Reden: %s, Waarschuwing %s/%s", - "warnkicked": "Je bent gekicked, omdat je teveel waarschuwingen hebt ontvangen.", - "warnbanned": "Te vaak gewaarschuwd.", - "adminwarnedplayer": "**%s** heeft **%s** gewaarschuwd, Reden: %s, Waarschuwing %s/%s", - - "searchuser": "Zoek gebruiker", - "searchuserguide": "Zoek een speler met behulp van zijn gebruikersnaam of ID", - - "setconvar": "Pas de Convar aan", - "setconvarguide": "~r~NOTITIE: Gebruik dit alleen, als je weet wat je aan het doen bent!~w~\nVeranderd de Convar", - "adminchangedconvar": "**%s** veranderde de convar **%s** to **%s**", - "adminstartedresource": "**%s** startte resource **%s**", - "adminstoppedresource": "**%s** stopte resource **%s**", - - "convarname": "Convar Naam", - "convarvalue": "Convar Waarde", - - "cleanarea": "Opruim Gebied", - "cleanareaguide": "Verwijdert alle entities van het geselecteerde type", - "cars": "Voertuigen", - "peds": "Herzenlozen", - "props": "Voorwerpen", - "cleaningcar": "Voertuigen aan het verwijderen %s", - "cleaningped": "Herzenlozen aan het verwijderen %s", - "cleaningprop": "Voorwerpen aan het verwijderen %s", - "finishedcleaning": "Klaar met verwijderen %s", - "admincleanedup": "**%s** Alle **%s** in een **%s** gebied zijn verwijderd.", - "radius": "Radius", - "type": "Type", - "deepclean":"Grondige Schoonmaak", - "deepcleanguide":"Verwijdert ook client entities.", - - "forceeasteregg": "Forceer Easter Egg", - - "permissioneditor": "Permissies Aanpassen", - "permissioneditorguide": "Pas de permissies aan, opgeslagen in ~g~easyadmin_permissions.cfg~w~", - - "aces": "ACEs", - "permission": "Permissies", - "principals": "Principals", - "addace": "~g~ACE toevoegen", - "addaceguide": "Je kan het aanpassen, voordat je het opslaat.", - "addprincipal": "~g~Principal toevoegen", - "group": "Groep", - "entergroup": "Voeg groep naam toe (bijv. group.admin)", - "enterperm": "Vul permissie naam in (bijv. easyadmin.player.kick)", - "state": "Staat", - "stateguide": "Permissies weigeren functioneerd niet, sta alleen permissies toe.", - "location": "Locatie", - "locationguide": "Deze waarde kan niet aangepast worden.", - "principal": "Principal", - "enterprincipal": "Vul een principal in (bijv. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Verwijder permissies", - "deletepermissionguide": "~r~Waarschuwing! Dit zal de permissie intrekken en verwijderen!", - "deleteprincipal": "~r~Verwijder Principal", - "deleteprincipalguide": "~r~Waarschuwing! Dit zal de Principal intrekken en verwijderen!", - - "itemdeleted": "~r~Het voorwerp is verwijderd.", - "savechanges": "~g~Sla de wijzigingen op.", - "savechangesguide": "~r~Waarschuwing! ~w~Dit kan niet ongedaan worden gemaakt!", - "admineditedpermissions": "**%s** heeft de server permissies aangepast.", - - "reportviewer": "Report Overzicht", - "call": "Oproep", - "report": "Report", - "open": "Open", - "reporter": "Reporter", - "reported": "Gerapporteerd", - "closereport": "Sluit Report", - "closesimilarreports": "~r~Sluit alle soortgelijke reports~w~", - "closesimilarreportsguide": "Zal alle reports van deze gebruiker verwijderen.", - "adminclosedreport": "**%s** sloot Report #**%s**", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "entertoopen": "Selecteer om de opties voor de speler te bekijken.", - "refreshreports": "Vernieuw Reports", - "refreshreportsguide": "Laat nieuwe reports zien, als er nieuwe zijn.", - "waitbeforeusingagain": "Je moet wachten voor je dit opnieuw kan gebruiken!", - "invalidreport": "Je moet een reden specificeren!", - "claimreport":"Claim Report", - "claimedby":"Geclaimed Door", - "reportalreadyclaimed": "Deze report is al geclaimed!", - "notification":"Notificatie", - "deferral":"Banlijst aan het checken, even geduld.. (%s%%)", - "savebanchanges":"Sla aanpassingen op", - "savebanguide":"~r~~h~NOTE:~h~~w~ Als je dit bevestigt zal de ban worden geüpdatet.", - "adminimmune":"Je hebt geen permissie om dit te doen, die speler is immuun.", - "teleportmeback":"Teleport jezelf terug", - "teleportplayerback":"Teleport Speler terug", - "teleportintoclosestvehicle":"in het dichtstbijzijnde voertuig", - - "copiedtoclipboard":"Tekst gekopieerd naar het klembord!", - - "spectatevehicleseatoccupied":"Het voertuig waar je net in zat is nu bezet.", - "spectatenovehiclefound":"Het voertuig waar je net in zat kan niet worden gevonden.", - "screenreader":"Screen Reader (TTS)", - "screenreaderguide":"Schakelt Text to Speech in voor de GUI" - -}] diff --git a/server-data/resources/[esx]/EasyAdmin/language/pl.json b/server-data/resources/[esx]/EasyAdmin/language/pl.json deleted file mode 100644 index 38c9b73bf..000000000 --- a/server-data/resources/[esx]/EasyAdmin/language/pl.json +++ /dev/null @@ -1,241 +0,0 @@ -[{ - "translator": "EasyAdmin, robbybaseplate, Kubamaz", - "language": "pl", - "on": "Włącz", - "off": "Wyłącz", - "spectatingUser": "Obserwowanie ~b~%s.", - "stoppedSpectating": "Przerwano obserwację.", - "hour": "Godzina", - "hours": "Godziny", - "day": "Dzień", - "days": "Dni", - "week": "Tydzień", - "weeks": "Tygodnie", - "month": "Miesiąc", - "months": "Miesiące", - "year": "Rok", - "years": "Lata", - "customtime": "Czas niestandardowy", - "nocustombantime": "Najpierw musisz ustawić niestandardową długość bana!", - "permanent": "Permanentny", - "confirm": "Potwierdź", - "playermanagement": "Zarządzanie Graczami", - "servermanagement": "Zarządzanie Serwerem", - "settings": "Ustawienia", - "kickplayer": "Wyrzuć Gracza", - "banplayer": "Zbanuj Gracza", - "reason": "Powód", - "kickreasonguide": "Dodaj powód wyrzucenia.", - "noreason": "Nie określono przyczyny", - "confirmkick": "Potwierdź wyrzucenie", - "confirmkickguide": "~r~~h~UWAGA:~h~~w~ Naciśnięcie potwierdź spowoduje wyrzucenie tego Gracza z określonymi ustawieniami.", - "banreasonguide": "Dodaj powód blokady.", - "banlength": "Długość bana", - "banlengthguide": "Do kiedy powinien zostać zbanowany Gracz?", - "confirmban": "Potwierdź bana", - "confirmbanguide": "~r~~h~UWAGA:~h~~w~ Naciśnięcie potwierdź spowoduje zablokowanie tego gracza z określonymi ustawieniami.", - "mute": "Niemy Gracz", - "muteguide": "~r~~h~NOTE:~h~~w~ Uniemożliwi to graczowi korzystanie z czatu tekstowego", - "adminmutedplayer": "**%s** wyciszył **%s**", - "adminunmutedplayer": "**%s** odciszył **%s**", - "playermuted": "został wyciszony!", - "playerunmuted": "został bez wyciszenia!", - "playermute": "Jesteś wyciszony!", - "spectateplayer": "Obserwuj gracza", - "teleportplayer": "Teleport", - "teleporttoplayer": "do gracza", - "teleportplayertome": "gracza do mnie", - "slapplayer": "Uderz Gracza", - "setplayerfrozen": "Unieruchom gracza", - "allplayers": "Wszyscy gracze", - "teleporttome": "Teleportuj do mnie", - "teleporttomeguide": "~r~~h~UWAGA:~h~~w~ Spowoduje to teleportację do ciebie ~h~wszystkich ~h~ graczy.", - "setgametype": "Ustaw Tryb Gry", - "setgametypeguide": "~r~~h~UWAGA:~h~~w~ Spowoduje to ustawienie Typu Gry zgodnie z listą serwerów.", - "setmapname": "Ustaw Nazwę Mapy", - "setmapnameguide": "~r~~h~UWAGA:~h~~w~ Spowoduje to ustawienie nazwy mapy zgodnie z listą serwerów.", - "startresourcebyname": "Uruchom zasoby według nazwy", - "startresourcebynameguide": "~r~~h~UWAGA:~h~~w~ Spowoduje to uruchomienie zasobu zainstalowanego na serwerze.", - "stopresourcebyname": "Zatrzymaj zasoby według nazwy", - "stopresourcebynameguide": "~r~~h~UWAGA:~h~~w~ Spowoduje to zatrzymanie zasobu zainstalowanego na serwerze.", - "badidea": "Nie rób tego, proszę.", - "viewbanlist": "Pokaż listę banów", - "unbanplayer": "Odblokuj Gracza", - "unbanplayerguide": "~r~~h~UWAGA:~h~~w~ Naciśnięcie Potwierdź spowoduje odblokowanie tego Gracza.", - "banlistshowtype": "~h~Lista blokad:~h~ pokaż Typ", - "unbanreasons": "Powód", - "unbanlicenses": "Licencje", - "banlistshowtypeguide": "Przełącz między przyczynami lub identyfikatorami banu w menu 'Odblokuj Gracza'.\nWymaga ponownego otwarcia.", - "refreshbanlist": "Odśwież banlistę", - "refreshbanlistguide": "To odświeża banlistę w menu 'Odblokuj Gracza'.\nWymaga ponownego otwarcia.", - "refreshpermissions": "Odśwież uprawnienia", - "refreshpermissionsguide": "To odświeża twoje obecne uprawnienia.\nWymaga ponownego otwarcia.", - "godmodedetected": "Nieśmiertelność: ~r~Wykryta~w~", - "godmodenotdetected": "Nieśmiertelność: ~g~Nie Wykryta~w~", - "antiragdoll": "~r~Anty-Przewracanie~w~", - "health": "Zdrowie", - "armor": "Opancerzenie", - "wantedlevel": "Poziom poszukiwań", - "exitspectator": "Naciśnij E, aby wyjść z trybu obserwatora", - - "allowlist": "Nie ma Cię na białej liście tego serwera", - "checkingallowlist": "Sprawdzanie listy dostępu..", - "bannedjoin": "Zostałeś zbanowany na tym serwerze, \nPowód: %s, Czas trwania: %s, Ban ID: %s", - "kicked": "Wyrzucony przez %s, Powód: %s", - "banned": "Zostałeś zbanowany na tym serwerze, Powód: %s, Czas trwania: %s", - "reasonadd": " ( Gracz: %s ), Zablokowany przez: %s", - "nongiven": "Nie podano", - "announcement": "Ogłoszenie", - "announcementguide": "Wyślij ogłoszenie do wszystkich graczy", - "adminannouncement": "**%s** wysłał ogłoszenie: **%s**", - "playernotfound": "Nie można znaleźć gracza.", - "done": "Zrobione!", - "adminkickedplayer": "**%s** wyrzucił **%s**, Powód: %s", - "adminbannedplayer": "**%s** zbanował **%s**, Powód: %s, Czas trwania: %s, ID: %s", - "adminunbannedplayer": "**%s** odblokował **%s** [ %s ]", - "adminslappedplayer": "**%s** uderzył **%s** za **%s HP**", - - "adminfrozeplayer": "**%s** zamrożony **%s**.", - "adminunfrozeplayer": "**%s** odmrożony **%s**.", - - "left": "Lewa", - "middle": "Środek", - "right": "Prawa", - "menuOrientation": "Orientacja menu", - "menuOrientationguide": "Ustaw orientację menu ( Lewa, Prawa lub Środek ) \nWymaga restartu gry.", - "menuOffset": "Przesunięcie menu", - "menuOffsetguide": "Ustaw Przesunięcie menu\nWymaga Ponownego otwarcie menu.", - "resetmenuOffset": "Resetuj przesunięcie menu ", - "takescreenshot": "Zrzut ekranu", - "admintookscreenshot": "**%s** zrobił zrzut ekranu **%s**'s Zrzut ekranu: %s", - "screenshotinprogress": "Zrzut ekranu jest w trakcie tworzenia, zaczekaj!", - "screenshotlink": "Zrzut ekranu jest dostępny tutaj: %s", - "anonymous": "Anonimowy Admin", - "anonymousguide": "Ukrywa twój nick kiedy wykonujesz akcje Administracyjne.", - "adminofflinebannedplayer": "**%s** zbanowany offline **%s**, Powód: %s, Ban wygaśnie: %s", - "cachedplayers": "Ostatni gracze", - "refreshcachedplayers": "Odśwież ostatnich graczy", - "refreshcachedplayersguide": "Odśwież listę zapisanych graczy, wymaga restartu menu.", - "adminrequestedofflineinfo": "**%s** zarządał informacji gracza Offline **%s**", - "nextpage": "Następna strona»", - "firstpage": "««Pierwsza strona", - "lastpage": "Ostatnia strona»»", - "previouspage": "«Poprzednia strona", - "playercalledforadmin": "Użytkownik %s wzywa Administratora!\nPowód: %s\nID: %s\n", - "playerreportedplayer": "Użytkownik %s zgłosił gracza!\n%s, Powód: %s\nReport %s/%s\nID: %s\n", - "successfullyreported": "Pomyślnie zgłoszono gracza!", - "reportbantext": "Zgłosił %s Graczy w krótkim czasie.", - "alreadyreported": "Już zgłosiłeś tego gracza!", - "reportedusageerror": "Błąd! Użyj: /report nick powód", - "admincalled": "Pomyślnie zawiadomiono Administratora!", - - "spectatedplayer": "**%s** ogląda **%s**", - "teleportedtoplayer": "**%s** teleportowany do **%s**", - "searchbans": "Wyszukaj bana", - "searchbansfail": "Nie znaleziono bana z wprowadzonymi parametrami.", - "identifier": "Identyfikator %s", - "warnplayer": "Ostrzeż gracza", - "warnreasonguide": "Dodaj powód do ostrzeżenia.", - "confirmwarn": "Potwierdź ostrzeżenie", - "confirmwarnguide": "~r~~h~UWAGA:~h~~w~ Naciśnięcie Potwierdź spowoduje ostrzeżenie użytkownika z określonymi ustawieniami.", - "warnedtitle": "Ostrzeżenie", - "warnedby": "ostrzeżony przez", - "warndismiss": "Przytrzymaj spację przez 10 sekund aby odrzucić powiadomienie.", - "warned": "Zostałeś ostrzeżony, Powód: %s, Ostrzeżenie %s/%s", - "warnkicked": "Zostałeś wyrzucony z serwera, z powodu otrzymania zbyt wielu ostrzeżeń..", - "warnbanned": "Ostrzeżono cię zbyt wiele razy.", - "adminwarnedplayer": "**%s** ostrzegł **%s**, Powód: %s, Ostrzeżenie %s/%s", - - "searchuser": "Wyszukaj użytkownika", - "searchuserguide": "Znajdź gracza, używając jego nazwy użytkownika lub identyfikatora", - - "setconvar": "Ustaw convar-a", - "setconvarguide": "~r~UWAGA: Jeżeli nie wiesz co robisz, nie dotykaj tego!~w~\nZmienia convar-a", - "adminchangedconvar": "**%s** zmieniono convar-a **%s** na **%s**", - "adminstartedresource": "**%s** uruchomiono skrypt **%s**", - "adminstoppedresource": "**%s** zatrzymano skrypt **%s**", - - "convarname": "Nazwa Convar-a", - "convarvalue": "Wartość Convar-a", - - "cleanarea": "Wyczyść lokalizację", - "cleanareaguide": "Usuń wszystkie pobliskie obiekty wybranego typu", - "cars": "Pojazdy", - "peds": "Pedy", - "props": "Propy", - "cleaningcar": "Usuwanie pojazdu %s", - "cleaningped": "Usuwanie peda %s", - "cleaningprop": "Usunięto obiekt %s", - "finishedcleaning": "Zakończono czyszczenie %s", - "admincleanedup": "**%s** Wszystkie **%s** w **%s** obszarze zostały usunięte.", - "radius": "Zasięg", - "type": "Rodzaj", - "deepclean":"Głębokie Czyszczenie", - "deepcleanguide":"Usuwa również podmioty po stronie klienta.", - - "forceeasteregg": "Wymuś Easter Egg-a", - - "permissioneditor": "Edytor uprawnień", - "permissioneditorguide": "Edytuje uprawnienia na serwerze, zapisuje do ~g~easyadmin_permissions.cfg~w~", - - "aces": "Uprawnienia ACE", - "permission": "Uprawnienia", - "principals": "Principale", - "addace": "~g~Dodaj uprawnienia ACE", - "addaceguide": "Masz możliwość edytowania tego przed zapisaniem.", - "addprincipal": "~g~Dodaj principala", - "group": "Grupa", - "entergroup": "Wprowadź nazwę grupy (np. group.admin)", - "enterperm": "Wprowadź nazwę uprawnienia (np. easyadmin.player.kick)", - "state": "Stan", - "stateguide": "Odmawianie uprawnień nie działa poprawnie, zezwalaj tylko na uprawnienia, które chcesz, aby gracz posiadał.", - "location": "Lokalizacja", - "locationguide": "Ta wartość nie może być zmieniona.", - "principal": "Principal", - "enterprincipal": "Wprowadź identyfikator (np. identifier.steam:aabbccddeefff)", - "deletepermission": "~r~Usuń uprawnienie", - "deletepermissionguide": "~r~Ostrzeżenie! To całkowicie usunie uprawnienia!", - "deleteprincipal": "~r~Usuń principala", - "deleteprincipalguide": "~r~Ostrzeżenie! Spowoduje to całkowite usunięcie principala!", - - "itemdeleted": "~r~Ten element został usunięty.", - "savechanges": "~g~Zapisz zmiany", - "savechangesguide": "~r~Ostrzeżenie! ~w~To nie może być cofnięte!", - "admineditedpermissions": "**%s** edytował uprawnienia serwera.", - - "reportviewer": "Otwórz zgłoszenia", - "call": "Wezwanie", - "report": "Zgłoszenie", - "open": "Otwarty", - "reporter": "Zgłaszający", - "reported": "Zgłoszony", - "closereport": "Zamknij zgłoszenie", - "closesimilarreports": "~r~Zamknij wszystkie podobne zgłoszenia~w~", - "closesimilarreportsguide": "Spróbuję znaleźć i usunąć raporty wykonane przez tego samego użytkownika.", - "adminclosedreport": "**%s** zamknięte zgłoszenie #**%s**", - "adminclaimedreport": "**%s** claimed Report #**%s**", - "entertoopen": "Wybierz, aby wyświetlić opcje dla tego gracza.", - "refreshreports": "Odśwież zgłoszenia", - "refreshreportsguide": "Pokazuje nowe zgłoszenia, jeśli są jakieś od ostatniego otwarcia menu.", - "waitbeforeusingagain": "Musisz poczekać przed użyciem tego ponownie!", - "invalidreport": "Musisz podać powód!", - "claimreport":"Zajmij zgłoszenie", - "claimedby":"Zajęty przez", - "reportalreadyclaimed": "To zgłoszenie zostało już zajęte!", - "notification":"Powiadomienie", - "deferral":"Sprawdzanie listy banów, proszę czekać.. (%s%%)", - "savebanchanges":"Zapisz zmiany", - "savebanguide":"~r~~h~UWAGA:~h~~w~ Naciśnięcie przycisku Potwierdź zaktualizuje ten ban na serwerze.", - "adminimmune":"Nie masz uprawnień aby wykonać tą akcje, docelowy gracz jest nietykalny.", - "teleportmeback":"Teleportuj mnie z powrotem", - "teleportplayerback":"Teleportuj gracza z powrotem", - "teleportintoclosestvehicle":"do najbliższego pojazdu", - - "copiedtoclipboard":"Tekst skopiowany do schowka!", - - "spectatevehicleseatoccupied":"Miejsce w pojeździe, na którym byłeś, jest teraz zajęte.", - "spectatenovehiclefound":"Nie można już znaleźć pojazdu, w którym byłeś.", - "screenreader":"Narrator (TTS)", - "screenreaderguide":"Włącz narratora dla GUI" - -}] diff --git a/server-data/resources/[esx]/EasyAdmin/package.json b/server-data/resources/[esx]/EasyAdmin/package.json deleted file mode 100644 index 8cf1d3c80..000000000 --- a/server-data/resources/[esx]/EasyAdmin/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "dependencies": { - "@discordjs/rest": "1.6.0", - "@discordjs/core": "0.6.0", - "@discordjs/formatters": "0.3.1", - "@discordjs/collection": "1.5.1", - "@discordjs/builders": "1.6.3", - "@discordjs/util": "0.3.1", - "@discordjs/ws": "0.8.1", - "ascii-table": "0.0.9", - "discord-api-types": "0.37.51", - "discord.js": "14.12.1", - "erlpack": "0.1.4", - "juration": "0.1.1", - "pretty-ms": "7.0.1", - "sprintf-js": "1.1.2", - "zlib-sync": "0.1.8" - }, - "devDependencies": { - "eslint": "8.46.0" - }, - "resolutions": { - "@discordjs/rest": "1.6.0", - "@discordjs/core": "0.6.0", - "@discordjs/formatters": "0.3.1", - "@discordjs/collection": "1.5.1", - "@discordjs/builders": "1.6.3", - "@discordjs/util": "0.3.1", - "@discordjs/ws": "0.8.1" - } -} diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/.gitkeep b/server-data/resources/[esx]/EasyAdmin/plugins/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/eadiag/eadiag_server.lua b/server-data/resources/[esx]/EasyAdmin/plugins/eadiag/eadiag_server.lua deleted file mode 100644 index e79da1f49..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/eadiag/eadiag_server.lua +++ /dev/null @@ -1,155 +0,0 @@ -Citizen.CreateThread(function() - local diagActive = false - RegisterCommand("eaDiag", function(source, args, rawCommand) - if diagActive then - PrintDebugMessage("eaDiag is still running, please wait, or report any errors!", 1) - return false - end - - if DoesPlayerHavePermission(source, "server") then - diagActive = true - - PrintDebugMessage("eaDiag triggered, this may take a while, please do not restart EasyAdmin in the meantime.^7\n", 1) - - local supportData = {} - - PrintDebugMessage("Collecting EasyAdmin Config....^7\n", 1) - - local version,ismaster = GetVersion() - supportData.config = { - gamename = GetConvar("gamename", "not-rdr3"), - version = version, - ismaster = tostring(ismaster), - ea_moderationNotification = GetConvar("ea_moderationNotification", "false"), - ea_screenshoturl = GetConvar("ea_screenshoturl", 'https://wew.wtf/upload.php'), - onesync = GetConvar("onesync", "off"), - steam_webApiKey = GetConvar("steam_webApiKey", ""), - ea_LanguageName = GetConvar("ea_LanguageName", "en"), - ea_enableDebugging = GetConvar("ea_enableDebugging", "false"), - ea_logLevel = GetConvar("ea_logLevel", 1), - ea_minIdentifierMatches = GetConvarInt("ea_minIdentifierMatches", 2), - ea_defaultKey = GetConvar("ea_defaultKey", "none"), - ea_alwaysShowButtons = GetConvar("ea_alwaysShowButtons", "false"), - ea_enableCallAdminCommand = GetConvar("ea_enableCallAdminCommand", "true"), - ea_enableReportCommand = GetConvar("ea_enableReportCommand", "true"), - ea_defaultMinReports = GetConvarInt("ea_defaultMinReports", 3), - ea_MinReportPlayers = GetConvarInt("ea_MinReportPlayers", 12), - ea_MinReportModifierEnabled = GetConvar("ea_MinReportModifierEnabled", "true"), - ea_ReportBanTime = GetConvarInt("ea_ReportBanTime", 86400), - ea_custombanlist = GetConvar("ea_custombanlist", "false"), - ea_maxWarnings = GetConvarInt("ea_maxWarnings", 3), - ea_warnAction = GetConvar("ea_warnAction", "kick"), - ea_warningBanTime = GetConvarInt("ea_warningBanTime", 604800), - ea_enableSplash = GetConvar("ea_enableSplash", "true"), - ea_playerCacheExpiryTime = GetConvarInt("ea_playerCacheExpiryTime", 900), - ea_chatReminderTime = GetConvarInt("ea_chatReminderTime", 0), - ea_backupFrequency = GetConvarInt("ea_backupFrequency", 72), - ea_maxBackupCount = GetConvarInt("ea_maxBackupCount", 10), - ea_useTokenIdentifiers = GetConvar("ea_useTokenIdentifiers", "true"), - } - - for i,v in pairs(supportData.config) do - PrintDebugMessage(i.." = "..v, 4) - end - - Wait(500) - - PrintDebugMessage("Collecting Server Config....^7\n", 1) - - local path = GetResourcePath(GetCurrentResourceName()) - local occurance = string.find(path, "/resources") - local path = string.reverse(string.sub(string.reverse(path), -occurance)) - - local servercfg = io.open(path.."server.cfg") - if servercfg then - supportData.serverconfig = servercfg:read("*a") - servercfg:close() - - else - PrintDebugMessage("Could not find your server.cfg file, it should be called `server.cfg` in the parent folder of `resources`, otherwise EasyAdmin cannot read it, this report may be incomplete.\n", 1) - end - - PrintDebugMessage("Collecting easyadmin_permissions.cfg....^7\n", 1) - local permissions = io.open(path.."easyadmin_permissions.cfg") - if permissions then - supportData.serverconfig = supportData.serverconfig.."\n#### The following are the contents of the easyadmin_permissions.cfg ####\n"..permissions:read("*a") - permissions:close() - else - PrintDebugMessage("Could not find your easyadmin_permissions.cfg file, it should be called `easyadmin_permissions.cfg` in the parent folder of `resources`, otherwise EasyAdmin cannot read it, this report may be incomplete.\n", 1) - end - - PrintDebugMessage("Collecting Players....^7\n", 1) - - local players = {} - for i, player in pairs(GetPlayers()) do - players[player] = GetPlayerIdentifiers(player) - if IsPlayerAdmin(player) then - PrintDebugMessage(GetPlayerName(player).." is an Admin.", 1) - end - end - - local lines = string.split(supportData.serverconfig, "\n") - - for i, line in pairs(lines) do - if string.find(line, "add_ace group.admin easyadmin allow") then - adminAllowed = true - end - - if string.find(line, "add_principal identifier.steam") then - local steamid = string.match(line, "add_principal identifier.steam:(.*) group") - if not string.match(steamid,"%a") then - PrintDebugMessage("Please double check the following line: \n"..line.."\n\nthe steamid might be in decimal format, it should be hexadecimal.\n", 1) - end - end - - if string.find(line, "add_ace group.") then - if string.find(line, "deny") then - PrintDebugMessage("The following line is denying permissions, this WILL cause problems, only allow permissions you want to allow! \n"..line.."\n", 1) - end - end - - end - - - - - if supportData.config.steam_webApiKey == "" then - PrintDebugMessage("POSSIBLE ISSUE: steam_webApiKey is not defined! Steam Identifiers will not work.\n", 1) - end - - if supportData.config.ea_custombanlist == "true" then - PrintDebugMessage("POSSIBLE ISSUE: Custom Banlist is enabled, this is no longer supported.\n", 1) - end - - if supportData.config.ea_minIdentifierMatches <= 1 then - PrintDebugMessage("POSSIBLE ISSUE: ea_minIdentifierMatches should never be less than 1.\n", 1) - end - - if type(supportData.config.ea_defaultKey) == "number" then - PrintDebugMessage("POSSIBLE ISSUE: ea_defaultKey should be a Key, not a Control id.\n", 1) - end - - if supportData.config.ismaster then - PrintDebugMessage("EasyAdmin isn't using the latest stable release, this may or may not be an issue.\n", 1) - end - - if updateAvailable then - PrintDebugMessage("POSSIBLE ISSUE: EasyAdmin is outdated, you should update ASAP.\n", 1) - end - - if not adminAllowed then - PrintDebugMessage("POSSIBLE ISSUE: 'add_ace group.admin easyadmin allow' is missing \n", 1) - end - - PrintDebugMessage("eaDiag Finished.", 1) - diagActive=false - - - - - end - end, false) - - - -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/example_client.lua b/server-data/resources/[esx]/EasyAdmin/plugins/example_client.lua deleted file mode 100644 index 601b9c01f..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/example_client.lua +++ /dev/null @@ -1,58 +0,0 @@ - --- EasyAdmin Plugin Example, this allows injecting new UI Elements directly into EasyAdmin Menus, see NativeUILua Docs on Guides how to add new Items, below is an example code. - -local somevalue = false - --- functions MUST be prefixed with local! - -local function playerOption(playerId) - local thisItem = NativeUI.CreateItem("Example Item","Player ID is "..playerId) -- create our new item - thisPlayer:AddItem(thisItem) -- thisPlayer is global. - thisItem.Activated = function(ParentMenu,SelectedItem) - -- enter your clientside code here, this will be run once the button has been activated. - somevalue = true -- set some value we want to undo once the menu closes. - - end - - if DoesPlayerHavePermission(-1, "player.kick") then -- you can also check if a user has a specific Permission. - local thisExampleMenu = _menuPool:AddSubMenu(thisPlayer,"Example Submenu","",true) -- Submenus work, too! - thisExampleMenu:SetMenuWidthOffset(menuWidth) - - local thisItem = NativeUI.CreateItem("Example Submenu Item","") - thisExampleMenu:AddItem(thisItem) -- Items dont require a trigger. - - end -end - -local function mainMenu() - error("You have the example plugin enabled, this is only meant to be used as a template for new plugins.") -end - -local function cachedMenu() -end - -local function serverMenu() -end - -local function settingsMenu() -end - -local function menuRemoved() - somevalue = false -- reset our value :) -end - - -local pluginData = { - name = "Demo", - functions = { - mainMenu = mainMenu, - playerMenu = playerOption, - cachedMenu = cachedMenu, - serverMenu = serverMenu, - settingsMenu = settingsMenu, - menuRemoved = menuRemoved, - } -} - --- uncomment to enable plugin --- addPlugin(pluginData) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/example_server.lua b/server-data/resources/[esx]/EasyAdmin/plugins/example_server.lua deleted file mode 100644 index f2eacb23d..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/example_server.lua +++ /dev/null @@ -1,8 +0,0 @@ --- EasyAdmin Plugin Example, you can run any server-code here, below is an example that shows this functionality - -if false == false then return end - -function foo(bar) - return bar.." baz" -end -AddEventHandler("EasyAdmin:foo", foo) diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/example_shared.lua b/server-data/resources/[esx]/EasyAdmin/plugins/example_shared.lua deleted file mode 100644 index 230df7fd5..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/example_shared.lua +++ /dev/null @@ -1,8 +0,0 @@ - ---[[ This code is disabled as it's an example. - -Citizen.CreateThread(function() -- needs to be in a thread. - permissions["plugin.exmaple"] = false -- adds "easyadmin.plugin.example" Permission which can be used from both the clientside and server side to check for permissions. -end - -]] diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/notifications/mythic-notify_client.lua b/server-data/resources/[esx]/EasyAdmin/plugins/notifications/mythic-notify_client.lua deleted file mode 100644 index 606cbbfff..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/notifications/mythic-notify_client.lua +++ /dev/null @@ -1,20 +0,0 @@ --- --- This is an example Plugin to pass EasyAdmin's Internal events to an External Resource as a Notification, the reader is expected to form their own functions from this, editing this code is *NOT* recommended, --- as updates will overwrite it, instead, make your own plugin from this example (copying the file and renaming it is enough.) --- the end result will look like this: https://blumlaut.me/s/H9pHGsXgjAFeHDP/preview --- --- Feel free to make Pull Requests to add aditional features for this, this is merely an example of whats possible. --- - --- this bit of code tells EasyAdmin to not draw the V Notification. -AddEventHandler("EasyAdmin:receivedNotification", function() - if GetResourceState("mythic-notify") == "started" then - CancelEvent() - end -end) - -AddEventHandler("EasyAdmin:showNotification", function(text, important) - if GetResourceState("mythic-notify") == "started" then - exports['mythic_notify']:DoHudText('inform', text) - end -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/notifications/pNotify_client.lua b/server-data/resources/[esx]/EasyAdmin/plugins/notifications/pNotify_client.lua deleted file mode 100644 index d6781b301..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/notifications/pNotify_client.lua +++ /dev/null @@ -1,20 +0,0 @@ --- --- This is an example Plugin to pass EasyAdmin's Internal events to an External Resource as a Notification, the reader is expected to form their own functions from this, editing this code is *NOT* recommended, --- as updates will overwrite it, instead, make your own plugin from this example (copying the file and renaming it is enough.) --- the end result will look like this: https://blumlaut.me/s/H9pHGsXgjAFeHDP/preview --- --- Feel free to make Pull Requests to add aditional features for this, this is merely an example of whats possible. --- - --- this bit of code tells EasyAdmin to not draw the V Notification. -AddEventHandler("EasyAdmin:receivedNotification", function() - if GetResourceState("pNotify") == "started" then - CancelEvent() - end -end) - -AddEventHandler("EasyAdmin:showNotification", function(text, important) - if GetResourceState("pNotify") == "started" then - exports['pNotify']:SendNotification({layout = "centerLeft", type = "alert", text = text}) - end -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/plugins/notifications/t-notify_client.lua b/server-data/resources/[esx]/EasyAdmin/plugins/notifications/t-notify_client.lua deleted file mode 100644 index 370327135..000000000 --- a/server-data/resources/[esx]/EasyAdmin/plugins/notifications/t-notify_client.lua +++ /dev/null @@ -1,20 +0,0 @@ --- --- This is an example Plugin to pass EasyAdmin's Internal events to an External Resource as a Notification, the reader is expected to form their own functions from this, editing this code is *NOT* recommended, --- as updates will overwrite it, instead, make your own plugin from this example (copying the file and renaming it is enough.) --- the end result will look like this: https://blumlaut.me/s/H9pHGsXgjAFeHDP/preview --- --- Feel free to make Pull Requests to add aditional features for this, this is merely an example of whats possible. --- - --- this bit of code tells EasyAdmin to not draw the V Notification. -AddEventHandler("EasyAdmin:receivedNotification", function() - if GetResourceState("t-notify") == "started" then - CancelEvent() - end -end) - -AddEventHandler("EasyAdmin:showNotification", function(text, important) - if GetResourceState("t-notify") == "started" then - exports['t-notify']:Alert({style = "error", message = text}) - end -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/admin_server.lua b/server-data/resources/[esx]/EasyAdmin/server/admin_server.lua deleted file mode 100644 index 53a837fe9..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/admin_server.lua +++ /dev/null @@ -1,976 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - --- Chat Reminder Code -function sendRandomReminder() - reminderTime = GetConvarInt("ea_chatReminderTime", 0) - if reminderTime ~= 0 and #ChatReminders > 0 then - local reminder = ChatReminders[ math.random( #ChatReminders ) ] -- select random reminder from table - local adminNames = "" - local t = {} - for i,_ in pairs(OnlineAdmins) do - table.insert(t, getName(i)) - end - for i,n in ipairs(t) do - if i == 1 then - adminNames = n - elseif i == #t then - adminNames = adminNames.." "..n - else - adminNames = adminNames.." "..n.."," - end - end - t=nil - - if adminNames == "" then adminNames = "@admins" end -- if no admins are online just print @admins - reminder = string.gsub(reminder, "@admins", adminNames) - - reminder = string.gsub(reminder, "@bancount", #blacklist) - - reminder = string.gsub(reminder, "@time", os.date("%X", os.time())) - reminder = string.gsub(reminder, "@date", os.date("%x", os.time())) - TriggerClientEvent("chat:addMessage", -1, { args = { "EasyAdmin", reminder } }) - end -end - -function announce(reason) - if reason then - TriggerClientEvent("EasyAdmin:showNotification", -1, "[" .. GetLocalisedText("announcement") .. "] " .. reason) - return true - else - return false - end -end - -exports('announce', announce) - -Citizen.CreateThread(function() - --Wait(10000) - reminderTime = GetConvarInt("ea_chatReminderTime", 0) - if reminderTime ~= 0 then - while true do - Wait(reminderTime*60000) - sendRandomReminder() - end - else - while true do - Wait(20000) - sendRandomReminder() -- check for changes in the convar - end - end -end) - - -function getAllPlayerIdentifiers(playerId) --Gets all info that could identify a player - local identifiers = GetPlayerIdentifiers(playerId) - local tokens = {} - if GetConvar("ea_useTokenIdentifiers", "true") == "true" then - if not GetNumPlayerTokens or not GetPlayerToken then - PrintDebugMessage("Server Version is below artifact 3335, disabling Token identifiers, please consider updating your FXServer!", 1) - SetConvar("ea_useTokenIdentifiers", "false") - PrintDebugMessage("Set ea_useTokenIdentifiers to false for this session.", 1) - return identifiers - end - for i=0,GetNumPlayerTokens(playerId) do - table.insert(tokens, GetPlayerToken(playerId, i)) - end - end - return mergeTables(identifiers, tokens) -end -exports('getAllPlayerIdentifiers', getAllPlayerIdentifiers) - - -function checkForChangedIdentifiers(playerIds, bannedIds) - local unbannedIds = {} - for _,playerId in pairs(playerIds) do - local thisIdBanned = false - for _,bannedId in pairs(bannedIds) do - if playerId == bannedId then - thisIdBanned = true - end - end - if not thisIdBanned then --They have a new/changed identifier - table.insert(unbannedIds, playerId) - end - end - return unbannedIds -end - - -AddEventHandler('playerDropped', function (reason) - if OnlineAdmins[source] then - OnlineAdmins[source] = nil - end - if FrozenPlayers[source] then - FrozenPlayers[source] = nil - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:SetPlayerFrozen", i, 1000, source, nil) - end - end - if MutedPlayers[source] then - MutedPlayers[source] = nil - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:SetPlayerMuted", i, 1000, source, nil) - end - end - PrintDebugMessage(source.." disconnected.", 4) -end) - - -RegisterServerEvent("EasyAdmin:GetInfinityPlayerList", function() - PrintDebugMessage(getName(source, true).." requested Playerlist.", 4) - if IsPlayerAdmin(source) then - local l = {} - local players = GetPlayers() - - for i, player in pairs(players) do - local player = tonumber(player) - cachePlayer(player) - for i, cached in pairs(CachedPlayers) do - if (cached.id == player) then - local pData = {id = cached.id, name = cached.name, immune = cached.immune} - for i, v in pairs(cached.identifiers) do - if v == "discord:178889658128793600" then - pData.developer = true - elseif v == "discord:736521574383091722" --[[ Jaccosf ]] or v == "discord:1001065851790839828" --[[ robbybaseplate ]] or v == "discord:840695262460641311" --[[ Knight ]] or v == "discord:270731163822325770" --[[ Skypo ]] or v == "discord:186980021850734592" --[[ coleminer0112 ]] then - pData.contributor = true - end - end - table.insert(l, pData) - end - end - end - - -- each player is more or less 2000bytes big. - TriggerLatentClientEvent("EasyAdmin:GetInfinityPlayerList", source, 200000, l) - end -end) - -function GetOnlineAdmins() - return OnlineAdmins -end -exports('GetOnlineAdmins', GetOnlineAdmins) - -function IsPlayerAdmin(pid) - return OnlineAdmins[pid] -end -exports('IsPlayerAdmin', IsPlayerAdmin) - - -Citizen.CreateThread(function() - - if not CachedPlayers or GetVersion() == nil then - print("^7--------------------------------------------------------------") - print("^1EasyAdmin self-test failed! Your EasyAdmin **will not work**, likely you edited some files and broke EasyAdmin in the progress, please reinstall EasyAdmin.") - print("^7--------------------------------------------------------------") - return - end - - if GetConvar("gamename", "gta5") == "rdr3" then - RedM = true - PrintDebugMessage("Starting in rdr3 Mode.", 4) - else - RedM = false - PrintDebugMessage("Starting in gta5 Mode.", 4) - end - - AnonymousAdmins = {} - - loadLanguageStrings() - - moderationNotification = GetConvar("ea_moderationNotification", "false") - reportNotification = GetConvar("ea_reportNotification", "false") - detailNotification = GetConvar("ea_detailNotification", "false") - minimumMatchingIdentifierCount = GetConvarInt("ea_minIdentifierMatches", 2) - - - RegisterServerEvent('EasyAdmin:amiadmin', function() - local source = source - - cachePlayer(source) -- this will do nothing if player is already cached. - - if CachedPlayers[source].lastPermRequest and CachedPlayers[source].lastPermRequest+10 > os.time() then - PrintDebugMessage(getName(source).." hit Permission Check Ratelimit! "..CachedPlayers[source].lastPermRequest+10-os.time().." seconds left.", 3) - return - end - - CachedPlayers[source].lastPermRequest = os.time() - - local identifiers = getAllPlayerIdentifiers(source) - local perms = {} - for perm,val in pairs(permissions) do - local thisPerm = DoesPlayerHavePermission(source, perm) - if perm == "player.screenshot" and not screenshots then - thisPerm = false - end - if string.find(perm, "server.permissions") and disablePermissionEditor then - thisPerm = false - end - --if (perm == "teleport" or perm == "spectate") and infinity then - --if (perm == "spectate") and infinity then - -- thisPerm = false - --end - if thisPerm == true then - OnlineAdmins[source] = true - end - perms[perm] = thisPerm - PrintDebugMessage("Processed Perm "..perm.." for "..getName(source, true)..", result: "..tostring(thisPerm), 3) - end - - TriggerLatentClientEvent("EasyAdmin:adminresponse", source, 10000, perms) - TriggerClientEvent('chat:addSuggestion', source, '/easyadmin', "EasyAdmin Menu", {{name="report or player id", help="[Optional] Report or Player ID"}}) - TriggerClientEvent('chat:addSuggestion', source, '/ea', "EasyAdmin Menu", {{name="report or player id", help="[Optional] Report or Player ID"}}) - - if GetConvar("ea_enableReportCommand", "true") == "true" then - TriggerClientEvent('chat:addSuggestion', source, '/'..GetConvar("ea_reportCommandName", "report"), "Report player", {{name='player', help="player name / id"}, {name='reason', help="Reason"}}) - end - - if GetConvar("ea_enableCallAdminCommand", "true") == "true" then - TriggerClientEvent('chat:addSuggestion', source, '/'..GetConvar("ea_callAdminCommandName", "calladmin"), "Call Admin", {{name='reason', help="Reason"}}) - end - - - - if RedM then - -- give player the right settings to work with - local key = GetConvar("ea_defaultKey", "none") - - TriggerClientEvent("EasyAdmin:SetSetting", source, "button", key) - end - - if GetConvar("ea_alwaysShowButtons", "false") == "true" then - TriggerClientEvent("EasyAdmin:SetSetting", source, "forceShowGUIButtons", true) - else - TriggerClientEvent("EasyAdmin:SetSetting", source, "forceShowGUIButtons", false) - end - if updateAvailable then - TriggerClientEvent("EasyAdmin:SetSetting", source, "updateAvailable", updateAvailable) - end - - -- if you remove this code then you're a killjoy, can't we have nice things? just once? it's not like this changes the whole admin menu or how it behaves, its a single subtitle. - if os.date("%d/%m") == "22/08" then - local age = tonumber(os.date("%Y"))-2017 local ordinal = "th" last_digit = age % 10 if last_digit == 1 and age ~= 11 then ordinal = 'st' elseif last_digit == 2 and age ~= 12 then ordinal = 'nd' elseif last_digit == 3 and age ~= 13 then ordinal = 'rd' end - TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeTitle", "~b~Today is EasyAdmin's "..age..""..ordinal.." birthday! :)") - elseif os.date("%m") == "06" and (tonumber(os.date("%d")) >= 1 and tonumber(os.date("%d")) <= 14) then - TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeLogo", "pride") - elseif os.date("%m") == "04" and os.date("%d") == "01" then - TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeLogo", "logo-hardadmin") - TriggerClientEvent("EasyAdmin:SetSetting", source, "alternativeBanner", "banner-hardadmin") - end - - if (infinity) then - TriggerClientEvent("EasyAdmin:SetSetting", source, "infinity", true) - end - - TriggerLatentClientEvent("EasyAdmin:fillShortcuts", source, 10000, MessageShortcuts) - - TriggerLatentClientEvent("EasyAdmin:SetLanguage", source, 10000, strings) - - end) - - RegisterServerEvent("EasyAdmin:kickPlayer", function(playerId,reason) - if DoesPlayerHavePermission(source, "player.kick") and not CachedPlayers[playerId].immune then - reason = formatShortcuts(reason) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(source, false, true), getName(playerId, true, true), reason), "kick", 16711680) - PrintDebugMessage("Kicking Player "..getName(source, true).." for "..reason, 3) - DropPlayer(playerId, string.format(GetLocalisedText("kicked"), getName(source), reason) ) - elseif CachedPlayers[playerId].immune then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) - end - end) - - RegisterServerEvent("EasyAdmin:requestSpectate", function(playerId) - if DoesPlayerHavePermission(source, "player.spectate") then - PrintDebugMessage("Player "..getName(source,true).." Requested Spectate to "..getName(playerId,true), 3) - local tgtCoords = GetEntityCoords(GetPlayerPed(playerId)) - TriggerClientEvent("EasyAdmin:requestSpectate", source, playerId, tgtCoords) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('spectatedplayer'), getName(source, false, true), getName(playerId, true, true)), "spectate", 16777214) - end - end) - - function cleanupArea(type, radius, player) - if not radius then radius = "global" end - if (onesync ~= "off" and onesync ~= "legacy") then - local toDelete = {} - if type == "cars" then - toDelete = GetAllVehicles() - elseif type == "peds" then - toDelete = GetAllPeds() - elseif type == "props" then - toDelete = GetAllObjects() - end - PrintDebugMessage("server-known entities: "..table_to_string(toDelete), 4) - for _,entity in pairs(toDelete) do - PrintDebugMessage("starting deletion for entity "..entity, 4) - if DoesEntityExist(entity) and not (type == "cars" and IsPedAPlayer(GetPedInVehicleSeat(entity, -1))) and not (type == "peds" and IsPedAPlayer(entity)) then - if radius == "global" then - PrintDebugMessage("deleting entity "..entity, 3) - DeleteEntity(entity) - else - local entityCoords = GetEntityCoords(entity) - local playerCoords = GetEntityCoords(GetPlayerPed(player)) - if #(playerCoords - entityCoords) < radius then - PrintDebugMessage("deleting entity "..entity, 3) - DeleteEntity(entity) - end - end - end - end - return true - else - return false - end - end - exports('cleanupArea', cleanupArea) - - RegisterServerEvent("EasyAdmin:requestCleanup", function(type, radius, deep) - local source=source - if DoesPlayerHavePermission(source, "server.cleanup."..type) then - PrintDebugMessage("Player "..getName(source,true).." Requested Cleanup for "..type, 3) - cleanupArea(type, radius, source) - - if deep then - TriggerClientEvent("EasyAdmin:requestCleanup", source, type, radius) - end - TriggerClientEvent("EasyAdmin:showNotification", source, string.format(GetLocalisedText("finishedcleaning"), GetLocalisedText(type))) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('admincleanedup'), getName(source, false, true), type, radius), "cleanup", 16777214) - end - end) - - RegisterServerEvent("EasyAdmin:SetGameType", function(text) - if DoesPlayerHavePermission(source, "server.convars") then - PrintDebugMessage("Player "..getName(source,true).." set Gametype to "..text, 3) - SetGameType(text) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminchangedconvar'), getName(source, false, true), "gametype", text), "settings", 16777214) - end - end) - - RegisterServerEvent("EasyAdmin:SetMapName", function(text) - if DoesPlayerHavePermission(source, "server.convars") then - PrintDebugMessage("Player "..getName(source,true).." set Map Name to "..text, 3) - SetMapName(text) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminchangedconvar'), getName(source, false, true), "mapname", text), "settings", 16777214) - end - end) - - RegisterServerEvent("EasyAdmin:StartResource", function(text) - if DoesPlayerHavePermission(source, "server.resources.start") then - PrintDebugMessage("Player "..getName(source,true).." started Resource "..text, 3) - StartResource(text) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminstartedresource'), getName(source, false, true), text), "settings", 65280) - end - end) - - RegisterServerEvent("EasyAdmin:StopResource", function(text) - if DoesPlayerHavePermission(source, "server.resources.stop") then - PrintDebugMessage("Player "..getName(source,true).." stopped Resource "..text, 3) - StopResource(text) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminstoppedresource'), getName(source, false, true), text), "settings", 16711680) - end - end) - - RegisterServerEvent("EasyAdmin:SetConvar", function(convarname, convarvalue) - if DoesPlayerHavePermission(source, "server.convars") then - PrintDebugMessage("Player "..getName(source,true).." set convar "..convarname.. " to "..convarvalue, 3) - SetConvar(convarname, convarvalue) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminchangedconvar'), getName(source, false, true), convarname, convarvalue), "settings", 16777214) - end - end) - - RegisterServerEvent("EasyAdmin:Announce", function(text) - if DoesPlayerHavePermission(source, "server.announce") then - PrintDebugMessage("Player "..getName(source,true).." sent a announcement: "..text, 3) - announce(text) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText('adminannouncement'), getName(source, false, true), text), "settings", 16777214) - end - end) - - RegisterServerEvent("EasyAdmin:TeleportPlayerToCoords", function(playerId,tgtCoords) - if DoesPlayerHavePermission(source, "player.teleport.single") then - PrintDebugMessage("Player "..getName(source,true).." requsted teleport to "..tgtCoords.x..", "..tgtCoords.y..", "..tgtCoords.z, 3) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - local playerName = getName(playerId, true, true) - if playerId == -1 then - playerName = GetLocalisedText("allplayers") - end - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("teleportedtoplayer"), playerName, getName(source, false, true)), "teleport", 16777214) - TriggerClientEvent("EasyAdmin:TeleportRequest", playerId, false, tgtCoords) - end - end) - - RegisterServerEvent("EasyAdmin:TeleportAdminToPlayer", function(id) - if not CachedPlayers[id].dropped and DoesPlayerHavePermission(source, "player.teleport.single") then - local tgtPed = GetPlayerPed(id) - local tgtCoords = GetEntityCoords(tgtPed) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("teleportedtoplayer"), getName(source, false, true), getName(id, true, true)), "teleport", 16777214) - TriggerClientEvent('EasyAdmin:TeleportRequest', source, id,tgtCoords) - else - PrintDebugMessage('EASYADMIN FAILED TO TELEPORT'..source..' TO ID: '..id, 2) - end - end) - - RegisterServerEvent("EasyAdmin:TeleportPlayerBack", function(id) - if not CachedPlayers[id].dropped and DoesPlayerHavePermission(source, "player.teleport.single") then - TriggerClientEvent('EasyAdmin:TeleportPlayerBack', id) - end - end) - - function slapPlayer(playerId,slapAmount) - if not CachedPlayers[playerId].immune then - TriggerClientEvent("EasyAdmin:SlapPlayer", playerId, slapAmount) - return true - else - return false - end - end - exports('slapPlayer', slapPlayer) - - RegisterServerEvent("EasyAdmin:SlapPlayer", function(playerId,slapAmount) - if DoesPlayerHavePermission(source, "player.slap") and slapPlayer(playerId, slapAmount) then - PrintDebugMessage("Player "..getName(source,true).." slapped "..getName(playerId,true).." for "..slapAmount.." HP", 3) - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminslappedplayer"), getName(source, false, true), getName(playerId, true, true), slapAmount), "slap", 16777214) - elseif CachedPlayers[playerId].immune then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) - end - end) - - function freezePlayer(playerId, toggle) - if not toggle then toggle = not FrozenPlayers[playerId] end - if not CachedPlayers[playerId].immune then - FrozenPlayers[playerId] = (toggle == true or nil) - TriggerClientEvent("EasyAdmin:FreezePlayer", playerId, toggle) - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:SetPlayerFrozen", i, 1000, playerId, (toggle == true or nil)) - end - return true - else - return false - end - end - exports('freezePlayer', freezePlayer) - - RegisterServerEvent("EasyAdmin:FreezePlayer", function(playerId,toggle) - if DoesPlayerHavePermission(source, "player.freeze") and not CachedPlayers[playerId].immune then - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - freezePlayer(playerId, toggle) - if toggle then - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminfrozeplayer"), getName(source, false, true), getName(playerId, true, true)), "freeze", 16777214) - PrintDebugMessage("Player "..getName(source,true).." froze "..getName(playerId,true), 3) - else - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminunfrozeplayer"), getName(source, false, true), getName(playerId, true, true)), "freeze", 16777214) - PrintDebugMessage("Player "..getName(source,true).." unfroze "..getName(playerId,true), 3) - end - elseif CachedPlayers[playerId].immune then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) - end - end) - - scrinprogress = false - - function isScreenshotInProgress() - return scrinprogress - end - exports('isScreenshotInProgress', isScreenshotInProgress) - - RegisterServerEvent("EasyAdmin:TakeScreenshot", function(playerId) - if scrinprogress then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("screenshotinprogress")) - return - end - local src=source - local playerId = playerId - local invokingResource - if GetInvokingResource() and GetInvokingResource() ~= GetCurrentResourceName() then - invokingResource = "`"..GetInvokingResource().."`" - end - - if DoesPlayerHavePermission(source, "player.screenshot") then - scrinprogress = true - thistemporaryevent = RegisterServerEvent("EasyAdmin:TookScreenshot", function(result) - if result == "ERROR" then return false end - - res = matchURL(tostring(result)) - - PrintDebugMessage("Screenshot taken, result:\n "..res, 4) - SendWebhookMessage(moderationNotification, string.format(GetLocalisedText("admintookscreenshot"), invokingResource or getName(src), getName(playerId, true, true), res), "screenshot", 16777214, "Screenshot Captured", res) - TriggerClientEvent('chat:addMessage', src, { template = '', args = { res } }) - TriggerClientEvent("chat:addMessage", src, { args = { "EasyAdmin", string.format(GetLocalisedText("screenshotlink"), res) } }) - PrintDebugMessage("Screenshot for Player "..getName(playerId,true).." done, "..res.." requsted by"..getName(src,true), 3) - scrinprogress = false - RemoveEventHandler(thistemporaryevent) - end) - - TriggerClientEvent("EasyAdmin:CaptureScreenshot", playerId) - local timeoutwait = 0 - repeat - timeoutwait=timeoutwait+1 - Wait(5000) - if timeoutwait == 5 then - RemoveEventHandler(thistemporaryevent) - scrinprogress = false -- cancel screenshot, seems like it failed - PrintDebugMessage("Screenshot timed out", 4) - TriggerClientEvent("EasyAdmin:showNotification", src, "Screenshot Failed!") - end - until not scrinprogress - end - end) - - RegisterServerEvent("EasyAdmin:mutePlayer", function(playerId) - local src = source - if DoesPlayerHavePermission(src,"player.mute") and not CachedPlayers[playerId].immune then - local muted = mutePlayer(playerId, not MutedPlayers[playerId]) - - if muted then - if MutedPlayers[playerId] then - TriggerClientEvent("EasyAdmin:showNotification", src, getName(playerId) .. " " .. GetLocalisedText("playermuted")) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminmutedplayer"), getName(source, false, true), getName(playerId, false, true)), "mute", 16777214) - else - TriggerClientEvent("EasyAdmin:showNotification", src, getName(playerId) .. " " .. GetLocalisedText("playerunmuted")) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminunmutedplayer"), getName(source, false, false), getName(playerId, false, true)), "mute", 16777214) - end - else - -- todo: handle false retval - end - end - end) - - function mutePlayer(playerId, toggle) - if not CachedPlayers[playerId].immune then - if toggle and not MutedPlayers[playerId] then - MutedPlayers[playerId] = true - if MumbleSetPlayerMuted then -- workaround for outdated servers - MumbleSetPlayerMuted(playerId, true) - end - PrintDebugMessage("muted "..getName(playerId,true), 3) - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:SetPlayerMuted", i, 1000, playerId, (MutedPlayers[playerId] == true or nil)) - end - return true - elseif not toggle and MutedPlayers[playerId] then - MutedPlayers[playerId] = nil - if MumbleSetPlayerMuted then -- workaround for outdated servers - MumbleSetPlayerMuted(playerId, false) - end - PrintDebugMessage("unmuted "..getName(playerId,true), 3) - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:SetPlayerMuted", i, 1000, playerId, (MutedPlayers[playerId] == true or nil)) - end - return true - else - return false - end - else - return false - end - end - exports('mutePlayer', mutePlayer) - - RegisterServerEvent("EasyAdmin:SetAnonymous", function(playerId) - if DoesPlayerHavePermission(source, "anon") then - if AnonymousAdmins[source] then - AnonymousAdmins[source] = nil - PrintDebugMessage("Player "..getName(source,true).." un-anoned themself", 3) - else - AnonymousAdmins[source] = true - PrintDebugMessage("Player "..getName(source,true).." anoned themself", 3) - end - end - end) - - -- Very basic function that turns "source" into a useable player name. - function getName(src,anonymousdisabled,identifierenabled) - local identifierPref = GetConvar("ea_logIdentifier", "steam,discord,license") - if identifierPref == "false" then identifierenabled = false end; - local identifiers, identifier = {}, "~No Identifier~" - if (src == 0 or src == "") then - return "Console" - else - if AnonymousAdmins[src] and not anonymousdisabled then - return GetLocalisedText("anonymous") - elseif CachedPlayers[src] and CachedPlayers[src].name then - if CachedPlayers[src].identifiers then - if identifierPref then - -- split identifierPref by comma and find first identifier in CachedPlayers[src].identifiers that starts with the split string - -- this code was written by GitHub Copilot, neat, huh? - for i,v in ipairs(identifierPref:split(",")) do - for i2,v2 in ipairs(CachedPlayers[src].identifiers) do - if string.sub(v2, 1, string.len(v)) == v then - identifier = v2 - break - end - end - if identifier ~= "~No Identifier~" then break end - end - end - end - if identifier:find('discord:') then - identifier = string.gsub(identifier, "discord:", "") - identifier = "<@"..identifier..">" - end - if identifierenabled then - return (string.format("%s [ %s ]", CachedPlayers[src].name, identifier)) - else - return CachedPlayers[src].name - end - elseif (GetPlayerName(src)) then - identifiers = getAllPlayerIdentifiers(src) - if identifierPref then - for i,v in ipairs(identifierPref:split(",")) do - for i2,v2 in ipairs(identifiers) do - if string.sub(v2, 1, string.len(v)) == v then - identifier = v2 - break - end - end - if identifier ~= "~No Identifier~" then break end - end - end - if identifier:find('discord:') then - identifier = string.gsub(identifier, "discord:", "") - identifier = "<@"..identifier..">" - end - if identifierenabled then - return (string.format("%s [ %s ]", GetPlayerName(src), identifier)) - else - return GetPlayerName(src) - end - else - return "Unknown - " .. src - end - end - end - exports('getName', getName) - - RegisterServerEvent("EasyAdmin:warnPlayer", function(id, reason) - local src = source - if DoesPlayerHavePermission(src,"player.warn") and not CachedPlayers[id].immune then - reason = formatShortcuts(reason) - local maxWarnings = GetConvarInt("ea_maxWarnings", 3) - if not WarnedPlayers[id] then - WarnedPlayers[id] = {name = getName(id, true), identifiers = getAllPlayerIdentifiers(id), warns = 0} - end - WarnedPlayers[id].warns = WarnedPlayers[id].warns+1 - TriggerClientEvent('chat:addMessage', id, { - template = '
    {0}
    ', - args = { string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings) }, color = { 255, 255, 255 } - }) - TriggerClientEvent("txAdminClient:warn", id, getName(src), string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), getName(src, false, true), getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) - if WarnedPlayers[id].warns >= maxWarnings then - if GetConvar("ea_warnAction", "kick") == "kick" then - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), getName(src, false, true), getName(id, true, true), reason), "kick", 16711680) - DropPlayer(id, GetLocalisedText("warnkicked")) - WarnedPlayers[id] = nil - elseif GetConvar("ea_warnAction", "kick") == "ban" then - local bannedIdentifiers = CachedPlayers[id].identifiers or getAllPlayerIdentifiers(id) - local bannedUsername = CachedPlayers[id].name or getName(id, true) - local expires = os.time()+GetConvarInt("ea_warningBanTime", 604800) - - reason = GetLocalisedText("warnbanned").. string.format(GetLocalisedText("reasonadd"), CachedPlayers[id].name, getName(source, true) ) - local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires } - updateBlacklist( ban ) - - PrintDebugMessage("Player "..getName(source,true).." warnbanned player "..CachedPlayers[id].name.." for "..reason, 3) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), bannedUsername, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) - DropPlayer(id, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) - WarnedPlayers[id] = nil - - end - end - elseif CachedPlayers[id].immune then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) - end - end) - - function warnPlayerExport(src, id, reason) - if not CachedPlayers[id].immune then - local maxWarnings = GetConvarInt("ea_maxWarnings", 3) - if not WarnedPlayers[id] then - WarnedPlayers[id] = {name = getName(id, true), identifiers = getAllPlayerIdentifiers(id), warns = 0} - end - WarnedPlayers[id].warns = WarnedPlayers[id].warns+1 - TriggerClientEvent('chat:addMessage', id, { - template = '
    {0}
    ', - args = { string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings) }, color = { 255, 255, 255 } - }) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminwarnedplayer"), src, getName(id, true, true), reason, WarnedPlayers[id].warns, maxWarnings), "warn", 16711680) - TriggerClientEvent("txAdminClient:warn", id, src, string.format(GetLocalisedText("warned"), reason, WarnedPlayers[id].warns, maxWarnings), GetLocalisedText("warnedtitle"), GetLocalisedText("warnedby"),GetLocalisedText("warndismiss")) - if WarnedPlayers[id].warns >= maxWarnings then - if GetConvar("ea_warnAction", "kick") == "kick" then - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminkickedplayer"), src, getName(id, true, true), reason), "kick", 16711680) - DropPlayer(id, GetLocalisedText("warnkicked")) - WarnedPlayers[id] = nil - elseif GetConvar("ea_warnAction", "kick") == "ban" then - local expires = os.time()+GetConvarInt("ea_warningBanTime", 604800) - addBanExport(id, reason, formatDateString(expires), src) - WarnedPlayers[id] = nil - end - end - return true - else - return false - end - end - - exports('warnPlayer', warnPlayerExport) - - function getPlayerWarnings(playerId) - if not WarnedPlayers[playerId] then - return 0 - else - return WarnedPlayers[playerId].warns - end - end - exports('getPlayerWarnings', getPlayerWarnings) - - AddEventHandler("EasyAdmin:GetVersion", function(cb) - cb(GetVersion()) - end) - - local chatEventsSupported = false - - pcall(function() -- this will prevent our script from erroring if the exports are missing, also mutes any errors. - if exports.chat.registerMessageHook and exports.chat.registerMode then - chatEventsSupported = true - end - end) - - if chatEventsSupported then - exports.chat:registerMessageHook(function(source, outMessage, hookRef) - if MutedPlayers[source] then - hookRef.cancel() - TriggerClientEvent("EasyAdmin:showNotification", source, getName(source) .. ", " .. GetLocalisedText("playermute")) - end - end) - else - AddEventHandler('chatMessage', function(source, name, msg) - if MutedPlayers[source] then - CancelEvent() - TriggerClientEvent("chat:addMessage", source, { args = { "EasyAdmin", GetLocalisedText("playermute") } }) - TriggerClientEvent("EasyAdmin:showNotification", source, getName(source) .. ", " .. GetLocalisedText("playermute")) - end - end) - end - - if GetConvar("ea_enableChat", "true") == "true" and chatEventsSupported then - exports.chat:registerMode({ - name = "admins", - displayName = "Admin Chat", - color = "#19A2E3", - seObject = "easyadmin.server.chat", - cb = function(source, message, cbs) - cbs.updateMessage({ - template = "^5[ADMIN CHAT]^7" .. ' {}' - }) - - cbs.setSeObject("easyadmin.server.chat") - end - }) - end -end) - -Citizen.CreateThread(function() - function HTTPRequest(url, ...) - local err,response,headers - - PerformHttpRequest(url, function(e,r,h) - err,response,headers = e,r,h - end, ...) - repeat - Wait(10) - until (response) - - return response - end - exports('HTTPRequest', HTTPRequest) -end) - -Citizen.CreateThread(function() - AddEventHandler('playerConnecting', function(playerName, setKickReason, deferrals) - local player = source - local numIds = getAllPlayerIdentifiers(player) - local matchingIdentifierCount = 0 - local matchingIdentifiers = {} - local showProgress = GetConvar("ea_presentDeferral", "true") - - deferrals.defer() - Wait(0) - local deferralText = string.format(GetLocalisedText("deferral"), 0) - if showProgress == "false" then - deferralText = deferralText:sub(1, -6) - end - - deferrals.update(deferralText) - PrintDebugMessage(getName(player).."'s Identifiers:\n "..table_to_string(numIds), 3) - if not blacklist then - print("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - print("EasyAdmin: ^1Failed^7 to load Banlist!\n") - print("EasyAdmin: Please check this error soon, ^1Bans *will not* work!^7\n") - print("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - deferrals.done("\n\nEasyAdmin: A fatal error occured, please contact a Server Administrator to resolve this issue.") - return - end - Wait(0) - - local lastPercentage = 0 - for bi,blacklisted in ipairs(blacklist) do - if showProgress == "true" then - local percentage = math.round(bi/#blacklist*100) - if bi % 12 == 0 and percentage >= lastPercentage+4 then -- only update on every 12th ban - Wait(0) - deferrals.update(string.format(GetLocalisedText("deferral"), percentage)) - lastPercentage = percentage - end - end - for i,theId in ipairs(numIds) do - for ci,identifier in ipairs(blacklisted.identifiers) do - if identifier == theId and matchingIdentifiers[theId] ~= true then - matchingIdentifierCount = matchingIdentifierCount+1 - matchingIdentifiers[theId] = true -- make sure we remember the identifier for later - PrintDebugMessage("IDENTIFIER MATCH! "..identifier.." Required: "..matchingIdentifierCount.."/"..minimumMatchingIdentifierCount, 3) - local notBannedIds = checkForChangedIdentifiers(numIds, blacklisted.identifiers) - if matchingIdentifierCount >= minimumMatchingIdentifierCount then - if #notBannedIds > 0 then - local newBanData = blacklisted - newBanData.identifiers = mergeTables(blacklisted.identifiers, notBannedIds) -- add newly found identifiers to the existing ban - updateBan(blacklisted.banid,newBanData) -- send it off! - end - PrintDebugMessage("Connection of "..getName(player).." Declined, Banned for "..blacklist[bi].reason..", Ban ID: "..blacklist[bi].banid.."\n", 3) - deferrals.done(string.format( GetLocalisedText("bannedjoin"), blacklist[bi].reason, formatDateString(blacklist[bi].expire), blacklist[bi].banid)) - return - end - end - end - end - end - - if GetConvar("ea_enableAllowlist", "false") == "true" then - deferrals.update(GetLocalisedText("checkingallowlist")) - local allowlistAttempts = 0 - local allowlisted = false - repeat - allowlisted = DoesPlayerHavePermission(player, "player.allowlist") - allowlistAttempts = allowlistAttempts+1 - Wait(100) - until (allowlistAttempts >= 15 or allowlisted == true) - - if DoesPlayerHavePermission(player, "player.allowlist") then - deferrals.done() - else - deferrals.done(GetLocalisedText("allowlist")) - return - end - else - deferrals.done() - end - - end) -end) - -curVersion, isMaster = GetVersion() -local resourceName = "EasyAdmin ("..GetCurrentResourceName()..")" -function checkVersion() - local remoteVersion,remoteURL = getLatestVersion() - - if GetResourceKvpString('currentVersion') ~= curVersion then - local legacyFiles = { - '__resource.lua', - 'version.json', - 'admin_server.lua', - 'admin_client.lua', - 'gui_c.lua', - 'util_shared.lua', - 'yarn.lock', - '.yarn.installed', - 'server/bot/notifications.js' - } - - for i,file in pairs(legacyFiles) do - local fileExists = LoadResourceFile(GetCurrentResourceName(), file) - if fileExists then - os.remove(GetResourcePath(GetCurrentResourceName()).."/"..file) - PrintDebugMessage("Found legacy file "..file.." in EasyAdmin Folder and attempted deletion.", 2) - end - end - - PrintDebugMessage('EasyAdmin has been updated, or just been installed for the first time, please restart EasyAdmin to ensure smooth operation.', 1) - - SetResourceKvpNoSync('currentVersion', curVersion) - end - - if isMaster then - PrintDebugMessage("You are using an unstable version of EasyAdmin, if this was not your intention, please download the latest stable version from "..remoteURL, 1) - end - if curVersion ~= remoteVersion and tonumber(curVersion) < tonumber(remoteVersion) then - print("\n--------------------------------------------------------------------------") - print("\n"..resourceName.." is outdated.\nNewest Version: "..remoteVersion.."\nYour Version: "..curVersion.."\nPlease update it from "..remoteURL) - print("\n--------------------------------------------------------------------------") - updateAvailable = remoteVersion - elseif tonumber(curVersion) > tonumber(remoteVersion) then - PrintDebugMessage("Your version of "..resourceName.." seems to be higher than the current stable version.", 2) - end - - if GetResourceState("screenshot-basic") == "missing" then - PrintDebugMessage("screenshot-basic is not installed, screenshots unavailable", 3) - else - StartResource("screenshot-basic") - screenshots = true - end - - local onesync = GetConvar("onesync", "off") - if (onesync ~= "off" and onesync ~= "legacy") then - PrintDebugMessage("Onesync is Infinity", 3) - infinity = true - end - - if GetConvar("ea_defaultKey", "none") == "none" and RedM then - PrintDebugMessage("ea_defaultKey is not defined, EasyAdmin can only be opened using the /easyadmin command, to define a key:\nhttps://easyadmin.readthedocs.io/en/latest", 1) - end - - readAcePermissions() -end - -Citizen.CreateThread(function() - repeat - Wait(1000) - until updateBlacklist - while true do - updateBlacklist() - Wait(300000) - end -end) - --- DO NOT TOUCH THESE --- DO NOT TOUCH THESE --- DO NOT TOUCH THESE --- DO NOT TOUCH THESE -MutedPlayers = {} -- DO NOT TOUCH THIS -OnlineAdmins = {} -- DO NOT TOUCH THIS -ChatReminders = {} -- DO NOT TOUCH THIS -MessageShortcuts = {} -- DO NOT TOUCH THIS -WarnedPlayers = {} -- DO NOT TOUCH THIS -reports = {} -- DO NOT TOUCH THIS -FrozenPlayers = {} -- DO NOT TOUCH THIS --- DO NOT TOUCH THESE --- DO NOT TOUCH THESE --- DO NOT TOUCH THESE --- DO NOT TOUCH THESE \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/backups.lua b/server-data/resources/[esx]/EasyAdmin/server/backups.lua deleted file mode 100644 index 0e3ab8695..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/backups.lua +++ /dev/null @@ -1,134 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -Citizen.CreateThread(function() - backupInfos = LoadResourceFile(GetCurrentResourceName(), "backups/_backups.json") - - while true do - repeat - Wait(5000) - until blacklist - if backupInfos == nil then - lastBackupTime = 0 - else - backupData = json.decode(backupInfos) - lastBackupTime = backupData.lastBackup - end - if (GetConvarInt("ea_backupFrequency", 72) ~= 0) and (lastBackupTime+(GetConvarInt("ea_backupFrequency", 72)*3600) < os.time()) then - createBackup() - end - Wait(120000) - end -end) - - -function loadBackupName(name) - local backup = LoadResourceFile(GetCurrentResourceName(), "backups/"..name) - if backup then - local backupJson = json.decode(backup) - if backupJson then - PrintDebugMessage("Loading Backup..") - for i,ban in pairs(blacklist) do - UnbanId(ban.banid) - PrintDebugMessage("removing ban "..ban.banid, 4) - Wait(50) - end - - for i,ban in pairs(backupJson) do - addBan(ban) - PrintDebugMessage("adding ban "..ban.banid, 4) - TriggerEvent("ea_data:addBan", ban) - Wait(50) - end - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - updateBlacklist() - PrintDebugMessage("Backup should be loaded!") - else - PrintDebugMessage("^1EasyAdmin:^7 Backup Could not be loaded, in most cases this comes from there being a formatting error, please use a JSON Validator on the file and fix the errors!") - end - - else - PrintDebugMessage("^1EasyAdmin:^7 Backup Name Invalid or missing from Backups.") - end -end - - -function createBackup() - local backupTime = os.time() - local backupDate = os.date("%H_%M_%d_%m_%Y") - local backupName = "banlist_"..backupDate..".json" - local resourceName = GetCurrentResourceName() - PrintDebugMessage("Creating Banlist Backup to "..backupName, 3) - - local saved = SaveResourceFile(resourceName, "backups/"..backupName, json.encode(blacklist, {indent = true}), -1) - - if not saved then - PrintDebugMessage("^1Saving banlist backup failed! Please check if EasyAdmin has Permission to write in the backups folder!^7", 1) - end - - backupInfos = LoadResourceFile(resourceName, "backups/_backups.json") - if backupInfos then - backupData = json.decode(backupInfos) - table.insert(backupData.backups, {id = getNewBackupid(backupData), backupFile = backupName, backupTimestamp = backupTime, backupDate = backupDate}) - - - if #backupData.backups > GetConvarInt("ea_maxBackupCount", 10) then - deleteBackup(backupData,1) - end - backupData.lastBackup = backupTime - SaveResourceFile(resourceName, "backups/_backups.json", json.encode(backupData, {indent = true})) - - else - local backupData = {lastBackup = backupTime, backups = {}} - table.insert(backupData.backups, {id = getNewBackupid(backupData), backupFile = backupName, backupTimestamp = backupTime, backupDate = backupDate}) - SaveResourceFile(resourceName, "backups/_backups.json", json.encode(backupData, {indent = true})) - end - - return id,timestamp -end - -function deleteBackup(backupData,id) - local expiredBackup = backupData.backups[id] - table.remove(backupData.backups, id) - - local backupFileName = expiredBackup.backupFile - - local fullResourcePath = GetResourcePath(GetCurrentResourceName()) - os.remove(fullResourcePath.."/backups/"..backupFileName) - PrintDebugMessage("Removed Backup "..backupFileName, 3) - -end - -function getNewBackupid(backupData) - if backupData then - local lastBackup = backupData.lastbackup - local backups = backupData.backups - return #backups+1 - else - return 0 - end -end - -RegisterCommand("ea_createBackup", function(source, args, rawCommand) - if DoesPlayerHavePermission(source, "server") then - createBackup() - end -end, false) - -RegisterCommand("ea_loadBackup", function(source,args,rawCommand) - if DoesPlayerHavePermission(source, "server") and args[1] then - loadBackupName(args[1]) - end -end,false) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/banlist.lua b/server-data/resources/[esx]/EasyAdmin/server/banlist.lua deleted file mode 100644 index f8f0df73e..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/banlist.lua +++ /dev/null @@ -1,500 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -blacklist = {} - -RegisterServerEvent("EasyAdmin:banPlayer", function(playerId,reason,expires) - if playerId ~= nil then - if (DoesPlayerHavePermission(source, "player.ban.temporary") or DoesPlayerHavePermission(source, "player.ban.permanent")) and CachedPlayers[playerId] and not CachedPlayers[playerId].immune then - local bannedIdentifiers = CachedPlayers[playerId].identifiers or getAllPlayerIdentifiers(playerId) - local username = CachedPlayers[playerId].name or getName(playerId, true) - if expires and expires < os.time() then - expires = os.time()+expires - elseif not expires then - expires = 10444633200 - end - if expires >= 10444633200 and not DoesPlayerHavePermission(source, "player.ban.permanent") then - return false - end - - reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source, true), reason = reason, expire = expires, expireString = formatDateString(expires) } - updateBlacklist( ban ) - PrintDebugMessage("Player "..getName(source,true).." banned player "..CachedPlayers[playerId].name.." for "..reason, 3) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) - DropPlayer(playerId, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) - elseif CachedPlayers[playerId].immune then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) - end - end -end) - -RegisterServerEvent("EasyAdmin:offlinebanPlayer", function(playerId,reason,expires) - if playerId ~= nil and not CachedPlayers[playerId].immune then - if (DoesPlayerHavePermission(source, "player.ban.temporary") or DoesPlayerHavePermission(source, "player.ban.permanent")) and not CachedPlayers[playerId].immune then - local bannedIdentifiers = CachedPlayers[playerId].identifiers or getAllPlayerIdentifiers(playerId) - local username = CachedPlayers[playerId].name or getName(playerId, true) - if expires and expires < os.time() then - expires = os.time()+expires - elseif not expires then - expires = 10444633200 - end - if expires >= 10444633200 and not DoesPlayerHavePermission(source, "player.ban.permanent") then - return false - end - - reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), CachedPlayers[playerId].name, getName(source) ) - local ban = {banid = GetFreshBanId(), name = username,identifiers = bannedIdentifiers, banner = getName(source), reason = reason, expire = expires } - updateBlacklist( ban ) - PrintDebugMessage("Player "..getName(source,true).." offline banned player "..CachedPlayers[playerId].name.." for "..reason, 3) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminofflinebannedplayer"), getName(source, false, true), CachedPlayers[playerId].name, reason, formatDateString( expires ) ), "ban", 16711680) - end - elseif CachedPlayers[playerId].immune then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("adminimmune")) - end -end) - -AddEventHandler('banCheater', function(playerId,reason) - Citizen.Trace("^1EasyAdmin^7: the banCheater event is ^1deprecated^7 and has been removed! Please adjust your ^3"..GetInvokingResource().."^7 Resource to use EasyAdmin:addBan instead.") -end) - - -function addBanExport(playerId,reason,expires,banner) - local bannedIdentifiers = {} - local bannedUsername = "Unknown" - if type(playerId) == "table" then -- if playerId is a table of identifiers - offline = true - bannedIdentifiers = playerId - elseif CachedPlayers[playerId] then - if CachedPlayers[playerId].dropped then - offline = true - end - if CachedPlayers[playerId].immune then - return false - end - bannedIdentifiers = CachedPlayers[playerId].identifiers - bannedUsername = CachedPlayers[playerId].name or getName(playerId, true) - else - PrintDebugMessage("Couldn't find any Infos about Player "..playerId..", no ban issued.", 1) - return false - end - - - if expires and expires < os.time() then - expires = os.time()+expires - elseif not expires then - expires = 10444633200 - end - reason = formatShortcuts(reason).. string.format(GetLocalisedText("reasonadd"), getName(tostring(playerId) or "?"), banner or "Unknown" ) - local ban = {banid = GetFreshBanId(), name = bannedUsername,identifiers = bannedIdentifiers, banner = banner or "Unknown", reason = reason, expire = expires, expireString = formatDateString(expires) } - updateBlacklist( ban ) - - if source then - PrintDebugMessage("Player "..getName(source,true).." added ban "..reason, 3) - end - - - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminbannedplayer"), banner or "Unknown", getName(tostring(playerId) or "?", false, true), reason, formatDateString( expires ), tostring(ban.banid) ), "ban", 16711680) - if not offline then - DropPlayer(playerId, string.format(GetLocalisedText("banned"), reason, formatDateString( expires ) ) ) - end - return ban -end -exports('addBan', addBanExport) -AddEventHandler("EasyAdmin:addBan", addBanExport) - -RegisterServerEvent("EasyAdmin:updateBanlist", function(playerId) - local src = source - if DoesPlayerHavePermission(source, "player.ban.view") then - updateBlacklist(false,true) - Citizen.Wait(300) - TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, blacklist) - PrintDebugMessage("Banlist Refreshed by "..getName(src,true), 3) - end -end) - -RegisterServerEvent("EasyAdmin:requestBanlist", function() - local src = source - if DoesPlayerHavePermission(source, "player.ban.view") then - TriggerLatentClientEvent("EasyAdmin:fillBanlist", src, 100000, blacklist) - PrintDebugMessage("Banlist Requested by "..getName(src,true), 3) - end -end) - -RegisterCommand("unban", function(source, args, rawCommand) - if args[1] and DoesPlayerHavePermission(source, "player.ban.remove") then - PrintDebugMessage("Player "..getName(source,true).." Unbanned "..args[1], 3) - if tonumber(args[1]) then - UnbanId(tonumber(args[1])) - else - UnbanIdentifier(args[1]) - end - if (source ~= 0) then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("done")) - else - Citizen.Trace(GetLocalisedText("done")) - end - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminunbannedplayer"), getName(source, false, true), args[1], "Unbanned via Command"), "ban", 16711680) - end -end, false) - - -RegisterServerEvent("EasyAdmin:editBan", function(ban) - if DoesPlayerHavePermission(source, "player.ban.edit") then - updateBan(ban.banid,ban) - -- TODO Webhook - end -end) - -function unbanPlayer(banId) - local thisBan = nil - for i,ban in ipairs(blacklist) do - if ban.banid == banId then - thisBan = ban - break - end - end - if thisBan == nil then - return false - end - UnbanId(banId) - return true -end -exports('unbanPlayer', unbanPlayer) - - -function fetchBan(banId) - for i,ban in ipairs(blacklist) do - if ban.banid == banId then - return ban - end - end - return false -end -exports('fetchBan', fetchBan) - -RegisterServerEvent("EasyAdmin:unbanPlayer", function(banId) - if DoesPlayerHavePermission(source, "player.ban.remove") then - local thisBan = fetchBan(banId) - local ret = unbanPlayer(banId) - if ret then - PrintDebugMessage("Player "..getName(source,true).." unbanned "..banId, 3) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminunbannedplayer"), getName(source, false, true), banId, thisBan.reason), "ban", 16711680) - end - end -end) - -function GetFreshBanId() - if blacklist[#blacklist] then - return blacklist[#blacklist].banid+1 - else - return 1 - end -end -exports('GetFreshBanId', GetFreshBanId) - - -RegisterCommand("convertbanlist", function(source, args, rawCommand) - if GetConvar("ea_custombanlist", "false") == "true" then - local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") - local ob = json.decode(content) - for i,theBan in ipairs(ob) do - TriggerEvent("ea_data:addBan", theBan) - print("processed ban: "..i.."\n") - end - content=nil - else - print("Custom Banlist is not enabled, converting back to json.") - TriggerEvent('ea_data:retrieveBanlist', function(banlist) - blacklist = banlist - for i,theBan in ipairs(blacklist) do - if not theBan.identifiers then theBan.identifiers = {} end - if theBan.steam then - table.insert(theBan.identifiers, theBan.steam) - theBan.steam=nil - end - if theBan.identifier then - table.insert(theBan.identifiers, theBan.identifier) - theBan.identifier=nil - end - if theBan.discord then - table.insert(theBan.identifiers, theBan.discord) - theBan.discord=nil - end - end - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - end) - end -end, true) - -function updateBan(id,newData) - if id and newData and newData.identifiers and newData.banid and newData.reason and newData.expire then - for i, ban in pairs(blacklist) do - if ban.banid == newData.banid then - blacklist[i] = newData - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - if GetConvar("ea_custombanlist", "false") == "true" then - TriggerEvent("ea_data:updateBan", newData) - end - break - end - end - end -end - - -function addBan(data) - if data then - table.insert(blacklist, data) - end -end - - - -function updateBlacklist(data,remove, forceChange) - local change = (forceChange or false) --mark if file was changed to save up on disk writes. - if GetConvar("ea_custombanlist", "false") == "true" then - PrintDebugMessage("You are using a Custom Banlist System, this is ^3not currently supported^7 and WILL cause issues! Only use this if you know what you are doing, otherwise, disable ea_custombanlist.", 1) - if data and not remove then - addBan(data) - TriggerEvent("ea_data:addBan", data) - - elseif data and remove then - UnbanId(data.banid) - elseif not data then - TriggerEvent('ea_data:retrieveBanlist', function(banlist) - blacklist = banlist - PrintDebugMessage("updated banlist custom banlist", 4) - for i,theBan in ipairs(blacklist) do - if theBan.expire < os.time() then - table.remove(blacklist,i) - PrintDebugMessage("removing old ban custom banlist", 4) - TriggerEvent("ea_data:removeBan", theBan) - end - end - end) - end - return - end - - local content = LoadResourceFile(GetCurrentResourceName(), "banlist.json") - if not content then - PrintDebugMessage("banlist.json file was missing, we created a new one.", 2) - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode({}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - content = json.encode({}) - end - blacklist = json.decode(content) - - if not blacklist then - PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - PrintDebugMessage("^1Failed^7 to load Banlist!\n") - PrintDebugMessage("Please check your banlist file for errors, ^1Bans *will not* work!^7\n") - PrintDebugMessage("^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^4-^5-^6-^8-^9-^1-^2-^3-^3!^1FATAL ERROR^3!^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^1-^9-^8-^6-^5-^4-^3-^2-^7\n") - return - end - - upgraded = performBanlistUpgrades(blacklist) - if upgraded then change = true end - - if data and not remove then - addBan(data) - PrintDebugMessage("Added the following data to banlist:\n"..table_to_string(data), 4) - change=true - elseif not data then - for i,theBan in ipairs(blacklist) do - theBan.id = nil - if not theBan.banid then - if i==1 then - theBan.banid = 1 - else - theBan.banid = blacklist[i].banid or i - end - PrintDebugMessage("Ban "..theBan.banid.." did not have an ID, assigned one.", 4) - change=true - end - if not theBan.expire then - PrintDebugMessage("Ban "..theBan.banid.." did not have an expiry time, removing..", 4) - table.remove(blacklist,i) - change=true - elseif not theBan.identifiers then -- make sure 1 identifier is given, otherwise its a broken ban - PrintDebugMessage("Ban "..theBan.banid.." did not have any identifiers, removing..", 4) - table.remove(blacklist,i) - change=true - elseif not theBan.identifiers[1] then - PrintDebugMessage("Ban "..theBan.banid.." did not have one identifier, removing..", 4) - table.remove(blacklist,i) - change=true - elseif theBan.expire < os.time() then - PrintDebugMessage("Ban "..theBan.banid.." expired, removing..", 4) - table.remove(blacklist,i) - change=true - elseif theBan.expire == 1924300800 then - PrintDebugMessage("Ban "..theBan.banid.." had legacy expiry time, we fixed it", 4) - blacklist[i].expire = 10444633200 - change=true - end - end - end - if data and remove then - PrintDebugMessage("Removed the following data from banlist:\n"..table_to_string(data), 4) - UnbanId(data.banid) - change = true - end - if change then - PrintDebugMessage("Banlist changed, saving..", 4) - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - end - PrintDebugMessage("Completed Banlist Update.", 4) -end - -function BanIdentifier(identifier,reason) - updateBlacklist( {identifiers = {identifier} , banner = "Unknown", reason = reason, expire = 10444633200} ) -end - -function BanIdentifiers(identifier,reason) - updateBlacklist( {identifiers = identifier , banner = "Unknown", reason = reason, expire = 10444633200} ) -end - -function UnbanIdentifier(identifier) - if identifier then - for i,ban in pairs(blacklist) do - for index,id in pairs(ban.identifiers) do - if identifier == id then - table.remove(blacklist,i) - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - - if GetConvar("ea_custombanlist", "false") == "true" then - TriggerEvent("ea_data:removeBan", ban) - end - PrintDebugMessage("removed ban as per unbanidentifier func", 4) - return - end - end - end - end -end - -function UnbanId(id) - for i,ban in pairs(blacklist) do - if ban.banid == id then - table.remove(blacklist,i) - local saved = SaveResourceFile(GetCurrentResourceName(), "banlist.json", json.encode(blacklist, {indent = true}), -1) - if not saved then - PrintDebugMessage("^1Saving banlist.json failed! Please check if EasyAdmin has Permission to write in its own folder!^7", 1) - end - - if GetConvar("ea_custombanlist", "false") == "true" then - TriggerEvent("ea_data:removeBan", ban) - end - end - end -end - -function performBanlistUpgrades() - local upgraded = false - - local takenIds = {} - for i,b in pairs(blacklist) do - if takenIds[b.banid] then - local freshId = GetFreshBanId() - PrintDebugMessage("ID "..b.banid.." was assigned twice, reassigned to "..freshId, 4) - blacklist[i].banid = freshId - upgraded = true - end - takenIds[b.banid] = true - end - takenIds=nil - - for i,ban in pairs(blacklist) do - if type(i) == "string" then - PrintDebugMessage("Ban "..ban.banid.." had a string as indice, fixed it.", 4) - blacklist[i] = nil - table.insert(blacklist,ban) - upgraded = true - end - end - for i,ban in ipairs(blacklist) do - if ban.identifiers then - for k, identifier in pairs(ban.identifiers) do - if identifier == "" then - PrintDebugMessage("Ban "..ban.banid.." had an empty identifier, removed it.", 4) - ban.identifiers[k] = nil - upgraded = true - end - end - end - if not ban.expireString then - upgraded = true - ban.expireString = formatDateString(ban.expire) - end - end - if blacklist[1] and (blacklist[1].identifier or blacklist[1].steam or blacklist[1].discord) then - Citizen.Trace("Upgrading Banlist...\n", 4) - for i,ban in ipairs(blacklist) do - if not ban.identifiers then - ban.identifiers = {} - PrintDebugMessage("Ban "..ban.banid.." had no identifiers, added one.", 4) - upgraded=true - end - if ban.identifier then - table.insert(ban.identifiers, ban.identifier) - PrintDebugMessage("Ban "..ban.banid.." had identifier, converted to identifiers table", 4) - ban.identifier = nil - upgraded=true - end - if ban.steam then - table.insert(ban.identifiers, ban.steam) - PrintDebugMessage("Ban "..ban.banid.." had seperate steam identifier, converted to identifiers table", 4) - ban.steam = nil - upgraded=true - end - if ban.discord and ban.discord ~= "" then - table.insert(ban.identifiers, ban.discord) - PrintDebugMessage("Ban "..ban.banid.." had seperate discord identifier, converted to identifiers table", 4) - ban.discord = nil - upgraded=true - end - end - Citizen.Trace("Banlist Upgraded.\n", 4) - end - return upgraded -end - - - -function IsIdentifierBanned(theIdentifier) - local identifierfound = false - for index,value in ipairs(blacklist) do - for i,identifier in ipairs(value.identifiers) do - if theIdentifier == identifier then - identifierfound = true - end - end - end - return identifierfound -end -exports('IsIdentifierBanned', IsIdentifierBanned) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/bot.js b/server-data/resources/[esx]/EasyAdmin/server/bot/bot.js deleted file mode 100644 index 918d866a2..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/bot.js +++ /dev/null @@ -1,116 +0,0 @@ -/*eslint no-global-assign: "off", no-unused-vars: "off"*/ -process.on('uncaughtException', function(err) { - console.log('Caught exception: ', err.stack) -}) -process.on('unhandledRejection', function(err) { - console.log('Caught exception: ', err.stack) -}) - -AsciiTable = require('ascii-table') -sprintf = require('sprintf-js').sprintf -juration = require('juration') -const prettyMilliseconds = require('pretty-ms') -const { Client, EmbedBuilder, Collection, Intents, Partials, ButtonStyle, ActionRowBuilder, ButtonBuilder, SelectMenuBuilder, Guild, Util, ModalBuilder, TextInputBuilder, GatewayIntentBits, InteractionType, TextInputStyle } = require('discord.js') - -const { SlashCommandBuilder } = require('@discordjs/builders') - - -client = new Client({ - partials: [Partials.GuildMember, Partials.User, Partials.Message, Partials.Channel, Partials.Reaction], - intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMembers, GatewayIntentBits.MessageContent] -}) -client.commands = new Collection() - - -async function RegisterClientCommands(clientId) { - const { REST } = require('@discordjs/rest') - const { Routes } = require('discord-api-types/v10') - const fs = require('fs') - - const commands = [] - const commandFiles = fs.readdirSync(`${resourcePath}/server/bot/commands`).filter(file => file.endsWith('.js')) - - - for (const file of commandFiles) { - const command = require(`${resourcePath}/server/bot/commands/${file}`) - commands.push(command.data.toJSON()) - client.commands.set(command.data.name, command) - } - - const rest = new REST({ version: '10' }).setToken(GetConvar('ea_botToken', '')) - - // compat: remove existing commands for homeguild - if (guild != '') { - rest.put(Routes.applicationGuildCommands(clientId, guild), { body: {} }) - } - await rest.put( - Routes.applicationCommands(clientId), - { body: commands }, - ) - - client.on('interactionCreate', async interaction => { - if (interaction.type != InteractionType.ApplicationCommand) return - - const command = client.commands.get(interaction.commandName) - - if (!command) return - - if (!(await DoesGuildMemberHavePermission(interaction.member, `bot.${command.data.name}`) == true) && !(command.data.name == 'refreshperms')) { - await refreshRolesForMember(interaction.member) - if (!(await DoesGuildMemberHavePermission(interaction.member, `bot.${command.data.name}`) == true)) { - await interaction.reply({ content: 'You don\'t have permission to run this command!', ephemeral: true }) - return false - } - } - try { - await command.execute(interaction, exports) // we need to pass exports here, otherwise we won't be able to access them inside the command - } catch (error) { - console.error(error) - var errorContent = { content: `There was an error while executing this command, please report the following stack trace here: \`\`\`js\n${error.stack}\`\`\``, ephemeral: true } - if (interaction.replied) { - interaction.followUp(errorContent) - - } else { - interaction.reply(errorContent) - } - } - }) -} - -if (GetConvar('ea_botToken', '') != '') { - - client.on('ready', async () => { - console.log(`Logged in as ${client.user.tag}!`) - client.user.setPresence({ activities: [{ name: `${GetConvar('sv_projectName', GetConvar('sv_hostname', 'default FXServer'))}`, type: 'WATCHING' }], status: 'online' }) - userID = client.user.id - resourcePath = GetResourcePath(GetCurrentResourceName()) // absolute resource path, needed for FS - guild = GetConvar('ea_botGuild', '') - - EasyAdmin = GetCurrentResourceName() // fetch our Resource name and claim we're called EasyAdmin, this just makes exports easier. - - currentVersion = await exports[EasyAdmin].GetVersion()[0] - latestVersionInfo = await exports[EasyAdmin].getLatestVersion() - - - RegisterClientCommands(client.user.id) - var startupMessage = `**EasyAdmin ${currentVersion}** has started.` - if (currentVersion != latestVersionInfo[0]) { - startupMessage+=`\nVersion ${latestVersionInfo[0]} is Available!\n Download it from ${latestVersionInfo[1]}` - } - LogDiscordMessage(startupMessage, 'startup') - }) - - client.on('debug', function(info){ - if (GetConvarInt('ea_logLevel', 1) >= 4 ) { - console.log(`${info}`) - } - }) - on('debug', function(info){ - if (GetConvarInt('ea_logLevel', 1) >= 4 ) { - console.log(`${info}`) - } - }) - - client.login(GetConvar('ea_botToken', '')) -} - diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/chat_bridge.js b/server-data/resources/[esx]/EasyAdmin/server/bot/chat_bridge.js deleted file mode 100644 index acea07b96..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/chat_bridge.js +++ /dev/null @@ -1,84 +0,0 @@ - -try { - - knownAvatars = {} - - - exports['chat'].registerMessageHook(async function(source, outMessage) { - - if (GetConvar('ea_botChatBridge', '') == '') { return } - - const user = await exports[EasyAdmin].getCachedPlayer(source) - - if (!user) { - return // chat message wasnt sent by a user, we don't care. - } - - - var userInfo = {name: outMessage.args[0]} - - if (knownAvatars[source] == undefined) { - var fivemAccount = false - for (let identifier of user.identifiers) { - if (identifier.search('fivem:') != -1) { - fivemAccount = identifier.substring(identifier.indexOf(':') + 1) - } - } - - if (fivemAccount) { - var response = await exports[EasyAdmin].HTTPRequest(`https://policy-live.fivem.net/api/getUserInfo/${fivemAccount}`) - try { - response = JSON.parse(response) - if (response.avatar_template) { - var avatarURL = response.avatar_template.replace('{size}', '96') - if (avatarURL.indexOf('http') == -1) { - avatarURL = `https://forum.cfx.re${avatarURL}` - } - userInfo.iconURL = avatarURL - knownAvatars[source] = avatarURL // we dont need to resolve the avatar every time. - } else { - knownAvatars[source] = false // avatar missing. - } - } catch { - knownAvatars[source] = false // something broke while trying to get discourse avatar, dont try again. - - } - } else { - knownAvatars[source] = false // no fivem identifier - } - } else { - userInfo.iconURL = knownAvatars[source] - } - if (knownAvatars[source] == false) { - userInfo.iconURL = undefined // dont send anything to discord, assume something went wrong - } - - var embed = await prepareGenericEmbed(undefined, undefined, 55555, undefined, undefined, userInfo, outMessage.args[1], false) - client.channels.cache.get(GetConvar('ea_botChatBridge', '')).send({ embeds: [embed] }) - }) - - -} catch(error) { - if (GetConvar('ea_botChatBridge', '') != '') { - console.error('Registering Chat Bridge failed, you will need to update your chat resource from https://github.com/citizenfx/cfx-server-data to use it.') - } -} - -client.on('messageCreate', async msg => { - if (GetConvar('ea_botChatBridge', '') == '') { return } - if (!msg.member || msg.author.bot) { return } // message-sender is a webhook - if(msg.author.id == userID) { - return - } - if(!msg.channel) { return } - if (msg.channel.id == GetConvar('ea_botChatBridge', '')) { - exports['chat'].addMessage(-1, { args: [msg.member.user.tag, msg.cleanContent]}) - } - -}) - -on('playerDropped', () => { - if (GetConvar('ea_botChatBridge', '') == '') { return } - knownAvatars[global.source] = undefined -}) - diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_ace.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_ace.js deleted file mode 100644 index 93e0c4ad8..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_ace.js +++ /dev/null @@ -1,53 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('add_ace') - .setDescription('Adds a permission to a group, saves into easyadmin_permissions.cfg'), - async execute(interaction, exports) { - var timestamp = Date.now() - - const modal = new ModalBuilder() - .setCustomId('addaceModal'+timestamp) - .setTitle('Add ACE') - - const groupName = new TextInputBuilder() - .setCustomId('groupName') - .setLabel('Group Name') - .setStyle(TextInputStyle.Short) - .setRequired(true) - .setMaxLength(120) - .setPlaceholder('group.admin') - - const permission = new TextInputBuilder() - .setCustomId('permission') - // The label is the prompt the user sees for this input - .setLabel('Permission') - // Short means only a single line of text - .setStyle(TextInputStyle.Short) - .setRequired(true) - .setMaxLength(120) - .setPlaceholder('easyadmin.bot.playerlist') - - const firstActionRow = new ActionRowBuilder().addComponents(groupName) - const secondActionRow = new ActionRowBuilder().addComponents(permission) - - modal.addComponents(firstActionRow, secondActionRow) - - interaction.showModal(modal) - - const filter = (interaction) => interaction.customId === 'addaceModal'+timestamp - interaction.awaitModalSubmit({ filter, time: 120000 }) - .then(async (interaction) => { - var group = interaction.fields.getTextInputValue('groupName') - var permission = interaction.fields.getTextInputValue('permission') - var query = `add_ace ${group} ${permission} allow` - exports[EasyAdmin].AddToFile('easyadmin_permissions.cfg', query) - - ExecuteCommand(query) - - interaction.reply(`\`${query}\` has been executed and saved.`) - - }).catch(async () => {}) // silently catch error, happens if the form times out - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_group.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_group.js deleted file mode 100644 index 7ebbb9631..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/add_group.js +++ /dev/null @@ -1,27 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('add_group') - .setDescription('Adds a group to a User (ACE), saves into easyadmin_permissions.cfg') - .addUserOption(option => - option.setName('user') - .setDescription('The user') - .setRequired(true)) - .addStringOption(option => - option.setName('group') - .setDescription('the group, for example, group.admin') - .setRequired(true)), - async execute(interaction, exports) { - const user = interaction.options.getUser('user').id - const groupName = interaction.options.getString('group') - - - var query = `add_principal identifier.discord:${user} ${groupName}` - exports[EasyAdmin].AddToFile('easyadmin_permissions.cfg', query) - - ExecuteCommand(query) - - interaction.reply(`\`${query}\` has been executed and saved.`) - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/announce.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/announce.js deleted file mode 100644 index da487f414..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/announce.js +++ /dev/null @@ -1,23 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('announce') - .setDescription('send a announcement to the server') - .addStringOption(option => - option.setName('reason') - .setDescription('Reason Text') - .setRequired(true)), - async execute(interaction, exports) { - var reason = exports[EasyAdmin].formatShortcuts(interaction.options.getString('reason')) - - var ret = await exports[EasyAdmin].announce(reason) - if (ret) { - let embed = await prepareGenericEmbed(`Succesfully sent an announcement \nreason: ${reason}`) - await interaction.reply({embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed('Could not send an annoucement.') - await interaction.reply({embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/ban.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/ban.js deleted file mode 100644 index 98a5c0c01..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/ban.js +++ /dev/null @@ -1,66 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('ban') - .setDescription('bans a User') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)) - .addStringOption(option => - option.setName('reason') - .setDescription('Reason Text') - .setRequired(true)) - .addStringOption(option => - option.setName('timeframe') - .setDescription('The timeframe in a human readable format (30 mins, 1 hour, 2 weeks, or permanent)') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - const reason = exports[EasyAdmin].formatShortcuts(interaction.options.getString('reason')) - const timeframe = exports[EasyAdmin].formatShortcuts(interaction.options.getString('timeframe')) - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var banTime - - try { - if (timeframe.toLowerCase() == 'permanent') { - banTime = 10444633200 - } else { - banTime = await juration.parse(timeframe) - if (banTime > 10444633200) { - banTime = 10444633200 - } - } - } catch (error) { - console.error(error) - interaction.reply({ content: 'Sorry, i couldn\'t understand the timeframe you provided.', ephemeral: true }) - return - } - - - if (banTime < 10444633200 && !await DoesGuildMemberHavePermission(interaction.member, 'player.ban.temporary')) { - interaction.reply({ content: 'Insufficient Permissions, you need `easyadmin.player.ban.temporary`.', ephemeral: true }) - return - } else if (banTime > 10444633200 && !await DoesGuildMemberHavePermission(interaction.member, 'player.ban.permanent')) { - interaction.reply({ content: 'Insufficient Permissions, you need `easyadmin.player.ban.permanent`.', ephemeral: true }) - return - } - - var ban = exports[EasyAdmin].addBan(user.id, reason, banTime, interaction.user.tag) - if (ban) { - let embed = await prepareGenericEmbed(`Successfully banned **${user.name}** for **${reason}** until ${ban.expireString} [#${ban.banid}.`) - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Failed banning **${user.name}**.`) - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/baninfo.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/baninfo.js deleted file mode 100644 index cadc431eb..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/baninfo.js +++ /dev/null @@ -1,49 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('baninfo') - .setDescription('Shows details of a ban') - .addIntegerOption(option => - option.setName('banid') - .setDescription('Ban ID') - .setRequired(true)), - async execute(interaction, exports) { - const banId = interaction.options.getInteger('banid') - - var ban = await exports[EasyAdmin].fetchBan(banId) - if (ban) { - let embed = new EmbedBuilder() - .setColor(16777214) - .setTimestamp() - - var discordAccount = false - for (let identifier of ban.identifiers) { - if (identifier.search('discord:') != -1) { - discordAccount = await client.users.fetch(identifier.substring(identifier.indexOf(':') + 1)) - } - } - - embed.addFields([ - { name: 'Ban Info', value: `Ban infos for **#${banId}**`}, - { name: 'Username', value: `\`\`\`${ban.name}\`\`\``, inline: true} - ]) - - if (discordAccount) { - embed.addFields([{ name: 'Discord Account', value: `\`\`\`${discordAccount.tag}\`\`\``, inline: true}]) - embed.setThumbnail(discordAccount.avatarURL()) - } - embed.addFields([ - { name: 'Banned by', value: `\`\`\`${ban.banner}\`\`\``, inline: true}, - { name: 'Reason', value: `\`\`\`\n${ban.reason}\`\`\``, inline: false}, - { name: 'Expires', value: `\`\`\`${ban.expireString}\`\`\``, inline: true} - ]) - - - interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed('No ban was found with this ID.') - interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/cleanup.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/cleanup.js deleted file mode 100644 index afbb4d046..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/cleanup.js +++ /dev/null @@ -1,31 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('cleanup') - .setDescription('Cleans up area of type') - .addStringOption(option => - option.setName('type') - .setDescription('Type of Entity to clean up.') - .setRequired(true) - .addChoices( - {name:'Vehicles', value:'cars'}, - {name:'Peds', value:'peds'}, - {name:'Props', value:'props'})), - - async execute(interaction, exports) { - const type = interaction.options.getString('type') - - var ret = exports[EasyAdmin].cleanupArea(type) - - if (ret) { - let embed = await prepareGenericEmbed(`Cleaned up **${type}**.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Could not cleanup **${type}**.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/configure.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/configure.js deleted file mode 100644 index 0e37e11ec..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/configure.js +++ /dev/null @@ -1,122 +0,0 @@ - -async function configForward(interaction, exports) { - var embed = await prepareGenericEmbed('Alright! Now please write the type of log to forward (see for examples)') - - - if (!interaction.replied) { - await interaction.reply({ embeds: [embed]}) - } else { - await interaction.followUp({ embeds: [embed]}) - } - - const filter = m => m.author.id == interaction.member.id && m.channel.id == interaction.channel.id - const collector = interaction.channel.createMessageCollector({ filter, time: 10000, max: 1 }) - - collector.on('collect', async m => { - await m.fetch() - let embed = await prepareGenericEmbed(`Great! Now please tag the channel you want me to log this in (like this: <#${interaction.channel.id}>).`) - - - await interaction.followUp({ embeds: [embed]}) - const filter = m => m.author.id == interaction.member.id && m.channel.id == interaction.channel.id - const collector = interaction.channel.createMessageCollector({ filter, time: 10000, max: 1 }) - - collector.on('collect', async message => { - await message.fetch() - let channel = message.mentions.channels.first().id - - exports[EasyAdmin].RemoveFromFile('easyadmin_permissions.cfg', `ea_addBotLogForwarding ${m.cleanContent}`, true) - - exports[EasyAdmin].AddToFile('easyadmin_permissions.cfg', `ea_addBotLogForwarding ${m.cleanContent} ${channel}`) - - - addBotLogForwarding('', [m.cleanContent, channel]) - interaction.followUp('Done! Result has been saved into `easyadmin_permissions.cfg`.') - }) - - }) -} - - -async function configBridge(interaction, exports) { - var embed = await prepareGenericEmbed(`Alright! Please tag the channel you want me to bridge (like this: <#${interaction.channel.id}>).`) - - - if (!interaction.replied) { - await interaction.reply({ embeds: [embed]}) - } else { - await interaction.followUp({ embeds: [embed]}) - } - - const filter = m => m.author.id == interaction.member.id && m.channel.id == interaction.channel.id - const collector = interaction.channel.createMessageCollector({ filter, time: 10000, max: 1 }) - - collector.on('collect', async m => { - await m.fetch() - let channel = m.mentions.channels.first().id - - exports[EasyAdmin].RemoveFromFile('easyadmin_permissions.cfg', 'set ea_botChatBridge', true) - - exports[EasyAdmin].AddToFile('easyadmin_permissions.cfg', `set ea_botChatBridge ${channel}`) - - SetConvar('ea_botChatBridge', `${channel}`) - - interaction.followUp('Done! Result has been saved into `easyadmin_permissions.cfg`.') - - }) -} - -async function configLiveStatus(interaction, exports) { - var embed = await prepareGenericEmbed(`Alright! Please tag the channel you want me to post the live status in, make sure its empty and that normal people can't write there! (like this: <#${interaction.channel.id}>).`) - - - if (!interaction.replied) { - await interaction.reply({ embeds: [embed]}) - } else { - await interaction.followUp({ embeds: [embed]}) - } - - const filter = m => m.author.id == interaction.member.id && m.channel.id == interaction.channel.id - const collector = interaction.channel.createMessageCollector({ filter, time: 10000, max: 1 }) - - collector.on('collect', async m => { - await m.fetch() - let channel = m.mentions.channels.first().id - - exports[EasyAdmin].RemoveFromFile('easyadmin_permissions.cfg', 'set ea_botStatusChannel', true) - - exports[EasyAdmin].AddToFile('easyadmin_permissions.cfg', `set ea_botStatusChannel ${channel}`) - - SetConvar('ea_botStatusChannel', `${channel}`) - - interaction.followUp('Done! Result has been saved into `easyadmin_permissions.cfg`.') - - }) -} - -module.exports = { - data: new SlashCommandBuilder() - .setName('configure') - .setDescription('Configure various easyadmin features') - .addStringOption(option => - option.setName('setting') - .setDescription('The setting to change') - .setRequired(true) - .addChoices( - {name:'Log Forwarding', value: 'logfwd'}, - {name:'Chat Bridge', value: 'chatbridge'}, - {name:'Live Server Status', value: 'serverstatus'} - )), - async execute(interaction, exports) { - const setting = interaction.options.getString('setting') - - - if (setting == 'logfwd') { - configForward(interaction, exports) - } else if (setting == 'chatbridge') { - configBridge(interaction, exports) - } else if (setting == 'serverstatus') { - configLiveStatus(interaction, exports) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/freeze.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/freeze.js deleted file mode 100644 index 8bed3f5b1..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/freeze.js +++ /dev/null @@ -1,33 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('freeze') - .setDescription('Freezes player') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var ret = await exports[EasyAdmin].freezePlayer(user.id, true) - - if (ret) { - let embed = await prepareGenericEmbed(`Successfully froze **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Could not freeze **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/kick.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/kick.js deleted file mode 100644 index b315d0996..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/kick.js +++ /dev/null @@ -1,34 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('kick') - .setDescription('Kicks a User') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)) - .addStringOption(option => - option.setName('reason') - .setDescription('Reason Text') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - const reason = exports[EasyAdmin].formatShortcuts(interaction.options.getString('reason')) - - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - - DropPlayer(user.id, sprintf(exports[EasyAdmin].GetLocalisedText('kicked'), interaction.user.tag, reason )) - - var embed = await prepareGenericEmbed(`Successfully kicked **${user.name}** for **${reason}**`) - - await interaction.reply({ embeds: [embed]}) - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/mute.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/mute.js deleted file mode 100644 index c9d85d3e7..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/mute.js +++ /dev/null @@ -1,33 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('mute') - .setDescription('Mutes a User') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var ret = await exports[EasyAdmin].mutePlayer(user.id, true) - - if (ret) { - let embed = await prepareGenericEmbed(`Successfully muted **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Could not mute **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerinfo.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerinfo.js deleted file mode 100644 index 4a6c1a774..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerinfo.js +++ /dev/null @@ -1,79 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('playerinfo') - .setDescription('Gives Info about a Player') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - - const user = await findPlayerFromUserInput(userOrId) - - if (!user) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided, if they have recently left, try using their ID instead of username', ephemeral: true}) - return - } - - var displayedIdentifiers = [] - - for (let identifier of user.identifiers) { - if ((isNaN(identifier.charAt(0))) && !(GetConvar('ea_IpPrivacy', 'true') == 'true' && identifier.search('ip:') != -1)) { - displayedIdentifiers.push(identifier) - } - } - - var table = AsciiTable.factory({ - heading: [ 'Identifiers'], - rows: displayedIdentifiers - }) - - var discordAccount = await getDiscordAccountFromPlayer(user) - var discordName = 'N/A' - if (discordAccount) { - discordName = discordAccount.tag - } - - - var embed = new EmbedBuilder() - .setColor(16777214) - .setTimestamp() - - embed.addFields([ - { name: 'Player Info', value: `Player infos for **${user.name}**`}, - { name: 'Discord Account', value: `\`\`\`${discordName}\`\`\``, inline: true} - ]) - - if (discordAccount) { - embed.setThumbnail(discordAccount.avatarURL()) - } - - embed.addFields([ - { name: 'Admin', value: `\`\`\`${exports[EasyAdmin].IsPlayerAdmin(user.id)}\`\`\``, inline: true}, - { name: 'Warnings', value: `\`\`\`${exports[EasyAdmin].getPlayerWarnings(user.id)}\`\`\``, inline: true} - ]) - - - - if (!user.dropped) { - var playerPed = GetPlayerPed(user.id) - embed.addFields([ - { name: 'Health', value: `\`\`\`${GetEntityHealth(playerPed)}\`\`\``, inline: true}, - { name: 'Armour', value: `\`\`\`${GetPedArmour(playerPed)}\`\`\``, inline: true} - ]) - if (GetPlayerInvincible(user.id)) { - embed.addFields([{ name: 'Godmode', value: '```ON```', inline: true}]) - } - } else { - embed.addFields([{ name: 'Status', value: '```Player Disconnected```'}]) - } - - - embed.addFields([{ name: 'Identifiers', value: `\`\`\`${table}\`\`\``}]) - - await interaction.reply({ embeds: [embed]}) - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerlist.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerlist.js deleted file mode 100644 index a430c1b09..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/playerlist.js +++ /dev/null @@ -1,143 +0,0 @@ - - - -function generatePaginatorRow(idFields, curPage, embedTimestamp) { - const row = new ActionRowBuilder() - - var selector = new SelectMenuBuilder() - - var fieldLength = idFields.length - if (fieldLength == 0) {fieldLength = 1} - selector.setCustomId(`pageSelector${embedTimestamp}`) - selector.setPlaceholder(`Page ${curPage+1}/${fieldLength}`) - - for (var i = 0; i < fieldLength; i++) { - selector.addOptions([ - { - label: `Page ${i+1}/${(fieldLength)}`, - value: `${i}`, - }]) - } - if (!idFields[1]) { - selector.setDisabled(true) - } - row.addComponents([selector]) - return row - -} - - -function generateEmbedFields(embed, idFields,usernameFields,discordnamefields, curPage) { - embed.addFields([{ - name: 'Id', - value: idFields[curPage], - inline: true - }, { - name: 'Name', - value: usernameFields[curPage], - inline: true - }, { - name: 'Discord', - value: discordnamefields[curPage], - inline: true - }]) -} - - -module.exports = { - data: new SlashCommandBuilder() - .setName('playerlist') - .setDescription('Shows a list of all Players'), - async execute(interaction, exports) { - var tempReply = await prepareGenericEmbed('```Processing Playerlist..```') - await interaction.reply({ - embeds: [tempReply] - }) - - var players = await exports[EasyAdmin].getCachedPlayers() - var embedTimestamp = Date.now() - - var embed = new EmbedBuilder() - .setColor(65280) - .setTimestamp() - - var idFields = [] - var usernameFields = [] - var discordnamefields = [] - var ids = '' - var usernames = '' - var discordnames = '' - var curPage = 0 - var row - - if (getPlayers().length != 0) { - for (let player of Object.values(players).entries()) { - if (!player.dropped) { - - if (ids.length >= 500 || usernames.length >= 500 || discordnames.length >= 500) { - idFields.push(ids) - usernameFields.push(usernames) - discordnamefields.push(discordnames) - ids = '' - usernames = '' - discordnames = '' - } - - var discordAccount = await getDiscordAccountFromPlayer(player) - if (discordAccount) { - discordAccount = discordAccount.tag - } else { - discordAccount = 'N/A' - } - - ids += `\n${player.id}` - usernames += `\n${player.name}` - discordnames += `\n${discordAccount}` - } - } - idFields.push(ids) - usernameFields.push(usernames) - discordnamefields.push(discordnames) - - generateEmbedFields(embed,idFields,usernameFields,discordnamefields,0) - - row = generatePaginatorRow(idFields, curPage, embedTimestamp) - if (idFields.length > 1) { - const filter = i => (i.customId === `pageSelector${embedTimestamp}`) - const collector = interaction.channel.createMessageComponentCollector({ - filter, - time: 120000 - }) - - collector.on('collect', async i => { - const embed = new EmbedBuilder() - .setColor(65280) - .setTimestamp() - if (i.customId === `pageSelector${embedTimestamp}`) { - curPage = parseInt(i.values[0]) - } - generateEmbedFields(embed,idFields,usernameFields,discordnamefields,curPage) - - const row = generatePaginatorRow(idFields, curPage, embedTimestamp) - - await interaction.editReply({ - embeds: [embed], - components: [row] - }) - await i.deferUpdate() - }) - } - } else { - embed = new EmbedBuilder() - .setColor(16777214) - .setTimestamp() - .addFields([{name: 'Player List', value: 'There are no players on the server!'}]) - row = generatePaginatorRow(idFields, 0, 0) - } - - await interaction.editReply({ - embeds: [embed], - components: [row] - }) - }, -} \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/refreshperms.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/refreshperms.js deleted file mode 100644 index b30b95e9a..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/refreshperms.js +++ /dev/null @@ -1,30 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('refreshperms') - .setDescription('Refreshes your EasyAdmin permissions') - .addUserOption(option => - option.setName('user') - .setDescription('the user to refresh permissions for, optional.') - .setRequired(false)), - - async execute(interaction) { - var member = interaction.member - let user = interaction.options.getUser('user') - if (user && user.id == member.id) user = null - if(user && !await DoesGuildMemberHavePermission(interaction.member, `bot.${interaction.commandName}`) == true) { - await interaction.reply({ content: 'You don\'t have permission to refresh other users permissions!', ephemeral: true }) - return - } else if (user) { - member = await interaction.guild.members.fetch(user.id) - } - - let username = (user && `${member.displayName}'s`) || 'your' - await refreshRolesForMember(member) - - var embed = await prepareGenericEmbed(`Successfully refreshed ${username} permissions.`) - - await interaction.reply({ embeds: [embed]}) - }, -} \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_ace.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_ace.js deleted file mode 100644 index 526aa7681..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_ace.js +++ /dev/null @@ -1,52 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('remove_ace') - .setDescription('Removes a permission from a group, saves into easyadmin_permissions.cfg'), - async execute(interaction, exports) { - var timestamp = Date.now() - - const modal = new ModalBuilder() - .setCustomId('removeaceModal'+timestamp) - .setTitle('Remove ACE') - - const groupName = new TextInputBuilder() - .setCustomId('groupName') - .setLabel('Group Name') - .setStyle(TextInputStyle.Short) - .setRequired(true) - .setMaxLength(120) - .setPlaceholder('group.admin') - - const permission = new TextInputBuilder() - .setCustomId('permission') - // The label is the prompt the user sees for this input - .setLabel('Permission') - // Short means only a single line of text - .setStyle(TextInputStyle.Short) - .setRequired(true) - .setMaxLength(120) - .setPlaceholder('easyadmin.bot.playerlist') - - const firstActionRow = new ActionRowBuilder().addComponents(groupName) - const secondActionRow = new ActionRowBuilder().addComponents(permission) - - modal.addComponents(firstActionRow, secondActionRow) - - interaction.showModal(modal) - - const filter = (interaction) => interaction.customId === 'removeaceModal'+timestamp - interaction.awaitModalSubmit({ filter, time: 120000 }) - .then(async (interaction) => { - var group = interaction.fields.getTextInputValue('groupName') - var permission = interaction.fields.getTextInputValue('permission') - var query = `remove_ace ${group} ${permission} allow` - exports[EasyAdmin].RemoveFromFile('easyadmin_permissions.cfg', `add_ace ${group} ${permission} allow`) - - ExecuteCommand(query) - - interaction.reply(`\`${query}\` has been executed and saved.`) - }).catch(async () => {}) // silently catch error, happens if the form times out - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_group.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_group.js deleted file mode 100644 index 75fb43963..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/remove_group.js +++ /dev/null @@ -1,27 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('remove_group') - .setDescription('Removes a group from a User (ACE), saves into easyadmin_permissions.cfg') - .addUserOption(option => - option.setName('user') - .setDescription('The user') - .setRequired(true)) - .addStringOption(option => - option.setName('group') - .setDescription('the group, for example, group.admin') - .setRequired(true)), - async execute(interaction, exports) { - const user = interaction.options.getUser('user').id - const groupName = interaction.options.getString('group') - - - var query = `remove_principal identifier.discord:${user} ${groupName}` - exports[EasyAdmin].RemoveFromFile('easyadmin_permissions.cfg', `add_principal identifier.discord:${user} ${groupName}`) - - ExecuteCommand(query) - - interaction.reply(`\`${query}\` has been executed and saved.`) - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/screenshot.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/screenshot.js deleted file mode 100644 index a95d7eff4..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/screenshot.js +++ /dev/null @@ -1,52 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('screenshot') - .setDescription('Takes a screenshot of the player\'s screen') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - var embed = await prepareGenericEmbed('Taking Screenshot, please wait.') - await interaction.reply({ embeds: [embed]}) - - - var inProgress = await exports[EasyAdmin].isScreenshotInProgress() - if (inProgress) { - let embed = await prepareGenericEmbed('A screenshot is already in progress! Please try again later.') - interaction.editReply({ embeds: [embed]}) - return - } - - const user = await findPlayerFromUserInput(userOrId) - if (!user || user.dropped) { - interaction.editReply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - emit('EasyAdmin:TakeScreenshot', user.id) - - const screenshotHandler = async function (result) { - if (result == 'ERROR') { return } - - var screenshotUrl = await exports[EasyAdmin].matchURL(result.toString()) - RemoveEventHandler('EasyAdmin:TookScreenshot', screenshotHandler) - clearTimeout(failedTimeout) - - let embed = await prepareGenericEmbed(`Screenshot of **${user.name}**'s game taken.`,undefined,undefined,undefined,screenshotUrl) - await interaction.editReply({ embeds: [embed]}) - } - - - onNet('EasyAdmin:TookScreenshot', screenshotHandler) - - var failedTimeout = setTimeout(async function () { - RemoveEventHandler('EasyAdmin:TookScreenshot', screenshotHandler) - let embed = await prepareGenericEmbed(`Screenshot of **${user.name}**'s game failed!`, undefined, 16711680) - await interaction.editReply({ embeds: [embed]}) - }, 25000) - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/slap.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/slap.js deleted file mode 100644 index 81c5dfbcf..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/slap.js +++ /dev/null @@ -1,38 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('slap') - .setDescription('Substracts amount of HP from player') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)) - .addIntegerOption(option => - option.setName('amount') - .setDescription('Amount of HP to slap the user for.') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - const slapAmount = interaction.options.getInteger('amount') - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var ret = exports[EasyAdmin].slapPlayer(user.id, slapAmount) - - if (ret) { - let embed = await prepareGenericEmbed(`Successfully slapped **${user.name}** for ${slapAmount} HP.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Could not slap **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unban.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unban.js deleted file mode 100644 index 88c274b31..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unban.js +++ /dev/null @@ -1,27 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('unban') - .setDescription('unbans a User') - .addIntegerOption(option => - option.setName('banid') - .setDescription('Ban ID') - .setRequired(true)), - async execute(interaction, exports) { - const banId = interaction.options.getInteger('banid') - - - var ret = await exports[EasyAdmin].unbanPlayer(banId) - - if (ret == true) { - let embed = await prepareGenericEmbed(`Successfully removed Ban **#${banId}**.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Failed to remove ban **#${banId}**, make sure the ID is valid.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unfreeze.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unfreeze.js deleted file mode 100644 index 98542c92b..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unfreeze.js +++ /dev/null @@ -1,33 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('unfreeze') - .setDescription('Unfreezes player') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var ret = await exports[EasyAdmin].freezePlayer(user.id, false) - - if (ret) { - let embed = await prepareGenericEmbed(`Successfully unfroze **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Could not unfreeze **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unmute.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unmute.js deleted file mode 100644 index e023d5268..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/unmute.js +++ /dev/null @@ -1,33 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('unmute') - .setDescription('Unmutes a User') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var ret = await exports[EasyAdmin].mutePlayer(user.id, false) - - if (ret) { - let embed = await prepareGenericEmbed(`Successfully unmuted **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } else { - let embed = await prepareGenericEmbed(`Could not unmute **${user.name}**.`) - - await interaction.reply({ embeds: [embed]}) - } - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/warn.js b/server-data/resources/[esx]/EasyAdmin/server/bot/commands/warn.js deleted file mode 100644 index c3749851c..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/commands/warn.js +++ /dev/null @@ -1,37 +0,0 @@ - - -module.exports = { - data: new SlashCommandBuilder() - .setName('warn') - .setDescription('Warn a User') - .addStringOption(option => - option.setName('user') - .setDescription('Username or ID') - .setRequired(true)) - .addStringOption(option => - option.setName('reason') - .setDescription('Reason Text') - .setRequired(true)), - async execute(interaction, exports) { - const userOrId = interaction.options.getString('user') - const reason = exports[EasyAdmin].formatShortcuts(interaction.options.getString('reason')) - - - const user = await findPlayerFromUserInput(userOrId) - - if (!user || user.dropped) { - interaction.reply({ content: 'Sorry, i couldn\'t find any user with the infos you provided.', ephemeral: true}) - return - } - - var src = interaction.member.user.tag - var ret = await exports[EasyAdmin].warnPlayer(src, user.id, reason) - var embed - if (ret) { - embed = await prepareGenericEmbed(`Successfully warned **${user.name}** for **${reason}**`) - } else { - embed = await prepareGenericEmbed('Could not warn this user. (Maybe this user is immune)') - } - await interaction.reply({ embeds: [embed]}) - }, -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/functions.js b/server-data/resources/[esx]/EasyAdmin/server/bot/functions.js deleted file mode 100644 index 037c30594..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/functions.js +++ /dev/null @@ -1,131 +0,0 @@ -/*eslint no-global-assign: "off", no-unused-vars: "off"*/ -// this file contains util functions the bot uses - -async function prepareGenericEmbed(message,feature,colour,title,image,customAuthor,description,timestamp) { - - if (feature && await exports[EasyAdmin].isWebhookFeatureExcluded(feature)) { - return - } - - const embed = new EmbedBuilder() - .setColor(colour || 16777214) - if (timestamp != false) { - embed.setTimestamp() - } - if (message) { - embed.addFields([{name: `**${(title || 'EasyAdmin')}**`, value: message}]) - } - if (description) { - embed.setDescription(description) - } - - if (customAuthor) { - embed.setAuthor(customAuthor) - } - - if (image) { - embed.setImage(image) - } - - return embed -} - -async function findPlayerFromUserInput(input) { - var user - - var players = await exports[EasyAdmin].getCachedPlayers() - - Object.keys(players).forEach(function(key) { - var player = players[key] - var name = player.name - if(!isNaN(input)) { - if (player.id == input) { - user = player - } - } else { - if (name.search(input) != -1) { - user = player - } - } - }) - - return user -} - - -async function DoesGuildMemberHavePermission(member, object) { // wrapper for Discord Permissions, use export for Player Permissions. - if (!member || !object) { return false } - var memberId = member.id - if(!memberId) { - return false - } - if (object.search('easyadmin.') == -1) { - object = `easyadmin.${object}` - } - - if (member.guild.ownerId === memberId) { // guild owner always has permissions, to everything. - return true - } - - - var allowed=IsPrincipalAceAllowed(`identifier.discord:${memberId}`, object) - return allowed -} - - -async function getDiscordAccountFromPlayer(user) { - var discordAccount = false - if (!isNaN(user)) { - user = await exports[EasyAdmin].getCachedPlayer(user) - } - - for (let identifier of user.identifiers) { - if (identifier.search('discord:') != -1) { - discordAccount = await client.users.fetch(identifier.substring(identifier.indexOf(':') + 1)) - } - } - - return discordAccount -} - - -async function getPlayerFromDiscordAccount(user) { - var id = user.id - - var players = await exports[EasyAdmin].getCachedPlayers() - - for (let [index, player] of Object.values(players).entries()) { - for (let identifier of player.identifiers) { - if (identifier == `discord:${id}`) { - return player - } - } - } - - return false -} - -async function refreshRolesForMember(member) { - var roles = await member.roles.cache.keys() - for (var role of roles) { - emit('debug', `role sync for ${member.user.tag} add_principal identifier.discord:${member.id} role:${role}`) - ExecuteCommand(`add_principal identifier.discord:${member.id} role:${role}`) - } - emit('debug', `roles synced for ${member.user.tag}`) -} - -async function refreshRolesForUser(user,roles) { - for (var role of roles) { - emit('debug', `role sync for ${user.tag} add_principal identifier.discord:${user.id} role:${role}`) - ExecuteCommand(`add_principal identifier.discord:${user.id} role:${role}`) - } - emit('debug', `roles synced for ${user.tag}`) -} - -// converts Lua format string to JS format string (e.g. %s -> %s) and replaces %s with arguments -function format(str, ...args) { - let formatted = str.replace(/%s/g, function() { - return args.shift() - }) - return formatted -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/logging.js b/server-data/resources/[esx]/EasyAdmin/server/bot/logging.js deleted file mode 100644 index c7f76a382..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/logging.js +++ /dev/null @@ -1,48 +0,0 @@ - - -botLogForwards = [] -async function addBotLogForwarding(source,args) { - var player=source - - if (await exports[GetCurrentResourceName()].DoesPlayerHavePermission(player, 'server')) { - var feature = args[0] - var channel = args[1] - - if (!feature || !parseInt(channel)) { - console.error('Invalid Usage! ea_addBotLogForwarding feature channelId') - return false - } - - console.log(`Added log fwd ${feature} => ${channel}`) - botLogForwards[feature] = channel - return true - } -} - - -RegisterCommand('ea_addBotLogForwarding', addBotLogForwarding) - - -async function LogDiscordMessage(text, feature, colour) { - if (!EasyAdmin) {return} // bot isnt running - if (GetConvar('ea_botLogChannel', '') == '') {return} - if (feature == 'report' || feature == 'calladmin') {return} // we dont care about reports, these get handled in reports.js - - const embed = await prepareGenericEmbed(text,undefined,colour) - - - var channel = await client.channels.cache.get(botLogForwards[feature] || GetConvar('ea_botLogChannel', '')) - - - if (channel) { - channel.send({ embeds: [embed] }).catch((error) => { - console.error('^7Failed to log message, please make sure you gave the bot permission to write in the log channel!\n\n') - console.error(error) - - }) - } else { - console.error('^7Failed to log message, please make sure you gave the bot permission to write in the log channel!\n\n') - } -} -exports('LogDiscordMessage', LogDiscordMessage) - diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/player_events.js b/server-data/resources/[esx]/EasyAdmin/server/bot/player_events.js deleted file mode 100644 index 56756622c..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/player_events.js +++ /dev/null @@ -1,35 +0,0 @@ -if (GetConvar('ea_botToken', '') != '') { - - on('playerJoining', function () { - const player = global.source - - if (GetConvar('ea_botToken', '') != '' && GetConvar('ea_botLogChannel', '') != '') { - var msg = `Player **${exports[EasyAdmin].getName(player,true,true)}** with id **${player}** joined the Server!` - LogDiscordMessage(msg, 'joinleave') - } - }) - - on('playerConnecting', function () { - if (GetConvar('ea_botToken', '') == '') return - const player = global.source - - exports[EasyAdmin].syncDiscordRoles(player) - }) - - - on('playerDropped', () => { - if (GetConvar('ea_botToken', '') == '') return - var player = global.source - - if (GetConvar('ea_botChatBridge', '') != '') { - knownAvatars[player] = undefined - } - - if (GetConvar('ea_botLogChannel', '') != '') { - var msg = `Player **${exports[EasyAdmin].getName(player,true,true)}** left the server!` - LogDiscordMessage(msg, 'joinleave') - } - }) - -} - diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/reports.js b/server-data/resources/[esx]/EasyAdmin/server/bot/reports.js deleted file mode 100644 index ffe8d6716..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/reports.js +++ /dev/null @@ -1,69 +0,0 @@ - -var reports = [] - -function generateReportEmbed(report, disabled, closed) { - var embed = new EmbedBuilder() - .setTimestamp() - - if (closed) { - embed.setColor(808080) - } else { - embed.setColor(65280) - } - - if (report.type == 1) { - embed.addFields([{name:'Player Report', value: `**${report.reporterName}** reported **${report.reportedName}**!`}]) - } else { - embed.addFields([{name:'Admin Call', value: `**${report.reporterName}** called for an Admin!`}]) - } - - embed.addFields([ - {name:'Reason', value: `\`\`\`\n${report.reason}\`\`\``}, - {name:'Report ID', value: `#${report.id}`, inline: true}, - {name:'Claimed by', value:`${(report.claimedName || 'Noone')}`, inline: true}]) - - return {embeds: [embed]} -} - -async function logNewReport(report) { - if (GetConvar('ea_botToken', '') != '') { - var reportId = report.id - reports[reportId] = report - var reportMessage = generateReportEmbed(report) - var channel = await client.channels.cache.get(GetConvar('ea_botLogChannel', '')) - if (report.type == 1 && botLogForwards['report']) { - channel = await client.channels.cache.get(botLogForwards['report']) - } else if (report.type == 0 && botLogForwards['calladmin']) { - channel = await client.channels.cache.get(botLogForwards['calladmin']) - } - - var msg = await channel.send(reportMessage) - reports[reportId].msg = msg - } else { - return false - } -} - - -on('EasyAdmin:reportAdded', async function(reportdata) { - logNewReport(reportdata) -}) - -on('EasyAdmin:reportClaimed', async function (reportdata) { - var reportId = reportdata.id - if(reports[reportId]) { - reports[reportId].claimed = reportdata.claimed - reports[reportId].claimedName = reportdata.claimedName - let reportMessage = generateReportEmbed(reports[reportId], true) - reports[reportId].msg.edit(reportMessage) - } -}) - -on('EasyAdmin:reportRemoved', async function(reportdata) { - var reportId = reportdata.id - if(reports[reportId]) { - var reportMessage = generateReportEmbed(reports[reportId], true, true) - reports[reportId].msg.edit(reportMessage) - reports[reportId] = undefined - } -}) diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/roles.js b/server-data/resources/[esx]/EasyAdmin/server/bot/roles.js deleted file mode 100644 index 66dd54200..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/roles.js +++ /dev/null @@ -1,48 +0,0 @@ - -async function syncDiscordRoles(player) { - if (!EasyAdmin) {return} // bot is down - var user - - - try { - var identifiers = await exports[EasyAdmin].getAllPlayerIdentifiers(player) - for (let identifier of identifiers) { - if (identifier.search('discord:') != -1) { - user = await client.users.fetch(identifier.substring(identifier.indexOf(':') + 1)) - } - } - if (!user) { - return false - } - } catch (error) { - return - } - - var roles = [] - for (const id of client.guilds.cache.keys()) { - const guild = client.guilds.cache.get(id) - if (guild.members.cache.has(user.id)) { - var guildMember = await guild.members.fetch(user.id) - if (guildMember) { - roles.push(...guildMember.roles.cache.keys()) - } - } - } - refreshRolesForUser(user, roles) -} -exports('syncDiscordRoles', syncDiscordRoles) - -if (GetConvar('ea_botToken', '') != '') { - client.on('guildMemberUpdate', async function(oldMember, newMember){ - oldRoles = await oldMember.roles.cache.keys() - newRoles = await newMember.roles.cache.keys() - - for (let role of oldRoles) { - ExecuteCommand(`remove_principal identifier.discord:${oldMember.id} role:${role}`) - } - - for (let role of newRoles) { - ExecuteCommand(`add_principal identifier.discord:${newMember.id} role:${role}`) - } - }) -} diff --git a/server-data/resources/[esx]/EasyAdmin/server/bot/server_status.js b/server-data/resources/[esx]/EasyAdmin/server/bot/server_status.js deleted file mode 100644 index 6bd1d4e4e..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/bot/server_status.js +++ /dev/null @@ -1,142 +0,0 @@ - -var statusMessage -var startTimestamp = new Date() - - -async function getServerStatus(why) { - var embed = new EmbedBuilder() - .setColor(65280) - .setTimestamp() - - - var joinURL = GetConvar('web_baseUrl', '') - var buttonRow = false - - - if(joinURL != '' && joinURL.indexOf('cfx.re' != -1) && joinURL.match(/^[^A-z0-9]/)==null) { - embed.setURL(`https://${joinURL}`) - buttonRow = new ActionRowBuilder() - var button = new ButtonBuilder() - .setURL(`https://${joinURL}`) - .setLabel('Join Server') - .setStyle(ButtonStyle.Link) - buttonRow.addComponents([button]) - } else { - joinURL = '' - } - var serverName = GetConvar('sv_projectName', GetConvar('sv_hostname', 'default FXServer')) - if (serverName.length > 255) { - serverName = serverName.substring(0,255) - } - serverName = serverName.replace(/\^[0-9]/g, '') - - embed.addFields([{name: 'Server Name', value: `\`\`\`${serverName}\`\`\``}]) - - - var reports = await exports[EasyAdmin].getAllReports() - var activeReports = 0 - var claimedReports = 0 - for (let report of Object.values(reports).entries()) { - activeReports+=1 - if (report.claimed) { - claimedReports+=1 - } - } - - embed.addFields([ - { name: 'Players Online', value: `\`\`\`${getPlayers().length}/${GetConvar('sv_maxClients', '')}\`\`\``, inline: true}, - { name: 'Admins Online', value: `\`\`\`${Object.values(exports[EasyAdmin].GetOnlineAdmins()).length}\`\`\``, inline: true}, - { name: 'Reports', value: `\`\`\`${activeReports} (${claimedReports} claimed)\`\`\``, inline: true}, - { name: 'Active Vehicles', value: `\`\`\`${GetAllVehicles().length}\`\`\``, inline: true}, - { name: 'Active Peds', value: `\`\`\`${GetAllPeds().length}\`\`\``, inline: true}, - { name: 'Active Objects', value: `\`\`\`${GetAllObjects().length}\`\`\``, inline: true} - ]) - - - if (joinURL != '') { - try { - let serverId = joinURL.substring(joinURL.lastIndexOf('-')+1,joinURL.indexOf('.users.cfx.re')) - let response = await exports[EasyAdmin].HTTPRequest(`https://servers-frontend.fivem.net/api/servers/single/${serverId}`) - response = JSON.parse(response).Data - embed.addFields([{ name: 'Upvotes', value: `\`\`\`${response.upvotePower} Upvotes, ${response.burstPower} Bursts\`\`\``, inline: false}]) - - embed.setAuthor({ name: `${serverName}`, iconURL: response.ownerAvatar, url: `https://${joinURL}`}) - - } catch (error) { - console.error(error) - } - } - embed.addFields([{ name: 'Uptime', value: `\`\`\`${prettyMilliseconds(new Date()-startTimestamp, {verbose: true, secondsDecimalDigits: 0})}\`\`\``, inline: false}]) - - - - if (why) { - embed.addFields([{name: 'Last Update', value: why}]) - } - - if (buttonRow) { - return {embeds: [embed], components: [buttonRow] } - } - return {embeds: [embed] } - - - - -} - -async function updateServerStatus(why) { - if (GetConvar('ea_botStatusChannel', '') == '') { return } - var channel = await client.channels.fetch(GetConvar('ea_botStatusChannel', '')) - - if (channel == undefined) { - console.error('Failed to configure bot status channel, please make sure the channel id is correct and the bot has read and write access.') - return - } - - if (!statusMessage) { - var messagesToDelete = [] - var messages = await channel.messages.fetch({ limit: 10 }).catch((error) => { - console.error('^7Failed to configure server status channel, please make sure you gave the bot permission to write in the channel!\n\n') - console.error(error) - - }) - for (var message of messages.values()) { - if (messages.size == 1 && message.author.id == client.user.id) { - statusMessage = message - break - } else { - messagesToDelete.push(message.id) - } - } - try { - if (statusMessage) { - updateServerStatus() - return - } - await channel.bulkDelete(messagesToDelete) - } catch (error) { - console.log('Could not bulk-delete messages in botStatusChannel.') - console.error(error) - } - let embed = await prepareGenericEmbed('Fetching Server Infos..') - statusMessage = await channel.send({ embeds: [embed] }) - } - const embed = await getServerStatus(why) - statusMessage.edit(embed) - -} - -client.on('messageCreate', async msg => { - if (!msg.member || msg.author.bot) { return } // message-sender is a webhook - if(msg.author.id == userID) { - return - } - if(!msg.channel) { return } - if (msg.channel.id == GetConvar('ea_botStatusChannel', '')) { - msg.delete() - updateServerStatus('manual') - } - -}) -setTimeout(updateServerStatus, 10000) -setInterval(updateServerStatus, 180000) diff --git a/server-data/resources/[esx]/EasyAdmin/server/commands.lua b/server-data/resources/[esx]/EasyAdmin/server/commands.lua deleted file mode 100644 index ad6147693..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/commands.lua +++ /dev/null @@ -1,91 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -RegisterCommand("ea_addShortcut", function(source, args, rawCommand) - if args[2] and DoesPlayerHavePermission(source, "server.shortcut.add") then - local shortcut = args[1] - local text = table.concat(args, " ", 2) - - PrintDebugMessage("added '"..shortcut.." -> "..text.."' as a shortcut", 3) - MessageShortcuts[shortcut] = text - - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:fillShortcuts", i, 10000, MessageShortcuts) - end - end -end) - -RegisterCommand("ea_addReminder", function(source, args, rawCommand) - if args[1] and DoesPlayerHavePermission(source, "server.reminder.add") then - local text = string.gsub(rawCommand, "ea_addReminder ", "") - local text = string.gsub(text, '"', '') - - PrintDebugMessage("added '"..text.."' as a Chat Reminder", 3) - table.insert(ChatReminders, text) - end -end, false) - -RegisterCommand("ea_printIdentifiers", function(source,args,rawCommand) - if source == 0 and args[1] then -- only let Console run this command - local id = tonumber(args[1]) - print(json.encode(CachedPlayers[id].identifiers)) -- puke all identifiers into console - end -end,false) - -Citizen.CreateThread(function() - RegisterCommand("ea_generateSupportFile", function(source, args, rawCommand) - if DoesPlayerHavePermission(source, "server") then - print("SupportFile is no longer supported, please use eaDiag instead.") - end - end, false) - -end) - -RegisterCommand("spectate", function(source, args, rawCommand) - if(source == 0) then - Citizen.Trace(GetLocalisedText("badidea")) -- Maybe should be it's own string saying something like "only players can do this" or something - end - - PrintDebugMessage("Player "..getName(source,true).." Requested Spectate on "..getName(args[1],true), 3) - - if args[1] and tonumber(args[1]) and DoesPlayerHavePermission(source, "player.spectate") then - if getName(args[1]) then - TriggerClientEvent("EasyAdmin:requestSpectate", source, args[1]) - else - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("playernotfound")) - end - end -end, false) - - -RegisterCommand("setgametype", function(source, args, rawCommand) - if args[1] and DoesPlayerHavePermission(source, "server.convars") then - PrintDebugMessage("Player "..getName(source,true).." set Gametype to "..args[1], 3) - SetGameType(args[1]) - end -end, false) - -RegisterCommand("setmapname", function(source, args, rawCommand) - if args[1] and DoesPlayerHavePermission(source, "server.convars") then - PrintDebugMessage("Player "..getName(source,true).." set Map Name to "..args[1], 3) - SetMapName(args[1]) - end -end, false) - -RegisterCommand("slap", function(source, args, rawCommand) - if args[1] and args[2] and DoesPlayerHavePermission(source, "player.slap") then - local preferredWebhook = detailNotification ~= "false" and detailNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("adminslappedplayer"), getName(source, false, true), getName(args[1], true, true), args[2]), "slap", 16711680) - PrintDebugMessage("Player "..getName(source,true).." slapped "..getName(args[1],true).." for "..args[2].." HP", 3) - TriggerClientEvent("EasyAdmin:SlapPlayer", args[1], args[2]) - end -end, false) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/permission_editor.lua b/server-data/resources/[esx]/EasyAdmin/server/permission_editor.lua deleted file mode 100644 index c7576b364..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/permission_editor.lua +++ /dev/null @@ -1,342 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -Citizen.CreateThread(function() - local add_aces = {} - local add_principals = {} - function readAcePermissions() - Citizen.CreateThread(function() - add_aces, add_principals, execs = FindInfosinFile("server.cfg") - for i, config in pairs(execs) do - local tempaces, tempprincipals, _ = FindInfosinFile(config) - add_aces = mergeTables(add_aces, tempaces) - add_principals = mergeTables(add_principals, tempprincipals) - end - end) - end - - function FindInfosinFile(filename) - local path = GetResourcePath(GetCurrentResourceName()) - local occurance = string.find(path, "/resources", 1, true) - local path = string.reverse(string.sub(string.reverse(path), -occurance)) - - local filename = filename - - local lines = {} - local needsExec = true - local needsResourcePerms = true - - if filename == "server.cfg" then - needsResourcePerms = false - elseif filename == "easyadmin_permissions.cfg" then - needsExec = false - else - needsResourcePerms, needsExec = false, false - end - local changes = false - local aces, principals, execs = {}, {}, {} - - PrintDebugMessage("reading "..filename, 4) - - local file = io.open(filename, "r") - if file then - line = file:read("*line") - while line do - table.insert(lines,line) - line = file:read("*line") - end - file:close() - - for i, line in pairs(lines) do - if filename == "server.cfg" then - needsResourcePerms = false - if string.find(line, "exec easyadmin_permissions.cfg", 1, true) then - needsExec = false - end - elseif filename == "easyadmin_permissions.cfg" then - needsExec = false - if string.find(line, "add_ace resource."..GetCurrentResourceName().." command.add_ace allow", 1, true) then - needsResourcePerms = false - end - else - local broken = false - -- remove broken lines - if string.find(line, "exec easyadmin_permissions.cfg", 1, true) then - RemoveFromFile(filename, "exec easyadmin_permissions.cfg") - elseif string.find(line, "add_ace resource."..GetCurrentResourceName().." command.", 1, true) then - RemoveFromFile(filename, line) - end - end - - -- filteredLine variable converts tabs to spaces, and multiple spaces to single space - local filteredLine = string.gsub(line, "%s+", " ") - -- remove comments - filteredLine = string.gsub(filteredLine, "%s*#.*$", "") - - - - -- strip the arguments from the "add_ace", "add_principal" and "exec" commands and insert them into their respective tables - if string.find(filteredLine, "add_ace", 1, true) then - local args = string.split(filteredLine, " ") - if args[2] and args[3] and args[4] then - table.insert(aces, {file = filename, oldline = line, args[2], args[3], args[4]}) - end - elseif string.find(filteredLine, "add_principal", 1, true) then - local args = string.split(filteredLine, " ") - if args[2] and args[3] then - table.insert(principals, {file = filename, oldline = line, args[2], args[3]}) - end - elseif string.find(filteredLine, "exec", 1, true) then - local args = string.split(filteredLine, " ") - if args[2] then - table.insert(execs, args[2]) - end - end - end - - if needsExec or needsResourcePerms or changes then - local newLines = {} - if needsExec then - table.insert(newLines, "exec easyadmin_permissions.cfg") - table.insert(execs, "easyadmin_permissions.cfg") - PrintDebugMessage("Did not find `exec easyadmin_permissions.cfg`, added it automatically", 4) - changes=true - end - if needsResourcePerms then - table.insert(newLines, "# This file was generated automatically by EasyAdmin #") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.add_ace allow") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.remove_ace allow") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.add_principal allow") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.remove_principal allow") - PrintDebugMessage("Did not find `add_ace resource."..GetCurrentResourceName().."` lines, added them automatically", 4) - changes=true - end - local output = "\n" - if changes then - local file = io.open(filename, "a+") -- reopen in write mode - for i, line in pairs(newLines) do - output=output..line.."\n" - end - file:write(output) -- write our lines - file:close() - end - end - - for i,ace in pairs(aces) do - PrintDebugMessage("parsed ace ^1" - ..tostring(ace[1]).." " - ..tostring(ace[2]).." " - ..tostring(ace[3]).."^7 in " - ..filename.."\n", 4) - end - - for i,ace in pairs(principals) do - PrintDebugMessage("parsed principal ^1" - ..tostring(ace[1]).." " - ..tostring(ace[2]).."^7 in " - ..filename.."\n", 4) - end - - for i,ace in pairs(execs) do - PrintDebugMessage("parsed exec ^1" - ..tostring(ace).."^7 in " - ..filename.."\n", 4) - end - - return aces, principals, execs - else - if filename == "easyadmin_permissions.cfg" then - local file = io.open(filename, "w") - local newLines = {} - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.add_ace allow") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.remove_ace allow") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.add_principal allow") - table.insert(newLines, "add_ace resource."..GetCurrentResourceName().." command.remove_principal allow") - local output = "" - for i, line in pairs(newLines) do - output=output..line.."\n" - end - file:write(output) -- write our lines - file:close() - end - PrintDebugMessage(filename.." cannot be read, bailing.", 4) - return {}, {}, {} - end - end - - Citizen.CreateThread(function() - lockedFiles = {} - function AddToFile(filename, args) - if not GetInvokingResource() or GetInvokingResource() == GetCurrentResourceName() then -- sorry, but i _really_ dont want other resources hooking into easyadmins file edit functions. - - local path = GetResourcePath(GetCurrentResourceName()) - local occurance = string.find(path, "/resources", 1, true) - local path = string.reverse(string.sub(string.reverse(path), -occurance)) - - - local args = args - local filename = filename - while lockedFiles[filename] do - Wait(100) - end - lockedFiles[filename] = true - - - local file = io.open(filename, "a") - if file then - file:write("\n"..args) -- write our lines - file:close() - else - PrintDebugMessage(filename.." cannot be read, bailing.", 4) - return {}, {}, {} - end - Wait(500) -- without waiting after saving a file it sometimes does not properly save, some OS limitation maybe? - lockedFiles[filename] = false - end - end - exports('AddToFile', AddToFile) - - function RemoveFromFile(filename, args, partial) - if not GetInvokingResource() or GetInvokingResource() == GetCurrentResourceName() then -- sorry, but i _really_ dont want other resources hooking into easyadmins file edit functions. - - local path = GetResourcePath(GetCurrentResourceName()) - local occurance = string.find(path, "/resources", 1, true) - local path = string.reverse(string.sub(string.reverse(path), -occurance)) - - local args = args - local filename = filename - while lockedFiles[filename] do - Wait(100) - end - lockedFiles[filename] = true - - local file = io.open(filename, "r") - local lines = {} - if file then - local line = file:read("*line") - while line do - if (partial and string.find(line, args) or (not partial and line == args)) or (filename == "easyadmin_permissions.cfg" and line == "") then -- skip lines we dont want, incl. empty lines - else - table.insert(lines, line) - end - line = file:read("*line") - end - file:close() - local output = "" - for i, line in pairs(lines) do - output=output..line.."\n" - end - local file = io.open(filename, "w") - file:write(output) -- write our lines - file:close() - else - PrintDebugMessage(filename.." cannot be read, bailing.", 4) - return {}, {}, {} - end - Wait(500) -- without waiting after saving a file it sometimes does not properly save, some OS limitation maybe? - lockedFiles[filename] = false - end - end - exports('RemoveFromFile', RemoveFromFile) - end) - - RegisterServerEvent("EasyAdmin:getServerAces", function() - if DoesPlayerHavePermission(source, "server.permissions.read") then - TriggerLatentClientEvent("EasyAdmin:getServerAces", source, 100000, add_aces, add_principals) - end - end) - - RegisterServerEvent("EasyAdmin:setServerAces", function(aces,principals) - if DoesPlayerHavePermission(source, "server.permissions.write") then - local source=source - local aces=aces - local principals=principals - -- reconfigure aces - for i, ace in pairs(add_aces) do - - if not aces[i] then - if not ace.file then ace.file = "easyadmin_permissions.cfg" end - - ExecuteCommand("remove_ace "..ace[1].." "..ace[2].." "..ace[3]) - RemoveFromFile(ace.file, ace.oldline or "add_ace "..ace[1].." "..ace[2].." "..ace[3]) - - - PrintDebugMessage("Executed remove_ace "..ace[1].." "..ace[2].." "..ace[3], 4) - elseif aces[i][1] ~= ace[1] or aces[i][2] ~= ace[2] or aces[i][3] ~= ace[3] then - if not ace.file then ace.file = "easyadmin_permissions.cfg" end - if not aces[i].file then aces[i].file = "easyadmin_permissions.cfg" end - ExecuteCommand("remove_ace "..ace[1].." "..ace[2].." "..ace[3]) - RemoveFromFile(ace.file, ace.oldline or "add_ace "..ace[1].." "..ace[2].." "..ace[3]) - - ExecuteCommand("add_ace "..aces[i][1].." "..aces[i][2].." "..aces[i][3]) - AddToFile(aces[i].file, "add_ace "..aces[i][1].." "..aces[i][2].." "..aces[i][3]) - - - PrintDebugMessage("Executed remove_ace "..ace[1].." "..ace[2].." "..ace[3], 4) - PrintDebugMessage("Executed add_ace "..aces[i][1].." "..aces[i][2].." "..aces[i][3], 4) - end - end - for i, ace in pairs(aces) do - if not add_aces[i] then - if not ace.file then ace.file = "easyadmin_permissions.cfg" end - - ExecuteCommand("add_ace "..ace[1].." "..ace[2].." "..ace[3]) - AddToFile(ace.file, "add_ace "..ace[1].." "..ace[2].." "..ace[3]) - - PrintDebugMessage("Executed add_ace "..ace[1].." "..ace[2].." "..ace[3], 4) - end - end - -- reconfigure principals - for i, principal in pairs(add_principals) do - - -- set file as our permissions file in case its unset - - if not principals[i] then - if not principal.file then principal.file = "easyadmin_permissions.cfg" end - - ExecuteCommand("remove_principal "..principal[1].." "..principal[2]) - RemoveFromFile(principal.file, principal.oldline or "add_principal "..principal[1].." "..principal[2]) - - PrintDebugMessage("Executed remove_principal "..principal[1].." "..principal[2], 4) - elseif principals[i][1] ~= principal[1] or principals[i][2] ~= principal[2] then - if not principal.file then principal.file = "easyadmin_permissions.cfg" end - if not principals[i].file then principals[i].file = "easyadmin_permissions.cfg" end - - ExecuteCommand("remove_principal "..principal[1].." "..principal[2]) - RemoveFromFile(principal.file, principal.oldline or "add_principal "..principal[1].." "..principal[2]) - - - ExecuteCommand("add_principal "..principals[i][1].." "..principals[i][2]) - AddToFile(principals[i].file, "add_principal "..principals[i][1].." "..principals[i][2]) - - PrintDebugMessage("Executed remove_principal "..principal[1].." "..principal[2], 4) - PrintDebugMessage("Executed add_principal "..principals[i][1].." "..principals[i][2], 4) - end - end - for i, principal in pairs(principals) do - if not add_principals[i] then - if not principal.file then principal.file = "easyadmin_permissions.cfg" end - ExecuteCommand("add_principal "..principal[1].." "..principal[2]) - AddToFile(principal.file, "add_principal "..principal[1].." "..principal[2]) - - PrintDebugMessage("Executed add_principal "..principal[1].." "..principal[2], 4) - end - end - - - add_aces = aces - add_principals = principals - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("admineditedpermissions"), getName(source, false, true)), "permissions", 16777214) - TriggerLatentClientEvent("EasyAdmin:getServerAces", source, 100000, add_aces, add_principals) - end - end) -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/playercache.lua b/server-data/resources/[esx]/EasyAdmin/server/playercache.lua deleted file mode 100644 index fd83500f4..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/playercache.lua +++ /dev/null @@ -1,67 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -CachedPlayers = {} -- DO NOT TOUCH THIS - -Citizen.CreateThread(function() - while true do - Wait(20000) - local osTime = os.time() - local playerCacheExpiry = GetConvarInt("ea_playerCacheExpiryTime", 1800) - for i, player in pairs(CachedPlayers) do - if player.droppedTime and (osTime > player.droppedTime+playerCacheExpiry) then - PrintDebugMessage("Cache for "..player.id.." expired, removing from cache.", 3) - for i, report in pairs(reports) do - if report.reported == player.id then - reports[i] = nil - end - end - CachedPlayers[i]=nil - end - end - end -end) - -function cachePlayer(playerId) - if not CachedPlayers[playerId] then - CachedPlayers[playerId] = {id = playerId, name = getName(playerId, true), identifiers = getAllPlayerIdentifiers(playerId), immune = DoesPlayerHavePermission(playerId, "immune")} - PrintDebugMessage(getName(playerId).." has been added to cache.", 4) - return true - end - return false -end - -RegisterServerEvent("EasyAdmin:requestCachedPlayers", function() - PrintDebugMessage(getName(source, true).." requested Cache.", 4) - local src = source - if (DoesPlayerHavePermission(source, "player.ban.temporary") or DoesPlayerHavePermission(source, "player.ban.permanent")) then - TriggerLatentClientEvent("EasyAdmin:fillCachedPlayers", src, 200000, CachedPlayers) - end -end) - -function getCachedPlayers() -- this is server-only for security reasons. - return CachedPlayers -end -exports('getCachedPlayers', getCachedPlayers) - -function getCachedPlayer(id) - cachePlayer(tonumber(id)) - return CachedPlayers[tonumber(id)] -end -exports('getCachedPlayer', getCachedPlayer) - -AddEventHandler('playerDropped', function (reason) - if CachedPlayers[source] then - CachedPlayers[source].droppedTime = os.time() - CachedPlayers[source].dropped = true - end -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/reports.lua b/server-data/resources/[esx]/EasyAdmin/server/reports.lua deleted file mode 100644 index 787c0a18f..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/reports.lua +++ /dev/null @@ -1,248 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -AddEventHandler('playerDropped', function (reason) - for i, report in pairs(reports) do - if report.reporter == source or (report.reported and report.reported == source) then - removeReport(report.id) - end - end - if cooldowns[source] then - cooldowns[source] = nil - end -end) - -cooldowns = {} -- DO NOT TOUCH THIS - -Citizen.CreateThread(function() - - PlayerReports = {} - - if GetConvar("ea_enableCallAdminCommand", "true") == "true" then - RegisterCommand(GetConvar("ea_callAdminCommandName", "calladmin"), function(source, args, rawCommand) - if args[1] then - local time = os.time() - local cooldowntime = GetConvarInt("ea_callAdminCooldown", 60) - local source=source - if cooldowns[source] and cooldowns[source] > (time - cooldowntime) then - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("waitbeforeusingagain")) - return - end - - local reason = string.gsub(rawCommand, GetConvar("ea_callAdminCommandName", "calladmin").." ", "") - local reportid = addNewReport(0, source, _,reason) - for i,_ in pairs(OnlineAdmins) do - local notificationText = string.format(string.gsub(GetLocalisedText("playercalledforadmin"), "```", ""), getName(source,true,false), reason, reportid) - TriggerClientEvent("EasyAdmin:showNotification", i, notificationText) - end - - - local preferredWebhook = (reportNotification ~= "false") and reportNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("playercalledforadmin"), getName(source, true, true), reason, reportid), "calladmin", 16776960) - --TriggerClientEvent('chatMessage', source, "^3EasyAdmin^7", {255,255,255}, GetLocalisedText("admincalled")) - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("admincalled")) - - time = os.time() - cooldowns[source] = time - else - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidreport")) - end - end, false) - end - if GetConvar("ea_enableReportCommand", "true") == "true" then - RegisterCommand(GetConvar("ea_reportCommandName", "report"), function(source, args, rawCommand) - if args[2] then - local source = source - local id = args[1] - local valid = false - local minimumreports = GetConvarInt("ea_defaultMinReports", 3) - if GetConvar("ea_MinReportModifierEnabled", "true") == "true" then - if #GetPlayers() > GetConvarInt("ea_MinReportPlayers", 12) then - minimumreports = math.round(#GetPlayers()/GetConvarInt("ea_MinReportModifier", 4),0) - end - end - if id and not GetPlayerIdentifier(id, 1) then - for i, player in pairs(GetPlayers()) do - if string.find(string.lower(getName(player, true)), string.lower(id)) then - id = player - valid = true - break - end - end - else - valid = true - end - - - if id and valid then - local reason = string.gsub(rawCommand, GetConvar("ea_reportCommandName", "report").." " ..args[1].." ", "") - if not PlayerReports[id] then - PlayerReports[id] = { } - end - local addReport = true - for i, report in pairs(PlayerReports[id]) do - if report.source == source or report.sourceName == getName(source, true) then - addReport = false - end - end - if addReport then - table.insert(PlayerReports[id], {source = source, sourceName = getName(source, true), reason = reason, time = os.time()}) - local reportid = addNewReport(1, source, id, reason) - local preferredWebhook = (reportNotification ~= "false") and reportNotification or moderationNotification - SendWebhookMessage(preferredWebhook,string.format(GetLocalisedText("playerreportedplayer"), getName(source, false, true), getName(id, true, true), reason, #PlayerReports[id], minimumreports, reportid), "report", 16776960) - if GetConvar("ea_enableReportScreenshots", "true") == "true" then - TriggerEvent("EasyAdmin:TakeScreenshot", id) - end - - - for i,_ in pairs(OnlineAdmins) do - local notificationText = string.format(string.gsub(GetLocalisedText("playerreportedplayer"), "```", ""), getName(source, false, false), getName(id, true, false), reason, #PlayerReports[id], minimumreports, reportid) - TriggerClientEvent('chat:addMessage', i, { - template = '
    {0}
    ', - args = { "^3EasyAdmin Report^7\n"..notificationText }, color = { 255, 255, 255 } - }) - TriggerClientEvent("EasyAdmin:showNotification", i, notificationText) - end - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("successfullyreported")) - - if #PlayerReports[id] >= minimumreports then - TriggerEvent("EasyAdmin:addBan", id, string.format(GetLocalisedText("reportbantext"), minimumreports), os.time()+GetConvarInt("ea_ReportBanTime", 86400)) - end - else - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("alreadyreported")) - end - else - TriggerClientEvent('chat:addMessage', source, { - template = '
    {0}:
    {1}
    ', - args = { "^3EasyAdmin^7", GetLocalisedText("reportedusageerror") }, color = { 255, 255, 255 } - }) - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("reportedusageerror")) - end - else - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("invalidreport")) - end - end, false) - end - - - function addNewReport(type, reporter, reported, reason) - local t = nil - if type == 1 then - t = {type=type, reporter=reporter, reporterName=getName(reporter, true), reported=reported, reportedName=getName(reported, true),reason=reason} - else - t = {type=type, reporter=reporter, reporterName=getName(reporter, true), reason=reason} - end - t.id = #reports+1 - reports[t.id] = t - for i,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:NewReport", i, 10000, t) - end - TriggerEvent("EasyAdmin:reportAdded", t) - return t.id - end - - RegisterServerEvent("EasyAdmin:ClaimReport", function(reportId) - if DoesPlayerHavePermission(source, "player.reports.claim") then - if not reports[reportId].claimed then - reports[reportId].claimed = source - reports[reportId].claimedName = getName(source,true) - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:ClaimedReport", admin, 10000, reports[reportId]) - end - TriggerEvent("EasyAdmin:reportClaimed", reports[reportId]) - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminclaimedreport"), getName(source, false, true), reportId), "reports", 16777214) - else - TriggerClientEvent("EasyAdmin:showNotification", source, GetLocalisedText("reportalreadyclaimed")) - end - end - end) - - function removeReport(index,reporter,reported,reason) - for i, report in pairs(reports) do - if (index and i == index) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, report) - end - TriggerEvent("EasyAdmin:reportRemoved", report) - reports[i] = nil - elseif (reporter and reporter == report.reporter) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, report) - end - TriggerEvent("EasyAdmin:reportRemoved", report) - reports[i] = nil - elseif (reported and reported == report.reported) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, report) - end - TriggerEvent("EasyAdmin:reportRemoved", report) - reports[i] = nil - end - end - end - - function removeSimilarReports(report) - for i, r in pairs(reports) do - if (report.reporter and report.reported) and (report.reporter == r.reporter and report.reported == r.reported) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, r) - end - TriggerEvent("EasyAdmin:reportRemoved", r) - reports[i] = nil - end - if (report.reason and report.reporter) and (report.reason == r.reason and report.reporter == r.reporter) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, r) - end - TriggerEvent("EasyAdmin:reportRemoved", r) - reports[i] = nil - end - if (report.reported) and (report.reported == r.reported) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, r) - end - TriggerEvent("EasyAdmin:reportRemoved", r) - reports[i] = nil - end - if (report.reporter) and (report.reporter == r.reporter) then - for admin,_ in pairs(OnlineAdmins) do - TriggerLatentClientEvent("EasyAdmin:RemoveReport", admin, 10000, r) - end - TriggerEvent("EasyAdmin:reportRemoved", r) - reports[i] = nil - end - end - end - - - function getAllReports() - return reports - end - exports('getAllReports', getAllReports) - - - - RegisterServerEvent("EasyAdmin:RemoveReport", function(report) - if DoesPlayerHavePermission(source, "player.reports.process") then - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminclosedreport"), getName(source, false, true), report.id), "reports", 16777214) - removeReport(report.id) - end - end) - - RegisterServerEvent("EasyAdmin:RemoveSimilarReports", function(report) - if DoesPlayerHavePermission(source, "player.reports.process") then - SendWebhookMessage(moderationNotification,string.format(GetLocalisedText("adminclosedreport"), getName(source, false, true), report.id), "reports", 16777214) - removeSimilarReports(report) - end - end) - -end) \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/server/webhook.lua b/server-data/resources/[esx]/EasyAdmin/server/webhook.lua deleted file mode 100644 index c8f3714a2..000000000 --- a/server-data/resources/[esx]/EasyAdmin/server/webhook.lua +++ /dev/null @@ -1,63 +0,0 @@ ------------------------------------- ------------------------------------- ----- DONT TOUCH ANY OF THIS IF YOU DON'T KNOW WHAT YOU ARE DOING ----- THESE ARE **NOT** CONFIG VALUES, USE THE CONVARS IF YOU WANT TO CHANGE SOMETHING ----- ----- ----- If you are a developer and want to change something, consider writing a plugin instead: ----- https://easyadmin.readthedocs.io/en/latest/plugins/ ----- ------------------------------------- ------------------------------------- - -ExcludedWebhookFeatures = {} -RegisterCommand("ea_testWebhook", function(source, args, rawCommand) - if DoesPlayerHavePermission(source, "server") then - SendWebhookMessage(moderationNotification, "**Testing Webhook for moderationNotification**", false, 65280) - SendWebhookMessage(detailNotification, "**Testing Webhook for detailNotification**", false, 65280) - SendWebhookMessage(reportNotification, "**Testing Webhook for reportNotification**", false, 65280) - PrintDebugMessage("Webhook Message Sent") - end -end, false) - -RegisterCommand("ea_excludeWebhookFeature", function(source, args, rawCommand) - if DoesPlayerHavePermission(source, "server") then - ExcludedWebhookFeatures = Set(args) - PrintDebugMessage("Webhook excludes set", 3) - end -end, false) - - -function isWebhookFeatureExcluded(feature) - return ExcludedWebhookFeatures[feature] -end -exports('isWebhookFeatureExcluded', isWebhookFeatureExcluded) - -function SendWebhookMessage(webhook,message,feature,colour,title,image) - moderationNotification = GetConvar("ea_moderationNotification", "false") - reportNotification = GetConvar("ea_reportNotification", "false") - detailNotification = GetConvar("ea_detailNotification", "false") - - local embed = { - { - ["color"] = (colour or 16777214), - ["title"] = "**"..(title or "EasyAdmin").."**", - ["description"] = message, - ["footer"] = { - ["text"] = "EasyAdmin on "..formatDateString(os.time()), - }, - } - } - if image then - embed[1]["image"] = { ["url"] = image } - end - - if GetConvar("ea_botLogChannel", "") ~= "" then - exports[GetCurrentResourceName()]:LogDiscordMessage(message, feature, colour) - return - end - - if webhook ~= "false" and ExcludedWebhookFeatures[feature] ~= true then - PerformHttpRequest(webhook, function(err, text, headers) end, 'POST', json.encode({embeds = embed}), { ['Content-Type'] = 'application/json' }) - end -end \ No newline at end of file diff --git a/server-data/resources/[esx]/EasyAdmin/shared/util_shared.lua b/server-data/resources/[esx]/EasyAdmin/shared/util_shared.lua deleted file mode 100644 index 5948bb4cf..000000000 --- a/server-data/resources/[esx]/EasyAdmin/shared/util_shared.lua +++ /dev/null @@ -1,352 +0,0 @@ -permissions = { - ["player.ban.temporary"] = false, - ["player.ban.permanent"] = false, - ["player.ban.view"] = false, - ["player.ban.edit"] = false, - ["player.ban.remove"] = false, - ["player.kick"] = false, - ["player.spectate"] = false, - ["player.teleport.single"] = false, - ["player.slap"] = false, - ["player.freeze"] = false, - ["player.screenshot"] = false, - ["player.mute"] = false, - ["player.warn"] = false, - ["player.teleport.everyone"] = false, - ["player.reports.view"] = false, - ["player.reports.claim"] = false, - ["player.reports.process"] = false, - ["server.cleanup.cars"] = false, - ["server.cleanup.props"] = false, - ["server.cleanup.peds"] = false, - ["server.permissions.read"] = false, - ["server.permissions.write"] = false, - ["server.shortcut.add"] = false, - ["server.reminder.add"] = false, - ["server.announce"] = false, - ["server.convars"] = false, - ["server.resources.start"] = false, - ["server.resources.stop"] = false, - ["server.chat"] = false, - ["immune"] = false, - ["anon"] = false, -} - - -function PrintDebugMessage(msg,level) - loglevel = (GetConvarInt("ea_logLevel", 1)) - if not level or not tonumber(level) then level = 3 end - - if level == 1 and loglevel >= level then -- ERROR Loglevel - Citizen.Trace("^1"..GetCurrentResourceName().."^7: "..msg.."^7\n") - - if IsDuplicityVersion() then - for i,k in pairs(GetOnlineAdmins()) do - TriggerClientEvent("EasyAdmin:showNotification", i, string.gsub(msg, "%^%d", "")) - end - else - TriggerEvent("EasyAdmin:showNotification", string.gsub(msg, "%^%d", "")) - end - elseif level == 2 and loglevel >= level then -- WARN Loglevel - Citizen.Trace("^3"..GetCurrentResourceName().."^7: "..msg.."^7\n") - elseif level == 3 and loglevel >= level then -- INFO Loglevel - Citizen.Trace("^0"..GetCurrentResourceName().."^7: "..msg.."^7\n") - elseif level == 4 and loglevel >= level then -- DEV Loglevel - Citizen.Trace("^7"..GetCurrentResourceName().."^7: "..msg.."^7\n") - elseif level > 4 and loglevel >= level then -- anything above 4 shouldn't exist, but kept just in case - Citizen.Trace("^5"..GetCurrentResourceName().."^7: "..msg.."^7\n") - end -end - -if IsDuplicityVersion() then - if GetConvar("ea_enableDebugging", "false") ~= "false" or GetConvarInt("ea_logLevel", 1) ~= 1 then - SetConvar("ea_enableDebugging", "false") - if GetConvarInt("ea_logLevel", 1) == 1 then - SetConvar("ea_logLevel", 3) - end - if GetConvarInt("ea_logLevel", 1) > 1 then - PrintDebugMessage("Debug Messages Enabled, Verbosity is ^2"..GetConvarInt("ea_logLevel", 1).."^7.", 2) - end - else - enableDebugging = false - end -end - -if not IsDuplicityVersion() then - RegisterNUICallback("keyboardFinished", function(data, cb) - keyboardResult = data.result - keyboardState = data.state - cb('ok') - end) - - function ttsSpeechItem(item) - local ttsText = "" - if not item or GetResourceKvpInt('ea_tts') == 0 then return end - if type(item.Text) == "table" then - if item.Text._Text then - ttsText = item.Text._Text - if item.Label then - ttsText = ttsText .. ", " .. item.Label.Text._Text - end - end - elseif type(item.Text) == "function" then - ttsText = item.Base.Text._Text - if item.Checked == true then - ttsText = ttsText .. ", Checked" - elseif item.Checked == false then - ttsText = ttsText .. ", Unchecked" - end - if item.ItemText then - ttsText = ttsText .. ", " .. item.ItemText._Text - end - end - SendNUIMessage({action= "speak", text=ttsText}) - end - - function ttsSpeechText(text) - if not text or GetResourceKvpInt('ea_tts') == 0 then return end - SendNUIMessage({action= "speak", text=text}) - end -end - - -function displayKeyboardInput(title,default,maxLength) - if alreadyTyping then return nil end - keyboardResult, keyboardState = nil - local label = GetLabelText(title) - - SetNuiFocus(true, true) - SendNUIMessage({action= "open", title=label, default=default, maxLength=maxLength, resource=GetCurrentResourceName()}) - - alreadyTyping = true - - while not keyboardState do --While typing is not aborted and not finished, this loop waits - Citizen.Wait(0) - end - - alreadyTyping = false - SetNuiFocus(false,false) - if keyboardState == 0 then - return keyboardResult - else - return nil - end -end - -function copyToClipboard(text) - SendNUIMessage({action= "clip", text=text}) - TriggerEvent("EasyAdmin:showNotification", GetLocalisedText("copiedtoclipboard")) -end - -function DoesPlayerHavePermission(player, object) - if IsDuplicityVersion() then - local haspermission = false - if (player == 0 or player == "") then - return true - end-- Console. It's assumed this will be an admin with access. - - if not string.find(object, "easyadmin.") then -- compatability with outdated plugins - object = "easyadmin."..object - end - - if IsPlayerAceAllowed(player,object) then -- check if the player has access to this permission - haspermission = true - PrintDebugMessage(getName(player, true).." has Permissions for "..object..".", 4) - else - haspermission = false - PrintDebugMessage(getName(player, true).." does not have Permissions for "..object..".", 4) - end - return haspermission - else - return (permissions[object] or false) - end -end -exports('DoesPlayerHavePermission', DoesPlayerHavePermission) - -function DoesPlayerHavePermissionForCategory(player, object) - for perm in pairs(permissions) do - if string.startswith(perm, object) then - if DoesPlayerHavePermission(player, perm) then - return true - end - end - end - return false -end -exports('DoesPlayerHavePermissionForCategory', DoesPlayerHavePermissionForCategory) - - -function GetVersion() - local resourceName = GetCurrentResourceName() - local version = GetResourceMetadata(resourceName, 'version', 0) - local is_master = GetResourceMetadata(resourceName, 'is_master', 0) == "yes" or false - return version, is_master -end -exports('GetVersion', GetVersion) - - -function loadLanguageStrings() - local strfile = LoadResourceFile(GetCurrentResourceName(), "language/"..GetConvar("ea_LanguageName", "en")..".json") - if strfile then - strings = json.decode(strfile)[1] - else - strings = {language=GetConvar("ea_LanguageName", "en")} - end -end - -function GetLocalisedText(string) - if not strings then return "Strings not Loaded yet!" end - if not string then return "No String!" end - if strings[string] then - return strings[string] - else - return "String "..string.." not found in "..strings.language - end -end -exports('GetLocalisedText', GetLocalisedText) - -function formatDateString(string) - local dateFormat = GetConvar("ea_dateFormat", '%d/%m/%Y %H:%M:%S') - return os.date(dateFormat, string) -end -exports('formatDateString', formatDateString) - -function formatShortcuts(thisstring) - if not thisstring then return thisstring end - local cleanString = string.gsub(string.lower(thisstring), " ", "") - for shortcut,value in pairs(MessageShortcuts) do - if string.lower(shortcut) == cleanString then - thisstring = value - end - end - return thisstring -end -exports('formatShortcuts', formatShortcuts) - -function formatRightString(thisstring, customWidth) - if not thisstring then return thisstring end -- in case string is nil, just yeet it back. - local width = (customWidth or maxRightTextWidth) - if string.len(thisstring) > width then - thisstring = string.sub(thisstring, 1, width)..".." - end - - return thisstring -end - --- some util funcs so i dont have to mess with NativeUI Source Code. -function getMenuItemTitle(item) - if (item.Base and type(item.Base.Text) == "table" and item.Base.Text._Text) then - return item.Base.Text._Text - elseif (item.Text and type(item.Text) == "table" and item.Text._Text) then - return item.Text._Text - end -end - -function setMenuItemTitle(item,text) - if (item.Base and type(item.Base.Text) == "table" and item.Base.Text._Text) then - item.Base.Text._Text = text - elseif (item.Text and type(item.Text) == "table" and item.Text._Text) then - item.Text._Text = text - end -end - -function getCachedPlayer(playerId) - if CachedPlayers[playerId] then - return CachedPlayers[playerId] - else - return false - end -end -exports('getCachedPlayer', getCachedPlayer) - -function math.round(num, numDecimalPlaces) - if numDecimalPlaces and numDecimalPlaces>0 then - local mult = 10^numDecimalPlaces - return math.floor(num * mult + 0.5) / mult - end - return math.floor(num + 0.5) -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.reverse(s) - local r = "" - for p,c in utf8.codes(s) do - r = utf8.char(c)..r - end - return r -end - -function string.startswith(string,start) - return string:sub(1,string.len(start))==start -end - ---- http://www.lua.org/pil/11.5.html -function Set (list) - local set = {} - for _, l in ipairs(list) do set[l] = true end - return set -end - --- Convert a lua table into a lua syntactically correct string -function table_to_string(tbl) - return json.encode(tbl) -end - -function mergeTables(t1, t2) - local t = t1 - for i,v in pairs(t2) do - table.insert(t, v) - end - return t -end - - --- terrible function to look for URLs in a string -function matchURL(text_with_URLs) - - local domains = [[.ac.ad.ae.aero.af.ag.ai.al.am.an.ao.aq.ar.arpa.as.asia.at.au.aw.ax.az.ba.bb.bd.be.bf.bg.bh.bi.biz.bj.bm.bn.bo.br.bs.bt.bv.bw.by.bz.ca.cat.cc.cd.cf.cg.ch.ci.ck.cl.cm.cn.co.com.coop.cr.cs.cu.cv.cx.cy.cz.dd.de.dj.dk.dm.do.dz.ec.edu.ee.eg.eh.er.es.et.eu.fi.firm.fj.fk.fm.fo.fr.fx.ga.gb.gd.ge.gf.gh.gi.gl.gm.gn.gov.gp.gq.gr.gs.gt.gu.gw.gy.hk.hm.hn.hr.ht.hu.id.ie.il.im.in.info.int.io.iq.ir.is.it.je.jm.jo.jobs.jp.ke.kg.kh.ki.km.kn.kp.kr.kw.ky.kz.la.lb.lc.li.lk.lr.ls.lt.lu.lv.ly.ma.mc.md.me.mg.mh.mil.mk.ml.mm.mn.mo.mobi.mp.mq.mr.ms.mt.mu.museum.mv.mw.mx.my.mz.na.name.nato.nc.ne.net.nf.ng.ni.nl.no.nom.np.nr.nt.nu.nz.om.org.pa.pe.pf.pg.ph.pk.pl.pm.pn.post.pr.pro.ps.pt.pw.py.qa.re.ro.ru.rw.sa.sb.sc.sd.se.sg.sh.si.sj.sk.sl.sm.sn.so.sr.ss.st.store.su.sv.sy.sz.tc.td.tel.tf.tg.th.tj.tk.tl.tm.tn.to.tp.tr.travel.tt.tv.tw.tz.ua.ug.uk.um.us.uy.va.vc.ve.vg.vi.vn.vu.web.wf.ws.xxx.ye.yt.yu.za.zm.zr.zw]] - - local tlds = {} - for tld in domains:gmatch'%w+' do - tlds[tld] = true - end - local function max4(a,b,c,d) return math.max(a+0, b+0, c+0, d+0) end - local protocols = {[''] = 0, ['http://'] = 0, ['https://'] = 0, ['ftp://'] = 0} - local finished = {} - - for pos_start, url, prot, subd, tld, colon, port, slash, path in - text_with_URLs:gmatch'()(([%w_.~!*:@&+$/?%%#-]-)(%w[-.%w]*%.)(%w+)(:?)(%d*)(/?)([%w_.~!*:@&+$/?%%#=-]*))' - do - if protocols[prot:lower()] == (1 - #slash) * #path and not subd:find'%W%W' - and (colon == '' or port ~= '' and port + 0 < 65536) - and (tlds[tld:lower()] or tld:find'^%d+$' and subd:find'^%d+%.%d+%.%d+%.$' - and max4(tld, subd:match'^(%d+)%.(%d+)%.(%d+)%.$') < 256) - then - finished[pos_start] = true - return url - end - end - - for pos_start, url, prot, dom, colon, port, slash, path in - text_with_URLs:gmatch'()((%f[%w]%a+://)(%w[-.%w]*)(:?)(%d*)(/?)([%w_.~!*:@&+$/?%%#=-]*))' - do - if not finished[pos_start] and not (dom..'.'):find'%W%W' - and protocols[prot:lower()] == (1 - #slash) * #path - and (colon == '' or port ~= '' and port + 0 < 65536) - then - return url - end - end -end -exports('matchURL', matchURL) \ No newline at end of file