diff --git a/[qb]/qb-tunerchip/.github/ISSUE_TEMPLATE/bug_report.md b/[qb]/qb-tunerchip/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..62f702f --- /dev/null +++ b/[qb]/qb-tunerchip/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve or fix something +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. A stranger to qbcore should be able to read your bug report and understand how to reproduce it themselves and understand how the feature should work normally. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Use this item '....' (item's name from shared.lua if applicable) +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Questions (please complete the following information):** + - When you last updated: [e.g. last week] + - Are you using custom resource? which ones? [e.g. zdiscord, qb-target] + - Have you renamed `qb-` to something custom? [e.g. yes/no] + +**Additional context** +Add any other context about the problem here. diff --git a/[qb]/qb-tunerchip/.github/ISSUE_TEMPLATE/feature-request.md b/[qb]/qb-tunerchip/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..9e9bf3e --- /dev/null +++ b/[qb]/qb-tunerchip/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest an idea for QBCore +title: "[SUGGESTION]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. and with as much detail as possible how it would function in your opinion. Please try to keep it unique. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered for people to have in mind just in case the main idea isn't liked but a derivative is. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/[qb]/qb-tunerchip/.github/auto_assign.yml b/[qb]/qb-tunerchip/.github/auto_assign.yml new file mode 100644 index 0000000..2a80921 --- /dev/null +++ b/[qb]/qb-tunerchip/.github/auto_assign.yml @@ -0,0 +1,17 @@ +# Set to true to add reviewers to pull requests +addReviewers: true + +# Set to true to add assignees to pull requests +addAssignees: author + +# A list of reviewers to be added to pull requests (GitHub user name) +reviewers: + - /maintenance + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +skipKeywords: + - wip + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 0 \ No newline at end of file diff --git a/[qb]/qb-tunerchip/.github/contributing.md b/[qb]/qb-tunerchip/.github/contributing.md new file mode 100644 index 0000000..21fb806 --- /dev/null +++ b/[qb]/qb-tunerchip/.github/contributing.md @@ -0,0 +1,201 @@ +# Contributing to QBCore + +First of all, thank you for taking the time to contribute! + +These guidelines will help you help us in the best way possible regardless of your skill level. We ask that you try to read everything related to the way you'd like to contribute and try and use your best judgement for anything not covered. + +### Table of Contents + +[Code of Conduct](#code-of-conduct) + +[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Suggesting Features / Enhancements](#suggesting-features--enhancements) + * [Your First Code Contribution](#your-first-code-contribution) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Git Commit Messages](#git-commit-messages) + * [Lua Styleguide](#lua-styleguide) + * [JavaScript Styleguide](#javascript-styleguide) + + + +## Code of Conduct + +- Refrain from using languages other than English. +- Refrain from discussing any politically charged or inflammatory topics. +- Uphold mature conversations and respect each other; excessive profanity, hate speech or any kind of harassment will not be tolerated. +- No advertising of any kind. +- Follow these guidelines. +- Do not mention members of github unless a question is directed at them and can't be answered by anyone else. +- Do not mention any of the development team for any reason. We will read things as we get to them. + +## I don't want to read this whole thing I just have a question!!! + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +* [QBCore Website](https://qbcore.org) +* [QBCore Discord](https://discord.gg/qbcore) +* [FiveM Discord - #qbcore channel](https://discord.gg/fivem) + + + + + + + + + + +## How Can I Contribute? + +### Reporting Bugs + +The easiest way to contribute for most people is just to report bugs you find cause if nobody reports it there's a chance we'll never know it exists and then we'll never fix it. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out the bug-report template with the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Check the docs** There's a chance what you see as a bug might just work differently than you expect and if you think it could work better consider a feature enhancement report instead. +* **Search the [discord](https://discord.gg/qbcore)** to see if anyone else has run into the issue and see if it was solved through user error or code changes. (if the code change isn't pending a PR and you know what you're doing consider submitting one following [Pull Requests](#pull-requests) ) +* **Determine which resource the problem should be reported in**. If the bug is related to the inventory for example report this bug under qb-inventory rather than under qb-core or some other resource. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your bug is related to, create an issue on that repository and provide the following information by filling in bug-report template. + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. If something happened with only a specific group or single item but not others, specify that. +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **Include screenshots** which show the specific bug in action or before and after. +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions if possible: + +* **Did the problem start happening recently** (e.g. after updating to a new version of QBCore?) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of QBCore?** What's the most recent commit in which the problem doesn't happen? +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your setup: + +* **When was your QBCore last updated?** +* **What OS is the server running on**? +* **Which *extra* resources do you have installed?** + + +--- + + +### Suggesting Features / Enhancements + +This section guides you through submitting an enhancement suggestion for QBCore, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion. + +Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in feature request template, including the steps that you imagine you would take if the feature you're requesting existed. + +#### Before Submitting An Enhancement Suggestion + +* **Make sure it doesn't already exist.** Sounds silly, but there's a lot of features built in to qbcore that people don't realize so take a look through the docs and stuff to make sure it's not already there. +* **Check if there's already PR which provides that enhancement.** +* **Determine which resource the enhancement should be suggested in.** if it fits with another resource suggest it in that resource. if it would be it's own resource suggest it in the main qb-core repository. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your enhancement suggestion is related to, create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of QBCore which the suggestion is related to. +* **Explain why this enhancement would be useful.** +* **Be creative and unique.** Stealing ideas from popular servers 1:1 detail isn't going to get accepted. + + +--- + + + +### Your First Code Contribution + +Unsure where to begin contributing to QBCore? You can start by looking through these `beginner` and `help-wanted` issues. + + + +--- + + +### Pull Requests + +The process described here has several goals: + +- Maintain QBCore's quality. +- Fix problems that are important to users. +- Engage the community in working toward the best possible QBCore. +- Enable a sustainable system for QBCore's maintainers to review contributions. + +Please follow these steps to have your contribution considered by the maintainers: + +1. Follow all instructions in The Pull Request template. +2. Follow the [styleguides](#styleguides). +3. Await review by the reviewer(s). + +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + + +--- + +## Styleguides + +### Git Commit Messages + +* Limit the first line to 72 characters or less. +* Reference issues and pull requests liberally after the first line. +* Consider starting the commit message with an applicable emoji: + * :art: `:art:` when improving the format/structure of the code + * :racehorse: `:racehorse:` when improving performance + * :memo: `:memo:` when writing docs + * :bug: `:bug:` when fixing a bug + * :fire: `:fire:` when removing code or files + * :white_check_mark: `:white_check_mark:` when adding tests + * :lock: `:lock:` when dealing with security + * :arrow_up: `:arrow_up:` when upgrading dependencies + * :arrow_down: `:arrow_down:` when downgrading dependencies + * :shirt: `:shirt:` when removing linter warnings + +### Lua Styleguide + +All lua code should be done using all the best practices of proper lua using the easiest to read yet fastest/most optimized methods of execution. + +- Use 4 Space indentation +- Aim for lua 5.4 (include `lua54 'yes'` in the fxmanifest.lua) +- Use `PlayerPedId()` instead of `GetPlayerPed(-1)` +- Use `#(vector3 - vector3)` instead of `GetDistanceBetweenCoords()` +- Don't create unnecessary threads. always try to find a better method of triggering events +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables +- For distance checking loops set longer waits if you're outside of a range +- Job specific loops should only run for players with that job, don't waste cycles +- When possible don't trust the client, esspecially with transactions +- Balance security and optimizations +- [Consider this Lua Performance guide](https://springrts.com/wiki/Lua_Performance) +- Use local varriables everywhere possible +- Make use of config options where it makes sense making features optional or customizable +- Instead of `table.insert(myTable, "Value")` use `myTable[#myTable + 1] = "Value"` +- Instead of `table.insert(ages, "bob", 30)` use `ages["bob"] = 30` + + +### JavaScript Styleguide + +- Use 4 Space indentation +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables. diff --git a/[qb]/qb-tunerchip/.github/pull_request_template.md b/[qb]/qb-tunerchip/.github/pull_request_template.md new file mode 100644 index 0000000..000f0f9 --- /dev/null +++ b/[qb]/qb-tunerchip/.github/pull_request_template.md @@ -0,0 +1,10 @@ +**Describe Pull request** +First, make sure you've read and are following the contribution guidelines and style guide and your code reflects that. +Write up a clear and concise description of what your pull request adds or fixes and if it's an added feature explain why you think it should be included in the core. + +If your PR is to fix an issue mention that issue here + +**Questions (please complete the following information):** +- Have you personally loaded this code into an updated qbcore project and checked all it's functionality? [yes/no] (Be honest) +- Does your code fit the style guidelines? [yes/no] +- Does your PR fit the contribution guidelines? [yes/no] diff --git a/[qb]/qb-tunerchip/.github/workflows/lint.yml b/[qb]/qb-tunerchip/.github/workflows/lint.yml new file mode 100644 index 0000000..fb74fd6 --- /dev/null +++ b/[qb]/qb-tunerchip/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint +on: [push, pull_request_target] +jobs: + lint: + name: Lint Resource + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Lint + uses: iLLeniumStudios/fivem-lua-lint-action@v2 + with: + capture: "junit.xml" + args: "-t --formatter JUnit" + extra_libs: mysql+polyzone+qblocales + - name: Generate Lint Report + if: always() + uses: mikepenz/action-junit-report@v3 + with: + report_paths: "**/junit.xml" + check_name: Linting Report + fail_on_failure: false \ No newline at end of file diff --git a/[qb]/qb-tunerchip/LICENSE b/[qb]/qb-tunerchip/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/[qb]/qb-tunerchip/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[qb]/qb-tunerchip/README.md b/[qb]/qb-tunerchip/README.md new file mode 100644 index 0000000..8763b3a --- /dev/null +++ b/[qb]/qb-tunerchip/README.md @@ -0,0 +1,47 @@ +# qb-tunerchip +Vehicle Tuning System for QB-Core Framework :: + +# License + + QBCore Framework + Copyright (C) 2021 Joshua Eger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + + +## Dependencies +- [qb-core](https://github.com/qbcore-framework/qb-core) +- [qb-hud](https://github.com/qbcore-framework/qb-hud) - For NOS status display +- [vstancer](https://github.com/carmineos/fivem-vstancer) - To use Stancer + +## Screenshots +![Tunerchip](https://imgur.com/1NbUqXc.png) +![Headlight Adjust](https://imgur.com/F1VDS9k.png) +![RGB Headlights & Neons](https://imgur.com/nRN7v6w) +![NOS](https://imgur.com/yAbcEdl.png) + +## Features +- Vehicle tuning with Tunerchip (Item name: tunerlaptop) +- Usable NOS for vehicles (Item name: nitrous) + +## Installation +### Manual +- Download the script and put it in the `[qb]` directory. +- Add the following code to your server.cfg/resouces.cfg +``` +ensure qb-core +ensure qb-hud +ensure qb-tunerchip +ensure vstancer +``` diff --git a/[qb]/qb-tunerchip/client/main.lua b/[qb]/qb-tunerchip/client/main.lua new file mode 100644 index 0000000..9ecbd23 --- /dev/null +++ b/[qb]/qb-tunerchip/client/main.lua @@ -0,0 +1,272 @@ +QBCore = exports['qb-core']:GetCoreObject() +local RainbowNeon = false +LastEngineMultiplier = 1.0 + +function setVehData(veh,data) + local multp = 0.12 + local dTrain = 0.0 + if tonumber(data.drivetrain) == 2 then dTrain = 0.5 elseif tonumber(data.drivetrain) == 3 then dTrain = 1.0 end + if not DoesEntityExist(veh) or not data then return nil end + SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce", data.boost * multp) + SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia", data.acceleration * multp) + SetVehicleEnginePowerMultiplier(veh, data.gearchange * multp) + LastEngineMultiplier = data.gearchange * multp + SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront", dTrain*1.0) + SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront", data.breaking * multp) +end + +function resetVeh(veh) + SetVehicleHandlingFloat(veh, "CHandlingData", "fInitialDriveForce", 1.0) + SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveInertia", 1.0) + SetVehicleEnginePowerMultiplier(veh, 1.0) + SetVehicleHandlingFloat(veh, "CHandlingData", "fDriveBiasFront", 0.5) + SetVehicleHandlingFloat(veh, "CHandlingData", "fBrakeBiasFront", 1.0) +end + +RegisterNUICallback('save', function(data, cb) + QBCore.Functions.TriggerCallback('qb-tunerchip:server:HasChip', function(HasChip) + if HasChip then + local ped = PlayerPedId() + local veh = GetVehiclePedIsUsing(ped) + setVehData(veh, data) + QBCore.Functions.Notify(Lang:t("error.tunerchip_vehicle_tuned"), 'error') + + TriggerServerEvent('qb-tunerchip:server:TuneStatus', QBCore.Functions.GetPlate(veh), true) + end + cb('ok') + end, data) +end) + +RegisterNetEvent('qb-tunerchip:client:TuneStatus', function() + local ped = PlayerPedId() + local closestVehicle = GetClosestVehicle(GetEntityCoords(ped), 5.0, 0, 70) + local plate = QBCore.Functions.GetPlate(closestVehicle) + local vehModel = GetEntityModel(closestVehicle) + if vehModel ~= 0 then + QBCore.Functions.TriggerCallback('qb-tunerchip:server:GetStatus', function(status) + if status then + QBCore.Functions.Notify(Lang:t("success.this_vehicle_has_been_tuned"), 'success') + else + QBCore.Functions.Notify(Lang:t("error.this_vehicle_has_not_been_tuned"), 'error') + end + end, plate) + else + QBCore.Functions.Notify(Lang:t("error.no_vehicle_nearby"), 'error') + end +end) + +RegisterNUICallback('checkItem', function(data, cb) + local retval = false + local result = QBCore.Functions.HasItem(data.item) + if result then + retval = true + end + cb(retval) +end) + +RegisterNUICallback('reset', function(_, cb) + local ped = PlayerPedId() + local veh = GetVehiclePedIsUsing(ped) + resetVeh(veh) + QBCore.Functions.Notify(Lang:t("error.tunerchip_vehicle_has_been_reset"), 'error') + cb("ok") +end) + +RegisterNetEvent('qb-tunerchip:client:openChip', function() + local ped = PlayerPedId() + local inVehicle = IsPedInAnyVehicle(ped) + + if inVehicle then + QBCore.Functions.Progressbar("connect_laptop", Lang:t("error.tunerchip_vehicle_has_been_reset"), 2000, false, true, { + disableMovement = true, + disableCarMovement = true, + disableMouse = false, + disableCombat = true, + }, { + animDict = "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", + anim = "machinic_loop_mechandplayer", + flags = 16, + }, {}, {}, function() -- Done + StopAnimTask(PlayerPedId(), "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", "machinic_loop_mechandplayer", 1.0) + openTunerLaptop(true) + end, function() -- Cancel + StopAnimTask(PlayerPedId(), "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", "machinic_loop_mechandplayer", 1.0) + QBCore.Functions.Notify(Lang:t("error.canceled"), "error") + end) + else + QBCore.Functions.Notify(Lang:t("error.you_are_not_in_a_vehicle"), "error") + end +end) + +RegisterNUICallback('exit', function(_, cb) + openTunerLaptop(false) + SetNuiFocus(false, false) + cb('ok') +end) + +local LastRainbowNeonColor = 0 + +local RainbowNeonColors = { + [1] = { + r = 255, + g = 0, + b = 0 + }, + [2] = { + r = 255, + g = 165, + b = 0 + }, + [3] = { + r = 255, + g = 255, + b = 0 + }, + [4] = { + r = 0, + g = 0, + b = 255 + }, + [5] = { + r = 75, + g = 0, + b = 130 + }, + [6] = { + r = 238, + g = 130, + b = 238 + }, +} + +RegisterNUICallback('saveNeon', function(data, cb) + QBCore.Functions.TriggerCallback('qb-tunerchip:server:HasChip', function(HasChip) + if HasChip then + if not data.rainbowEnabled then + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + + if tonumber(data.neonEnabled) == 1 then + SetVehicleNeonLightEnabled(veh, 0, true) + SetVehicleNeonLightEnabled(veh, 1, true) + SetVehicleNeonLightEnabled(veh, 2, true) + SetVehicleNeonLightEnabled(veh, 3, true) + if tonumber(data.r) ~= nil and tonumber(data.g) ~= nil and tonumber(data.b) ~= nil then + SetVehicleNeonLightsColour(veh, tonumber(data.r), tonumber(data.g), tonumber(data.b)) + else + SetVehicleNeonLightsColour(veh, 255, 255, 255) + end + RainbowNeon = false + else + SetVehicleNeonLightEnabled(veh, 0, false) + SetVehicleNeonLightEnabled(veh, 1, false) + SetVehicleNeonLightEnabled(veh, 2, false) + SetVehicleNeonLightEnabled(veh, 3, false) + RainbowNeon = false + end + else + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + + if tonumber(data.neonEnabled) == 1 then + if not RainbowNeon then + RainbowNeon = true + SetVehicleNeonLightEnabled(veh, 0, true) + SetVehicleNeonLightEnabled(veh, 1, true) + SetVehicleNeonLightEnabled(veh, 2, true) + SetVehicleNeonLightEnabled(veh, 3, true) + CreateThread(function() + while true do + if RainbowNeon then + if (LastRainbowNeonColor + 1) ~= 7 then + LastRainbowNeonColor = LastRainbowNeonColor + 1 + SetVehicleNeonLightsColour(veh, RainbowNeonColors[LastRainbowNeonColor].r, RainbowNeonColors[LastRainbowNeonColor].g, RainbowNeonColors[LastRainbowNeonColor].b) + else + LastRainbowNeonColor = 1 + SetVehicleNeonLightsColour(veh, RainbowNeonColors[LastRainbowNeonColor].r, RainbowNeonColors[LastRainbowNeonColor].g, RainbowNeonColors[LastRainbowNeonColor].b) + end + else + break + end + + Wait(350) + end + end) + end + else + RainbowNeon = false + SetVehicleNeonLightEnabled(veh, 0, false) + SetVehicleNeonLightEnabled(veh, 1, false) + SetVehicleNeonLightEnabled(veh, 2, false) + SetVehicleNeonLightEnabled(veh, 3, false) + end + end + end + cb('ok') + end) +end) + +local RainbowHeadlight = false +local RainbowHeadlightValue = 0 + +RegisterNUICallback('saveHeadlights', function(data, cb) + QBCore.Functions.TriggerCallback('qb-tunerchip:server:HasChip', function(HasChip) + if HasChip then + if data.rainbowEnabled then + RainbowHeadlight = true + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + local value = tonumber(data.value) + + CreateThread(function() + while true do + if RainbowHeadlight then + if (RainbowHeadlightValue + 1) ~= 12 then + RainbowHeadlightValue = RainbowHeadlightValue + 1 + ToggleVehicleMod(veh, 22, true) + SetVehicleHeadlightsColour(veh, RainbowHeadlightValue) + else + RainbowHeadlightValue = 1 + ToggleVehicleMod(veh, 22, true) + SetVehicleHeadlightsColour(veh, RainbowHeadlightValue) + end + else + break + end + Wait(300) + end + end) + ToggleVehicleMod(veh, 22, true) + SetVehicleHeadlightsColour(veh, value) + else + RainbowHeadlight = false + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + local value = tonumber(data.value) + + ToggleVehicleMod(veh, 22, true) + SetVehicleHeadlightsColour(veh, value) + end + end + cb('ok') + end) +end) + +function openTunerLaptop(bool) + SetNuiFocus(bool, bool) + SendNUIMessage({ + action = "ui", + toggle = bool + }) +end + +RegisterNUICallback('SetStancer', function(data, cb) + local fOffset = data.fOffset * 100 / 1000 + local fRotation = data.fRotation * 100 / 1000 + local rOffset = data.rOffset * 100 / 1000 + local rRotation = data.rRotation * 100 / 1000 + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + exports["vstancer"]:SetWheelPreset(veh, -fOffset, -fRotation, -rOffset, -rRotation) + cb("ok") +end) diff --git a/[qb]/qb-tunerchip/client/nos.lua b/[qb]/qb-tunerchip/client/nos.lua new file mode 100644 index 0000000..5465bd0 --- /dev/null +++ b/[qb]/qb-tunerchip/client/nos.lua @@ -0,0 +1,235 @@ +local NitrousActivated = false +local NitrousBoost = 35.0 +local VehicleNitrous = {} +local Fxs = {} + +local function trim(value) + if not value then return nil end + return (string.gsub(value, '^%s*(.-)%s*$', '%1')) +end + +RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() + QBCore.Functions.TriggerCallback('nitrous:GetNosLoadedVehs', function(vehs) + VehicleNitrous = vehs + end) +end) + +RegisterNetEvent('smallresource:client:LoadNitrous', function() + local IsInVehicle = IsPedInAnyVehicle(PlayerPedId()) + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + + if not NitrousActivated then + if IsInVehicle and not IsThisModelABike(GetEntityModel(GetVehiclePedIsIn(ped))) then + if GetPedInVehicleSeat(veh, -1) == ped then + QBCore.Functions.Progressbar("use_nos", Lang:t("text.connecting_nos"), 1000, false, true, { + disableMovement = false, + disableCarMovement = false, + disableMouse = false, + disableCombat = true, + }, {}, {}, {}, function() -- Done + TriggerEvent("inventory:client:ItemBox", QBCore.Shared.Items['nitrous'], "remove") + TriggerServerEvent("nitrous:server:removeItem") + local CurrentVehicle = GetVehiclePedIsIn(PlayerPedId()) + local Plate = trim(GetVehicleNumberPlateText(CurrentVehicle)) + TriggerServerEvent('nitrous:server:LoadNitrous', Plate) + end) + else + QBCore.Functions.Notify(Lang:t("error.you_cannot_do_that_from_this_seat"), "error") + end + else + QBCore.Functions.Notify(Lang:t("error.you_are_not_in_a_vehicle"), 'error') + end + else + QBCore.Functions.Notify(Lang:t("error.you_already_have_nos_active"), 'error') + end +end) + +local nosupdated = false + +CreateThread(function() + while true do + local IsInVehicle = IsPedInAnyVehicle(PlayerPedId()) + local CurrentVehicle = GetVehiclePedIsIn(PlayerPedId()) + if IsInVehicle then + local Plate = trim(GetVehicleNumberPlateText(CurrentVehicle)) + if VehicleNitrous[Plate] ~= nil then + if VehicleNitrous[Plate].hasnitro then + if IsControlJustPressed(0, 36) and GetPedInVehicleSeat(CurrentVehicle, -1) == PlayerPedId() then + SetVehicleEnginePowerMultiplier(CurrentVehicle, NitrousBoost) + SetVehicleEngineTorqueMultiplier(CurrentVehicle, NitrousBoost) + SetEntityMaxSpeed(CurrentVehicle, 999.0) + NitrousActivated = true + + CreateThread(function() + while NitrousActivated do + if VehicleNitrous[Plate].level - 1 ~= 0 then + TriggerServerEvent('nitrous:server:UpdateNitroLevel', Plate, (VehicleNitrous[Plate].level - 1)) + TriggerEvent('hud:client:UpdateNitrous', VehicleNitrous[Plate].hasnitro, VehicleNitrous[Plate].level, true) + else + TriggerServerEvent('nitrous:server:UnloadNitrous', Plate) + NitrousActivated = false + SetVehicleBoostActive(CurrentVehicle, 0) + SetVehicleEnginePowerMultiplier(CurrentVehicle, LastEngineMultiplier) + SetVehicleEngineTorqueMultiplier(CurrentVehicle, 1.0) + StopScreenEffect("RaceTurbo") + for index,_ in pairs(Fxs) do + StopParticleFxLooped(Fxs[index], 1) + TriggerServerEvent('nitrous:server:StopSync', trim(GetVehicleNumberPlateText(CurrentVehicle))) + Fxs[index] = nil + end + end + Wait(100) + end + end) + end + + if IsControlJustReleased(0, 36) and GetPedInVehicleSeat(CurrentVehicle, -1) == PlayerPedId() then + if NitrousActivated then + local veh = GetVehiclePedIsIn(PlayerPedId()) + SetVehicleBoostActive(veh, 0) + SetVehicleEnginePowerMultiplier(veh, LastEngineMultiplier) + SetVehicleEngineTorqueMultiplier(veh, 1.0) + for index,_ in pairs(Fxs) do + StopParticleFxLooped(Fxs[index], 1) + TriggerServerEvent('nitrous:server:StopSync', trim(GetVehicleNumberPlateText(veh))) + Fxs[index] = nil + end + StopScreenEffect("RaceTurbo") + TriggerEvent('hud:client:UpdateNitrous', VehicleNitrous[Plate].hasnitro, VehicleNitrous[Plate].level, false) + NitrousActivated = false + end + end + end + else + if not nosupdated then + TriggerEvent('hud:client:UpdateNitrous', false, nil, false) + nosupdated = true + end + StopScreenEffect("RaceTurbo") + end + else + if nosupdated then + nosupdated = false + end + StopScreenEffect("RaceTurbo") + Wait(1500) + end + Wait(3) + end +end) + +p_flame_location = { + "exhaust", + "exhaust_2", + "exhaust_3", + "exhaust_4", + "exhaust_5", + "exhaust_6", + "exhaust_7", + "exhaust_8", + "exhaust_9", + "exhaust_10", + "exhaust_11", + "exhaust_12", + "exhaust_13", + "exhaust_14", + "exhaust_15", + "exhaust_16", +} + +ParticleDict = "veh_xs_vehicle_mods" +ParticleFx = "veh_nitrous" +ParticleSize = 1.4 + +CreateThread(function() + while true do + if NitrousActivated then + local veh = GetVehiclePedIsIn(PlayerPedId()) + if veh ~= 0 then + TriggerServerEvent('nitrous:server:SyncFlames', VehToNet(veh)) + SetVehicleBoostActive(veh, 1) + StartScreenEffect("RaceTurbo", 0.0, 0) + + for _,bones in pairs(p_flame_location) do + if GetEntityBoneIndexByName(veh, bones) ~= -1 then + if Fxs[bones] == nil then + RequestNamedPtfxAsset(ParticleDict) + while not HasNamedPtfxAssetLoaded(ParticleDict) do + Wait(0) + end + SetPtfxAssetNextCall(ParticleDict) + UseParticleFxAssetNextCall(ParticleDict) + Fxs[bones] = StartParticleFxLoopedOnEntityBone(ParticleFx, veh, 0.0, -0.02, 0.0, 0.0, 0.0, 0.0, GetEntityBoneIndexByName(veh, bones), ParticleSize, 0.0, 0.0, 0.0) + end + end + end + end + end + Wait(0) + end +end) + +local NOSPFX = {} + +RegisterNetEvent('nitrous:client:SyncFlames', function(netid, nosid) + local veh = NetToVeh(netid) + if veh ~= 0 then + local myid = GetPlayerServerId(PlayerId()) + if NOSPFX[trim(GetVehicleNumberPlateText(veh))] == nil then + NOSPFX[trim(GetVehicleNumberPlateText(veh))] = {} + end + if myid ~= nosid then + for _,bones in pairs(p_flame_location) do + if NOSPFX[trim(GetVehicleNumberPlateText(veh))][bones] == nil then + NOSPFX[trim(GetVehicleNumberPlateText(veh))][bones] = {} + end + if GetEntityBoneIndexByName(veh, bones) ~= -1 then + if NOSPFX[trim(GetVehicleNumberPlateText(veh))][bones].pfx == nil then + RequestNamedPtfxAsset(ParticleDict) + while not HasNamedPtfxAssetLoaded(ParticleDict) do + Wait(0) + end + SetPtfxAssetNextCall(ParticleDict) + UseParticleFxAssetNextCall(ParticleDict) + NOSPFX[trim(GetVehicleNumberPlateText(veh))][bones].pfx = StartParticleFxLoopedOnEntityBone(ParticleFx, veh, 0.0, -0.05, 0.0, 0.0, 0.0, 0.0, GetEntityBoneIndexByName(veh, bones), ParticleSize, 0.0, 0.0, 0.0) + + end + end + end + end + end +end) + +RegisterNetEvent('nitrous:client:StopSync', function(plate) + for k, v in pairs(NOSPFX[plate]) do + StopParticleFxLooped(v.pfx, 1) + NOSPFX[plate][k].pfx = nil + end +end) + +RegisterNetEvent('nitrous:client:UpdateNitroLevel', function(Plate, level) + VehicleNitrous[Plate].level = level +end) + +RegisterNetEvent('nitrous:client:LoadNitrous', function(Plate) + VehicleNitrous[Plate] = { + hasnitro = true, + level = 100, + } + local CurrentVehicle = GetVehiclePedIsIn(PlayerPedId()) + local CPlate = trim(GetVehicleNumberPlateText(CurrentVehicle)) + if CPlate == Plate then + TriggerEvent('hud:client:UpdateNitrous', VehicleNitrous[Plate].hasnitro, VehicleNitrous[Plate].level, false) + end +end) + +RegisterNetEvent('nitrous:client:UnloadNitrous', function(Plate) + VehicleNitrous[Plate] = nil + local CurrentVehicle = GetVehiclePedIsIn(PlayerPedId()) + local CPlate = trim(GetVehicleNumberPlateText(CurrentVehicle)) + if CPlate == Plate then + NitrousActivated = false + TriggerEvent('hud:client:UpdateNitrous', false, nil, false) + end +end) diff --git a/[qb]/qb-tunerchip/fxmanifest.lua b/[qb]/qb-tunerchip/fxmanifest.lua new file mode 100644 index 0000000..33a1a8f --- /dev/null +++ b/[qb]/qb-tunerchip/fxmanifest.lua @@ -0,0 +1,26 @@ +fx_version 'cerulean' +game 'gta5' + +description 'QB-TunerChip' +version '1.2.0' + +ui_page 'html/index.html' + +client_scripts { + 'client/main.lua', + 'client/nos.lua' +} + +shared_scripts { + '@qb-core/shared/locale.lua', + 'locales/en.lua', + 'locales/*.lua', +} + +server_script 'server/main.lua' + +files { + 'html/*', +} + +lua54 'yes' diff --git a/[qb]/qb-tunerchip/html/bg.png b/[qb]/qb-tunerchip/html/bg.png new file mode 100644 index 0000000..7e03e9d Binary files /dev/null and b/[qb]/qb-tunerchip/html/bg.png differ diff --git a/[qb]/qb-tunerchip/html/index.html b/[qb]/qb-tunerchip/html/index.html new file mode 100644 index 0000000..aa9da65 --- /dev/null +++ b/[qb]/qb-tunerchip/html/index.html @@ -0,0 +1,159 @@ + + + + + + + + + + + + QB Tunerchip + + +
+ screen-frame +
+
+
+ Boost (Normal | High) + +
+ +
+ Acceleration (Normal | Faster) + +
+ +
+ Gear Change (Normal | Faster) + +
+ +
+ Breaking (Rear | Front) + +
+ +
+ Drivetrain (RWD | AWD | FWD) + +
+ +
+

Save

+

Cancel

+

Neon

+

Headlights

+

Stancer

+
+
+
+
+
+
+ Front Offset + +
+ +
+ Front Rotation + +
+ +
+ Rear Offset + +
+ +
+ Rear Rotation + +
+ +
+

Save

+

Cancel

+

Neon

+

Headlights

+

Tuning

+
+
+
+
+
+ +
+ Neon Lights(Off | On) + +
+ + + +
+

Neon Color (RGB)

+ + + +
+ +
+

Color Presets

+
+
+
+
+
+
+ +
+

Save

+

Cancel

+

Tuning

+

Headlights

+

Stancer

+
+
+
+
+
+ +
+

Headlight Colors

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+

Selected Color

+
+
+ +
+

Save

+

Cancel

+

Tuning

+

Neon

+

Stancer

+
+
+
+
+ + + diff --git a/[qb]/qb-tunerchip/html/reset.css b/[qb]/qb-tunerchip/html/reset.css new file mode 100644 index 0000000..e2c0181 --- /dev/null +++ b/[qb]/qb-tunerchip/html/reset.css @@ -0,0 +1,44 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} \ No newline at end of file diff --git a/[qb]/qb-tunerchip/html/screen-frame.png b/[qb]/qb-tunerchip/html/screen-frame.png new file mode 100644 index 0000000..b36ae01 Binary files /dev/null and b/[qb]/qb-tunerchip/html/screen-frame.png differ diff --git a/[qb]/qb-tunerchip/html/script.js b/[qb]/qb-tunerchip/html/script.js new file mode 100644 index 0000000..7ab0eb0 --- /dev/null +++ b/[qb]/qb-tunerchip/html/script.js @@ -0,0 +1,157 @@ +QBTuner = {} + +var headlightVal = 0; +var RainbowNeon = false; +var RainbowHeadlight = false; + +$(document).ready(function(){ + $('.container').hide(); + + window.addEventListener('message', function(event){ + var eventData = event.data; + + if (eventData.action == "ui") { + if (eventData.toggle) { + QBTuner.Open() + } + } + }); +}); + +$(document).on('keydown', function() { + switch(event.keyCode) { + case 27: + QBTuner.Close(); + break; + } +}); + +$(document).on('click', '#save', function(){ + $.post('https://qb-tunerchip/save', JSON.stringify({ + boost: $("#boost-slider").val(), + acceleration: $("#acceleration-slider").val(), + gearchange: $("#gear-slider").val(), + breaking: $("#breaking-slider").val(), + drivetrain: $("#drivetrain-slider").val() + })); +}); + +$(document).on('click', '#reset', function(){ + $.post('https://qb-tunerchip/reset'); +}); + +$(document).on('click', '#cancel', function(){ + QBTuner.Close(); +}); + +$(document).on('click', "#neon", function(){ + $(".tunerchip-block").css("display", "none"); + $(".headlights-block").css("display", "none"); + $(".stancer-block").css("display", "none"); + $(".neon-block").css("display", "block"); +}) + +$(document).on('click', "#headlights", function(){ + $(".tunerchip-block").css("display", "none"); + $(".neon-block").css("display", "none"); + $(".stancer-block").css("display", "none"); + $(".headlights-block").css("display", "block"); +}) + +$(document).on('click', "#tuning", function(){ + $(".headlights-block").css("display", "none"); + $(".neon-block").css("display", "none"); + $(".stancer-block").css("display", "none"); + $(".tunerchip-block").css("display", "block"); +}); + +$(document).on('click', "#stancer", function(){ + $(".headlights-block").css("display", "none"); + $(".neon-block").css("display", "none"); + $(".tunerchip-block").css("display", "none"); + $(".stancer-block").css("display", "block"); +}); + +$(document).on('click', "#save-neon", function(){ + if (RainbowNeon) { + $.post('https://qb-tunerchip/saveNeon', JSON.stringify({ + neonEnabled: $("#neon-slider").val(), + r: $("#color-r").val(), + g: $("#color-g").val(), + b: $("#color-b").val(), + rainbowEnabled: true, + })); + } else { + $.post('https://qb-tunerchip/saveNeon', JSON.stringify({ + neonEnabled: $("#neon-slider").val(), + r: $("#color-r").val(), + g: $("#color-g").val(), + b: $("#color-b").val(), + rainbowEnabled: false, + })); + } +}) + +$(document).on('click', '#save-headlights', function(){ + $.post('https://qb-tunerchip/saveHeadlights', JSON.stringify({ + value: headlightVal, + rainbowEnabled: RainbowHeadlight, + })) +}); + +$(document).on('click', '#save-stancer', function(){ + var front_offset = $("#front-offset").val(); + var front_rotation = $("#front-rotation").val(); + var rear_offset = $("#rear-offset").val(); + var rear_rotation = $("#rear-rotation").val(); + + $.post('https://qb-tunerchip/SetStancer', JSON.stringify({ + fOffset: front_offset, + fRotation: front_rotation, + rOffset: rear_offset, + rRotation: rear_rotation, + })); +}); + +$(document).on('click', ".neon-software-color-pallete-color", function(){ + var headlightValue = $(this).data('value'); + + if (headlightValue === "rainbow") { + RainbowHeadlight = true; + } else { + RainbowHeadlight = false; + } + + if (!$(this).data('rainbow')) { + var r = $(this).data('r') + var g = $(this).data('g') + var b = $(this).data('b') + + $("#color-r").val(r) + $("#color-g").val(g) + $("#color-b").val(b) + + + if (headlightValue != null) { + headlightVal = headlightValue + var colorValue = $(this).css("background-color"); + $(".neon-software-color-pallete-color-current").css("background-color", colorValue); + } + RainbowNeon = false; + } else { + RainbowNeon = true; + } +}); + +QBTuner.Open = function() { + $.post('https://qb-tunerchip/checkItem', JSON.stringify({item: "tunerlaptop"}), function(hasItem){ + if (hasItem) { + $('.container').fadeIn(250); + } + }) +} + +QBTuner.Close = function() { + $('.container').fadeOut(250); + $.post('https://qb-tunerchip/exit'); +} \ No newline at end of file diff --git a/[qb]/qb-tunerchip/html/style.css b/[qb]/qb-tunerchip/html/style.css new file mode 100644 index 0000000..487b217 --- /dev/null +++ b/[qb]/qb-tunerchip/html/style.css @@ -0,0 +1,355 @@ +@import url('https://fonts.googleapis.com/css?family=Lato&display=swap'); + +.container { + height: 100vh; +} + +.tunerchip-block { + display: block; + position: absolute; + width: 55vh; + left: 12vh; + height: 35vh; + top: 20vh; + z-index: 100; +} + +.neon-block { + display: none; + position: absolute; + width: 55vh; + left: 12vh; + height: 35vh; + top: 20vh; + z-index: 100; +} + +.headlights-block { + display: none; + position: absolute; + width: 55vh; + left: 12vh; + height: 35vh; + top: 20vh; + z-index: 100; +} + +.stancer-block { + display: none; + position: absolute; + width: 55vh; + left: 12vh; + height: 35vh; + top: 20vh; + z-index: 100; +} + +.neon-software-color-pallete { + position: absolute; + width: 20vh; + height: 20vh; + top: 9vh; + left: 8vh; +} + +.neon-software-color-pallete-presets { + position: absolute; + width: 20vh; + height: 20vh; + top: 15vh; + left: 8vh; +} + +.headlights-software-color-pallete-presets { + position: absolute; + width: 20vh; + height: 20vh; + top: 4vh; + left: 8vh; +} + +.neon-software-color-pallete-color { + position: relative; + float: left; + height: 3vh; + width: 3vh; + text-align: center; + line-height: 3vh; + margin-bottom: 0.5vh; + margin-right: 0.5vh; + border-radius: 0.5vh; + background: white; + border: 2px solid rgba(255, 255, 255, 0.705); + transition: all 0.1s linear; +} + +.neon-software-color-pallete-color:hover { + border-radius: 0.6vh; +} + +.neon-software-color-pallete-color-current { + position: relative; + float: left; + height: 3vh; + width: 3vh; + text-align: center; + line-height: 3vh; + margin-bottom: 0.5vh; + margin-right: 0.5vh; + border-radius: 0.5vh; + background: white; + border: 2px solid rgba(255, 255, 255, 0.705); + transition: all 0.1s linear; +} + +.neon-software-color-pallete-color-current:hover { + border-radius: 0.6vh; +} + +.color-code { + width: 5vh; + height: 2vh; + background: none; + border: none; + border-bottom: 2px solid rgb(127, 119, 192); + text-align: center; + font-family: sans-serif; + text-transform: uppercase; + font-weight: bold; + font-size: 1.1vh; + letter-spacing: 0.1vh; + color: rgb(10, 10, 10); + outline: none; + transition: all 0.1s linear; +} + +/* +position: absolute; +margin: 0 auto; +pointer-events: none; +z-index: 101; +height: 40vh; */ + +.screen-frame { + position: absolute; + left: 10vh; + top: 14.5vh; + margin: 0 auto; + pointer-events: none; + z-index: 101; + height: 43vh; +} + +.tunerchip-header { + position: absolute; + width: 100%; + height: 7%; + background-color: rgba(0, 0, 0, 0.5); +} + +.tunerchip-software { + /* display: none; */ + position: absolute; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.521); + background-image: url('./bg.png'); + background-size: cover; + background-repeat: no-repeat; + bottom: 0; + z-index: 10000; +} +.tunerchip-btn { + position: relative; + background-color: #322f58; + height: 3vh; + margin-bottom: 2vh; + border-radius: 0.3vh; + transition: all 0.2s linear; +} + +.tunerchip-btn:hover { + border-radius: 0.5vh; + background-color: #4c4881; +} + +.tunerchip-btn > p { + text-align: center; + color: white; + font-size: 1vh; + text-transform: uppercase; + font-family: sans-serif; + font-weight: bold; + letter-spacing: 0.1vh; + line-height: 3vh; +} + +.tunerchip-software-buttons { + position: absolute; + height: 20vh; + width: 12vh; + right: 2.5vh; + top: 5.6vh; +} + +.tunerchip-software-boost-slider { + position: absolute; + width: 30vh; + top: 4vh; + left: 8vh; +} + +.neon-software-neon-slider { + position: absolute; + width: 30vh; + top: 4vh; + left: 8vh; +} + +.tunerchip-software-acceleration-slider { + position: absolute; + width: 30vh; + top: 9vh; + left: 8vh; +} + +.headlights-software-color-pallete-currentcolor { + position: absolute; + width: 20vh; + height: 20vh; + top: 19vh; + left: 8vh; +} + +.tunerchip-software-gear-slider { + position: absolute; + width: 30vh; + top: 14vh; + left: 8vh; +} + +.tunerchip-software-breaking-slider { + position: absolute; + width: 30vh; + top: 19vh; + left: 8vh; +} + +.tunerchip-software-drivetrain-slider { + position: absolute; + width: 30vh; + top: 24vh; + left: 8vh; +} + +#slider-title { + font-family: 'Lato'; + font-weight: bold; + font-size: 1vh; + letter-spacing: 0.15vh; + padding-left: 0.2vh; + padding-bottom: 1vh; +} + +.neon-software-color-pallete > p { + font-family: 'Lato'; + font-weight: bold; + font-size: 1vh; + letter-spacing: 0.15vh; + padding-left: 0.2vh; + padding-bottom: 1vh; +} + +.neon-software-color-pallete-presets > p { + font-family: 'Lato'; + font-weight: bold; + font-size: 1vh; + letter-spacing: 0.15vh; + padding-left: 0.2vh; + padding-bottom: 1vh; +} + +.headlights-software-color-pallete-currentcolor > p { + font-family: 'Lato'; + font-weight: bold; + font-size: 1vh; + letter-spacing: 0.15vh; + padding-left: 0.2vh; + padding-bottom: 1vh; +} + +.headlights-software-color-pallete-presets > p { + font-family: 'Lato'; + font-weight: bold; + font-size: 1vh; + letter-spacing: 0.15vh; + padding-left: 0.2vh; + padding-bottom: 1vh; +} + + +#normalhigh { + font-size: 9px; + font-weight: lighter; + margin-left: 8px; +} + +.slider { + -webkit-appearance: none; + width: 100%; + height: 1vh; + background: #d3d3d3; + outline: none; + opacity: 0.7; + -webkit-transition: .2s; + transition: opacity .2s; + box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.15); +} + +.slider:hover { + opacity: 1; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 1vh; + height: 1vh; + background: rgb(72, 67, 107); + cursor: pointer; +} + +.slider::-moz-range-thumb { + width: 1vh; + height: 1vh; + background: rgb(72, 67, 107); + cursor: pointer; +} + +.stancer-front-offset-slider { + position: absolute; + width: 30vh; + top: 4vh; + left: 8vh; +} + +.stancer-front-rotation-slider { + position: absolute; + width: 30vh; + top: 9vh; + left: 8vh; +} + +.stancer-rear-offset-slider { + position: absolute; + width: 30vh; + top: 14vh; + left: 8vh; +} + +.stancer-rear-rotation-slider { + position: absolute; + width: 30vh; + top: 19vh; + left: 8vh; +} diff --git a/[qb]/qb-tunerchip/locales/en.lua b/[qb]/qb-tunerchip/locales/en.lua new file mode 100644 index 0000000..37c5611 --- /dev/null +++ b/[qb]/qb-tunerchip/locales/en.lua @@ -0,0 +1,24 @@ +local Translations = { + success = { + this_vehicle_has_been_tuned = "This Vehicle Has Been Tuned", + }, + text = { + this_is_not_the_idea_is_it = "This is not the idea, is it?", + connecting_nos = "Connecting NOS...", + }, + error = { + tunerchip_vehicle_tuned = "TunerChip v1.05: Vehicle Tuned!", + this_vehicle_has_not_been_tuned = "This Vehicle Has Not Been Tuned", + no_vehicle_nearby = "No Vehicle Nearby", + tunerchip_vehicle_has_been_reset = "TunerChip v1.05: Vehicle has been reset!", + you_are_not_in_a_vehicle = "You Are Not In A Vehicle", + you_cannot_do_that_from_this_seat = "You cannot do that from this seat!", + you_already_have_nos_active = "You Already Have NOS Active", + canceled = "Canceled", + }, +} + +Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true +}) diff --git a/[qb]/qb-tunerchip/locales/es.lua b/[qb]/qb-tunerchip/locales/es.lua new file mode 100644 index 0000000..a6524db --- /dev/null +++ b/[qb]/qb-tunerchip/locales/es.lua @@ -0,0 +1,26 @@ +local Translations = { + success = { + this_vehicle_has_been_tuned = "Este vehículo ha sido ajustado", + }, + text = { + this_is_not_the_idea_is_it = "Esta no es la idea, ¿verdad?", + connecting_nos = "Conectando NOS...", + }, + error = { + tunerchip_vehicle_tuned = "TunerChip v1.05: ¡Vehículo tuneado!", + this_vehicle_has_not_been_tuned = "Este vehículo no ha sido afinado", + no_vehicle_nearby = "No hay vehiculos cerca", + tunerchip_vehicle_has_been_reset = "TunerChip v1.05: ¡El vehículo ha sido reiniciado!", + you_are_not_in_a_vehicle = "No estas en un auto", + you_cannot_do_that_from_this_seat = "¡No puedes hacer eso desde este asiento!", + you_already_have_nos_active = "Ya tiene el NOS activo", + canceled = "Cancelado", + }, +} + +if GetConvar('qb_locale', 'en') == 'es' then + Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true + }) +end diff --git a/[qb]/qb-tunerchip/locales/pt.lua b/[qb]/qb-tunerchip/locales/pt.lua new file mode 100644 index 0000000..cbec883 --- /dev/null +++ b/[qb]/qb-tunerchip/locales/pt.lua @@ -0,0 +1,26 @@ +local Translations = { + success = { + this_vehicle_has_been_tuned = "Este veículo foi ajustado", + }, + text = { + this_is_not_the_idea_is_it = "Esta não é ideia, nao é?", + connecting_nos = "Carregando o NOS...", + }, + error = { + tunerchip_vehicle_tuned = "TunerChip v1.05: Veículo tunado!", + this_vehicle_has_not_been_tuned = "Este veículo foi tunado", + no_vehicle_nearby = "Não há nenhum veículo próximo", + tunerchip_vehicle_has_been_reset = "TunerChip v1.05: O veículo foi resetado!", + you_are_not_in_a_vehicle = "Você não está em um veículo", + you_cannot_do_that_from_this_seat = "Você não pode fazer isso deste assento!", + you_already_have_nos_active = "Você já tem NOS ativo", + canceled = "Cancelado", + }, +} + +if GetConvar('qb_locale', 'en') == 'pt' then + Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true + }) +end diff --git a/[qb]/qb-tunerchip/server/main.lua b/[qb]/qb-tunerchip/server/main.lua new file mode 100644 index 0000000..f8defd5 --- /dev/null +++ b/[qb]/qb-tunerchip/server/main.lua @@ -0,0 +1,71 @@ +local QBCore = exports['qb-core']:GetCoreObject() +local tunedVehicles = {} +local Threshold = { + ['boost'] = {min = 1, max = 5}, + ['acceleration'] = {min = 1, max = 5}, + ['gearchange'] = {min = 1, max = 5}, + ['breaking'] = {min = 1, max = 5}, + ['drivetrain'] = {min = 1, max = 3}, +} + +QBCore.Functions.CreateUseableItem("tunerlaptop", function(source) + TriggerClientEvent('qb-tunerchip:client:openChip', source) +end) + +RegisterNetEvent('qb-tunerchip:server:TuneStatus', function(plate, bool) + if bool then + tunedVehicles[plate] = bool + else + tunedVehicles[plate] = nil + end +end) + +QBCore.Functions.CreateCallback('qb-tunerchip:server:HasChip', function(source, cb, data) + local src = source + local Ply = QBCore.Functions.GetPlayer(src) + local Chip = Ply.Functions.GetItemByName('tunerlaptop') + if data then + for k,v in pairs(data) do + if Threshold[k].min > tonumber(v) or Threshold[k].max < tonumber(v) then Chip = nil end + end + end + if Chip then cb(true) return end + DropPlayer(src, Lang:t("text.this_is_not_the_idea_is_it")) + cb(false) +end) + +QBCore.Functions.CreateCallback('qb-tunerchip:server:GetStatus', function(_, cb, plate) + cb(tunedVehicles[plate]) +end) + +QBCore.Functions.CreateUseableItem("nitrous", function(source) + TriggerClientEvent('smallresource:client:LoadNitrous', source) +end) + +RegisterNetEvent('nitrous:server:LoadNitrous', function(Plate) + TriggerClientEvent('nitrous:client:LoadNitrous', -1, Plate) +end) + +RegisterNetEvent('nitrous:server:SyncFlames', function(netId) + TriggerClientEvent('nitrous:client:SyncFlames', -1, netId, source) +end) + +RegisterNetEvent('nitrous:server:UnloadNitrous', function(Plate) + TriggerClientEvent('nitrous:client:UnloadNitrous', -1, Plate) +end) + +RegisterNetEvent('nitrous:server:UpdateNitroLevel', function(Plate, level) + TriggerClientEvent('nitrous:client:UpdateNitroLevel', -1, Plate, level) +end) + +RegisterNetEvent('nitrous:server:StopSync', function(plate) + TriggerClientEvent('nitrous:client:StopSync', -1, plate) +end) + +RegisterNetEvent('nitrous:server:removeItem', function() + local Player = QBCore.Functions.GetPlayer(source) + + if not Player then return end + + Player.Functions.RemoveItem('nitrous', 1) +end) diff --git a/[qb]/qb-vehiclefailure/.github/ISSUE_TEMPLATE/bug_report.md b/[qb]/qb-vehiclefailure/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..62f702f --- /dev/null +++ b/[qb]/qb-vehiclefailure/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve or fix something +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. A stranger to qbcore should be able to read your bug report and understand how to reproduce it themselves and understand how the feature should work normally. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Use this item '....' (item's name from shared.lua if applicable) +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Questions (please complete the following information):** + - When you last updated: [e.g. last week] + - Are you using custom resource? which ones? [e.g. zdiscord, qb-target] + - Have you renamed `qb-` to something custom? [e.g. yes/no] + +**Additional context** +Add any other context about the problem here. diff --git a/[qb]/qb-vehiclefailure/.github/ISSUE_TEMPLATE/feature-request.md b/[qb]/qb-vehiclefailure/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..9e9bf3e --- /dev/null +++ b/[qb]/qb-vehiclefailure/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest an idea for QBCore +title: "[SUGGESTION]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. and with as much detail as possible how it would function in your opinion. Please try to keep it unique. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered for people to have in mind just in case the main idea isn't liked but a derivative is. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/[qb]/qb-vehiclefailure/.github/auto_assign.yml b/[qb]/qb-vehiclefailure/.github/auto_assign.yml new file mode 100644 index 0000000..2a80921 --- /dev/null +++ b/[qb]/qb-vehiclefailure/.github/auto_assign.yml @@ -0,0 +1,17 @@ +# Set to true to add reviewers to pull requests +addReviewers: true + +# Set to true to add assignees to pull requests +addAssignees: author + +# A list of reviewers to be added to pull requests (GitHub user name) +reviewers: + - /maintenance + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +skipKeywords: + - wip + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 0 \ No newline at end of file diff --git a/[qb]/qb-vehiclefailure/.github/contributing.md b/[qb]/qb-vehiclefailure/.github/contributing.md new file mode 100644 index 0000000..21fb806 --- /dev/null +++ b/[qb]/qb-vehiclefailure/.github/contributing.md @@ -0,0 +1,201 @@ +# Contributing to QBCore + +First of all, thank you for taking the time to contribute! + +These guidelines will help you help us in the best way possible regardless of your skill level. We ask that you try to read everything related to the way you'd like to contribute and try and use your best judgement for anything not covered. + +### Table of Contents + +[Code of Conduct](#code-of-conduct) + +[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Suggesting Features / Enhancements](#suggesting-features--enhancements) + * [Your First Code Contribution](#your-first-code-contribution) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Git Commit Messages](#git-commit-messages) + * [Lua Styleguide](#lua-styleguide) + * [JavaScript Styleguide](#javascript-styleguide) + + + +## Code of Conduct + +- Refrain from using languages other than English. +- Refrain from discussing any politically charged or inflammatory topics. +- Uphold mature conversations and respect each other; excessive profanity, hate speech or any kind of harassment will not be tolerated. +- No advertising of any kind. +- Follow these guidelines. +- Do not mention members of github unless a question is directed at them and can't be answered by anyone else. +- Do not mention any of the development team for any reason. We will read things as we get to them. + +## I don't want to read this whole thing I just have a question!!! + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +* [QBCore Website](https://qbcore.org) +* [QBCore Discord](https://discord.gg/qbcore) +* [FiveM Discord - #qbcore channel](https://discord.gg/fivem) + + + + + + + + + + +## How Can I Contribute? + +### Reporting Bugs + +The easiest way to contribute for most people is just to report bugs you find cause if nobody reports it there's a chance we'll never know it exists and then we'll never fix it. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out the bug-report template with the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Check the docs** There's a chance what you see as a bug might just work differently than you expect and if you think it could work better consider a feature enhancement report instead. +* **Search the [discord](https://discord.gg/qbcore)** to see if anyone else has run into the issue and see if it was solved through user error or code changes. (if the code change isn't pending a PR and you know what you're doing consider submitting one following [Pull Requests](#pull-requests) ) +* **Determine which resource the problem should be reported in**. If the bug is related to the inventory for example report this bug under qb-inventory rather than under qb-core or some other resource. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your bug is related to, create an issue on that repository and provide the following information by filling in bug-report template. + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. If something happened with only a specific group or single item but not others, specify that. +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **Include screenshots** which show the specific bug in action or before and after. +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions if possible: + +* **Did the problem start happening recently** (e.g. after updating to a new version of QBCore?) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of QBCore?** What's the most recent commit in which the problem doesn't happen? +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your setup: + +* **When was your QBCore last updated?** +* **What OS is the server running on**? +* **Which *extra* resources do you have installed?** + + +--- + + +### Suggesting Features / Enhancements + +This section guides you through submitting an enhancement suggestion for QBCore, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion. + +Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in feature request template, including the steps that you imagine you would take if the feature you're requesting existed. + +#### Before Submitting An Enhancement Suggestion + +* **Make sure it doesn't already exist.** Sounds silly, but there's a lot of features built in to qbcore that people don't realize so take a look through the docs and stuff to make sure it's not already there. +* **Check if there's already PR which provides that enhancement.** +* **Determine which resource the enhancement should be suggested in.** if it fits with another resource suggest it in that resource. if it would be it's own resource suggest it in the main qb-core repository. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your enhancement suggestion is related to, create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of QBCore which the suggestion is related to. +* **Explain why this enhancement would be useful.** +* **Be creative and unique.** Stealing ideas from popular servers 1:1 detail isn't going to get accepted. + + +--- + + + +### Your First Code Contribution + +Unsure where to begin contributing to QBCore? You can start by looking through these `beginner` and `help-wanted` issues. + + + +--- + + +### Pull Requests + +The process described here has several goals: + +- Maintain QBCore's quality. +- Fix problems that are important to users. +- Engage the community in working toward the best possible QBCore. +- Enable a sustainable system for QBCore's maintainers to review contributions. + +Please follow these steps to have your contribution considered by the maintainers: + +1. Follow all instructions in The Pull Request template. +2. Follow the [styleguides](#styleguides). +3. Await review by the reviewer(s). + +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + + +--- + +## Styleguides + +### Git Commit Messages + +* Limit the first line to 72 characters or less. +* Reference issues and pull requests liberally after the first line. +* Consider starting the commit message with an applicable emoji: + * :art: `:art:` when improving the format/structure of the code + * :racehorse: `:racehorse:` when improving performance + * :memo: `:memo:` when writing docs + * :bug: `:bug:` when fixing a bug + * :fire: `:fire:` when removing code or files + * :white_check_mark: `:white_check_mark:` when adding tests + * :lock: `:lock:` when dealing with security + * :arrow_up: `:arrow_up:` when upgrading dependencies + * :arrow_down: `:arrow_down:` when downgrading dependencies + * :shirt: `:shirt:` when removing linter warnings + +### Lua Styleguide + +All lua code should be done using all the best practices of proper lua using the easiest to read yet fastest/most optimized methods of execution. + +- Use 4 Space indentation +- Aim for lua 5.4 (include `lua54 'yes'` in the fxmanifest.lua) +- Use `PlayerPedId()` instead of `GetPlayerPed(-1)` +- Use `#(vector3 - vector3)` instead of `GetDistanceBetweenCoords()` +- Don't create unnecessary threads. always try to find a better method of triggering events +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables +- For distance checking loops set longer waits if you're outside of a range +- Job specific loops should only run for players with that job, don't waste cycles +- When possible don't trust the client, esspecially with transactions +- Balance security and optimizations +- [Consider this Lua Performance guide](https://springrts.com/wiki/Lua_Performance) +- Use local varriables everywhere possible +- Make use of config options where it makes sense making features optional or customizable +- Instead of `table.insert(myTable, "Value")` use `myTable[#myTable + 1] = "Value"` +- Instead of `table.insert(ages, "bob", 30)` use `ages["bob"] = 30` + + +### JavaScript Styleguide + +- Use 4 Space indentation +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables. diff --git a/[qb]/qb-vehiclefailure/.github/pull_request_template.md b/[qb]/qb-vehiclefailure/.github/pull_request_template.md new file mode 100644 index 0000000..000f0f9 --- /dev/null +++ b/[qb]/qb-vehiclefailure/.github/pull_request_template.md @@ -0,0 +1,10 @@ +**Describe Pull request** +First, make sure you've read and are following the contribution guidelines and style guide and your code reflects that. +Write up a clear and concise description of what your pull request adds or fixes and if it's an added feature explain why you think it should be included in the core. + +If your PR is to fix an issue mention that issue here + +**Questions (please complete the following information):** +- Have you personally loaded this code into an updated qbcore project and checked all it's functionality? [yes/no] (Be honest) +- Does your code fit the style guidelines? [yes/no] +- Does your PR fit the contribution guidelines? [yes/no] diff --git a/[qb]/qb-vehiclefailure/.github/workflows/lint.yml b/[qb]/qb-vehiclefailure/.github/workflows/lint.yml new file mode 100644 index 0000000..fb74fd6 --- /dev/null +++ b/[qb]/qb-vehiclefailure/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint +on: [push, pull_request_target] +jobs: + lint: + name: Lint Resource + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Lint + uses: iLLeniumStudios/fivem-lua-lint-action@v2 + with: + capture: "junit.xml" + args: "-t --formatter JUnit" + extra_libs: mysql+polyzone+qblocales + - name: Generate Lint Report + if: always() + uses: mikepenz/action-junit-report@v3 + with: + report_paths: "**/junit.xml" + check_name: Linting Report + fail_on_failure: false \ No newline at end of file diff --git a/[qb]/qb-vehiclefailure/LICENSE b/[qb]/qb-vehiclefailure/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/[qb]/qb-vehiclefailure/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[qb]/qb-vehiclefailure/README.md b/[qb]/qb-vehiclefailure/README.md new file mode 100644 index 0000000..eff8a98 --- /dev/null +++ b/[qb]/qb-vehiclefailure/README.md @@ -0,0 +1,20 @@ +# qb-vehiclefailure +Vehicle Failure For QB-Core + +# License + + QBCore Framework + Copyright (C) 2021 Joshua Eger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see diff --git a/[qb]/qb-vehiclefailure/client.lua b/[qb]/qb-vehiclefailure/client.lua new file mode 100644 index 0000000..1414b1d --- /dev/null +++ b/[qb]/qb-vehiclefailure/client.lua @@ -0,0 +1,666 @@ +local QBCore = exports['qb-core']:GetCoreObject() +local pedInSameVehicleLast=false +local vehicle +local lastVehicle +local vehicleClass +local fCollisionDamageMult = 0.0 +local fDeformationDamageMult = 0.0 +local fEngineDamageMult = 0.0 +local fBrakeForce = 1.0 +local isBrakingForward = false +local isBrakingReverse = false +local healthEngineLast = 1000.0 +local healthEngineCurrent = 1000.0 +local healthEngineNew = 1000.0 +local healthEngineDelta = 0.0 +local healthEngineDeltaScaled = 0.0 +local healthBodyLast = 1000.0 +local healthBodyCurrent = 1000.0 +local healthBodyNew = 1000.0 +local healthBodyDelta = 0.0 +local healthBodyDeltaScaled = 0.0 +local healthPetrolTankLast = 1000.0 +local healthPetrolTankCurrent = 1000.0 +local healthPetrolTankNew = 1000.0 +local healthPetrolTankDelta = 0.0 +local healthPetrolTankDeltaScaled = 0.0 +local tireBurstLuckyNumber +local fixMessagePos = math.random(repairCfg.fixMessageCount) +local noFixMessagePos = math.random(repairCfg.noFixMessageCount) +local tireBurstMaxNumber = cfg.randomTireBurstInterval * 1200; +if cfg.randomTireBurstInterval ~= 0 then tireBurstLuckyNumber = math.random(tireBurstMaxNumber) end + +local DamageComponents = { + "radiator", + "axle", + "clutch", + "fuel", + "brakes", +} + +-- Functions + +local function DamageRandomComponent() + if cfg.degradeRandomComponents == false then + return + end + local dmgFctr = math.random() + math.random(0, 2) + local randomComponent = DamageComponents[math.random(1, #DamageComponents)] + local randomDamage = (math.random() + math.random(0, 1)) * dmgFctr + exports['qb-mechanicjob']:SetVehicleStatus(QBCore.Functions.GetPlate(vehicle), randomComponent, exports['qb-mechanicjob']:GetVehicleStatus(QBCore.Functions.GetPlate(vehicle), randomComponent) - randomDamage) +end + +local function CleanVehicle(veh) + local ped = PlayerPedId() + TaskStartScenarioInPlace(ped, "WORLD_HUMAN_MAID_CLEAN", 0, true) + QBCore.Functions.Progressbar("cleaning_vehicle", Lang:t("progress.clean_veh"), math.random(10000, 20000), false, true, { + disableMovement = true, + disableCarMovement = true, + disableMouse = false, + disableCombat = true, + }, {}, {}, {}, function() -- Done + QBCore.Functions.Notify(Lang:t("success.cleaned_veh")) + SetVehicleDirtLevel(veh, 0.1) + SetVehicleUndriveable(veh, false) + WashDecalsFromVehicle(veh, 1.0) + TriggerServerEvent('qb-vehiclefailure:server:removewashingkit', veh) + TriggerEvent('inventory:client:ItemBox', QBCore.Shared.Items["cleaningkit"], "remove") + ClearAllPedProps(ped) + ClearPedTasks(ped) + end, function() -- Cancel + QBCore.Functions.Notify(Lang:t("error.failed_notification"), "error") + ClearAllPedProps(ped) + ClearPedTasks(ped) + end) +end + +local function IsBackEngine(vehModel) + if BackEngineVehicles[vehModel] then return true else return false end +end + +local function RepairVehicleFull(veh) + if (IsBackEngine(GetEntityModel(veh))) then + SetVehicleDoorOpen(veh, 5, false, false) + else + SetVehicleDoorOpen(veh, 4, false, false) + end + + QBCore.Functions.Progressbar("repair_vehicle", Lang:t("progress.repair_veh"), math.random(20000, 30000), false, true, { + disableMovement = true, + disableCarMovement = true, + disableMouse = false, + disableCombat = true, + }, { + animDict = "mini@repair", + anim = "fixing_a_player", + flags = 1, + }, {}, {}, function() -- Done + StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0) + QBCore.Functions.Notify(Lang:t("success.repaired_veh")) + SetVehicleEngineHealth(veh, 1000.0) + SetVehicleEngineOn(veh, true, false) + for i = 0, 5 do + SetVehicleTyreFixed(veh, i) + TriggerEvent('qb-vehiclefailure:client:TyreSync', veh, i) + end + if (IsBackEngine(GetEntityModel(veh))) then + SetVehicleDoorShut(veh, 5, false) + else + SetVehicleDoorShut(veh, 4, false) + end + TriggerServerEvent('qb-vehiclefailure:removeItem', "advancedrepairkit") + end, function() -- Cancel + StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0) + QBCore.Functions.Notify(Lang:t("error.failed_notification"), "error") + if (IsBackEngine(GetEntityModel(veh))) then + SetVehicleDoorShut(veh, 5, false) + else + SetVehicleDoorShut(veh, 4, false) + end + end) +end + +local function RepairVehicle(veh) + if (IsBackEngine(GetEntityModel(veh))) then + SetVehicleDoorOpen(veh, 5, false, false) + else + SetVehicleDoorOpen(veh, 4, false, false) + end + QBCore.Functions.Progressbar("repair_vehicle", Lang:t("progress.repair_veh"), math.random(10000, 20000), false, true, { + disableMovement = true, + disableCarMovement = true, + disableMouse = false, + disableCombat = true, + }, { + animDict = "mini@repair", + anim = "fixing_a_player", + flags = 1, + }, {}, {}, function() -- Done + StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0) + QBCore.Functions.Notify(Lang:t("success.repaired_veh")) + SetVehicleEngineHealth(veh, 500.0) + SetVehicleEngineOn(veh, true, false) + for i = 0, 5 do + SetVehicleTyreFixed(veh, i) + TriggerEvent('qb-vehiclefailure:client:TyreSync', veh, i) + end + if (IsBackEngine(GetEntityModel(veh))) then + SetVehicleDoorShut(veh, 5, false) + else + SetVehicleDoorShut(veh, 4, false) + end + TriggerServerEvent('qb-vehiclefailure:removeItem', "repairkit") + end, function() -- Cancel + StopAnimTask(PlayerPedId(), "mini@repair", "fixing_a_player", 1.0) + QBCore.Functions.Notify(Lang:t("error.failed_notification"), "error") + if (IsBackEngine(GetEntityModel(veh))) then + SetVehicleDoorShut(veh, 5, false) + else + SetVehicleDoorShut(veh, 4, false) + end + end) +end + +local function isPedDrivingAVehicle() + local ped = PlayerPedId() + vehicle = GetVehiclePedIsIn(ped, false) + if IsPedInAnyVehicle(ped, false) then + -- Check if ped is in driver seat + if GetPedInVehicleSeat(vehicle, -1) == ped then + local class = GetVehicleClass(vehicle) + -- We don't want planes, helicopters, bicycles and trains + if class ~= 15 and class ~= 16 and class ~=21 and class ~=13 then + return true + end + end + end + return false +end + +local function IsNearMechanic() + local ped = PlayerPedId() + local pedLocation = GetEntityCoords(ped, 0) + for _, item in pairs(repairCfg.mechanics) do + local distance = #(vector3(item.x, item.y, item.z) - pedLocation) + if distance <= item.r then + return true + end + end +end + +local function fscale(inputValue, originalMin, originalMax, newBegin, newEnd, curve) + local OriginalRange + local NewRange + local zeroRefCurVal + local normalizedCurVal + local rangedValue + local invFlag = 0 + + if (curve > 10.0) then curve = 10.0 end + if (curve < -10.0) then curve = -10.0 end + + curve = (curve * -.1) + curve = 10.0 ^ curve + + if (inputValue < originalMin) then + inputValue = originalMin + end + if inputValue > originalMax then + inputValue = originalMax + end + + OriginalRange = originalMax - originalMin + + if (newEnd > newBegin) then + NewRange = newEnd - newBegin + else + NewRange = newBegin - newEnd + invFlag = 1 + end + + zeroRefCurVal = inputValue - originalMin + normalizedCurVal = zeroRefCurVal / OriginalRange + + if (originalMin > originalMax ) then + return 0 + end + + if (invFlag == 0) then + rangedValue = ((normalizedCurVal ^ curve) * NewRange) + newBegin + else + rangedValue = newBegin - ((normalizedCurVal ^ curve) * NewRange) + end + + return rangedValue +end + +local function tireBurstLottery() + local tireBurstNumber = math.random(tireBurstMaxNumber) + if tireBurstNumber == tireBurstLuckyNumber then + -- We won the lottery, lets burst a tire. + if GetVehicleTyresCanBurst(vehicle) == false then return end + local numWheels = GetVehicleNumberOfWheels(vehicle) + local affectedTire + if numWheels == 2 then + affectedTire = (math.random(2) - 1) * 4 -- wheel 0 or 4 + elseif numWheels == 4 then + affectedTire = (math.random(4) - 1) + if affectedTire > 1 then affectedTire = affectedTire + 2 end -- 0, 1, 4, 5 + elseif numWheels == 6 then + affectedTire = (math.random(6) - 1) + else + affectedTire = 0 + end + SetVehicleTyreBurst(vehicle, affectedTire, false, 1000.0) + tireBurstLuckyNumber = math.random(tireBurstMaxNumber) -- Select a new number to hit, just in case some numbers occur more often than others + end +end + +-- Events + +RegisterNetEvent('qb-vehiclefailure:client:RepairVehicle', function() + local veh = QBCore.Functions.GetClosestVehicle() + local engineHealth = GetVehicleEngineHealth(veh) --This is to prevent people from "repairing" a vehicle and setting engine health lower than what the vehicles engine health was before repairing. + if veh ~= nil and veh ~= 0 and engineHealth < 500 then + local ped = PlayerPedId() + local pos = GetEntityCoords(ped) + local vehpos = GetEntityCoords(veh) + if #(pos - vehpos) < 5.0 and not IsPedInAnyVehicle(ped) then + local drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, 2.5, 0) + if (IsBackEngine(GetEntityModel(veh))) then + drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, -2.5, 0) + end + if #(pos - drawpos) < 2.0 and not IsPedInAnyVehicle(ped) then + RepairVehicle(veh) + end + else + if #(pos - vehpos) > 4.9 then + QBCore.Functions.Notify(Lang:t("error.out_range_veh"), "error") + else + QBCore.Functions.Notify(Lang:t("error.inside_veh"), "error") + end + end + else + if veh == nil or veh == 0 then + QBCore.Functions.Notify(Lang:t("error.not_near_veh"), "error") + else + QBCore.Functions.Notify(Lang:t("error.healthy_veh"), "error") + end + end +end) + +RegisterNetEvent('qb-vehiclefailure:client:SyncWash', function(veh) + SetVehicleDirtLevel(veh, 0.1) + SetVehicleUndriveable(veh, false) + WashDecalsFromVehicle(veh, 1.0) +end) + +RegisterNetEvent('qb-vehiclefailure:client:CleanVehicle', function() + local veh = QBCore.Functions.GetClosestVehicle() + if veh ~= nil and veh ~= 0 then + local ped = PlayerPedId() + local pos = GetEntityCoords(ped) + local vehpos = GetEntityCoords(veh) + if #(pos - vehpos) < 3.0 and not IsPedInAnyVehicle(ped) then + CleanVehicle(veh) + end + end +end) + +RegisterNetEvent('qb-vehiclefailure:client:RepairVehicleFull', function() + local veh = QBCore.Functions.GetClosestVehicle() + if veh ~= nil and veh ~= 0 then + local ped = PlayerPedId() + local pos = GetEntityCoords(ped) + local vehpos = GetEntityCoords(veh) + if #(pos - vehpos) < 5.0 and not IsPedInAnyVehicle(ped) then + local drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, 2.5, 0) + if (IsBackEngine(GetEntityModel(veh))) then + drawpos = GetOffsetFromEntityInWorldCoords(veh, 0, -2.5, 0) + end + if #(pos - drawpos) < 2.0 and not IsPedInAnyVehicle(ped) then + RepairVehicleFull(veh) + end + else + if #(pos - vehpos) > 4.9 then + QBCore.Functions.Notify(Lang:t("error.out_range_veh"), "error") + else + QBCore.Functions.Notify(Lang:t("error.inside_veh"), "error") + end + end + else + QBCore.Functions.Notify(Lang:t("error.not_near_veh"), "error") + end +end) + +RegisterNetEvent('qb-vehiclefailure:client:TyreSync', function(veh, tyre) + SetVehicleTyreFixed(veh, tyre) + SetVehicleWheelHealth(veh, tyre, 100) +end) + +RegisterNetEvent('iens:repaira', function() + local ped = PlayerPedId() + if IsPedInAnyVehicle(ped, false) or IsPedInAnyPlane(ped) then + vehicle = GetVehiclePedIsIn(ped, false) + SetVehicleDirtLevel(vehicle) + SetVehicleUndriveable(vehicle, false) + WashDecalsFromVehicle(vehicle, 1.0) + QBCore.Functions.Notify(Lang:t("success.repaired_veh")) + SetVehicleFixed(vehicle) + healthBodyLast = 1000.0 + healthEngineLast = 1000.0 + healthPetrolTankLast = 1000.0 + SetVehicleEngineOn(vehicle, true, false ) + return true + end + QBCore.Functions.Notify(Lang:t("error.inside_veh_req")) +end) + +RegisterNetEvent('iens:besked', function() + QBCore.Functions.Notify(Lang:t("error.roadside_avail")) +end) + +RegisterNetEvent('iens:notAllowed', function() + QBCore.Functions.Notify(Lang:t("error.no_permission")) +end) + +RegisterNetEvent('iens:repair', function() + if isPedDrivingAVehicle() then + local ped = PlayerPedId() + vehicle = GetVehiclePedIsIn(ped, false) + if IsNearMechanic() then + return + end + if GetVehicleEngineHealth(vehicle) < cfg.cascadingFailureThreshold + 5 then + if GetVehicleOilLevel(vehicle) > 0 then + SetVehicleUndriveable(vehicle, false) + SetVehicleEngineHealth(vehicle, cfg.cascadingFailureThreshold + 5) + SetVehiclePetrolTankHealth(vehicle, 750.0) + healthEngineLast=cfg.cascadingFailureThreshold +5 + healthPetrolTankLast=750.0 + SetVehicleEngineOn(vehicle, true, false ) + SetVehicleOilLevel(vehicle,(GetVehicleOilLevel(vehicle)/3)-0.5) + QBCore.Functions.Notify(Lang:t(('fix_message_%s'):format(fixMessagePos))) + fixMessagePos = fixMessagePos + 1 + if fixMessagePos > repairCfg.fixMessageCount then fixMessagePos = 1 end + else + QBCore.Functions.Notify(Lang:t("error.veh_damaged")) + end + else + QBCore.Functions.Notify(Lang:t(('nofix_message_%s'):format(noFixMessagePos))) + noFixMessagePos = noFixMessagePos + 1 + if noFixMessagePos > repairCfg.noFixMessageCount then noFixMessagePos = 1 end + end + else + QBCore.Functions.Notify(Lang:t("error.inside_veh_req")) + end +end) + +-- Threads + +CreateThread(function() + if (cfg.displayBlips == true) then + for _, item in pairs(repairCfg.mechanics) do + item.blip = AddBlipForCoord(item.x, item.y, item.z) + SetBlipSprite(item.blip, item.id) + SetBlipScale(item.blip, 0.8) + SetBlipAsShortRange(item.blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString(item.name) + EndTextCommandSetBlipName(item.blip) + end + end +end) + +if cfg.torqueMultiplierEnabled or cfg.preventVehicleFlip or cfg.limpMode then + CreateThread(function() + while true do + Wait(0) + if cfg.torqueMultiplierEnabled or cfg.sundayDriver or cfg.limpMode then + if pedInSameVehicleLast then + local factor = 1.0 + if cfg.torqueMultiplierEnabled and healthEngineNew < 900 then + factor = (healthEngineNew+200.0) / 1100 + end + if cfg.sundayDriver and GetVehicleClass(vehicle) ~= 14 then -- Not for boats + local accelerator = GetControlValue(2,71) + local brake = GetControlValue(2,72) + local speed = GetEntitySpeedVector(vehicle, true)['y'] + -- Change Braking force + local brk = fBrakeForce + if speed >= 1.0 then + -- Going forward + if accelerator > 127 then + -- Forward and accelerating + local acc = fscale(accelerator, 127.0, 254.0, 0.1, 1.0, 10.0-(cfg.sundayDriverAcceleratorCurve*2.0)) + factor = factor * acc + end + if brake > 127 then + -- Forward and braking + isBrakingForward = true + brk = fscale(brake, 127.0, 254.0, 0.01, fBrakeForce, 10.0-(cfg.sundayDriverBrakeCurve*2.0)) + --exports['qb-vehicletuning']:SetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes", exports['qb-vehicletuning']:GetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes") - 0.01) + end + elseif speed <= -1.0 then + -- Going reverse + if brake > 127 then + -- Reversing and accelerating (using the brake) + local rev = fscale(brake, 127.0, 254.0, 0.1, 1.0, 10.0-(cfg.sundayDriverAcceleratorCurve*2.0)) + factor = factor * rev + --exports['qb-vehicletuning']:SetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes", exports['qb-vehicletuning']:GetVehicleStatus(QBCore.Functions.GetPlate(vehicle), "brakes") - 0.01) + end + if accelerator > 127 then + -- Reversing and braking (Using the accelerator) + isBrakingReverse = true + brk = fscale(accelerator, 127.0, 254.0, 0.01, fBrakeForce, 10.0 - (cfg.sundayDriverBrakeCurve * 2.0)) + end + else + -- Stopped or almost stopped or sliding sideways + local entitySpeed = GetEntitySpeed(vehicle) + if entitySpeed < 1 then + -- Not sliding sideways + if isBrakingForward == true then + --Stopped or going slightly forward while braking + DisableControlAction(2, 72, true) -- Disable Brake until user lets go of brake + SetVehicleForwardSpeed(vehicle, speed * 0.98) + SetVehicleBrakeLights(vehicle, true) + end + if isBrakingReverse == true then + --Stopped or going slightly in reverse while braking + DisableControlAction(2, 71, true) -- Disable reverse Brake until user lets go of reverse brake (Accelerator) + SetVehicleForwardSpeed(vehicle,speed*0.98) + SetVehicleBrakeLights(vehicle,true) + end + if isBrakingForward == true and GetDisabledControlNormal(2, 72) == 0 then + -- We let go of the brake + isBrakingForward = false + end + if isBrakingReverse == true and GetDisabledControlNormal(2, 71) == 0 then + -- We let go of the reverse brake (Accelerator) + isBrakingReverse = false + end + end + end + if brk > fBrakeForce - 0.02 then brk = fBrakeForce end -- Make sure we can brake max. + SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fBrakeForce', brk) -- Set new Brake Force multiplier + end + if cfg.limpMode == true and healthEngineNew < cfg.engineSafeGuard + 5 then + factor = cfg.limpModeMultiplier + end + SetVehicleEngineTorqueMultiplier(vehicle, factor) + end + end + if cfg.preventVehicleFlip then + local roll = GetEntityRoll(vehicle) + if (roll > 75.0 or roll < -75.0) and GetEntitySpeed(vehicle) < 2 then + DisableControlAction(2, 59, true) -- Disable left/right + DisableControlAction(2, 60, true) -- Disable up/down + end + end + end + end) +end + +CreateThread(function() + while true do + Wait(50) + local ped = PlayerPedId() + if isPedDrivingAVehicle() then + vehicle = GetVehiclePedIsIn(ped, false) + vehicleClass = GetVehicleClass(vehicle) + healthEngineCurrent = GetVehicleEngineHealth(vehicle) + if healthEngineCurrent == 1000 then healthEngineLast = 1000.0 end + healthEngineNew = healthEngineCurrent + healthEngineDelta = healthEngineLast - healthEngineCurrent + healthEngineDeltaScaled = healthEngineDelta * cfg.damageFactorEngine * cfg.classDamageMultiplier[vehicleClass] + + healthBodyCurrent = GetVehicleBodyHealth(vehicle) + if healthBodyCurrent == 1000 then healthBodyLast = 1000.0 end + healthBodyNew = healthBodyCurrent + healthBodyDelta = healthBodyLast - healthBodyCurrent + healthBodyDeltaScaled = healthBodyDelta * cfg.damageFactorBody * cfg.classDamageMultiplier[vehicleClass] + + healthPetrolTankCurrent = GetVehiclePetrolTankHealth(vehicle) + if cfg.compatibilityMode and healthPetrolTankCurrent < 1 then + -- SetVehiclePetrolTankHealth(vehicle, healthPetrolTankLast) + -- healthPetrolTankCurrent = healthPetrolTankLast + healthPetrolTankLast = healthPetrolTankCurrent + end + if healthPetrolTankCurrent == 1000 then healthPetrolTankLast = 1000.0 end + healthPetrolTankNew = healthPetrolTankCurrent + healthPetrolTankDelta = healthPetrolTankLast-healthPetrolTankCurrent + healthPetrolTankDeltaScaled = healthPetrolTankDelta * cfg.damageFactorPetrolTank * cfg.classDamageMultiplier[vehicleClass] + + if healthEngineCurrent > cfg.engineSafeGuard+1 then + SetVehicleUndriveable(vehicle,false) + end + + if healthEngineCurrent <= cfg.engineSafeGuard+1 and cfg.limpMode == false then + local vehpos = GetEntityCoords(vehicle) + StartParticleFxLoopedAtCoord("ent_ray_heli_aprtmnt_l_fire", vehpos.x, vehpos.y, vehpos.z-0.7, 0.0, 0.0, 0.0, 1.0, false, false, false, false) + SetVehicleUndriveable(vehicle,true) + end + + -- If ped spawned a new vehicle while in a vehicle or teleported from one vehicle to another, handle as if we just entered the car + if vehicle ~= lastVehicle then + pedInSameVehicleLast = false + end + + + if pedInSameVehicleLast == true then + -- Damage happened while in the car = can be multiplied + + -- Only do calculations if any damage is present on the car. Prevents weird behavior when fixing using trainer or other script + if healthEngineCurrent ~= 1000.0 or healthBodyCurrent ~= 1000.0 or healthPetrolTankCurrent ~= 1000.0 then + + -- Combine the delta values (Get the largest of the three) + local healthEngineCombinedDelta = math.max(healthEngineDeltaScaled, healthBodyDeltaScaled, healthPetrolTankDeltaScaled) + + -- If huge damage, scale back a bit + if healthEngineCombinedDelta > (healthEngineCurrent - cfg.engineSafeGuard) then + healthEngineCombinedDelta = healthEngineCombinedDelta * 0.7 + end + + -- If complete damage, but not catastrophic (ie. explosion territory) pull back a bit, to give a couple of seconds og engine runtime before dying + if healthEngineCombinedDelta > healthEngineCurrent then + healthEngineCombinedDelta = healthEngineCurrent - (cfg.cascadingFailureThreshold / 5) + end + + + ------- Calculate new value + + healthEngineNew = healthEngineLast - healthEngineCombinedDelta + + + ------- Sanity Check on new values and further manipulations + + -- If somewhat damaged, slowly degrade until slightly before cascading failure sets in, then stop + + if healthEngineNew > (cfg.cascadingFailureThreshold + 5) and healthEngineNew < cfg.degradingFailureThreshold then + healthEngineNew = healthEngineNew-(0.038 * cfg.degradingHealthSpeedFactor) + end + + -- If Damage is near catastrophic, cascade the failure + if healthEngineNew < cfg.cascadingFailureThreshold then + healthEngineNew = healthEngineNew-(0.1 * cfg.cascadingFailureSpeedFactor) + end + + -- Prevent Engine going to or below zero. Ensures you can reenter a damaged car. + if healthEngineNew < cfg.engineSafeGuard then + healthEngineNew = cfg.engineSafeGuard + end + + -- Prevent Explosions + if cfg.compatibilityMode == false and healthPetrolTankCurrent < 750 then + healthPetrolTankNew = 750.0 + end + + -- Prevent negative body damage. + if healthBodyNew < 0 then + healthBodyNew = 0.0 + end + end + else + -- Just got in the vehicle. Damage can not be multiplied this round + -- Set vehicle handling data + fDeformationDamageMult = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fDeformationDamageMult') + fBrakeForce = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fBrakeForce') + local newFDeformationDamageMult = fDeformationDamageMult ^ cfg.deformationExponent -- Pull the handling file value closer to 1 + if cfg.deformationMultiplier ~= -1 then SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fDeformationDamageMult', newFDeformationDamageMult * cfg.deformationMultiplier) end -- Multiply by our factor + if cfg.weaponsDamageMultiplier ~= -1 then SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fWeaponDamageMult', cfg.weaponsDamageMultiplier/cfg.damageFactorBody) end -- Set weaponsDamageMultiplier and compensate for damageFactorBody + + --Get the CollisionDamageMultiplier + fCollisionDamageMult = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fCollisionDamageMult') + --Modify it by pulling all number a towards 1.0 + local newFCollisionDamageMultiplier = fCollisionDamageMult ^ cfg.collisionDamageExponent -- Pull the handling file value closer to 1 + SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fCollisionDamageMult', newFCollisionDamageMultiplier) + + --Get the EngineDamageMultiplier + fEngineDamageMult = GetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fEngineDamageMult') + --Modify it by pulling all number a towards 1.0 + local newFEngineDamageMult = fEngineDamageMult ^ cfg.engineDamageExponent -- Pull the handling file value closer to 1 + SetVehicleHandlingFloat(vehicle, 'CHandlingData', 'fEngineDamageMult', newFEngineDamageMult) + + -- If body damage catastrophic, reset somewhat so we can get new damage to multiply + if healthBodyCurrent < cfg.cascadingFailureThreshold then + healthBodyNew = cfg.cascadingFailureThreshold + end + pedInSameVehicleLast = true + end + + -- set the actual new values + if healthEngineNew ~= healthEngineCurrent then + SetVehicleEngineHealth(vehicle, healthEngineNew) + local dmgFactr = (healthEngineCurrent - healthEngineNew) + if dmgFactr > 0.8 then + DamageRandomComponent() + end + end + if healthBodyNew ~= healthBodyCurrent then + SetVehicleBodyHealth(vehicle, healthBodyNew) + DamageRandomComponent() + end + if healthPetrolTankNew ~= healthPetrolTankCurrent then + SetVehiclePetrolTankHealth(vehicle, healthPetrolTankNew) + end + + -- Store current values, so we can calculate delta next time around + healthEngineLast = healthEngineNew + healthBodyLast = healthBodyNew + healthPetrolTankLast = healthPetrolTankNew + lastVehicle=vehicle + if cfg.randomTireBurstInterval ~= 0 and GetEntitySpeed(vehicle) > 10 then tireBurstLottery() end + else + if pedInSameVehicleLast == true then + -- We just got out of the vehicle + lastVehicle = GetVehiclePedIsIn(ped, true) + if cfg.deformationMultiplier ~= -1 then SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fDeformationDamageMult', fDeformationDamageMult) end -- Restore deformation multiplier + SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fBrakeForce', fBrakeForce) -- Restore Brake Force multiplier + if cfg.weaponsDamageMultiplier ~= -1 then SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fWeaponDamageMult', cfg.weaponsDamageMultiplier) end -- Since we are out of the vehicle, we should no longer compensate for bodyDamageFactor + SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fCollisionDamageMult', fCollisionDamageMult) -- Restore the original CollisionDamageMultiplier + SetVehicleHandlingFloat(lastVehicle, 'CHandlingData', 'fEngineDamageMult', fEngineDamageMult) -- Restore the original EngineDamageMultiplier + end + pedInSameVehicleLast = false + end + end +end) diff --git a/[qb]/qb-vehiclefailure/config.lua b/[qb]/qb-vehiclefailure/config.lua new file mode 100644 index 0000000..8abf215 --- /dev/null +++ b/[qb]/qb-vehiclefailure/config.lua @@ -0,0 +1,216 @@ +BackEngineVehicles = { + [`ninef`] = true, + [`adder`] = true, + [`vagner`] = true, + [`t20`] = true, + [`infernus`] = true, + [`zentorno`] = true, + [`reaper`] = true, + [`comet2`] = true, + [`jester`] = true, + [`jester2`] = true, + [`cheetah`] = true, + [`cheetah2`] = true, + [`prototipo`] = true, + [`turismor`] = true, + [`pfister811`] = true, + [`ardent`] = true, + [`nero`] = true, + [`nero2`] = true, + [`tempesta`] = true, + [`vacca`] = true, + [`bullet`] = true, + [`osiris`] = true, + [`entityxf`] = true, + [`turismo2`] = true, + [`fmj`] = true, + [`re7b`] = true, + [`tyrus`] = true, + [`italigtb`] = true, + [`penetrator`] = true, + [`monroe`] = true, + [`ninef2`] = true, + [`stingergt`] = true, + [`surfer`] = true, + [`surfer2`] = true, + [`comet3`] = true, +} + +-- Configuration: + +-- IMPORTANT: Some of these values MUST be defined as a floating point number. ie. 10.0 instead of 10 + Config = {} + Config.Paid = true + Config.Price = 750 + +cfg = { + degradeRandomComponents = true, -- Enable or disable degradation of random components (radiator, brakes etc). Set to false if you aren't used qb-mechanicjob. + deformationMultiplier = -1, -- How much should the vehicle visually deform from a collision. Range 0.0 to 10.0 Where 0.0 is no deformation and 10.0 is 10x deformation. -1 = Don't touch. Visual damage does not sync well to other players. + deformationExponent = 0.5, -- How much should the handling file deformation setting be compressed toward 1.0. (Make cars more similar). A value of 1=no change. Lower values will compress more, values above 1 it will expand. Dont set to zero or negative. + collisionDamageExponent = 0.5, -- How much should the handling file deformation setting be compressed toward 1.0. (Make cars more similar). A value of 1=no change. Lower values will compress more, values above 1 it will expand. Dont set to zero or negative. + + damageFactorEngine = 3.0, -- Sane values are 1 to 100. Higher values means more damage to vehicle. A good starting point is 10 + damageFactorBody = 3.0, -- Sane values are 1 to 100. Higher values means more damage to vehicle. A good starting point is 10 + damageFactorPetrolTank = 32.0, -- Sane values are 1 to 200. Higher values means more damage to vehicle. A good starting point is 64 + engineDamageExponent = 0.3, -- How much should the handling file engine damage setting be compressed toward 1.0. (Make cars more similar). A value of 1=no change. Lower values will compress more, values above 1 it will expand. Dont set to zero or negative. + weaponsDamageMultiplier = 1.2, -- How much damage should the vehicle get from weapons fire. Range 0.0 to 10.0, where 0.0 is no damage and 10.0 is 10x damage. -1 = don't touch + degradingHealthSpeedFactor = 2, -- Speed of slowly degrading health, but not failure. Value of 10 means that it will take about 0.25 second per health point, so degradation from 800 to 305 will take about 2 minutes of clean driving. Higher values means faster degradation + cascadingFailureSpeedFactor = 4.0, -- Sane values are 1 to 100. When vehicle health drops below a certain point, cascading failure sets in, and the health drops rapidly until the vehicle dies. Higher values means faster failure. A good starting point is 8 + + degradingFailureThreshold = 250.0, -- Below this value, slow health degradation will set in + cascadingFailureThreshold = 200.0, -- Below this value, health cascading failure will set in + engineSafeGuard = 50.0, -- Final failure value. Set it too high, and the vehicle won't smoke when disabled. Set too low, and the car will catch fire from a single bullet to the engine. At health 100 a typical car can take 3-4 bullets to the engine before catching fire. + + torqueMultiplierEnabled = true, -- Decrease engine torque as engine gets more and more damaged + + limpMode = false, -- If true, the engine never fails completely, so you will always be able to get to a mechanic unless you flip your vehicle and preventVehicleFlip is set to true + limpModeMultiplier = 0.15, -- The torque multiplier to use when vehicle is limping. Sane values are 0.05 to 0.25 + + preventVehicleFlip = true, -- If true, you can't turn over an upside down vehicle + + sundayDriver = true, -- If true, the accelerator response is scaled to enable easy slow driving. Will not prevent full throttle. Does not work with binary accelerators like a keyboard. Set to false to disable. The included stop-without-reversing and brake-light-hold feature does also work for keyboards. + sundayDriverAcceleratorCurve = 7.5, -- The response curve to apply to the accelerator. Range 0.0 to 10.0. Higher values enables easier slow driving, meaning more pressure on the throttle is required to accelerate forward. Does nothing for keyboard drivers + sundayDriverBrakeCurve = 5.0, -- The response curve to apply to the Brake. Range 0.0 to 10.0. Higher values enables easier braking, meaning more pressure on the throttle is required to brake hard. Does nothing for keyboard drivers + + displayBlips = false, -- Show blips for mechanics locations + + compatibilityMode = false, -- prevents other scripts from modifying the fuel tank health to avoid random engine failure with BVA 2.01 (Downside is it disabled explosion prevention) + + randomTireBurstInterval = 0, -- Number of minutes (statistically, not precisely) to drive above 22 mph before you get a tire puncture. 0=feature is disabled + + + -- Class Damagefactor Multiplier + -- The damageFactor for engine, body and Petroltank will be multiplied by this value, depending on vehicle class + -- Use it to increase or decrease damage for each class + + classDamageMultiplier = { + [0] = 0.3, -- 0: Compacts + 0.3, -- 1: Sedans + 0.3, -- 2: SUVs + 0.3, -- 3: Coupes + 0.3, -- 4: Muscle + 0.3, -- 5: Sports Classics + 0.3, -- 6: Sports + 0.3, -- 7: Super + 0.0, -- 8: Motorcycles + 0.3, -- 9: Off-road + 0.3, -- 10: Industrial + 0.3, -- 11: Utility + 0.3, -- 12: Vans + 0.3, -- 13: Cycles + 0.3, -- 14: Boats + 0.3, -- 15: Helicopters + 0.3, -- 16: Planes + 0.3, -- 17: Service + 0.3, -- 18: Emergency + 0.3, -- 19: Military + 0.3, -- 20: Commercial + 0.3 -- 21: Trains + } +} + + + +--[[ + + -- Alternate configuration values provided by ImDylan93 - Vehicles can take more damage before failure, and the balance between vehicles has been tweaked. + -- To use: comment out the settings above, and uncomment this section. + +cfg = { + + deformationMultiplier = -1, -- How much should the vehicle visually deform from a collision. Range 0.0 to 10.0 Where 0.0 is no deformation and 10.0 is 10x deformation. -1 = Don't touch + deformationExponent = 1.0, -- How much should the handling file deformation setting be compressed toward 1.0. (Make cars more similar). A value of 1=no change. Lower values will compress more, values above 1 it will expand. Dont set to zero or negative. + collisionDamageExponent = 1.0, -- How much should the handling file deformation setting be compressed toward 1.0. (Make cars more similar). A value of 1=no change. Lower values will compress more, values above 1 it will expand. Dont set to zero or negative. + + damageFactorEngine = 5.1, -- Sane values are 1 to 100. Higher values means more damage to vehicle. A good starting point is 10 + damageFactorBody = 5.1, -- Sane values are 1 to 100. Higher values means more damage to vehicle. A good starting point is 10 + damageFactorPetrolTank = 61.0, -- Sane values are 1 to 100. Higher values means more damage to vehicle. A good starting point is 64 + engineDamageExponent = 1.0, -- How much should the handling file engine damage setting be compressed toward 1.0. (Make cars more similar). A value of 1=no change. Lower values will compress more, values above 1 it will expand. Dont set to zero or negative. + weaponsDamageMultiplier = 0.124, -- How much damage should the vehicle get from weapons fire. Range 0.0 to 10.0, where 0.0 is no damage and 10.0 is 10x damage. -1 = don't touch + degradingHealthSpeedFactor = 7.4, -- Speed of slowly degrading health, but not failure. Value of 10 means that it will take about 0.25 second per health point, so degradation from 800 to 305 will take about 2 minutes of clean driving. Higher values means faster degradation + cascadingFailureSpeedFactor = 1.5, -- Sane values are 1 to 100. When vehicle health drops below a certain point, cascading failure sets in, and the health drops rapidly until the vehicle dies. Higher values means faster failure. A good starting point is 8 + + degradingFailureThreshold = 677.0, -- Below this value, slow health degradation will set in + cascadingFailureThreshold = 310.0, -- Below this value, health cascading failure will set in + engineSafeGuard = 100.0, -- Final failure value. Set it too high, and the vehicle won't smoke when disabled. Set too low, and the car will catch fire from a single bullet to the engine. At health 100 a typical car can take 3-4 bullets to the engine before catching fire. + + torqueMultiplierEnabled = true, -- Decrease engine torge as engine gets more and more damaged + + limpMode = true, -- If true, the engine never fails completely, so you will always be able to get to a mechanic unless you flip your vehicle and preventVehicleFlip is set to true + limpModeMultiplier = 0.15, -- The torque multiplier to use when vehicle is limping. Sane values are 0.05 to 0.25 + + preventVehicleFlip = true, -- If true, you can't turn over an upside down vehicle + + sundayDriver = true, -- If true, the accelerator response is scaled to enable easy slow driving. Will not prevent full throttle. Does not work with binary accelerators like a keyboard. Set to false to disable. The included stop-without-reversing and brake-light-hold feature does also work for keyboards. + sundayDriverAcceleratorCurve = 7.5, -- The response curve to apply to the accelerator. Range 0.0 to 10.0. Higher values enables easier slow driving, meaning more pressure on the throttle is required to accelerate forward. Does nothing for keyboard drivers + sundayDriverBrakeCurve = 5.0, -- The response curve to apply to the Brake. Range 0.0 to 10.0. Higher values enables easier braking, meaning more pressure on the throttle is required to brake hard. Does nothing for keyboard drivers + + displayBlips = true, -- Show blips for mechanics locations + + classDamageMultiplier = { + [0] = 1.0, -- 0: Compacts + 1.0, -- 1: Sedans + 1.0, -- 2: SUVs + 0.95, -- 3: Coupes + 1.0, -- 4: Muscle + 0.95, -- 5: Sports Classics + 0.95, -- 6: Sports + 0.95, -- 7: Super + 0.27, -- 8: Motorcycles + 0.7, -- 9: Off-road + 0.25, -- 10: Industrial + 0.35, -- 11: Utility + 0.85, -- 12: Vans + 1.0, -- 13: Cycles + 0.4, -- 14: Boats + 0.7, -- 15: Helicopters + 0.7, -- 16: Planes + 0.75, -- 17: Service + 0.85, -- 18: Emergency + 0.67, -- 19: Military + 0.43, -- 20: Commercial + 1.0 -- 21: Trains + } +} + +]]-- + + + + + +-- End of Main Configuration + +-- Configure Repair system + +-- id=446 for wrench icon, id=72 for spraycan icon + +repairCfg = { + mechanics = { + {name="Garage", id=446, r=25.0, x=-337.0, y=-135.0, z=39.0}, -- LSC Burton + {name="Garage", id=446, r=25.0, x=-1155.0, y=-2007.0, z=13.0}, -- LSC by airport + {name="Garage", id=446, r=25.0, x=734.0, y=-1085.0, z=22.0}, -- LSC La Mesa + {name="Garage", id=446, r=25.0, x=1177.0, y=2640.0, z=37.0}, -- LSC Harmony + {name="Garage", id=446, r=25.0, x=108.0, y=6624.0, z=31.0}, -- LSC Paleto Bay + {name="Garage", id=446, r=18.0, x=538.0, y=-183.0, z=54.0}, -- Mechanic Hawic + {name="Garage", id=446, r=15.0, x=1774.0, y=3333.0, z=41.0}, -- Mechanic Sandy Shores Airfield + {name="Garage", id=446, r=15.0, x=1143.0, y=-776.0, z=57.0}, -- Mechanic Mirror Park + {name="Garage", id=446, r=30.0, x=2508.0, y=4103.0, z=38.0}, -- Mechanic East Joshua Rd. + {name="Garage", id=446, r=16.0, x=2006.0, y=3792.0, z=32.0}, -- Mechanic Sandy Shores gas station + {name="Garage", id=446, r=25.0, x=484.0, y=-1316.0, z=29.0}, -- Hayes Auto, Little Bighorn Ave. + {name="Garage", id=446, r=33.0, x=-1419.0, y=-450.0, z=36.0}, -- Hayes Auto Body Shop, Del Perro + {name="Garage", id=446, r=33.0, x=268.0, y=-1810.0, z=27.0}, -- Hayes Auto Body Shop, Davis + -- {name="Mechanic", id=446, r=24.0, x=288.0, y=-1730.0, z=29.0}, -- Hayes Auto, Rancho (Disabled, looks like a warehouse for the Davis branch) + {name="Garage", id=446, r=27.0, x=1915.0, y=3729.0, z=32.0}, -- Otto's Auto Parts, Sandy Shores + {name="Garage", id=446, r=45.0, x=-29.0, y=-1665.0, z=29.0}, -- Mosley Auto Service, Strawberry + {name="Garage", id=446, r=44.0, x=-212.0, y=-1378.0, z=31.0}, -- Glass Heroes, Strawberry + {name="Garage", id=446, r=33.0, x=258.0, y=2594.0, z=44.0}, -- Mechanic Harmony + {name="Garage", id=446, r=18.0, x=-32.0, y=-1090.0, z=26.0}, -- Simeons + --{name="Garage", id=446, r=25.0, x=-211.0, y=-1325.0, z=31.0}, -- Bennys + {name="Garage", id=446, r=25.0, x=903.0, y=3563.0, z=34.0}, -- Auto Repair, Grand Senora Desert + {name="Garage", id=446, r=25.0, x=437.0, y=3568.0, z=38.0} -- Auto Shop, Grand Senora Desert + }, + + fixMessageCount = 7, + noFixMessageCount = 6 +} diff --git a/[qb]/qb-vehiclefailure/fxmanifest.lua b/[qb]/qb-vehiclefailure/fxmanifest.lua new file mode 100644 index 0000000..59f7a46 --- /dev/null +++ b/[qb]/qb-vehiclefailure/fxmanifest.lua @@ -0,0 +1,17 @@ +fx_version 'cerulean' +game 'gta5' + +description 'QB-VehicleFailure' +version '1.2.2' + +shared_scripts { + '@qb-core/shared/locale.lua', + 'locales/en.lua', + 'locales/*.lua', + 'config.lua' +} + +client_script 'client.lua' +server_script 'server.lua' + +lua54 'yes' diff --git a/[qb]/qb-vehiclefailure/locales/ar.lua b/[qb]/qb-vehiclefailure/locales/ar.lua new file mode 100644 index 0000000..72fbd06 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/ar.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "فشل", + ["not_near_veh"] = "أنت لست بالقرب من سيارة", + ["out_range_veh"] = "أنت بعيد جدًا عن السيارة", + ["inside_veh"] = "لا يمكنك إصلاح محرك السيارة من الداخل", + ["healthy_veh"] = "السيارة صحية للغاية وتحتاج إلى أدوات أفضل", + ["inside_veh_req"] = "يجب أن تكون في مركبة لإصلاحها", + ["roadside_avail"] = "تتوفر خدمة المساعدة على الطريق ، يمكنك الاتصال بذلك عبر هاتفك!", + ["no_permission"] = "ليس لديك إذن لإصلاح المركبات", + ["fix_message"] = "%{message} والآن اذهب إلى المرآب", + ["veh_damaged"] = "سيارتك تالفة للغاية", + ["nofix_message_1"] = "نظرت إلى مستوى الزيت الخاص بك وبدا هذا طبيعيًا", + ["nofix_message_2"] = "نظرت إلى دراجتك ولا يبدو أن هناك شيئًا خطأ", + ["nofix_message_3"] = "نظرت إلى شريط البط الموجود على خرطوم الزيت وبدا أنك بخير", + ["nofix_message_4"] = "لقد قمت بتشغيل الراديو وذهب الآن ضجيج المحرك الغريب", + ["nofix_message_5"] = "لم يكن لمزيل الصدأ الذي استخدمته أي تأثير", + ["nofix_message_6"] = "لا تحاول أبدًا صنع شيء لم يتم كسره ولكنك لم تستمع إليه", + }, + success = { + ["cleaned_veh"] = "تم تنظيف السيارة", + ["repaired_veh"] = "مركبة تم تصليحها", + ["fix_message_1"] = "لقد قمت بفحص مستوى الزيت", + ["fix_message_2"] = "لقد أغلقت بقعة الزيت", + ["fix_message_3"] = "لقد صنعت خرطوم الزيت بشريط لاصق", + ["fix_message_4"] = "لقد قمت بإيقاف تسرب الزيت مؤقتًا", + ["fix_message_5"] = "لقد ركلت الدراجة وهي تعمل مرة أخرى", + ["fix_message_6"] = "قمت بإزالة بعض الصدأ", + ["fix_message_7"] = "صرخت في سيارتك وهي تعمل مرة أخرى", + }, + progress = { + ["clean_veh"] = "تنظيف السيارة", + ["repair_veh"] = "تصليح المركبة", + + } +} + +if GetConvar('qb_locale', 'en') == 'ar' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/cs.lua b/[qb]/qb-vehiclefailure/locales/cs.lua new file mode 100644 index 0000000..77f4bfd --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/cs.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Selhání!", + ["not_near_veh"] = "Nejste v blízkosti vozidla!", + ["out_range_veh"] = "Jste příliš daleko od vozidla!", + ["inside_veh"] = "Nemůžete opravit motor vozidla zevnitř!", + ["healthy_veh"] = "Vozidlo je příliš zdravé a potřebujete lepší nářadí!", + ["inside_veh_req"] = "Pro opravu vozidla musíte být uvnitř!", + ["roadside_avail"] = "Je k dispozici silniční asistence, kterou zavoláte prostřednictvím svého telefonu!", + ["no_permission"] = "Nemáte oprávnění opravovat vozidla", + ["fix_message"] = "%{message}, a nyní jeďte do servisu!", + ["veh_damaged"] = "Vaše vozidlo je příliš poškozené!", + ["nofix_message_1"] = "Podívali jste se na hladinu oleje a ta je v normě", + ["nofix_message_2"] = "Podívali jste se na své kolo a zdá se, že nic není v nepořádku", + ["nofix_message_3"] = "Podíval jste se na lepicí pásku na olejové hadici a zdála se je v pořádku", + ["nofix_message_4"] = "Zesílili jste rádio. Podivný zvuk motoru je nyní pryč", + ["nofix_message_5"] = "Odstraňovač rezivění, který jste použili, neměl žádný účinek", + ["nofix_message_6"] = "Nikdy se nepokoušejte opravit něco, co není rozbité, ale vy jste neposlechli", + }, + success = { + ["cleaned_veh"] = "Vozidlo vyčištěno!", + ["repaired_veh"] = "Vozidlo opraveno!", + ["fix_message_1"] = "Zkontroloval jste hladinu oleje", + ["fix_message_2"] = "Únik oleje jste uzavřeli žvýkačkou", + ["fix_message_3"] = "Uzavřel jste olejovou hadici pomocí pásky", + ["fix_message_4"] = "Dočasně jste zastavili únik oleje", + ["fix_message_5"] = "Kolo jste nakopli a opět funguje", + ["fix_message_6"] = "Odstranili jste rez", + ["fix_message_7"] = "Křičeli jste na auto a ono opět funguje", + }, + progress = { + ["clean_veh"] = "Čištění auta...", + ["repair_veh"] = "Oprava vozidla...", + + } +} + +if GetConvar('qb_locale', 'en') == 'cs' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/da.lua b/[qb]/qb-vehiclefailure/locales/da.lua new file mode 100644 index 0000000..c22ee17 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/da.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Fejlede!", + ["not_near_veh"] = "Du er ikke i nærheden af et køretøj!", + ["out_range_veh"] = "Du er for langt væk fra køretøjet!", + ["inside_veh"] = "Du kan ikke reparere et køretøj indefra!", + ["healthy_veh"] = "Køretøjet er ikke beskadigt nok, der skal et andet værktøj til!", + ["inside_veh_req"] = "Du skal sidde inde i køretøjet for at kunne reparere den!", + ["roadside_avail"] = "Der er vejhjælp at benytte sig af, giv dem et kald fra mobilen!", + ["no_permission"] = "Du har ingen agang til dette", + ["fix_message"] = "%{message}, og gå nu til din garage!", + ["veh_damaged"] = "Dit køretøj er for ødelagt!", + ["nofix_message_1"] = "Du kiggede på oliestanden, og det så normalt ud", + ["nofix_message_2"] = "Du kiggede på din cykel, og noget ser forkert ud", + ["nofix_message_3"] = "Du kiggede på gaffatapen på olieslangen, det så fint ud", + ["nofix_message_4"] = "Du skruede op for højtalerne. Den mærkelig motor lyd er nu væk", + ["nofix_message_5"] = "Rust fjerneren virkede vidst ikke her", + ["nofix_message_6"] = "Aldrig reparere noget der ikke er istykker, men du lyttede ikke", + }, + success = { + ["cleaned_veh"] = "Køretøjet er vasket!", + ["repaired_veh"] = "Køretøjet repareret!", + ["fix_message_1"] = "Du kiggede på oliestanden", + ["fix_message_2"] = "Du lukkede oliekappen med en klat tyggegummi", + ["fix_message_3"] = "Du reparerede olieslangen med gaffatape", + ["fix_message_4"] = "Du stoppede midlertidigt oliespilden", + ["fix_message_5"] = "Du sparkede til cyklen og den virker igen!", + ["fix_message_6"] = "Du fjernede rusten", + ["fix_message_7"] = "Du råbte af køretøjet, og nu virker den fint igen!", + }, + progress = { + ["clean_veh"] = "Vasker bilen...", + ["repair_veh"] = "Reparere køretøjet..", + + } +} + +if GetConvar('qb_locale', 'en') == 'da' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/de.lua b/[qb]/qb-vehiclefailure/locales/de.lua new file mode 100644 index 0000000..ef69e64 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/de.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Fehlgeschlagen!", + ["not_near_veh"] = "Du bist nicht in der Nähe eines Fahrzeugs!", + ["out_range_veh"] = "Du bist zu weit vom Fahrzeug!", + ["inside_veh"] = "Du kannst keinen Fahrzeug Motor von Innen Reparieren!", + ["healthy_veh"] = "Fahrzeug ist in guter Kondition und benötigt bessere Werkzeuge!", + ["inside_veh_req"] = "Du musst in einem Fahrzeug sein!", + ["roadside_avail"] = "Es ist ein Mechaniker verfügbar rufe ihn!", + ["no_permission"] = "Du hast keine Recht zum Reparieren!", + ["fix_message"] = "%{message}, und gehe jetzt zu deiner Garage!", + ["veh_damaged"] = "Dein Fahrzeug ist zu Kapputt!", + ["nofix_message_1"] = "Du hast den Ölstand geprüft, Er Sieht gut aus!", + ["nofix_message_2"] = "Du hast dein Bike gechecked, es sieht gut aus!", + ["nofix_message_3"] = "Du hast das klebeband auf deiner Öl Leitung geprüft alles ok!", + ["nofix_message_4"] = "Du hast das Radio lauter gemacht das komische motor geräusch ist nun weg!", + ["nofix_message_5"] = "Der Rost Entferner machte keinen Unterschied", + ["nofix_message_6"] = "Repariere nie was was nicht Kaputt ist!", + }, + success = { + ["cleaned_veh"] = "Fahrzeug geputzt!", + ["repaired_veh"] = "Fahrzeug repariert!", + ["fix_message_1"] = "Du hast den Öl-Stand geprüft.", + ["fix_message_2"] = "Du hast das Öl-loch gestopft mit Kaugummi", + ["fix_message_3"] = "Du hast eine Neue Öl Verbindung gebaut mit Klebeband", + ["fix_message_4"] = "Du hast das Öl Problem temporär repariert", + ["fix_message_5"] = "Du hast das Bike getreten es geht wieder!", + ["fix_message_6"] = "Du hast etwas Rost entfernt", + ["fix_message_7"] = "Du hast das Auto angeschrien, Es geht wieder!", + }, + progress = { + ["clean_veh"] = "Putze das Auto...", + ["repair_veh"] = "Repariere Fahrzeug..", + + } +} + +if GetConvar('qb_locale', 'en') == 'de' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/en.lua b/[qb]/qb-vehiclefailure/locales/en.lua new file mode 100644 index 0000000..f557711 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/en.lua @@ -0,0 +1,41 @@ +local Translations = { + error = { + ["failed_notification"] = "Failed!", + ["not_near_veh"] = "You are not near a vehicle!", + ["out_range_veh"] = "You are too far from the vehicle!", + ["inside_veh"] = "You cannot repair a vehicle engine from the inside!", + ["healthy_veh"] = "Vehicle is too healthy and needs better tools!", + ["inside_veh_req"] = "You must be in a vehicle to repair it!", + ["roadside_avail"] = "There is roadside assistance available call that via your phone!", + ["no_permission"] = "You don't have permission to repair vehicles", + ["fix_message"] = "%{message}, and now go to a garage!", + ["veh_damaged"] = "Your vehicle is too damaged!", + ["nofix_message_1"] = "You looked at your oil level, and this looked normal", + ["nofix_message_2"] = "You looked at your bike, and nothing seems wrong", + ["nofix_message_3"] = "You looked at the duck tape on your oil hose and seemed fine", + ["nofix_message_4"] = "You turned the radio up. The weird engine noise is now gone", + ["nofix_message_5"] = "The rust remover you used had no effect", + ["nofix_message_6"] = "Never try to make something that isn't broken, but you didn't listen", + }, + success = { + ["cleaned_veh"] = "Vehicle cleaned!", + ["repaired_veh"] = "Vehicle repaired!", + ["fix_message_1"] = "You checked the oil level", + ["fix_message_2"] = "You closed the oil spill with chewing gum", + ["fix_message_3"] = "You made the oil hose with tape", + ["fix_message_4"] = "You have temporarily stopped the oil leak", + ["fix_message_5"] = "You kicked the bike and it works again", + ["fix_message_6"] = "You removed some rust", + ["fix_message_7"] = "You yelled at your car, and it works again", + }, + progress = { + ["clean_veh"] = "Cleaning the car...", + ["repair_veh"] = "Repairing vehicle..", + + } +} + +Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true +}) diff --git a/[qb]/qb-vehiclefailure/locales/es.lua b/[qb]/qb-vehiclefailure/locales/es.lua new file mode 100644 index 0000000..8c3b3b1 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/es.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "¡Ha fallado!", + ["not_near_veh"] = "¡No estás cerca de un vehículo!", + ["out_range_veh"] = "¡Está demasiado lejos del vehículo!", + ["inside_veh"] = "¡No se puede reparar el motor de un vehículo desde el interior!", + ["healthy_veh"] = "¡El vehículo está bien y necesita mejores herramientas!", + ["inside_veh_req"] = "¡Debes estar en un vehículo para repararlo!", + ["roadside_avail"] = "¡Hay asistencia en carretera disponible para llamar a través de su teléfono!", + ["no_permission"] = "No tienes permiso para reparar vehículos.", + ["fix_message"] = "¡%{message}, ahora ve a un garaje!", + ["veh_damaged"] = "¡Tu vehículo está demasiado dañado!", + ["nofix_message_1"] = "Miraste el nivel de aceite y parecía normal", + ["nofix_message_2"] = "Miraste tu bicicleta y nada parece estar mal", + ["nofix_message_3"] = "Miraste la cinta adhesiva en la goma de aceite y parece estar bien", + ["nofix_message_4"] = "Subiste la radio. El extraño ruido del motor ahora se ha ido.", + ["nofix_message_5"] = "El desoxidante que usaste no tuvo efecto", + ["nofix_message_6"] = "Nunca intentes hacer algo que no esté roto, pero no escuchaste", + }, + success = { + ["cleaned_veh"] = "¡Vehículo limpiado!", + ["repaired_veh"] = "¡Vehículo reparado!", + ["fix_message_1"] = "Revisaste el nivel de aceite", + ["fix_message_2"] = "Tapaste el derrame de gasolina con un chicle", + ["fix_message_3"] = "Hiciste la manguera de aceite con cinta", + ["fix_message_4"] = "Ha detenido temporalmente la fuga de aceite.", + ["fix_message_5"] = "Pateaste la bicicleta y vuelve a funcionar", + ["fix_message_6"] = "Quitaste algo de óxido", + ["fix_message_7"] = "Le gritaste a tu auto, y vuelve a funcionar", + }, + progress = { + ["clean_veh"] = "Limpiando el coche...", + ["repair_veh"] = "Reparando vehiculo...", + + } +} + +if GetConvar('qb_locale', 'en') == 'es' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/fr.lua b/[qb]/qb-vehiclefailure/locales/fr.lua new file mode 100644 index 0000000..d7c490e --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/fr.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Echoué!", + ["not_near_veh"] = "Vous n'êtes pas près d'un véhicule!", + ["out_range_veh"] = "Vous êtes trop loin d'un véhicule!", + ["inside_veh"] = "Vous ne pouvez pas réparer le moteur depuis l'intérieur du véhicule!", + ["healthy_veh"] = "Votre véhicule est en trop bon état et nécessite de meilleurs outil pour être réparer!", + ["inside_veh_req"] = "Vous devez être dans un véhicule pour le réparer!", + ["roadside_avail"] = "De l'assistance routière est disponible appeler les via votre téléphone!", + ["no_permission"] = "Vous n'avez pas la permission de réparer les véhicules", + ["fix_message"] = "%{message}, Maintenant aller dans un garage!", + ["veh_damaged"] = "Votre véhicule est trop endomagé!", + ["nofix_message_1"] = "Nous avons regarder votre niveau d'huile, et ils ont l'air normal", + ["nofix_message_2"] = "Nous avons regarder votre moto, et rien n'a l'air cassé", + ["nofix_message_3"] = "Vous avez regardé le scotch sur le tuyau d'huile et il vous avez semblé en bon état", + ["nofix_message_4"] = "Vous avez monter le son de la radio. Le bruit bizzare du moteur à disparu", + ["nofix_message_5"] = "Le dissolvant de rouille que vous avez utilisé n'a eu aucun effet", + ["nofix_message_6"] = "n'essayez jamais de réparer quelque chose de cassé, mais vous n'avez pas écouté", + }, + success = { + ["cleaned_veh"] = "Vehicule nettoyé!", + ["repaired_veh"] = "Vehicule réparé!", + ["fix_message_1"] = "Vous avez vérifié le niveau d'huile", + ["fix_message_2"] = "Vous avez fermé la fuite d'huile avec du chewing gum", + ["fix_message_3"] = "Vous avez réparé le tuyau d'huile avec du scotch", + ["fix_message_4"] = "Vous avez temporairement arreter la fuite d'huile", + ["fix_message_5"] = "Vous avez mis un coup a votre moto et elle remarche !", + ["fix_message_6"] = "Vous avez retirer de la rouille", + ["fix_message_7"] = "Vous avez crié a votre voiture et elle remarche !", + }, + progress = { + ["clean_veh"] = "Vous nettoyez la voiture...", + ["repair_veh"] = "Vous réparez la voiture..", + + } +} + +if GetConvar('qb_locale', 'en') == 'fr' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/it.lua b/[qb]/qb-vehiclefailure/locales/it.lua new file mode 100644 index 0000000..dc1a8af --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/it.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Non riuscito!", + ["not_near_veh"] = "Non sei vicino ad un veicolo!", + ["out_range_veh"] = "Sei troppo lontano dal veicolo!", + ["inside_veh"] = "Non puoi riparare un motore di un veicolo dall'interno!", + ["healthy_veh"] = "Veicolo è troppo riparato e ha bisogno di strumenti migliori!", + ["inside_veh_req"] = "Devi essere in un veicolo per ripararlo!", + ["roadside_avail"] = "L'assistenza stradale è disponibile da chiamare tramite il telefono!", + ["no_permission"] = "Non hai il permesso di riparare i veicoli", + ["fix_message"] = "%{message}, e ora vai in un garage!", + ["veh_damaged"] = "Il tuo veicolo è troppo danneggiato!", + ["nofix_message_1"] = "Hai guardato il tuo livello di olio, e sembra normale", + ["nofix_message_2"] = "Hai guardato la tua moto e non c'è niente che non va", + ["nofix_message_3"] = "Hai guardato il nastro adesivo sul tubo dell'olio e sembra a posto", + ["nofix_message_4"] = "Hai acceso la radio. Lo strano rumore del motore è sparito", + ["nofix_message_5"] = "L'antiruggine che hai usato non ha avuto effetto", + ["nofix_message_6"] = "Non provare mai a sistemare qualcosa che non è rotto, ma non hai ascoltato", + }, + success = { + ["cleaned_veh"] = "Veicolo pulito!", + ["repaired_veh"] = "Veicolo riparato!", + ["fix_message_1"] = "Hai controllato il livello dell'olio", + ["fix_message_2"] = "Hai chiuso la fuoriuscita di olio con una chewing gum", + ["fix_message_3"] = "Hai sistemato il tubo dell'olio con il nastro", + ["fix_message_4"] = "Hai temporaneamente fermato la perdita di olio", + ["fix_message_5"] = "Hai dato un calcio alla moto e funziona di nuovo", + ["fix_message_6"] = "Hai rimosso un po' di ruggine", + ["fix_message_7"] = "Hai urlato alla macchina e funziona di nuovo", + }, + progress = { + ["clean_veh"] = "Lavando la macchina...", + ["repair_veh"] = "Riparando il veicolo..", + + } +} + +if GetConvar('qb_locale', 'en') == 'it' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/pt.lua b/[qb]/qb-vehiclefailure/locales/pt.lua new file mode 100644 index 0000000..cd091c0 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/pt.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Falhou!", + ["not_near_veh"] = "Não estás perto de um veículo!", + ["out_range_veh"] = "Estás demasiado longe do veículo!", + ["inside_veh"] = "Não podes reparar o motor dentro do veículo!", + ["healthy_veh"] = "O veículo está demasiado bem e por isso precisas de ferramentas melhores!", + ["inside_veh_req"] = "Tens de estar num veículo para o reparar!", + ["roadside_avail"] = "Existem serviços de apoio disponíveis! Consulta os serviços no teu telemóvel.", + ["no_permission"] = "Não tens permissão para reparar veículos", + ["fix_message"] = "%{message}. Visita uma oficina agora!", + ["veh_damaged"] = "Este veículo está demasiado danificado!", + ["nofix_message_1"] = "Olhaste para o nível de óleo e nada parece errado", + ["nofix_message_2"] = "Olhaste para o teu motociclo e nada parece errado", + ["nofix_message_3"] = "Olhaste para a fita adesiva na mangueira do óleo e a mesma parece estar bem", + ["nofix_message_4"] = "Ligaste o rádio. O barulho estranho no motor desapareceu", + ["nofix_message_5"] = "O removedor de ferrugem que usaste não teve qualquer efeito", + ["nofix_message_6"] = "Tentaste resolver o problema mas ficou tudo na mesma", + }, + success = { + ["cleaned_veh"] = "Veículo limpo!", + ["repaired_veh"] = "Veículo reparado!", + ["fix_message_1"] = "Verificaste o nível do óleo", + ["fix_message_2"] = "Reparaste o derrame de óleo com pastilha elástica", + ["fix_message_3"] = "Fizeste a mangueira do óleo com fita adesiva", + ["fix_message_4"] = "Paraste a fuga de óleo temporariamente", + ["fix_message_5"] = "Deste um pontapé no motociclo e voltou a funcionar", + ["fix_message_6"] = "Removeste alguma ferrugem", + ["fix_message_7"] = "Gritaste com o carro e ele voltou a funcionar", + }, + progress = { + ["clean_veh"] = "A limpar veículo...", + ["repair_veh"] = "A reparar veículo..", + + } +} + +if GetConvar('qb_locale', 'en') == 'pt' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/locales/sv.lua b/[qb]/qb-vehiclefailure/locales/sv.lua new file mode 100644 index 0000000..3799db8 --- /dev/null +++ b/[qb]/qb-vehiclefailure/locales/sv.lua @@ -0,0 +1,44 @@ +local Translations = { + error = { + ["failed_notification"] = "Misslyckades!", + ["not_near_veh"] = "Du är inte nära ett fordon!", + ["out_range_veh"] = "Du är för långt från fordonet!", + ["inside_veh"] = "Du kan inte reparera en fordonsmotor från insidan!", + ["healthy_veh"] = "Fordonet är för friskt och behöver bättre verktyg!", + ["inside_veh_req"] = "Du måste vara i ett fordon för att reparera det!", + ["roadside_avail"] = "Det finns vägassistans tillgängligt ring det via din telefon!", + ["no_permission"] = "Du har inte tillstånd att reparera fordon!", + ["fix_message"] = "%{message}, och nu gå till ett garage!", + ["veh_damaged"] = "Ditt fordon är för skadat!", + ["nofix_message_1"] = "Du tittade på din oljenivå och det här såg normalt ut", + ["nofix_message_2"] = "Du tittade på din cykel och inget verkar fel", + ["nofix_message_3"] = "Du tittade på ankbandet på din oljeslang och verkade bra", + ["nofix_message_4"] = "Du satte upp radion. Det konstiga motorljudet är nu borta", + ["nofix_message_5"] = "Rostborttagningsmedlet du använde hade ingen effekt", + ["nofix_message_6"] = "Försök aldrig göra något som inte är trasigt, men du lyssnade inte", + }, + success = { + ["cleaned_veh"] = "Fordonet städat!", + ["repaired_veh"] = "Fordonet reparerat!", + ["fix_message_1"] = "Du kollade oljenivån", + ["fix_message_2"] = "Du stängde oljespillet med tuggummi", + ["fix_message_3"] = "Du gjorde oljeslangen med tejp", + ["fix_message_4"] = "Du har tillfälligt stoppat oljeläckan", + ["fix_message_5"] = "Du sparkade på cykeln och den fungerar igen", + ["fix_message_6"] = "Du tog bort lite rost", + ["fix_message_7"] = "Du skrek på din bil och den fungerar igen", + }, + progress = { + ["clean_veh"] = "Rengör bilen...", + ["repair_veh"] = "Reparerar fordon..", + + } +} + +if GetConvar('qb_locale', 'en') == 'sv' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclefailure/server.lua b/[qb]/qb-vehiclefailure/server.lua new file mode 100644 index 0000000..a3460e9 --- /dev/null +++ b/[qb]/qb-vehiclefailure/server.lua @@ -0,0 +1,39 @@ +local QBCore = exports['qb-core']:GetCoreObject() +QBCore.Commands.Add("fix", "Repair your vehicle (Admin Only)", {}, false, function(source) + TriggerClientEvent('iens:repaira', source) + TriggerClientEvent('vehiclemod:client:fixEverything', source) +end, "admin") + +QBCore.Functions.CreateUseableItem("repairkit", function(source, item) + local Player = QBCore.Functions.GetPlayer(source) + if Player.Functions.GetItemBySlot(item.slot) ~= nil then + TriggerClientEvent("qb-vehiclefailure:client:RepairVehicle", source) + end +end) + +QBCore.Functions.CreateUseableItem("cleaningkit", function(source, item) + local Player = QBCore.Functions.GetPlayer(source) + if Player.Functions.GetItemBySlot(item.slot) ~= nil then + TriggerClientEvent("qb-vehiclefailure:client:CleanVehicle", source) + end +end) + +QBCore.Functions.CreateUseableItem("advancedrepairkit", function(source, item) + local Player = QBCore.Functions.GetPlayer(source) + if Player.Functions.GetItemBySlot(item.slot) ~= nil then + TriggerClientEvent("qb-vehiclefailure:client:RepairVehicleFull", source) + end +end) + +RegisterNetEvent('qb-vehiclefailure:removeItem', function(item) + local src = source + local ply = QBCore.Functions.GetPlayer(src) + ply.Functions.RemoveItem(item, 1) +end) + +RegisterNetEvent('qb-vehiclefailure:server:removewashingkit', function(veh) + local src = source + local ply = QBCore.Functions.GetPlayer(src) + ply.Functions.RemoveItem("cleaningkit", 1) + TriggerClientEvent('qb-vehiclefailure:client:SyncWash', -1, veh) +end) \ No newline at end of file diff --git a/[qb]/qb-vehiclekeys/.github/ISSUE_TEMPLATE/bug_report.md b/[qb]/qb-vehiclekeys/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..62f702f --- /dev/null +++ b/[qb]/qb-vehiclekeys/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve or fix something +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. A stranger to qbcore should be able to read your bug report and understand how to reproduce it themselves and understand how the feature should work normally. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Use this item '....' (item's name from shared.lua if applicable) +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Questions (please complete the following information):** + - When you last updated: [e.g. last week] + - Are you using custom resource? which ones? [e.g. zdiscord, qb-target] + - Have you renamed `qb-` to something custom? [e.g. yes/no] + +**Additional context** +Add any other context about the problem here. diff --git a/[qb]/qb-vehiclekeys/.github/ISSUE_TEMPLATE/feature-request.md b/[qb]/qb-vehiclekeys/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..9e9bf3e --- /dev/null +++ b/[qb]/qb-vehiclekeys/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest an idea for QBCore +title: "[SUGGESTION]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. and with as much detail as possible how it would function in your opinion. Please try to keep it unique. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered for people to have in mind just in case the main idea isn't liked but a derivative is. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/[qb]/qb-vehiclekeys/.github/auto_assign.yml b/[qb]/qb-vehiclekeys/.github/auto_assign.yml new file mode 100644 index 0000000..2a80921 --- /dev/null +++ b/[qb]/qb-vehiclekeys/.github/auto_assign.yml @@ -0,0 +1,17 @@ +# Set to true to add reviewers to pull requests +addReviewers: true + +# Set to true to add assignees to pull requests +addAssignees: author + +# A list of reviewers to be added to pull requests (GitHub user name) +reviewers: + - /maintenance + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +skipKeywords: + - wip + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 0 \ No newline at end of file diff --git a/[qb]/qb-vehiclekeys/.github/contributing.md b/[qb]/qb-vehiclekeys/.github/contributing.md new file mode 100644 index 0000000..21fb806 --- /dev/null +++ b/[qb]/qb-vehiclekeys/.github/contributing.md @@ -0,0 +1,201 @@ +# Contributing to QBCore + +First of all, thank you for taking the time to contribute! + +These guidelines will help you help us in the best way possible regardless of your skill level. We ask that you try to read everything related to the way you'd like to contribute and try and use your best judgement for anything not covered. + +### Table of Contents + +[Code of Conduct](#code-of-conduct) + +[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Suggesting Features / Enhancements](#suggesting-features--enhancements) + * [Your First Code Contribution](#your-first-code-contribution) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Git Commit Messages](#git-commit-messages) + * [Lua Styleguide](#lua-styleguide) + * [JavaScript Styleguide](#javascript-styleguide) + + + +## Code of Conduct + +- Refrain from using languages other than English. +- Refrain from discussing any politically charged or inflammatory topics. +- Uphold mature conversations and respect each other; excessive profanity, hate speech or any kind of harassment will not be tolerated. +- No advertising of any kind. +- Follow these guidelines. +- Do not mention members of github unless a question is directed at them and can't be answered by anyone else. +- Do not mention any of the development team for any reason. We will read things as we get to them. + +## I don't want to read this whole thing I just have a question!!! + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +* [QBCore Website](https://qbcore.org) +* [QBCore Discord](https://discord.gg/qbcore) +* [FiveM Discord - #qbcore channel](https://discord.gg/fivem) + + + + + + + + + + +## How Can I Contribute? + +### Reporting Bugs + +The easiest way to contribute for most people is just to report bugs you find cause if nobody reports it there's a chance we'll never know it exists and then we'll never fix it. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out the bug-report template with the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Check the docs** There's a chance what you see as a bug might just work differently than you expect and if you think it could work better consider a feature enhancement report instead. +* **Search the [discord](https://discord.gg/qbcore)** to see if anyone else has run into the issue and see if it was solved through user error or code changes. (if the code change isn't pending a PR and you know what you're doing consider submitting one following [Pull Requests](#pull-requests) ) +* **Determine which resource the problem should be reported in**. If the bug is related to the inventory for example report this bug under qb-inventory rather than under qb-core or some other resource. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your bug is related to, create an issue on that repository and provide the following information by filling in bug-report template. + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. If something happened with only a specific group or single item but not others, specify that. +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **Include screenshots** which show the specific bug in action or before and after. +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions if possible: + +* **Did the problem start happening recently** (e.g. after updating to a new version of QBCore?) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of QBCore?** What's the most recent commit in which the problem doesn't happen? +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your setup: + +* **When was your QBCore last updated?** +* **What OS is the server running on**? +* **Which *extra* resources do you have installed?** + + +--- + + +### Suggesting Features / Enhancements + +This section guides you through submitting an enhancement suggestion for QBCore, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion. + +Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in feature request template, including the steps that you imagine you would take if the feature you're requesting existed. + +#### Before Submitting An Enhancement Suggestion + +* **Make sure it doesn't already exist.** Sounds silly, but there's a lot of features built in to qbcore that people don't realize so take a look through the docs and stuff to make sure it's not already there. +* **Check if there's already PR which provides that enhancement.** +* **Determine which resource the enhancement should be suggested in.** if it fits with another resource suggest it in that resource. if it would be it's own resource suggest it in the main qb-core repository. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your enhancement suggestion is related to, create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of QBCore which the suggestion is related to. +* **Explain why this enhancement would be useful.** +* **Be creative and unique.** Stealing ideas from popular servers 1:1 detail isn't going to get accepted. + + +--- + + + +### Your First Code Contribution + +Unsure where to begin contributing to QBCore? You can start by looking through these `beginner` and `help-wanted` issues. + + + +--- + + +### Pull Requests + +The process described here has several goals: + +- Maintain QBCore's quality. +- Fix problems that are important to users. +- Engage the community in working toward the best possible QBCore. +- Enable a sustainable system for QBCore's maintainers to review contributions. + +Please follow these steps to have your contribution considered by the maintainers: + +1. Follow all instructions in The Pull Request template. +2. Follow the [styleguides](#styleguides). +3. Await review by the reviewer(s). + +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + + +--- + +## Styleguides + +### Git Commit Messages + +* Limit the first line to 72 characters or less. +* Reference issues and pull requests liberally after the first line. +* Consider starting the commit message with an applicable emoji: + * :art: `:art:` when improving the format/structure of the code + * :racehorse: `:racehorse:` when improving performance + * :memo: `:memo:` when writing docs + * :bug: `:bug:` when fixing a bug + * :fire: `:fire:` when removing code or files + * :white_check_mark: `:white_check_mark:` when adding tests + * :lock: `:lock:` when dealing with security + * :arrow_up: `:arrow_up:` when upgrading dependencies + * :arrow_down: `:arrow_down:` when downgrading dependencies + * :shirt: `:shirt:` when removing linter warnings + +### Lua Styleguide + +All lua code should be done using all the best practices of proper lua using the easiest to read yet fastest/most optimized methods of execution. + +- Use 4 Space indentation +- Aim for lua 5.4 (include `lua54 'yes'` in the fxmanifest.lua) +- Use `PlayerPedId()` instead of `GetPlayerPed(-1)` +- Use `#(vector3 - vector3)` instead of `GetDistanceBetweenCoords()` +- Don't create unnecessary threads. always try to find a better method of triggering events +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables +- For distance checking loops set longer waits if you're outside of a range +- Job specific loops should only run for players with that job, don't waste cycles +- When possible don't trust the client, esspecially with transactions +- Balance security and optimizations +- [Consider this Lua Performance guide](https://springrts.com/wiki/Lua_Performance) +- Use local varriables everywhere possible +- Make use of config options where it makes sense making features optional or customizable +- Instead of `table.insert(myTable, "Value")` use `myTable[#myTable + 1] = "Value"` +- Instead of `table.insert(ages, "bob", 30)` use `ages["bob"] = 30` + + +### JavaScript Styleguide + +- Use 4 Space indentation +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables. diff --git a/[qb]/qb-vehiclekeys/.github/pull_request_template.md b/[qb]/qb-vehiclekeys/.github/pull_request_template.md new file mode 100644 index 0000000..000f0f9 --- /dev/null +++ b/[qb]/qb-vehiclekeys/.github/pull_request_template.md @@ -0,0 +1,10 @@ +**Describe Pull request** +First, make sure you've read and are following the contribution guidelines and style guide and your code reflects that. +Write up a clear and concise description of what your pull request adds or fixes and if it's an added feature explain why you think it should be included in the core. + +If your PR is to fix an issue mention that issue here + +**Questions (please complete the following information):** +- Have you personally loaded this code into an updated qbcore project and checked all it's functionality? [yes/no] (Be honest) +- Does your code fit the style guidelines? [yes/no] +- Does your PR fit the contribution guidelines? [yes/no] diff --git a/[qb]/qb-vehiclekeys/.github/workflows/lint.yml b/[qb]/qb-vehiclekeys/.github/workflows/lint.yml new file mode 100644 index 0000000..fb74fd6 --- /dev/null +++ b/[qb]/qb-vehiclekeys/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint +on: [push, pull_request_target] +jobs: + lint: + name: Lint Resource + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Lint + uses: iLLeniumStudios/fivem-lua-lint-action@v2 + with: + capture: "junit.xml" + args: "-t --formatter JUnit" + extra_libs: mysql+polyzone+qblocales + - name: Generate Lint Report + if: always() + uses: mikepenz/action-junit-report@v3 + with: + report_paths: "**/junit.xml" + check_name: Linting Report + fail_on_failure: false \ No newline at end of file diff --git a/[qb]/qb-vehiclekeys/LICENSE b/[qb]/qb-vehiclekeys/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/[qb]/qb-vehiclekeys/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[qb]/qb-vehiclekeys/NUI/images/qbcorekey.png b/[qb]/qb-vehiclekeys/NUI/images/qbcorekey.png new file mode 100644 index 0000000..692d96c Binary files /dev/null and b/[qb]/qb-vehiclekeys/NUI/images/qbcorekey.png differ diff --git a/[qb]/qb-vehiclekeys/NUI/index.html b/[qb]/qb-vehiclekeys/NUI/index.html new file mode 100644 index 0000000..ff87d22 --- /dev/null +++ b/[qb]/qb-vehiclekeys/NUI/index.html @@ -0,0 +1,52 @@ + + + + + + + + + qb-vehiclekeys + + + + + + +
+
+ + + + +
+ +
+ + + + + + \ No newline at end of file diff --git a/[qb]/qb-vehiclekeys/NUI/script.js b/[qb]/qb-vehiclekeys/NUI/script.js new file mode 100644 index 0000000..60620e1 --- /dev/null +++ b/[qb]/qb-vehiclekeys/NUI/script.js @@ -0,0 +1,60 @@ + +window.onload = function () { + var eventCallback = { + setText: function(data) { + var key = document.querySelector('#'+data.id+' span'); + var html = data.value; + saferInnerHTML(key, html); + }, + }; +} +$(function() +{ + $('.container').hide(); + window.addEventListener('message', function(event) + { + var cdata = event.data; + if (cdata.casemenue == 'open') + { + $('.container').show(); + } + }, false); + + document.onkeyup = function (data) { + if (data.which == 27) { // Escape key + $.post('https://qb-vehiclekeys/closui', JSON.stringify({ message: null })); + $('.container').hide(); + } + }; + +}); + +function unlock() +{ + $.post('https://qb-vehiclekeys/unlock', JSON.stringify({ + + })) + $('.container').hide(); +} +function lock() +{ + $.post('https://qb-vehiclekeys/lock', JSON.stringify({ + + })) + $('.container').hide(); +} +function trunk() +{ + $.post('https://qb-vehiclekeys/trunk', JSON.stringify({ + + })) + $('.container').hide(); +} +function engine() +{ + $.post('https://qb-vehiclekeys/engine', JSON.stringify({ + + })) + $('.container').hide(); +} + diff --git a/[qb]/qb-vehiclekeys/NUI/style.css b/[qb]/qb-vehiclekeys/NUI/style.css new file mode 100644 index 0000000..34ef35b --- /dev/null +++ b/[qb]/qb-vehiclekeys/NUI/style.css @@ -0,0 +1,78 @@ +.container{ + position:fixed; + top: 65%; + left: 85%; + width: 200px; + height: 300px; + border-radius: 50%; + +} +.container img{ + position: absolute; + width: 200px; + height: 300px; +} +.button{ + position:relative; + top:50px; +} +div { + margin-bottom: 20px; +} +.btn4{ + width: 50px; + height: 50px; + position: absolute; + top: 75px; + left: 104px; + z-index: 1; + border-radius:5px; + font-size: 25px; + background-color: rgba(248, 0, 0, 0); + border: 0; + cursor: pointer; +} +.btn5{ + width: 50px; + height: 50px; + position: absolute; + top: 75px; + left: 45px; + z-index: 1; + border-radius:5px; + font-size: 25px; + background-color: rgba(248, 0, 0, 0); + border: 0; + cursor: pointer; +} +.btn7{ + width: 50px; + height: 50px; + position: absolute; + top: 130px; + left: 47px; + z-index: 1; + border-radius:5px; + font-size: 25px; + background-color: rgba(248, 0, 0, 0); + border: 0; + cursor: pointer; +} +.btn9{ + width: 50px; + height: 50px; + position: absolute; + top: 130px; + left: 105px; + z-index: 1; + border-radius:5px; + border: 0; + font-size: 25px; + background-color: rgba(248, 0, 0, 0); + cursor: pointer; +} +img { + display: block; + width: 400px; + margin: auto; +} diff --git a/[qb]/qb-vehiclekeys/README.md b/[qb]/qb-vehiclekeys/README.md new file mode 100644 index 0000000..6def448 --- /dev/null +++ b/[qb]/qb-vehiclekeys/README.md @@ -0,0 +1,24 @@ +# qb-vehiclekeys +Vehicle Keys System For QB-Core + +# [video] +[![Watch the video](https://media.discordapp.net/attachments/964009269408186448/1053708299553484850/image.png)](https://www.youtube.com/watch?v=7E9TXR3lXPI) + + +# License + + QBCore Framework + Copyright (C) 2021 Joshua Eger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see diff --git a/[qb]/qb-vehiclekeys/client/main.lua b/[qb]/qb-vehiclekeys/client/main.lua new file mode 100644 index 0000000..e18ae98 --- /dev/null +++ b/[qb]/qb-vehiclekeys/client/main.lua @@ -0,0 +1,762 @@ +----------------------- +---- Variables ---- +----------------------- +local QBCore = exports['qb-core']:GetCoreObject() +local KeysList = {} +local isTakingKeys = false +local isCarjacking = false +local canCarjack = true +local AlertSend = false +local lastPickedVehicle = nil +local usingAdvanced = false +local IsHotwiring = false +local trunkclose = true +local looped = false +local function robKeyLoop() + if looped == false then + looped = true + while true do + local sleep = 1000 + if LocalPlayer.state.isLoggedIn then + sleep = 100 + + local ped = PlayerPedId() + local entering = GetVehiclePedIsTryingToEnter(ped) + local carIsImmune = false + if entering ~= 0 and not isBlacklistedVehicle(entering) then + sleep = 2000 + local plate = QBCore.Functions.GetPlate(entering) + + local driver = GetPedInVehicleSeat(entering, -1) + for _, veh in ipairs(Config.ImmuneVehicles) do + if GetEntityModel(entering) == joaat(veh) then + carIsImmune = true + end + end + -- Driven vehicle logic + if driver ~= 0 and not IsPedAPlayer(driver) and not HasKeys(plate) and not carIsImmune then + if IsEntityDead(driver) then + if not isTakingKeys then + isTakingKeys = true + + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 1) + QBCore.Functions.Progressbar("steal_keys", Lang:t("progress.takekeys"), 2500, false, false, { + disableMovement = false, + disableCarMovement = true, + disableMouse = false, + disableCombat = true + }, {}, {}, {}, function() -- Done + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) + isTakingKeys = false + end, function() + isTakingKeys = false + end) + end + elseif Config.LockNPCDrivingCars then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 2) + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 1) + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) + + --Make passengers flee + local pedsInVehicle = GetPedsInVehicle(entering) + for _, pedInVehicle in pairs(pedsInVehicle) do + if pedInVehicle ~= GetPedInVehicleSeat(entering, -1) then + MakePedFlee(pedInVehicle) + end + end + end + -- Parked car logic + elseif driver == 0 and entering ~= lastPickedVehicle and not HasKeys(plate) and not isTakingKeys then + QBCore.Functions.TriggerCallback('qb-vehiclekeys:server:checkPlayerOwned', function(playerOwned) + if not playerOwned then + if Config.LockNPCParkedCars then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 2) + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(entering), 1) + end + end + end, plate) + + end + end + + -- Hotwiring while in vehicle, also keeps engine off for vehicles you don't own keys to + if IsPedInAnyVehicle(ped, false) and not IsHotwiring then + sleep = 1000 + local vehicle = GetVehiclePedIsIn(ped) + local plate = QBCore.Functions.GetPlate(vehicle) + + if GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() and not HasKeys(plate) and not isBlacklistedVehicle(vehicle) and not AreKeysJobShared(vehicle) then + sleep = 0 + + local vehiclePos = GetOffsetFromEntityInWorldCoords(vehicle, 0.0, 1.0, 0.5) + DrawText3D(vehiclePos.x, vehiclePos.y, vehiclePos.z, Lang:t("info.skeys")) + SetVehicleEngineOn(vehicle, false, false, true) + + if IsControlJustPressed(0, 74) then + Hotwire(vehicle, plate) + end + end + end + + if Config.CarJackEnable and canCarjack then + local playerid = PlayerId() + local aiming, target = GetEntityPlayerIsFreeAimingAt(playerid) + if aiming and (target ~= nil and target ~= 0) then + if DoesEntityExist(target) and IsPedInAnyVehicle(target, false) and not IsEntityDead(target) and not IsPedAPlayer(target) then + local targetveh = GetVehiclePedIsIn(target) + for _, veh in ipairs(Config.ImmuneVehicles) do + if GetEntityModel(targetveh) == joaat(veh) then + carIsImmune = true + end + end + if GetPedInVehicleSeat(targetveh, -1) == target and not IsBlacklistedWeapon() then + local pos = GetEntityCoords(ped, true) + local targetpos = GetEntityCoords(target, true) + if #(pos - targetpos) < 5.0 and not carIsImmune then + CarjackVehicle(target) + end + end + end + end + end + if entering == 0 and not IsPedInAnyVehicle(ped, false) and GetSelectedPedWeapon(ped) == `WEAPON_UNARMED` then + looped = false + break + end + end + Wait(sleep) + end + end +end + +function isBlacklistedVehicle(vehicle) + local isBlacklisted = false + for _,v in ipairs(Config.NoLockVehicles) do + if GetHashKey(v) == GetEntityModel(vehicle) then + isBlacklisted = true + break; + end + end + if Entity(vehicle).state.ignoreLocks or GetVehicleClass(vehicle) == 13 then isBlacklisted = true end + return isBlacklisted +end + +function addNoLockVehicles(model) + Config.NoLockVehicles[#Config.NoLockVehicles+1] = model +end +exports('addNoLockVehicles', addNoLockVehicles) + +function removeNoLockVehicles(model) + for k,v in pairs(Config.NoLockVehicles) do + if v == model then + Config.NoLockVehicles[k] = nil + end + end +end +exports('removeNoLockVehicles', removeNoLockVehicles) + +function isBlacklistedVehicle(vehicle) + local isBlacklisted = false + for _,v in ipairs(Config.NoLockVehicles) do + if GetHashKey(v) == GetEntityModel(vehicle) then + isBlacklisted = true + break; + end + end + if Entity(vehicle).state.ignoreLocks or GetVehicleClass(vehicle) == 13 then isBlacklisted = true end + return isBlacklisted +end + +function addNoLockVehicles(model) + Config.NoLockVehicles[#Config.NoLockVehicles+1] = model +end +exports('addNoLockVehicles', addNoLockVehicles) + +function removeNoLockVehicles(model) + for k,v in pairs(Config.NoLockVehicles) do + if v == model then + Config.NoLockVehicles[k] = nil + end + end +end +exports('removeNoLockVehicles', removeNoLockVehicles) + +----------------------- +---- Client Events ---- +----------------------- +RegisterKeyMapping('togglelocks', Lang:t("info.tlock"), 'keyboard', 'L') +RegisterCommand('togglelocks', function() + local ped = PlayerPedId() + if IsPedInAnyVehicle(ped, false) then + ToggleVehicleLockswithoutnui(GetVehicle()) + else + if Config.UseKeyfob then + openmenu() + else + ToggleVehicleLockswithoutnui(GetVehicle()) + end + end +end) +RegisterKeyMapping('engine', Lang:t("info.engine"), 'keyboard', 'G') +RegisterCommand('engine', function() + local vehicle = GetVehicle() + if vehicle and IsPedInVehicle(PlayerPedId(), vehicle) then + ToggleEngine(vehicle) + end +end) + +AddEventHandler('onResourceStart', function(resourceName) + if resourceName == GetCurrentResourceName() and QBCore.Functions.GetPlayerData() ~= {} then + GetKeys() + end +end) +-- Handles state right when the player selects their character and location. +RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() + GetKeys() +end) +-- Resets state on logout, in case of character change. +RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() + KeysList = {} +end) +RegisterNetEvent('qb-vehiclekeys:client:AddKeys', function(plate) + KeysList[plate] = true + local ped = PlayerPedId() + if IsPedInAnyVehicle(ped, false) then + local vehicle = GetVehiclePedIsIn(ped) + local vehicleplate = QBCore.Functions.GetPlate(vehicle) + if plate == vehicleplate then + SetVehicleEngineOn(vehicle, false, false, false) + end + end +end) +RegisterNetEvent('qb-vehiclekeys:client:RemoveKeys', function(plate) + KeysList[plate] = nil +end) +RegisterNetEvent('qb-vehiclekeys:client:ToggleEngine', function() + local EngineOn = GetIsVehicleEngineRunning(GetVehiclePedIsIn(PlayerPedId())) + local vehicle = GetVehiclePedIsIn(PlayerPedId(), true) + if HasKeys(QBCore.Functions.GetPlate(vehicle)) then + if EngineOn then + SetVehicleEngineOn(vehicle, false, false, true) + else + SetVehicleEngineOn(vehicle, true, false, true) + end + end +end) +RegisterNetEvent('qb-vehiclekeys:client:GiveKeys', function(id) + local targetVehicle = GetVehicle() + if targetVehicle then + local targetPlate = QBCore.Functions.GetPlate(targetVehicle) + if HasKeys(targetPlate) then + if id and type(id) == "number" then -- Give keys to specific ID + GiveKeys(id, targetPlate) + else + if IsPedSittingInVehicle(PlayerPedId(), targetVehicle) then -- Give keys to everyone in vehicle + local otherOccupants = GetOtherPlayersInVehicle(targetVehicle) + for p=1,#otherOccupants do + TriggerServerEvent('qb-vehiclekeys:server:GiveVehicleKeys', GetPlayerServerId(NetworkGetPlayerIndexFromPed(otherOccupants[p])), targetPlate) + end + else -- Give keys to closest player + GiveKeys(GetPlayerServerId(QBCore.Functions.GetClosestPlayer()), targetPlate) + end + end + else + QBCore.Functions.Notify(Lang:t("notify.ydhk"), 'error') + end + end +end) + +RegisterNetEvent('QBCore:Client:EnteringVehicle', function() + robKeyLoop() +end) + +RegisterNetEvent('weapons:client:DrawWeapon', function() + Wait(2000) + robKeyLoop() +end) + + +RegisterNetEvent('lockpicks:UseLockpick', function(isAdvanced) + LockpickDoor(isAdvanced) +end) +-- Backwards Compatibility ONLY -- Remove at some point -- +RegisterNetEvent('vehiclekeys:client:SetOwner', function(plate) + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) +end) +-- Backwards Compatibility ONLY -- Remove at some point -- +----------------------- +---- Functions ---- +----------------------- +function openmenu() + TriggerServerEvent("InteractSound_SV:PlayWithinDistance", 0.5, "key", 0.3) + SendNUIMessage({ casemenue = 'open' }) + SetNuiFocus(true, true) +end +function isBlacklistedVehicle(vehicle) + local isBlacklisted = false + for _,v in ipairs(Config.NoLockVehicles) do + if GetHashKey(v) == GetEntityModel(vehicle) then + isBlacklisted = true + break; + end + end + if Entity(vehicle).state.ignoreLocks or GetVehicleClass(vehicle) == 13 then isBlacklisted = true end + return isBlacklisted +end +function ToggleEngine(veh) + if veh then + local EngineOn = GetIsVehicleEngineRunning(veh) + if not isBlacklistedVehicle(veh) then + if HasKeys(QBCore.Functions.GetPlate(veh)) or AreKeysJobShared(veh) then + if EngineOn then + SetVehicleEngineOn(veh, false, false, true) + else + SetVehicleEngineOn(veh, true, true, true) + end + end + end + end +end +function ToggleVehicleLockswithoutnui(veh) + if veh then + if not isBlacklistedVehicle(veh) then + if HasKeys(QBCore.Functions.GetPlate(veh)) or AreKeysJobShared(veh) then + local ped = PlayerPedId() + local vehLockStatus = GetVehicleDoorLockStatus(veh) + + loadAnimDict("anim@mp_player_intmenu@key_fob@") + TaskPlayAnim(ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) + + TriggerServerEvent("InteractSound_SV:PlayWithinDistance", 5, "lock", 0.3) + + NetworkRequestControlOfEntity(veh) + if vehLockStatus == 1 then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 2) + QBCore.Functions.Notify(Lang:t("notify.vlock"), "primary") + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + QBCore.Functions.Notify(Lang:t("notify.vunlock"), "success") + end + + SetVehicleLights(veh, 2) + Wait(250) + SetVehicleLights(veh, 1) + Wait(200) + SetVehicleLights(veh, 0) + Wait(300) + ClearPedTasks(ped) + else + QBCore.Functions.Notify(Lang:t("notify.ydhk"), 'error') + end + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + end + end +end +function GiveKeys(id, plate) + local distance = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(GetPlayerPed(GetPlayerFromServerId(id)))) + if distance < 1.5 and distance > 0.0 then + TriggerServerEvent('qb-vehiclekeys:server:GiveVehicleKeys', id, plate) + else + QBCore.Functions.Notify(Lang:t("notify.nonear"),'error') + end +end +function GetKeys() + QBCore.Functions.TriggerCallback('qb-vehiclekeys:server:GetVehicleKeys', function(keysList) + KeysList = keysList + end) +end +function HasKeys(plate) + return KeysList[plate] +end +exports('HasKeys', HasKeys) +function loadAnimDict(dict) + while (not HasAnimDictLoaded(dict)) do + RequestAnimDict(dict) + Wait(0) + end +end +-- If in vehicle returns that, otherwise tries 3 different raycasts to get the vehicle they are facing. +-- Raycasts picture: https://i.imgur.com/FRED0kV.png + +function GetVehicle() + local ped = PlayerPedId() + local pos = GetEntityCoords(ped) + local vehicle = GetVehiclePedIsIn(PlayerPedId()) + + while vehicle == 0 do + vehicle = QBCore.Functions.GetClosestVehicle() + if #(pos - GetEntityCoords(vehicle)) > 8 then + QBCore.Functions.Notify(Lang:t("notify.vehclose"), "error") + return + end + end + + if not IsEntityAVehicle(vehicle) then vehicle = nil end + return vehicle +end +function AreKeysJobShared(veh) + local vehName = GetDisplayNameFromVehicleModel(GetEntityModel(veh)) + local vehPlate = QBCore.Functions.GetPlate(veh) + local jobName = QBCore.Functions.GetPlayerData().job.name + local onDuty = QBCore.Functions.GetPlayerData().job.onduty + for job, v in pairs(Config.SharedKeys) do + if job == jobName then + if Config.SharedKeys[job].requireOnduty and not onDuty then return false end + for _, vehicle in pairs(v.vehicles) do + if string.upper(vehicle) == string.upper(vehName) then + if not HasKeys(vehPlate) then + TriggerServerEvent("qb-vehiclekeys:server:AcquireVehicleKeys", vehPlate) + end + return true + end + end + end + end + return false +end +function ToggleVehicleLocks(veh) + if veh then + if not isBlacklistedVehicle(veh) then + if HasKeys(QBCore.Functions.GetPlate(veh)) or AreKeysJobShared(veh) then + local ped = PlayerPedId() + local vehLockStatus = GetVehicleDoorLockStatus(veh) + loadAnimDict("anim@mp_player_intmenu@key_fob@") + TaskPlayAnim(ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) + TriggerServerEvent("InteractSound_SV:PlayWithinDistance", 5, "lock", 0.3) + NetworkRequestControlOfEntity(veh) + while NetworkGetEntityOwner(veh) ~= 128 do + NetworkRequestControlOfEntity(veh) + Wait(0) + end + if vehLockStatus == 1 then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 2) + QBCore.Functions.Notify(Lang:t("notify.vlock"), "primary") + end + SetVehicleLights(veh, 2) + Wait(250) + SetVehicleLights(veh, 1) + Wait(200) + SetVehicleLights(veh, 0) + Wait(300) + ClearPedTasks(ped) + else + QBCore.Functions.Notify(Lang:t("notify.ydhk"), 'error') + end + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + end + end +end +function ToggleVehicleunLocks(veh) + if veh then + if not isBlacklistedVehicle(veh) then + if HasKeys(QBCore.Functions.GetPlate(veh)) or AreKeysJobShared(veh) then + local ped = PlayerPedId() + local vehLockStatus = GetVehicleDoorLockStatus(veh) + loadAnimDict("anim@mp_player_intmenu@key_fob@") + TaskPlayAnim(ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) + TriggerServerEvent("InteractSound_SV:PlayWithinDistance", 5, "lock", 0.3) + NetworkRequestControlOfEntity(veh) + if vehLockStatus == 2 then + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + QBCore.Functions.Notify(Lang:t("notify.vunlock"), "success") + end + SetVehicleLights(veh, 2) + Wait(250) + SetVehicleLights(veh, 1) + Wait(200) + SetVehicleLights(veh, 0) + Wait(300) + ClearPedTasks(ped) + else + QBCore.Functions.Notify(Lang:t("notify.ydhk"), 'error') + end + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + end + end +end +function ToggleVehicleTrunk(veh) + if veh then + if not isBlacklistedVehicle(veh) then + if HasKeys(QBCore.Functions.GetPlate(veh)) or AreKeysJobShared(veh) then + local ped = PlayerPedId() + local boot = GetEntityBoneIndexByName(GetVehiclePedIsIn(GetPlayerPed(-1), false), 'boot') + loadAnimDict("anim@mp_player_intmenu@key_fob@") + TaskPlayAnim(ped, 'anim@mp_player_intmenu@key_fob@', 'fob_click', 3.0, 3.0, -1, 49, 0, false, false, false) + TriggerServerEvent("InteractSound_SV:PlayWithinDistance", 5, "lock", 0.3) + NetworkRequestControlOfEntity(veh) + if boot ~= -1 or DoesEntityExist(veh) then + if trunkclose == true then + SetVehicleLights(veh, 2) + Citizen.Wait(150) + SetVehicleLights(veh, 0) + Citizen.Wait(150) + SetVehicleLights(veh, 2) + Citizen.Wait(150) + SetVehicleLights(veh, 0) + Citizen.Wait(150) + SetVehicleDoorOpen(veh, 5) + trunkclose = false + ClearPedTasks(ped) + else + SetVehicleLights(veh, 2) + Citizen.Wait(150) + SetVehicleLights(veh, 0) + Citizen.Wait(150) + SetVehicleLights(veh, 2) + Citizen.Wait(150) + SetVehicleLights(veh, 0) + Citizen.Wait(150) + SetVehicleDoorShut(veh, 5) + trunkclose = true + ClearPedTasks(ped) + end + end + else + QBCore.Functions.Notify(Lang:t("notify.ydhk"), 'error') + end + else + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(veh), 1) + end + end +end +function GetOtherPlayersInVehicle(vehicle) + local otherPeds = {} + for seat=-1,GetVehicleModelNumberOfSeats(GetEntityModel(vehicle))-2 do + local pedInSeat = GetPedInVehicleSeat(vehicle, seat) + if IsPedAPlayer(pedInSeat) and pedInSeat ~= PlayerPedId() then + otherPeds[#otherPeds+1] = pedInSeat + end + end + return otherPeds +end + +function GetPedsInVehicle(vehicle) + local otherPeds = {} + for seat=-1,GetVehicleModelNumberOfSeats(GetEntityModel(vehicle))-2 do + local pedInSeat = GetPedInVehicleSeat(vehicle, seat) + if not IsPedAPlayer(pedInSeat) and pedInSeat ~= 0 then + otherPeds[#otherPeds+1] = pedInSeat + end + end + return otherPeds +end + +function IsBlacklistedWeapon() + local weapon = GetSelectedPedWeapon(PlayerPedId()) + if weapon ~= nil then + for _, v in pairs(Config.NoCarjackWeapons) do + if weapon == GetHashKey(v) then + return true + end + end + end + return false +end + +function LockpickDoor(isAdvanced) + local ped = PlayerPedId() + local pos = GetEntityCoords(ped) + local vehicle = QBCore.Functions.GetClosestVehicle() + + if vehicle == nil or vehicle == 0 then return end + if HasKeys(QBCore.Functions.GetPlate(vehicle)) then return end + if #(pos - GetEntityCoords(vehicle)) > 2.5 then return end + if GetVehicleDoorLockStatus(vehicle) <= 0 then return end + + usingAdvanced = isAdvanced + Config.LockPickDoorEvent() +end +function LockpickFinishCallback(success) + local vehicle = QBCore.Functions.GetClosestVehicle() + + local chance = math.random() + if success then + TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) + lastPickedVehicle = vehicle + + if GetPedInVehicleSeat(vehicle, -1) == PlayerPedId() then + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', QBCore.Functions.GetPlate(vehicle)) + else + QBCore.Functions.Notify(Lang:t("notify.vlockpick"), 'success') + TriggerServerEvent('qb-vehiclekeys:server:setVehLockState', NetworkGetNetworkIdFromEntity(vehicle), 1) + end + + else + TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) + AttemptPoliceAlert("steal") + end + + if usingAdvanced then + if chance <= Config.RemoveLockpickAdvanced then + TriggerServerEvent("qb-vehiclekeys:server:breakLockpick", "advancedlockpick") + end + else + if chance <= Config.RemoveLockpickNormal then + TriggerServerEvent("qb-vehiclekeys:server:breakLockpick", "lockpick") + end + end +end + +function Hotwire(vehicle, plate) + local hotwireTime = math.random(Config.minHotwireTime, Config.maxHotwireTime) + local ped = PlayerPedId() + IsHotwiring = true + + SetVehicleAlarm(vehicle, true) + SetVehicleAlarmTimeLeft(vehicle, hotwireTime) + QBCore.Functions.Progressbar("hotwire_vehicle", Lang:t("progress.hskeys"), hotwireTime, false, true, { + disableMovement = true, + disableCarMovement = true, + disableMouse = false, + disableCombat = true + }, { + animDict = "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", + anim = "machinic_loop_mechandplayer", + flags = 16 + }, {}, {}, function() -- Done + StopAnimTask(ped, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", "machinic_loop_mechandplayer", 1.0) + TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) + if (math.random() <= Config.HotwireChance) then + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) + else + QBCore.Functions.Notify(Lang:t("notify.fvlockpick"), "error") + end + Wait(Config.TimeBetweenHotwires) + IsHotwiring = false + end, function() -- Cancel + StopAnimTask(ped, "anim@amb@clubhouse@tutorial@bkr_tut_ig3@", "machinic_loop_mechandplayer", 1.0) + IsHotwiring = false + end) + SetTimeout(10000, function() + AttemptPoliceAlert("steal") + end) + IsHotwiring = false +end +function CarjackVehicle(target) + if not Config.CarJackEnable then return end + isCarjacking = true + canCarjack = false + loadAnimDict('mp_am_hold_up') + local vehicle = GetVehiclePedIsUsing(target) + local occupants = GetPedsInVehicle(vehicle) + for p=1,#occupants do + local ped = occupants[p] + CreateThread(function() + TaskPlayAnim(ped, "mp_am_hold_up", "holdup_victim_20s", 8.0, -8.0, -1, 49, 0, false, false, false) + PlayPain(ped, 6, 0) + end) + Wait(math.random(200,500)) + end + -- Cancel progress bar if: Ped dies during robbery, car gets too far away + CreateThread(function() + while isCarjacking do + local distance = #(GetEntityCoords(PlayerPedId()) - GetEntityCoords(target)) + if IsPedDeadOrDying(target) or distance > 7.5 then + TriggerEvent("progressbar:client:cancel") + end + Wait(100) + end + end) + QBCore.Functions.Progressbar("rob_keys", Lang:t("progress.acjack"), Config.CarjackingTime, false, true, {}, {}, {}, {}, function() + local hasWeapon, weaponHash = GetCurrentPedWeapon(PlayerPedId(), true) + if hasWeapon and isCarjacking then + local carjackChance + if Config.CarjackChance[tostring(GetWeapontypeGroup(weaponHash))] then + carjackChance = Config.CarjackChance[tostring(GetWeapontypeGroup(weaponHash))] + else + carjackChance = 0.5 + end + if math.random() <= carjackChance then + local plate = QBCore.Functions.GetPlate(vehicle) + for p=1,#occupants do + local ped = occupants[p] + CreateThread(function() + TaskLeaveVehicle(ped, vehicle, 0) + PlayPain(ped, 6, 0) + Wait(1250) + ClearPedTasksImmediately(ped) + PlayPain(ped, math.random(7,8), 0) + MakePedFlee(ped) + end) + end + TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) + TriggerServerEvent('qb-vehiclekeys:server:AcquireVehicleKeys', plate) + else + QBCore.Functions.Notify(Lang:t("notify.cjackfail"), "error") + MakePedFlee(target) + TriggerServerEvent('hud:server:GainStress', math.random(1, 4)) + end + isCarjacking = false + Wait(2000) + AttemptPoliceAlert("carjack") + Wait(Config.DelayBetweenCarjackings) + canCarjack = true + end + end, function() + MakePedFlee(target) + isCarjacking = false + Wait(Config.DelayBetweenCarjackings) + canCarjack = true + end) +end + +function AttemptPoliceAlert(type) + if not AlertSend then + local chance = Config.PoliceAlertChance + if GetClockHours() >= 1 and GetClockHours() <= 6 then + chance = Config.PoliceNightAlertChance + end + if math.random() <= chance then + TriggerServerEvent('police:server:policeAlert', Lang:t("info.palert") .. type) + end + AlertSend = true + SetTimeout(Config.AlertCooldown, function() + AlertSend = false + end) + end +end +function MakePedFlee(ped) + SetPedFleeAttributes(ped, 0, 0) + TaskReactAndFleePed(ped, PlayerPedId()) +end +function DrawText3D(x, y, z, text) + SetTextScale(0.35, 0.35) + SetTextFont(4) + SetTextProportional(1) + SetTextColour(255, 255, 255, 215) + SetTextEntry("STRING") + SetTextCentre(true) + AddTextComponentString(text) + SetDrawOrigin(x, y, z, 0) + DrawText(0.0, 0.0) + local factor = (string.len(text)) / 370 + DrawRect(0.0, 0.0 + 0.0125, 0.017 + factor, 0.03, 0, 0, 0, 75) + ClearDrawOrigin() +end +----------------------- +---- NUICallback ---- +----------------------- +RegisterNUICallback('closui', function() + SetNuiFocus(false, false) +end) +RegisterNUICallback('unlock', function() + ToggleVehicleunLocks(GetVehicle()) + SetNuiFocus(false, false) +end) +RegisterNUICallback('lock', function() + ToggleVehicleLocks(GetVehicle()) + SetNuiFocus(false, false) +end) +RegisterNUICallback('trunk', function() + ToggleVehicleTrunk(GetVehicle()) + SetNuiFocus(false, false) +end) +RegisterNUICallback('engine', function() + ToggleEngine(GetVehicle()) + SetNuiFocus(false, false) +end) diff --git a/[qb]/qb-vehiclekeys/config.lua b/[qb]/qb-vehiclekeys/config.lua new file mode 100644 index 0000000..c8906ee --- /dev/null +++ b/[qb]/qb-vehiclekeys/config.lua @@ -0,0 +1,98 @@ +Config = {} + +-- NPC Vehicle Lock States +Config.LockNPCDrivingCars = true -- Lock state for NPC cars being driven by NPCs [true = locked, false = unlocked] +Config.LockNPCParkedCars = true -- Lock state for NPC parked cars [true = locked, false = unlocked] +Config.UseKeyfob = false -- you can set this true if you dont need ui +-- Lockpick Settings +Config.RemoveLockpickNormal = 0.5 -- Chance to remove lockpick on fail +Config.RemoveLockpickAdvanced = 0.2 -- Chance to remove advanced lockpick on fail +Config.LockPickDoorEvent = function() -- This function is called when a player attempts to lock pick a vehicle + TriggerEvent('qb-lockpick:client:openLockpick', LockpickFinishCallback) +end + +-- Carjack Settings +Config.CarJackEnable = true -- True allows for the ability to car jack peds. +Config.CarjackingTime = 7500 -- How long it takes to carjack +Config.DelayBetweenCarjackings = 10000 -- Time before you can carjack again +Config.CarjackChance = { + ['2685387236'] = 0.0, -- melee + ['416676503'] = 0.5, -- handguns + ['-957766203'] = 0.75, -- SMG + ['860033945'] = 0.90, -- shotgun + ['970310034'] = 0.90, -- assault + ['1159398588'] = 0.99, -- LMG + ['3082541095'] = 0.99, -- sniper + ['2725924767'] = 0.99, -- heavy + ['1548507267'] = 0.0, -- throwable + ['4257178988'] = 0.0, -- misc +} + +-- Hotwire Settings +Config.HotwireChance = 0.5 -- Chance for successful hotwire or not +Config.TimeBetweenHotwires = 5000 -- Time in ms between hotwire attempts +Config.minHotwireTime = 20000 -- Minimum hotwire time in ms +Config.maxHotwireTime = 40000 -- Maximum hotwire time in ms + +-- Police Alert Settings +Config.AlertCooldown = 10000 -- 10 seconds +Config.PoliceAlertChance = 0.75 -- Chance of alerting police during the day +Config.PoliceNightAlertChance = 0.50 -- Chance of alerting police at night (times:01-06) + +-- Job Settings +Config.SharedKeys = { -- Share keys amongst employees. Employees can lock/unlock any job-listed vehicle + ['police'] = { -- Job name + requireOnduty = false, + vehicles = { + 'police', -- Vehicle model + 'police2', -- Vehicle model + } + }, + + ['mechanic'] = { + requireOnduty = false, + vehicles = { + 'towtruck', + } + } +} + +-- These vehicles cannot be jacked +Config.ImmuneVehicles = { + 'stockade' +} + +-- These vehicles will never lock +Config.NoLockVehicles = {} + +-- These weapons cannot be used for carjacking +Config.NoCarjackWeapons = { + "WEAPON_UNARMED", + "WEAPON_Knife", + "WEAPON_Nightstick", + "WEAPON_HAMMER", + "WEAPON_Bat", + "WEAPON_Crowbar", + "WEAPON_Golfclub", + "WEAPON_Bottle", + "WEAPON_Dagger", + "WEAPON_Hatchet", + "WEAPON_KnuckleDuster", + "WEAPON_Machete", + "WEAPON_Flashlight", + "WEAPON_SwitchBlade", + "WEAPON_Poolcue", + "WEAPON_Wrench", + "WEAPON_Battleaxe", + "WEAPON_Grenade", + "WEAPON_StickyBomb", + "WEAPON_ProximityMine", + "WEAPON_BZGas", + "WEAPON_Molotov", + "WEAPON_FireExtinguisher", + "WEAPON_PetrolCan", + "WEAPON_Flare", + "WEAPON_Ball", + "WEAPON_Snowball", + "WEAPON_SmokeGrenade", +} \ No newline at end of file diff --git a/[qb]/qb-vehiclekeys/fxmanifest.lua b/[qb]/qb-vehiclekeys/fxmanifest.lua new file mode 100644 index 0000000..298110d --- /dev/null +++ b/[qb]/qb-vehiclekeys/fxmanifest.lua @@ -0,0 +1,23 @@ +fx_version 'cerulean' +game 'gta5' +description 'QB-VehicleKeys' +version '1.2.5' +ui_page 'NUI/index.html' + +files { + 'NUI/index.html', + 'NUI/style.css', + 'NUI/script.js', + 'NUI/images/*', +} + +shared_scripts { + '@qb-core/shared/locale.lua', + 'locales/*.lua', + 'config.lua', +} + +client_script 'client/main.lua' +server_script 'server/main.lua' + +lua54 'yes' diff --git a/[qb]/qb-vehiclekeys/locales/ar.lua b/[qb]/qb-vehiclekeys/locales/ar.lua new file mode 100644 index 0000000..c6fea6c --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/ar.lua @@ -0,0 +1,49 @@ +local Translations = { + notify = { + ydhk = 'ليس لديك مفاتيح لهذه السيارة.', + nonear = 'لا يوجد أحد في الجوار لتسليم المفاتيح إليه', + vlock = 'السيارة مقفلة!', + vunlock = 'السيارة مقفلة!', + vlockpick = 'لقد تمكنت من فتح قفل الباب!', + fvlockpick = 'أنت تفشل في العثور على المفاتيح وتحبط.', + vgkeys = 'أنت تسلم المفاتيح.', + vgetkeys = 'تحصل على مفاتيح السيارة!', + fpid = 'قم بتعبئة معرف اللاعب وحجج اللوحة', + cjackfail = 'فشل سرقة السيارة!', + vehclose = 'لا توجد سيارة قريبة!', + }, + progress = { + takekeys = 'أخذ المفاتيح من الجسد...', + hskeys = 'البحث عن مفاتيح السيارة...', + acjack = 'محاولة سرقة السيارات...', + }, + info = { + skeys = '~g~[H]~w~ - Search for Keys', + tlock = 'تبديل أقفال السيارة', + palert = 'سرقة السيارة قيد التقدم. يكتب: ', + engine = 'تبديل المحرك', + }, + addcom = { + givekeys = 'Hand over the keys to someone. If no ID, gives to closest person or everyone in the vehicle.', + givekeys_id = 'id', + givekeys_id_help = 'Player ID', + addkeys = 'Adds keys to a vehicle for someone.', + addkeys_id = 'id', + addkeys_id_help = 'Player ID', + addkeys_plate = 'plate', + addkeys_plate_help = 'Plate', + rkeys = 'Remove keys to a vehicle for someone.', + rkeys_id = 'id', + rkeys_id_help = 'Player ID', + rkeys_plate = 'plate', + rkeys_plate_help = 'Plate', + } + +} + +if GetConvar('qb_locale', 'en') == 'ar' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/de.lua b/[qb]/qb-vehiclekeys/locales/de.lua new file mode 100644 index 0000000..b769f3b --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/de.lua @@ -0,0 +1,50 @@ +local Translations = { + notify = { + ydhk = 'Sie haben keine Schlüssel für das Fahrzeug!', + nonear = 'Es ist niemand in der Nähe, der den Schlüssel bekommen könnte!', + vlock = 'Fahrzeug verriegelt!', + vunlock = 'Fahrzeug entriegelt.', + vlockpick = 'Sie haben es geschafft, das Türschloss zu knacken!', + fvlockpick = 'Sie können keine Schlüssel finden und sind frustriert.', + vgkeys = 'Sie geben die Schlüssel ab.', + vgetkeys = 'Sie erhalten die Schlüssel für das Fahrzeug!', + fpid = 'Geben Sie alle Argumente, nämlich die Bürger-ID und das Kennzeichen, an.', + cjackfail = 'Knacken des Autos fehlgeschlagen!', + vehclose = 'Thers no close vehicle!', + }, + progress = { + takekeys = 'Fahrzeugschlüssel abnehmen...', + hskeys = 'Suche nach Fahrzeugschlüssel...', + acjack = 'Versuchter Autodiebstahl...', + }, + info = { + skeys = '~g~[H]~w~ - Nach Schlüssel suchen', + tlock = 'Fahrzeug auf-/zuschließen', + palert = 'Fahrzeugdiebstahl im Gange. Typ: ', + engine = 'Motor ein-/ausschalten', + }, + addcom = { + givekeys = 'Übergeben Sie die Schlüssel an jemanden. Wenn keine Bürger-ID angegeben, geht er an die nächstgelegene Person oder an alle Personen im Fahrzeug.', + givekeys_id = 'id', + givekeys_id_help = 'Bürger-ID', + addkeys = 'Fügt Schlüssel zu einem Fahrzeug für jemanden hinzu.', + addkeys_id = 'id', + addkeys_id_help = 'Bürger-ID', + addkeys_plate = 'kennzeichen', + addkeys_plate_help = 'Kennzeichen', + rkeys = 'Jemandem die Schlüssel eines Fahrzeugs abnehmen.', + rkeys_id = 'id', + rkeys_id_help = 'Bürger-ID', + rkeys_plate = 'kennzeichen', + rkeys_plate_help = 'Kennzeichen', + } + +} + +if GetConvar('qb_locale', 'en') == 'de' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/en.lua b/[qb]/qb-vehiclekeys/locales/en.lua new file mode 100644 index 0000000..784992e --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/en.lua @@ -0,0 +1,47 @@ +local Translations = { + notify = { + ydhk = 'You don\'t have keys to this vehicle.', + nonear = 'There is nobody nearby to hand keys to', + vlock = 'Vehicle locked!', + vunlock = 'Vehicle unlocked!', + vlockpick = 'You managed to pick the door lock open!', + fvlockpick = 'You fail to find the keys and get frustrated.', + vgkeys = 'You hand over the keys.', + vgetkeys = 'You get keys to the vehicle!', + fpid = 'Fill out the player ID and Plate arguments', + cjackfail = 'Carjacking failed!', + vehclose = 'There\'s no close vehicle!', + }, + progress = { + takekeys = 'Taking keys from body...', + hskeys = 'Searching for the car keys...', + acjack = 'Attempting Carjacking...', + }, + info = { + skeys = '~g~[H]~w~ - Search for Keys', + tlock = 'Toggle Vehicle Locks', + palert = 'Vehicle theft in progress. Type: ', + engine = 'Toggle Engine', + }, + addcom = { + givekeys = 'Hand over the keys to someone. If no ID, gives to closest person or everyone in the vehicle.', + givekeys_id = 'id', + givekeys_id_help = 'Player ID', + addkeys = 'Adds keys to a vehicle for someone.', + addkeys_id = 'id', + addkeys_id_help = 'Player ID', + addkeys_plate = 'plate', + addkeys_plate_help = 'Plate', + rkeys = 'Remove keys to a vehicle for someone.', + rkeys_id = 'id', + rkeys_id_help = 'Player ID', + rkeys_plate = 'plate', + rkeys_plate_help = 'Plate', + } + +} + +Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true +}) diff --git a/[qb]/qb-vehiclekeys/locales/es.lua b/[qb]/qb-vehiclekeys/locales/es.lua new file mode 100644 index 0000000..19c819e --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/es.lua @@ -0,0 +1,50 @@ +local Translations = { + notify = { + ydhk = 'No tienes las llaves de este vehículo', + nonear = 'No hay nadie cerca a quién darle las llaves', + vlock = '¡Vehículo cerrado!', + vunlock = '¡Vehículo abierto!', + vlockpick = '¡Lograste abrir la cerradura!', + fvlockpick = 'No logras encontrar las llaves y te frustras', + vgkeys = 'Has entregado las llaves', + vgetkeys = '¡Has recibido las llaves al vehículo!', + fpid = 'Llena los argumentos de ID y placa del jugador', + cjackfail = '¡Robo de carro falló!', + vehclose = '¡No hay vehículo cerca!', + }, + progress = { + takekeys = 'Obteniendo las llaves del cuerpo...', + hskeys = 'Buscando las llaves del carro...', + acjack = 'Intentando robar carro...', + }, + info = { + skeys = '[H] - Buscar llaves', + tlock = 'Habilitar/deshabilitar seguro de carro', + palert = 'Robo de vehículo en progreso. Tipo: ', + engine = 'Encender/apagar motor', + }, + addcom = { + givekeys = 'Entregar llaves a alguien. Si no hay ID, entregar a la persona más cercana o a todos en el vehículo.', + givekeys_id = 'id', + givekeys_id_help = 'ID de jugador', + addkeys = 'Agrega llaves a un vehículo para alguien.', + addkeys_id = 'id', + addkeys_id_help = 'ID de jugador', + addkeys_plate = 'placa', + addkeys_plate_help = 'Placa', + rkeys = 'Quitar llaves de un vehículo a alguien.', + rkeys_id = 'id', + rkeys_id_help = 'ID de jugador', + rkeys_plate = 'placa', + rkeys_plate_help = 'Placa', + } + +} + +if GetConvar('qb_locale', 'en') == 'es' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/et.lua b/[qb]/qb-vehiclekeys/locales/et.lua new file mode 100644 index 0000000..4cc0727 --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/et.lua @@ -0,0 +1,50 @@ +local Translations = { + notify = { + ydhk = 'Teil pole selle sõiduki võtmeid.', + nonear = 'Läheduses pole kedagi, kellele võtmed kätte anda', + vlock = 'Sõiduk lukus!', + vunlock = 'Sõiduk avatud!', + vlockpick = 'Sul õnnestus ukselukk lahti keerata!', + fvlockpick = 'Te ei leia võtmeid ja olete pettunud.', + vgkeys = 'Annad võtmed üle.', + vgetkeys = 'Saate auto võtmed!', + fpid = 'Täitke mängija ID ja plaadi argumendid', + cjackfail = 'Autovargamine ebaõnnestus!', + vehclose = 'Thers no close vehicle!', + }, + progress = { + takekeys = 'Võtmete kehast võtmine...', + hskeys = 'Autovõtmete otsimine...', + acjack = 'Autovarguse katse...', + }, + info = { + skeys = '~g~[H]~w~ – Võtmete otsimine', + tlock = 'Lülitage sõiduki lukud sisse', + palert = 'Sõiduki vargus käsil. Tüüp:', + engine = 'Mootori sisse- ja väljalülitamine', + }, + addcom = { + givekeys = 'Andke võtmed kellelegi üle. Kui isikut tõendav dokument puudub, annab see lähimale inimesele või kõigile sõidukis viibijatele.', + givekeys_id = 'id', + givekeys_id_help = 'Mängija ID', + addkeys = 'Lisab kellegi jaoks sõidukile võtmed.', + addkeys_id = 'id', + addkeys_id_help = 'Mängija ID', + addkeys_plate = 'Numbrimärk', + addkeys_plate_help = 'Numbrimärk', + rkeys = 'Eemaldage kellegi jaoks sõiduki võtmed.', + rkeys_id = 'id', + rkeys_id_help = 'Mängija ID', + rkeys_plate = 'Numbrimärk', + rkeys_plate_help = 'Numbrimärk', + } + +} + +if GetConvar('qb_locale', 'en') == 'et' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/fr.lua b/[qb]/qb-vehiclekeys/locales/fr.lua new file mode 100644 index 0000000..c8ca47d --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/fr.lua @@ -0,0 +1,50 @@ +local Translations = { + notify = { + ydhk = 'Vous n\'avez pas de clés de ce véhicule.', + nonear = 'Il n\'y a personne à proximité.', + vlock = 'Véhicule verrouillé!', + vunlock = 'Véhicule déverrouillé!', + vlockpick = 'Vous avez réussi à ouvrir le véhicule!', + fvlockpick = 'Vous n\'avez pas réussi à ouvrir le véhicule et vous êtes frustré.', + vgkeys = 'Vous donnez les clés.', + vgetkeys = 'Vous obtenez les clés du véhicule!', + fpid = 'Remplissez les arguments ID et plaque.', + cjackfail = 'Le détournement de voiture a échoué', + vehclose = 'Thers no close vehicle!', + }, + progress = { + takekeys = 'Prend les clés du corps..', + hskeys = 'Cherche les clés du véhicule..', + acjack = 'Tentative de vol de carjack..', + }, + info = { + skeys = '~g~[H]~w~ - Chercher les clés', + tlock = 'Verrouiller/Déverrouiller le véhicule', + palert = 'Vol de véhicule en cours. Type: ', + engine = 'Allumer/Eteindre le moteur', + }, + addcom = { + givekeys = 'Donner les clés à quelqu\'un. Si aucun ID, donne à la personne la plus proche ou à tout le monde dans le véhicule.', + givekeys_id = 'id', + givekeys_id_help = 'ID du joueur', + addkeys = 'Ajouter les clés à un véhicule pour quelqu\'un.', + addkeys_id = 'id', + addkeys_id_help = 'ID du joueur', + addkeys_plate = 'plaque', + addkeys_plate_help = 'Plaque', + rkeys = 'Retirer les clés à un véhicule pour quelqu\'un.', + rkeys_id = 'id', + rkeys_id_help = 'ID du joueur', + rkeys_plate = 'plaque', + rkeys_plate_help = 'Plaque', + } + +} + +if GetConvar('qb_locale', 'en') == 'fr' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/pt.lua b/[qb]/qb-vehiclekeys/locales/pt.lua new file mode 100644 index 0000000..4342122 --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/pt.lua @@ -0,0 +1,50 @@ +local Translations = { + notify = { + ydhk = 'Não tem as chaves deste veículo.', + nonear = 'Não há ninguém por perto para entregar as chaves', + vlock = 'Veículo trancado!', + vunlock = 'Veículo destrancado!', + vlockpick = 'Conseguiu abrir a fechadura da porta!', + fvlockpick = 'Não conseguiu encontrar as chaves e agora sente-se frustrado.', + vgkeys = 'Entregou as chaves', + vgetkeys = 'Recebeu as chaves do veículo!', + fpid = 'Preencha o ID do jogador e a matricula', + cjackfail = 'Falha no roubo do veículo!', + vehclose = 'Nenhum veículo por perto!', + }, + progress = { + takekeys = 'Tirando as chaves do corpo...', + hskeys = 'Procurando as chaves do veículo...', + acjack = 'Tentativa de roubo de veículo...', + }, + info = { + skeys = '~g~[H]~w~ - Procurar as chaves', + tlock = 'Trancar/Destrancar Veículo', + palert = 'Roubo de veículo em andamento. Tipo: ', + engine = 'Ligar/Desligar Motor', + }, + addcom = { + givekeys = 'Entregue as chaves a alguém. Se não houver identificação, entregue à pessoa mais próxima ou a todos no veículo.', + givekeys_id = 'id', + givekeys_id_help = 'ID do jogador', + addkeys = 'Adicionar chaves a um veículo para alguém', + addkeys_id = 'id', + addkeys_id_help = 'ID do jogador', + addkeys_plate = 'Matricula', + addkeys_plate_help = 'Matricula', + rkeys = 'Remover as chaves de um veículo para alguém.', + rkeys_id = 'id', + rkeys_id_help = 'ID do jogador', + rkeys_plate = 'Matricula', + rkeys_plate_help = 'Matricula', + } + +} + +if GetConvar('qb_locale', 'en') == 'pt' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/ro.lua b/[qb]/qb-vehiclekeys/locales/ro.lua new file mode 100644 index 0000000..1fc6727 --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/ro.lua @@ -0,0 +1,54 @@ +--[[ +Romanian base language translation for qb-vehiclekeys +Translation done by wanderrer (Martin Riggs#0807 on Discord) +]]-- +local Translations = { + notify = { + ydhk = 'Nu ai cheile acestui vehicul.', + nonear = 'Nu este nimeni langa tine sa-i dai cheile vehiculului', + vlock = 'Vehicul blocat - Alarma activa!', + vunlock = 'Vehicul deblocat - Alarma inactiva!', + vlockpick = 'Felicitari, ai reusit sa spargi incuietoarea!', + fvlockpick = 'Nu ai putut clona cheile masinii si te stresezi.', + vgkeys = 'Ai dat cheile vehiculului.', + vgetkeys = 'Ai primit cheile vehiculului!', + fpid = 'Trebuie sa introduci ID-ul jucatorului si numarul de inmatriculare', + cjackfail = 'Furtul de mașină a eșuat', + vehclose = 'Thers no close vehicle!', + }, + progress = { + takekeys = 'Iei chile de la vehicul...', + hskeys = 'Clonezi cheile vehiculului...', + acjack = 'Incerci sa fur vehiculul...', + }, + info = { + skeys = '~g~[H]~w~ - Cloneaza cheile', + tlock = 'Blocheaza/Deblocheaza vehiculul', + palert = 'Furt auto in desfasurare. Tip: ', + engine = 'Porneste/Opreste motorul', + }, + addcom = { + givekeys = 'Dai cheile vehiculului unei alte persoane. Daca nu se precizeaza ID-ul, cheile se duc celei mai apropiate persoane de tine.', + givekeys_id = 'ID', + givekeys_id_help = 'ID-ul jucatorului caruia vrei sa-i dai cheile', + addkeys = 'Adaugi un set de chei al unui vehicul pentru o alta persoana.', + addkeys_id = 'ID', + addkeys_id_help = 'ID-ul jucatorului', + addkeys_plate = 'numar', + addkeys_plate_help = 'Numarul de inmatriculare al vehiculului', + rkeys = 'Anulezi setul de chei aditional, pentru o persoana.', + rkeys_id = 'ID', + rkeys_id_help = 'ID-ul jucatorului', + rkeys_plate = 'numar', + rkeys_plate_help = 'Numarul de inmatriculare al vehiculului', + } + +} + +if GetConvar('qb_locale', 'en') == 'ro' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclekeys/locales/sv.lua b/[qb]/qb-vehiclekeys/locales/sv.lua new file mode 100644 index 0000000..4306b7c --- /dev/null +++ b/[qb]/qb-vehiclekeys/locales/sv.lua @@ -0,0 +1,49 @@ +local Translations = { + notify = { + ydhk = 'Du har inga nycklar till det här fordonet.', + nonear = 'Det finns ingen i närheten.', + vlock = 'Fordon låst!', + vunlock = 'Fordon upplåst!', + vlockpick = 'Du lyckades dyrka dörrlåset!', + fvlockpick = 'Du hittar inte nycklarna och blir frustrerad.', + vgkeys = 'Du överlämnar ett par nycklar.', + vgetkeys = 'Du fick nycklar till fordonet!', + fpid = 'Fyll i personens ID och reg.nr på fordonet', + cjackfail = 'Bilstöld misslyckades!', + vehclose = 'Det finns inget fordon tillräckligt nära!', + }, + progress = { + takekeys = 'Tar nycklar från personen...', + hskeys = 'Letar efter bilnycklarna...', + acjack = 'Snor fordonet...', + }, + info = { + skeys = '~g~[H]~w~ - Leta efter nycklarna', + tlock = 'Lås/Lås upp fordon', + palert = 'Fordonsstöld pågår. Typ: ', + engine = 'Motor Av/På', + }, + addcom = { + givekeys = 'Överlämna nycklar till någon. Om inget ID anges, ges nycklarna till närmaste personen eller alla i fordonet.', + givekeys_id = 'id', + givekeys_id_help = 'Person ID', + addkeys = 'Lägg till nycklar till ett fordon för någon.', + addkeys_id = 'id', + addkeys_id_help = 'Person ID', + addkeys_plate = 'reg.nr', + addkeys_plate_help = 'Reg.nr', + rkeys = 'Ta bort nycklarna till ett fordon för någon.', + rkeys_id = 'id', + rkeys_id_help = 'Person ID', + rkeys_plate = 'reg.nr', + rkeys_plate_help = 'Reg.nr', + } + +} + +if GetConvar('qb_locale', 'en') == 'sv' then + Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true + }) +end diff --git a/[qb]/qb-vehiclekeys/server/main.lua b/[qb]/qb-vehiclekeys/server/main.lua new file mode 100644 index 0000000..d3f7a76 --- /dev/null +++ b/[qb]/qb-vehiclekeys/server/main.lua @@ -0,0 +1,126 @@ +----------------------- +---- Variables ---- +----------------------- +local QBCore = exports['qb-core']:GetCoreObject() +local VehicleList = {} + +----------------------- +---- Threads ---- +----------------------- + +----------------------- +---- Server Events ---- +----------------------- + +-- Event to give keys. receiver can either be a single id, or a table of ids. +-- Must already have keys to the vehicle, trigger the event from the server, or pass forcegive paramter as true. +RegisterNetEvent('qb-vehiclekeys:server:GiveVehicleKeys', function(receiver, plate) + local giver = source + + if HasKeys(giver, plate) then + TriggerClientEvent('QBCore:Notify', giver, Lang:t("notify.vgkeys"), 'success') + if type(receiver) == 'table' then + for _,r in ipairs(receiver) do + GiveKeys(receiver[r], plate) + end + else + GiveKeys(receiver, plate) + end + else + TriggerClientEvent('QBCore:Notify', giver, Lang:t("notify.ydhk"), "error") + end +end) + +RegisterNetEvent('qb-vehiclekeys:server:AcquireVehicleKeys', function(plate) + local src = source + GiveKeys(src, plate) +end) + +RegisterNetEvent('qb-vehiclekeys:server:breakLockpick', function(itemName) + local Player = QBCore.Functions.GetPlayer(source) + if not Player then return end + if not (itemName == "lockpick" or itemName == "advancedlockpick") then return end + if Player.Functions.RemoveItem(itemName, 1) then + TriggerClientEvent("inventory:client:ItemBox", source, QBCore.Shared.Items[itemName], "remove") + end +end) + +RegisterNetEvent('qb-vehiclekeys:server:setVehLockState', function(vehNetId, state) + SetVehicleDoorsLocked(NetworkGetEntityFromNetworkId(vehNetId), state) +end) + +QBCore.Functions.CreateCallback('qb-vehiclekeys:server:GetVehicleKeys', function(source, cb) + local Player = QBCore.Functions.GetPlayer(source) + if not Player then return end + local citizenid = Player.PlayerData.citizenid + local keysList = {} + for plate, citizenids in pairs (VehicleList) do + if citizenids[citizenid] then + keysList[plate] = true + end + end + cb(keysList) +end) + +QBCore.Functions.CreateCallback('qb-vehiclekeys:server:checkPlayerOwned', function(_, cb, plate) + local playerOwned = false + if VehicleList[plate] then + playerOwned = true + end + cb(playerOwned) +end) + +----------------------- +---- Functions ---- +----------------------- + +function GiveKeys(id, plate) + local citizenid = QBCore.Functions.GetPlayer(id).PlayerData.citizenid + + if not VehicleList[plate] then VehicleList[plate] = {} end + VehicleList[plate][citizenid] = true + + TriggerClientEvent('QBCore:Notify', id, Lang:t("notify.vgetkeys")) + TriggerClientEvent('qb-vehiclekeys:client:AddKeys', id, plate) +end + +function RemoveKeys(id, plate) + local citizenid = QBCore.Functions.GetPlayer(id).PlayerData.citizenid + + if VehicleList[plate] and VehicleList[plate][citizenid] then + VehicleList[plate][citizenid] = nil + end + + TriggerClientEvent('qb-vehiclekeys:client:RemoveKeys', id, plate) +end + +function HasKeys(id, plate) + local citizenid = QBCore.Functions.GetPlayer(id).PlayerData.citizenid + if VehicleList[plate] and VehicleList[plate][citizenid] then + return true + end + return false +end + +QBCore.Commands.Add("givekeys", Lang:t("addcom.givekeys"), {{name = Lang:t("addcom.givekeys_id"), help = Lang:t("addcom.givekeys_id_help")}}, false, function(source, args) + local src = source + TriggerClientEvent('qb-vehiclekeys:client:GiveKeys', src, tonumber(args[1])) +end) + +QBCore.Commands.Add("addkeys", Lang:t("addcom.addkeys"), {{name = Lang:t("addcom.addkeys_id"), help = Lang:t("addcom.addkeys_id_help")}, {name = Lang:t("addcom.addkeys_plate"), help = Lang:t("addcom.addkeys_plate_help")}}, true, function(source, args) + local src = source + if not args[1] or not args[2] then + TriggerClientEvent('QBCore:Notify', src, Lang:t("notify.fpid")) + return + end + GiveKeys(tonumber(args[1]), args[2]) +end, 'admin') + +QBCore.Commands.Add("removekeys", Lang:t("addcom.rkeys"), {{name = Lang:t("addcom.rkeys_id"), help = Lang:t("addcom.rkeys_id_help")}, {name = Lang:t("addcom.rkeys_plate"), help = Lang:t("addcom.rkeys_plate_help")}}, true, function(source, args) + local src = source + if not args[1] or not args[2] then + TriggerClientEvent('QBCore:Notify', src, Lang:t("notify.fpid")) + return + end + RemoveKeys(tonumber(args[1]), args[2]) +end, 'admin') diff --git a/[qb]/qb-vehiclesales/.github/ISSUE_TEMPLATE/bug_report.md b/[qb]/qb-vehiclesales/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..62f702f --- /dev/null +++ b/[qb]/qb-vehiclesales/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,32 @@ +--- +name: Bug report +about: Create a report to help us improve or fix something +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. A stranger to qbcore should be able to read your bug report and understand how to reproduce it themselves and understand how the feature should work normally. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Use this item '....' (item's name from shared.lua if applicable) +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Questions (please complete the following information):** + - When you last updated: [e.g. last week] + - Are you using custom resource? which ones? [e.g. zdiscord, qb-target] + - Have you renamed `qb-` to something custom? [e.g. yes/no] + +**Additional context** +Add any other context about the problem here. diff --git a/[qb]/qb-vehiclesales/.github/ISSUE_TEMPLATE/feature-request.md b/[qb]/qb-vehiclesales/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000..9e9bf3e --- /dev/null +++ b/[qb]/qb-vehiclesales/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,20 @@ +--- +name: Feature Request +about: Suggest an idea for QBCore +title: "[SUGGESTION]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the feature you'd like** +A clear and concise description of what you want to happen. and with as much detail as possible how it would function in your opinion. Please try to keep it unique. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered for people to have in mind just in case the main idea isn't liked but a derivative is. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/[qb]/qb-vehiclesales/.github/auto_assign.yml b/[qb]/qb-vehiclesales/.github/auto_assign.yml new file mode 100644 index 0000000..2a80921 --- /dev/null +++ b/[qb]/qb-vehiclesales/.github/auto_assign.yml @@ -0,0 +1,17 @@ +# Set to true to add reviewers to pull requests +addReviewers: true + +# Set to true to add assignees to pull requests +addAssignees: author + +# A list of reviewers to be added to pull requests (GitHub user name) +reviewers: + - /maintenance + +# A list of keywords to be skipped the process that add reviewers if pull requests include it +skipKeywords: + - wip + +# A number of reviewers added to the pull request +# Set 0 to add all the reviewers (default: 0) +numberOfReviewers: 0 \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/.github/contributing.md b/[qb]/qb-vehiclesales/.github/contributing.md new file mode 100644 index 0000000..21fb806 --- /dev/null +++ b/[qb]/qb-vehiclesales/.github/contributing.md @@ -0,0 +1,201 @@ +# Contributing to QBCore + +First of all, thank you for taking the time to contribute! + +These guidelines will help you help us in the best way possible regardless of your skill level. We ask that you try to read everything related to the way you'd like to contribute and try and use your best judgement for anything not covered. + +### Table of Contents + +[Code of Conduct](#code-of-conduct) + +[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Suggesting Features / Enhancements](#suggesting-features--enhancements) + * [Your First Code Contribution](#your-first-code-contribution) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Git Commit Messages](#git-commit-messages) + * [Lua Styleguide](#lua-styleguide) + * [JavaScript Styleguide](#javascript-styleguide) + + + +## Code of Conduct + +- Refrain from using languages other than English. +- Refrain from discussing any politically charged or inflammatory topics. +- Uphold mature conversations and respect each other; excessive profanity, hate speech or any kind of harassment will not be tolerated. +- No advertising of any kind. +- Follow these guidelines. +- Do not mention members of github unless a question is directed at them and can't be answered by anyone else. +- Do not mention any of the development team for any reason. We will read things as we get to them. + +## I don't want to read this whole thing I just have a question!!! + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +* [QBCore Website](https://qbcore.org) +* [QBCore Discord](https://discord.gg/qbcore) +* [FiveM Discord - #qbcore channel](https://discord.gg/fivem) + + + + + + + + + + +## How Can I Contribute? + +### Reporting Bugs + +The easiest way to contribute for most people is just to report bugs you find cause if nobody reports it there's a chance we'll never know it exists and then we'll never fix it. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out the bug-report template with the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Check the docs** There's a chance what you see as a bug might just work differently than you expect and if you think it could work better consider a feature enhancement report instead. +* **Search the [discord](https://discord.gg/qbcore)** to see if anyone else has run into the issue and see if it was solved through user error or code changes. (if the code change isn't pending a PR and you know what you're doing consider submitting one following [Pull Requests](#pull-requests) ) +* **Determine which resource the problem should be reported in**. If the bug is related to the inventory for example report this bug under qb-inventory rather than under qb-core or some other resource. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your bug is related to, create an issue on that repository and provide the following information by filling in bug-report template. + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. If something happened with only a specific group or single item but not others, specify that. +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected to see instead and why.** +* **Include screenshots** which show the specific bug in action or before and after. +* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below. + +Provide more context by answering these questions if possible: + +* **Did the problem start happening recently** (e.g. after updating to a new version of QBCore?) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of QBCore?** What's the most recent commit in which the problem doesn't happen? +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your setup: + +* **When was your QBCore last updated?** +* **What OS is the server running on**? +* **Which *extra* resources do you have installed?** + + +--- + + +### Suggesting Features / Enhancements + +This section guides you through submitting an enhancement suggestion for QBCore, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion. + +Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in feature request template, including the steps that you imagine you would take if the feature you're requesting existed. + +#### Before Submitting An Enhancement Suggestion + +* **Make sure it doesn't already exist.** Sounds silly, but there's a lot of features built in to qbcore that people don't realize so take a look through the docs and stuff to make sure it's not already there. +* **Check if there's already PR which provides that enhancement.** +* **Determine which resource the enhancement should be suggested in.** if it fits with another resource suggest it in that resource. if it would be it's own resource suggest it in the main qb-core repository. +* **Perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Aqbcore-framework)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you've determined which resource your enhancement suggestion is related to, create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the current behavior** and **explain which behavior you expected to see instead** and why. +* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of QBCore which the suggestion is related to. +* **Explain why this enhancement would be useful.** +* **Be creative and unique.** Stealing ideas from popular servers 1:1 detail isn't going to get accepted. + + +--- + + + +### Your First Code Contribution + +Unsure where to begin contributing to QBCore? You can start by looking through these `beginner` and `help-wanted` issues. + + + +--- + + +### Pull Requests + +The process described here has several goals: + +- Maintain QBCore's quality. +- Fix problems that are important to users. +- Engage the community in working toward the best possible QBCore. +- Enable a sustainable system for QBCore's maintainers to review contributions. + +Please follow these steps to have your contribution considered by the maintainers: + +1. Follow all instructions in The Pull Request template. +2. Follow the [styleguides](#styleguides). +3. Await review by the reviewer(s). + +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + + +--- + +## Styleguides + +### Git Commit Messages + +* Limit the first line to 72 characters or less. +* Reference issues and pull requests liberally after the first line. +* Consider starting the commit message with an applicable emoji: + * :art: `:art:` when improving the format/structure of the code + * :racehorse: `:racehorse:` when improving performance + * :memo: `:memo:` when writing docs + * :bug: `:bug:` when fixing a bug + * :fire: `:fire:` when removing code or files + * :white_check_mark: `:white_check_mark:` when adding tests + * :lock: `:lock:` when dealing with security + * :arrow_up: `:arrow_up:` when upgrading dependencies + * :arrow_down: `:arrow_down:` when downgrading dependencies + * :shirt: `:shirt:` when removing linter warnings + +### Lua Styleguide + +All lua code should be done using all the best practices of proper lua using the easiest to read yet fastest/most optimized methods of execution. + +- Use 4 Space indentation +- Aim for lua 5.4 (include `lua54 'yes'` in the fxmanifest.lua) +- Use `PlayerPedId()` instead of `GetPlayerPed(-1)` +- Use `#(vector3 - vector3)` instead of `GetDistanceBetweenCoords()` +- Don't create unnecessary threads. always try to find a better method of triggering events +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables +- For distance checking loops set longer waits if you're outside of a range +- Job specific loops should only run for players with that job, don't waste cycles +- When possible don't trust the client, esspecially with transactions +- Balance security and optimizations +- [Consider this Lua Performance guide](https://springrts.com/wiki/Lua_Performance) +- Use local varriables everywhere possible +- Make use of config options where it makes sense making features optional or customizable +- Instead of `table.insert(myTable, "Value")` use `myTable[#myTable + 1] = "Value"` +- Instead of `table.insert(ages, "bob", 30)` use `ages["bob"] = 30` + + +### JavaScript Styleguide + +- Use 4 Space indentation +- Don't repeat yourself.. if you're using the same operations in many different places convert them into a function with flexible variables. diff --git a/[qb]/qb-vehiclesales/.github/pull_request_template.md b/[qb]/qb-vehiclesales/.github/pull_request_template.md new file mode 100644 index 0000000..000f0f9 --- /dev/null +++ b/[qb]/qb-vehiclesales/.github/pull_request_template.md @@ -0,0 +1,10 @@ +**Describe Pull request** +First, make sure you've read and are following the contribution guidelines and style guide and your code reflects that. +Write up a clear and concise description of what your pull request adds or fixes and if it's an added feature explain why you think it should be included in the core. + +If your PR is to fix an issue mention that issue here + +**Questions (please complete the following information):** +- Have you personally loaded this code into an updated qbcore project and checked all it's functionality? [yes/no] (Be honest) +- Does your code fit the style guidelines? [yes/no] +- Does your PR fit the contribution guidelines? [yes/no] diff --git a/[qb]/qb-vehiclesales/.github/workflows/lint.yml b/[qb]/qb-vehiclesales/.github/workflows/lint.yml new file mode 100644 index 0000000..fb74fd6 --- /dev/null +++ b/[qb]/qb-vehiclesales/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint +on: [push, pull_request_target] +jobs: + lint: + name: Lint Resource + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Lint + uses: iLLeniumStudios/fivem-lua-lint-action@v2 + with: + capture: "junit.xml" + args: "-t --formatter JUnit" + extra_libs: mysql+polyzone+qblocales + - name: Generate Lint Report + if: always() + uses: mikepenz/action-junit-report@v3 + with: + report_paths: "**/junit.xml" + check_name: Linting Report + fail_on_failure: false \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/LICENSE b/[qb]/qb-vehiclesales/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/[qb]/qb-vehiclesales/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[qb]/qb-vehiclesales/README.md b/[qb]/qb-vehiclesales/README.md new file mode 100644 index 0000000..3a514e1 --- /dev/null +++ b/[qb]/qb-vehiclesales/README.md @@ -0,0 +1,347 @@ +# qb-vehiclesales +Used Car Sale for QB-Core Framework :blue_car: + +# License + + QBCore Framework + Copyright (C) 2021 Joshua Eger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + + +## Dependencies +- [qb-core](https://github.com/qbcore-framework/qb-core) +- [qb-garages](https://github.com/qbcore-framework/qb-garages) - Vehicle ownership +- [qb-phone](https://github.com/qbcore-framework/qb-phone) - For the e-mail +- [qb-logs](https://github.com/qbcore-framework/qb-logs) - Keep event logs + +## Screenshots +![Put Vehicle On Sale](https://imgur.com/bzE9e3o.png) +![Vehicle Sale Contract](https://imgur.com/A1ARcFV.png) +![Sell Vehicle To Dealer](https://imgur.com/zpEeBwk.png) +![Vehicle Sold Mail](https://imgur.com/vvz2UM3.png) +![Buy Vehicle](https://imgur.com/BEf5nDu.png) +![Vehicle Actions](https://imgur.com/HMuXtBd.png) + +## Features +- Ability to put your vehicle on sale for other players to buy +- Ability to take your vehicle back if it is not sold yet +- Ability to sell your vehicle to the dealer for a fixed amount + +## Installation +### Manual +- Download the script and put it in the `[qb]` directory. +- Import `qb-vehiclesales.sql` in your database +- Add the following code to your server.cfg/resouces.cfg +``` +ensure qb-core +ensure qb-garages +ensure qb-phone +ensure qb-logs +ensure qb-vehiclesales +``` + +## Configuration +- To adjust tax, go to qb-vehiclesales\server\main.lua row 85 and change "77" to your liking +``` +Config = Config or {} + +Config.SellVehicle = { -- Put Vehicle On Sale Marker + ["x"] = 1235.43, + ["y"] = 2730.95, + ["z"] = 37.91, +} + +Config.SellVehicleBack = { -- Sell Vehicle To Dealer Marker + ["x"] = 1235.1, + ["y"] = 2740.7, + ["z"] = 37.68, +} + +Config.BuyVehicle = { -- Location Bought Vehicle Will Be Spawned + ["x"] = 1213.31, + ["y"] = 2735.4, + ["z"] = 38.27, + ["h"] = 182.5 + } + +Config.OccasionSlots = { -- Vehicle Display Places + { + ["x"] = 1237.07, + ["y"] = 2699.0, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1232.98, + ["y"] = 2698.92, + ["z"] = 38.27, + ["h"] = 2.5 + }, + + { + ["x"] = 1228.9, + ["y"] = 2698.78, + ["z"] = 38.27, + ["h"] = 3.5 + }, + + { + ["x"] = 1224.9, + ["y"] = 2698.51, + ["z"] = 38.27, + ["h"] = 2.5 + }, + + { + ["x"] = 1220.93, + ["y"] = 2698.28, + ["z"] = 38.27, + ["h"] = 2.5 + }, + + + { + ["x"] = 1216.97, + ["y"] = 2698.05, + ["z"] = 38.27, + ["h"] = 0.5 + }, + + { + ["x"] = 1216.67, + ["y"] = 2709.21, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1220.67, + ["y"] = 2709.26, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1224.53, + ["y"] = 2709.27, + ["z"] = 38.27, + ["h"] = 2.5 + }, + + { + ["x"] = 1228.52, + ["y"] = 2709.42, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1232.53, + ["y"] = 2709.49, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1236.71, + ["y"] = 2709.51, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1216.41, + ["y"] = 2717.99, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1220.39, + ["y"] = 2718.0, + ["z"] = 38.27, + ["h"] = 0.5 + }, + + { + ["x"] = 1224.35, + ["y"] = 2718.07, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1228.41, + ["y"] = 2718.22, + ["z"] = 38.27, + ["h"] = 1.5 + }, + + { + ["x"] = 1249.63, + ["y"] = 2707.84, + ["z"] = 38.27, + ["h"] = 99.5 + }, + + { + ["x"] = 1248.92, + ["y"] = 2712.25, + ["z"] = 38.27, + ["h"] = 101.5 + }, + + { + ["x"] = 1247.3, + ["y"] = 2716.59, + ["z"] = 38.27, + ["h"] = 120.5 + }, + + { + ["x"] = 1244.09, + ["y"] = 2720.4, + ["z"] = 38.27, + ["h"] = 149.5 + }, + + { + ["x"] = 1239.93, + ["y"] = 2722.39, + ["z"] = 38.27, + ["h"] = 163.5 + }, + + { + ["x"] = 1248.28, + ["y"] = 2727.41, + ["z"] = 38.53, + ["h"] = 338.5 + }, + + + { + ["x"] = 1251.84, + ["y"] = 2725.65, + ["z"] = 38.52, + ["h"] = 331.5 + }, + + + { + ["x"] = 1255.19, + ["y"] = 2723.21, + ["z"] = 38.44, + ["h"] = 309.5 + }, + + + { + ["x"] = 1257.28, + ["y"] = 2719.77, + ["z"] = 38.49, + ["h"] = 296.5 + }, + + + { + ["x"] = 1258.43, + ["y"] = 2716.2, + ["z"] = 38.46, + ["h"] = 285.5 + }, + + + { + ["x"] = 1258.92, + ["y"] = 2712.28, + ["z"] = 38.37, + ["h"] = 270.5 + }, + + + { + ["x"] = 1258.9, + ["y"] = 2708.3, + ["z"] = 38.21, + ["h"] = 269.5 + }, + + + { + ["x"] = 1258.87, + ["y"] = 2704.21, + ["z"] = 38.05, + ["h"] = 271.5 + }, + + + { + ["x"] = 1269.11, + ["y"] = 2694.67, + ["z"] = 37.85, + ["h"] = 184.5 + }, + + + { + ["x"] = 1273.53, + ["y"] = 2694.98, + ["z"] = 37.87, + ["h"] = 184.5 + }, + + + { + ["x"] = 1277.56, + ["y"] = 2695.43, + ["z"] = 37.87, + ["h"] = 183.5 + }, + + + { + ["x"] = 1281.53, + ["y"] = 2695.67, + ["z"] = 37.88, + ["h"] = 186.5 + }, + + + { + ["x"] = 1285.51, + ["y"] = 2696.02, + ["z"] = 37.88, + ["h"] = 185.5 + }, + + + { + ["x"] = 1289.74, + ["y"] = 2696.27, + ["z"] = 37.88, + ["h"] = 182.5 + }, + + + { + ["x"] = 1293.75, + ["y"] = 2696.47, + ["z"] = 37.86, + ["h"] = 184.5 + }, +} +``` diff --git a/[qb]/qb-vehiclesales/client/main.lua b/[qb]/qb-vehiclesales/client/main.lua new file mode 100644 index 0000000..7937e3a --- /dev/null +++ b/[qb]/qb-vehiclesales/client/main.lua @@ -0,0 +1,470 @@ +local QBCore = exports['qb-core']:GetCoreObject() +local Zone = nil +local TextShown = false +local AcitveZone = {} +local CurrentVehicle = {} +local SpawnZone = {} +local EntityZones = {} +local occasionVehicles = {} + +-- Functions + +local function spawnOccasionsVehicles(vehicles) + if Zone then + local oSlot = Config.Zones[Zone].VehicleSpots + if not occasionVehicles[Zone] then occasionVehicles[Zone] = {} end + if vehicles then + for i = 1, #vehicles, 1 do + local model = GetHashKey(vehicles[i].model) + RequestModel(model) + while not HasModelLoaded(model) do + Wait(0) + end + occasionVehicles[Zone][i] = { + car = CreateVehicle(model, oSlot[i].x, oSlot[i].y, oSlot[i].z, false, false), + loc = vector3(oSlot[i].x, oSlot[i].y, oSlot[i].z), + price = vehicles[i].price, + owner = vehicles[i].seller, + model = vehicles[i].model, + plate = vehicles[i].plate, + oid = vehicles[i].occasionid, + desc = vehicles[i].description, + mods = vehicles[i].mods + } + + QBCore.Functions.SetVehicleProperties(occasionVehicles[Zone][i].car, json.decode(occasionVehicles[Zone][i].mods)) + + SetModelAsNoLongerNeeded(model) + SetVehicleOnGroundProperly(occasionVehicles[Zone][i].car) + SetEntityInvincible(occasionVehicles[Zone][i].car,true) + SetEntityHeading(occasionVehicles[Zone][i].car, oSlot[i].w) + SetVehicleDoorsLocked(occasionVehicles[Zone][i].car, 3) + SetVehicleNumberPlateText(occasionVehicles[Zone][i].car, occasionVehicles[Zone][i].oid) + FreezeEntityPosition(occasionVehicles[Zone][i].car,true) + if Config.UseTarget then + if not EntityZones then EntityZones = {} end + EntityZones[i] = exports['qb-target']:AddTargetEntity(occasionVehicles[Zone][i].car, { + options = { + { + type = "client", + event = "qb-vehiclesales:client:OpenContract", + icon = "fas fa-car", + label = Lang:t("menu.view_contract"), + Contract = i + } + }, + distance = 2.0 + }) + end + end + end + end +end + +local function despawnOccasionsVehicles() + if not Zone then return end + local oSlot = Config.Zones[Zone].VehicleSpots + for i = 1, #oSlot, 1 do + local loc = oSlot[i] + local oldVehicle = GetClosestVehicle(loc.x, loc.y, loc.z, 1.3, 0, 70) + + if oldVehicle then + QBCore.Functions.DeleteVehicle(oldVehicle) + end + + if EntityZones[i] and Config.UseTarget then + exports['qb-target']:RemoveZone(EntityZones[i]) + end + end + EntityZones = {} +end + +local function openSellContract(bool) + local pData = QBCore.Functions.GetPlayerData() + + SetNuiFocus(bool, bool) + SendNUIMessage({ + action = "sellVehicle", + showTakeBackOption = false, + bizName = Config.Zones[Zone].BusinessName, + sellerData = { + firstname = pData.charinfo.firstname, + lastname = pData.charinfo.lastname, + account = pData.charinfo.account, + phone = pData.charinfo.phone + }, + plate = QBCore.Functions.GetPlate(GetVehiclePedIsUsing(PlayerPedId())) + }) +end + +local function openBuyContract(sellerData, vehicleData) + local pData = QBCore.Functions.GetPlayerData() + SetNuiFocus(true, true) + SendNUIMessage({ + action = "buyVehicle", + showTakeBackOption = sellerData.charinfo.firstname == pData.charinfo.firstname and sellerData.charinfo.lastname == pData.charinfo.lastname, + bizName = Config.Zones[Zone].BusinessName, + sellerData = { + firstname = sellerData.charinfo.firstname, + lastname = sellerData.charinfo.lastname, + account = sellerData.charinfo.account, + phone = sellerData.charinfo.phone + }, + vehicleData = { + desc = vehicleData.desc, + price = vehicleData.price + }, + plate = vehicleData.plate + }) +end + +local function sellVehicleWait(price) + DoScreenFadeOut(250) + Wait(250) + QBCore.Functions.DeleteVehicle(GetVehiclePedIsIn(PlayerPedId())) + Wait(1500) + DoScreenFadeIn(250) + QBCore.Functions.Notify(Lang:t('success.car_up_for_sale', { value = price }), 'success') + PlaySound(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0, 0, 1) +end + +local function SellData(data, model) + QBCore.Functions.TriggerCallback("qb-vehiclesales:server:CheckModelName",function(DataReturning) + local vehicleData = {} + vehicleData.ent = GetVehiclePedIsUsing(PlayerPedId()) + vehicleData.model = DataReturning + vehicleData.plate = model + vehicleData.mods = QBCore.Functions.GetVehicleProperties(vehicleData.ent) + vehicleData.desc = data.desc + TriggerServerEvent('qb-occasions:server:sellVehicle', data.price, vehicleData) + sellVehicleWait(data.price) + end, model) +end + +local listen = false +local function Listen4Control(spot) -- Uses this to listen for controls to open various menus. + listen = true + CreateThread(function() + while listen do + if IsControlJustReleased(0, 38) then -- E + if spot then + local data = {Contract = spot} + TriggerEvent('qb-vehiclesales:client:OpenContract', data) + else + if IsPedInAnyVehicle(PlayerPedId(), false) then + listen = false + TriggerEvent('qb-occasions:client:MainMenu') + --TriggerEvent('qb-vehiclesales:client:SellVehicle') + else + QBCore.Functions.Notify(Lang:t("error.not_in_veh"), "error", 4500) + end + end + end + Wait(0) + end + end) +end + +---- ** Main Zone Functions ** ---- + +local function CreateZones() + for k, v in pairs(Config.Zones) do + local SellSpot = PolyZone:Create(v.PolyZone, { + name = k, + minZ = v.MinZ, + maxZ = v.MaxZ, + debugPoly = false + }) + + SellSpot:onPlayerInOut(function(isPointInside) + if isPointInside and Zone ~= k then + Zone = k + QBCore.Functions.TriggerCallback('qb-occasions:server:getVehicles', function(vehicles) + despawnOccasionsVehicles() + spawnOccasionsVehicles(vehicles) + end) + else + despawnOccasionsVehicles() + Zone = nil + end + end) + AcitveZone[k] = SellSpot + end +end + +local function DeleteZones() + for k in pairs(AcitveZone) do + AcitveZone[k]:destroy() + end + AcitveZone = {} +end + +local function IsCarSpawned(Car) + local bool = false + + if occasionVehicles then + for k in pairs(occasionVehicles[Zone]) do + if k == Car then + bool = true + break + end + end + end + return bool +end + +-- NUI Callbacks + +RegisterNUICallback('sellVehicle', function(data, cb) + local plate = QBCore.Functions.GetPlate(GetVehiclePedIsUsing(PlayerPedId())) --Getting the plate and sending to the function + SellData(data,plate) + cb('ok') +end) + +RegisterNUICallback('close', function(_, cb) + SetNuiFocus(false, false) + cb('ok') +end) + +RegisterNUICallback('buyVehicle', function(_, cb) + TriggerServerEvent('qb-occasions:server:buyVehicle', CurrentVehicle) + cb('ok') +end) + +RegisterNUICallback('takeVehicleBack', function(_, cb) + TriggerServerEvent('qb-occasions:server:ReturnVehicle', CurrentVehicle) + cb('ok') +end) + +-- Events + +RegisterNetEvent('qb-occasions:client:BuyFinished', function(vehdata) + local vehmods = json.decode(vehdata.mods) + + DoScreenFadeOut(250) + Wait(500) + QBCore.Functions.TriggerCallback('QBCore:Server:SpawnVehicle', function(netId) + local veh = NetToVeh(netId) + SetVehicleNumberPlateText(veh, vehdata.plate) + SetEntityHeading(veh, Config.Zones[Zone].BuyVehicle.w) + TaskWarpPedIntoVehicle(PlayerPedId(), veh, -1) + SetVehicleFuelLevel(veh, 100) + QBCore.Functions.Notify(Lang:t('success.vehicle_bought'), "success", 2500) + TriggerEvent("vehiclekeys:client:SetOwner", vehdata.plate) + SetVehicleEngineOn(veh, true, true) + Wait(500) + QBCore.Functions.SetVehicleProperties(veh, vehmods) + end, vehdata.model, Config.Zones[Zone].BuyVehicle, true) + Wait(500) + DoScreenFadeIn(250) + CurrentVehicle = {} +end) + +RegisterNetEvent('qb-occasions:client:SellBackCar', function() + local ped = PlayerPedId() + if IsPedInAnyVehicle(ped, false) then + local vehicleData = {} + local vehicle = GetVehiclePedIsIn(ped, false) + vehicleData.model = GetEntityModel(vehicle) + vehicleData.plate = GetVehicleNumberPlateText(vehicle) + QBCore.Functions.TriggerCallback('qb-garage:server:checkVehicleOwner', function(owned, balance) + if owned then + if balance < 1 then + TriggerServerEvent('qb-occasions:server:sellVehicleBack', vehicleData) + QBCore.Functions.DeleteVehicle(vehicle) + else + QBCore.Functions.Notify(Lang:t('error.finish_payments'), 'error', 3500) + end + else + QBCore.Functions.Notify(Lang:t('error.not_your_vehicle'), 'error', 3500) + end + end, vehicleData.plate) + else + QBCore.Functions.Notify(Lang:t("error.not_in_veh"), "error", 4500) + end +end) + +RegisterNetEvent('qb-occasions:client:ReturnOwnedVehicle', function(vehdata) + local vehmods = json.decode(vehdata.mods) + DoScreenFadeOut(250) + Wait(500) + QBCore.Functions.TriggerCallback('QBCore:Server:SpawnVehicle', function(netId) + local veh = NetToVeh(netId) + SetVehicleNumberPlateText(veh, vehdata.plate) + SetEntityHeading(veh, Config.Zones[Zone].BuyVehicle.w) + TaskWarpPedIntoVehicle(PlayerPedId(), veh, -1) + SetVehicleFuelLevel(veh, 100) + QBCore.Functions.Notify(Lang:t('info.vehicle_returned')) + TriggerEvent("vehiclekeys:client:SetOwner", vehdata.plate) + SetVehicleEngineOn(veh, true, true) + Wait(500) + QBCore.Functions.SetVehicleProperties(veh, vehmods) + end, vehdata.model, Config.Zones[Zone].BuyVehicle, true) + Wait(500) + DoScreenFadeIn(250) + CurrentVehicle = {} +end) + +RegisterNetEvent('qb-occasion:client:refreshVehicles', function() + if Zone then + QBCore.Functions.TriggerCallback('qb-occasions:server:getVehicles', function(vehicles) + despawnOccasionsVehicles() + spawnOccasionsVehicles(vehicles) + end) + end +end) + +RegisterNetEvent('qb-vehiclesales:client:SellVehicle', function() + local VehiclePlate = QBCore.Functions.GetPlate(GetVehiclePedIsIn(PlayerPedId())) + QBCore.Functions.TriggerCallback('qb-garage:server:checkVehicleOwner', function(owned, balance) + if owned then + if balance < 1 then + QBCore.Functions.TriggerCallback('qb-occasions:server:getVehicles', function(vehicles) + if vehicles == nil or #vehicles < #Config.Zones[Zone].VehicleSpots then + openSellContract(true) + else + QBCore.Functions.Notify(Lang:t('error.no_space_on_lot'), 'error', 3500) + end + end) + else + QBCore.Functions.Notify(Lang:t('error.finish_payments'), 'error', 3500) + end + else + QBCore.Functions.Notify(Lang:t('error.not_your_vehicle'), 'error', 3500) + end + end, VehiclePlate) +end) + +RegisterNetEvent('qb-vehiclesales:client:OpenContract', function(data) + CurrentVehicle = occasionVehicles[Zone][data.Contract] + if CurrentVehicle then + QBCore.Functions.TriggerCallback('qb-occasions:server:getSellerInformation', function(info) + if info then + info.charinfo = json.decode(info.charinfo) + else + info = {} + info.charinfo = { + firstname = Lang:t('charinfo.firstname'), + lastname = Lang:t('charinfo.lastname'), + account = Lang:t('charinfo.account'), + phone = Lang:t('charinfo.phone') + } + end + + openBuyContract(info, CurrentVehicle) + end, CurrentVehicle.owner) + else + QBCore.Functions.Notify(Lang:t("error.not_for_sale"), 'error', 7500) + end +end) + +RegisterNetEvent('qb-occasions:client:MainMenu', function() + local MainMenu = { + { + isMenuHeader = true, + header = Config.Zones[Zone].BusinessName + }, + { + header = Lang:t("menu.sell_vehicle"), + txt = Lang:t("menu.sell_vehicle_help"), + params = { + event = 'qb-vehiclesales:client:SellVehicle', + } + }, + { + header = Lang:t("menu.sell_back"), + txt = Lang:t("menu.sell_back_help"), + params = { + event = 'qb-occasions:client:SellBackCar', + } + } + } + + exports['qb-menu']:openMenu(MainMenu) +end) + +-- Threads + +CreateThread(function() + for _, cars in pairs(Config.Zones) do + local OccasionBlip = AddBlipForCoord(cars.SellVehicle.x, cars.SellVehicle.y, cars.SellVehicle.z) + SetBlipSprite (OccasionBlip, 326) + SetBlipDisplay(OccasionBlip, 4) + SetBlipScale (OccasionBlip, 0.75) + SetBlipAsShortRange(OccasionBlip, true) + SetBlipColour(OccasionBlip, 3) + BeginTextCommandSetBlipName("STRING") + AddTextComponentSubstringPlayerName(Lang:t('info.used_vehicle_lot')) + EndTextCommandSetBlipName(OccasionBlip) + end +end) + +CreateThread(function() + for k, cars in pairs(Config.Zones) do + SpawnZone[k] = CircleZone:Create(vector3(cars.SellVehicle.x, cars.SellVehicle.y, cars.SellVehicle.z), 3.0, { + name="OCSell"..k, + debugPoly = false, + }) + + SpawnZone[k]:onPlayerInOut(function(isPointInside) + if isPointInside and IsPedInAnyVehicle(PlayerPedId(), false) then + exports['qb-core']:DrawText(Lang:t("menu.interaction"), 'left') + TextShown = true + Listen4Control() + else + listen = false + if TextShown then + TextShown = false + exports['qb-core']:HideText() + end + end + end) + if not Config.UseTarget then + for k2, v in pairs(Config.Zones[k].VehicleSpots) do + local VehicleZones = BoxZone:Create(vector3(v.x, v.y, v.z), 4.3, 3.6, { + name="VehicleSpot"..k..k2, + debugPoly = false, + minZ = v.z-2, + maxZ = v.z+2, + }) + + VehicleZones:onPlayerInOut(function(isPointInside) + if isPointInside and IsCarSpawned(k2) then + exports['qb-core']:DrawText(Lang:t("menu.view_contract_int"), 'left') + TextShown = true + Listen4Control(k2) + else + listen = false + if TextShown then + TextShown = false + exports['qb-core']:HideText() + end + end + end) + end + end + end +end) + +---- ** Mostly just to ensure you can restart resources live without issues, also improves the code slightly. ** ---- + +RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() + CreateZones() +end) + +RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() + DeleteZones() +end) + +AddEventHandler('onResourceStart', function(resourceName) + if GetCurrentResourceName() == resourceName then + CreateZones() + end +end) + +AddEventHandler('onResourceStop', function(resourceName) + if GetCurrentResourceName() == resourceName then + DeleteZones() + despawnOccasionsVehicles() + end +end) diff --git a/[qb]/qb-vehiclesales/config.lua b/[qb]/qb-vehiclesales/config.lua new file mode 100644 index 0000000..1c7b7ec --- /dev/null +++ b/[qb]/qb-vehiclesales/config.lua @@ -0,0 +1,48 @@ +Config = Config or {} + +Config.UseTarget = GetConvar('UseTarget', 'false') == 'true' + +Config.Zones = { + ["SandyOccasions"] = { + BusinessName = "Vehicle Sales Contract - Larry's Vehicle Sales", + SellVehicle = vector4(1235.61, 2733.44, 37.4, 0.42), + BuyVehicle = vector4(1213.31, 2735.4, 38.27, 182.5), + + PolyZone = { + vector2(1338.3748779297, 2645.0153808594), + vector2(1098.9381103516, 2621.7487792969), + vector2(1117.9478759766, 2822.0729980469), + vector2(1370.98828125, 2859.197265625) + }, + MinZ = 36.0, + MaxZ = 64.0, + + VehicleSpots = { + vector4(1237.07, 2699, 38.27, 1.5), + vector4(1232.98, 2698.92, 38.27, 2.5), + vector4(1228.9, 2698.78, 38.27, 3.5), + vector4(1224.9, 2698.51, 38.27, 2.5), + vector4(1220.93, 2698.28, 38.27, 2.5), + vector4(1216.97, 2698.05, 38.27, 0.5), + vector4(1216.67, 2709.21, 38.27, 1.5), + vector4(1220.67, 2709.26, 38.27, 1.5), + vector4(1224.53, 2709.27, 38.27, 2.5), + vector4(1228.52, 2709.42, 38.27, 1.5), + vector4(1232.53, 2709.49, 38.27, 1.5), + vector4(1236.71, 2709.51, 38.27, 1.6), + vector4(1216.41, 2717.99, 38.27, 1.5), + vector4(1220.39, 2718, 38.27, 0.5), + vector4(1224.35, 2718.07, 38.27, 1.5), + vector4(1228.41, 2718.22, 38.27, 1.5), + vector4(1249.63, 2707.84, 38.27, 99.5), + vector4(1248.92, 2712.25, 38.27, 101.5), + vector4(1247.3, 2716.59, 38.27, 120.5), + vector4(1244.09, 2720.4, 38.27, 149.5), + vector4(1239.93, 2722.39, 38.27, 163.5), + vector4(1248.28, 2727.41, 38.53, 338.5), + vector4(1251.84, 2725.65, 38.52, 331.5), + vector4(1255.19, 2723.21, 38.44, 309.5), + vector4(1257.28, 2719.77, 38.49, 296.5), + } + } +} diff --git a/[qb]/qb-vehiclesales/fxmanifest.lua b/[qb]/qb-vehiclesales/fxmanifest.lua new file mode 100644 index 0000000..8fd7368 --- /dev/null +++ b/[qb]/qb-vehiclesales/fxmanifest.lua @@ -0,0 +1,37 @@ +fx_version 'cerulean' +game 'gta5' + +description 'QB-VehicleSales' +version '1.2.0' + +ui_page 'html/ui.html' + +shared_scripts { + 'config.lua', + '@qb-core/shared/locale.lua', + 'locales/en.lua', + 'locales/*.lua' +} + +client_scripts { + '@PolyZone/client.lua', + '@PolyZone/BoxZone.lua', + '@PolyZone/EntityZone.lua', + '@PolyZone/CircleZone.lua', + '@PolyZone/ComboZone.lua', + 'client/main.lua' +} +server_scripts { + '@oxmysql/lib/MySQL.lua', + 'server/main.lua' +} + +files { + 'html/logo.svg', + 'html/ui.css', + 'html/ui.html', + 'html/vue.min.js', + 'html/ui.js', +} + +lua54 'yes' \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/html/logo.svg b/[qb]/qb-vehiclesales/html/logo.svg new file mode 100644 index 0000000..2f082d8 --- /dev/null +++ b/[qb]/qb-vehiclesales/html/logo.svg @@ -0,0 +1,26 @@ + + + + + + + + + RV ASSOCIATION + LOS SANTOS + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/html/ui.css b/[qb]/qb-vehiclesales/html/ui.css new file mode 100644 index 0000000..101f46c --- /dev/null +++ b/[qb]/qb-vehiclesales/html/ui.css @@ -0,0 +1,339 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); +@import url('https://fonts.googleapis.com/css?family=Bebas Neue'); +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;500&display=swap'); + +body { + overflow: hidden; + display: none; +} + +.container { + overflow: hidden; +} + +.centred { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + +} + +.centred-h { + position: fixed; + left: 28%; + transform: translate(-50%, 0%); +} + +#sell-contract { + max-width: 700px; + max-height: 70vh; + width: 30vw; + height: 75vh; + background-color: white; + padding: 10px; + background: rgb(23 23 23); + border-radius: 8px; + box-shadow: 0rem 0rem 0.1rem 0.05rem #000000; +} + +#buy-contract { + position: absolute; + width: 165vh; + margin: 0 auto; + left: 0; + right: 0; + user-select: none; +} + +#buy-vehicle-desc { + position: absolute; + left: 66.8vh; + bottom: 48vh; + font-family: 'Poppins'; + font-size: 13px; + width: 45vh; + overflow: hidden; + word-wrap: break-word; +} + +.vehicle-description:hover { + color: #ffffff; +} + +.vehicle-description:focus { + color: #ffffff; +} + +.vehicle-description::placeholder { + text-align: left; + color:white +} + +#sell-vehicle { + width: 100px; + height: 35px; + background: rgb(30 30 30); + box-shadow: -0.072rem 0rem 0.6rem 0.05rem #000000; + border-radius: 25px 0px 0px 25px; + margin-top: 100px; + margin-left: 452px; + left: 0; + right: 0; + transition: all 0.1s ease-in-out; +} + +#sell-vehicle:hover { + background: #dc143c; + transition: all 0.1s ease-in-out; +} + +#sell-vehicle > p { + text-align: center; + line-height: 3.5vh; + color: white; + font-size: 20px; + font-family: "Poppins", sans-serif !important; + font-weight: 700; + text-transform: uppercase; + cursor: pointer; +} + +input.vehicle-sell-price::placeholder { + color: white; + opacity: 1; + +} + +#buy-vehicle { + +width: 250px; + +height: 35px; + +background: rgb(30 30 30); + +box-shadow: -0.072rem 0rem 0.6rem 0.05rem #000000; + +border-radius: 25px; + +transition: all 0.1s ease-in-out; + +bottom: 50vh; + +margin:auto; + +user-select: none; +} + +#buy-vehicle-back { + width: 250px; + height: 35px; + background: rgb(30 30 30); + box-shadow: -0.072rem 0rem 0.6rem 0.05rem #000000; + border-radius: 25px; + transition: all 0.1s ease-in-out; + bottom: 88vh; + margin: 0 auto; + user-select: none; +} + +#buy-vehicle:hover { + border-radius: 25px; + background: #dc143c; + transition: all 0.1s ease-in-out; +} + + #buy-vehicle-back:hover { + border-radius: 25px; + background: #dc143c; + transition: all 0.1s ease-in-out; + +} + +#buy-vehicle > p { + text-align: center; + line-height: 3.5vh; + color: rgb(255, 255, 255); + font-size: 19px; + font-family: "Poppins", sans-serif !important; + text-transform: uppercase; + cursor: pointer; +} + +#buy-vehicle-back > p { + text-align: center; + line-height: 3.5vh; + color: rgb(255, 255, 255); + font-size: 19px; + font-family: "Poppins", sans-serif !important; + text-transform: uppercase; + cursor: pointer; +} + + +#form-header { + height: 120px; + border-bottom: solid; + border-color: #ffffffde; + border-width: 2px; + margin-bottom: 30px; + display: flex; + flex-direction: row; +} + +.v-centred-container { + position: relative; + +} + +.vertical-center { + margin: 0; + position: absolute; + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} + +.logo { + width: 20%; + text-align: center; + padding-top: 5px; + padding-bottom: 10px; +} + +#biz-name { + width: 68%; + text-align: center; + font-family: 'Bebas Neue'; + font-size: 35px; + font-weight: bold; + color: #ffffff; + letter-spacing: 3.0px; +} + +#the-form { + margin-top: 45px; + width: 85%; + height: 72%; +} + +ul { + list-style-type: none; +} + +.errors { + font-family: 'Poppins'; + background: #f7a261; + width: 50%; + border-radius: 25px; + bottom: -10vh; + left:25vh; +} + +.form-entry { + margin-top: 60px; + height: 2%; +} + +.form-entry input { + width: 90.5%; + margin-top: 1px; + border-radius: 0.1px; + outline:none; + border-top: none; + border-left: none; + border-right: none; + border-bottom: solid rgb(255, 255, 255) 1.5px; +} + +.form-field { + font-family: 'Poppins'; + font-size: 16px; + padding-top: 3px; + border-top: solid; + color: rgb(170, 170, 170); + border-color: #d8d8d8b0; + border-width: 1px; +} + +.form-field-summary { + font-family: 'Poppins'; + font-size: 14px; + color: #ffffff; +} + +.vehicle-description { + outline: none; + background: none; + font-family: 'Poppins'; + overflow: hidden; + display: inline-block; + text-overflow: ellipsis; + white-space: nowrap; + color: white +} + +.vehicle-sell-price { + font-size: 12px; + outline: none; + color: rgb(255, 255, 255); + background-color: #00000000; +} + +table { + font-size: 18px; + bottom: -500px; + font-family: 'Poppins'; + color: white; +} + +.pricing-table { + margin-bottom: 480px; + margin-left: -50px; + width: 50%; +} + + +.table-field-heading { + font-weight:500; +} + +#buy-price { + position: absolute; + left: 87vh; + bottom: 35.8vh; + font-family: 'Poppins'; + font-size: 12px; +} + +#buy-seller-name { + position: absolute; + left: 66.8vh; + top: 21.5vh; + font-family: 'Poppins'; + font-size: 14px; +} + +#buy-seller-banknr { + position: absolute; + left: 66.8vh; + top: 28vh; + font-family: 'Poppins'; + font-size: 14px; +} + +#buy-seller-telnr { + position: absolute; + left: 66.8vh; + top: 34.2vh; + font-family: 'Poppins'; + font-size: 14px; +} + +#buy-vehicle-plate { + position: absolute; + left: 66.8vh; + top: 40.5vh; + font-family: 'Poppins'; +} diff --git a/[qb]/qb-vehiclesales/html/ui.html b/[qb]/qb-vehiclesales/html/ui.html new file mode 100644 index 0000000..d6237bb --- /dev/null +++ b/[qb]/qb-vehiclesales/html/ui.html @@ -0,0 +1,89 @@ + + + + + + + + qb-vehiclesales + + +
+
+
+ +
+
{{ bizName }}
+
+
+ +
+
+
{{ sellerName }}
+ (Name of Seller) +
+
+
{{ bankAccount }}
+ (Bank Account Number) +
+
+
{{ phoneNumber }}
+ (Telephone Number) +
+
+
{{ licensePlate }}
+ (Vehicle License Plate) +
+
+
+ +
+ (Vehicle Description. max 500) +
{{ vehicleDescription }}
+
+ +
+ + + + + +
Selling Price: + +
Tax: $ {{ tax }}
Mosely's Cut: $ {{ mosleys }}
Total Yield: $ {{ total }}
+
+ +
+ + +
Vehicle Price: $ {{ sellPrice }}
+
+ +

Sell

+

Buy

+

Take Back

+ +
+
    +
  • {{ error }}
  • +
+
+
+ +
+ +
+ + + + + diff --git a/[qb]/qb-vehiclesales/html/ui.js b/[qb]/qb-vehiclesales/html/ui.js new file mode 100644 index 0000000..8b215e3 --- /dev/null +++ b/[qb]/qb-vehiclesales/html/ui.js @@ -0,0 +1,130 @@ +var app = new Vue({ + el: "#app", + data: { + mode: "", + bizName: "", + sellerName: "", + bankAccount: "", + phoneNumber: "", + licensePlate: "", + vehicleDescription: "", + sellPrice: "", + showTakeBackOption: false, + errors: [] + }, + methods: { + sell(sellPrice) { + this.errors = []; + if (this.sellPrice != "") { + if (!isNaN(sellPrice)) { + const requestOptions = { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + price: this.sellPrice, + desc: this.vehicleDescription + }) + }; + fetch("https://qb-vehiclesales/sellVehicle", requestOptions); + this.close(); + } else { + this.errors.push("Price must be a numeric value only"); + } + } else { + this.errors.push("Sale price required") + } + }, + buy() { + const requestOptions = { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({}) + }; + fetch("https://qb-vehiclesales/buyVehicle", requestOptions); + this.close(); + }, + takeBack() { + const requestOptions = { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({}) + }; + fetch("https://qb-vehiclesales/takeVehicleBack", requestOptions); + this.close(); + }, + close() { + const requestOptions = { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({}) + }; + fetch("https://qb-vehiclesales/close", requestOptions); + + this.resetForm(); + this.hideForm(); + + }, + resetForm() { + this.sellerName = ""; + this.bankAccount = ""; + this.phoneNumber = ""; + this.licensePlate = ""; + this.vehicleDescription = ""; + this.sellPrice = ""; + this.errors = []; + }, + hideForm() { + document.body.style.display = "none"; + } + + }, + computed: { + tax() { + return (this.sellPrice / 100 * 19).toFixed(0); + }, + mosleys() { + return (this.sellPrice / 100 * 4).toFixed(0); + }, + total() { + return (this.sellPrice / 100 * 77).toFixed(0); + } + } + +}); + +function setupForm(data) { + if(data.action === "sellVehicle" || data.action === "buyVehicle") { + document.body.style.display = "block"; + } + + app.mode = data.action; + + app.showTakeBackOption = data.showTakeBackOption; + + app.bizName = data.bizName; + + app.sellerName = data.sellerData.firstname + " " + data.sellerData.lastname; + app.bankAccount = data.sellerData.account; + app.phoneNumber = data.sellerData.phone; + app.licensePlate = data.plate; + + if (data.action === "buyVehicle") { + app.vehicleDescription = data.vehicleData.desc; + app.sellPrice = data.vehicleData.price; + } +} + +document.onreadystatechange = () => { + if (document.readyState === "complete") { + window.addEventListener("message", (event) => { + this.setupForm(event.data); + }); + } +}; + +/* Handle escape key press to close the menu */ +document.onkeyup = function (data) { + if (data.key != "Escape") return; + + app.close(); +}; diff --git a/[qb]/qb-vehiclesales/html/vue.min.js b/[qb]/qb-vehiclesales/html/vue.min.js new file mode 100644 index 0000000..d998ff7 --- /dev/null +++ b/[qb]/qb-vehiclesales/html/vue.min.js @@ -0,0 +1,6 @@ +/*! + * Vue.js v2.6.14 + * (c) 2014-2021 Evan You + * Released under the MIT License. + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Vue=t()}(this,function(){"use strict";var e=Object.freeze({});function t(e){return null==e}function n(e){return null!=e}function r(e){return!0===e}function i(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function o(e){return null!==e&&"object"==typeof e}var a=Object.prototype.toString;function s(e){return"[object Object]"===a.call(e)}function c(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function u(e){return n(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function l(e){return null==e?"":Array.isArray(e)||s(e)&&e.toString===a?JSON.stringify(e,null,2):String(e)}function f(e){var t=parseFloat(e);return isNaN(t)?e:t}function p(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}var m=Object.prototype.hasOwnProperty;function y(e,t){return m.call(e,t)}function g(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var _=/-(\w)/g,b=g(function(e){return e.replace(_,function(e,t){return t?t.toUpperCase():""})}),$=g(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),w=/\B([A-Z])/g,C=g(function(e){return e.replace(w,"-$1").toLowerCase()});var x=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function k(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function A(e,t){for(var n in t)e[n]=t[n];return e}function O(e){for(var t={},n=0;n0,Z=J&&J.indexOf("edge/")>0,G=(J&&J.indexOf("android"),J&&/iphone|ipad|ipod|ios/.test(J)||"ios"===K),X=(J&&/chrome\/\d+/.test(J),J&&/phantomjs/.test(J),J&&J.match(/firefox\/(\d+)/)),Y={}.watch,Q=!1;if(V)try{var ee={};Object.defineProperty(ee,"passive",{get:function(){Q=!0}}),window.addEventListener("test-passive",null,ee)}catch(e){}var te=function(){return void 0===B&&(B=!V&&!z&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),B},ne=V&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function re(e){return"function"==typeof e&&/native code/.test(e.toString())}var ie,oe="undefined"!=typeof Symbol&&re(Symbol)&&"undefined"!=typeof Reflect&&re(Reflect.ownKeys);ie="undefined"!=typeof Set&&re(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ae=S,se=0,ce=function(){this.id=se++,this.subs=[]};ce.prototype.addSub=function(e){this.subs.push(e)},ce.prototype.removeSub=function(e){h(this.subs,e)},ce.prototype.depend=function(){ce.target&&ce.target.addDep(this)},ce.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t-1)if(o&&!y(i,"default"))a=!1;else if(""===a||a===C(e)){var c=Re(String,i.type);(c<0||s0&&(ct((u=e(u,(a||"")+"_"+c))[0])&&ct(f)&&(s[l]=he(f.text+u[0].text),u.shift()),s.push.apply(s,u)):i(u)?ct(f)?s[l]=he(f.text+u):""!==u&&s.push(he(u)):ct(u)&&ct(f)?s[l]=he(f.text+u.text):(r(o._isVList)&&n(u.tag)&&t(u.key)&&n(a)&&(u.key="__vlist"+a+"_"+c+"__"),s.push(u)));return s}(e):void 0}function ct(e){return n(e)&&n(e.text)&&!1===e.isComment}function ut(e,t){if(e){for(var n=Object.create(null),r=oe?Reflect.ownKeys(e):Object.keys(e),i=0;i0,a=t?!!t.$stable:!o,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&r&&r!==e&&s===r.$key&&!o&&!r.$hasNormal)return r;for(var c in i={},t)t[c]&&"$"!==c[0]&&(i[c]=vt(n,c,t[c]))}else i={};for(var u in n)u in i||(i[u]=ht(n,u));return t&&Object.isExtensible(t)&&(t._normalized=i),R(i,"$stable",a),R(i,"$key",s),R(i,"$hasNormal",o),i}function vt(e,t,n){var r=function(){var e=arguments.length?n.apply(null,arguments):n({}),t=(e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:st(e))&&e[0];return e&&(!t||1===e.length&&t.isComment&&!pt(t))?void 0:e};return n.proxy&&Object.defineProperty(e,t,{get:r,enumerable:!0,configurable:!0}),r}function ht(e,t){return function(){return e[t]}}function mt(e,t){var r,i,a,s,c;if(Array.isArray(e)||"string"==typeof e)for(r=new Array(e.length),i=0,a=e.length;idocument.createEvent("Event").timeStamp&&(cn=function(){return un.now()})}function ln(){var e,t;for(sn=cn(),on=!0,en.sort(function(e,t){return e.id-t.id}),an=0;anan&&en[n].id>e.id;)n--;en.splice(n+1,0,e)}else en.push(e);rn||(rn=!0,Qe(ln))}}(this)},pn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||o(e)||this.deep){var t=this.value;if(this.value=e,this.user){var n='callback for watcher "'+this.expression+'"';Be(this.cb,this.vm,[e,t],this.vm,n)}else this.cb.call(this.vm,e,t)}}},pn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},pn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},pn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||h(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var dn={enumerable:!0,configurable:!0,get:S,set:S};function vn(e,t,n){dn.get=function(){return this[t][n]},dn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,dn)}function hn(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[];e.$parent&&$e(!1);var o=function(o){i.push(o);var a=Ie(o,t,n,e);xe(r,o,a),o in e||vn(e,"_props",o)};for(var a in t)o(a);$e(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]="function"!=typeof t[n]?S:x(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;s(t=e._data="function"==typeof t?function(e,t){le();try{return e.call(t,t)}catch(e){return He(e,t,"data()"),{}}finally{fe()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&y(r,o)||(a=void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&vn(e,"_data",o))}var a;Ce(t,!0)}(e):Ce(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=te();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new pn(e,a||S,S,mn)),i in e||yn(e,i,o)}}(e,t.computed),t.watch&&t.watch!==Y&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i-1:"string"==typeof e?e.split(",").indexOf(t)>-1:(n=e,"[object RegExp]"===a.call(n)&&e.test(t));var n}function On(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=a.name;s&&!t(s)&&Sn(n,o,r,i)}}}function Sn(e,t,n,r){var i=e[t];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),e[t]=null,h(n,t)}!function(t){t.prototype._init=function(t){var n=this;n._uid=$n++,n._isVue=!0,t&&t._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),r=t._parentVnode;n.parent=t.parent,n._parentVnode=r;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(n,t):n.$options=De(wn(n.constructor),t||{},n),n._renderProxy=n,n._self=n,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(n),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&Wt(e,t)}(n),function(t){t._vnode=null,t._staticTrees=null;var n=t.$options,r=t.$vnode=n._parentVnode,i=r&&r.context;t.$slots=lt(n._renderChildren,i),t.$scopedSlots=e,t._c=function(e,n,r,i){return Ht(t,e,n,r,i,!1)},t.$createElement=function(e,n,r,i){return Ht(t,e,n,r,i,!0)};var o=r&&r.data;xe(t,"$attrs",o&&o.attrs||e,null,!0),xe(t,"$listeners",n._parentListeners||e,null,!0)}(n),Qt(n,"beforeCreate"),function(e){var t=ut(e.$options.inject,e);t&&($e(!1),Object.keys(t).forEach(function(n){xe(e,n,t[n])}),$e(!0))}(n),hn(n),function(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}(n),Qt(n,"created"),n.$options.el&&n.$mount(n.$options.el)}}(Cn),function(e){var t={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=ke,e.prototype.$delete=Ae,e.prototype.$watch=function(e,t,n){if(s(t))return bn(this,e,t,n);(n=n||{}).user=!0;var r=new pn(this,e,t,n);if(n.immediate){var i='callback for immediate watcher "'+r.expression+'"';le(),Be(t,this,[r.value],this,i),fe()}return function(){r.teardown()}}}(Cn),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this;if(Array.isArray(e))for(var i=0,o=e.length;i1?k(t):t;for(var n=k(arguments,1),r='event handler for "'+e+'"',i=0,o=t.length;iparseInt(this.max)&&Sn(e,t[0],t,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var e in this.cache)Sn(this.cache,e,this.keys)},mounted:function(){var e=this;this.cacheVNode(),this.$watch("include",function(t){On(e,function(e){return An(t,e)})}),this.$watch("exclude",function(t){On(e,function(e){return!An(t,e)})})},updated:function(){this.cacheVNode()},render:function(){var e=this.$slots.default,t=zt(e),n=t&&t.componentOptions;if(n){var r=kn(n),i=this.include,o=this.exclude;if(i&&(!r||!An(i,r))||o&&r&&An(o,r))return t;var a=this.cache,s=this.keys,c=null==t.key?n.Ctor.cid+(n.tag?"::"+n.tag:""):t.key;a[c]?(t.componentInstance=a[c].componentInstance,h(s,c),s.push(c)):(this.vnodeToCache=t,this.keyToCache=c),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return F}};Object.defineProperty(e,"config",t),e.util={warn:ae,extend:A,mergeOptions:De,defineReactive:xe},e.set=ke,e.delete=Ae,e.nextTick=Qe,e.observable=function(e){return Ce(e),e},e.options=Object.create(null),I.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,A(e.options.components,Nn),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=k(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=De(this.options,e),this}}(e),xn(e),function(e){I.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&s(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}(e)}(Cn),Object.defineProperty(Cn.prototype,"$isServer",{get:te}),Object.defineProperty(Cn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Cn,"FunctionalRenderContext",{value:Et}),Cn.version="2.6.14";var En=p("style,class"),jn=p("input,textarea,option,select,progress"),Dn=function(e,t,n){return"value"===n&&jn(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Ln=p("contenteditable,draggable,spellcheck"),In=p("events,caret,typing,plaintext-only"),Mn=function(e,t){return Bn(t)||"false"===t?"false":"contenteditable"===e&&In(t)?t:"true"},Fn=p("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),Pn="http://www.w3.org/1999/xlink",Rn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Hn=function(e){return Rn(e)?e.slice(6,e.length):""},Bn=function(e){return null==e||!1===e};function Un(e){for(var t=e.data,r=e,i=e;n(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(t=Vn(i.data,t));for(;n(r=r.parent);)r&&r.data&&(t=Vn(t,r.data));return function(e,t){if(n(e)||n(t))return zn(e,Kn(t));return""}(t.staticClass,t.class)}function Vn(e,t){return{staticClass:zn(e.staticClass,t.staticClass),class:n(e.class)?[e.class,t.class]:t.class}}function zn(e,t){return e?t?e+" "+t:e:t||""}function Kn(e){return Array.isArray(e)?function(e){for(var t,r="",i=0,o=e.length;i-1?mr(e,t,n):Fn(t)?Bn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Ln(t)?e.setAttribute(t,Mn(t,n)):Rn(t)?Bn(n)?e.removeAttributeNS(Pn,Hn(t)):e.setAttributeNS(Pn,t,n):mr(e,t,n)}function mr(e,t,n){if(Bn(n))e.removeAttribute(t);else{if(q&&!W&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var yr={create:vr,update:vr};function gr(e,r){var i=r.elm,o=r.data,a=e.data;if(!(t(o.staticClass)&&t(o.class)&&(t(a)||t(a.staticClass)&&t(a.class)))){var s=Un(r),c=i._transitionClasses;n(c)&&(s=zn(s,Kn(c))),s!==i._prevClass&&(i.setAttribute("class",s),i._prevClass=s)}}var _r,br,$r,wr,Cr,xr,kr={create:gr,update:gr},Ar=/[\w).+\-_$\]]/;function Or(e){var t,n,r,i,o,a=!1,s=!1,c=!1,u=!1,l=0,f=0,p=0,d=0;for(r=0;r=0&&" "===(h=e.charAt(v));v--);h&&Ar.test(h)||(u=!0)}}else void 0===i?(d=r+1,i=e.slice(0,r).trim()):m();function m(){(o||(o=[])).push(e.slice(d,r).trim()),d=r+1}if(void 0===i?i=e.slice(0,r).trim():0!==d&&m(),o)for(r=0;r-1?{exp:e.slice(0,wr),key:'"'+e.slice(wr+1)+'"'}:{exp:e,key:null};br=e,wr=Cr=xr=0;for(;!zr();)Kr($r=Vr())?qr($r):91===$r&&Jr($r);return{exp:e.slice(0,Cr),key:e.slice(Cr+1,xr)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Vr(){return br.charCodeAt(++wr)}function zr(){return wr>=_r}function Kr(e){return 34===e||39===e}function Jr(e){var t=1;for(Cr=wr;!zr();)if(Kr(e=Vr()))qr(e);else if(91===e&&t++,93===e&&t--,0===t){xr=wr;break}}function qr(e){for(var t=e;!zr()&&(e=Vr())!==t;);}var Wr,Zr="__r",Gr="__c";function Xr(e,t,n){var r=Wr;return function i(){null!==t.apply(null,arguments)&&ei(e,i,n,r)}}var Yr=Ke&&!(X&&Number(X[1])<=53);function Qr(e,t,n,r){if(Yr){var i=sn,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=i||e.timeStamp<=0||e.target.ownerDocument!==document)return o.apply(this,arguments)}}Wr.addEventListener(e,t,Q?{capture:n,passive:r}:n)}function ei(e,t,n,r){(r||Wr).removeEventListener(e,t._wrapper||t,n)}function ti(e,r){if(!t(e.data.on)||!t(r.data.on)){var i=r.data.on||{},o=e.data.on||{};Wr=r.elm,function(e){if(n(e[Zr])){var t=q?"change":"input";e[t]=[].concat(e[Zr],e[t]||[]),delete e[Zr]}n(e[Gr])&&(e.change=[].concat(e[Gr],e.change||[]),delete e[Gr])}(i),it(i,o,Qr,ei,Xr,r.context),Wr=void 0}}var ni,ri={create:ti,update:ti};function ii(e,r){if(!t(e.data.domProps)||!t(r.data.domProps)){var i,o,a=r.elm,s=e.data.domProps||{},c=r.data.domProps||{};for(i in n(c.__ob__)&&(c=r.data.domProps=A({},c)),s)i in c||(a[i]="");for(i in c){if(o=c[i],"textContent"===i||"innerHTML"===i){if(r.children&&(r.children.length=0),o===s[i])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===i&&"PROGRESS"!==a.tagName){a._value=o;var u=t(o)?"":String(o);oi(a,u)&&(a.value=u)}else if("innerHTML"===i&&Wn(a.tagName)&&t(a.innerHTML)){(ni=ni||document.createElement("div")).innerHTML=""+o+"";for(var l=ni.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;l.firstChild;)a.appendChild(l.firstChild)}else if(o!==s[i])try{a[i]=o}catch(e){}}}}function oi(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var r=e.value,i=e._vModifiers;if(n(i)){if(i.number)return f(r)!==f(t);if(i.trim)return r.trim()!==t.trim()}return r!==t}(e,t))}var ai={create:ii,update:ii},si=g(function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach(function(e){if(e){var r=e.split(n);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t});function ci(e){var t=ui(e.style);return e.staticStyle?A(e.staticStyle,t):t}function ui(e){return Array.isArray(e)?O(e):"string"==typeof e?si(e):e}var li,fi=/^--/,pi=/\s*!important$/,di=function(e,t,n){if(fi.test(t))e.style.setProperty(t,n);else if(pi.test(n))e.style.setProperty(C(t),n.replace(pi,""),"important");else{var r=hi(t);if(Array.isArray(n))for(var i=0,o=n.length;i-1?t.split(gi).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function bi(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(gi).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function $i(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&A(t,wi(e.name||"v")),A(t,e),t}return"string"==typeof e?wi(e):void 0}}var wi=g(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),Ci=V&&!W,xi="transition",ki="animation",Ai="transition",Oi="transitionend",Si="animation",Ti="animationend";Ci&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Ai="WebkitTransition",Oi="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Si="WebkitAnimation",Ti="webkitAnimationEnd"));var Ni=V?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function Ei(e){Ni(function(){Ni(e)})}function ji(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),_i(e,t))}function Di(e,t){e._transitionClasses&&h(e._transitionClasses,t),bi(e,t)}function Li(e,t,n){var r=Mi(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===xi?Oi:Ti,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=xi,l=a,f=o.length):t===ki?u>0&&(n=ki,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?xi:ki:null)?n===xi?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===xi&&Ii.test(r[Ai+"Property"])}}function Fi(e,t){for(;e.length1}function Vi(e,t){!0!==t.data.show&&Ri(t)}var zi=function(e){var o,a,s={},c=e.modules,u=e.nodeOps;for(o=0;ov?_(e,t(i[y+1])?null:i[y+1].elm,i,d,y,o):d>y&&$(r,p,v)}(p,h,y,o,l):n(y)?(n(e.text)&&u.setTextContent(p,""),_(p,null,y,0,y.length-1,o)):n(h)?$(h,0,h.length-1):n(e.text)&&u.setTextContent(p,""):e.text!==i.text&&u.setTextContent(p,i.text),n(v)&&n(d=v.hook)&&n(d=d.postpatch)&&d(e,i)}}}function k(e,t,i){if(r(i)&&n(e.parent))e.parent.data.pendingInsert=t;else for(var o=0;o-1,a.selected!==o&&(a.selected=o);else if(E(Zi(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function Wi(e,t){return t.every(function(t){return!E(t,e)})}function Zi(e){return"_value"in e?e._value:e.value}function Gi(e){e.target.composing=!0}function Xi(e){e.target.composing&&(e.target.composing=!1,Yi(e.target,"input"))}function Yi(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function Qi(e){return!e.componentInstance||e.data&&e.data.transition?e:Qi(e.componentInstance._vnode)}var eo={model:Ki,show:{bind:function(e,t,n){var r=t.value,i=(n=Qi(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Ri(n,function(){e.style.display=o})):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;!r!=!t.oldValue&&((n=Qi(n)).data&&n.data.transition?(n.data.show=!0,r?Ri(n,function(){e.style.display=e.__vOriginalDisplay}):Hi(n,function(){e.style.display="none"})):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},to={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function no(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?no(zt(t.children)):e}function ro(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[b(o)]=i[o];return t}function io(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var oo=function(e){return e.tag||pt(e)},ao=function(e){return"show"===e.name},so={name:"transition",props:to,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(oo)).length){var r=this.mode,o=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return o;var a=no(o);if(!a)return o;if(this._leaving)return io(e,o);var s="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=ro(this),u=this._vnode,l=no(u);if(a.data.directives&&a.data.directives.some(ao)&&(a.data.show=!0),l&&l.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(a,l)&&!pt(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=A({},c);if("out-in"===r)return this._leaving=!0,ot(f,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),io(e,o);if("in-out"===r){if(pt(a))return u;var p,d=function(){p()};ot(c,"afterEnter",d),ot(c,"enterCancelled",d),ot(f,"delayLeave",function(e){p=e})}}return o}}},co=A({tag:String,moveClass:String},to);function uo(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function lo(e){e.data.newPos=e.elm.getBoundingClientRect()}function fo(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete co.mode;var po={Transition:so,TransitionGroup:{props:co,beforeMount:function(){var e=this,t=this._update;this._update=function(n,r){var i=Gt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,i(),t.call(e,n,r)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=ro(this),s=0;s-1?Xn[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:Xn[e]=/HTMLUnknownElement/.test(t.toString())},A(Cn.options.directives,eo),A(Cn.options.components,po),Cn.prototype.__patch__=V?zi:S,Cn.prototype.$mount=function(e,t){return function(e,t,n){var r;return e.$el=t,e.$options.render||(e.$options.render=ve),Qt(e,"beforeMount"),r=function(){e._update(e._render(),n)},new pn(e,r,S,{before:function(){e._isMounted&&!e._isDestroyed&&Qt(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,Qt(e,"mounted")),e}(this,e=e&&V?Qn(e):void 0,t)},V&&setTimeout(function(){F.devtools&&ne&&ne.emit("init",Cn)},0);var vo=/\{\{((?:.|\r?\n)+?)\}\}/g,ho=/[-.*+?^${}()|[\]\/\\]/g,mo=g(function(e){var t=e[0].replace(ho,"\\$&"),n=e[1].replace(ho,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")});var yo={staticKeys:["staticClass"],transformNode:function(e,t){t.warn;var n=Pr(e,"class");n&&(e.staticClass=JSON.stringify(n));var r=Fr(e,"class",!1);r&&(e.classBinding=r)},genData:function(e){var t="";return e.staticClass&&(t+="staticClass:"+e.staticClass+","),e.classBinding&&(t+="class:"+e.classBinding+","),t}};var go,_o={staticKeys:["staticStyle"],transformNode:function(e,t){t.warn;var n=Pr(e,"style");n&&(e.staticStyle=JSON.stringify(si(n)));var r=Fr(e,"style",!1);r&&(e.styleBinding=r)},genData:function(e){var t="";return e.staticStyle&&(t+="staticStyle:"+e.staticStyle+","),e.styleBinding&&(t+="style:("+e.styleBinding+"),"),t}},bo=function(e){return(go=go||document.createElement("div")).innerHTML=e,go.textContent},$o=p("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),wo=p("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),Co=p("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),xo=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,ko=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Ao="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+P.source+"]*",Oo="((?:"+Ao+"\\:)?"+Ao+")",So=new RegExp("^<"+Oo),To=/^\s*(\/?)>/,No=new RegExp("^<\\/"+Oo+"[^>]*>"),Eo=/^]+>/i,jo=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Fo=/&(?:lt|gt|quot|amp|#39);/g,Po=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Ro=p("pre,textarea",!0),Ho=function(e,t){return e&&Ro(e)&&"\n"===t[0]};function Bo(e,t){var n=t?Po:Fo;return e.replace(n,function(e){return Mo[e]})}var Uo,Vo,zo,Ko,Jo,qo,Wo,Zo,Go=/^@|^v-on:/,Xo=/^v-|^@|^:|^#/,Yo=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,Qo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,ea=/^\(|\)$/g,ta=/^\[.*\]$/,na=/:(.*)$/,ra=/^:|^\.|^v-bind:/,ia=/\.[^.\]]+(?=[^\]]*$)/g,oa=/^v-slot(:|$)|^#/,aa=/[\r\n]/,sa=/[ \f\t\r\n]+/g,ca=g(bo),ua="_empty_";function la(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:ya(t),rawAttrsMap:{},parent:n,children:[]}}function fa(e,t){Uo=t.warn||Tr,qo=t.isPreTag||T,Wo=t.mustUseProp||T,Zo=t.getTagNamespace||T;t.isReservedTag;zo=Nr(t.modules,"transformNode"),Ko=Nr(t.modules,"preTransformNode"),Jo=Nr(t.modules,"postTransformNode"),Vo=t.delimiters;var n,r,i=[],o=!1!==t.preserveWhitespace,a=t.whitespace,s=!1,c=!1;function u(e){if(l(e),s||e.processed||(e=pa(e,t)),i.length||e===n||n.if&&(e.elseif||e.else)&&va(n,{exp:e.elseif,block:e}),r&&!e.forbidden)if(e.elseif||e.else)a=e,(u=function(e){var t=e.length;for(;t--;){if(1===e[t].type)return e[t];e.pop()}}(r.children))&&u.if&&va(u,{exp:a.elseif,block:a});else{if(e.slotScope){var o=e.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[o]=e}r.children.push(e),e.parent=r}var a,u;e.children=e.children.filter(function(e){return!e.slotScope}),l(e),e.pre&&(s=!1),qo(e.tag)&&(c=!1);for(var f=0;f]*>)","i")),p=e.replace(f,function(e,n,r){return u=r.length,Lo(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),Ho(l,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});c+=e.length-p.length,e=p,A(l,c-u,c)}else{var d=e.indexOf("<");if(0===d){if(jo.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),C(v+3);continue}}if(Do.test(e)){var h=e.indexOf("]>");if(h>=0){C(h+2);continue}}var m=e.match(Eo);if(m){C(m[0].length);continue}var y=e.match(No);if(y){var g=c;C(y[0].length),A(y[1],g,c);continue}var _=x();if(_){k(_),Ho(_.tagName,e)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(d>=0){for($=e.slice(d);!(No.test($)||So.test($)||jo.test($)||Do.test($)||(w=$.indexOf("<",1))<0);)d+=w,$=e.slice(d);b=e.substring(0,d)}d<0&&(b=e),b&&C(b.length),t.chars&&b&&t.chars(b,c-b.length,c)}if(e===n){t.chars&&t.chars(e);break}}function C(t){c+=t,e=e.substring(t)}function x(){var t=e.match(So);if(t){var n,r,i={tagName:t[1],attrs:[],start:c};for(C(t[0].length);!(n=e.match(To))&&(r=e.match(ko)||e.match(xo));)r.start=c,C(r[0].length),r.end=c,i.attrs.push(r);if(n)return i.unarySlash=n[1],C(n[0].length),i.end=c,i}}function k(e){var n=e.tagName,c=e.unarySlash;o&&("p"===r&&Co(n)&&A(r),s(n)&&r===n&&A(n));for(var u=a(n)||!!c,l=e.attrs.length,f=new Array(l),p=0;p=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,n,o);i.length=a,r=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}A()}(e,{warn:Uo,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,o,a,l,f){var p=r&&r.ns||Zo(e);q&&"svg"===p&&(o=function(e){for(var t=[],n=0;nc&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=Or(r[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+r[0].length}return c-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Mr(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Ur(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Ur(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Ur(t,"$$c")+"}",null,!0)}(e,r,i);else if("input"===o&&"radio"===a)!function(e,t,n){var r=n&&n.number,i=Fr(e,"value")||"null";Er(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),Mr(e,"change",Ur(t,i),null,!0)}(e,r,i);else if("input"===o||"textarea"===o)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Zr:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=Ur(t,l);c&&(f="if($event.target.composing)return;"+f),Er(e,"value","("+t+")"),Mr(e,u,f,null,!0),(s||a)&&Mr(e,"blur","$forceUpdate()")}(e,r,i);else if(!F.isReservedTag(o))return Br(e,r,i),!1;return!0},text:function(e,t){t.value&&Er(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Er(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:$o,mustUseProp:Dn,canBeLeftOpenTag:wo,isReservedTag:Zn,getTagNamespace:Gn,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}($a)},ka=g(function(e){return p("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))});function Aa(e,t){e&&(wa=ka(t.staticKeys||""),Ca=t.isReservedTag||T,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||d(e.tag)||!Ca(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(wa)))}(t);if(1===t.type){if(!Ca(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function(?:\s+[\w$]+)?\s*\(/,Sa=/\([^)]*?\);*$/,Ta=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Na={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Ea={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},ja=function(e){return"if("+e+")return null;"},Da={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:ja("$event.target !== $event.currentTarget"),ctrl:ja("!$event.ctrlKey"),shift:ja("!$event.shiftKey"),alt:ja("!$event.altKey"),meta:ja("!$event.metaKey"),left:ja("'button' in $event && $event.button !== 0"),middle:ja("'button' in $event && $event.button !== 1"),right:ja("'button' in $event && $event.button !== 2")};function La(e,t){var n=t?"nativeOn:":"on:",r="",i="";for(var o in e){var a=Ia(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":r+='"'+o+'":'+a+","}return r="{"+r.slice(0,-1)+"}",i?n+"_d("+r+",["+i.slice(0,-1)+"])":n+r}function Ia(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return Ia(e)}).join(",")+"]";var t=Ta.test(e.value),n=Oa.test(e.value),r=Ta.test(e.value.replace(Sa,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(Da[s])o+=Da[s],Na[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=ja(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(Ma).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+".apply(null, arguments)":n?"return ("+e.value+").apply(null, arguments)":r?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(r?"return "+e.value:e.value)+"}"}function Ma(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=Na[e],r=Ea[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var Fa={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:S},Pa=function(e){this.options=e,this.warn=e.warn||Tr,this.transforms=Nr(e.modules,"transformCode"),this.dataGenFns=Nr(e.modules,"genData"),this.directives=A(A({},Fa),e.directives);var t=e.isReservedTag||T;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Ra(e,t){var n=new Pa(t);return{render:"with(this){return "+(e?"script"===e.tag?"null":Ha(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function Ha(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return Ba(e,t);if(e.once&&!e.onceProcessed)return Ua(e,t);if(e.for&&!e.forProcessed)return za(e,t);if(e.if&&!e.ifProcessed)return Va(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=Wa(e,t),i="_t("+n+(r?",function(){return "+r+"}":""),o=e.attrs||e.dynamicAttrs?Xa((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:b(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)n=function(e,t,n){var r=t.inlineTemplate?null:Wa(t,n,!0);return"_c("+e+","+Ka(t,n)+(r?","+r:"")+")"}(e.component,e,t);else{var r;(!e.plain||e.pre&&t.maybeComponent(e))&&(r=Ka(e,t));var i=e.inlineTemplate?null:Wa(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];if(n&&1===n.type){var r=Ra(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+Xa(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ja(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ja))}function qa(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Va(e,t,qa,"null");if(e.for&&!e.forProcessed)return za(e,t,qa);var r=e.slotScope===ua?"":String(e.slotScope),i="function("+r+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(Wa(e,t)||"undefined")+":undefined":Wa(e,t)||"undefined":Ha(e,t))+"}",o=r?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function Wa(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(r||Ha)(a,t)+s}var c=n?function(e,t){for(var n=0,r=0;r':'
',ns.innerHTML.indexOf(" ")>0}var as=!!V&&os(!1),ss=!!V&&os(!0),cs=g(function(e){var t=Qn(e);return t&&t.innerHTML}),us=Cn.prototype.$mount;return Cn.prototype.$mount=function(e,t){if((e=e&&Qn(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=cs(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){var i=is(r,{outputSourceRange:!1,shouldDecodeNewlines:as,shouldDecodeNewlinesForHref:ss,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return us.call(this,e,t)},Cn.compile=is,Cn}); \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/locales/ar.lua b/[qb]/qb-vehiclesales/locales/ar.lua new file mode 100644 index 0000000..da9a43c --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/ar.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'هذه ليست سيارتك', + vehicle_does_not_exist = 'السيارة غير موجودة', + not_enough_money = 'ليس لديك ما يكفي من المال', + finish_payments = 'يجب عليك إنهاء سداد هذه السيارة قبل بيعها', + no_space_on_lot = 'لا توجد مساحة لسيارتك' + }, + success = { + sold_car_for_price = '$%{value} لقد قمت ببيع سيارتك مقابل', + car_up_for_sale = '$%{value} سيارتك معروضة للبيع مقابل', + vehicle_bought = 'السيارة تم شراؤها' + }, + info = { + confirm_cancel = '~g~Y~w~ - ﺀﺍﺮﺷ / ~r~N~w~ - ﺀﺎﻐﻟﺍ ~g~', + vehicle_returned = 'يتم إرجاع سيارتك', + used_vehicle_lot = 'سيارات مستعملة', + sell_vehicle_to_dealer = '[~g~E~w~] - ~g~$%{value} ~w~ﻞﺑﺎﻘﻣ ﺮﺟﺎﺘﻠﻟ ﺓﺭﺎﻴﺴﻟﺍ ﻊﻴﺑ', + view_contract = '[~g~E~w~] - ﺔﺒﻛﺮﻤﻟﺍ ﺪﻘﻋ ﺽﺮﻋ', + cancel_sale = '[~r~G~w~] - ﺓﺭﺎﻴﺴﻟﺍ ﻊﻴﺑ ﺀﺎﻐﻟﺇ', + model_price = '%{value}, :ﺮﻌﺴﻟﺍ ~g~$%{value2}', + are_you_sure = '؟ﻚﺗﺭﺎﻴﺳ ﻊﻴﺑ ﻲﻓ ﺐﻏﺮﺗ ﺪﻌﺗ ﻢﻟ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ', + yes_no = '[~g~7~w~] - ﻢﻌﻧ | [~r~8~w~] - ﻻ', + place_vehicle_for_sale = '[~g~E~w~] - ﻊﻴﺒﻠﻟ ﺓﺭﺎﻴﺴﻟﺍ ﻊﺿﻭ' + }, + charinfo = { + firstname = 'ليس', + lastname = 'معروف', + account = 'الحساب غير معروف', + phone = 'رقم الهاتف غير معروف' + }, + mail = { + sender = 'السيارات المستعملة', + subject = 'لقد قمت ببيع سيارة', + message = '$%{value} = %{value2} تم البيع' + } +} + +if GetConvar('qb_locale', 'en') == 'ar' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/de.lua b/[qb]/qb-vehiclesales/locales/de.lua new file mode 100644 index 0000000..4804e13 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/de.lua @@ -0,0 +1,56 @@ +local Translations = { + error = { + not_your_vehicle = 'Dies ist nicht Ihr Fahrzeug..', + vehicle_does_not_exist = 'Das Fahrzeug existiert nicht', + not_enough_money = 'Sie haben nicht genug Geld', + finish_payments = 'Sie müssen dieses Fahrzeug abbezahlen, bevor Sie es verkaufen können.', + no_space_on_lot = 'Es gibt keinen Platz für Ihr Auto auf dem Parkplatz!', + not_in_veh = 'Sie befinden sich nicht in einem Fahrzeug!', + not_for_sale = 'Dieses Fahrzeug steht nicht zum verkauf!', + }, + menu = { + view_contract = 'Vertrag ansehen', + view_contract_int = '[E] Vertrag ansehen', + sell_vehicle = 'Fahrzeug verkaufen', + sell_vehicle_help = 'Fahrzeug an Mitbürger verkaufen!', + sell_back = 'Auto zurückkaufen!', + sell_back_help = 'Verkaufen Sie Ihr Auto sofort zu einem reduzierten Preis zurück!', + interaction = '[E] Fahrzeug verkaufen', + }, + success = { + sold_car_for_price = 'Sie haben Ihr Auto verkauft für $%{value}', + car_up_for_sale = 'Ihr Auto ist zum Verkauf angeboten worden! Preis - $%{value}', + vehicle_bought = 'Gekauftes Fahrzeug', + }, + info = { + confirm_cancel = '~g~Y~w~ - Bestätigen / ~r~N~w~ - Abbrechen ~g~', + vehicle_returned = 'Ihr Fahrzeug wird zurückgebracht', + used_vehicle_lot = 'Gebrauchtwagenplatz', + sell_vehicle_to_dealer = '[~g~E~w~] - Fahrzeug an Händler verkaufen für ~g~$%{value}', + view_contract = '[~g~E~w~] - Fahrzeugvertrag ansehen', + cancel_sale = '[~r~G~w~] - Fahrzeugverkauf stornieren', + model_price = '%{value}, Preis: ~g~$%{value2}', + are_you_sure = 'Sind Sie sicher, dass Sie Ihr Fahrzeug nicht mehr verkaufen wollen?', + yes_no = '[~g~7~w~] - Ja | [~r~8~w~] - Nein', + place_vehicle_for_sale = '[~g~E~w~] - Platziere Fahrzeug zum Verkauf', + }, + charinfo = { + firstname = 'Vorname', + lastname = 'Nachname', + account = 'Konto nicht bekannt..', + phone = 'Telefonnummer nicht bekannt..', + }, + mail = { + sender = 'Larrys RV-Verkäufe', + subject = 'Sie haben ein Fahrzeug verkauft!', + message = 'Sie haben $%{value} aus dem Verkauf Ihrer %{value2}.', + } +} + +if GetConvar('qb_locale', 'en') == 'de' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/en.lua b/[qb]/qb-vehiclesales/locales/en.lua new file mode 100644 index 0000000..1fb1e96 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/en.lua @@ -0,0 +1,53 @@ +local Translations = { + error = { + not_your_vehicle = 'This is not your vehicle..', + vehicle_does_not_exist = 'Vehicle does not exist', + not_enough_money = 'You dont have enough money', + finish_payments = 'You must finish paying off this vehicle, Before you can sell it..', + no_space_on_lot = 'There is not space for your car on the lot!', + not_in_veh = 'You are not in a vehicle!', + not_for_sale = 'This vehicle is NOT for sale!', + }, + menu = { + view_contract = 'View Contract', + view_contract_int = '[E] View Contract', + sell_vehicle = 'Sell Vehicle', + sell_vehicle_help = 'Sell vehicle to fellow citizens!', + sell_back = 'Sell back car!', + sell_back_help = 'Sell your car straight back for a reduced price!', + interaction = '[E] Sell Vehicle', + }, + success = { + sold_car_for_price = 'You have sold your car for $%{value}', + car_up_for_sale = 'Your car has been put up for sale! Price - $%{value}', + vehicle_bought = 'Vehicle Bought', + }, + info = { + confirm_cancel = '~g~Y~w~ - Confirm / ~r~N~w~ - Cancel ~g~', + vehicle_returned = 'Your vehicle is returned', + used_vehicle_lot = 'Used Car Lot', + sell_vehicle_to_dealer = '[~g~E~w~] - Sell Vehicle To Dealer For ~g~$%{value}', + view_contract = '[~g~E~w~] - View Vehicle Contract', + cancel_sale = '[~r~G~w~] - Cancel Vehicle Sale', + model_price = '%{value}, Price: ~g~$%{value2}', + are_you_sure = 'Are you sure you no longer want to sell your vehicle?', + yes_no = '[~g~7~w~] - Yes | [~r~8~w~] - No', + place_vehicle_for_sale = '[~g~E~w~] - Place Vehicle For Sale By Owner', + }, + charinfo = { + firstname = 'not', + lastname = 'known', + account = 'Account not known..', + phone = 'telephone number not known..', + }, + mail = { + sender = 'Larrys RV Sales', + subject = 'You have sold a vehicle!', + message = 'You made $%{value} from the sale of your %{value2}.', + } +} + +Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true +}) diff --git a/[qb]/qb-vehiclesales/locales/es.lua b/[qb]/qb-vehiclesales/locales/es.lua new file mode 100644 index 0000000..c9f0e01 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/es.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Este no es tu vehículo..', + vehicle_does_not_exist = 'El vehículo no existe', + not_enough_money = 'No tienes suficiente dinero', + finish_payments = 'Debe terminar de pagar este vehículo, antes de poder venderlo..', + no_space_on_lot = '¡No hay espacio para su vehículo en el lote!' + }, + success = { + sold_car_for_price = 'Has vendido tu coche por $%{value}', + car_up_for_sale = '¡Tu coche ha sido puesto a la venta! Precio - $%{value}', + vehicle_bought = 'Vehículo comprado' + }, + info = { + confirm_cancel = '~g~S~w~ - Confirmar / ~r~N~w~ - Cancelar ~g~', + vehicle_returned = 'Su vehículo se ha devuelto', + used_vehicle_lot = 'Lote de vehículos usados', + sell_vehicle_to_dealer = '[~g~E~w~] - Vender vehículo al concesionario por ~g~$%{value}', + view_contract = '[~g~E~w~] - Ver contrato de vehículo', + cancel_sale = '[~r~G~w~] - Cancelar venta del vehículo', + model_price = '%{value}, Precio: ~g~$%{value2}', + are_you_sure = '¿Estás seguro de que ya no quieres vender tu vehículo?', + yes_no = '[~g~7~w~] - Sí | [~r~8~w~] - No', + place_vehicle_for_sale = '[~g~E~w~] - Colocar vehículo a la venta por el propietario' + }, + charinfo = { + firstname = 'no', + lastname = 'conocido', + account = 'Cuenta desconocida..', + phone = 'número de teléfono desconocido..' + }, + mail = { + sender = 'Venta de vehículos Larrys RV', + subject = '¡Has vendido un vehículo!', + message = 'Ha ganado $%{value} con la venta de tu %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'es' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/et.lua b/[qb]/qb-vehiclesales/locales/et.lua new file mode 100644 index 0000000..ace056d --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/et.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'See ei ole sinu sõiduk..', + vehicle_does_not_exist = 'Sõidukit ei eksisteeri', + not_enough_money = 'Sul ei ole piisavalt raha', + finish_payments = 'Peate lõpetama selle sõiduki tasumise, enne kui saate selle müüa.', + no_space_on_lot = 'Krundil pole teie auto jaoks ruumi!' + }, + success = { + sold_car_for_price = 'Olete müünud oma auto hinnaga $%{value}', + car_up_for_sale = 'Teie auto on müüki pandud! Hind – %{value} $', + vehicle_bought = 'Sõiduk ostetud' + }, + info = { + confirm_cancel = '~g~Y~w~ - Kinnita / ~r~N~w~ - Tühista ~g~', + vehicle_returned = 'Teie sõiduk tagastatakse', + used_vehicle_lot = 'Kasutatud autode plats', + sell_vehicle_to_dealer = '[~g~E~w~] – müüge sõiduk edasimüüjale hinnaga ~g~$%{value}', + view_contract = '[~g~E~w~] - Vaata sõiduki lepingut', + cancel_sale = '[~r~G~w~] – Tühista sõidukite müük', + model_price = '%{value}, hind: ~g~$%{value2}', + are_you_sure = 'Kas olete kindel, et ei soovi enam oma sõidukit müüa?', + yes_no = '[~g~7~w~] – jah | [~r~8~w~] – Ei', + place_vehicle_for_sale = '[~g~E~w~] – sõiduki müük omaniku poolt' + }, + charinfo = { + firstname = 'mitte', + lastname = 'teatud', + account = 'Isik pole teada..', + phone = 'telefoninumber pole teada..' + }, + mail = { + sender = 'Larrys RV Sales', + subject = 'Olete müünud sõiduki!', + message = 'Te teenisite oma %{value2} müügist $%{value}.' + } +} + +if GetConvar('qb_locale', 'en') == 'et' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/locales/fr.lua b/[qb]/qb-vehiclesales/locales/fr.lua new file mode 100644 index 0000000..ea6853f --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/fr.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Ce n\'est pas votre véhicule..', + vehicle_does_not_exist = 'Le véhicule n\'existe pas', + not_enough_money = 'Vous n\'avez pas assez d\'argent', + finish_payments = 'Vous devez finir de financer le véhicule avant de pouvoir le vendre..', + no_space_on_lot = 'Il n\'y a plus de place pour votre véhicule!' + }, + success = { + sold_car_for_price = 'Vous avez vendu votre véhicule pour: $%{value}', + car_up_for_sale = 'Votre véhicule a été mis en vente! Prix - $%{value}', + vehicle_bought = 'Véhicule acheté !' + }, + info = { + confirm_cancel = '~g~Y~w~ - Confirmer / ~r~N~w~ - Annuler ~g~', + vehicle_returned = 'Votre véhicule a été retourné', + used_vehicle_lot = 'Vendeur de voitures d\'occasion', + sell_vehicle_to_dealer = '[~g~E~w~] - Vendre le véhicule pour ~g~$%{value}', + view_contract = '[~g~E~w~] - Voir le contrat', + cancel_sale = '[~r~G~w~] - Annuler la vente', + model_price = '%{value}, Prix: ~g~$%{value2}', + are_you_sure = 'Êtes vous sûr de ne plus vouloir vendre le véhicule?', + yes_no = '[~g~7~w~] - Oui | [~r~8~w~] - Non', + place_vehicle_for_sale = '[~g~E~w~] - Placer le vehicule en vente' + }, + charinfo = { + firstname = 'Pas', + lastname = 'Connu', + account = 'Compte Inconnu..', + phone = 'Numéro Inconnu..' + }, + mail = { + sender = 'Larrys RV Sales', + subject = 'Vous avez vendu un véhicule!', + message = 'Vous avez gagné $%{value} Pour la vente de votre %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'fr' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/is.lua b/[qb]/qb-vehiclesales/locales/is.lua new file mode 100644 index 0000000..6ca13a5 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/is.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Þetta er ekki þitt farartæki..', + vehicle_does_not_exist = 'Ökutæki er ekki til', + not_enough_money = 'Þú átt ekki nóg af peningum', + finish_payments = 'Þú verður að klára að borga af þessu ökutæki, áður en þú getur selt það..', + no_space_on_lot = 'Það er ekki pláss fyrir bílinn þinn á lóðinni!' + }, + success = { + sold_car_for_price = 'Þú hefur selt bílinn þinn fyrir ISK%{value}', + car_up_for_sale = 'Bíllinn þinn hefur verið settur á sölu! Verð - ISK%{value}', + vehicle_bought = 'Vehicle Bought' + }, + info = { + confirm_cancel = '~g~Y~w~ - Staðfesta / ~r~N~w~ - Afturkalla ~g~', + vehicle_returned = 'Ökutækinu þínu er skilað', + used_vehicle_lot = 'Notaður bíll lota', + sell_vehicle_to_dealer = '[~g~E~w~] - Selja ökutæki til söluaðila fyrir ~g~ISK%{value}', + view_contract = '[~g~E~w~] - Skoða samning um ökutæki', + cancel_sale = '[~r~G~w~] - Hætta við sölu ökutækja', + model_price = '%{value}, Verð: ~g~ISK%{value2}', + are_you_sure = 'Ertu viss um að þú viljir ekki lengur selja bílinn þinn?', + yes_no = '[~g~7~w~] - Já | [~r~8~w~] - Nei', + place_vehicle_for_sale = '[~g~E~w~] - Settu ökutæki til sölu af eiganda' + }, + charinfo = { + firstname = 'ekki', + lastname = 'þekktur', + account = 'Reikningur ekki þekktur..', + phone = 'símanúmer ekki þekkt..' + }, + mail = { + sender = 'Larrys RV Sales', + subject = 'Þú hefur selt ökutæki!', + message = 'Þú gerðir ISK%{value} frá sölu þinni %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'is' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/it.lua b/[qb]/qb-vehiclesales/locales/it.lua new file mode 100644 index 0000000..65ce752 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/it.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Questo non è il tuo veicolo..', + vehicle_does_not_exist = 'Il veicolo non esiste', + not_enough_money = 'Non hai abbastanza soldi', + finish_payments = 'Devi finire di pagare questo veicolo, prima di poterlo vendere..', + no_space_on_lot = 'Non c\'è spazio per la vostra auto sul lotto!' + }, + success = { + sold_car_for_price = 'Hai venduto la tua auto per $%{value}', + car_up_for_sale = 'La tua auto è stata messa in vendita! Prezzo - $%{value}', + vehicle_bought = 'Veicolo Acquistato' + }, + info = { + confirm_cancel = '~g~Y~w~ - Conferma / ~r~N~w~ - Annulla ~g~', + vehicle_returned = 'Il tuo veicolo è stato restituito', + used_vehicle_lot = 'Lotto di veicoli usati', + sell_vehicle_to_dealer = '[~g~E~w~] - Vendi veicolo al rivenditore per ~g~$%{value}', + view_contract = '[~g~E~w~] - Visualizza contratto veicolo', + cancel_sale = '[~r~G~w~] - Annullar la vendita del veicolo', + model_price = '%{value}, Prezzo: ~g~$%{value2}', + are_you_sure = 'Sei sicuro di non voler più vendere il tuo veicolo?', + yes_no = '[~g~7~w~] - Si | [~r~8~w~] - No', + place_vehicle_for_sale = '[~g~E~w~] - Metti veicolo in vendita' + }, + charinfo = { + firstname = 'non', + lastname = 'conosciuto', + account = 'Conto non noto..', + phone = 'numero di telefono non noto..' + }, + mail = { + sender = 'Usato Larrys', + subject = 'Hai venduto un veicolo!', + message = 'Hai ricevuto $%{value} dalla vendite della tua %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'it' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/nl.lua b/[qb]/qb-vehiclesales/locales/nl.lua new file mode 100644 index 0000000..4636c4e --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/nl.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Dit is niet jouw voertuig..', + vehicle_does_not_exist = 'Voertuig bestaat niet', + not_enough_money = 'Je hebt geen genoeg geld', + finish_payments = 'Je moet dit voertuig afbetalen voordat je het kunt verkopen..', + no_space_on_lot = 'Er is geen ruimte voor je voertuig op de kavel!' + }, + success = { + sold_car_for_price = 'je hebt je auto verkocht voor $%{value}', + car_up_for_sale = 'Je auto is te koop gezet! Prijs - $%{value}', + vehicle_bought = 'Voertuig Gekocht' + }, + info = { + confirm_cancel = '~g~Y~w~ - Bevestigen / ~r~N~w~ - Annuleren ~g~', + vehicle_returned = 'Je voertuig wordt teruggebracht', + used_vehicle_lot = 'Tweedehands Auto', + sell_vehicle_to_dealer = '[~g~E~w~] - Verkoop voertuig aan dealer voor ~g~$%{value}', + view_contract = '[~g~E~w~] - Voertuigcontract bekijken', + cancel_sale = '[~r~G~w~] - Verkoop Annuleren', + model_price = '%{value}, Prijs: ~g~$%{value2}', + are_you_sure = 'Weet je zeker dat je je voertuig niet langer wilt verkopen?', + yes_no = '[~g~7~w~] - Ja | [~r~8~w~] - Nee', + place_vehicle_for_sale = '[~g~E~w~] - Plaats voertuig te koop door eigenaar' + }, + charinfo = { + firstname = 'niet', + lastname = 'bekend', + account = 'Account niet bekend..', + phone = 'telefoonnummer niet bekend..' + }, + mail = { + sender = 'TweedeKansje', + subject = 'Je hebt een voertuig verkocht!', + message = 'Je hebt $%{value} verdiend met de verkoop van je %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'nl' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/pt.lua b/[qb]/qb-vehiclesales/locales/pt.lua new file mode 100644 index 0000000..e451343 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/pt.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Este não é o teu veículo...', + vehicle_does_not_exist = 'Veículo não existe', + not_enough_money = 'Não tens dinheiro suficiente', + finish_payments = 'Tens que terminar de pagar este veículo antes de o conseguires vender...', + no_space_on_lot = 'Não existem lugares livres neste parque para o teu veículo!' + }, + success = { + sold_car_for_price = 'Vendeste a tua viatura por $%{value}', + car_up_for_sale = 'A tua viatura foi colocada à venda! Preço - $%{value}', + vehicle_bought = 'Veículo Comprado' + }, + info = { + confirm_cancel = '~g~Y~w~ - Confirmar / ~r~N~w~ - Cancelar ~g~', + vehicle_returned = 'O teu veículo foi devolvido', + used_vehicle_lot = 'Parque de Veículos Usados', + sell_vehicle_to_dealer = '[~g~E~w~] - Vender o teu veiculo ao negociante por ~g~$%{value}', + view_contract = '[~g~E~w~] - Ver Contracto do Veículo', + cancel_sale = '[~r~G~w~] - Cancelar Venda do Veículo', + model_price = '%{value}, Preço: ~g~$%{value2}', + are_you_sure = 'Tens a certeza que já não queres vender o teu veículo?', + yes_no = '[~g~7~w~] - Sim | [~r~8~w~] - Não', + place_vehicle_for_sale = '[~g~E~w~] - Colocar veículo à venda pelo dono' + }, + charinfo = { + firstname = 'sem', + lastname = 'conhecimento', + account = 'Conta desconhecida...', + phone = 'número de telefone desconhecido...' + }, + mail = { + sender = 'Vendas RV', + subject = 'Vendeste um veículo!', + message = 'Fizeste $%{value} na venda do teu %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'pt' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/ro.lua b/[qb]/qb-vehiclesales/locales/ro.lua new file mode 100644 index 0000000..d1ab132 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/ro.lua @@ -0,0 +1,56 @@ +local Translations = { + error = { + not_your_vehicle = 'Acesta nu este vehiculul tau..', + vehicle_does_not_exist = 'Vehiculul nu exista', + not_enough_money = 'Nu ai suficienti bani', + finish_payments = 'Trebuie sa achiti de platit ratele, inainte de a putea vinde vehiculul...', + no_space_on_lot = 'Din pacate, nu mai sunt spatii pentru a pune masina la vanzare!', + not_in_veh = 'Nu esti intr-un vehicul!', + not_for_sale = 'Acest vehicul nu este de vanzare!', + }, + menu = { + view_contract = 'Vezi contractul', + view_contract_int = '[E] Vezi contractul', + sell_vehicle = 'Vinde vehiculul', + sell_vehicle_help = 'Vinde vehiculul tau catre cetateni interesati', + sell_back = 'Cumpara inapoi vehiculul!', + sell_back_help = 'Cumpara-ti masina inapoi la un pret promotional!', + interaction = '[E] Vinde vehicul', + }, + success = { + sold_car_for_price = 'Ai vandut vehiculul pentru suma de $%{value}', + car_up_for_sale = 'Vehiculul tau a fost pus la vanzare! Pretul - $%{value}', + vehicle_bought = 'Vehiculul a fost cumparat', + }, + info = { + confirm_cancel = '~g~Y~w~ - Confirma / ~r~N~w~ - Anuleaza ~g~', + vehicle_returned = 'Ti-a fost returnat vehiculul', + used_vehicle_lot = 'Masini Second hand', + sell_vehicle_to_dealer = '[~g~E~w~] - Vine vehiculul pentru suma de ~g~$%{value}', + view_contract = '[~g~E~w~] - Vezi contractul', + cancel_sale = '[~r~G~w~] - Anuleaza vanzarea vehiculului', + model_price = '%{value}, Pret: ~g~$%{value2}', + are_you_sure = 'Esti sigur(a) ca nu mai vrei sa vinzi acest vehicul?', + yes_no = '[~g~7~w~] - Da | [~r~8~w~] - Nu', + place_vehicle_for_sale = '[~g~E~w~] - Vanzare vehicul de la proprietar', + }, + charinfo = { + firstname = 'Necunoscut', + lastname = 'Necunoscut', + account = 'Necunoscut..', + phone = 'Necunoscut..', + }, + mail = { + sender = 'Larrys RV Sales', + subject = 'Un vehicul a fost vandut!', + message = 'Ai castigat suma de $%{value} din vanzarea vehiculului %{value2}.', + } +} + +if GetConvar('qb_locale', 'en') == 'ro' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/sv.lua b/[qb]/qb-vehiclesales/locales/sv.lua new file mode 100644 index 0000000..6312767 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/sv.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Det här är inte ditt fordon..', + vehicle_does_not_exist = 'Fordonet existerar inte', + not_enough_money = 'Du har inte tillräckligt med pengar', + finish_payments = 'Du måste avsluta avbetalningen för fordonet innan du kan sälja det..', + no_space_on_lot = 'Det finns ingen plats för din bil på parkeringen!' + }, + success = { + sold_car_for_price = 'Du har sålt din bil för $%{value}', + car_up_for_sale = 'Din bil har lagts ut till försäljning för $%{value}', + vehicle_bought = 'Fordon köpt' + }, + info = { + confirm_cancel = '~g~Y~w~ - Godkänn / ~r~N~w~ - Avbryt ~g~', + vehicle_returned = 'Ditt fordon har lämnats tillbaka', + used_vehicle_lot = 'Begagnade bilar', + sell_vehicle_to_dealer = '[~g~E~w~] - Sälj fordonet till bilförsäljaren för ~g~$%{value}', + view_contract = '[~g~E~w~] - Visa fordonskontrakt', + cancel_sale = '[~r~G~w~] - Avbrut försäljning', + model_price = '%{value}, Pris: ~g~$%{value2}', + are_you_sure = 'Är du säker på att du inte längre vill sälja fordonet?', + yes_no = '[~g~7~w~] - Ja | [~r~8~w~] - Nej', + place_vehicle_for_sale = '[~g~E~w~] - Sätt fordonet till försäljning av ägaren' + }, + charinfo = { + firstname = 'Inte', + lastname = 'Känt', + account = 'Kontonummer okänt', + phone = 'Telefonnummer okänt..' + }, + mail = { + sender = 'Blocket', + subject = 'Din bil är såld!', + message = 'Du fick $%{value} för försäljningen av din %{value2}.' + } +} + +if GetConvar('qb_locale', 'en') == 'sv' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/tr.lua b/[qb]/qb-vehiclesales/locales/tr.lua new file mode 100644 index 0000000..f61f94d --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/tr.lua @@ -0,0 +1,45 @@ +local Translations = { + error = { + not_your_vehicle = 'Bu senin aracın değil..', + vehicle_does_not_exist = 'Araç mevcut değil', + not_enough_money = 'Yeterli paran yok', + finish_payments = 'Bu aracı satmadan önce borcunu ödemelisin..', + no_space_on_lot = 'Otoparkta arabanız için yer yok!' + }, + success = { + sold_car_for_price = 'Arabanızı $%{value} karşılığında sattınız', + car_up_for_sale = 'Arabanız satışa çıkarıldı! Fiyat - $%{value}', + vehicle_bought = 'Araç Satın Alındı' + }, + info = { + confirm_cancel = '~g~Y~w~ - Onayla / ~r~N~w~ - İptal Et ~g~', + vehicle_returned = 'Aracınız iade edildi', + used_vehicle_lot = 'İkinci El Araba', + sell_vehicle_to_dealer = '[~g~E~w~] - Aracı Satıcıya $%{value} karşılığında Sat', + view_contract = '[~g~E~w~] - Araç Sözleşmesini Görüntüle', + cancel_sale = '[~r~G~w~] - Araç Satışını İptal Et', + model_price = '%{value}, Fiyat: ~g~$%{value2}', + are_you_sure = 'Artık aracınızı satmak istemediğinizden emin misiniz?', + yes_no = '[~g~7~w~] - Evet | [~r~8~w~] - Hayır', + place_vehicle_for_sale = '[~g~E~w~] - Sahibinden Satılık Aracı Yerleştirin' + }, + charinfo = { + firstname = 'veri', + lastname = 'yok', + account = 'Hesap bilinmiyor..', + phone = 'telefon numarası bilinmiyor..' + }, + mail = { + sender = 'Sahibinden', + subject = 'Bir araç sattın!', + message = '%{value2} satışından $%{value} kazandınız.' + } +} + +if GetConvar('qb_locale', 'en') == 'tr' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/locales/vi.lua b/[qb]/qb-vehiclesales/locales/vi.lua new file mode 100644 index 0000000..aa286a3 --- /dev/null +++ b/[qb]/qb-vehiclesales/locales/vi.lua @@ -0,0 +1,56 @@ +local Translations = { + error = { + not_your_vehicle = 'Đây không phải xe của bạn.', + vehicle_does_not_exist = 'Xe này không tồn tại', + not_enough_money = 'Bạn không có đủ tiền', + finish_payments = 'Bạn phải trả tiền xong cho chiếc xe này, trước khi bạn có thể bán nó.', + no_space_on_lot = 'Không còn chỗ cho xe của bạn trên bãi!', + not_in_veh = 'Bạn cần ở trong phương tiện!', + not_for_sale = 'Xe này KHÔNG phải để bán!', + }, + menu = { + view_contract = 'Xem hợp đồng', + view_contract_int = '[E] Xem hợp đồng', + sell_vehicle = 'Bán xe', + sell_vehicle_help = 'Bán xe cho cư dân!', + sell_back = 'Bán lại xe!', + sell_back_help = 'Bán lại xe của bạn ngay với giá giảm!', + interaction = '[E] Bán xe', + }, + success = { + sold_car_for_price = 'Bạn đã bán chiếc xe của mình với giá $%{value}', + car_up_for_sale = 'Xe của bạn đã được rao bán! Giá - $%{value}', + vehicle_bought = 'Xe đã mua', + }, + info = { + confirm_cancel = '~g~Y~w~ - Xác nhận / ~r~N~w~ - Cancel ~g~', + vehicle_returned = 'Xe của bạn được trả lại', + used_vehicle_lot = 'Lô xe đã qua sử dụng', + sell_vehicle_to_dealer = '[~g~E~w~] - Bán xe cho đại lý ~g~$%{value}', + view_contract = '[~g~E~w~] - Xem hợp đồng xe', + cancel_sale = '[~r~G~w~] - Hủy bán xe', + model_price = '%{value}, Giá: ~g~$%{value2}', + are_you_sure = 'Bạn có chắc chắn rằng bạn không còn muốn bán chiếc xe của mình?', + yes_no = '[~g~7~w~] - Có | [~r~8~w~] - Không', + place_vehicle_for_sale = '[~g~E~w~] - Đặt Xe Chính Chủ Bán', + }, + charinfo = { + firstname = 'not', + lastname = 'known', + account = 'Account not known..', + phone = 'telephone number not known..', + }, + mail = { + sender = 'Larrys RV Sales', + subject = 'Bạn đã bán một chiếc xe!', + message = 'Bạn nhận được $%{value} từ việc bán %{value2} của bạn.', + } +} + +if GetConvar('qb_locale', 'en') == 'vi' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehiclesales/qb-vehiclesales.sql b/[qb]/qb-vehiclesales/qb-vehiclesales.sql new file mode 100644 index 0000000..e07637f --- /dev/null +++ b/[qb]/qb-vehiclesales/qb-vehiclesales.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS `occasion_vehicles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `seller` varchar(50) DEFAULT NULL, + `price` int(11) DEFAULT NULL, + `description` longtext DEFAULT NULL, + `plate` varchar(50) DEFAULT NULL, + `model` varchar(50) DEFAULT NULL, + `mods` text DEFAULT NULL, + `occasionid` varchar(50) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `occasionId` (`occasionid`) +) ENGINE=InnoDB AUTO_INCREMENT=325 DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/[qb]/qb-vehiclesales/server/main.lua b/[qb]/qb-vehiclesales/server/main.lua new file mode 100644 index 0000000..cd1dd08 --- /dev/null +++ b/[qb]/qb-vehiclesales/server/main.lua @@ -0,0 +1,127 @@ +local QBCore = exports['qb-core']:GetCoreObject() + +-- Functions + +local function generateOID() + local num = math.random(1, 10) .. math.random(111, 999) + + return "OC" .. num +end + +-- Callbacks + +QBCore.Functions.CreateCallback('qb-occasions:server:getVehicles', function(_, cb) + local result = MySQL.query.await('SELECT * FROM occasion_vehicles', {}) + if result[1] then + cb(result) + else + cb(nil) + end +end) + +QBCore.Functions.CreateCallback("qb-occasions:server:getSellerInformation", function(_, cb, citizenid) + MySQL.query('SELECT * FROM players WHERE citizenid = ?', {citizenid}, function(result) + if result[1] then + cb(result[1]) + else + cb(nil) + end + end) +end) + +QBCore.Functions.CreateCallback("qb-vehiclesales:server:CheckModelName", function(_, cb, plate) + if plate then + local ReturnData = MySQL.scalar.await("SELECT vehicle FROM player_vehicles WHERE plate = ?", {plate}) + cb(ReturnData) + end +end) + +-- Events + +RegisterNetEvent('qb-occasions:server:ReturnVehicle', function(vehicleData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + local result = MySQL.query.await('SELECT * FROM occasion_vehicles WHERE plate = ? AND occasionid = ?', {vehicleData['plate'], vehicleData["oid"]}) + if result[1] then + if result[1].seller == Player.PlayerData.citizenid then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, state) VALUES (?, ?, ?, ?, ?, ?, ?)', {Player.PlayerData.license, Player.PlayerData.citizenid, vehicleData["model"], joaat(vehicleData["model"]), vehicleData["mods"], vehicleData["plate"], 0}) + MySQL.query('DELETE FROM occasion_vehicles WHERE occasionid = ? AND plate = ?', {vehicleData["oid"], vehicleData['plate']}) + TriggerClientEvent("qb-occasions:client:ReturnOwnedVehicle", src, result[1]) + TriggerClientEvent('qb-occasion:client:refreshVehicles', -1) + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.not_your_vehicle'), 'error', 3500) + end + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.vehicle_does_not_exist'), 'error', 3500) + end +end) + +RegisterNetEvent('qb-occasions:server:sellVehicle', function(vehiclePrice, vehicleData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + MySQL.query('DELETE FROM player_vehicles WHERE plate = ? AND vehicle = ?',{vehicleData.plate, vehicleData.model}) + MySQL.insert('INSERT INTO occasion_vehicles (seller, price, description, plate, model, mods, occasionid) VALUES (?, ?, ?, ?, ?, ?, ?)',{Player.PlayerData.citizenid, vehiclePrice, vehicleData.desc, vehicleData.plate, vehicleData.model,json.encode(vehicleData.mods), generateOID()}) + TriggerEvent("qb-log:server:CreateLog", "vehicleshop", "Vehicle for Sale", "red","**" .. GetPlayerName(src) .. "** has a " .. vehicleData.model .. " priced at " .. vehiclePrice) + TriggerClientEvent('qb-occasion:client:refreshVehicles', -1) +end) + +RegisterNetEvent('qb-occasions:server:sellVehicleBack', function(vehData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + local price = 0 + local plate = vehData.plate + for _, v in pairs(QBCore.Shared.Vehicles) do + if v["hash"] == vehData.model then + price = tonumber(v["price"]) + break + end + end + local payout = math.floor(tonumber(price * 0.5)) -- This will give you half of the cars value + Player.Functions.AddMoney('bank', payout) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.sold_car_for_price', { value = payout }), 'success', 5500) + MySQL.query('DELETE FROM player_vehicles WHERE plate = ?', {plate}) +end) + +RegisterNetEvent('qb-occasions:server:buyVehicle', function(vehicleData) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + local result = MySQL.query.await('SELECT * FROM occasion_vehicles WHERE plate = ? AND occasionid = ?',{vehicleData['plate'], vehicleData["oid"]}) + if result[1] and next(result[1]) then + if Player.PlayerData.money.bank >= result[1].price then + local SellerCitizenId = result[1].seller + local SellerData = QBCore.Functions.GetPlayerByCitizenId(SellerCitizenId) + local NewPrice = math.ceil((result[1].price / 100) * 77) + Player.Functions.RemoveMoney('bank', result[1].price) + MySQL.insert( + 'INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, state) VALUES (?, ?, ?, ?, ?, ?, ?)', { + Player.PlayerData.license, + Player.PlayerData.citizenid, result[1]["model"], + GetHashKey(result[1]["model"]), + result[1]["mods"], + result[1]["plate"], + 0 + }) + if SellerData then + SellerData.Functions.AddMoney('bank', NewPrice) + else + local BuyerData = MySQL.query.await('SELECT * FROM players WHERE citizenid = ?',{SellerCitizenId}) + if BuyerData[1] then + local BuyerMoney = json.decode(BuyerData[1].money) + BuyerMoney.bank = BuyerMoney.bank + NewPrice + MySQL.update('UPDATE players SET money = ? WHERE citizenid = ?', {json.encode(BuyerMoney), SellerCitizenId}) + end + end + TriggerEvent("qb-log:server:CreateLog", "vehicleshop", "bought", "green", "**" .. GetPlayerName(src) .. "** has bought for " .. result[1].price .. " (" .. result[1].plate ..") from **" .. SellerCitizenId .. "**") + TriggerClientEvent("qb-occasions:client:BuyFinished", src, result[1]) + TriggerClientEvent('qb-occasion:client:refreshVehicles', -1) + MySQL.query('DELETE FROM occasion_vehicles WHERE plate = ? AND occasionid = ?',{result[1].plate, result[1].occasionid}) + exports['qb-phone']:sendNewMailToOffline(SellerCitizenId, { + sender = Lang:t('mail.sender'), + subject = Lang:t('mail.subject'), + message = Lang:t('mail.message', { value = NewPrice, value2 = QBCore.Shared.Vehicles[result[1].model].name}) + }) + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.not_enough_money'), 'error', 3500) + end + end +end) diff --git a/[qb]/qb-vehicleshop/LICENSE b/[qb]/qb-vehicleshop/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/[qb]/qb-vehicleshop/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/[qb]/qb-vehicleshop/README.md b/[qb]/qb-vehicleshop/README.md new file mode 100644 index 0000000..f37e602 --- /dev/null +++ b/[qb]/qb-vehicleshop/README.md @@ -0,0 +1,224 @@ +# qb-vehicleshop + +**Test Drives:** +* Configurable time +* Returns player once time is up +* Can't take out more than one vehicle + +**Financing:** +* Configurable down payment +* Configurable maximum payments +* Configurable commission amount for private dealerships +* Checks for payments due on player join and updates times on player logout or quit + +**Shops:** +* Lock to a specific job +* Commission paid to sales person for private dealer +* Create as many as desired with easy polyzone creation +* Vehicle sale amount gets deposited into the cardealer society fund for private dealer + +**Planned Updates** +* QB-Phone support to make payments + +**Preview header when near a vehicle at the public dealership:** + +![image](https://user-images.githubusercontent.com/57848836/138773379-836be2a6-a800-47a4-8037-84d9052a964c.png) + +**After pressing the focus key and selecting the preview header (default: LEFT ALT)** + +![image](https://user-images.githubusercontent.com/57848836/138770886-15e056db-3e57-43ea-b855-3ef4fd107acf.png) + +**Configurable test drive times that automatically return the player** +![20211025160757_1](https://user-images.githubusercontent.com/57848836/138771162-00ee2607-0b56-418b-848c-5d8a009f4acd.jpg) + +**Vehicle purchasing** +![20211025160853_1](https://user-images.githubusercontent.com/57848836/138772385-ce16c0e6-baea-4b54-8eff-dbf44c54f568.jpg) + +**Private job-based dealership menu (works off closest player)** + +![image](https://user-images.githubusercontent.com/57848836/138772120-9513fa09-a22f-4a5f-8afe-6dc7756999f4.png) + +**Financing a vehicle with configurable max payment amount and minimum downpayment percentage** +![image](https://user-images.githubusercontent.com/57848836/138771328-0b88078c-9f3d-4754-a4c7-bd5b68dd5129.png) + +**Financing preview header** + +![image](https://user-images.githubusercontent.com/57848836/138773600-d6f510f8-a476-436d-8211-21e8c920eb6b.png) + +**Finance vehicle list** + +![image](https://user-images.githubusercontent.com/57848836/138771582-727e7fd4-4837-4320-b79a-479a6268b7ac.png) + +**Make a payment or pay off vehicle in full** + +![image](https://user-images.githubusercontent.com/57848836/138771627-faed7fcb-73c8-4b77-a33f-fffbb738ab03.png) + +### Dependencies: + +**[PolyZone](https://github.com/qbcore-framework/PolyZone)** + +* You need to create new PolyZones if you want to create a new dealership or move default locations to another area. After you create the new PolyZones, add them to the Config.Shops > [Shape] + +* Here's a Wiki on how to create new PolyZone: +https://github.com/mkafrin/PolyZone/wiki/Using-the-creation-script + +**[qb-menu](https://github.com/qbcore-framework/qb-menu)** + +**[qb-input](https://github.com/qbcore-framework/qb-input)** + +```lua +Config = {} +Config.UsingTarget = false -- If you are using qb-target (uses entity zones to target vehicles) +Config.Commission = 0.10 -- Percent that goes to sales person from a full car sale - default 10% +Config.FinanceCommission = 0.05 -- Percent that goes to sales person from a finance sale - default 5% +Config.FinanceZone = vector3(-29.53, -1103.67, 26.42) -- Where the finance menu is located +Config.PaymentWarning = 10 -- time in minutes that player has to make payment before repo - default 10 +Config.PaymentInterval = 24 -- time in hours between payment being due - default 24 +Config.MinimumDown = 10 -- minimum percentage allowed down - default 10 +Config.MaximumPayments = 24 -- maximum payments allowed - default 24 +Config.Shops = { + ['pdm'] = { + ['Type'] = 'free-use', -- no player interaction is required to purchase a car + ['Zone'] = { + ['Shape'] = { --polygon that surrounds the shop + vector2(-56.727394104004, -1086.2325439453), + vector2(-60.612808227539, -1096.7795410156), + vector2(-58.26834487915, -1100.572265625), + vector2(-35.927803039551, -1109.0034179688), + vector2(-34.427627563477, -1108.5111083984), + vector2(-32.02657699585, -1101.5877685547), + vector2(-33.342102050781, -1101.0377197266), + vector2(-31.292987823486, -1095.3717041016) + }, + ['minZ'] = 25.0, -- min height of the shop zone + ['maxZ'] = 28.0 -- max height of the shop zone + }, + ['Job'] = 'none', -- Name of job or none + ['ShopLabel'] = 'Premium Deluxe Motorsport', -- Blip name + ['Categories'] = { -- Categories available to browse + ['sportsclassics'] = 'Sports Classics', + ['sedans'] = 'Sedans', + ['coupes'] = 'Coupes', + ['suvs'] = 'SUVs', + ['offroad'] = 'Offroad', + ['muscle'] = 'Muscle', + ['compacts'] = 'Compacts', + ['motorcycles'] = 'Motorcycles', + ['vans'] = 'Vans' + }, + ['TestDriveTimeLimit'] = 0.5, -- Time in minutes until the vehicle gets deleted - default 0.5 (30 seconds) + ['Location'] = vector3(-45.67, -1098.34, 26.42), -- Blip Location + ['ReturnLocation'] = vector3(-44.74, -1082.58, 26.68), -- Location to return vehicle, only enables if the vehicleshop has a job owned + ['VehicleSpawn'] = vector4(-56.79, -1109.85, 26.43, 71.5), -- Spawn location when vehicle is bought + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(-45.65, -1093.66, 25.44, 69.5), -- where the vehicle will spawn on display + defaultVehicle = 'adder', -- Default display vehicle + chosenVehicle = 'adder', -- Same as default but is dynamically changed when swapping vehicles + }, + [2] = { + coords = vector4(-48.27, -1101.86, 25.44, 294.5), + defaultVehicle = 'schafter2', + chosenVehicle = 'schafter2', + }, + [3] = { + coords = vector4(-39.6, -1096.01, 25.44, 66.5), + defaultVehicle = 'comet2', + chosenVehicle = 'comet2', + }, + [4] = { + coords = vector4(-51.21, -1096.77, 25.44, 254.5), + defaultVehicle = 'vigero', + chosenVehicle = 'vigero', + }, + [5] = { + coords = vector4(-40.18, -1104.13, 25.44, 338.5), + defaultVehicle = 't20', + chosenVehicle = 't20', + }, + [6] = { + coords = vector4(-43.31, -1099.02, 25.44, 52.5), + defaultVehicle = 'bati', + chosenVehicle = 'bati', + }, + [7] = { + coords = vector4(-50.66, -1093.05, 25.44, 222.5), + defaultVehicle = 'bati', + chosenVehicle = 'bati', + }, + [8] = { + coords = vector4(-44.28, -1102.47, 25.44, 298.5), + defaultVehicle = 'bati', + chosenVehicle = 'bati', + } + }, + }, + ['luxury'] = { + ['Type'] = 'managed', -- meaning a real player has to sell the car + ['Zone'] = { + ['Shape'] = { + vector2(-81.724754333496, 72.436462402344), + vector2(-60.159938812256, 60.576206207275), + vector2(-55.763122558594, 61.749210357666), + vector2(-52.965869903564, 69.869110107422), + vector2(-50.352680206299, 75.886123657227), + vector2(-61.261016845703, 81.564918518066), + vector2(-63.812171936035, 75.633102416992), + vector2(-76.546226501465, 81.189826965332) + }, + ['minZ'] = 69.0, + ['maxZ'] = 76.0 + }, + ['Job'] = 'cardealer', -- Name of job or none + ['ShopLabel'] = 'Luxury Vehicle Shop', + ['Categories'] = { + ['super'] = 'Super', + ['sports'] = 'Sports' + }, + ['TestDriveTimeLimit'] = 0.5, + ['Location'] = vector3(-63.59, 68.25, 73.06), + ['ReturnLocation'] = vector3(-65.05, 81.23, 71.16), + ['VehicleSpawn'] = vector4(-71.13, 84.04, 71.09, 65.23), + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(-75.96, 74.78, 70.90, 221.69), + defaultVehicle = 'italirsx', + chosenVehicle = 'italirsx', + }, + [2] = { + coords = vector4(-66.52, 74.33, 70.65, 188.03), + defaultVehicle = 'italigtb', + chosenVehicle = 'italigtb', + }, + [3] = { + coords = vector4(-71.83, 68.60, 70.75, 276.57), + defaultVehicle = 'nero', + chosenVehicle = 'nero', + }, + [4] = { + coords = vector4(-59.95, 68.61, 70.85, 181.44), + defaultVehicle = 'comet2', + chosenVehicle = 'comet2', + } + } + } -- Add your next table under this comma +} +``` + +# License + + QBCore Framework + Copyright (C) 2021 Joshua Eger + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see diff --git a/[qb]/qb-vehicleshop/client.lua b/[qb]/qb-vehicleshop/client.lua new file mode 100644 index 0000000..2950ef4 --- /dev/null +++ b/[qb]/qb-vehicleshop/client.lua @@ -0,0 +1,880 @@ +-- Variables +local QBCore = exports['qb-core']:GetCoreObject() +local PlayerData = QBCore.Functions.GetPlayerData() +local testDriveZone = nil +local vehicleMenu = {} +local Initialized = false +local testDriveVeh, inTestDrive = 0, false +local ClosestVehicle = 1 +local zones = {} +local insideShop, tempShop = nil, nil + +-- Handlers +AddEventHandler('QBCore:Client:OnPlayerLoaded', function() + PlayerData = QBCore.Functions.GetPlayerData() + local citizenid = PlayerData.citizenid + TriggerServerEvent('qb-vehicleshop:server:addPlayer', citizenid) + TriggerServerEvent('qb-vehicleshop:server:checkFinance') + if not Initialized then Init() end +end) + +AddEventHandler('onResourceStart', function(resource) + if resource ~= GetCurrentResourceName() then + return + end + if next(PlayerData) ~= nil and not Initialized then + PlayerData = QBCore.Functions.GetPlayerData() + local citizenid = PlayerData.citizenid + TriggerServerEvent('qb-vehicleshop:server:addPlayer', citizenid) + TriggerServerEvent('qb-vehicleshop:server:checkFinance') + Init() + end +end) + +RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) + PlayerData.job = JobInfo +end) + +RegisterNetEvent('QBCore:Client:OnPlayerUnload', function() + local citizenid = PlayerData.citizenid + TriggerServerEvent('qb-vehicleshop:server:removePlayer', citizenid) + PlayerData = {} +end) + +-- Static Headers +local vehHeaderMenu = { + { + header = Lang:t('menus.vehHeader_header'), + txt = Lang:t('menus.vehHeader_txt'), + icon = "fa-solid fa-car", + params = { + event = 'qb-vehicleshop:client:showVehOptions' + } + } +} + +local financeMenu = { + { + header = Lang:t('menus.financed_header'), + txt = Lang:t('menus.finance_txt'), + icon = "fa-solid fa-user-ninja", + params = { + event = 'qb-vehicleshop:client:getVehicles' + } + } +} + +local returnTestDrive = { + { + header = Lang:t('menus.returnTestDrive_header'), + icon = "fa-solid fa-flag-checkered", + params = { + event = 'qb-vehicleshop:client:TestDriveReturn' + } + } +} + +-- Functions +local function drawTxt(text, font, x, y, scale, r, g, b, a) + SetTextFont(font) + SetTextScale(scale, scale) + SetTextColour(r, g, b, a) + SetTextOutline() + SetTextCentre(1) + SetTextEntry("STRING") + AddTextComponentString(text) + DrawText(x, y) +end + +local function tablelength(T) + local count = 0 + for _ in pairs(T) do + count = count + 1 + end + return count +end + +local function comma_value(amount) + local formatted = amount + local k + while true do + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') + if k == 0 then + break + end + end + return formatted +end + +local function getVehName() + return QBCore.Shared.Vehicles[Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle]["name"] +end + +local function getVehPrice() + return comma_value(QBCore.Shared.Vehicles[Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle]["price"]) +end + +local function getVehBrand() + return QBCore.Shared.Vehicles[Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle]['brand'] +end + +local function setClosestShowroomVehicle() + local pos = GetEntityCoords(PlayerPedId(), true) + local current = nil + local dist = nil + local closestShop = insideShop + for id in pairs(Config.Shops[closestShop]["ShowroomVehicles"]) do + local dist2 = #(pos - vector3(Config.Shops[closestShop]["ShowroomVehicles"][id].coords.x, Config.Shops[closestShop]["ShowroomVehicles"][id].coords.y, Config.Shops[closestShop]["ShowroomVehicles"][id].coords.z)) + if current then + if dist2 < dist then + current = id + dist = dist2 + end + else + dist = dist2 + current = id + end + end + if current ~= ClosestVehicle then + ClosestVehicle = current + end +end + +local function createTestDriveReturn() + testDriveZone = BoxZone:Create( + Config.Shops[insideShop]["ReturnLocation"], + 3.0, + 5.0, + { + name = "box_zone_testdrive_return_" .. insideShop, + }) + + testDriveZone:onPlayerInOut(function(isPointInside) + if isPointInside and IsPedInAnyVehicle(PlayerPedId()) then + SetVehicleForwardSpeed(GetVehiclePedIsIn(PlayerPedId(), false), 0) + exports['qb-menu']:openMenu(returnTestDrive) + else + exports['qb-menu']:closeMenu() + end + end) +end + +local function startTestDriveTimer(testDriveTime, prevCoords) + local gameTimer = GetGameTimer() + CreateThread(function() + Wait(2000) -- Avoids the condition to run before entering vehicle + while inTestDrive do + if GetGameTimer() < gameTimer + tonumber(1000 * testDriveTime) then + local secondsLeft = GetGameTimer() - gameTimer + if secondsLeft >= tonumber(1000 * testDriveTime) - 20 or GetPedInVehicleSeat(NetToVeh(testDriveVeh), -1) ~= PlayerPedId() then + TriggerServerEvent('qb-vehicleshop:server:deleteVehicle', testDriveVeh) + testDriveVeh = 0 + inTestDrive = false + SetEntityCoords(PlayerPedId(), prevCoords) + QBCore.Functions.Notify(Lang:t('general.testdrive_complete')) + end + drawTxt(Lang:t('general.testdrive_timer') .. math.ceil(testDriveTime - secondsLeft / 1000), 4, 0.5, 0.93, 0.50, 255, 255, 255, 180) + end + Wait(0) + end + end) +end + +local function createVehZones(shopName, entity) + if not Config.UsingTarget then + for i = 1, #Config.Shops[shopName]['ShowroomVehicles'] do + zones[#zones + 1] = BoxZone:Create( + vector3(Config.Shops[shopName]['ShowroomVehicles'][i]['coords'].x, + Config.Shops[shopName]['ShowroomVehicles'][i]['coords'].y, + Config.Shops[shopName]['ShowroomVehicles'][i]['coords'].z), + Config.Shops[shopName]['Zone']['size'], + Config.Shops[shopName]['Zone']['size'], + { + name = "box_zone_" .. shopName .. "_" .. i, + minZ = Config.Shops[shopName]['Zone']['minZ'], + maxZ = Config.Shops[shopName]['Zone']['maxZ'], + debugPoly = false, + }) + end + local combo = ComboZone:Create(zones, {name = "vehCombo", debugPoly = false}) + combo:onPlayerInOut(function(isPointInside) + if isPointInside then + if PlayerData and PlayerData.job and (PlayerData.job.name == Config.Shops[insideShop]['Job'] or Config.Shops[insideShop]['Job'] == 'none') then + exports['qb-menu']:showHeader(vehHeaderMenu) + end + else + exports['qb-menu']:closeMenu() + end + end) + else + exports['qb-target']:AddTargetEntity(entity, { + options = { + { + type = "client", + event = "qb-vehicleshop:client:showVehOptions", + icon = "fas fa-car", + label = Lang:t('general.vehinteraction'), + canInteract = function() + local closestShop = insideShop + return closestShop and (Config.Shops[closestShop]['Job'] == 'none' or PlayerData.job.name == Config.Shops[closestShop]['Job']) + end + }, + }, + distance = 3.0 + }) + end +end + +-- Zones +function createFreeUseShop(shopShape, name) + local zone = PolyZone:Create(shopShape, { + name = name, + minZ = shopShape.minZ, + maxZ = shopShape.maxZ + }) + + zone:onPlayerInOut(function(isPointInside) + if isPointInside then + insideShop = name + CreateThread(function() + while insideShop do + setClosestShowroomVehicle() + vehicleMenu = { + { + isMenuHeader = true, + icon = "fa-solid fa-circle-info", + header = getVehBrand():upper() .. ' ' .. getVehName():upper() .. ' - $' .. getVehPrice(), + }, + { + header = Lang:t('menus.test_header'), + txt = Lang:t('menus.freeuse_test_txt'), + icon = "fa-solid fa-car-on", + params = { + event = 'qb-vehicleshop:client:TestDrive', + } + }, + { + header = Lang:t('menus.freeuse_buy_header'), + txt = Lang:t('menus.freeuse_buy_txt'), + icon = "fa-solid fa-hand-holding-dollar", + params = { + isServer = true, + event = 'qb-vehicleshop:server:buyShowroomVehicle', + args = { + buyVehicle = Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle + } + } + }, + { + header = Lang:t('menus.finance_header'), + txt = Lang:t('menus.freeuse_finance_txt'), + icon = "fa-solid fa-coins", + params = { + event = 'qb-vehicleshop:client:openFinance', + args = { + price = getVehPrice(), + buyVehicle = Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle + } + } + }, + { + header = Lang:t('menus.swap_header'), + txt = Lang:t('menus.swap_txt'), + icon = "fa-solid fa-arrow-rotate-left", + params = { + event = Config.FilterByMake and 'qb-vehicleshop:client:vehMakes' or 'qb-vehicleshop:client:vehCategories', + } + }, + } + Wait(1000) + end + end) + else + insideShop = nil + ClosestVehicle = 1 + end + end) +end + +function createManagedShop(shopShape, name) + local zone = PolyZone:Create(shopShape, { + name = name, + minZ = shopShape.minZ, + maxZ = shopShape.maxZ + }) + + zone:onPlayerInOut(function(isPointInside) + if isPointInside then + insideShop = name + CreateThread(function() + while insideShop and PlayerData.job and PlayerData.job.name == Config.Shops[name]['Job'] do + setClosestShowroomVehicle() + vehicleMenu = { + { + isMenuHeader = true, + icon = "fa-solid fa-circle-info", + header = getVehBrand():upper() .. ' ' .. getVehName():upper() .. ' - $' .. getVehPrice(), + }, + { + header = Lang:t('menus.test_header'), + txt = Lang:t('menus.managed_test_txt'), + icon = "fa-solid fa-user-plus", + params = { + event = 'qb-vehicleshop:client:openIdMenu', + args = { + vehicle = Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle, + type = 'testDrive' + } + } + }, + { + header = Lang:t('menus.managed_sell_header'), + txt = Lang:t('menus.managed_sell_txt'), + icon = "fa-solid fa-cash-register", + params = { + event = 'qb-vehicleshop:client:openIdMenu', + args = { + vehicle = Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle, + type = 'sellVehicle' + } + } + }, + { + header = Lang:t('menus.finance_header'), + txt = Lang:t('menus.managed_finance_txt'), + icon = "fa-solid fa-coins", + params = { + event = 'qb-vehicleshop:client:openCustomFinance', + args = { + price = getVehPrice(), + vehicle = Config.Shops[insideShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle + } + } + }, + { + header = Lang:t('menus.swap_header'), + txt = Lang:t('menus.swap_txt'), + icon = "fa-solid fa-arrow-rotate-left", + params = { + event = Config.FilterByMake and 'qb-vehicleshop:client:vehMakes' or 'qb-vehicleshop:client:vehCategories', + } + }, + } + Wait(1000) + end + end) + else + insideShop = nil + ClosestVehicle = 1 + end + end) +end + +function Init() + Initialized = true + CreateThread(function() + for name, shop in pairs(Config.Shops) do + if shop['Type'] == 'free-use' then + createFreeUseShop(shop['Zone']['Shape'], name) + elseif shop['Type'] == 'managed' then + createManagedShop(shop['Zone']['Shape'], name) + end + end + end) + CreateThread(function() + local financeZone = BoxZone:Create(Config.FinanceZone, 2.0, 2.0, { + name = "vehicleshop_financeZone", + offset = {0.0, 0.0, 0.0}, + scale = {1.0, 1.0, 1.0}, + minZ = Config.FinanceZone.z - 1, + maxZ = Config.FinanceZone.z + 1, + debugPoly = false, + }) + + financeZone:onPlayerInOut(function(isPointInside) + if isPointInside then + exports['qb-menu']:showHeader(financeMenu) + else + exports['qb-menu']:closeMenu() + end + end) + end) + CreateThread(function() + for k in pairs(Config.Shops) do + for i = 1, #Config.Shops[k]['ShowroomVehicles'] do + local model = GetHashKey(Config.Shops[k]["ShowroomVehicles"][i].defaultVehicle) + RequestModel(model) + while not HasModelLoaded(model) do + Wait(0) + end + local veh = CreateVehicle(model, Config.Shops[k]["ShowroomVehicles"][i].coords.x, Config.Shops[k]["ShowroomVehicles"][i].coords.y, Config.Shops[k]["ShowroomVehicles"][i].coords.z, false, false) + SetModelAsNoLongerNeeded(model) + SetVehicleOnGroundProperly(veh) + SetEntityInvincible(veh, true) + SetVehicleDirtLevel(veh, 0.0) + SetVehicleDoorsLocked(veh, 3) + SetEntityHeading(veh, Config.Shops[k]["ShowroomVehicles"][i].coords.w) + FreezeEntityPosition(veh, true) + SetVehicleNumberPlateText(veh, 'BUY ME') + if Config.UsingTarget then createVehZones(k, veh) end + end + if not Config.UsingTarget then createVehZones(k) end + end + end) +end + +-- Events +RegisterNetEvent('qb-vehicleshop:client:homeMenu', function() + exports['qb-menu']:openMenu(vehicleMenu) +end) + +RegisterNetEvent('qb-vehicleshop:client:showVehOptions', function() + exports['qb-menu']:openMenu(vehicleMenu, true, true) +end) + +RegisterNetEvent('qb-vehicleshop:client:TestDrive', function() + if not inTestDrive and ClosestVehicle ~= 0 then + inTestDrive = true + local prevCoords = GetEntityCoords(PlayerPedId()) + tempShop = insideShop -- temp hacky way of setting the shop because it changes after the callback has returned since you are outside the zone + QBCore.Functions.TriggerCallback('QBCore:Server:SpawnVehicle', function(netId) + local veh = NetToVeh(netId) + exports['LegacyFuel']:SetFuel(veh, 100) + SetVehicleNumberPlateText(veh, 'TESTDRIVE') + SetEntityHeading(veh, Config.Shops[tempShop]["TestDriveSpawn"].w) + TriggerEvent('vehiclekeys:client:SetOwner', QBCore.Functions.GetPlate(veh)) + testDriveVeh = netId + QBCore.Functions.Notify(Lang:t('general.testdrive_timenoti', {testdrivetime = Config.Shops[tempShop]["TestDriveTimeLimit"]})) + end, Config.Shops[tempShop]["ShowroomVehicles"][ClosestVehicle].chosenVehicle, Config.Shops[tempShop]["TestDriveSpawn"], true) + createTestDriveReturn() + startTestDriveTimer(Config.Shops[tempShop]["TestDriveTimeLimit"] * 60, prevCoords) + else + QBCore.Functions.Notify(Lang:t('error.testdrive_alreadyin'), 'error') + end +end) + +RegisterNetEvent('qb-vehicleshop:client:customTestDrive', function(data) + if not inTestDrive then + inTestDrive = true + local vehicle = data + local prevCoords = GetEntityCoords(PlayerPedId()) + tempShop = insideShop -- temp hacky way of setting the shop because it changes after the callback has returned since you are outside the zone + QBCore.Functions.TriggerCallback('QBCore:Server:SpawnVehicle', function(netId) + local veh = NetToVeh(netId) + exports['LegacyFuel']:SetFuel(veh, 100) + SetVehicleNumberPlateText(veh, 'TESTDRIVE') + SetEntityHeading(veh, Config.Shops[tempShop]["TestDriveSpawn"].w) + TriggerEvent('vehiclekeys:client:SetOwner', QBCore.Functions.GetPlate(veh)) + testDriveVeh = netId + QBCore.Functions.Notify(Lang:t('general.testdrive_timenoti', {testdrivetime = Config.Shops[tempShop]["TestDriveTimeLimit"]})) + end, vehicle, Config.Shops[tempShop]["TestDriveSpawn"], true) + createTestDriveReturn() + startTestDriveTimer(Config.Shops[tempShop]["TestDriveTimeLimit"] * 60, prevCoords) + else + QBCore.Functions.Notify(Lang:t('error.testdrive_alreadyin'), 'error') + end +end) + +RegisterNetEvent('qb-vehicleshop:client:TestDriveReturn', function() + local ped = PlayerPedId() + local veh = GetVehiclePedIsIn(ped) + local entity = NetworkGetEntityFromNetworkId(testDriveVeh) + if veh == entity then + testDriveVeh = 0 + inTestDrive = false + DeleteEntity(veh) + exports['qb-menu']:closeMenu() + testDriveZone:destroy() + else + QBCore.Functions.Notify(Lang:t('error.testdrive_return'), 'error') + end +end) + +RegisterNetEvent('qb-vehicleshop:client:vehCategories', function(data) + local catmenu = {} + local firstvalue = nil + local categoryMenu = { + { + header = Lang:t('menus.goback_header'), + icon = "fa-solid fa-angle-left", + params = { + event = Config.FilterByMake and 'qb-vehicleshop:client:vehMakes' or 'qb-vehicleshop:client:homeMenu' + } + } + } + for k, v in pairs(QBCore.Shared.Vehicles) do + if type(QBCore.Shared.Vehicles[k]["shop"]) == 'table' then + for _, shop in pairs(QBCore.Shared.Vehicles[k]["shop"]) do + if shop == insideShop and (not Config.FilterByMake or QBCore.Shared.Vehicles[k]["brand"] == data.make) then + catmenu[v.category] = v.category + if firstvalue == nil then + firstvalue = v.category + end + end + end + elseif QBCore.Shared.Vehicles[k]["shop"] == insideShop and (not Config.FilterByMake or QBCore.Shared.Vehicles[k]["brand"] == data.make) then + catmenu[v.category] = v.category + if firstvalue == nil then + firstvalue = v.category + end + end + end + if Config.HideCategorySelectForOne and tablelength(catmenu) == 1 then + TriggerEvent("qb-vehicleshop:client:openVehCats", {catName = firstvalue, make = data.make, onecat = true}) + return + end + for k, v in pairs(catmenu) do + categoryMenu[#categoryMenu + 1] = { + header = v, + icon = "fa-solid fa-circle", + params = { + event = 'qb-vehicleshop:client:openVehCats', + args = { + catName = k, + } + } + } + end + exports['qb-menu']:openMenu(categoryMenu, Config.SortAlphabetically, true) +end) + +RegisterNetEvent('qb-vehicleshop:client:openVehCats', function(data) + local vehMenu = { + { + header = Lang:t('menus.goback_header'), + icon = "fa-solid fa-angle-left", + params = { + event = 'qb-vehicleshop:client:vehCategories', + args = { + make = data.make + } + } + } + } + if data.onecat == true then + vehMenu[1].params = { + event = 'qb-vehicleshop:client:vehMakes' + } + end + for k, v in pairs(QBCore.Shared.Vehicles) do + if QBCore.Shared.Vehicles[k]["category"] == data.catName then + if type(QBCore.Shared.Vehicles[k]["shop"]) == 'table' then + for _, shop in pairs(QBCore.Shared.Vehicles[k]["shop"]) do + if shop == insideShop then + vehMenu[#vehMenu + 1] = { + header = v.name, + txt = Lang:t('menus.veh_price') .. v.price, + icon = "fa-solid fa-car-side", + params = { + isServer = true, + event = 'qb-vehicleshop:server:swapVehicle', + args = { + toVehicle = v.model, + ClosestVehicle = ClosestVehicle, + ClosestShop = insideShop + } + } + } + end + end + elseif QBCore.Shared.Vehicles[k]["shop"] == insideShop then + vehMenu[#vehMenu + 1] = { + header = v.name, + txt = Lang:t('menus.veh_price') .. v.price, + icon = "fa-solid fa-car-side", + params = { + isServer = true, + event = 'qb-vehicleshop:server:swapVehicle', + args = { + toVehicle = v.model, + ClosestVehicle = ClosestVehicle, + ClosestShop = insideShop + } + } + } + end + end + end + exports['qb-menu']:openMenu(vehMenu, Config.SortAlphabetically, true) +end) + +RegisterNetEvent('qb-vehicleshop:client:vehMakes', function() + local makmenu = {} + local makeMenu = { + { + header = Lang:t('menus.goback_header'), + icon = "fa-solid fa-angle-left", + params = { + event = 'qb-vehicleshop:client:homeMenu' + } + } + } + for k, v in pairs(QBCore.Shared.Vehicles) do + if type(QBCore.Shared.Vehicles[k]["shop"]) == "table" then + for _, shop in pairs(QBCore.Shared.Vehicles[k]["shop"]) do + if shop == insideShop then + makmenu[v.brand] = v.brand + end + end + elseif QBCore.Shared.Vehicles[k]["shop"] == insideShop then + makmenu[v.brand] = v.brand + end + end + for k, v in pairs(makmenu) do + makeMenu[#makeMenu + 1] = { + header = v, + icon = "fa-solid fa-circle", + params = { + event = 'qb-vehicleshop:client:vehCategories', + args = { + make = v + } + } + } + end + exports['qb-menu']:openMenu(makeMenu, Config.SortAlphabetically, true) +end) + +RegisterNetEvent('qb-vehicleshop:client:openFinance', function(data) + local dialog = exports['qb-input']:ShowInput({ + header = getVehBrand():upper() .. ' ' .. data.buyVehicle:upper() .. ' - $' .. data.price, + submitText = Lang:t('menus.submit_text'), + inputs = { + { + type = 'number', + isRequired = true, + name = 'downPayment', + text = Lang:t('menus.financesubmit_downpayment') .. Config.MinimumDown .. '%' + }, + { + type = 'number', + isRequired = true, + name = 'paymentAmount', + text = Lang:t('menus.financesubmit_totalpayment') .. Config.MaximumPayments + } + } + }) + if dialog then + if not dialog.downPayment or not dialog.paymentAmount then return end + TriggerServerEvent('qb-vehicleshop:server:financeVehicle', dialog.downPayment, dialog.paymentAmount, data.buyVehicle) + end +end) + +RegisterNetEvent('qb-vehicleshop:client:openCustomFinance', function(data) + TriggerEvent('animations:client:EmoteCommandStart', {"tablet2"}) + local dialog = exports['qb-input']:ShowInput({ + header = getVehBrand():upper() .. ' ' .. data.vehicle:upper() .. ' - $' .. data.price, + submitText = Lang:t('menus.submit_text'), + inputs = { + { + type = 'number', + isRequired = true, + name = 'downPayment', + text = Lang:t('menus.financesubmit_downpayment') .. Config.MinimumDown .. '%' + }, + { + type = 'number', + isRequired = true, + name = 'paymentAmount', + text = Lang:t('menus.financesubmit_totalpayment') .. Config.MaximumPayments + }, + { + text = Lang:t('menus.submit_ID'), + name = "playerid", + type = "number", + isRequired = true + } + } + }) + if dialog then + if not dialog.downPayment or not dialog.paymentAmount or not dialog.playerid then return end + TriggerEvent('animations:client:EmoteCommandStart', {"c"}) + TriggerServerEvent('qb-vehicleshop:server:sellfinanceVehicle', dialog.downPayment, dialog.paymentAmount, data.vehicle, dialog.playerid) + end +end) + +RegisterNetEvent('qb-vehicleshop:client:swapVehicle', function(data) + local shopName = data.ClosestShop + if Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].chosenVehicle ~= data.toVehicle then + local closestVehicle, closestDistance = QBCore.Functions.GetClosestVehicle(vector3(Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.x, Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.y, Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.z)) + if closestVehicle == 0 then return end + if closestDistance < 5 then DeleteEntity(closestVehicle) end + while DoesEntityExist(closestVehicle) do + Wait(50) + end + Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].chosenVehicle = data.toVehicle + local model = GetHashKey(data.toVehicle) + RequestModel(model) + while not HasModelLoaded(model) do + Wait(50) + end + local veh = CreateVehicle(model, Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.x, Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.y, Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.z, false, false) + while not DoesEntityExist(veh) do + Wait(50) + end + SetModelAsNoLongerNeeded(model) + SetVehicleOnGroundProperly(veh) + SetEntityInvincible(veh, true) + SetEntityHeading(veh, Config.Shops[shopName]["ShowroomVehicles"][data.ClosestVehicle].coords.w) + SetVehicleDoorsLocked(veh, 3) + FreezeEntityPosition(veh, true) + SetVehicleNumberPlateText(veh, 'BUY ME') + if Config.UsingTarget then createVehZones(shopName, veh) end + end +end) + +RegisterNetEvent('qb-vehicleshop:client:buyShowroomVehicle', function(vehicle, plate) + tempShop = insideShop -- temp hacky way of setting the shop because it changes after the callback has returned since you are outside the zone + QBCore.Functions.TriggerCallback('QBCore:Server:SpawnVehicle', function(netId) + local veh = NetToVeh(netId) + exports['LegacyFuel']:SetFuel(veh, 100) + SetVehicleNumberPlateText(veh, plate) + SetEntityHeading(veh, Config.Shops[tempShop]["VehicleSpawn"].w) + TriggerEvent("vehiclekeys:client:SetOwner", QBCore.Functions.GetPlate(veh)) + TriggerServerEvent("qb-vehicletuning:server:SaveVehicleProps", QBCore.Functions.GetVehicleProperties(veh)) + end, vehicle, Config.Shops[tempShop]["VehicleSpawn"], true) +end) + +RegisterNetEvent('qb-vehicleshop:client:getVehicles', function() + QBCore.Functions.TriggerCallback('qb-vehicleshop:server:getVehicles', function(vehicles) + local ownedVehicles = {} + for _, v in pairs(vehicles) do + if v.balance ~= 0 then + local name = QBCore.Shared.Vehicles[v.vehicle]["name"] + local plate = v.plate:upper() + ownedVehicles[#ownedVehicles + 1] = { + header = name, + txt = Lang:t('menus.veh_platetxt') .. plate, + icon = "fa-solid fa-car-side", + params = { + event = 'qb-vehicleshop:client:getVehicleFinance', + args = { + vehiclePlate = plate, + balance = v.balance, + paymentsLeft = v.paymentsleft, + paymentAmount = v.paymentamount + } + } + } + end + end + if #ownedVehicles > 0 then + exports['qb-menu']:openMenu(ownedVehicles) + else + QBCore.Functions.Notify(Lang:t('error.nofinanced'), 'error', 7500) + end + end) +end) + +RegisterNetEvent('qb-vehicleshop:client:getVehicleFinance', function(data) + local vehFinance = { + { + header = Lang:t('menus.goback_header'), + params = { + event = 'qb-vehicleshop:client:getVehicles' + } + }, + { + isMenuHeader = true, + icon = "fa-solid fa-sack-dollar", + header = Lang:t('menus.veh_finance_balance'), + txt = Lang:t('menus.veh_finance_currency') .. comma_value(data.balance) + }, + { + isMenuHeader = true, + icon = "fa-solid fa-hashtag", + header = Lang:t('menus.veh_finance_total'), + txt = data.paymentsLeft + }, + { + isMenuHeader = true, + icon = "fa-solid fa-sack-dollar", + header = Lang:t('menus.veh_finance_reccuring'), + txt = Lang:t('menus.veh_finance_currency') .. comma_value(data.paymentAmount) + }, + { + header = Lang:t('menus.veh_finance_pay'), + icon = "fa-solid fa-hand-holding-dollar", + params = { + event = 'qb-vehicleshop:client:financePayment', + args = { + vehData = data, + paymentsLeft = data.paymentsleft, + paymentAmount = data.paymentamount + } + } + }, + { + header = Lang:t('menus.veh_finance_payoff'), + icon = "fa-solid fa-hand-holding-dollar", + params = { + isServer = true, + event = 'qb-vehicleshop:server:financePaymentFull', + args = { + vehBalance = data.balance, + vehPlate = data.vehiclePlate + } + } + }, + } + exports['qb-menu']:openMenu(vehFinance) +end) + +RegisterNetEvent('qb-vehicleshop:client:financePayment', function(data) + local dialog = exports['qb-input']:ShowInput({ + header = Lang:t('menus.veh_finance'), + submitText = Lang:t('menus.veh_finance_pay'), + inputs = { + { + type = 'number', + isRequired = true, + name = 'paymentAmount', + text = Lang:t('menus.veh_finance_payment') + } + } + }) + if dialog then + if not dialog.paymentAmount then return end + TriggerServerEvent('qb-vehicleshop:server:financePayment', dialog.paymentAmount, data.vehData) + end +end) + +RegisterNetEvent('qb-vehicleshop:client:openIdMenu', function(data) + local dialog = exports['qb-input']:ShowInput({ + header = QBCore.Shared.Vehicles[data.vehicle]["name"], + submitText = Lang:t('menus.submit_text'), + inputs = { + { + text = Lang:t('menus.submit_ID'), + name = "playerid", + type = "number", + isRequired = true + } + } + }) + if dialog then + if not dialog.playerid then return end + if data.type == 'testDrive' then + TriggerServerEvent('qb-vehicleshop:server:customTestDrive', data.vehicle, dialog.playerid) + elseif data.type == 'sellVehicle' then + TriggerServerEvent('qb-vehicleshop:server:sellShowroomVehicle', data.vehicle, dialog.playerid) + end + end +end) + +-- Threads +CreateThread(function() + for k, v in pairs(Config.Shops) do + if v.showBlip then + local Dealer = AddBlipForCoord(Config.Shops[k]["Location"]) + SetBlipSprite(Dealer, Config.Shops[k]["blipSprite"]) + SetBlipDisplay(Dealer, 4) + SetBlipScale(Dealer, 0.70) + SetBlipAsShortRange(Dealer, true) + SetBlipColour(Dealer, Config.Shops[k]["blipColor"]) + BeginTextCommandSetBlipName("STRING") + AddTextComponentSubstringPlayerName(Config.Shops[k]["ShopLabel"]) + EndTextCommandSetBlipName(Dealer) + end + end +end) diff --git a/[qb]/qb-vehicleshop/config.lua b/[qb]/qb-vehicleshop/config.lua new file mode 100644 index 0000000..b5b8288 --- /dev/null +++ b/[qb]/qb-vehicleshop/config.lua @@ -0,0 +1,288 @@ +Config = {} +Config.UsingTarget = GetConvar('UseTarget', 'false') == 'true' +Config.Commission = 0.10 -- Percent that goes to sales person from a full car sale 10% +Config.FinanceCommission = 0.05 -- Percent that goes to sales person from a finance sale 5% +Config.FinanceZone = vector3(-29.53, -1103.67, 26.42)-- Where the finance menu is located +Config.PaymentWarning = 10 -- time in minutes that player has to make payment before repo +Config.PaymentInterval = 24 -- time in hours between payment being due +Config.MinimumDown = 10 -- minimum percentage allowed down +Config.MaximumPayments = 24 -- maximum payments allowed +Config.PreventFinanceSelling = false -- allow/prevent players from using /transfervehicle if financed +Config.FilterByMake = false -- adds a make list before selecting category in shops +Config.SortAlphabetically = true -- will sort make, category, and vehicle selection menus alphabetically +Config.HideCategorySelectForOne = true -- will hide the category selection menu if a shop only sells one category of vehicle or a make has only one category +Config.Shops = { + ['pdm'] = { + ['Type'] = 'free-use', -- no player interaction is required to purchase a car + ['Zone'] = { + ['Shape'] = {--polygon that surrounds the shop + vector2(-56.727394104004, -1086.2325439453), + vector2(-60.612808227539, -1096.7795410156), + vector2(-58.26834487915, -1100.572265625), + vector2(-35.927803039551, -1109.0034179688), + vector2(-34.427627563477, -1108.5111083984), + vector2(-32.02657699585, -1101.5877685547), + vector2(-33.342102050781, -1101.0377197266), + vector2(-31.292987823486, -1095.3717041016) + }, + ['minZ'] = 25.0, -- min height of the shop zone + ['maxZ'] = 28.0, -- max height of the shop zone + ['size'] = 2.75 -- size of the vehicles zones + }, + ['Job'] = 'none', -- Name of job or none + ['ShopLabel'] = 'Premium Deluxe Motorsport', -- Blip name + ['showBlip'] = true, -- true or false + ['blipSprite'] = 326, -- Blip sprite + ['blipColor'] = 3, -- Blip color + ['TestDriveTimeLimit'] = 0.5, -- Time in minutes until the vehicle gets deleted + ['Location'] = vector3(-45.67, -1098.34, 26.42), -- Blip Location + ['ReturnLocation'] = vector3(-44.74, -1082.58, 26.68), -- Location to return vehicle, only enables if the vehicleshop has a job owned + ['VehicleSpawn'] = vector4(-56.79, -1109.85, 26.43, 71.5), -- Spawn location when vehicle is bought + ['TestDriveSpawn'] = vector4(-56.79, -1109.85, 26.43, 71.5), -- Spawn location for test drive + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(-45.65, -1093.66, 25.44, 69.5), -- where the vehicle will spawn on display + defaultVehicle = 'ardent', -- Default display vehicle + chosenVehicle = 'ardent', -- Same as default but is dynamically changed when swapping vehicles + }, + [2] = { + coords = vector4(-48.27, -1101.86, 25.44, 294.5), + defaultVehicle = 'schafter2', + chosenVehicle = 'schafter2' + }, + [3] = { + coords = vector4(-39.6, -1096.01, 25.44, 66.5), + defaultVehicle = 'coquette', + chosenVehicle = 'coquette' + }, + [4] = { + coords = vector4(-51.21, -1096.77, 25.44, 254.5), + defaultVehicle = 'vigero', + chosenVehicle = 'vigero' + }, + [5] = { + coords = vector4(-40.18, -1104.13, 25.44, 338.5), + defaultVehicle = 'rhapsody', + chosenVehicle = 'rhapsody' + }, + [6] = { + coords = vector4(-43.31, -1099.02, 25.44, 52.5), + defaultVehicle = 'bati', + chosenVehicle = 'bati' + }, + [7] = { + coords = vector4(-50.66, -1093.05, 25.44, 222.5), + defaultVehicle = 'bati', + chosenVehicle = 'bati' + }, + [8] = { + coords = vector4(-44.28, -1102.47, 25.44, 298.5), + defaultVehicle = 'bati', + chosenVehicle = 'bati' + } + }, + }, + ['luxury'] = { + ['Type'] = 'managed', -- meaning a real player has to sell the car + ['Zone'] = { + ['Shape'] = { + vector2(-1260.6973876953, -349.21334838867), + vector2(-1268.6248779297, -352.87365722656), + vector2(-1274.1533203125, -358.29794311523), + vector2(-1273.8425292969, -362.73715209961), + vector2(-1270.5701904297, -368.6716003418), + vector2(-1266.0561523438, -375.14080810547), + vector2(-1244.3684082031, -362.70278930664), + vector2(-1249.8704833984, -352.03326416016), + vector2(-1252.9503173828, -345.85726928711) + }, + ['minZ'] = 36.646457672119, + ['maxZ'] = 37.516143798828, + ['size'] = 2.75 -- size of the vehicles zones + }, + ['Job'] = 'cardealer', -- Name of job or none + ['ShopLabel'] = 'Luxury Vehicle Shop', + ['showBlip'] = true, -- true or false + ['blipSprite'] = 326, -- Blip sprite + ['blipColor'] = 3, -- Blip color + ['TestDriveTimeLimit'] = 0.5, + ['Location'] = vector3(-1255.6, -361.16, 36.91), + ['ReturnLocation'] = vector3(-1231.46, -349.86, 37.33), + ['VehicleSpawn'] = vector4(-1231.46, -349.86, 37.33, 26.61), + ['TestDriveSpawn'] = vector4(-1232.81, -347.99, 37.33, 23.28), -- Spawn location for test drive + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(-1265.31, -354.44, 35.91, 205.08), + defaultVehicle = 'italirsx', + chosenVehicle = 'italirsx' + }, + [2] = { + coords = vector4(-1270.06, -358.55, 35.91, 247.08), + defaultVehicle = 'italigtb', + chosenVehicle = 'italigtb' + }, + [3] = { + coords = vector4(-1269.21, -365.03, 35.91, 297.12), + defaultVehicle = 'nero', + chosenVehicle = 'nero' + }, + [4] = { + coords = vector4(-1252.07, -364.2, 35.91, 56.44), + defaultVehicle = 'bati', + chosenVehicle = 'bati' + }, + [5] = { + coords = vector4(-1255.49, -365.91, 35.91, 55.63), + defaultVehicle = 'carbonrs', + chosenVehicle = 'carbonrs' + }, + [6] = { + coords = vector4(-1249.21, -362.97, 35.91, 53.24), + defaultVehicle = 'hexer', + chosenVehicle = 'hexer' + }, + } + }, -- Add your next table under this comma + ['boats'] = { + ['Type'] = 'free-use', -- no player interaction is required to purchase a vehicle + ['Zone'] = { + ['Shape'] = {--polygon that surrounds the shop + vector2(-729.39, -1315.84), + vector2(-766.81, -1360.11), + vector2(-754.21, -1371.49), + vector2(-716.94, -1326.88) + }, + ['minZ'] = 0.0, -- min height of the shop zone + ['maxZ'] = 5.0, -- max height of the shop zone + ['size'] = 6.2 -- size of the vehicles zones + }, + ['Job'] = 'none', -- Name of job or none + ['ShopLabel'] = 'Marina Shop', -- Blip name + ['showBlip'] = true, -- true or false + ['blipSprite'] = 410, -- Blip sprite + ['blipColor'] = 3, -- Blip color + ['TestDriveTimeLimit'] = 1.5, -- Time in minutes until the vehicle gets deleted + ['Location'] = vector3(-738.25, -1334.38, 1.6), -- Blip Location + ['ReturnLocation'] = vector3(-714.34, -1343.31, 0.0), -- Location to return vehicle, only enables if the vehicleshop has a job owned + ['VehicleSpawn'] = vector4(-727.87, -1353.1, -0.17, 137.09), -- Spawn location when vehicle is bought + ['TestDriveSpawn'] = vector4(-722.23, -1351.98, 0.14, 135.33), -- Spawn location for test drive + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(-727.05, -1326.59, 0.00, 229.5), -- where the vehicle will spawn on display + defaultVehicle = 'seashark', -- Default display vehicle + chosenVehicle = 'seashark' -- Same as default but is dynamically changed when swapping vehicles + }, + [2] = { + coords = vector4(-732.84, -1333.5, -0.50, 229.5), + defaultVehicle = 'dinghy', + chosenVehicle = 'dinghy' + }, + [3] = { + coords = vector4(-737.84, -1340.83, -0.50, 229.5), + defaultVehicle = 'speeder', + chosenVehicle = 'speeder' + }, + [4] = { + coords = vector4(-741.53, -1349.7, -2.00, 229.5), + defaultVehicle = 'marquis', + chosenVehicle = 'marquis' + }, + }, + }, + ['air'] = { + ['Type'] = 'free-use', -- no player interaction is required to purchase a vehicle + ['Zone'] = { + ['Shape'] = {--polygon that surrounds the shop + vector2(-1607.58, -3141.7), + vector2(-1672.54, -3103.87), + vector2(-1703.49, -3158.02), + vector2(-1646.03, -3190.84) + }, + ['minZ'] = 12.99, -- min height of the shop zone + ['maxZ'] = 16.99, -- max height of the shop zone + ['size'] = 7.0, -- size of the vehicles zones + }, + ['Job'] = 'none', -- Name of job or none + ['ShopLabel'] = 'Air Shop', -- Blip name + ['showBlip'] = true, -- true or false + ['blipSprite'] = 251, -- Blip sprite + ['blipColor'] = 3, -- Blip color + ['TestDriveTimeLimit'] = 1.5, -- Time in minutes until the vehicle gets deleted + ['Location'] = vector3(-1652.76, -3143.4, 13.99), -- Blip Location + ['ReturnLocation'] = vector3(-1628.44, -3104.7, 13.94), -- Location to return vehicle, only enables if the vehicleshop has a job owned + ['VehicleSpawn'] = vector4(-1617.49, -3086.17, 13.94, 329.2), -- Spawn location when vehicle is bought + ['TestDriveSpawn'] = vector4(-1625.19, -3103.47, 13.94, 330.28), -- Spawn location for test drive + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(-1651.36, -3162.66, 12.99, 346.89), -- where the vehicle will spawn on display + defaultVehicle = 'volatus', -- Default display vehicle + chosenVehicle = 'volatus' -- Same as default but is dynamically changed when swapping vehicles + }, + [2] = { + coords = vector4(-1668.53, -3152.56, 12.99, 303.22), + defaultVehicle = 'luxor2', + chosenVehicle = 'luxor2' + }, + [3] = { + coords = vector4(-1632.02, -3144.48, 12.99, 31.08), + defaultVehicle = 'nimbus', + chosenVehicle = 'nimbus' + }, + [4] = { + coords = vector4(-1663.74, -3126.32, 12.99, 275.03), + defaultVehicle = 'frogger', + chosenVehicle = 'frogger' + }, + }, + }, + ['truck'] = { + ['Type'] = 'free-use', -- no player interaction is required to purchase a car + ['Zone'] = { + ['Shape'] = {--polygon that surrounds the shop + vector2(872.23, -1173.5), + vector2(868.88, -1162.7), + vector2(900.91, -1156.54), + vector2(901.96, -1173.71), + vector2(883.59, -1174.47), + vector2(884.59, -1161.29), + vector2(890.06, -1155.0), + vector2(907.71, -1168.71) + }, + ['minZ'] = 23.0, -- min height of the shop zone + ['maxZ'] = 28.0, -- max height of the shop zone + ['size'] = 5.75 -- size of the vehicles zones + }, + ['Job'] = 'none', -- Nome de trabalho ou nenhum + ['ShopLabel'] = 'Truck Motor Shop', -- Blip name + ['showBlip'] = true, -- true or false + ['blipSprite'] = 477, -- Blip sprite + ['blipColor'] = 2, -- Blip color + ['TestDriveTimeLimit'] = 0.5, -- Time in minutes until the vehicle gets deleted + ['Location'] = vector3(900.47, -1155.74, 25.16), -- Blip Location + ['ReturnLocation'] = vector3(900.47, -1155.74, 25.16), -- Location to return vehicle, only enables if the vehicleshop has a job owned + ['VehicleSpawn'] = vector4(909.35, -1181.58, 25.55, 177.57), -- Spawn location when vehicle is bought + ['TestDriveSpawn'] = vector4(867.65, -1192.4, 25.37, 95.72), -- Spawn location for test drive + ['ShowroomVehicles'] = { + [1] = { + coords = vector4(890.84, -1170.92, 25.08, 269.58), -- where the vehicle will spawn on display + defaultVehicle = 'hauler', -- Default display vehicle + chosenVehicle = 'hauler', -- Same as default but is dynamically changed when swapping vehicles + }, + [2] = { + coords = vector4(878.45, -1171.04, 25.05, 273.08), + defaultVehicle = 'phantom', + chosenVehicle = 'phantom' + }, + [3] = { + coords = vector4(880.44, -1163.59, 24.87, 273.08), + defaultVehicle = 'mule', + chosenVehicle = 'mule' + }, + [4] = { + coords = vector4(896.95, -1162.62, 24.98, 273.08), + defaultVehicle = 'mixer', + chosenVehicle = 'mixer' + }, + }, + }, +} diff --git a/[qb]/qb-vehicleshop/fxmanifest.lua b/[qb]/qb-vehicleshop/fxmanifest.lua new file mode 100644 index 0000000..4466650 --- /dev/null +++ b/[qb]/qb-vehicleshop/fxmanifest.lua @@ -0,0 +1,28 @@ +fx_version 'cerulean' +game 'gta5' + +description 'qb-vehicleshop' +version '2.1.0' + +shared_script { + 'config.lua', + '@qb-core/shared/locale.lua', + 'locales/en.lua', + 'locales/*.lua' +} + +client_scripts { + '@PolyZone/client.lua', + '@PolyZone/BoxZone.lua', + '@PolyZone/EntityZone.lua', + '@PolyZone/CircleZone.lua', + '@PolyZone/ComboZone.lua', + 'client.lua' +} + +server_scripts { + '@oxmysql/lib/MySQL.lua', + 'server.lua' +} + +lua54 'yes' diff --git a/[qb]/qb-vehicleshop/locales/cs.lua b/[qb]/qb-vehicleshop/locales/cs.lua new file mode 100644 index 0000000..e071615 --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/cs.lua @@ -0,0 +1,84 @@ +local Translations = { + error = { + testdrive_alreadyin = "Uz jsi v testovaci jizde!", + testdrive_return = "Toto neni vozidlo, ktere jsi testoval", + Invalid_ID = "Spatne ID", + playertoofar = "Hrac je moc daleko", + notenoughmoney = "Nemas dostatek penez", + minimumallowed = "Minimalni povolena vyse je CZK", + overpaid = "Preplatil jsi", + alreadypaid = "Vozidlo je uz zaplaceno", + notworth = "Vozidlo nema takovou cenu", + downtoosmall = "Down payment too small", + exceededmax = "Prekrocil jsi maximalni castku", + repossessed = "Tvoje registracni znacka %{plate} byla odebrana", + buyerinfo = "Nemohl jsem zjistit info kupujiciho", + notinveh = "Musis byt ve vozidle pokud ho chces prepsat", + vehinfo = "Nemohl jsem zjistit info o vozidle", + notown = "Nevlastnis toto vozidlo", + buyertoopoor = "Kupujuci nema dostatek financi", + nofinanced = "Nemas zadne vozidlo na splatky", + }, + success = { + purchased = "Gratulujeme k vyberu! At slouzi!", + earned_commission = "Dostal jsi %{amount} CZK v komisnim prodeji", + gifted = "Daroval jsi vozidlo", + received_gift = "Dostal jsi dar v podobe vozidla", + soldfor = "Prodal jsi vozidlo za CZK", + boughtfor = "Koupil jsi vozidlo za CZK", + }, + menus = { + vehHeader_header = "Moznosti vozidla", + vehHeader_txt = "Ovladej momentalni vozidlo", + financed_header = "Vozidla na splatky", + finance_txt = "Podivej se na vlastni vozidla na splatky", + returnTestDrive_header = "Ukoncit testovaci jizdu", + goback_header = "Jit zpet", + veh_price = "Cena: CZK", + veh_platetxt = "RZ: ", + veh_finance = "Financovani vozidla", + veh_finance_balance = "Celkova castka, ktera zbyva", + veh_finance_currency = "CZK", + veh_finance_total = "Celkova castka, ktera zbyva", + veh_finance_reccuring = "Cekajici platba castka", + veh_finance_pay = "Udelat platbu", + veh_finance_payoff = "Vyplaceni vozidla", + veh_finance_payment = "Castka k zaplaceni (CZK)", + submit_text = "Potvrdit", + test_header = "Testovaci jizda", + finance_header = "Financovani vozidla", + swap_header = "Vymenit vozidlo", + swap_txt = "Podivej se na jine vozidla", + financesubmit_downpayment = "Castka zalohy - Min ", + financesubmit_totalpayment = "Celkova castka - Max ", + --Free Use + freeuse_test_txt = "Vyzkousej momentalne vybrane vozidlo!", + freeuse_buy_header = "Koupit vozidlo", + freeuse_buy_txt = "Koupit momentalne vybrane vozidlo", + freeuse_finance_txt = "Financuj momentalne vybrane vozidlo", + --Managed + managed_test_txt = "Povolit hraci testovaci jizdu", + managed_sell_header = "Prodej vozidlo", + managed_sell_txt = "Prodej vozidlo hraci", + managed_finance_txt = "Financovani vozidla", + submit_ID = "Server ID (#)", + }, + general = { + testdrive_timer = "Kolik zbyva do konce testovaci jizdy:", + vehinteraction = "Ovladej vozidlo", + testdrive_timenoti = "Zbyva ti %{testdrivetime} minut", + testdrive_complete = "Testovaci jizda ukoncena!", + paymentduein = "Musis provest platbu vozidla do %{time} minut", + command_transfervehicle = "Daruj nebo prodej vozidlo", + command_transfervehicle_help = "ID kupujiciho", + command_transfervehicle_amount = "Prodejni castka (Na tobe)", + } +} + +if GetConvar('qb_locale', 'en') == 'cs' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehicleshop/locales/de.lua b/[qb]/qb-vehicleshop/locales/de.lua new file mode 100644 index 0000000..95754d3 --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/de.lua @@ -0,0 +1,84 @@ +local Translations = { + error = { + testdrive_alreadyin = "Bereits in der Probefahrt", + testdrive_return = "Dies ist kein Fahrzeug für eine Probefahrt", + Invalid_ID = "Ungültige Bürger-ID angegeben", + playertoofar = "Dieser Bürger ist nicht nah genug dran", + notenoughmoney = "Nicht genug Geld", + minimumallowed = "Die zulässige Mindestzahlung beträgt $", + overpaid = "Sie haben zu viel bezahlt", + alreadypaid = "Vehicle is already paid off", + notworth = "Vehicle is not worth that much", + downtoosmall = "Down payment too small", + exceededmax = "Exceeded maximum payment amount", + repossessed = "Your vehicle with plate %{plate} has been repossessed", + buyerinfo = "Couldn\'t get buyer info", + notinveh = "You must be in the vehicle you want to transfer", + vehinfo = "Couldn\'t get vehicle info", + notown = "You don\'t own this vehicle", + buyertoopoor = "The buyer doesn\'t have enough money", + nofinanced = "You don't have any financed vehicles", + }, + success = { + purchased = "Congratulations on your purchase!", + earned_commission = "You earned $ %{amount} in commission", + gifted = "You gifted your vehicle", + received_gift = "You were gifted a vehicle", + soldfor = "You sold your vehicle for $", + boughtfor = "You bought a vehicle for $", + }, + menus = { + vehHeader_header = "Vehicle Options", + vehHeader_txt = "Interact with the current vehicle", + financed_header = "Financed Vehicles", + finance_txt = "Browse your owned vehicles", + returnTestDrive_header = "Finish Test Drive", + goback_header = "Go Back", + veh_price = "Price: $", + veh_platetxt = "Plate: ", + veh_finance = "Vehicle Payment", + veh_finance_balance = "Total Balance Remaining", + veh_finance_currency = "$", + veh_finance_total = "Total Payments Remaining", + veh_finance_reccuring = "Recurring Payment Amount", + veh_finance_pay = "Make a payment", + veh_finance_payoff = "Payoff vehicle", + veh_finance_payment = "Payment Amount ($)", + submit_text = "Submit", + test_header = "Test Drive", + finance_header = "Finance Vehicle", + swap_header = "Swap Vehicle", + swap_txt = "Change currently selected vehicle", + financesubmit_downpayment = "Down Payment Amount - Min ", + financesubmit_totalpayment = "Total Payments - Max ", + --Free Use + freeuse_test_txt = "Test drive currently selected vehicle", + freeuse_buy_header = "Buy Vehicle", + freeuse_buy_txt = "Purchase currently selected vehicle", + freeuse_finance_txt = "Finance currently selected vehicle", + --Managed + managed_test_txt = "Allow player for test drive", + managed_sell_header = "Sell Vehicle", + managed_sell_txt = "Sell vehicle to Player", + managed_finance_txt = "Finance vehicle to Player", + submit_ID = "Server ID (#)", + }, + general = { + testdrive_timer = "Test Drive Time Remaining:", + vehinteraction = "Vehicle Interaction", + testdrive_timenoti = "You have %{testdrivetime} minutes remaining", + testdrive_complete = "Vehicle test drive complete", + paymentduein = "Your vehicle payment is due within %{time} minutes", + command_transfervehicle = "Gift or sell your vehicle", + command_transfervehicle_help = "ID of buyer", + command_transfervehicle_amount = "Sell amount (optionnal)", + } +} + +if GetConvar('qb_locale', 'en') == 'de' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehicleshop/locales/en.lua b/[qb]/qb-vehicleshop/locales/en.lua new file mode 100644 index 0000000..a56c022 --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/en.lua @@ -0,0 +1,82 @@ +local Translations = { + error = { + testdrive_alreadyin = "Already in test drive", + testdrive_return = "This is not your test drive vehicle", + Invalid_ID = "Invalid Player Id Supplied", + playertoofar = "This player is not close enough", + notenoughmoney = "Not enough money", + minimumallowed = "Minimum payment allowed is $", + overpaid = "You overpaid", + alreadypaid = "Vehicle is already paid off", + notworth = "Vehicle is not worth that much", + downtoosmall = "Down payment too small", + exceededmax = "Exceeded maximum payment amount", + repossessed = "Your vehicle with plate %{plate} has been repossessed", + buyerinfo = "Couldn\'t get buyer info", + notinveh = "You must be in the vehicle you want to transfer", + vehinfo = "Couldn\'t get vehicle info", + notown = "You don\'t own this vehicle", + buyertoopoor = "The buyer doesn\'t have enough money", + nofinanced = "You don't have any financed vehicles", + financed = "This vehicle is financed", + }, + success = { + purchased = "Congratulations on your purchase!", + earned_commission = "You earned $ %{amount} in commission", + gifted = "You gifted your vehicle", + received_gift = "You were gifted a vehicle", + soldfor = "You sold your vehicle for $", + boughtfor = "You bought a vehicle for $", + }, + menus = { + vehHeader_header = "Vehicle Options", + vehHeader_txt = "Interact with the current vehicle", + financed_header = "Financed Vehicles", + finance_txt = "Browse your owned vehicles", + returnTestDrive_header = "Finish Test Drive", + goback_header = "Go Back", + veh_price = "Price: $", + veh_platetxt = "Plate: ", + veh_finance = "Vehicle Payment", + veh_finance_balance = "Total Balance Remaining", + veh_finance_currency = "$", + veh_finance_total = "Total Payments Remaining", + veh_finance_reccuring = "Recurring Payment Amount", + veh_finance_pay = "Make a payment", + veh_finance_payoff = "Payoff vehicle", + veh_finance_payment = "Payment Amount ($)", + submit_text = "Submit", + test_header = "Test Drive", + finance_header = "Finance Vehicle", + swap_header = "Swap Vehicle", + swap_txt = "Change currently selected vehicle", + financesubmit_downpayment = "Down Payment Amount - Min ", + financesubmit_totalpayment = "Total Payments - Max ", + --Free Use + freeuse_test_txt = "Test drive currently selected vehicle", + freeuse_buy_header = "Buy Vehicle", + freeuse_buy_txt = "Purchase currently selected vehicle", + freeuse_finance_txt = "Finance currently selected vehicle", + --Managed + managed_test_txt = "Allow player for test drive", + managed_sell_header = "Sell Vehicle", + managed_sell_txt = "Sell vehicle to Player", + managed_finance_txt = "Finance vehicle to Player", + submit_ID = "Server ID (#)", + }, + general = { + testdrive_timer = "Test Drive Time Remaining:", + vehinteraction = "Vehicle Interaction", + testdrive_timenoti = "You have %{testdrivetime} minutes remaining", + testdrive_complete = "Vehicle test drive complete", + paymentduein = "Your vehicle payment is due within %{time} minutes", + command_transfervehicle = "Gift or sell your vehicle", + command_transfervehicle_help = "ID of buyer", + command_transfervehicle_amount = "Sell amount (optionnal)", + } +} + +Lang = Lang or Locale:new({ + phrases = Translations, + warnOnMissing = true +}) diff --git a/[qb]/qb-vehicleshop/locales/es.lua b/[qb]/qb-vehicleshop/locales/es.lua new file mode 100644 index 0000000..338db1a --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/es.lua @@ -0,0 +1,85 @@ +local Translations = { + error = { + testdrive_alreadyin = "Ya se encuentra en prueba de manejo", + testdrive_return = "Este no es tu vehículo para prueba de manejo", + Invalid_ID = "ID de jugador inválida", + playertoofar = "Este jugador no está lo suficientemente cerca", + notenoughmoney = "No tienes suficiente dinero", + minimumallowed = "Pago mínimo permitido es $", + overpaid = "Has sobrepagado", + alreadypaid = "El vehículo ya fue cancelado", + notworth = "El vehículo no vale tanto", + downtoosmall = "Pago inicial muy bajo", + exceededmax = "Monto de pago máximo excedido", + repossessed = "Tu vehículo con placa %{plate} ha sido embargado", + buyerinfo = "No pude obtener la información del comprador", + notinveh = "Debes estar en el vehículo si quieres transferirlo", + vehinfo = "No pude obtener la información del vehículo", + notown = "Este vehículo no es tuyo", + buyertoopoor = "El comprador no tiene suficiente dinero", + nofinanced = "No tienes ningún vehículo financiado", + financed = "Este vehículo es financiado", + }, + success = { + purchased = "¡Felicidades por tu compra!", + earned_commission = "Has ganado $%{amount} en comisión", + gifted = "Has regalado tu vehículo", + received_gift = "Te han regalado un vehículo", + soldfor = "Has vendido tu vehículo por $", + boughtfor = "Has comprado un vehículo por $", + }, + menus = { + vehHeader_header = "Opciones de vehículo", + vehHeader_txt = "Interactúa con el vehículo seleccionado", + financed_header = "Vehículos financiados", + finance_txt = "Navega tu colección de vehículos", + returnTestDrive_header = "Finalizar prueba de manejo", + goback_header = "Ir atrás", + veh_price = "Precio: $", + veh_platetxt = "Placa: ", + veh_finance = "Pago de vehículo", + veh_finance_balance = "Balance total pendiente", + veh_finance_currency = "$", + veh_finance_total = "Total de pagos pendientes", + veh_finance_reccuring = "Monto de pago recurrente", + veh_finance_pay = "Realizar un pago", + veh_finance_payoff = "Pagar totalidad de vehículo", + veh_finance_payment = "Monto de pago ($)", + submit_text = "Enviar", + test_header = "Prueba de manejo", + finance_header = "Financiar vehículo", + swap_header = "Intercambiar vehículo", + swap_txt = "Cambiar el vehículo seleccionado por otro", + financesubmit_downpayment = "Monto de pago inicial - Min ", + financesubmit_totalpayment = "Total de pagos - Max ", + --Free Use + freeuse_test_txt = "Ir a prueba de manejo con vehículo seleccionado", + freeuse_buy_header = "Comprar vehículo", + freeuse_buy_txt = "Comprar vehículo seleccionado", + freeuse_finance_txt = "Financiar vehículo seleccionado", + --Managed + managed_test_txt = "Permitir realizar prueba de manejo a jugador", + managed_sell_header = "Vender vehículo", + managed_sell_txt = "Vender vehículo a jugador", + managed_finance_txt = "Financiar vehículo a jugador", + submit_ID = "ID de servidor (#)", + }, + general = { + testdrive_timer = "Tiempo restante de prueba de manejo:", + vehinteraction = "Interacción de vehículo", + testdrive_timenoti = "Tienes %{testdrivetime} minuto(s) restante(s)", + testdrive_complete = "Prueba de manejo completada", + paymentduein = "El pago de tu vehículo es en %{time} minutos", + command_transfervehicle = "Regalar o vender tu vehículo", + command_transfervehicle_help = "ID del comprador", + command_transfervehicle_amount = "Monto de venta (opcional)", + } +} + +if GetConvar('qb_locale', 'en') == 'es' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehicleshop/locales/fr.lua b/[qb]/qb-vehicleshop/locales/fr.lua new file mode 100644 index 0000000..66738a6 --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/fr.lua @@ -0,0 +1,85 @@ +local Translations = { + error = { + testdrive_alreadyin = "Déjà dans le test", + testdrive_return = "Ce n'est pas votre véhicule de test", + Invalid_ID = "ID invalide", + playertoofar = "Ce joueur est trop loin", + notenoughmoney = "Pas assez d'argent", + minimumallowed = "Paiement minimum autorisé est $", + overpaid = "Vous avez trop payé", + alreadypaid = "Véhicule est déjà payé", + notworth = "Véhicule ne vaut pas tant que ça", + downtoosmall = "Paiement trop petit", + exceededmax = "Maximum de paiement dépassé", + repossessed = "Vote véhicule avec la plaque: %{plate} à été repris", + buyerinfo = "Impossible d'obtenir les informations de l\'acheteur", + notinveh = "Vous devez être dans le véhicule que vous voulez transférer", + vehinfo = "Impossible d'obtenir les informations du véhicule", + notown = "Vous ne possédez pas ce véhicule", + buyertoopoor = "L\'acheteur n'a pas assez d'argent", + nofinanced = "Vous n\'avez aucun véhicule financés", + financed = "Ce véhicule est financé", +}, + success = { + purchased = "Félicitations sur votre achat!", + earned_commission = "Vous avez gagné $ %{amount} de commission", + gifted = "Vous avez donné votre véhicule", + received_gift = "Vous avez été donné un véhicule", + soldfor = "Vous avez vendu votre véhicule pour $", + boughtfor = "Vous avez acheté un véhicule pour $", + }, + menus = { + vehHeader_header = "Options véhicule", + vehHeader_txt = "Interagissez avec le véhicule actuel", + financed_header = "Véhicules Financés", + finance_txt = "Parcourez vos véhicules", + returnTestDrive_header = "Terminer le test", + goback_header = "Retour", + veh_price = "Prix: $", + veh_platetxt = "Plaque: ", + veh_finance = "Paiement véhicule", + veh_finance_balance = "Solde total restant", + veh_finance_currency = "$", + veh_finance_total = "Paiements total restant", + veh_finance_reccuring = "Paiements récurrents restants", + veh_finance_pay = "Faire un paiement", + veh_finance_payoff = "Payer le véhicule", + veh_finance_payment = "Montant du paiement ($)", + submit_text = "Soumettre", + test_header = "Tester véhicule", + finance_header = "Financer véhicule", + swap_header = "Changer véhicule", + swap_txt = "Changer le véhicule actuel", + financesubmit_downpayment = "Montant paiements - Min ", + financesubmit_totalpayment = "Paiements totaux - Max ", + --Free Use + freeuse_test_txt = "Tester le véhicule actuel", + freeuse_buy_header = "Acheter véhicule", + freeuse_buy_txt = "Acheter ce véhicule", + freeuse_finance_txt = "Financer ce véhicule", + --Managed + managed_test_txt = "Faire tester le véhicule", + managed_sell_header = "Vendre véhicule", + managed_sell_txt = "Vendre ce véhicule à quelqu'un", + managed_finance_txt = "Faire financer ce véhicule", + submit_ID = "ID du joueur (#)", + }, + general = { + testdrive_timer = "Temps restant: ", + vehinteraction = "Intéraction véhicule", + testdrive_timenoti = "Il vous reste %{testdrivetime} minutes", + testdrive_complete = "Vous avez terminé le test", + paymentduein = "Paiement du prêt requis avant %{time} minutes", + command_transfervehicle = "Donner / Vendre véhicule", + command_transfervehicle_help = "ID du joueur", + command_transfervehicle_amount = "Montant de vente (optionnel)", + }, +} + +if GetConvar('qb_locale', 'en') == 'fr' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehicleshop/locales/pt.lua b/[qb]/qb-vehicleshop/locales/pt.lua new file mode 100644 index 0000000..b979e7b --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/pt.lua @@ -0,0 +1,84 @@ +local Translations = { + error = { + testdrive_alreadyin = "Já se encontra num teste drive", + testdrive_return = "Este não é o seu veículo de teste.", + Invalid_ID = "Id do cidadão inválido", + playertoofar = "Este jogar não se encontra perto", + notenoughmoney = "Não tem dinheiro suficiente", + minimumallowed = "O minimo pagamento permitido é: €", + overpaid = "Pagou demasiado", + alreadypaid = "Veículo já se encontra pago", + notworth = "Veículo não vale tanto!", + downtoosmall = "Adiantamento muito baixo.", + exceededmax = "Execedeu o valor para pagamento máximo", + repossessed = "O veículo com a matricula: %{plate} foi apreendido por falta de pagamento", + buyerinfo = "Não foi possível obter as informações do comprador", + notinveh = "Deve estar no veículo que deseja transferir", + vehinfo = "Não foi possível obter as informações do veículo", + notown = "Este veículo não lhe pertence", + buyertoopoor = "O comprador não tem dinheiro suficiente", + nofinanced = "Não tem nenhum veículo financiado", + financed = "Veículo financiado", + }, + success = { + purchased = "Parabéns pela sua compra!", + earned_commission = "Ganhou € %{amount} em comissão", + gifted = "Ofereceu o seu veículo", + received_gift = "Ofereceram-lhe um veículo", + soldfor = "Vendeu o seu veículo por: €", + boughtfor = "Comprou um veículo por: €", + }, + menus = { + vehHeader_header = "Opções - Veículo", + vehHeader_txt = "Interagir com o veículo atual", + financed_header = "Veículos financiados", + finance_txt = "Navegue pelos seus veículos", + returnTestDrive_header = "Terminar teste drive", + goback_header = "Voltar", + veh_price = "Preço: €", + veh_platetxt = "Matricula: ", + veh_finance = "Pagamento do veículo", + veh_finance_balance = "Saldo Total Restante", + veh_finance_currency = "€", + veh_finance_total = "Total de pagamentos restantes", + veh_finance_reccuring = "Valor do pagamento recorrente", + veh_finance_pay = "Efectuar pagamento", + veh_finance_payoff = "Pagar veículo na sua totalidade", + veh_finance_payment = "Valor do Pagamento: (€)", + submit_text = "Enviar", + test_header = "Teste Drive", + finance_header = "Pedir Financiamento", + swap_header = "Trocar Veículo", + swap_txt = "Alterar o veículo atualmente selecionado", + financesubmit_downpayment = "Valor do adiantamento - Min ", + financesubmit_totalpayment = "Total de Pagamentos - Max ", + --Free Use + freeuse_test_txt = "Teste drive - veículo atualmente selecionado", + freeuse_buy_header = "Comprar", + freeuse_buy_txt = "Comprar o veículo atualmente selecionado", + freeuse_finance_txt = "Financiar o veículo atualmente selecionado", + --Managed + managed_test_txt = "Permitir ao cidadão fazer o teste drive", + managed_sell_header = "Vender veículo", + managed_sell_txt = "Vender veículo a jogador", + managed_finance_txt = "Financiar veículo a jogador", + submit_ID = "ID do Servidor (#)", + }, + general = { + testdrive_timer = "Teste Drive - Tempo Restante:", + vehinteraction = "Interação do veículo", + testdrive_timenoti = "Restam %{testdrivetime} minutos", + testdrive_complete = "Teste drive do veículo concluído", + paymentduein = "O pagamento do seu veículo vence em %{time} minutos", + command_transfervehicle = "Presenteie ou venda seu veículo", + command_transfervehicle_help = "ID do comprador", + command_transfervehicle_amount = "Valor da venda (optional)", + } +} +if GetConvar('qb_locale', 'en') == 'pt' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehicleshop/locales/ro.lua b/[qb]/qb-vehicleshop/locales/ro.lua new file mode 100644 index 0000000..2e62724 --- /dev/null +++ b/[qb]/qb-vehicleshop/locales/ro.lua @@ -0,0 +1,89 @@ +--[[ +Romanian base language translation for qb-vehicleshop +Translation done by wanderrer (Martin Riggs#0807 on Discord) +]]-- +local Translations = { + error = { + testdrive_alreadyin = "Testezi deja un vehicul", + testdrive_return = "Acesta nu este vehiculul tau de test", + Invalid_ID = "S-a furnizat un ID invalid de jucator", + playertoofar = "Acest jucator, nu este suficient de aproape de tine", + notenoughmoney = "Nu ai suficienti bani", + minimumallowed = "Suma minima de plata este $", + overpaid = "Ai platit prea mult", + alreadypaid = "Vehiculul este achitat deja", + notworth = "Vehiculul nu valoreaza atat de mult", + downtoosmall = "Avansul platit, este prea mic", + exceededmax = "Suma maxima de plata depasita", + repossessed = "Vehiculul cu numarul %{plate} a fost confiscat", + buyerinfo = "Nu am putut obtine informatiile cumparatorului", + notinveh = "Trebuie sa fi in vehiculul pe care vrei sa-l transferi altcuiva", + vehinfo = "Nu am putut obtine informatiile vehiculului", + notown = "Acest vehicul nu este al tau", + buyertoopoor = "Cumparatorul nu are suficienti bani!", + nofinanced = "Nu ai niciun vehicul cumparat in leasing", + financed = "Acest vehicul este inca in leasing", + }, + success = { + purchased = "Felicitari pentru achizitie!", + earned_commission = "Ai castigat $ %{amount} comision de vanzare!", + gifted = "Ai dat vehiculul cadou", + received_gift = "Ai primit cadou un vehicul", + soldfor = "Ai vandut vehiculul pentru suma de $", + boughtfor = "Ai cumparat vehiculul pentru suma de $", + }, + menus = { + vehHeader_header = "Optiuni vehicul", + vehHeader_txt = "Interactioneaza cu vehiculul curent", + financed_header = "Vehicule finantate", + finance_txt = "Verifica vehiculele tale", + returnTestDrive_header = "Termina testarea vehiculului", + goback_header = "Inapoi", + veh_price = "Pret: $", + veh_platetxt = "Numar: ", + veh_finance = "Plata vehiculului", + veh_finance_balance = "Sold total ramas", + veh_finance_currency = "$", + veh_finance_total = "Total plati ramase", + veh_finance_reccuring = "Suma de plata recurenta", + veh_finance_pay = "Fa o plata", + veh_finance_payoff = "Achita integral vehicul", + veh_finance_payment = "Suma de plata ($)", + submit_text = "Trimite", + test_header = "Testarea vehiculului", + finance_header = "Cumpara vehicul in rate", + swap_header = "Schimba vehicul", + swap_txt = "Schimba vehiculul selectat", + financesubmit_downpayment = "Minimul sumei de avans ", + financesubmit_totalpayment = "Numar de plati maxime ", + --Free Use + freeuse_test_txt = "Testeaza vehiculul selectat", + freeuse_buy_header = "Cumpara vehiculul", + freeuse_buy_txt = "Cumpara vehiculul selectat", + freeuse_finance_txt = "Cumpara in rate vehiculul selectat", + --Managed + managed_test_txt = "Permite jucatorilor sa testeze vehiculele", + managed_sell_header = "Vinde vehiculul", + managed_sell_txt = "Vinde vehiculul jcatorului", + managed_finance_txt = "Vinde vehiculul in rate unui jucator", + submit_ID = "ID-ul serverului (#)", + }, + general = { + testdrive_timer = "Timp ramas pentru testare:", + vehinteraction = "Interactiuni cu vehiculul", + testdrive_timenoti = "Au mai ramas %{testdrivetime} minute din testare", + testdrive_complete = "S-a terminat testarea vehiculului", + paymentduein = "Au mai ramas %{time} minute, pana la plata ratei", + command_transfervehicle = "Ofera cadou sau vine vehiculul", + command_transfervehicle_help = "ID-ul cumparatorului", + command_transfervehicle_amount = "Suma de plata (optionnal)", + } +} + +if GetConvar('qb_locale', 'en') == 'ro' then + Lang = Locale:new({ + phrases = Translations, + warnOnMissing = true, + fallbackLang = Lang, + }) +end diff --git a/[qb]/qb-vehicleshop/server.lua b/[qb]/qb-vehicleshop/server.lua new file mode 100644 index 0000000..d564bb4 --- /dev/null +++ b/[qb]/qb-vehicleshop/server.lua @@ -0,0 +1,495 @@ +-- Variables +local QBCore = exports['qb-core']:GetCoreObject() +local financetimer = {} + +-- Handlers +-- Store game time for player when they load +RegisterNetEvent('qb-vehicleshop:server:addPlayer', function(citizenid) + financetimer[citizenid] = os.time() +end) + +-- Deduct stored game time from player on logout +RegisterNetEvent('qb-vehicleshop:server:removePlayer', function(citizenid) + if financetimer[citizenid] then + local playTime = financetimer[citizenid] + local financetime = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', {citizenid}) + for _, v in pairs(financetime) do + if v.balance >= 1 then + local newTime = (v.financetime-((os.time()-playTime)/60)) + if newTime < 0 then newTime = 0 end + MySQL.update('UPDATE player_vehicles SET financetime = ? WHERE plate = ?', {math.ceil(newTime), v.plate}) + end + end + end + financetimer[citizenid] = nil +end) + +-- Deduct stored game time from player on quit because we can't get citizenid +AddEventHandler('playerDropped', function() + local src = source + local license + for _, v in pairs(GetPlayerIdentifiers(src)) do + if string.sub(v, 1, string.len("license:")) == "license:" then + license = v + end + end + if license then + local vehicles = MySQL.query.await('SELECT * FROM player_vehicles WHERE license = ?', {license}) + if vehicles then + for _, v in pairs(vehicles) do + local playTime = financetimer[v.citizenid] + if v.balance >= 1 and playTime then + local newTime = (v.financetime-((os.time()-playTime)/60)) + if newTime < 0 then newTime = 0 end + MySQL.update('UPDATE player_vehicles SET financetime = ? WHERE plate = ?', {math.ceil(newTime), v.plate}) + end + end + if vehicles[1] and financetimer[vehicles[1].citizenid] then financetimer[vehicles[1].citizenid] = nil end + end + end +end) + +-- Functions +local function round(x) + return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) +end + +local function calculateFinance(vehiclePrice, downPayment, paymentamount) + local balance = vehiclePrice - downPayment + local vehPaymentAmount = balance / paymentamount + return round(balance), round(vehPaymentAmount) +end + +local function calculateNewFinance(paymentAmount, vehData) + local newBalance = tonumber(vehData.balance - paymentAmount) + local minusPayment = vehData.paymentsLeft - 1 + local newPaymentsLeft = newBalance / minusPayment + local newPayment = newBalance / newPaymentsLeft + return round(newBalance), round(newPayment), newPaymentsLeft +end + +local function GeneratePlate() + local plate = QBCore.Shared.RandomInt(1) .. QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(2) + local result = MySQL.scalar.await('SELECT plate FROM player_vehicles WHERE plate = ?', {plate}) + if result then + return GeneratePlate() + else + return plate:upper() + end +end + +local function comma_value(amount) + local formatted = amount + local k + while true do + formatted, k = string.gsub(formatted, '^(-?%d+)(%d%d%d)', '%1,%2') + if (k == 0) then + break + end + end + return formatted +end + +-- Callbacks +QBCore.Functions.CreateCallback('qb-vehicleshop:server:getVehicles', function(source, cb) + local src = source + local player = QBCore.Functions.GetPlayer(src) + if player then + local vehicles = MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', {player.PlayerData.citizenid}) + if vehicles[1] then + cb(vehicles) + end + end +end) + +-- Events + +-- Brute force vehicle deletion +RegisterNetEvent('qb-vehicleshop:server:deleteVehicle', function (netId) + local vehicle = NetworkGetEntityFromNetworkId(netId) + DeleteEntity(vehicle) +end) + +-- Sync vehicle for other players +RegisterNetEvent('qb-vehicleshop:server:swapVehicle', function(data) + local src = source + TriggerClientEvent('qb-vehicleshop:client:swapVehicle', -1, data) + Wait(1500)-- let new car spawn + TriggerClientEvent('qb-vehicleshop:client:homeMenu', src)-- reopen main menu +end) + +-- Send customer for test drive +RegisterNetEvent('qb-vehicleshop:server:customTestDrive', function(vehicle, playerid) + local src = source + local target = tonumber(playerid) + if not QBCore.Functions.GetPlayer(target) then + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.Invalid_ID'), 'error') + return + end + if #(GetEntityCoords(GetPlayerPed(src)) - GetEntityCoords(GetPlayerPed(target))) < 3 then + TriggerClientEvent('qb-vehicleshop:client:customTestDrive', target, vehicle) + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.playertoofar'), 'error') + end +end) + +-- Make a finance payment +RegisterNetEvent('qb-vehicleshop:server:financePayment', function(paymentAmount, vehData) + local src = source + local player = QBCore.Functions.GetPlayer(src) + local cash = player.PlayerData.money['cash'] + local bank = player.PlayerData.money['bank'] + local plate = vehData.vehiclePlate + paymentAmount = tonumber(paymentAmount) + local minPayment = tonumber(vehData.paymentAmount) + local timer = (Config.PaymentInterval * 60) + local newBalance, newPaymentsLeft, newPayment = calculateNewFinance(paymentAmount, vehData) + if newBalance > 0 then + if player and paymentAmount >= minPayment then + if cash >= paymentAmount then + player.Functions.RemoveMoney('cash', paymentAmount) + MySQL.update('UPDATE player_vehicles SET balance = ?, paymentamount = ?, paymentsleft = ?, financetime = ? WHERE plate = ?', {newBalance, newPayment, newPaymentsLeft, timer, plate}) + elseif bank >= paymentAmount then + player.Functions.RemoveMoney('bank', paymentAmount) + MySQL.update('UPDATE player_vehicles SET balance = ?, paymentamount = ?, paymentsleft = ?, financetime = ? WHERE plate = ?', {newBalance, newPayment, newPaymentsLeft, timer, plate}) + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notenoughmoney'), 'error') + end + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.minimumallowed') .. comma_value(minPayment), 'error') + end + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.overpaid'), 'error') + end +end) + + +-- Pay off vehice in full +RegisterNetEvent('qb-vehicleshop:server:financePaymentFull', function(data) + local src = source + local player = QBCore.Functions.GetPlayer(src) + local cash = player.PlayerData.money['cash'] + local bank = player.PlayerData.money['bank'] + local vehBalance = data.vehBalance + local vehPlate = data.vehPlate + if player and vehBalance ~= 0 then + if cash >= vehBalance then + player.Functions.RemoveMoney('cash', vehBalance) + MySQL.update('UPDATE player_vehicles SET balance = ?, paymentamount = ?, paymentsleft = ?, financetime = ? WHERE plate = ?', {0, 0, 0, 0, vehPlate}) + elseif bank >= vehBalance then + player.Functions.RemoveMoney('bank', vehBalance) + MySQL.update('UPDATE player_vehicles SET balance = ?, paymentamount = ?, paymentsleft = ?, financetime = ? WHERE plate = ?', {0, 0, 0, 0, vehPlate}) + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notenoughmoney'), 'error') + end + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.alreadypaid'), 'error') + end +end) + +-- Buy public vehicle outright +RegisterNetEvent('qb-vehicleshop:server:buyShowroomVehicle', function(vehicle) + local src = source + vehicle = vehicle.buyVehicle + local pData = QBCore.Functions.GetPlayer(src) + local cid = pData.PlayerData.citizenid + local cash = pData.PlayerData.money['cash'] + local bank = pData.PlayerData.money['bank'] + local vehiclePrice = QBCore.Shared.Vehicles[vehicle]['price'] + local plate = GeneratePlate() + if cash > tonumber(vehiclePrice) then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', { + pData.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0 + }) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.purchased'), 'success') + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', src, vehicle, plate) + pData.Functions.RemoveMoney('cash', vehiclePrice, 'vehicle-bought-in-showroom') + elseif bank > tonumber(vehiclePrice) then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', { + pData.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0 + }) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.purchased'), 'success') + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', src, vehicle, plate) + pData.Functions.RemoveMoney('bank', vehiclePrice, 'vehicle-bought-in-showroom') + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notenoughmoney'), 'error') + end +end) + +-- Finance public vehicle +RegisterNetEvent('qb-vehicleshop:server:financeVehicle', function(downPayment, paymentAmount, vehicle) + local src = source + downPayment = tonumber(downPayment) + paymentAmount = tonumber(paymentAmount) + local pData = QBCore.Functions.GetPlayer(src) + local cid = pData.PlayerData.citizenid + local cash = pData.PlayerData.money['cash'] + local bank = pData.PlayerData.money['bank'] + local vehiclePrice = QBCore.Shared.Vehicles[vehicle]['price'] + local timer = (Config.PaymentInterval * 60) + local minDown = tonumber(round((Config.MinimumDown / 100) * vehiclePrice)) + if downPayment > vehiclePrice then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notworth'), 'error') end + if downPayment < minDown then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.downtoosmall'), 'error') end + if paymentAmount > Config.MaximumPayments then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.exceededmax'), 'error') end + local plate = GeneratePlate() + local balance, vehPaymentAmount = calculateFinance(vehiclePrice, downPayment, paymentAmount) + if cash >= downPayment then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', { + pData.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0, + balance, + vehPaymentAmount, + paymentAmount, + timer + }) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.purchased'), 'success') + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', src, vehicle, plate) + pData.Functions.RemoveMoney('cash', downPayment, 'vehicle-bought-in-showroom') + elseif bank >= downPayment then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', { + pData.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0, + balance, + vehPaymentAmount, + paymentAmount, + timer + }) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.purchased'), 'success') + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', src, vehicle, plate) + pData.Functions.RemoveMoney('bank', downPayment, 'vehicle-bought-in-showroom') + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notenoughmoney'), 'error') + end +end) + +-- Sell vehicle to customer +RegisterNetEvent('qb-vehicleshop:server:sellShowroomVehicle', function(data, playerid) + local src = source + local player = QBCore.Functions.GetPlayer(src) + local target = QBCore.Functions.GetPlayer(tonumber(playerid)) + + if not target then + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.Invalid_ID'), 'error') + return + end + + if #(GetEntityCoords(GetPlayerPed(src)) - GetEntityCoords(GetPlayerPed(target.PlayerData.source))) < 3 then + local cid = target.PlayerData.citizenid + local cash = target.PlayerData.money['cash'] + local bank = target.PlayerData.money['bank'] + local vehicle = data + local vehiclePrice = QBCore.Shared.Vehicles[vehicle]['price'] + local commission = round(vehiclePrice * Config.Commission) + local plate = GeneratePlate() + if cash >= tonumber(vehiclePrice) then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', { + target.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0 + }) + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', target.PlayerData.source, vehicle, plate) + target.Functions.RemoveMoney('cash', vehiclePrice, 'vehicle-bought-in-showroom') + player.Functions.AddMoney('bank', commission) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.earned_commission', {amount = comma_value(commission)}), 'success') + exports['qb-management']:AddMoney(player.PlayerData.job.name, vehiclePrice) + TriggerClientEvent('QBCore:Notify', target.PlayerData.source, Lang:t('success.purchased'), 'success') + elseif bank >= tonumber(vehiclePrice) then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', { + target.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0 + }) + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', target.PlayerData.source, vehicle, plate) + target.Functions.RemoveMoney('bank', vehiclePrice, 'vehicle-bought-in-showroom') + player.Functions.AddMoney('bank', commission) + exports['qb-management']:AddMoney(player.PlayerData.job.name, vehiclePrice) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.earned_commission', {amount = comma_value(commission)}), 'success') + TriggerClientEvent('QBCore:Notify', target.PlayerData.source, Lang:t('success.purchased'), 'success') + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notenoughmoney'), 'error') + end + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.playertoofar'), 'error') + end +end) + +-- Finance vehicle to customer +RegisterNetEvent('qb-vehicleshop:server:sellfinanceVehicle', function(downPayment, paymentAmount, vehicle, playerid) + local src = source + local player = QBCore.Functions.GetPlayer(src) + local target = QBCore.Functions.GetPlayer(tonumber(playerid)) + + if not target then + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.Invalid_ID'), 'error') + return + end + + if #(GetEntityCoords(GetPlayerPed(src)) - GetEntityCoords(GetPlayerPed(target.PlayerData.source))) < 3 then + downPayment = tonumber(downPayment) + paymentAmount = tonumber(paymentAmount) + local cid = target.PlayerData.citizenid + local cash = target.PlayerData.money['cash'] + local bank = target.PlayerData.money['bank'] + local vehiclePrice = QBCore.Shared.Vehicles[vehicle]['price'] + local timer = (Config.PaymentInterval * 60) + local minDown = tonumber(round((Config.MinimumDown / 100) * vehiclePrice)) + if downPayment > vehiclePrice then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notworth'), 'error') end + if downPayment < minDown then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.downtoosmall'), 'error') end + if paymentAmount > Config.MaximumPayments then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.exceededmax'), 'error') end + local commission = round(vehiclePrice * Config.Commission) + local plate = GeneratePlate() + local balance, vehPaymentAmount = calculateFinance(vehiclePrice, downPayment, paymentAmount) + if cash >= downPayment then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', { + target.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0, + balance, + vehPaymentAmount, + paymentAmount, + timer + }) + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', target.PlayerData.source, vehicle, plate) + target.Functions.RemoveMoney('cash', downPayment, 'vehicle-bought-in-showroom') + player.Functions.AddMoney('bank', commission) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.earned_commission', {amount = comma_value(commission)}), 'success') + exports['qb-management']:AddMoney(player.PlayerData.job.name, vehiclePrice) + TriggerClientEvent('QBCore:Notify', target.PlayerData.source, Lang:t('success.purchased'), 'success') + elseif bank >= downPayment then + MySQL.insert('INSERT INTO player_vehicles (license, citizenid, vehicle, hash, mods, plate, garage, state, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', { + target.PlayerData.license, + cid, + vehicle, + GetHashKey(vehicle), + '{}', + plate, + 'pillboxgarage', + 0, + balance, + vehPaymentAmount, + paymentAmount, + timer + }) + TriggerClientEvent('qb-vehicleshop:client:buyShowroomVehicle', target.PlayerData.source, vehicle, plate) + target.Functions.RemoveMoney('bank', downPayment, 'vehicle-bought-in-showroom') + player.Functions.AddMoney('bank', commission) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.earned_commission', {amount = comma_value(commission)}), 'success') + exports['qb-management']:AddMoney(player.PlayerData.job.name, vehiclePrice) + TriggerClientEvent('QBCore:Notify', target.PlayerData.source, Lang:t('success.purchased'), 'success') + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notenoughmoney'), 'error') + end + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.playertoofar'), 'error') + end +end) + +-- Check if payment is due +RegisterNetEvent('qb-vehicleshop:server:checkFinance', function() + local src = source + local player = QBCore.Functions.GetPlayer(src) + local query = 'SELECT * FROM player_vehicles WHERE citizenid = ? AND balance > 0 AND financetime < 1' + local result = MySQL.query.await(query, {player.PlayerData.citizenid}) + if result[1] then + TriggerClientEvent('QBCore:Notify', src, Lang:t('general.paymentduein', {time = Config.PaymentWarning})) + Wait(Config.PaymentWarning * 60000) + local vehicles = MySQL.query.await(query, {player.PlayerData.citizenid}) + for _, v in pairs(vehicles) do + local plate = v.plate + MySQL.query('DELETE FROM player_vehicles WHERE plate = @plate', {['@plate'] = plate}) + --MySQL.update('UPDATE player_vehicles SET citizenid = ? WHERE plate = ?', {'REPO-'..v.citizenid, plate}) -- Use this if you don't want them to be deleted + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.repossessed', {plate = plate}), 'error') + end + end +end) + +-- Transfer vehicle to player in passenger seat +QBCore.Commands.Add('transfervehicle', Lang:t('general.command_transfervehicle'), {{name = 'ID', help = Lang:t('general.command_transfervehicle_help')}, {name = 'amount', help = Lang:t('general.command_transfervehicle_amount')}}, false, function(source, args) + local src = source + local buyerId = tonumber(args[1]) + local sellAmount = tonumber(args[2]) + if buyerId == 0 then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.Invalid_ID'), 'error') end + local ped = GetPlayerPed(src) + local targetPed = GetPlayerPed(buyerId) + if targetPed == 0 then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.buyerinfo'), 'error') end + local vehicle = GetVehiclePedIsIn(ped, false) + if vehicle == 0 then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notinveh'), 'error') end + local plate = QBCore.Shared.Trim(GetVehicleNumberPlateText(vehicle)) + if not plate then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.vehinfo'), 'error') end + local player = QBCore.Functions.GetPlayer(src) + local target = QBCore.Functions.GetPlayer(buyerId) + local row = MySQL.single.await('SELECT * FROM player_vehicles WHERE plate = ?', {plate}) + if Config.PreventFinanceSelling then + if row.balance > 0 then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.financed'), 'error') end + end + if row.citizenid ~= player.PlayerData.citizenid then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.notown'), 'error') end + if #(GetEntityCoords(ped) - GetEntityCoords(targetPed)) > 5.0 then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.playertoofar'), 'error') end + local targetcid = target.PlayerData.citizenid + local targetlicense = QBCore.Functions.GetIdentifier(target.PlayerData.source, 'license') + if not target then return TriggerClientEvent('QBCore:Notify', src, Lang:t('error.buyerinfo'), 'error') end + if not sellAmount then + MySQL.update('UPDATE player_vehicles SET citizenid = ?, license = ? WHERE plate = ?', {targetcid, targetlicense, plate}) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.gifted'), 'success') + TriggerClientEvent('vehiclekeys:client:SetOwner', buyerId, plate) + TriggerClientEvent('QBCore:Notify', buyerId, Lang:t('success.received_gift'), 'success') + return + end + if target.Functions.GetMoney('cash') > sellAmount then + MySQL.update('UPDATE player_vehicles SET citizenid = ?, license = ? WHERE plate = ?', {targetcid, targetlicense, plate}) + player.Functions.AddMoney('cash', sellAmount) + target.Functions.RemoveMoney('cash', sellAmount) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.soldfor') .. comma_value(sellAmount), 'success') + TriggerClientEvent('vehiclekeys:client:SetOwner', buyerId, plate) + TriggerClientEvent('QBCore:Notify', buyerId, Lang:t('success.boughtfor') .. comma_value(sellAmount), 'success') + elseif target.Functions.GetMoney('bank') > sellAmount then + MySQL.update('UPDATE player_vehicles SET citizenid = ?, license = ? WHERE plate = ?', {targetcid, targetlicense, plate}) + player.Functions.AddMoney('bank', sellAmount) + target.Functions.RemoveMoney('bank', sellAmount) + TriggerClientEvent('QBCore:Notify', src, Lang:t('success.soldfor') .. comma_value(sellAmount), 'success') + TriggerClientEvent('vehiclekeys:client:SetOwner', buyerId, plate) + TriggerClientEvent('QBCore:Notify', buyerId, Lang:t('success.boughtfor') .. comma_value(sellAmount), 'success') + else + TriggerClientEvent('QBCore:Notify', src, Lang:t('error.buyertoopoor'), 'error') + end +end) diff --git a/[qb]/qb-vehicleshop/vehshop.sql b/[qb]/qb-vehicleshop/vehshop.sql new file mode 100644 index 0000000..7e8f6eb --- /dev/null +++ b/[qb]/qb-vehicleshop/vehshop.sql @@ -0,0 +1,38 @@ +CREATE TABLE IF NOT EXISTS `player_vehicles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `license` varchar(50) DEFAULT NULL, + `citizenid` varchar(50) DEFAULT NULL, + `vehicle` varchar(50) DEFAULT NULL, + `hash` varchar(50) DEFAULT NULL, + `mods` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `plate` varchar(15) NOT NULL, + `fakeplate` varchar(50) DEFAULT NULL, + `garage` varchar(50) DEFAULT 'pillboxgarage', + `fuel` int(11) DEFAULT 100, + `engine` float DEFAULT 1000, + `body` float DEFAULT 1000, + `state` int(11) DEFAULT 1, + `depotprice` int(11) NOT NULL DEFAULT 0, + `drivingdistance` int(50) DEFAULT NULL, + `status` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `plate` (`plate`), + KEY `citizenid` (`citizenid`), + KEY `license` (`license`) +) ENGINE=InnoDB AUTO_INCREMENT=1; + +ALTER TABLE `player_vehicles` +ADD UNIQUE INDEX UK_playervehicles_plate (plate); + +ALTER TABLE `player_vehicles` +ADD CONSTRAINT FK_playervehicles_players FOREIGN KEY (citizenid) +REFERENCES `players` (citizenid) ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE `player_vehicles` +ADD COLUMN `balance` int(11) NOT NULL DEFAULT 0; +ALTER TABLE `player_vehicles` +ADD COLUMN `paymentamount` int(11) NOT NULL DEFAULT 0; +ALTER TABLE `player_vehicles` +ADD COLUMN `paymentsleft` int(11) NOT NULL DEFAULT 0; +ALTER TABLE `player_vehicles` +ADD COLUMN `financetime` int(11) NOT NULL DEFAULT 0;