forked from Simnation/Main
Merge branch 'master' of https://git.evolution-state-life.de/Evolution-State-Life/Main
This commit is contained in:
commit
52947985ab
36 changed files with 0 additions and 3062 deletions
|
@ -1,9 +0,0 @@
|
|||
# Ignore any IDE derived project configs
|
||||
.idea
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
/src/node_modules
|
||||
.vscode/*
|
||||
.vscode/settings.json
|
||||
.github/*
|
|
@ -1,674 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
@ -1,35 +0,0 @@
|
|||
if Config.Framework.name ~= "custom" then
|
||||
return;
|
||||
end
|
||||
|
||||
Framework = {};
|
||||
|
||||
function IsPlayerLoaded()
|
||||
return ESX.IsPlayerLoaded();
|
||||
end
|
||||
|
||||
RegisterNetEvent("esx:addInventoryItem")
|
||||
AddEventHandler("esx:addInventoryItem", function(item)
|
||||
for k, v in pairs(Config.Weapons) do
|
||||
if item == k then
|
||||
Sling.cachedWeapons[item] = v
|
||||
Sling.cachedWeapons[item].attachments = Inventory:GetWeaponAttachment(item)
|
||||
break;
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("esx:removeInventoryItem")
|
||||
AddEventHandler("esx:removeInventoryItem", function(item)
|
||||
for k, v in pairs(Config.Weapons) do
|
||||
if item == k then
|
||||
Sling.cachedWeapons[item] = nil
|
||||
if Sling.cachedAttachments[item] then
|
||||
if DoesEntityExist(Sling.cachedAttachments[item].obj) then
|
||||
DeleteEntity(Sling.cachedAttachments[item].obj)
|
||||
end
|
||||
end
|
||||
break;
|
||||
end
|
||||
end
|
||||
end)
|
|
@ -1,45 +0,0 @@
|
|||
if Config.Framework.name ~= "esx" then
|
||||
return;
|
||||
end
|
||||
|
||||
ESX = exports[(Config.Framework.resource == "auto" and "es_extended" or Config.Framework.resource)]:getSharedObject();
|
||||
|
||||
Framework = {};
|
||||
|
||||
function IsPlayerLoaded()
|
||||
return ESX.IsPlayerLoaded();
|
||||
end
|
||||
|
||||
RegisterNetEvent("esx:addInventoryItem")
|
||||
AddEventHandler("esx:addInventoryItem", function(item)
|
||||
item = item:lower()
|
||||
for k, v in pairs(Config.Weapons) do
|
||||
if item == k then
|
||||
Sling.cachedWeapons[item] = v
|
||||
Sling.cachedWeapons[item].attachments = Inventory:GetWeaponAttachment(item)
|
||||
break;
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("esx:removeInventoryItem")
|
||||
AddEventHandler("esx:removeInventoryItem", function(item)
|
||||
item = item:lower()
|
||||
for k, v in pairs(Config.Weapons) do
|
||||
k = k:lower()
|
||||
if item == k then
|
||||
if Sling.cachedAttachments[item] then
|
||||
if DoesEntityExist(Sling.cachedAttachments[item].obj) or DoesEntityExist(Sling.cachedAttachments[item].placeholder) then
|
||||
DeleteEntity(Sling.cachedAttachments[item].obj)
|
||||
NetworkUnregisterNetworkedEntity(Sling.cachedAttachments[item].obj)
|
||||
DeleteObject(Sling.cachedAttachments[item].obj)
|
||||
DetachEntity(Sling.cachedAttachments[item].placeholder, true, false)
|
||||
DeleteObject(Sling.cachedAttachments[item].placeholder)
|
||||
Sling.currentAttachedAmount = Sling.currentAttachedAmount - 1
|
||||
end
|
||||
end
|
||||
Sling.cachedWeapons[item] = nil
|
||||
break;
|
||||
end
|
||||
end
|
||||
end)
|
|
@ -1,61 +0,0 @@
|
|||
if Config.Framework.name ~= "qbcore" then
|
||||
return
|
||||
end
|
||||
|
||||
QBCore = exports[(Config.Framework.resource == "auto" and "qb-core" or Config.Framework.resource)]:GetCoreObject()
|
||||
|
||||
local isPlayerLoaded = false
|
||||
|
||||
CreateThread(function()
|
||||
while not LocalPlayer.state.isLoggedIn and not isPlayerLoaded do
|
||||
Wait(100)
|
||||
end
|
||||
isPlayerLoaded = true
|
||||
|
||||
local lower = string.lower
|
||||
|
||||
while true do
|
||||
local xPlayer = QBCore.Functions.GetPlayerData()
|
||||
if not xPlayer or not xPlayer.items then
|
||||
Wait(1000)
|
||||
else
|
||||
local cachedItems = {}
|
||||
for _, v in pairs(xPlayer.items) do
|
||||
local itemName = lower(v.name)
|
||||
if Config.Weapons[itemName] then
|
||||
cachedItems[itemName] = true
|
||||
Sling.cachedWeapons[itemName] = Config.Weapons[itemName]
|
||||
Sling.cachedWeapons[itemName].attachments = Inventory:GetWeaponAttachment(itemName)
|
||||
end
|
||||
end
|
||||
|
||||
for key, val in pairs(Config.Weapons) do
|
||||
local lowerKey = lower(key)
|
||||
if not cachedItems[lowerKey] then
|
||||
if Sling.cachedAttachments[lowerKey] then
|
||||
local attachment = Sling.cachedAttachments[lowerKey]
|
||||
if DoesEntityExist(attachment.obj) or DoesEntityExist(attachment.placeholder) then
|
||||
DeleteEntity(attachment.obj)
|
||||
NetworkUnregisterNetworkedEntity(attachment.obj)
|
||||
DeleteObject(attachment.obj)
|
||||
DetachEntity(attachment.placeholder, true, false)
|
||||
DeleteObject(attachment.placeholder)
|
||||
Sling.currentAttachedAmount = Sling.currentAttachedAmount - 1
|
||||
end
|
||||
end
|
||||
Sling.cachedWeapons[lowerKey] = nil
|
||||
end
|
||||
end
|
||||
Wait(1500)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function IsPlayerLoaded()
|
||||
return isPlayerLoaded
|
||||
end
|
||||
|
||||
RegisterNetEvent('QBCore:Client:OnPlayerLoaded')
|
||||
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
|
||||
isPlayerLoaded = true
|
||||
end)
|
|
@ -1,62 +0,0 @@
|
|||
if Config.Framework.name ~= "qbx" then
|
||||
return
|
||||
end
|
||||
|
||||
local isPlayerLoaded = false
|
||||
|
||||
CreateThread(function()
|
||||
while not LocalPlayer.state.isLoggedIn and not isPlayerLoaded do
|
||||
Wait(100)
|
||||
end
|
||||
isPlayerLoaded = true
|
||||
|
||||
local cachedItems = {}
|
||||
local lower = string.lower
|
||||
local ConfigWeapons = Config.Weapons
|
||||
local SlingCachedWeapons = Sling.cachedWeapons
|
||||
local SlingCachedAttachments = Sling.cachedAttachments
|
||||
|
||||
while true do
|
||||
local xPlayer = exports.qbx_core:GetPlayerData()
|
||||
if not xPlayer or not xPlayer.items then
|
||||
Wait(1000)
|
||||
else
|
||||
for _, v in pairs(xPlayer.items) do
|
||||
local itemName = lower(v.name)
|
||||
if ConfigWeapons[itemName] then
|
||||
cachedItems[itemName] = true
|
||||
SlingCachedWeapons[itemName] = ConfigWeapons[itemName]
|
||||
SlingCachedWeapons[itemName].attachments = Inventory:GetWeaponAttachment(itemName)
|
||||
end
|
||||
end
|
||||
|
||||
for key, _ in pairs(ConfigWeapons) do
|
||||
local lowerKey = lower(key)
|
||||
if not cachedItems[lowerKey] then
|
||||
if SlingCachedAttachments[lowerKey] then
|
||||
local attachment = SlingCachedAttachments[lowerKey]
|
||||
if DoesEntityExist(attachment.obj) or DoesEntityExist(attachment.placeholder) then
|
||||
DeleteEntity(attachment.obj)
|
||||
NetworkUnregisterNetworkedEntity(attachment.obj)
|
||||
DeleteObject(attachment.obj)
|
||||
DetachEntity(attachment.placeholder, true, false)
|
||||
DeleteObject(attachment.placeholder)
|
||||
Sling.currentAttachedAmount = Sling.currentAttachedAmount - 1
|
||||
end
|
||||
end
|
||||
SlingCachedWeapons[lowerKey] = nil
|
||||
end
|
||||
end
|
||||
Wait(1500)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function IsPlayerLoaded()
|
||||
return isPlayerLoaded
|
||||
end
|
||||
|
||||
RegisterNetEvent('QBCore:Client:OnPlayerLoaded')
|
||||
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
|
||||
isPlayerLoaded = true
|
||||
end)
|
|
@ -1,16 +0,0 @@
|
|||
if Config.Inventory ~= "custom" then return end
|
||||
CustomInventory = {}
|
||||
|
||||
function CustomInventory:GetWeapons()
|
||||
local weapons = {}
|
||||
local userInventory
|
||||
|
||||
return weapons
|
||||
end
|
||||
|
||||
function CustomInventory:GetWeaponAttachment(item)
|
||||
local components = {}
|
||||
local userInventory
|
||||
|
||||
return components
|
||||
end
|
|
@ -1,123 +0,0 @@
|
|||
local function cleanupEntities()
|
||||
local function safeDelete(entity)
|
||||
if DoesEntityExist(entity) then
|
||||
if IsEntityAttachedToAnyPed(entity) then
|
||||
DetachEntity(entity, true, true)
|
||||
end
|
||||
NetworkUnregisterNetworkedEntity(entity)
|
||||
DeleteObject(entity)
|
||||
SetEntityAsNoLongerNeeded(entity)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if Sling then
|
||||
if Sling.object then
|
||||
safeDelete(Sling.object)
|
||||
Sling.object = nil
|
||||
end
|
||||
|
||||
if Sling.cachedAttachments then
|
||||
for weaponName, attachment in pairs(Sling.cachedAttachments) do
|
||||
if attachment then
|
||||
safeDelete(attachment.obj)
|
||||
safeDelete(attachment.placeholder)
|
||||
Sling.cachedAttachments[weaponName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Sling.currentAttachedAmount = 0
|
||||
end
|
||||
|
||||
for playerId, weapons in pairs(otherPlayersWeapons) do
|
||||
for weaponName, _ in pairs(weapons) do
|
||||
if Sling and Sling.cachedAttachments and Sling.cachedAttachments[weaponName] then
|
||||
safeDelete(Sling.cachedAttachments[weaponName].obj)
|
||||
safeDelete(Sling.cachedAttachments[weaponName].placeholder)
|
||||
end
|
||||
end
|
||||
otherPlayersWeapons[playerId] = nil
|
||||
end
|
||||
|
||||
collectgarbage("collect")
|
||||
end
|
||||
|
||||
CreateThread(function()
|
||||
while not Sling do
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
AddEventHandler('onResourceStart', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
cleanupEntities()
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent('QBCore:Client:OnPlayerLoaded')
|
||||
AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
|
||||
cleanupEntities()
|
||||
end)
|
||||
|
||||
AddEventHandler('onResourceStop', function(resourceName)
|
||||
if resourceName == GetCurrentResourceName() then
|
||||
cleanupEntities()
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function()
|
||||
cleanupEntities()
|
||||
end)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('force-sling:client:syncWeapons')
|
||||
AddEventHandler('force-sling:client:syncWeapons', function(playerId, weaponData, action)
|
||||
if not Sling then return end
|
||||
if playerId == cache.serverId then return end
|
||||
|
||||
if action == 'attach' then
|
||||
local targetPed = GetPlayerPed(GetPlayerFromServerId(playerId))
|
||||
if not targetPed or not DoesEntityExist(targetPed) then return end
|
||||
|
||||
-- Erstelle einen eindeutigen Key für die Waffe des anderen Spielers
|
||||
local uniqueWeaponKey = 'player_' .. playerId .. '_' .. weaponData.weaponName
|
||||
|
||||
otherPlayersWeapons[playerId] = otherPlayersWeapons[playerId] or {}
|
||||
|
||||
if not otherPlayersWeapons[playerId][uniqueWeaponKey] then
|
||||
Utils:CreateAndAttachWeapon(
|
||||
uniqueWeaponKey,
|
||||
weaponData.weaponVal,
|
||||
weaponData.coords,
|
||||
targetPed,
|
||||
true -- Flag für andere Spieler
|
||||
)
|
||||
otherPlayersWeapons[playerId][uniqueWeaponKey] = true
|
||||
end
|
||||
|
||||
elseif action == 'detach' then
|
||||
local uniqueWeaponKey = 'player_' .. playerId .. '_' .. weaponData.weaponName
|
||||
if otherPlayersWeapons[playerId] and otherPlayersWeapons[playerId][uniqueWeaponKey] then
|
||||
Utils:DeleteWeapon(uniqueWeaponKey)
|
||||
otherPlayersWeapons[playerId][uniqueWeaponKey] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
RegisterNetEvent('force-sling:client:cleanupPlayerWeapons')
|
||||
AddEventHandler('force-sling:client:cleanupPlayerWeapons', function(playerId)
|
||||
if not Sling then return end
|
||||
if otherPlayersWeapons[playerId] then
|
||||
for weaponKey, _ in pairs(otherPlayersWeapons[playerId]) do
|
||||
Utils:DeleteWeapon(weaponKey)
|
||||
end
|
||||
otherPlayersWeapons[playerId] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,388 +0,0 @@
|
|||
local POSITION_CLAMP = 0.2
|
||||
local DEFAULT_SPEED = 0.001
|
||||
local FAST_SPEED = 0.01
|
||||
DEFAULT_BONE = 24816
|
||||
|
||||
Sling = {
|
||||
isPreset = false,
|
||||
|
||||
cachedPositions = {},
|
||||
cachedPresets = {},
|
||||
cachedWeapons = {},
|
||||
cachedAttachments = {},
|
||||
currentAttachedAmount = 0,
|
||||
|
||||
inPositioning = false,
|
||||
data = {
|
||||
object = nil,
|
||||
}
|
||||
}
|
||||
|
||||
function Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, action)
|
||||
local weaponData = {
|
||||
weaponName = weaponName,
|
||||
weaponVal = weaponVal,
|
||||
coords = coords
|
||||
}
|
||||
TriggerServerEvent('force-sling:server:syncWeapons', weaponData, action)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Sling:InitMain()
|
||||
Debug("info", "Initializing main thread")
|
||||
|
||||
Sling:InitSling()
|
||||
Sling:InitCommands()
|
||||
|
||||
Debug("info", "Main thread initialized")
|
||||
end
|
||||
|
||||
function Sling:InitSling()
|
||||
local libCallbackAwait = lib.callback.await
|
||||
Sling.cachedPositions = libCallbackAwait("force-sling:callback:getCachedPositions", false)
|
||||
Sling.cachedPresets = libCallbackAwait("force-sling:callback:getCachedPresets", false)
|
||||
Sling.cachedWeapons = Inventory:GetWeapons()
|
||||
Sling:WeaponThread()
|
||||
|
||||
local function loadBoneOptions()
|
||||
local bones = {}
|
||||
for boneName, _ in pairs(Config.Bones) do
|
||||
table.insert(bones, boneName)
|
||||
end
|
||||
return bones
|
||||
end
|
||||
|
||||
local function loadWeaponOptions()
|
||||
local weapons = {}
|
||||
for weaponName, _ in pairs(Config.Weapons) do
|
||||
table.insert(weapons, weaponName)
|
||||
end
|
||||
return weapons
|
||||
end
|
||||
|
||||
local bones = loadBoneOptions()
|
||||
local weapons = loadWeaponOptions()
|
||||
|
||||
local selectData = {
|
||||
boneId = DEFAULT_BONE,
|
||||
weapon = `w_pi_pistol50`,
|
||||
weaponName = "weapon_pistol50"
|
||||
}
|
||||
|
||||
lib.registerMenu({
|
||||
id = 'sling_select',
|
||||
title = locale("slingConfig"),
|
||||
position = 'top-right',
|
||||
onSideScroll = function(selected, scrollIndex, args)
|
||||
if selected == 1 then
|
||||
selectData.boneId = Config.Bones[args[scrollIndex]]
|
||||
elseif selected == 2 then
|
||||
local weapon = Config.Weapons[args[scrollIndex]]
|
||||
selectData.weapon = weapon.model
|
||||
selectData.weaponName = args[scrollIndex]
|
||||
end
|
||||
end,
|
||||
onSelected = function(selected, secondary, args)
|
||||
end,
|
||||
onClose = function(keyPressed)
|
||||
Sling.inPositioning = false
|
||||
end,
|
||||
options = {
|
||||
{ label = 'Bone', values = bones, args = bones },
|
||||
{ label = 'Weapon', values = weapons, args = weapons },
|
||||
{ label = 'Continue' },
|
||||
}
|
||||
}, function(selected, scrollIndex, args)
|
||||
Debug("info", "Selected weapon: " .. selectData.weapon)
|
||||
Debug("info", "Selected bone: " .. selectData.boneId)
|
||||
Sling:StartPositioning(selectData)
|
||||
end)
|
||||
end
|
||||
|
||||
function Sling:WeaponThread()
|
||||
local function handleWeaponAttachment(weaponName, weaponVal, playerPed, weapon)
|
||||
if not Sling.cachedAttachments[weaponName] then
|
||||
Sling.cachedAttachments[weaponName] = {}
|
||||
end
|
||||
|
||||
local isInVehicle = IsPedInAnyVehicle(playerPed, false)
|
||||
local isSitting = IsPedUsingScenario(playerPed, "PROP_HUMAN_SEAT_CHAIR")
|
||||
local isRagdoll = IsPedRagdoll(playerPed)
|
||||
local shouldHideWeapons = isInVehicle or isSitting or isRagdoll
|
||||
|
||||
if weapon == weaponVal.name or shouldHideWeapons then
|
||||
if DoesEntityExist(Sling.cachedAttachments[weaponName].obj) then
|
||||
Utils:DeleteWeapon(weaponName)
|
||||
Sling:SyncWeaponAttachment(weaponName, nil, nil, 'detach')
|
||||
end
|
||||
else
|
||||
if not DoesEntityExist(Sling.cachedAttachments[weaponName].obj) then
|
||||
local coords = Sling.cachedPositions[weaponName] or Sling.cachedPresets[weaponName] or
|
||||
{ coords = { x = 0.0, y = -0.15, z = 0.0 }, rot = { x = 0.0, y = 0.0, z = 0.0 }, boneId = DEFAULT_BONE }
|
||||
Utils:CreateAndAttachWeapon(weaponName, weaponVal, coords, playerPed, false)
|
||||
Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, 'attach')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
CreateThread(function()
|
||||
local lastState = {
|
||||
inVehicle = false,
|
||||
sitting = false,
|
||||
ragdoll = false
|
||||
}
|
||||
|
||||
while true do
|
||||
local playerPed = cache.ped
|
||||
local weapon = GetSelectedPedWeapon(playerPed)
|
||||
local currentState = {
|
||||
inVehicle = IsPedInAnyVehicle(playerPed, false),
|
||||
sitting = IsPedUsingScenario(playerPed, "PROP_HUMAN_SEAT_CHAIR"),
|
||||
ragdoll = IsPedRagdoll(playerPed)
|
||||
}
|
||||
|
||||
local stateChanged = lastState.inVehicle ~= currentState.inVehicle or
|
||||
lastState.sitting ~= currentState.sitting or
|
||||
lastState.ragdoll ~= currentState.ragdoll
|
||||
|
||||
if stateChanged then
|
||||
lastState = Utils:DeepCopy(currentState)
|
||||
local shouldHideWeapons = currentState.inVehicle or currentState.sitting or currentState.ragdoll
|
||||
|
||||
-- Nur eigene Waffen verarbeiten
|
||||
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
|
||||
if shouldHideWeapons then
|
||||
if DoesEntityExist(Sling.cachedAttachments[weaponName]?.obj) then
|
||||
Utils:DeleteWeapon(weaponName)
|
||||
Sling:SyncWeaponAttachment(weaponName, nil, nil, 'detach')
|
||||
end
|
||||
else
|
||||
if not DoesEntityExist(Sling.cachedAttachments[weaponName]?.obj) then
|
||||
local coords = Sling.cachedPositions[weaponName] or Sling.cachedPresets[weaponName] or
|
||||
{ coords = { x = 0.0, y = -0.15, z = 0.0 }, rot = { x = 0.0, y = 0.0, z = 0.0 }, boneId = DEFAULT_BONE }
|
||||
Utils:CreateAndAttachWeapon(weaponName, weaponVal, coords, playerPed, false)
|
||||
Sling:SyncWeaponAttachment(weaponName, weaponVal, coords, 'attach')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not (currentState.inVehicle or currentState.sitting or currentState.ragdoll) then
|
||||
-- Nur eigene Waffen verarbeiten
|
||||
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
|
||||
handleWeaponAttachment(weaponName, weaponVal, playerPed, weapon)
|
||||
end
|
||||
end
|
||||
|
||||
Wait(500)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function Sling:OnPositioningDone(coords, selectData)
|
||||
lib.hideTextUI()
|
||||
Sling.inPositioning = false
|
||||
local weapon = selectData.weapon
|
||||
coords.position = vec3(coords.position.x, coords.position.y, coords.position.z)
|
||||
local distanceFromMiddle = #(coords.position - vec3(0.0, 0.0, 0.0))
|
||||
local distanceFromMiddle2 = #(coords.position - vec3(0.0, 0.0, -0.2))
|
||||
local distanceFromMiddle3 = #(coords.position - vec3(0.0, 0.0, 0.2))
|
||||
if distanceFromMiddle < 0.14 or distanceFromMiddle2 < 0.14 or distanceFromMiddle3 < 0.14 then
|
||||
coords.position = vec3(coords.position.x, 0.17, coords.position.z)
|
||||
end
|
||||
TriggerServerEvent("force-sling:server:saveWeaponPosition", coords.position, coords.rotation, weapon,
|
||||
selectData.weaponName, selectData.boneId, Sling.isPreset)
|
||||
Sling.cachedPositions[selectData.weaponName] = {
|
||||
coords = coords.position,
|
||||
rot = coords.rotation,
|
||||
boneId = selectData.boneId
|
||||
}
|
||||
if Sling.cachedAttachments[selectData.weaponName] then
|
||||
if DoesEntityExist(Sling.cachedAttachments[selectData.weaponName].obj) or DoesEntityExist(Sling.cachedAttachments[selectData.weaponName].placeholder) then
|
||||
DeleteObject(Sling.cachedAttachments[selectData.weaponName].obj)
|
||||
DeleteObject(Sling.cachedAttachments[selectData.weaponName].placeholder)
|
||||
end
|
||||
end
|
||||
DeleteObject(Sling.object)
|
||||
SetModelAsNoLongerNeeded(selectData.weapon)
|
||||
end
|
||||
|
||||
local function DisableControls()
|
||||
local controls = {
|
||||
25, 44, 45, 51, 140, 141, 143,
|
||||
263, 264, 24, 96, 97, 47, 74, 177
|
||||
}
|
||||
for i = 1, #controls do
|
||||
DisableControlAction(0, controls[i], true)
|
||||
end
|
||||
end
|
||||
|
||||
function Sling:StartPositioning(selectData)
|
||||
if Sling.inPositioning then return end
|
||||
local coords = {
|
||||
position = vec3(0.0, 0.0, 0.0),
|
||||
rotation = vec3(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
if Sling.cachedAttachments[selectData.weaponName] and DoesEntityExist(Sling.cachedAttachments[selectData.weaponName].obj) then
|
||||
Utils:DeleteWeapon(selectData.weaponName)
|
||||
end
|
||||
if Sling.cachedPositions[selectData.weaponName] and selectData.boneId == Sling.cachedPositions[selectData.weaponName].boneId then
|
||||
coords.position = Sling.cachedPositions[selectData.weaponName].coords
|
||||
coords.rotation = Sling.cachedPositions[selectData.weaponName].rot
|
||||
elseif Sling.cachedPresets[selectData.weaponName] and selectData.boneId == Sling.cachedPresets[selectData.weaponName].boneId then
|
||||
coords.position = Sling.cachedPresets[selectData.weaponName].coords
|
||||
coords.rotation = Sling.cachedPresets[selectData.weaponName].rot
|
||||
end
|
||||
|
||||
Sling.inPositioning = true
|
||||
CreateThread(function()
|
||||
local speed = DEFAULT_SPEED
|
||||
local function updatePosition(axis, delta)
|
||||
local x, y, z = coords.position.x, coords.position.y, coords.position.z
|
||||
if axis == 'x' then
|
||||
x = lib.math.clamp(x + delta, -POSITION_CLAMP, POSITION_CLAMP)
|
||||
elseif axis == 'y' then
|
||||
y = lib.math.clamp(y + delta, -POSITION_CLAMP, POSITION_CLAMP)
|
||||
elseif axis == 'z' then
|
||||
z = lib.math.clamp(z + delta, -POSITION_CLAMP, POSITION_CLAMP)
|
||||
end
|
||||
coords.position = vec3(x, y, z)
|
||||
AttachEntityToEntity(Sling.object, cache.ped, GetPedBoneIndex(cache.ped, selectData.boneId),
|
||||
coords.position.x, coords.position.y, coords.position.z,
|
||||
coords.rotation.x, coords.rotation.y, coords.rotation.z,
|
||||
true, true, false, true, 2, true)
|
||||
end
|
||||
|
||||
local function updateRotation(axis, delta)
|
||||
local x, y, z = coords.rotation.x, coords.rotation.y, coords.rotation.z
|
||||
if axis == 'x' then
|
||||
x = x + delta
|
||||
elseif axis == 'y' then
|
||||
y = y + delta
|
||||
elseif axis == 'z' then
|
||||
z = z + delta
|
||||
end
|
||||
coords.rotation = vec3(x, y, z)
|
||||
AttachEntityToEntity(Sling.object, cache.ped, GetPedBoneIndex(cache.ped, selectData.boneId),
|
||||
coords.position.x, coords.position.y, coords.position.z,
|
||||
coords.rotation.x, coords.rotation.y, coords.rotation.z,
|
||||
true, true, false, true, 2, true)
|
||||
end
|
||||
|
||||
while Sling.inPositioning do
|
||||
if not DoesEntityExist(Sling.object) then
|
||||
if not HasModelLoaded(selectData.weapon) then
|
||||
lib.requestModel(selectData.weapon)
|
||||
end
|
||||
|
||||
Sling.object = CreateObject(selectData.weapon, 0, 0, 0, false, true, false)
|
||||
AttachEntityToEntity(Sling.object, cache.ped, GetPedBoneIndex(cache.ped, selectData.boneId), coords.position.x,
|
||||
coords.position.y, coords.position.z, coords.rotation.x, coords.rotation.y, coords.rotation.z, true, true,
|
||||
false, true, 2, true)
|
||||
SetEntityCollision(Sling.object, false, false)
|
||||
end
|
||||
|
||||
if IsDisabledControlJustPressed(0, 18) then
|
||||
Sling:OnPositioningDone(coords, selectData)
|
||||
break
|
||||
end
|
||||
|
||||
if IsDisabledControlJustPressed(0, 177) then
|
||||
DeleteObject(Sling.object)
|
||||
Sling.inPositioning = false
|
||||
lib.hideTextUI()
|
||||
SetModelAsNoLongerNeeded(selectData.weapon)
|
||||
break
|
||||
end
|
||||
|
||||
if IsDisabledControlPressed(0, 21) then
|
||||
speed = FAST_SPEED
|
||||
end
|
||||
|
||||
if IsDisabledControlReleased(0, 21) then
|
||||
speed = DEFAULT_SPEED
|
||||
end
|
||||
|
||||
if IsDisabledControlPressed(0, 44) then updatePosition('x', -speed) end
|
||||
if IsDisabledControlPressed(0, 46) then updatePosition('x', speed) end
|
||||
if IsDisabledControlPressed(0, 188) then updatePosition('y', speed) end
|
||||
if IsDisabledControlPressed(0, 187) then updatePosition('y', -speed) end
|
||||
if IsDisabledControlPressed(0, 189) then updatePosition('z', speed) end
|
||||
if IsDisabledControlPressed(0, 190) then updatePosition('z', -speed) end
|
||||
if IsDisabledControlPressed(0, 96) then updateRotation('x', speed + 1.0) end
|
||||
if IsDisabledControlPressed(0, 97) then updateRotation('x', -(speed + 1.0)) end
|
||||
if IsDisabledControlPressed(0, 48) then updateRotation('z', speed + 1.0) end
|
||||
if IsDisabledControlPressed(0, 73) then updateRotation('z', -(speed + 1.0)) end
|
||||
if IsDisabledControlPressed(0, 47) then updateRotation('y', speed + 1.0) end
|
||||
if IsDisabledControlPressed(0, 74) then updateRotation('y', -(speed + 1.0)) end
|
||||
|
||||
local text = ("pos: (%.2f, %.2f, %.2f) | rot: (%.2f, %.2f, %.2f)"):format(coords.position.x, coords.position.y,
|
||||
coords.position.z, coords.rotation.x, coords.rotation.y, coords.rotation.z)
|
||||
lib.showTextUI((locale("currentPosition") .. ": %s"):format(text) ..
|
||||
' \n ' ..
|
||||
'[QE] - ' ..
|
||||
locale("up") ..
|
||||
'/' ..
|
||||
locale("down") ..
|
||||
' \n' ..
|
||||
'[Arrows] - ' ..
|
||||
locale("move") ..
|
||||
', XY \n' ..
|
||||
'[Scroll]- ' ..
|
||||
locale("rotate") ..
|
||||
' \n' ..
|
||||
'[XZ]- ' ..
|
||||
locale("rotate") ..
|
||||
' \n' ..
|
||||
'[GH] - ' ..
|
||||
locale("rotate") ..
|
||||
' Z \n' ..
|
||||
'[Shift] - ' ..
|
||||
locale("speed") .. ' \n' .. '[ENTER] - ' .. locale("confirm") .. ' \n' .. '[BACKSPACE] - ' .. locale("cancel"))
|
||||
|
||||
DisableControls()
|
||||
|
||||
Wait(4)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function Sling:StartConfiguration(isPreset)
|
||||
Sling.isPreset = isPreset
|
||||
lib.showMenu('sling_select')
|
||||
end
|
||||
|
||||
function Sling:InitCommands()
|
||||
Debug("info", "Initializing commands")
|
||||
local admin = lib.callback.await("force-sling:callback:isPlayerAdmin", false)
|
||||
if Config.Debug or admin.isAdmin then
|
||||
RegisterCommand(Config.Command.name, function(source, args, raw)
|
||||
if Config.Command.permission ~= "any" and admin ~= Config.Command.permission then return end
|
||||
Sling:StartConfiguration(false)
|
||||
end, false)
|
||||
|
||||
RegisterCommand(Config.Command.reset, function(source, args, raw)
|
||||
if Config.Command.permission ~= "any" and admin ~= Config.Command.permission then return end
|
||||
local weapon = args[1] and args[1]:lower() or GetSelectedPedWeapon(cache.ped)
|
||||
if type(weapon) == "number" then
|
||||
for weaponName, weaponVal in pairs(Sling.cachedWeapons) do
|
||||
if weaponVal.name == weapon then
|
||||
weapon = weaponName
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
Sling.cachedPositions = lib.callback.await("force-sling:callback:resetWeaponPositions", false, weapon)
|
||||
end, false)
|
||||
|
||||
RegisterCommand(Config.Presets.command, function(source, args, raw)
|
||||
if Config.Presets.permission ~= "any" and admin ~= Config.Presets.permission then return end
|
||||
Sling:StartConfiguration(true)
|
||||
end, false)
|
||||
end
|
||||
|
||||
Debug("info", "Commands initialized")
|
||||
end
|
|
@ -1,12 +0,0 @@
|
|||
lib.locale(Config.Locale);
|
||||
|
||||
InitFramework()
|
||||
InitInventory()
|
||||
|
||||
CreateThread(function()
|
||||
while not IsPlayerLoaded() do
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
Sling:InitMain()
|
||||
end)
|
|
@ -1,80 +0,0 @@
|
|||
Inventory = {}
|
||||
|
||||
local function SafeInventoryCall(fn)
|
||||
local success, result = pcall(fn)
|
||||
if not success then
|
||||
Debug("error", "Inventory error: " .. tostring(result))
|
||||
return nil
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--- Retrieves the player's weapons from the inventory.
|
||||
--- @return table A table containing the player's weapons.
|
||||
function Inventory:GetWeapons()
|
||||
local weapons = {}
|
||||
local userInventory = self:GetUserInventory()
|
||||
|
||||
if not userInventory then
|
||||
Debug("warn", "Unsupported inventory system: " .. tostring(Config.Inventory))
|
||||
return weapons
|
||||
end
|
||||
|
||||
-- Iterate through the user's inventory and match weapons with the configured weapons
|
||||
for _, v in pairs(userInventory) do
|
||||
for key, val in pairs(Config.Weapons) do
|
||||
if v.name:lower() == key:lower() then
|
||||
weapons[key] = val
|
||||
weapons[key].attachments = self:GetWeaponAttachment(key)
|
||||
Debug("info", "Weapon found: " .. key)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return weapons
|
||||
end
|
||||
|
||||
--- Retrieves the attachments for a specific weapon.
|
||||
--- @param item string The weapon name.
|
||||
--- @return table A table containing the weapon's attachments.
|
||||
function Inventory:GetWeaponAttachment(item)
|
||||
if not Config.UseWeaponAttachments then return {} end
|
||||
local components = {}
|
||||
local userInventory = self:GetUserInventory()
|
||||
|
||||
if not userInventory then
|
||||
Debug("warn", "Unsupported inventory system: " .. tostring(Config.Inventory))
|
||||
return components
|
||||
end
|
||||
|
||||
-- Iterate through the user's inventory and match attachments with the specified weapon
|
||||
for _, v in pairs(userInventory) do
|
||||
if v.name:lower() == item:lower() and v.info and v.info.attachments then
|
||||
for _, attachment in pairs(v.info.attachments) do
|
||||
table.insert(components, attachment.component)
|
||||
Debug("info", "Attachment found for weapon: " .. item .. " component: " .. attachment?.component)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return components
|
||||
end
|
||||
|
||||
--- Retrieves the user's inventory based on the configured inventory system.
|
||||
--- @return table|nil The user's inventory or nil if the inventory system is unsupported.
|
||||
function Inventory:GetUserInventory()
|
||||
if Config.Inventory == "qs-inventory" then
|
||||
return SafeInventoryCall(function() return exports['qs-inventory']:getUserInventory() end)
|
||||
elseif Config.Inventory == "core_inventory" then
|
||||
return SafeInventoryCall(function() return exports.core_inventory:getInventory() end)
|
||||
elseif Config.Inventory == "qb-inventory" then
|
||||
return QBCore.Functions.GetPlayerData().items
|
||||
elseif Config.Inventory == "ox_inventory" then
|
||||
return exports.ox_inventory:GetPlayerItems()
|
||||
elseif Config.Inventory == "custom" then
|
||||
return CustomInventory:GetWeapons()
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
|
@ -1 +0,0 @@
|
|||
otherPlayersWeapons = {}
|
|
@ -1,244 +0,0 @@
|
|||
Utils = {}
|
||||
|
||||
function Utils:CreateAndAttachWeapon(weaponName, weaponVal, coords, playerPed, isOtherPlayer)
|
||||
if not isOtherPlayer and Sling.currentAttachedAmount >= Config.MaxWeaponsAttached then
|
||||
Debug("warn", "Max weapons attached reached")
|
||||
return false
|
||||
end
|
||||
|
||||
if not weaponVal or not weaponVal.name then
|
||||
Debug("error", "Invalid weapon data")
|
||||
return false
|
||||
end
|
||||
|
||||
-- Initialize the cached attachments for this weapon if it doesn't exist
|
||||
if not Sling.cachedAttachments[weaponName] then
|
||||
Sling.cachedAttachments[weaponName] = {}
|
||||
end
|
||||
|
||||
local weaponObject = CreateWeaponObject(weaponVal.name, 0, coords.coords.x, coords.coords.y, coords.coords.z, true, 1.0, 0)
|
||||
if not weaponObject then
|
||||
Debug("error", "Failed to create weapon object")
|
||||
return false
|
||||
end
|
||||
|
||||
if NetworkGetEntityIsNetworked(weaponObject) then
|
||||
NetworkUnregisterNetworkedEntity(weaponObject)
|
||||
end
|
||||
|
||||
SetEntityCollision(weaponObject, false, false)
|
||||
|
||||
if not isOtherPlayer and Config.UseWeaponAttachments then
|
||||
weaponVal.attachments = Inventory:GetWeaponAttachment(weaponName)
|
||||
end
|
||||
|
||||
for _, component in pairs(weaponVal.attachments or {}) do
|
||||
GiveWeaponComponentToWeaponObject(weaponObject, component)
|
||||
end
|
||||
|
||||
lib.requestModel(weaponVal.model)
|
||||
|
||||
local placeholder = CreateObjectNoOffset(weaponVal.model, coords.coords.x, coords.coords.y, coords.coords.z, true, true, false)
|
||||
SetEntityCollision(placeholder, false, false)
|
||||
SetEntityAlpha(placeholder, 0, false)
|
||||
|
||||
local boneIndex = GetPedBoneIndex(playerPed, (coords.boneId or DEFAULT_BONE))
|
||||
|
||||
AttachEntityToEntity(placeholder, playerPed, boneIndex,
|
||||
coords.coords.x, coords.coords.y, coords.coords.z,
|
||||
coords.rot.x, coords.rot.y, coords.rot.z,
|
||||
true, true, false, true, 2, true)
|
||||
|
||||
AttachEntityToEntity(weaponObject, placeholder, GetEntityBoneIndexByName(placeholder, "gun_root"),
|
||||
0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0,
|
||||
true, true, false, true, 2, true)
|
||||
|
||||
Sling.cachedAttachments[weaponName].obj = weaponObject
|
||||
Sling.cachedAttachments[weaponName].placeholder = placeholder
|
||||
if not isOtherPlayer then
|
||||
Sling.currentAttachedAmount = Sling.currentAttachedAmount + 1
|
||||
end
|
||||
|
||||
SetModelAsNoLongerNeeded(weaponVal.model)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function Utils:DeleteWeapon(weaponName)
|
||||
if not Sling.cachedAttachments[weaponName] then return end
|
||||
|
||||
local attachment = Sling.cachedAttachments[weaponName]
|
||||
|
||||
if attachment.obj and DoesEntityExist(attachment.obj) then
|
||||
if NetworkGetEntityIsNetworked(attachment.obj) then
|
||||
NetworkUnregisterNetworkedEntity(attachment.obj)
|
||||
end
|
||||
DeleteObject(attachment.obj)
|
||||
end
|
||||
|
||||
if attachment.placeholder then
|
||||
if IsEntityAttachedToAnyPed(attachment.placeholder) then
|
||||
DetachEntity(attachment.placeholder, true, false)
|
||||
end
|
||||
if DoesEntityExist(attachment.placeholder) then
|
||||
DeleteObject(attachment.placeholder)
|
||||
end
|
||||
end
|
||||
|
||||
Sling.cachedAttachments[weaponName] = nil
|
||||
|
||||
-- Nur verringern, wenn es keine Waffe eines anderen Spielers ist
|
||||
if not string.find(weaponName, '_') then
|
||||
Sling.currentAttachedAmount = math.max(0, Sling.currentAttachedAmount - 1)
|
||||
end
|
||||
end
|
||||
|
||||
function Utils:Debug(type, message)
|
||||
if not Config.Debug then return end
|
||||
|
||||
if type == "error" then
|
||||
print("^1[ERROR] " .. message .. "^7")
|
||||
elseif type == "success" then
|
||||
print("^2[SUCCESS] " .. message .. "^7")
|
||||
elseif type == "info" then
|
||||
print("^5[INFO] " .. message .. "^7")
|
||||
elseif type == "warn" then
|
||||
print("^3[WARN] " .. message .. "^7")
|
||||
end
|
||||
end
|
||||
|
||||
function Utils:Round(num, numDecimalPlaces)
|
||||
local mult = 10^(numDecimalPlaces or 0)
|
||||
return math.floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
function Utils:TableContains(table, element)
|
||||
for _, value in pairs(table) do
|
||||
if value == element then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function Utils:GetTableLength(table)
|
||||
local count = 0
|
||||
for _ in pairs(table) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function Utils:DeepCopy(orig)
|
||||
local orig_type = type(orig)
|
||||
local copy
|
||||
if orig_type == 'table' then
|
||||
copy = {}
|
||||
for orig_key, orig_value in next, orig, nil do
|
||||
copy[Utils:DeepCopy(orig_key)] = Utils:DeepCopy(orig_value)
|
||||
end
|
||||
setmetatable(copy, Utils:DeepCopy(getmetatable(orig)))
|
||||
else
|
||||
copy = orig
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
function Utils:MergeTable(t1, t2)
|
||||
for k, v in pairs(t2) do
|
||||
if type(v) == "table" and type(t1[k] or false) == "table" then
|
||||
Utils:MergeTable(t1[k], t2[k])
|
||||
else
|
||||
t1[k] = v
|
||||
end
|
||||
end
|
||||
return t1
|
||||
end
|
||||
|
||||
function Utils:GetDistance(coords1, coords2)
|
||||
return #(coords1 - coords2)
|
||||
end
|
||||
|
||||
function Utils:DrawText3D(coords, text)
|
||||
local onScreen, _x, _y = World3dToScreen2d(coords.x, coords.y, coords.z)
|
||||
local px, py, pz = table.unpack(GetGameplayCamCoords())
|
||||
local dist = #(vector3(px, py, pz) - coords)
|
||||
|
||||
local scale = (1 / dist) * 2
|
||||
local fov = (1 / GetGameplayCamFov()) * 100
|
||||
local scale = scale * fov
|
||||
|
||||
if onScreen then
|
||||
SetTextScale(0.0 * scale, 0.55 * scale)
|
||||
SetTextFont(0)
|
||||
SetTextProportional(1)
|
||||
SetTextColour(255, 255, 255, 255)
|
||||
SetTextDropshadow(0, 0, 0, 0, 255)
|
||||
SetTextEdge(2, 0, 0, 0, 150)
|
||||
SetTextDropShadow()
|
||||
SetTextOutline()
|
||||
SetTextEntry("STRING")
|
||||
SetTextCentre(1)
|
||||
AddTextComponentString(text)
|
||||
DrawText(_x,_y)
|
||||
end
|
||||
end
|
||||
|
||||
function Utils:LoadAnimDict(dict)
|
||||
while not HasAnimDictLoaded(dict) do
|
||||
RequestAnimDict(dict)
|
||||
Wait(5)
|
||||
end
|
||||
end
|
||||
|
||||
function Utils:PlayAnim(ped, dict, anim, settings)
|
||||
if not settings then settings = {} end
|
||||
|
||||
Utils:LoadAnimDict(dict)
|
||||
|
||||
TaskPlayAnim(ped, dict, anim,
|
||||
settings.blendInSpeed or 3.0,
|
||||
settings.blendOutSpeed or 3.0,
|
||||
settings.duration or -1,
|
||||
settings.flag or 49,
|
||||
settings.playbackRate or 0,
|
||||
settings.lockX or false,
|
||||
settings.lockY or false,
|
||||
settings.lockZ or false
|
||||
)
|
||||
|
||||
RemoveAnimDict(dict)
|
||||
end
|
||||
|
||||
function Utils:CreateBlip(coords, sprite, color, text, scale, category)
|
||||
local blip = AddBlipForCoord(coords.x, coords.y, coords.z)
|
||||
SetBlipSprite(blip, sprite)
|
||||
SetBlipDisplay(blip, 4)
|
||||
SetBlipScale(blip, scale or 0.8)
|
||||
SetBlipColour(blip, color)
|
||||
SetBlipAsShortRange(blip, true)
|
||||
if category then
|
||||
SetBlipCategory(blip, category)
|
||||
end
|
||||
|
||||
BeginTextCommandSetBlipName("STRING")
|
||||
AddTextComponentString(text)
|
||||
EndTextCommandSetBlipName(blip)
|
||||
|
||||
return blip
|
||||
end
|
||||
|
||||
function Utils:DrawMarker(type, coords, size, color, bobUpAndDown)
|
||||
DrawMarker(
|
||||
type,
|
||||
coords.x, coords.y, coords.z,
|
||||
0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0,
|
||||
size.x, size.y, size.z,
|
||||
color.r, color.g, color.b, color.a,
|
||||
false, bobUpAndDown or false, 2, false, nil, nil, false
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
Config = {}
|
||||
|
||||
-- Enable or disable debug mode
|
||||
-- @field [boolean] Debug - Set to true to enable debug mode, false to disable
|
||||
Config.Debug = false
|
||||
|
||||
-- Set the locale for the application
|
||||
-- @field [string] Locale - Language code for the locale ("ar", "en", "es", "fr", "pt", "de", "nl", "pl", "ru", "se" or "auto")
|
||||
Config.Locale = "de"
|
||||
|
||||
-- Admin configuration settings
|
||||
-- @field [table] Admin - Configuration for admin settings
|
||||
-- @field [table] Admin.Global - Global admin settings
|
||||
-- @field [boolean] Admin.Global.enabled - Enable or disable global admin commands
|
||||
-- @field [table] Admin.Global.players - List of player identifiers with admin access
|
||||
Config.Admin = {
|
||||
Global = {
|
||||
enabled = true,
|
||||
ace = "admin", -- Ace permission required for global admin access, set to false to disable
|
||||
players = {
|
||||
"discord:453870580374962177", -- Force
|
||||
"discord:566930948986241024", -- Olpis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Framework configuration settings
|
||||
-- @field [table] Framework - Configuration for the framework
|
||||
-- @field [string] Framework.name - Name of the framework ("esx", "qbcore", "qbx", "custom" or "auto")
|
||||
-- @field [string] Framework.resource - Resource name for ESX or QBCore
|
||||
Config.Framework = {
|
||||
name = "auto",
|
||||
resource = "auto"
|
||||
}
|
||||
|
||||
-- Inventory configuration settings
|
||||
-- @field [string] Inventory - Inventory system to use ("none", "auto", "qs-inventory", "qb-inventory", "core_inventory", "ox_inventory" or "custom")
|
||||
-- @field [boolean] UseWeaponAttachments - Enable or disable weapon attachments (may not work with all inventory systems and may lower performance)
|
||||
-- @field [number] MaxWeaponsAttached - Maximum number of weapons that can be attached to the player; the higher the number, the more performance impact
|
||||
Config.Inventory = "qs-inventory"
|
||||
Config.UseWeaponAttachments = true
|
||||
Config.MaxWeaponsAttached = 4
|
||||
|
||||
-- Command configuration settings
|
||||
-- @field [table] Command - Configuration for the sling command
|
||||
-- @field [string] Command.name - Name of the command
|
||||
-- @field [string] Command.reset - Command to reset the sling configuration
|
||||
-- @field [string] Command.description - Description of the command
|
||||
-- @field [string] Command.permission - Permission level required to use the command ("any" or specific permission)
|
||||
Config.Command = {
|
||||
name = "sling",
|
||||
reset = "resetsling",
|
||||
description = "Configure weapon positions",
|
||||
permission = "any"
|
||||
}
|
||||
|
||||
-- Preset command configuration settings
|
||||
-- @field [table] Presets - Configuration for the sling preset command
|
||||
-- @field [string] Presets.command - Name of the preset command
|
||||
-- @field [string] Presets.permission - Permission level required to use the preset command ("global" or specific permission)
|
||||
Config.Presets = {
|
||||
command = "slingpreset",
|
||||
permission = "global",
|
||||
}
|
||||
|
||||
-- Bone configuration settings
|
||||
-- @field [table] Bones - Configuration for weapon attachment bones
|
||||
-- @field [number] Bones.Back - Bone ID for back attachment
|
||||
-- @field [number] Bones.Front - Bone ID for front attachment
|
||||
Config.Bones = {
|
||||
Back = 24816,
|
||||
Front = 10706
|
||||
}
|
||||
|
||||
-- Editable weapon configuration settings
|
||||
-- @field [table] Weapons - Configuration for editable weapons
|
||||
-- @field [table] Weapons[weapon_name] - Configuration for a specific weapon
|
||||
-- @field [string] Weapons[weapon_name].model - Model name for the weapon
|
||||
-- @field [string] Weapons[weapon_name].name - Internal name for the weapon
|
||||
Config.Weapons = {
|
||||
["weapon_advancedrifle"] = { model = `w_ar_advancedrifle`, name = `weapon_advancedrifle` },
|
||||
["weapon_assaultshotgun"] = { model = `w_sg_assaultshotgun`, name = `weapon_assaultshotgun` },
|
||||
["weapon_assaultsmg"] = { model = `w_sb_assaultsmg`, name = `weapon_assaultsmg` },
|
||||
["weapon_bullpuprifle_mk2"] = { model = `w_ar_bullpupriflemk2`, name = `weapon_bullpuprifle_mk2` },
|
||||
["weapon_bullpupshotgun"] = { model = `w_sg_bullpupshotgun`, name = `weapon_bullpupshotgun` },
|
||||
["weapon_carbinerifle"] = { model = `w_ar_carbinerifle`, name = `weapon_carbinerifle` },
|
||||
["weapon_carbinerifle_mk2"] = { model = `w_ar_carbineriflemk2`, name = `weapon_carbinerifle_mk2` },
|
||||
["weapon_combatmg"] = { model = `w_mg_combatmg`, name = `weapon_combatmg` },
|
||||
["weapon_combatmg_mk2"] = { model = `w_mg_combatmgmk2`, name = `weapon_combatmg_mk2` },
|
||||
["weapon_combatpdw"] = { model = `w_sb_pdw`, name = `weapon_combatpdw` },
|
||||
["weapon_combatshotgun"] = { model = `w_sg_pumpshotgunh4`, name = `weapon_combatshotgun` },
|
||||
["weapon_compactrifle"] = { model = `w_ar_assaultrifle_smg`, name = `weapon_compactrifle` },
|
||||
["weapon_firework"] = { model = `w_lr_firework`, name = `weapon_firework` },
|
||||
["weapon_heavyrifle"] = { model = `w_ar_heavyrifleh`, name = `weapon_heavyrifle` },
|
||||
["weapon_heavyshotgun"] = { model = `w_sg_heavyshotgun`, name = `weapon_heavyshotgun` },
|
||||
["weapon_heavysniper"] = { model = `w_sr_heavysniper`, name = `weapon_heavysniper` },
|
||||
["weapon_marksmanrifle"] = { model = `w_sr_marksmanrifle`, name = `weapon_marksmanrifle` },
|
||||
["weapon_marksmanrifle_mk2"] = { model = `w_sr_marksmanriflemk2`, name = `weapon_marksmanrifle_mk2` },
|
||||
["weapon_mg"] = { model = `w_mg_mg`, name = `weapon_mg` },
|
||||
["weapon_militaryrifle"] = { model = `w_ar_bullpuprifleh4`, name = `weapon_militaryrifle` },
|
||||
["weapon_minismg"] = { model = `w_sb_minismg`, name = `weapon_minismg` },
|
||||
["weapon_musket"] = { model = `w_ar_musket`, name = `weapon_musket` },
|
||||
["weapon_pumpshotgun"] = { model = `w_sg_pumpshotgun`, name = `weapon_pumpshotgun` },
|
||||
["weapon_smg"] = { model = `w_sb_smg`, name = `weapon_smg` },
|
||||
["weapon_sniperrifle"] = { model = `w_sr_sniperrifle`, name = `weapon_sniperrifle` },
|
||||
["weapon_huntingrifle"] = { model = `w_sr_huntingrifle`, name = `weapon_huntingrifle` },
|
||||
["weapon_specialcarbine"] = { model = `w_ar_specialcarbine`, name = `weapon_specialcarbine` },
|
||||
["weapon_autoshotgun"] = { model = `w_sg_sweeper`, name = `weapon_autoshotgun` },
|
||||
["weapon_precisionrifle"] = { model = `w_sr_precisionrifle_reh`, name = `weapon_precisionrifle` },
|
||||
["weapon_tacticalrifle"] = { model = `w_ar_carbinerifle_reh`, name = `weapon_tacticalrifle` },
|
||||
["weapon_rayminigun"] = { model = `w_mg_sminigun`, name = `weapon_rayminigun` },
|
||||
["weapon_raycarbine"] = { model = `w_ar_srifle`, name = `weapon_raycarbine` },
|
||||
["weapon_railgun"] = { model = `w_ar_railgun`, name = `weapon_railgun` },
|
||||
["weapon_rpg"] = { model = `w_lr_rpg`, name = `weapon_rpg` },
|
||||
["weapon_hominglauncher"] = { model = `w_lr_homing`, name = `weapon_hominglauncher` },
|
||||
["weapon_minigun"] = { model = `w_mg_minigun`, name = `weapon_minigun` },
|
||||
["weapon_bat"] = { model = `w_me_bat`, name = `weapon_bat` },
|
||||
["weapon_battleaxe"] = { model = `w_me_battleaxe`, name = `weapon_battleaxe` },
|
||||
["weapon_crowbar"] = { model = `w_me_crowbar`, name = `weapon_crowbar` },
|
||||
["weapon_fireextinguisher"] = { model = `w_am_fire_exting`, name = `weapon_fireextinguisher` },
|
||||
["weapon_golfclub"] = { model = `w_me_gclub`, name = `weapon_golfclub` },
|
||||
["weapon_hatchet"] = { model = `w_me_hatchet`, name = `weapon_hatchet` },
|
||||
["weapon_hazardcan"] = { model = `w_ch_jerrycan`, name = `weapon_hazardcan` },
|
||||
["weapon_fertilizercan"] = { model = `w_am_jerrycan_sf`, name = `weapon_fertilizercan` },
|
||||
["weapon_machete"] = { model = `w_me_machette_lr`, name = `weapon_machete` },
|
||||
["weapon_poolcue"] = { model = `w_me_poolcue`, name = `weapon_poolcue` },
|
||||
["weapon_stone_hatchet"] = { model = `w_me_stonehatchet`, name = `weapon_stone_hatchet` },
|
||||
["weapon_wrench"] = { model = `w_me_wrench`, name = `weapon_wrench` },
|
||||
["weapon_candycane"] = { model = `w_me_candy_xm3`, name = `weapon_candycane` },
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
fx_version "cerulean"
|
||||
lua54 'yes'
|
||||
game "gta5"
|
||||
|
||||
author 'Force Developments <discord:@force3883>'
|
||||
description 'Fivem Sling system for ESX, QBCore and custom frameworks'
|
||||
version '1.3.0'
|
||||
|
||||
dependencies {
|
||||
'ox_lib',
|
||||
'/assetpacks',
|
||||
'mysql-async' -- oder 'oxmysql', je nachdem was du verwendest
|
||||
}
|
||||
|
||||
shared_scripts {
|
||||
'@ox_lib/init.lua',
|
||||
"shared/variables.lua",
|
||||
"shared/*.lua",
|
||||
"config.lua",
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'@mysql-async/lib/MySQL.lua', -- oder '@mysql-async/lib/MySQL.lua'
|
||||
'version.lua',
|
||||
"server/events.lua",
|
||||
"server/functions.lua",
|
||||
"server/main.lua",
|
||||
"server/misc/*.lua",
|
||||
"server/callbacks.lua"
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
"client/utils.lua",
|
||||
"client/functions.lua",
|
||||
"client/events.lua",
|
||||
"client/main.lua",
|
||||
"client/custom/frameworks/*.lua",
|
||||
"client/custom/*.lua",
|
||||
"client/misc/*.lua"
|
||||
}
|
||||
|
||||
files {
|
||||
"locales/*.json",
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"license:60b5f8ebf593295e3f65cbf4cb4c85096b37da2a": {
|
||||
"weapon_specialcarbine": {
|
||||
"rot": {
|
||||
"x": -6.00599956512451,
|
||||
"y": 66.06597900390625,
|
||||
"z": 196.19642639160157
|
||||
},
|
||||
"coords": {
|
||||
"x": 0.0,
|
||||
"y": 0.20000000298023,
|
||||
"z": -0.20000000298023
|
||||
},
|
||||
"boneId": 10706
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
{
|
||||
"weapon_assaultsmg": {
|
||||
"coords": {
|
||||
"x": 0.0,
|
||||
"z": -0.35,
|
||||
"y": 0.17
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 180.0,
|
||||
"y": 55.0
|
||||
},
|
||||
"boneId": 10706
|
||||
},
|
||||
"weapon_heavyshotgun": {
|
||||
"coords": {
|
||||
"x": 0.1,
|
||||
"z": -0.2,
|
||||
"y": 0.17
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 190.0,
|
||||
"y": 60.0
|
||||
},
|
||||
"boneId": 10706
|
||||
},
|
||||
"weapon_specialcarbine": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": 0.03,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 5.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_tacticalrifle": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": 0.11,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 2.0,
|
||||
"y": -15.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_carbinerifle": {
|
||||
"coords": {
|
||||
"x": 0.0,
|
||||
"z": -0.25,
|
||||
"y": 0.17
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 180.0,
|
||||
"y": 75.0
|
||||
},
|
||||
"boneId": 10706
|
||||
},
|
||||
"weapon_bullpuprifle": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": 0.055,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 0.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_assaultrifle": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": 0.11,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 2.0,
|
||||
"y": -15.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_smg_mk2": {
|
||||
"coords": {
|
||||
"x": -0.14,
|
||||
"z": 0.21,
|
||||
"y": 0.05
|
||||
},
|
||||
"rot": {
|
||||
"x": 90.0,
|
||||
"z": 50.0,
|
||||
"y": 120.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_sniperrifle": {
|
||||
"coords": {
|
||||
"x": 0.005,
|
||||
"z": 0.0,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 2.0,
|
||||
"y": -15.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_appistol": {
|
||||
"coords": {
|
||||
"x": -0.14,
|
||||
"z": -0.21,
|
||||
"y": 0.05
|
||||
},
|
||||
"rot": {
|
||||
"x": 90.0,
|
||||
"z": 50.0,
|
||||
"y": 90.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_musket": {
|
||||
"coords": {
|
||||
"x": 0.0,
|
||||
"z": 0.0,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 2.0,
|
||||
"y": 15.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_pistol50": {
|
||||
"coords": {
|
||||
"x": 0.0,
|
||||
"y": -0.20000000298023,
|
||||
"z": 0.18999999761581
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 0.0,
|
||||
"y": 0.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_assaultshotgun": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": 0.015,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 160.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_pumpshotgun": {
|
||||
"coords": {
|
||||
"x": 0.1275,
|
||||
"z": 0.03,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 25.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_microsmg": {
|
||||
"coords": {
|
||||
"x": -0.2,
|
||||
"z": -0.21,
|
||||
"y": 0.05
|
||||
},
|
||||
"rot": {
|
||||
"x": 90.0,
|
||||
"z": 50.0,
|
||||
"y": 110.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_carbinerifle_mk2": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": -0.11,
|
||||
"y": -0.17
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 20.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_smg": {
|
||||
"coords": {
|
||||
"x": 0.1275,
|
||||
"z": -0.055,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 35.0
|
||||
},
|
||||
"boneId": 24816
|
||||
},
|
||||
"weapon_advancedrifle": {
|
||||
"coords": {
|
||||
"x": 0.2275,
|
||||
"z": -0.055,
|
||||
"y": -0.16
|
||||
},
|
||||
"rot": {
|
||||
"x": 0.0,
|
||||
"z": 1.0,
|
||||
"y": 35.0
|
||||
},
|
||||
"boneId": 24816
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "حمالة",
|
||||
"slingConfig": "إعدادات الحمالة",
|
||||
"currentPosition": "الموقع الحالي",
|
||||
"up": "أعلى",
|
||||
"down": "أسفل",
|
||||
"move": "تحريك",
|
||||
"rotate": "تدوير",
|
||||
"speed": "السرعة",
|
||||
"confirm": "تأكيد",
|
||||
"cancel": "إلغاء"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Schlinge",
|
||||
"slingConfig": "Schlingenkonfiguration",
|
||||
"currentPosition": "Aktuelle Position",
|
||||
"up": "Oben",
|
||||
"down": "Unten",
|
||||
"move": "Bewegen",
|
||||
"rotate": "Drehen",
|
||||
"speed": "Geschwindigkeit",
|
||||
"confirm": "Bestätigen",
|
||||
"cancel": "Abbrechen"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Sling",
|
||||
"slingConfig": "Sling Configuration",
|
||||
"currentPosition": "Current Position",
|
||||
"up": "Up",
|
||||
"down": "Down",
|
||||
"move": "Move",
|
||||
"rotate": "Rotate",
|
||||
"speed": "Speed",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Eslinga",
|
||||
"slingConfig": "Configuración de eslinga",
|
||||
"currentPosition": "Posición actual",
|
||||
"up": "Arriba",
|
||||
"down": "Abajo",
|
||||
"move": "Mover",
|
||||
"rotate": "Girar",
|
||||
"speed": "Velocidad",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Sangle",
|
||||
"slingConfig": "Configuration de la sangle",
|
||||
"currentPosition": "Position actuelle",
|
||||
"up": "Haut",
|
||||
"down": "Bas",
|
||||
"move": "Déplacer",
|
||||
"rotate": "Pivoter",
|
||||
"speed": "Vitesse",
|
||||
"confirm": "Confirmer",
|
||||
"cancel": "Annuler"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Draagband",
|
||||
"slingConfig": "Draagbandconfiguratie",
|
||||
"currentPosition": "Huidige positie",
|
||||
"up": "Omhoog",
|
||||
"down": "Omlaag",
|
||||
"move": "Verplaatsen",
|
||||
"rotate": "Draaien",
|
||||
"speed": "Snelheid",
|
||||
"confirm": "Bevestigen",
|
||||
"cancel": "Annuleren"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Pasek",
|
||||
"slingConfig": "Konfiguracja paska",
|
||||
"currentPosition": "Aktualna pozycja",
|
||||
"up": "Góra",
|
||||
"down": "Dół",
|
||||
"move": "Przesuń",
|
||||
"rotate": "Obróć",
|
||||
"speed": "Prędkość",
|
||||
"confirm": "Potwierdź",
|
||||
"cancel": "Anuluj"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Alça",
|
||||
"slingConfig": "Configuração da alça",
|
||||
"currentPosition": "Posição atual",
|
||||
"up": "Cima",
|
||||
"down": "Baixo",
|
||||
"move": "Mover",
|
||||
"rotate": "Girar",
|
||||
"speed": "Velocidade",
|
||||
"confirm": "Confirmar",
|
||||
"cancel": "Cancelar"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Стропа",
|
||||
"slingConfig": "Настройка стропы",
|
||||
"currentPosition": "Текущее положение",
|
||||
"up": "Вверх",
|
||||
"down": "Вниз",
|
||||
"move": "Переместить",
|
||||
"rotate": "Повернуть",
|
||||
"speed": "Скорость",
|
||||
"confirm": "Подтвердить",
|
||||
"cancel": "Отмена"
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"sling": "Sele",
|
||||
"slingConfig": "Selekonfiguration",
|
||||
"currentPosition": "Nuvarande position",
|
||||
"up": "Upp",
|
||||
"down": "Ner",
|
||||
"move": "Flytta",
|
||||
"rotate": "Rotera",
|
||||
"speed": "Hastighet",
|
||||
"confirm": "Bekräfta",
|
||||
"cancel": "Avbryt"
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
# FiveM Sling System / Weapons on back
|
||||
|
||||
The resource allows players to manage weapon sling positions in the game, dynamically reflecting the weapons they have in their inventory. This enhances immersion and adds a realistic touch to your FiveM server. [Documentation](https://docs.forcedevelopments.com/) [Forum](https://forum.cfx.re/t/free-sling-system-weapons-on-back-esx-qb-core-custom/5290047)
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Debug Mode**: Enable or disable debug mode for troubleshooting.
|
||||
- **Locale Support**: Set the locale for the resource.
|
||||
- **Admin Configuration**: Manage admin commands and permissions.
|
||||
- **Framework Support**: Compatible with ESX, QBCore, and custom frameworks.
|
||||
- **Inventory System**: Supports various inventory systems including qs-inventory, qb-inventory, core_inventory, and ox_inventory.
|
||||
- **Weapon Attachments**: Enable or disable weapon attachments.
|
||||
- **Command Configuration**: Customize commands for configuring weapon positions.
|
||||
- **Preset Commands**: Use preset configurations for weapon positions.
|
||||
- **Bone Configuration**: Configure player bones where the weapon can be attached.
|
||||
- **Editable Weapons**: Manage configurations for editable weapons.
|
||||
|
||||
### Preview
|
||||
|
||||

|
||||
_Controls while placing_
|
||||
|
||||

|
||||
_Config menu ingame_
|
||||
|
||||

|
||||
_Placement_
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before setting up the resource, ensure that you have the following dependency installed:
|
||||
|
||||
- **ox_lib**: This library is required for the resource to function correctly.
|
||||
- **GitHub Repository**: [ox_lib](https://github.com/overextended/ox_lib)
|
||||
- **Documentation**: [ox_lib Documentation](https://overextended.dev/ox_lib)
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
1. **Download and Install**
|
||||
|
||||
- Clone or download this repository to your `resources` folder in your FiveM server.
|
||||
|
||||
2. **Install ox_lib**
|
||||
|
||||
- Ensure that you have `ox_lib` installed. You can find it on [GitHub](https://github.com/overextended/ox_lib) or refer to the [ox_lib Documentation](https://overextended.dev/ox_lib) for installation instructions.
|
||||
|
||||
3. **Configure**
|
||||
|
||||
- Open the `config.lua` file and modify settings to fit your server’s framework and preferences.
|
||||
- Example:
|
||||
```lua
|
||||
Config.Locale = "en" -- Change to your preferred language (e.g., "fr", "es", "ru").
|
||||
Config.Framework.name = "esx" -- Set to your framework: "esx", "qbcore", or "custom".
|
||||
Config.Inventory = "ox_inventory" -- Match your inventory system: "auto", "qs_inventory", etc.
|
||||
```
|
||||
|
||||
4. **Add to Server Config**
|
||||
|
||||
- Add the following line to your `server.cfg`:
|
||||
```cfg
|
||||
ensure ox_lib
|
||||
ensure force-sling
|
||||
```
|
||||
|
||||
5. **Start Your Server**
|
||||
|
||||
- Restart your server or the resource to load the resource.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
The resource includes a [config.lua](https://github.com/Force-Developing/force-sling/blob/main/config.lua) file to customize functionality:
|
||||
|
||||
- **Debug Mode**:
|
||||
- Enable or disable debug logging.
|
||||
- **Locale**:
|
||||
- Set the language for the system.
|
||||
- Supported languages: `ar`, `en`, `es`, `fr`, `pt`, `de`, `nl`, `pl`, `ru`, `se`, or `auto`.
|
||||
- **Admin Tools**:
|
||||
- Add admin player identifiers under the `Config.Admin.Global.players` array.
|
||||
- **Framework Support**:
|
||||
- Supports ESX, QBCore, or custom frameworks.
|
||||
- **Inventory Integration**:
|
||||
- Compatible with popular inventory systems like `ox_inventory` and `qb_inventory`.
|
||||
|
||||
Refer to the Configuration section for detailed information on each setting.
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
The resource provides several commands to manage weapon positions:
|
||||
|
||||
- **`/sling`**
|
||||
|
||||
- **Description**: Configure weapon positions.
|
||||
- **Permission**: Any player can use this command.
|
||||
|
||||
- **`/resetsling`**
|
||||
|
||||
- **Description**: Reset personal sling position to global.
|
||||
- **Permission**: Any player can use this command.
|
||||
|
||||
- **`/slingpreset`**
|
||||
- **Description**: Configure global weapon positions.
|
||||
- **Permission**: Only admins are allowed to use this by default.
|
||||
|
||||
Refer to the Commands section for a list of available commands and their usage.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the GPL License. See the [LICENSE](https://github.com/Force-Developing/force-sling/blob/main/LICENSE) file for more details.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We appreciate contributions! To contribute:
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a feature branch: `git checkout -b feature-name`
|
||||
3. Commit your changes: `git commit -m 'Add feature'`
|
||||
4. Push to the branch: `git push origin feature-name`
|
||||
5. Create a pull request.
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For questions, issues, or feature requests, please open an [issue](https://github.com/Force-Developing/force-sling/issues) or reach out on our [Discord](https://discord.gg/927gfpcyDe).
|
||||
|
||||
---
|
||||
|
||||
Thank you for using the resource. We hope this documentation helps you get the most out of the resource.
|
|
@ -1,123 +0,0 @@
|
|||
local positions = {}
|
||||
local presets = {}
|
||||
|
||||
-- Load positions from database
|
||||
local function LoadPositionsFromDB()
|
||||
local result = MySQL.query.await('SELECT * FROM weapon_positions')
|
||||
if result then
|
||||
for _, data in ipairs(result) do
|
||||
local weaponData = {
|
||||
coords = vector3(data.position_x, data.position_y, data.position_z),
|
||||
rot = vector3(data.rotation_x, data.rotation_y, data.rotation_z),
|
||||
boneId = data.bone_id
|
||||
}
|
||||
|
||||
if data.is_preset == 1 then
|
||||
presets[data.weapon_name] = weaponData
|
||||
else
|
||||
if not positions[data.identifier] then
|
||||
positions[data.identifier] = {}
|
||||
end
|
||||
positions[data.identifier][data.weapon_name] = weaponData
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Save position to database
|
||||
local function SavePositionToDB(identifier, weaponName, position, rotation, boneId, isPreset)
|
||||
MySQL.query.await('INSERT INTO weapon_positions (identifier, weapon_name, position_x, position_y, position_z, rotation_x, rotation_y, rotation_z, bone_id, is_preset) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE position_x = VALUES(position_x), position_y = VALUES(position_y), position_z = VALUES(position_z), rotation_x = VALUES(rotation_x), rotation_y = VALUES(rotation_y), rotation_z = VALUES(rotation_z), bone_id = VALUES(bone_id)', {
|
||||
identifier,
|
||||
weaponName,
|
||||
position.x,
|
||||
position.y,
|
||||
position.z,
|
||||
rotation.x,
|
||||
rotation.y,
|
||||
rotation.z,
|
||||
boneId,
|
||||
isPreset and 1 or 0
|
||||
})
|
||||
end
|
||||
|
||||
-- Delete position from database
|
||||
local function DeletePositionFromDB(identifier, weaponName)
|
||||
MySQL.query.await('DELETE FROM weapon_positions WHERE identifier = ? AND weapon_name = ?', {
|
||||
identifier,
|
||||
weaponName
|
||||
})
|
||||
end
|
||||
|
||||
-- Load data when resource starts
|
||||
CreateThread(function()
|
||||
LoadPositionsFromDB()
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:getCachedPositions', function(source)
|
||||
local src = source
|
||||
local identifier = GetPlayerIdentifier(src)
|
||||
return positions[identifier] or {}
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:getCachedPresets', function(source)
|
||||
return presets
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:isPlayerAdmin', function(source)
|
||||
local src = source
|
||||
-- Add your admin check logic here
|
||||
return {isAdmin = true}
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:resetWeaponPositions', function(source, weapon)
|
||||
local src = source
|
||||
local identifier = GetPlayerIdentifier(src)
|
||||
|
||||
if weapon then
|
||||
if positions[identifier] then
|
||||
positions[identifier][weapon] = nil
|
||||
DeletePositionFromDB(identifier, weapon)
|
||||
end
|
||||
else
|
||||
positions[identifier] = {}
|
||||
MySQL.query.await('DELETE FROM weapon_positions WHERE identifier = ? AND is_preset = 0', {identifier})
|
||||
end
|
||||
|
||||
return positions[identifier] or {}
|
||||
end)
|
||||
|
||||
RegisterNetEvent('force-sling:server:saveWeaponPosition')
|
||||
AddEventHandler('force-sling:server:saveWeaponPosition', function(position, rotation, weapon, weaponName, boneId, isPreset)
|
||||
local src = source
|
||||
local identifier = GetPlayerIdentifier(src)
|
||||
|
||||
local data = {
|
||||
coords = position,
|
||||
rot = rotation,
|
||||
boneId = boneId
|
||||
}
|
||||
|
||||
if isPreset then
|
||||
presets[weaponName] = data
|
||||
SavePositionToDB('preset', weaponName, position, rotation, boneId, true)
|
||||
else
|
||||
if not positions[identifier] then
|
||||
positions[identifier] = {}
|
||||
end
|
||||
positions[identifier][weaponName] = data
|
||||
SavePositionToDB(identifier, weaponName, position, rotation, boneId, false)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Helper function to get player identifier
|
||||
function GetPlayerIdentifier(source)
|
||||
local identifiers = GetPlayerIdentifiers(source)
|
||||
-- Modify this based on your framework (e.g., for QBCore or ESX)
|
||||
for _, identifier in pairs(identifiers) do
|
||||
if string.find(identifier, 'license:') then
|
||||
return identifier
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
local function SafeSavePosition(filePath, data)
|
||||
local success, error = pcall(function()
|
||||
local fileData = json.decode(LoadResourceFile(GetCurrentResourceName(), filePath)) or {}
|
||||
fileData = type(fileData) == 'table' and fileData or {}
|
||||
|
||||
for k, v in pairs(data) do
|
||||
fileData[k] = v
|
||||
end
|
||||
|
||||
return SaveResourceFile(GetCurrentResourceName(), filePath, json.encode(fileData, { indent = true }), -1)
|
||||
end)
|
||||
|
||||
if not success then
|
||||
Debug("error", "Failed to save position data: " .. tostring(error))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- @param coords table The coordinates of the weapon.
|
||||
--- @param rot table The rotation of the weapon.
|
||||
--- @param weapon string The weapon model.
|
||||
--- @param weaponName string The weapon name.
|
||||
--- @param boneId number The bone ID to attach the weapon to.
|
||||
--- @param isPreset boolean Whether the position is a preset.
|
||||
--- @return nil
|
||||
RegisterNetEvent("force-sling:server:saveWeaponPosition", function(coords, rot, weapon, weaponName, boneId, isPreset)
|
||||
Debug("info", "Saving weapon position for weapon: " .. weaponName .. " isPreset: " .. tostring(isPreset))
|
||||
|
||||
if not isPreset then
|
||||
local positions = {
|
||||
[GetPlayerIdentifierByType(source, "license")] = {
|
||||
[weaponName] = {
|
||||
coords = coords,
|
||||
rot = rot,
|
||||
boneId = boneId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if SafeSavePosition("json/positions.json", positions) then
|
||||
Debug("info",
|
||||
"Weapon position saved for player: " .. GetPlayerIdentifierByType(source, "license") .. " weapon: " .. weaponName)
|
||||
end
|
||||
else
|
||||
local presets = {
|
||||
[weaponName] = {
|
||||
coords = coords,
|
||||
rot = rot,
|
||||
boneId = boneId
|
||||
}
|
||||
}
|
||||
|
||||
if SafeSavePosition("json/presets.json", presets) then
|
||||
Debug("info", "Weapon preset saved for weapon: " .. weaponName)
|
||||
end
|
||||
end
|
||||
end)
|
|
@ -1,47 +0,0 @@
|
|||
Sling = {}
|
||||
|
||||
function Sling:InitMain()
|
||||
Debug("info", "Initializing main")
|
||||
|
||||
Sling:LoadServerCallbacks()
|
||||
|
||||
Debug("info", "Main initialized")
|
||||
end
|
||||
|
||||
function Sling:LoadServerCallbacks()
|
||||
Debug("info", "Loading server callbacks")
|
||||
|
||||
local resourceName = GetCurrentResourceName()
|
||||
local callbacks = {
|
||||
["force-sling:callback:isPlayerAdmin"] = function(source, target)
|
||||
if not target then target = source end
|
||||
return Admin:IsPlayerAdmin(target)
|
||||
end,
|
||||
["force-sling:callback:getCachedPositions"] = function(source)
|
||||
local identifier = GetPlayerIdentifierByType(source, "license")
|
||||
local positions = json.decode(LoadResourceFile(resourceName, "json/positions.json")) or {}
|
||||
Debug("info", "Returning cached positions for identifier: " .. tostring(identifier))
|
||||
return positions[identifier] or {}
|
||||
end,
|
||||
["force-sling:callback:getCachedPresets"] = function()
|
||||
Debug("info", "Returning cached presets")
|
||||
return json.decode(LoadResourceFile(resourceName, "json/presets.json")) or {}
|
||||
end,
|
||||
["force-sling:callback:resetWeaponPositions"] = function(source, weapon)
|
||||
Debug("info",
|
||||
"Resetting weapon positions for source = " .. tostring(source) .. " and weapon = " .. tostring(weapon))
|
||||
local identifier = GetPlayerIdentifierByType(source, "license")
|
||||
local positions = json.decode(LoadResourceFile(resourceName, "json/positions.json")) or {}
|
||||
positions[identifier] = positions[identifier] or {}
|
||||
positions[identifier][weapon] = nil
|
||||
SaveResourceFile(resourceName, "json/positions.json", json.encode(positions), -1)
|
||||
return positions[identifier]
|
||||
end
|
||||
}
|
||||
|
||||
for name, func in pairs(callbacks) do
|
||||
lib.callback.register(name, func)
|
||||
end
|
||||
|
||||
Debug("info", "Server callbacks loaded")
|
||||
end
|
|
@ -1,183 +0,0 @@
|
|||
local positions = {}
|
||||
local presets = {}
|
||||
local positionsFile = 'positions.json'
|
||||
local presetsFile = 'presets.json'
|
||||
local attachedWeapons = {}
|
||||
|
||||
-- Load saved positions from JSON file
|
||||
local function LoadPositions()
|
||||
local file = LoadResourceFile(GetCurrentResourceName(), 'json/' .. positionsFile)
|
||||
if file then
|
||||
positions = json.decode(file) or {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Load saved presets from JSON file
|
||||
local function LoadPresets()
|
||||
local file = LoadResourceFile(GetCurrentResourceName(), 'json/' .. presetsFile)
|
||||
if file then
|
||||
presets = json.decode(file) or {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Save positions to JSON file
|
||||
local function SavePositions()
|
||||
SaveResourceFile(GetCurrentResourceName(), 'json/' .. positionsFile, json.encode(positions), -1)
|
||||
end
|
||||
|
||||
-- Save presets to JSON file
|
||||
local function SavePresets()
|
||||
SaveResourceFile(GetCurrentResourceName(), 'json/' .. presetsFile, json.encode(presets), -1)
|
||||
end
|
||||
|
||||
-- Überprüfe ob ein Spieler eine Waffe besitzt
|
||||
local function DoesPlayerHaveWeapon(source, weaponName)
|
||||
if Config.Framework.name == "qbcore" then
|
||||
local Player = QBCore.Functions.GetPlayer(source)
|
||||
if Player then
|
||||
for _, item in pairs(Player.PlayerData.items) do
|
||||
if item.name == weaponName then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif Config.Framework.name == "esx" then
|
||||
local xPlayer = ESX.GetPlayerFromId(source)
|
||||
if xPlayer then
|
||||
local hasWeapon = xPlayer.hasWeapon(weaponName)
|
||||
return hasWeapon
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Load data when resource starts
|
||||
CreateThread(function()
|
||||
LoadPositions()
|
||||
LoadPresets()
|
||||
end)
|
||||
|
||||
-- Weapon sync with verification
|
||||
RegisterNetEvent('force-sling:server:syncWeapons')
|
||||
AddEventHandler('force-sling:server:syncWeapons', function(weaponData, action)
|
||||
local src = source
|
||||
|
||||
if action == 'attach' then
|
||||
-- Überprüfe ob der Spieler die Waffe besitzt
|
||||
if not DoesPlayerHaveWeapon(src, weaponData.weaponName) then
|
||||
return
|
||||
end
|
||||
|
||||
attachedWeapons[src] = attachedWeapons[src] or {}
|
||||
attachedWeapons[src][weaponData.weaponName] = weaponData
|
||||
elseif action == 'detach' then
|
||||
if attachedWeapons[src] then
|
||||
attachedWeapons[src][weaponData.weaponName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
TriggerClientEvent('force-sling:client:syncWeapons', -1, src, weaponData, action)
|
||||
end)
|
||||
|
||||
-- Callbacks
|
||||
lib.callback.register('force-sling:callback:getCachedPositions', function(source)
|
||||
local src = source
|
||||
local playerPositions = {}
|
||||
|
||||
-- Filtere nur die Positionen für Waffen, die der Spieler besitzt
|
||||
for weaponName, posData in pairs(positions) do
|
||||
if DoesPlayerHaveWeapon(src, weaponName) then
|
||||
playerPositions[weaponName] = posData
|
||||
end
|
||||
end
|
||||
|
||||
return playerPositions
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:getCachedPresets', function(source)
|
||||
return presets
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:isPlayerAdmin', function(source)
|
||||
local src = source
|
||||
if Config.Framework.name == "qbcore" then
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
return Player and Player.PlayerData.admin or false
|
||||
elseif Config.Framework.name == "esx" then
|
||||
local xPlayer = ESX.GetPlayerFromId(src)
|
||||
return xPlayer and xPlayer.getGroup() == 'admin' or false
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
lib.callback.register('force-sling:callback:resetWeaponPositions', function(source, weapon)
|
||||
local src = source
|
||||
if weapon then
|
||||
if DoesPlayerHaveWeapon(src, weapon) then
|
||||
positions[weapon] = nil
|
||||
SavePositions()
|
||||
end
|
||||
else
|
||||
-- Beim kompletten Reset nur die Positionen der Waffen löschen, die der Spieler besitzt
|
||||
local newPositions = {}
|
||||
for weaponName, posData in pairs(positions) do
|
||||
if not DoesPlayerHaveWeapon(src, weaponName) then
|
||||
newPositions[weaponName] = posData
|
||||
end
|
||||
end
|
||||
positions = newPositions
|
||||
SavePositions()
|
||||
end
|
||||
return positions
|
||||
end)
|
||||
|
||||
RegisterNetEvent('force-sling:server:saveWeaponPosition')
|
||||
AddEventHandler('force-sling:server:saveWeaponPosition', function(position, rotation, weapon, weaponName, boneId, isPreset)
|
||||
local src = source
|
||||
local data = {
|
||||
coords = position,
|
||||
rot = rotation,
|
||||
boneId = boneId
|
||||
}
|
||||
|
||||
if isPreset then
|
||||
-- Nur Admins können Presets speichern
|
||||
local isAdmin = lib.callback.await('force-sling:callback:isPlayerAdmin', src)
|
||||
if isAdmin then
|
||||
presets[weaponName] = data
|
||||
SavePresets()
|
||||
end
|
||||
else
|
||||
-- Überprüfe ob der Spieler die Waffe besitzt
|
||||
if DoesPlayerHaveWeapon(src, weaponName) then
|
||||
positions[weaponName] = data
|
||||
SavePositions()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Cleanup on player drop
|
||||
AddEventHandler('playerDropped', function()
|
||||
local src = source
|
||||
if attachedWeapons[src] then
|
||||
TriggerClientEvent('force-sling:client:cleanupPlayerWeapons', -1, src)
|
||||
attachedWeapons[src] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
-- Periodische Überprüfung der angehängten Waffen
|
||||
CreateThread(function()
|
||||
while true do
|
||||
for playerId, weapons in pairs(attachedWeapons) do
|
||||
for weaponName, _ in pairs(weapons) do
|
||||
if not DoesPlayerHaveWeapon(playerId, weaponName) then
|
||||
-- Spieler hat die Waffe nicht mehr, entferne sie
|
||||
TriggerClientEvent('force-sling:client:syncWeapons', -1, playerId, {weaponName = weaponName}, 'detach')
|
||||
attachedWeapons[playerId][weaponName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
Wait(5000) -- Alle 5 Sekunden überprüfen
|
||||
end
|
||||
end)
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
Admin = {}
|
||||
|
||||
--- Checks if a player is an admin.
|
||||
---
|
||||
--- This function determines if a player is an admin by checking their Discord roles
|
||||
--- and global configuration settings.
|
||||
---
|
||||
--- @param target number The target player.
|
||||
--- @return boolean | string The player's admin status and type.
|
||||
--- - `isAdmin` (boolean): Whether the player is an admin.
|
||||
--- - `adminType` (string): The type of admin ("none" or "global").
|
||||
function Admin:IsPlayerAdmin(target)
|
||||
Debug("info", "Checking if player is admin for target: " .. tostring(target))
|
||||
--- @type boolean | string
|
||||
local admin = false;
|
||||
|
||||
-- Check if the player is an admin via global configuration
|
||||
if Config.Admin.Global.enabled and not admin then
|
||||
if Config.Admin.Global.ace then
|
||||
local isAdceAllowed = IsPlayerAceAllowed(target, Config.Admin.Global.ace)
|
||||
if isAdceAllowed then
|
||||
admin = "global"
|
||||
end
|
||||
end
|
||||
|
||||
local identifiers = GetPlayerIdentifiers(target)
|
||||
for _, identifier in ipairs(identifiers) do
|
||||
if lib.table.contains(Config.Admin.Global.players, identifier) then
|
||||
admin = "global"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Debug("info",
|
||||
"Admin check completed for target: " ..
|
||||
tostring(target) .. " admin: " .. tostring(admin))
|
||||
return admin
|
||||
end
|
||||
|
||||
--- Gets a player's identifier.
|
||||
---
|
||||
--- This function retrieves a specific type of identifier for a given player.
|
||||
--- It logs the process of finding the identifier and returns the identifier if found.
|
||||
---
|
||||
--- @param target number The target player.
|
||||
--- @param identifierType string The type of identifier to get.
|
||||
--- @return string | nil The player's identifier.
|
||||
function Admin:getPlayerIdentifier(target, identifierType)
|
||||
Debug("info",
|
||||
"Getting player identifier for target: " .. tostring(target) .. " identifierType: " .. identifierType)
|
||||
local identifiers = GetPlayerIdentifiers(target)
|
||||
for _, identifier in ipairs(identifiers) do
|
||||
if string.find(identifier, identifierType) then
|
||||
Debug("info", "Player identifier for target: " .. tostring(target) .. " is: " .. tostring(identifier))
|
||||
return identifier
|
||||
end
|
||||
end
|
||||
Debug("info", "Player identifier for target: " .. tostring(target) .. " not found")
|
||||
return nil
|
||||
end
|
|
@ -1,56 +0,0 @@
|
|||
function IsResourceStartingOrStarted(resource)
|
||||
return GetResourceState(resource) == "starting" or GetResourceState(resource) == "started"
|
||||
end
|
||||
|
||||
function Debug(level, message, ...)
|
||||
if not Config.Debug then return end
|
||||
|
||||
local levels = {
|
||||
error = function(msg) lib.print.error(msg) end,
|
||||
warn = function(msg) lib.print.warn(msg) end,
|
||||
info = function(msg) lib.print.info(msg) end,
|
||||
debug = function(msg) lib.print.debug(msg) end
|
||||
}
|
||||
|
||||
local fn = levels[level] or levels.info
|
||||
fn(string.format(message, ...))
|
||||
end
|
||||
|
||||
function InitFramework()
|
||||
if not Config.Framework.name == "auto" then return end
|
||||
local frameworks = {
|
||||
{ name = "esx", resource = "es_extended" },
|
||||
{ name = "qbx", resource = "qbx_core" },
|
||||
{ name = "qbcore", resource = "qb-core" }
|
||||
}
|
||||
|
||||
Debug("info", "Initializing framework")
|
||||
for _, framework in ipairs(frameworks) do
|
||||
if IsResourceStartingOrStarted(framework.resource) then
|
||||
Config.Framework = framework
|
||||
return
|
||||
end
|
||||
end
|
||||
Debug("info", "Framework initialized: " .. Config.Framework.name)
|
||||
end
|
||||
|
||||
function InitInventory()
|
||||
if not Config.Inventory == "auto" then return end
|
||||
local inventories = {
|
||||
{ name = "qs-inventory", resource = "qs-inventory" },
|
||||
{ name = "core_inventory", resource = "core_inventory" },
|
||||
{ name = "qb-inventory", resource = "qb-inventory" },
|
||||
{ name = "ox_inventory", resource = "ox_inventory" }
|
||||
}
|
||||
|
||||
Debug("info", "Initializing inventory")
|
||||
for _, inventory in ipairs(inventories) do
|
||||
if IsResourceStartingOrStarted(inventory.resource) then
|
||||
Config.Inventory = inventory.name
|
||||
Debug("info", "Inventory initialized: " .. Config.Inventory)
|
||||
return
|
||||
end
|
||||
end
|
||||
Config.Inventory = "none"
|
||||
Debug("info", "Inventory initialized: " .. Config.Inventory)
|
||||
end
|
|
@ -1 +0,0 @@
|
|||
otherPlayersWeapons = {}
|
|
@ -1,93 +0,0 @@
|
|||
lib.versionCheck('Force-Developing/force-sling')
|
||||
|
||||
local latestVersionUrl =
|
||||
"https://gist.githubusercontent.com/Force-Developing/ee739a3263bc3421257d901e53e27b10/raw/force-sling"
|
||||
local currentVersion = GetResourceMetadata(GetCurrentResourceName(), 'version', 0)
|
||||
|
||||
local function parseVersion(version)
|
||||
local major, minor, patch = version:match("(%d+)%.(%d+)%.(%d+)")
|
||||
if not major then return nil end
|
||||
return {
|
||||
major = tonumber(major),
|
||||
minor = tonumber(minor),
|
||||
patch = tonumber(patch)
|
||||
}
|
||||
end
|
||||
|
||||
local function isNewerVersion(current, latest)
|
||||
local currentParsed = parseVersion(current)
|
||||
local latestParsed = parseVersion(latest)
|
||||
|
||||
if not currentParsed or not latestParsed then
|
||||
return false
|
||||
end
|
||||
|
||||
if latestParsed.major > currentParsed.major then return true end
|
||||
if latestParsed.major < currentParsed.major then return false end
|
||||
if latestParsed.minor > currentParsed.minor then return true end
|
||||
if latestParsed.minor < currentParsed.minor then return false end
|
||||
return latestParsed.patch > currentParsed.patch
|
||||
end
|
||||
|
||||
local function formatChangelogs(changelogs)
|
||||
if not changelogs then return "No changelog available" end
|
||||
return changelogs:gsub("%-", "\n-"):gsub("^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local function versionCheck()
|
||||
PerformHttpRequest(latestVersionUrl, function(err, response, headers)
|
||||
if err ~= 200 then
|
||||
lib.print.error(string.format("Version check failed with error code: %s", err))
|
||||
return
|
||||
end
|
||||
|
||||
local success, result = pcall(function()
|
||||
local version, changelogs = response:match("<(.-)>(.-)<")
|
||||
if not version then
|
||||
version = response:match("<(.-)>")
|
||||
changelogs = response:match(">(.-)<")
|
||||
end
|
||||
|
||||
if not version then
|
||||
error("Invalid version format in response")
|
||||
end
|
||||
|
||||
version = version:gsub("[<>]", "")
|
||||
local isNewer = isNewerVersion(currentVersion, version)
|
||||
|
||||
local output = string.format([[
|
||||
-------------
|
||||
Current Version: %s
|
||||
Latest Version: %s
|
||||
-------------
|
||||
%s
|
||||
-------------]],
|
||||
currentVersion,
|
||||
version,
|
||||
isNewer and string.format(
|
||||
"Update available!\nChangelogs:\n%s",
|
||||
formatChangelogs(changelogs)
|
||||
) or "You are running the latest version."
|
||||
)
|
||||
|
||||
if isNewer then
|
||||
lib.print.warn(output)
|
||||
else
|
||||
lib.print.info(output)
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then
|
||||
lib.print.error(string.format("Failed to process version check: %s", result))
|
||||
end
|
||||
end, 'GET', '', {
|
||||
['Cache-Control'] = 'no-cache',
|
||||
['Content-Type'] = 'application/json',
|
||||
['User-Agent'] = string.format('force-appearance/%s', currentVersion)
|
||||
})
|
||||
end
|
||||
|
||||
CreateThread(function()
|
||||
Wait(5000)
|
||||
versionCheck()
|
||||
end)
|
Loading…
Add table
Add a link
Reference in a new issue