├── LICENSE
├── README.md
├── client
    ├── client.lua
    ├── placeables.lua
    └── pushables.lua
├── fxmanifest.lua
├── images
    ├── beachball.png
    ├── beachtowel.png
    ├── beachumbrella.png
    ├── bodybag.png
    ├── campchair_blue.png
    ├── campchair_green.png
    ├── campchair_plaid.png
    ├── campfire.png
    ├── candycane.png
    ├── canopy.png
    ├── cargobox1.png
    ├── cargobox2.png
    ├── cargobox3.png
    ├── cargobox4.png
    ├── cargobox5.png
    ├── cargobox6.png
    ├── cargobox7.png
    ├── cargobox8.png
    ├── carjack.png
    ├── constructionbarrier.png
    ├── constructionbarrier2.png
    ├── constructionbarrier3.png
    ├── constructiongenerator.png
    ├── cot.png
    ├── crate1.png
    ├── crate2.png
    ├── crate3.png
    ├── crate4.png
    ├── crate5.png
    ├── drinkcart.png
    ├── examlight.png
    ├── folding_chair.png
    ├── greenscreen.png
    ├── handtruck.png
    ├── hazardbin.png
    ├── hobomattress.png
    ├── hoboshelter.png
    ├── hobostove.png
    ├── hospitalbed1.png
    ├── hospitalbed2.png
    ├── hospitalbedtable.png
    ├── janitorcart1.png
    ├── janitorcart2.png
    ├── largetent.png
    ├── lawnmower.png
    ├── medbag.png
    ├── medmachine.png
    ├── medtable.png
    ├── meshfence.png
    ├── metalcart.png
    ├── metalramp.png
    ├── microscope.png
    ├── mopbucket.png
    ├── oldtent.png
    ├── oscillator.png
    ├── pallet1.png
    ├── pallet2.png
    ├── pallet3.png
    ├── pallet4.png
    ├── pallet5.png
    ├── plastic_chair.png
    ├── plastictable.png
    ├── roadclosedbarrier.png
    ├── roadcone.png
    ├── roadconebig.png
    ├── roadpole.png
    ├── roadworkahead.png
    ├── roomtrolly.png
    ├── ropebarrier.png
    ├── sexdoll.png
    ├── shoppingcart.png
    ├── skateramp.png
    ├── sleepingbag.png
    ├── snowman1.png
    ├── snowman2.png
    ├── snowman3.png
    ├── snowman4.png
    ├── soccerball.png
    ├── stepladder.png
    ├── stretcher.png
    ├── teacart.png
    ├── tent.png
    ├── toolchest.png
    ├── trafficdevice.png
    ├── trashbin.png
    ├── trifinishbanner.png
    ├── tristartbanner.png
    ├── tristarttable.png
    ├── warehousetrolly1.png
    ├── warehousetrolly2.png
    ├── waterbarrel.png
    ├── wheelbarrow.png
    ├── woodramp.png
    ├── woodtable.png
    ├── worklight.png
    ├── worklight2.png
    ├── worklight3.png
    ├── xmaspresent.png
    ├── xmastree1.png
    └── xmastree2.png
├── server
    └── server.lua
└── shared
    ├── config.lua
    └── framework.lua
/LICENSE:
--------------------------------------------------------------------------------
  1 |                     GNU GENERAL PUBLIC LICENSE
  2 |                        Version 3, 29 June 2007
  3 | 
  4 |  Copyright (C) 2007 Free Software Foundation, Inc. 
  5 |  Everyone is permitted to copy and distribute verbatim copies
  6 |  of this license document, but changing it is not allowed.
  7 | 
  8 |                             Preamble
  9 | 
 10 |   The GNU General Public License is a free, copyleft license for
 11 | software and other kinds of works.
 12 | 
 13 |   The licenses for most software and other practical works are designed
 14 | to take away your freedom to share and change the works.  By contrast,
 15 | the GNU General Public License is intended to guarantee your freedom to
 16 | share and change all versions of a program--to make sure it remains free
 17 | software for all its users.  We, the Free Software Foundation, use the
 18 | GNU General Public License for most of our software; it applies also to
 19 | any other work released this way by its authors.  You can apply it to
 20 | your programs, too.
 21 | 
 22 |   When we speak of free software, we are referring to freedom, not
 23 | price.  Our General Public Licenses are designed to make sure that you
 24 | have the freedom to distribute copies of free software (and charge for
 25 | them if you wish), that you receive source code or can get it if you
 26 | want it, that you can change the software or use pieces of it in new
 27 | free programs, and that you know you can do these things.
 28 | 
 29 |   To protect your rights, we need to prevent others from denying you
 30 | these rights or asking you to surrender the rights.  Therefore, you have
 31 | certain responsibilities if you distribute copies of the software, or if
 32 | you modify it: responsibilities to respect the freedom of others.
 33 | 
 34 |   For example, if you distribute copies of such a program, whether
 35 | gratis or for a fee, you must pass on to the recipients the same
 36 | freedoms that you received.  You must make sure that they, too, receive
 37 | or can get the source code.  And you must show them these terms so they
 38 | know their rights.
 39 | 
 40 |   Developers that use the GNU GPL protect your rights with two steps:
 41 | (1) assert copyright on the software, and (2) offer you this License
 42 | giving you legal permission to copy, distribute and/or modify it.
 43 | 
 44 |   For the developers' and authors' protection, the GPL clearly explains
 45 | that there is no warranty for this free software.  For both users' and
 46 | authors' sake, the GPL requires that modified versions be marked as
 47 | changed, so that their problems will not be attributed erroneously to
 48 | authors of previous versions.
 49 | 
 50 |   Some devices are designed to deny users access to install or run
 51 | modified versions of the software inside them, although the manufacturer
 52 | can do so.  This is fundamentally incompatible with the aim of
 53 | protecting users' freedom to change the software.  The systematic
 54 | pattern of such abuse occurs in the area of products for individuals to
 55 | use, which is precisely where it is most unacceptable.  Therefore, we
 56 | have designed this version of the GPL to prohibit the practice for those
 57 | products.  If such problems arise substantially in other domains, we
 58 | stand ready to extend this provision to those domains in future versions
 59 | of the GPL, as needed to protect the freedom of users.
 60 | 
 61 |   Finally, every program is threatened constantly by software patents.
 62 | States should not allow patents to restrict development and use of
 63 | software on general-purpose computers, but in those that do, we wish to
 64 | avoid the special danger that patents applied to a free program could
 65 | make it effectively proprietary.  To prevent this, the GPL assures that
 66 | patents cannot be used to render the program non-free.
 67 | 
 68 |   The precise terms and conditions for copying, distribution and
 69 | modification follow.
 70 | 
 71 |                        TERMS AND CONDITIONS
 72 | 
 73 |   0. Definitions.
 74 | 
 75 |   "This License" refers to version 3 of the GNU General Public License.
 76 | 
 77 |   "Copyright" also means copyright-like laws that apply to other kinds of
 78 | works, such as semiconductor masks.
 79 | 
 80 |   "The Program" refers to any copyrightable work licensed under this
 81 | License.  Each licensee is addressed as "you".  "Licensees" and
 82 | "recipients" may be individuals or organizations.
 83 | 
 84 |   To "modify" a work means to copy from or adapt all or part of the work
 85 | in a fashion requiring copyright permission, other than the making of an
 86 | exact copy.  The resulting work is called a "modified version" of the
 87 | earlier work or a work "based on" the earlier work.
 88 | 
 89 |   A "covered work" means either the unmodified Program or a work based
 90 | on the Program.
 91 | 
 92 |   To "propagate" a work means to do anything with it that, without
 93 | permission, would make you directly or secondarily liable for
 94 | infringement under applicable copyright law, except executing it on a
 95 | computer or modifying a private copy.  Propagation includes copying,
 96 | distribution (with or without modification), making available to the
 97 | public, and in some countries other activities as well.
 98 | 
 99 |   To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies.  Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 | 
103 |   An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License.  If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 | 
112 |   1. Source Code.
113 | 
114 |   The "source code" for a work means the preferred form of the work
115 | for making modifications to it.  "Object code" means any non-source
116 | form of a work.
117 | 
118 |   A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 | 
123 |   The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form.  A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 | 
134 |   The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities.  However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work.  For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 | 
147 |   The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 | 
151 |   The Corresponding Source for a work in source code form is that
152 | same work.
153 | 
154 |   2. Basic Permissions.
155 | 
156 |   All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met.  This License explicitly affirms your unlimited
159 | permission to run the unmodified Program.  The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work.  This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 | 
164 |   You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force.  You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright.  Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 | 
175 |   Conveying under any other circumstances is permitted solely under
176 | the conditions stated below.  Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 | 
179 |   3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 | 
181 |   No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 | 
187 |   When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 | 
195 |   4. Conveying Verbatim Copies.
196 | 
197 |   You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 | 
205 |   You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 | 
208 |   5. Conveying Modified Source Versions.
209 | 
210 |   You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 | 
214 |     a) The work must carry prominent notices stating that you modified
215 |     it, and giving a relevant date.
216 | 
217 |     b) The work must carry prominent notices stating that it is
218 |     released under this License and any conditions added under section
219 |     7.  This requirement modifies the requirement in section 4 to
220 |     "keep intact all notices".
221 | 
222 |     c) You must license the entire work, as a whole, under this
223 |     License to anyone who comes into possession of a copy.  This
224 |     License will therefore apply, along with any applicable section 7
225 |     additional terms, to the whole of the work, and all its parts,
226 |     regardless of how they are packaged.  This License gives no
227 |     permission to license the work in any other way, but it does not
228 |     invalidate such permission if you have separately received it.
229 | 
230 |     d) If the work has interactive user interfaces, each must display
231 |     Appropriate Legal Notices; however, if the Program has interactive
232 |     interfaces that do not display Appropriate Legal Notices, your
233 |     work need not make them do so.
234 | 
235 |   A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit.  Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 | 
245 |   6. Conveying Non-Source Forms.
246 | 
247 |   You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 | 
252 |     a) Convey the object code in, or embodied in, a physical product
253 |     (including a physical distribution medium), accompanied by the
254 |     Corresponding Source fixed on a durable physical medium
255 |     customarily used for software interchange.
256 | 
257 |     b) Convey the object code in, or embodied in, a physical product
258 |     (including a physical distribution medium), accompanied by a
259 |     written offer, valid for at least three years and valid for as
260 |     long as you offer spare parts or customer support for that product
261 |     model, to give anyone who possesses the object code either (1) a
262 |     copy of the Corresponding Source for all the software in the
263 |     product that is covered by this License, on a durable physical
264 |     medium customarily used for software interchange, for a price no
265 |     more than your reasonable cost of physically performing this
266 |     conveying of source, or (2) access to copy the
267 |     Corresponding Source from a network server at no charge.
268 | 
269 |     c) Convey individual copies of the object code with a copy of the
270 |     written offer to provide the Corresponding Source.  This
271 |     alternative is allowed only occasionally and noncommercially, and
272 |     only if you received the object code with such an offer, in accord
273 |     with subsection 6b.
274 | 
275 |     d) Convey the object code by offering access from a designated
276 |     place (gratis or for a charge), and offer equivalent access to the
277 |     Corresponding Source in the same way through the same place at no
278 |     further charge.  You need not require recipients to copy the
279 |     Corresponding Source along with the object code.  If the place to
280 |     copy the object code is a network server, the Corresponding Source
281 |     may be on a different server (operated by you or a third party)
282 |     that supports equivalent copying facilities, provided you maintain
283 |     clear directions next to the object code saying where to find the
284 |     Corresponding Source.  Regardless of what server hosts the
285 |     Corresponding Source, you remain obligated to ensure that it is
286 |     available for as long as needed to satisfy these requirements.
287 | 
288 |     e) Convey the object code using peer-to-peer transmission, provided
289 |     you inform other peers where the object code and Corresponding
290 |     Source of the work are being offered to the general public at no
291 |     charge under subsection 6d.
292 | 
293 |   A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 | 
297 |   A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling.  In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage.  For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product.  A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 | 
310 |   "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source.  The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 | 
318 |   If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information.  But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 | 
329 |   The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed.  Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 | 
337 |   Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 | 
343 |   7. Additional Terms.
344 | 
345 |   "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law.  If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 | 
354 |   When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it.  (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.)  You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 | 
361 |   Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 | 
365 |     a) Disclaiming warranty or limiting liability differently from the
366 |     terms of sections 15 and 16 of this License; or
367 | 
368 |     b) Requiring preservation of specified reasonable legal notices or
369 |     author attributions in that material or in the Appropriate Legal
370 |     Notices displayed by works containing it; or
371 | 
372 |     c) Prohibiting misrepresentation of the origin of that material, or
373 |     requiring that modified versions of such material be marked in
374 |     reasonable ways as different from the original version; or
375 | 
376 |     d) Limiting the use for publicity purposes of names of licensors or
377 |     authors of the material; or
378 | 
379 |     e) Declining to grant rights under trademark law for use of some
380 |     trade names, trademarks, or service marks; or
381 | 
382 |     f) Requiring indemnification of licensors and authors of that
383 |     material by anyone who conveys the material (or modified versions of
384 |     it) with contractual assumptions of liability to the recipient, for
385 |     any liability that these contractual assumptions directly impose on
386 |     those licensors and authors.
387 | 
388 |   All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10.  If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term.  If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 | 
398 |   If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 | 
403 |   Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 | 
407 |   8. Termination.
408 | 
409 |   You may not propagate or modify a covered work except as expressly
410 | provided under this License.  Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 | 
415 |   However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 | 
422 |   Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 | 
429 |   Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License.  If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 | 
435 |   9. Acceptance Not Required for Having Copies.
436 | 
437 |   You are not required to accept this License in order to receive or
438 | run a copy of the Program.  Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance.  However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work.  These actions infringe copyright if you do
443 | not accept this License.  Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 | 
446 |   10. Automatic Licensing of Downstream Recipients.
447 | 
448 |   Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License.  You are not responsible
451 | for enforcing compliance by third parties with this License.
452 | 
453 |   An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations.  If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 | 
463 |   You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License.  For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 | 
471 |   11. Patents.
472 | 
473 |   A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based.  The
475 | work thus licensed is called the contributor's "contributor version".
476 | 
477 |   A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version.  For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 | 
487 |   Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 | 
492 |   In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement).  To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 | 
499 |   If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients.  "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 | 
513 |   If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 | 
521 |   A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License.  You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 | 
536 |   Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 | 
540 |   12. No Surrender of Others' Freedom.
541 | 
542 |   If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License.  If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all.  For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 | 
552 |   13. Use with the GNU Affero General Public License.
553 | 
554 |   Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work.  The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 | 
563 |   14. Revised Versions of this License.
564 | 
565 |   The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time.  Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 | 
570 |   Each version is given a distinguishing version number.  If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation.  If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 | 
579 |   If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 | 
584 |   Later license versions may give you additional or different
585 | permissions.  However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 | 
589 |   15. Disclaimer of Warranty.
590 | 
591 |   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 | 
600 |   16. Limitation of Liability.
601 | 
602 |   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 | 
612 |   17. Interpretation of Sections 15 and 16.
613 | 
614 |   If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 | 
621 |                      END OF TERMS AND CONDITIONS
622 | 
623 |             How to Apply These Terms to Your New Programs
624 | 
625 |   If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 | 
629 |   To do so, attach the following notices to the program.  It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 | 
634 |     
635 |     Copyright (C)   
636 | 
637 |     This program is free software: you can redistribute it and/or modify
638 |     it under the terms of the GNU General Public License as published by
639 |     the Free Software Foundation, either version 3 of the License, or
640 |     (at your option) any later version.
641 | 
642 |     This program is distributed in the hope that it will be useful,
643 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
644 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
645 |     GNU General Public License for more details.
646 | 
647 |     You should have received a copy of the GNU General Public License
648 |     along with this program.  If not, see .
649 | 
650 | Also add information on how to contact you by electronic and paper mail.
651 | 
652 |   If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 | 
655 |       Copyright (C)   
656 |     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 |     This is free software, and you are welcome to redistribute it
658 |     under certain conditions; type `show c' for details.
659 | 
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License.  Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 | 
664 |   You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 | 
669 |   The GNU General Public License does not permit incorporating your program
670 | into proprietary programs.  If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library.  If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License.  But first, please read
674 | .
--------------------------------------------------------------------------------
/client/client.lua:
--------------------------------------------------------------------------------
 1 | function RequestNetworkControlOfObject(netId, itemEntity)
 2 |     if NetworkDoesNetworkIdExist(netId) then
 3 |         NetworkRequestControlOfNetworkId(netId)
 4 |         while not NetworkHasControlOfNetworkId(netId) do
 5 |             Wait(100)
 6 |             NetworkRequestControlOfNetworkId(netId)
 7 |         end
 8 |     end
 9 | 
10 |     if DoesEntityExist(itemEntity) then
11 |         NetworkRequestControlOfEntity(itemEntity)
12 |         while not NetworkHasControlOfEntity(itemEntity) do
13 |             Wait(100)
14 |             NetworkRequestControlOfEntity(itemEntity)
15 |         end
16 |     end
17 | end
18 | 
19 | function LoadAnimDict(dict)
20 |     while not HasAnimDictLoaded(dict) do
21 |         RequestAnimDict(dict)
22 |         Wait(10)
23 |     end
24 | end
25 | 
--------------------------------------------------------------------------------
/client/placeables.lua:
--------------------------------------------------------------------------------
  1 | local animationDict = "pickup_object"
  2 | local animation = "pickup_low"
  3 | local isInPlaceItemMode = false
  4 | 
  5 | -- Keeps track of the models that have already been set with target options, ensuring we don't create duplicate options for the same model
  6 | -- In some frameworks like OX, if you create targetOptions on a model that already has it, it will append the options, whereas
  7 | -- in QB it will override and only the last set of options will be used. We just need to add one option to the target model and then
  8 | -- the pickup event will use the statebag to determine the correct item to give back to the player
  9 | local targetModels = {}
 10 | 
 11 | local function LoadPropDict(model)
 12 |     while not HasModelLoaded(GetHashKey(model)) do
 13 |         RequestModel(GetHashKey(model))
 14 |         Wait(10)
 15 |     end
 16 | end
 17 | 
 18 | -- Gets the direction the camera is looking for the raycast function
 19 | local function RotationToDirection(rotation)
 20 |     local adjustedRotation = {
 21 |         x = (math.pi / 180) * rotation.x,
 22 |         y = (math.pi / 180) * rotation.y,
 23 |         z = (math.pi / 180) * rotation.z,
 24 |     }
 25 |     local direction = {
 26 |         x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
 27 |         y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
 28 |         z = math.sin(adjustedRotation.x),
 29 |     }
 30 |     return direction
 31 | end
 32 | 
 33 | -- Uses a RayCast to get the entity, coords, and whether we "hit" something with the raycast
 34 | -- Object passed in, is the current object that we want the raycast to ignore
 35 | local function RayCastGamePlayCamera(distance, object, raycastDetectWorldOnly)
 36 |     local cameraRotation = GetGameplayCamRot()
 37 |     local cameraCoord = GetGameplayCamCoord()
 38 |     local direction = RotationToDirection(cameraRotation)
 39 |     local destination = {
 40 |         x = cameraCoord.x + direction.x * distance,
 41 |         y = cameraCoord.y + direction.y * distance,
 42 |         z = cameraCoord.z + direction.z * distance,
 43 |     }
 44 | 
 45 |     -- Trace flag 4294967295 means the raycast will intersect with everything (including vehicles)
 46 |     -- Trace flag 1 means the raycast will only intersect with the world (ignoring other entities like peds, cars, etc)
 47 |     local traceFlag = 4294967295
 48 |     if raycastDetectWorldOnly then
 49 |         traceFlag = 1
 50 |     end
 51 | 
 52 |     local a, hit, coords, d, entity = GetShapeTestResult(StartShapeTestRay(cameraCoord.x, cameraCoord.y, cameraCoord.z, destination.x, destination.y, destination.z, traceFlag, object, 0))
 53 |     return hit, coords, entity
 54 | end
 55 | 
 56 | -- Used to Draw the text on the screen
 57 | local function Draw2DText(content, font, colour, scale, x, y)
 58 |     SetTextFont(font)
 59 |     SetTextScale(scale, scale)
 60 |     SetTextColour(colour[1], colour[2], colour[3], 255)
 61 |     SetTextEntry("STRING")
 62 |     SetTextDropShadow(0, 0, 0, 0, 255)
 63 |     SetTextDropShadow()
 64 |     SetTextEdge(4, 0, 0, 0, 255)
 65 |     SetTextOutline()
 66 |     AddTextComponentString(content)
 67 |     DrawText(x, y)
 68 | end
 69 | 
 70 | -- This handles placing the actual item that is network synced
 71 | local function placeItem(item, coords, heading, shouldSnapToGround)
 72 |     local ped = PlayerPedId()
 73 |     local itemName = item.item
 74 |     local itemModel = item.model
 75 |     local shouldFreezeItem = item.isFrozen
 76 | 
 77 |     -- Cancel any active animation
 78 |     ClearPedTasks(ped)
 79 | 
 80 |     Progressbar("place_item", "Placing " .. item.label, 750, false, true, {
 81 |         disableMovement = false,
 82 |         disableCarMovement = false,
 83 |         disableMouse = false,
 84 |         disableCombat = true,
 85 |     }, {
 86 |         animDict = animationDict,
 87 |         anim = animation,
 88 |         flags = 0,
 89 |     }, nil, nil, function() -- Done
 90 |         -- Stop playing the animation
 91 |         StopAnimTask(ped, animationDict, animation, 1.0)
 92 | 
 93 |         -- Remove the item from the inventory
 94 |         TriggerServerEvent("wp-placeables:server:RemoveItem", itemName)
 95 | 
 96 |         LoadPropDict(itemModel)
 97 | 
 98 |         -- Spawn prop on ground at the provided coords and heading
 99 |         local obj = CreateObject(itemModel, GetEntityCoords(ped), true)
100 |         if obj ~= 0 then
101 |             SetEntityRotation(obj, 0.0, 0.0, heading, false, false)
102 |             SetEntityCoords(obj, coords)
103 | 
104 |             if shouldFreezeItem then
105 |                 FreezeEntityPosition(obj, true)
106 |             end
107 | 
108 |             -- Some items dont go to the ground properly with this, and it actually makes them hover
109 |             if shouldSnapToGround then
110 |                 PlaceObjectOnGroundProperly(obj)
111 |             end
112 | 
113 |             -- Use statebag property itemName to set the itemName on the entity.
114 |             -- This value is used to grant the correct item back to the player when they pick it up.
115 |             -- It also solves the issue of the same model being used for multiple items
116 |             Entity(obj).state:set("itemName", itemName, true)
117 | 
118 |             CreateLog(itemName, true)
119 |         end
120 | 
121 |         SetModelAsNoLongerNeeded(itemModel)
122 |     end, function() -- Cancel
123 |         StopAnimTask(ped, animationDict, animation, 1.0)
124 |         Notify("Canceled..", "error")
125 |     end)
126 | end
127 | 
128 | -- Starts a thread that puts the player into item placement mode
129 | -- This will spawn a local object that only the player can see and move around to position it
130 | -- Once the player places the object it will delete the local one and then create a new network synced object
131 | local function startItemPlacementMode(item)
132 |     -- This is to prevent entering place mode multiple times if its already active
133 |     if isInPlaceItemMode then
134 |         Notify("Already placing an item", "error", 5000)
135 |         return
136 |     end
137 | 
138 |     isInPlaceItemMode = true
139 |     local ped = PlayerPedId()
140 |     local itemModel = item.model
141 | 
142 |     -- Create a local object for only this client (not synced to network) and make it transparent
143 |     local obj = CreateObject(itemModel, GetEntityCoords(ped), false, false)
144 |     SetEntityAlpha(obj, 150, false)
145 |     SetEntityCollision(obj, false, false)
146 | 
147 |     local zOffset = 0
148 | 
149 |     -- This is used to determine if the raycast should only detect the world or if it should detect everything (including vehicles)
150 |     local raycastDetectWorldOnly = true
151 | 
152 |     CreateThread(function()
153 |         while isInPlaceItemMode do
154 |             -- Use raycast based on where the camera is pointed
155 |             local hit, coords, entity = RayCastGamePlayCamera(Config.ItemPlacementModeRadius, obj, raycastDetectWorldOnly)
156 | 
157 |             -- Move the object to the coords from the raycast
158 |             SetEntityCoords(obj, coords.x, coords.y, coords.z + zOffset)
159 | 
160 |             -- Display the controls
161 |             Draw2DText("[E] Place\n[Shift+E] Place on ground\n[Scroll Up/Down] Rotate\n[Shift+Scroll Up/Down] Raise/lower", 4, { 255, 255, 255, }, 0.4, 0.85, 0.85)
162 |             Draw2DText("[Scroll Click] Change mode\n[Right Click / Backspace] Exit place mode", 4, { 255, 255, 255, }, 0.4, 0.85, 0.945)
163 | 
164 |             -- Handle various key presses and actions
165 | 
166 |             -- Controls for placing item
167 | 
168 |             -- Pressed Shift + E - Place object on ground
169 |             if IsControlJustReleased(0, 38) and IsControlPressed(0, 21) then
170 |                 isInPlaceItemMode = false
171 | 
172 |                 local objHeading = GetEntityHeading(obj)
173 |                 local snapToGround = true
174 | 
175 |                 DeleteEntity(obj)
176 |                 placeItem(item, vector3(coords.x, coords.y, coords.z + zOffset), objHeading, snapToGround)
177 | 
178 |             -- Pressed E - Place object at current position
179 |             elseif IsControlJustReleased(0, 38) then
180 |                 isInPlaceItemMode = false
181 | 
182 |                 local objHeading = GetEntityHeading(obj)
183 |                 local snapToGround = false
184 | 
185 |                 DeleteEntity(obj)
186 |                 placeItem(item, vector3(coords.x, coords.y, coords.z + zOffset), objHeading, snapToGround)
187 |             end
188 | 
189 |             -- Controls for rotating item
190 | 
191 |             -- Mouse Wheel Up (and Shift not pressed), rotate by +10 degrees
192 |             if IsControlJustReleased(0, 241) and not IsControlPressed(0, 21) then
193 |                 local objHeading = GetEntityHeading(obj)
194 |                 SetEntityRotation(obj, 0.0, 0.0, objHeading + 10, false, false)
195 |             end
196 | 
197 |             -- Mouse Wheel Down (and shift not pressed), rotate by -10 degrees
198 |             if IsControlJustReleased(0, 242) and not IsControlPressed(0, 21) then
199 |                 local objHeading = GetEntityHeading(obj)
200 |                 SetEntityRotation(obj, 0.0, 0.0, objHeading - 10, false, false)
201 |             end
202 | 
203 |             -- Controls for raising/lowering item
204 | 
205 |             -- Shift + Mouse Wheel Up, move item up
206 |             if IsControlPressed(0, 21) and IsControlJustReleased(0, 241) then
207 |                 zOffset = zOffset + 0.1
208 |                 if zOffset > Config.maxZOffset then
209 |                     zOffset = Config.maxZOffset
210 |                 end
211 |             end
212 | 
213 |             -- Shift + Mouse Wheel Down, move item down
214 |             if IsControlPressed(0, 21) and IsControlJustReleased(0, 242) then
215 |                 zOffset = zOffset - 0.1
216 |                 if zOffset < Config.minZOffset then
217 |                     zOffset = Config.minZOffset
218 |                 end
219 |             end
220 | 
221 |             -- Mouse Wheel Click, change placement mode
222 |             if IsControlJustReleased(0, 348) then
223 |                 raycastDetectWorldOnly = not raycastDetectWorldOnly
224 |             end
225 | 
226 |             -- Right click or Backspace to exit out of placement mode and delete the local object
227 |             if IsControlJustReleased(0, 177) then
228 |                 isInPlaceItemMode = false
229 |                 DeleteEntity(obj)
230 |             end
231 | 
232 |             Wait(1)
233 |         end
234 |     end)
235 | end
236 | 
237 | -- Handles picking up the prop, deleting it from the world and adding it to the players inventory
238 | local function pickUpItem(itemData)
239 |     local ped = PlayerPedId()
240 |     local itemEntity = itemData.entity
241 |     local itemModel = itemData.itemModel
242 | 
243 |     -- When picking up the item, try to get the itemName from the statebag property first, else fallback to the itemName from the itemData provided by the target script
244 |     -- Using the statebag property ensures we get the correct item name if the prop model is shared by multiple items..
245 |     local itemName = Entity(itemEntity).state.itemName or itemData.itemName
246 | 
247 |     if itemName then
248 |         -- Cancel any active animation
249 |         ClearPedTasks(ped)
250 | 
251 |         Progressbar("pickup_item", "Picking up item", 200, false, true, {
252 |             disableMovement = false,
253 |             disableCarMovement = false,
254 |             disableMouse = false,
255 |             disableCombat = true,
256 |         }, {
257 |             animDict = animationDict,
258 |             anim = animation,
259 |             flags = 0,
260 |         }, nil, nil, function() -- Done
261 |             -- Stop playing the animation
262 |             StopAnimTask(ped, animationDict, animation, 1.0)
263 | 
264 |             -- Add the item to the inventory
265 |             TriggerServerEvent("wp-placeables:server:AddItem", itemName)
266 | 
267 |             -- First request control of networkId and wait until have control of netId before deleting it
268 |             -- Item will not properly delete if the client doesn't have control of the networkId
269 |             local coords = GetEntityCoords(itemEntity)
270 |             local netId = NetworkGetNetworkIdFromEntity(itemEntity)
271 |             RequestNetworkControlOfObject(netId, itemEntity)
272 |             SetEntityAsMissionEntity(itemEntity, true, true)
273 |             DeleteEntity(itemEntity)
274 | 
275 |             local object = { coords = coords, model = itemModel, }
276 |             TriggerServerEvent("wp-placeables:server:deleteWorldObject", object)
277 | 
278 |             CreateLog(itemName, false)
279 |         end, function() -- Cancel
280 |             StopAnimTask(ped, animationDict, animation, 1.0)
281 |             Notify("Canceled..", "error")
282 |         end)
283 |     end
284 | end
285 | 
286 | RegisterNetEvent("wp-placeables:client:placeItem", function(item)
287 |     if not IsPedInAnyVehicle(PlayerPedId(), true) then
288 |         startItemPlacementMode(item)
289 |     else
290 |         Notify("You cannot place items while in a vehicle", "error", 5000)
291 |     end
292 | end)
293 | 
294 | RegisterNetEvent("wp-placeables:client:pickUpItem", function(data)
295 |     pickUpItem(data)
296 | end)
297 | 
298 | -- Setup each placeable prop to use QB target
299 | -- Itemname is in the options so we know which item to give back when picked up
300 | for _, prop in pairs(Config.PlaceableProps) do
301 |     local pickUpEvent = "wp-placeables:client:pickUpItem"
302 |     if prop.customPickupEvent then
303 |         pickUpEvent = prop.customPickupEvent
304 |     end
305 |     local targetOptions = {
306 |         {
307 |             event = pickUpEvent,
308 |             icon = "fas fa-hand-holding",
309 |             label = "Pick up",
310 |             itemName = prop.item,
311 |             itemModel = prop.model,
312 |         },
313 |     }
314 | 
315 |     -- Add custom target options to the target options for this item prop
316 |     if prop.customTargetOptions then
317 |         for _, customOption in pairs(prop.customTargetOptions) do
318 |             -- Stamp the itemName and itemModel onto the data so the custom events have access to this info
319 |             customOption.itemName = prop.item
320 |             customOption.itemModel = prop.model
321 | 
322 |             targetOptions[#targetOptions + 1] = customOption
323 |         end
324 |     end
325 | 
326 |     -- Make sure we only define the target options once for each model
327 |     -- If you define the same model twice:
328 |     --      In qb-target, it will override the options, and the last one defined is used
329 |     --      In ox_target, it will append the options, resulting in N duplicate options
330 |     if not targetModels[prop.model] then
331 |         AddTargetModel(prop.model, {
332 |             options = targetOptions,
333 |             distance = 1.5,
334 |         })
335 |         targetModels[prop.model] = true
336 |     end
337 | end
338 | 
339 | -- Delete the world object
340 | -- object = {coords = coords, model = itemModel}
341 | RegisterNetEvent("wp-placeables:client:deleteWorldObject", function(object)
342 |     local entity = GetClosestObjectOfType(object.coords.x, object.coords.y, object.coords.z, 0.1, object.model, false, false, false)
343 |     if DoesEntityExist(entity) then
344 |         SetEntityAsMissionEntity(entity, 1, 1)
345 |         DeleteObject(entity)
346 |         SetEntityAsNoLongerNeeded(entity)
347 |     end
348 | end)
349 | 
350 | -- Runs a thread to loop through the deleted world objects table and removes the item if it exists
351 | -- This is to handle cases if the item were to have respawned
352 | -- Disabling this for now since we dont need to care if the item respawns
353 | -- There is currently an issue where this was being used for both player placed objects as well as world props
354 | -- If you placed an item, picked it up and placed the same item down again, the cleanup thread would delete it since its at the same coords.
355 | -- If we want to re-enable this, we need to find a way to only use this cleanup thread on world spawned props
356 | -- AddEventHandler('QBCore:Client:OnPlayerLoaded', function()
357 | --     QBCore.Functions.TriggerCallback('wp-placeables:server:GetDeletedWorldObjects', function(deletedObjects)
358 | --         objects = deletedObjects
359 | --     end)
360 | -- end)
361 | -- CreateThread(function()
362 | --     while true do
363 | --         for k = 1, #objects, 1 do
364 | --             v = objects[k]
365 | --             local entity = GetClosestObjectOfType(v.coords.x, v.coords.y, v.coords.z, 0.1, v.model, false, false, false)
366 | --             if DoesEntityExist(entity) then
367 | --                 SetEntityAsMissionEntity(entity, 1, 1)
368 | --                 DeleteObject(entity)
369 | --                 SetEntityAsNoLongerNeeded(entity)
370 | --             end
371 | --         end
372 | --         Wait(10000)
373 | --     end
374 | -- end)
375 | 
--------------------------------------------------------------------------------
/client/pushables.lua:
--------------------------------------------------------------------------------
  1 | local isAttached = false
  2 | local isSittingOnObject = false
  3 | local pushableObject = nil
  4 | 
  5 | -- Detaches the object from the player, stops the animation, and resets the variables
  6 | local function ReleasePushableObject()
  7 |     DetachEntity(pushableObject, false, true)
  8 |     SetEntityCollision(pushableObject, true, true)
  9 |     ClearPedTasksImmediately(PlayerPedId())
 10 | 
 11 |     -- Reset variables
 12 |     isAttached = false
 13 |     isSittingOnObject = false
 14 |     pushableObject = nil
 15 | end
 16 | 
 17 | -- Runs a thread while player is attached to the pushable object, disables some keys and listens for E keypress to release the object
 18 | -- Also checks that player is still in the animation, if not then puts them back into the animation
 19 | local function PushableObjectAttachedThread(animDict, anim)
 20 |     CreateThread(function()
 21 |         while isAttached do
 22 |             -- Disables attacking controls
 23 |             DisableControlAction(0, 24, true)  -- disable attack
 24 |             DisableControlAction(0, 25, true)  -- disable aim
 25 |             DisableControlAction(0, 47, true)  -- disable weapon
 26 |             DisableControlAction(0, 58, true)  -- disable weapon
 27 |             DisableControlAction(0, 263, true) -- disable melee
 28 |             DisableControlAction(0, 264, true) -- disable melee
 29 |             DisableControlAction(0, 257, true) -- disable melee
 30 |             DisableControlAction(0, 140, true) -- disable melee
 31 |             DisableControlAction(0, 141, true) -- disable melee
 32 |             DisableControlAction(0, 142, true) -- disable melee
 33 |             DisableControlAction(0, 143, true) -- disable melee
 34 | 
 35 |             -- Check to see if animation is still playing, if not then put the player back in the animation
 36 |             local playerPed = PlayerPedId()
 37 |             if not IsEntityPlayingAnim(playerPed, animDict, anim, 3) and not IsPedRagdoll(playerPed) then
 38 |                 local animFlag = isSittingOnObject and 5 or 51
 39 |                 ClearPedTasks(playerPed)
 40 |                 LoadAnimDict(animDict)
 41 |                 TaskPlayAnim(playerPed, animDict, anim, 3.0, 3.0, -1, animFlag, 0, 0, 0, 0)
 42 |             end
 43 | 
 44 |             -- Pressed E
 45 |             if IsControlJustPressed(0, 38) then
 46 |                 ReleasePushableObject()
 47 |             end
 48 | 
 49 |             Wait(5) -- Needs to be a small number so that player doesnt need to spam press E to get it to take
 50 |         end
 51 |     end)
 52 | end
 53 | 
 54 | -- Attaches the object to the player and plays a pushing animation
 55 | local function PushObject(data)
 56 |     local PlayerPed = PlayerPedId()
 57 | 
 58 |     pushableObject = data.entity
 59 | 
 60 |     if pushableObject then
 61 |         local netId = NetworkGetNetworkIdFromEntity(pushableObject)
 62 |         RequestNetworkControlOfObject(netId, pushableObject)
 63 | 
 64 |         LoadAnimDict(data.animationPushOptions.animationDict)
 65 |         TaskPlayAnim(PlayerPed, data.animationPushOptions.animationDict, data.animationPushOptions.animationName, 8.0, 8.0, -1, 51, 0, 0, 0, 0)
 66 | 
 67 |         local offset = data.animationPushOptions.offset
 68 |         local rotation = data.animationPushOptions.rotation
 69 | 
 70 |         SetTimeout(150, function()
 71 |             isAttached = true
 72 |             SetEntityCollision(pushableObject, false, false)
 73 |             AttachEntityToEntity(
 74 |                 pushableObject,
 75 |                 PlayerPed,
 76 |                 GetPedBoneIndex(PlayerPed, 28422),
 77 |                 offset.x,
 78 |                 offset.y,
 79 |                 offset.z,
 80 |                 rotation.x,
 81 |                 rotation.y,
 82 |                 rotation.z,
 83 |                 false, -- p9
 84 |                 false, -- usesoftpinner
 85 |                 true,  -- collision
 86 |                 false, -- isPed
 87 |                 2,     -- rotationorder
 88 |                 true   -- fixrot
 89 |             )
 90 | 
 91 |             PushableObjectAttachedThread(data.animationPushOptions.animationDict, data.animationPushOptions.animationName)
 92 |         end)
 93 |     end
 94 | end
 95 | 
 96 | -- Attaches player to object and plays the sitting animation
 97 | local function SitOnObject(data)
 98 |     local PlayerPed = PlayerPedId()
 99 | 
100 |     pushableObject = data.entity
101 | 
102 |     if pushableObject ~= nil then
103 |         LoadAnimDict(data.animationSitOptions.animationDict)
104 |         TaskPlayAnim(PlayerPed, data.animationSitOptions.animationDict, data.animationSitOptions.animationName, 8.0, 8.0, -1, 5, 0, 0, 0, 0)
105 | 
106 |         local offset = data.animationSitOptions.offset
107 |         local rotation = data.animationSitOptions.rotation
108 | 
109 |         SetTimeout(150, function()
110 |             isAttached = true
111 |             isSittingOnObject = true
112 |             AttachEntityToEntity(
113 |                 PlayerPed,
114 |                 pushableObject,
115 |                 GetPedBoneIndex(PlayerPed, 11816),
116 |                 offset.x,
117 |                 offset.y,
118 |                 offset.z,
119 |                 rotation.x,
120 |                 rotation.y,
121 |                 rotation.z,
122 |                 false, -- p9
123 |                 false, -- usesoftpinner
124 |                 false, -- collision -- if this is true, then the peds body will collide with things as you push it around
125 |                 true,  -- isPed
126 |                 2,     -- rotationorder
127 |                 true   -- fixrot
128 |             )
129 | 
130 |             PushableObjectAttachedThread(data.animationSitOptions.animationDict, data.animationSitOptions.animationName)
131 |         end)
132 |     end
133 | end
134 | 
135 | AddEventHandler("onResourceStop", function(resource)
136 |     if resource == GetCurrentResourceName() then
137 |         if pushableObject then
138 |             ReleasePushableObject()
139 |             DeleteObject(pushableObject)
140 |             ClearPedTasksImmediately(PlayerPedId())
141 |         end
142 |     end
143 | end)
144 | 
145 | RegisterNetEvent("wp-placeables:client:pushObject", function(data)
146 |     PushObject(data)
147 | end)
148 | 
149 | RegisterNetEvent("wp-placeables:client:sitOnObject", function(data)
150 |     SitOnObject(data)
151 | end)
152 | 
153 | local function IsPlayerSittingOnPlaceableProp()
154 |     return isSittingOnObject
155 | end
156 | exports("IsPlayerSittingOnPlaceableProp", IsPlayerSittingOnPlaceableProp)
157 | 
--------------------------------------------------------------------------------
/fxmanifest.lua:
--------------------------------------------------------------------------------
 1 | fx_version "cerulean"
 2 | game "gta5"
 3 | 
 4 | description "Waypoint Placeables"
 5 | author "BackSH00TER - Waypoint RP"
 6 | version "1.1.4"
 7 | 
 8 | shared_script {
 9 |     -- "@ox_lib/init.lua", -- Uncomment this if you are planning to integrate with any ox scripts
10 |     "shared/config.lua",
11 |     "shared/framework.lua",
12 | }
13 | 
14 | client_scripts {
15 |     "client/client.lua",
16 |     "client/pushables.lua",
17 |     "client/placeables.lua",
18 | }
19 | 
20 | server_scripts {
21 |     "server/server.lua",
22 | }
23 | 
24 | lua54 "yes"
25 | 
--------------------------------------------------------------------------------
/images/beachball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/beachball.png
--------------------------------------------------------------------------------
/images/beachtowel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/beachtowel.png
--------------------------------------------------------------------------------
/images/beachumbrella.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/beachumbrella.png
--------------------------------------------------------------------------------
/images/bodybag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/bodybag.png
--------------------------------------------------------------------------------
/images/campchair_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/campchair_blue.png
--------------------------------------------------------------------------------
/images/campchair_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/campchair_green.png
--------------------------------------------------------------------------------
/images/campchair_plaid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/campchair_plaid.png
--------------------------------------------------------------------------------
/images/campfire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/campfire.png
--------------------------------------------------------------------------------
/images/candycane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/candycane.png
--------------------------------------------------------------------------------
/images/canopy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/canopy.png
--------------------------------------------------------------------------------
/images/cargobox1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox1.png
--------------------------------------------------------------------------------
/images/cargobox2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox2.png
--------------------------------------------------------------------------------
/images/cargobox3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox3.png
--------------------------------------------------------------------------------
/images/cargobox4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox4.png
--------------------------------------------------------------------------------
/images/cargobox5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox5.png
--------------------------------------------------------------------------------
/images/cargobox6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox6.png
--------------------------------------------------------------------------------
/images/cargobox7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox7.png
--------------------------------------------------------------------------------
/images/cargobox8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cargobox8.png
--------------------------------------------------------------------------------
/images/carjack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/carjack.png
--------------------------------------------------------------------------------
/images/constructionbarrier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/constructionbarrier.png
--------------------------------------------------------------------------------
/images/constructionbarrier2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/constructionbarrier2.png
--------------------------------------------------------------------------------
/images/constructionbarrier3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/constructionbarrier3.png
--------------------------------------------------------------------------------
/images/constructiongenerator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/constructiongenerator.png
--------------------------------------------------------------------------------
/images/cot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/cot.png
--------------------------------------------------------------------------------
/images/crate1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/crate1.png
--------------------------------------------------------------------------------
/images/crate2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/crate2.png
--------------------------------------------------------------------------------
/images/crate3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/crate3.png
--------------------------------------------------------------------------------
/images/crate4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/crate4.png
--------------------------------------------------------------------------------
/images/crate5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/crate5.png
--------------------------------------------------------------------------------
/images/drinkcart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/drinkcart.png
--------------------------------------------------------------------------------
/images/examlight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/examlight.png
--------------------------------------------------------------------------------
/images/folding_chair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/folding_chair.png
--------------------------------------------------------------------------------
/images/greenscreen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/greenscreen.png
--------------------------------------------------------------------------------
/images/handtruck.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/handtruck.png
--------------------------------------------------------------------------------
/images/hazardbin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hazardbin.png
--------------------------------------------------------------------------------
/images/hobomattress.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hobomattress.png
--------------------------------------------------------------------------------
/images/hoboshelter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hoboshelter.png
--------------------------------------------------------------------------------
/images/hobostove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hobostove.png
--------------------------------------------------------------------------------
/images/hospitalbed1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hospitalbed1.png
--------------------------------------------------------------------------------
/images/hospitalbed2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hospitalbed2.png
--------------------------------------------------------------------------------
/images/hospitalbedtable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/hospitalbedtable.png
--------------------------------------------------------------------------------
/images/janitorcart1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/janitorcart1.png
--------------------------------------------------------------------------------
/images/janitorcart2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/janitorcart2.png
--------------------------------------------------------------------------------
/images/largetent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/largetent.png
--------------------------------------------------------------------------------
/images/lawnmower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/lawnmower.png
--------------------------------------------------------------------------------
/images/medbag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/medbag.png
--------------------------------------------------------------------------------
/images/medmachine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/medmachine.png
--------------------------------------------------------------------------------
/images/medtable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/medtable.png
--------------------------------------------------------------------------------
/images/meshfence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/meshfence.png
--------------------------------------------------------------------------------
/images/metalcart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/metalcart.png
--------------------------------------------------------------------------------
/images/metalramp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/metalramp.png
--------------------------------------------------------------------------------
/images/microscope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/microscope.png
--------------------------------------------------------------------------------
/images/mopbucket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/mopbucket.png
--------------------------------------------------------------------------------
/images/oldtent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/oldtent.png
--------------------------------------------------------------------------------
/images/oscillator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/oscillator.png
--------------------------------------------------------------------------------
/images/pallet1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/pallet1.png
--------------------------------------------------------------------------------
/images/pallet2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/pallet2.png
--------------------------------------------------------------------------------
/images/pallet3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/pallet3.png
--------------------------------------------------------------------------------
/images/pallet4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/pallet4.png
--------------------------------------------------------------------------------
/images/pallet5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/pallet5.png
--------------------------------------------------------------------------------
/images/plastic_chair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/plastic_chair.png
--------------------------------------------------------------------------------
/images/plastictable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/plastictable.png
--------------------------------------------------------------------------------
/images/roadclosedbarrier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/roadclosedbarrier.png
--------------------------------------------------------------------------------
/images/roadcone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/roadcone.png
--------------------------------------------------------------------------------
/images/roadconebig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/roadconebig.png
--------------------------------------------------------------------------------
/images/roadpole.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/roadpole.png
--------------------------------------------------------------------------------
/images/roadworkahead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/roadworkahead.png
--------------------------------------------------------------------------------
/images/roomtrolly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/roomtrolly.png
--------------------------------------------------------------------------------
/images/ropebarrier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/ropebarrier.png
--------------------------------------------------------------------------------
/images/sexdoll.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/sexdoll.png
--------------------------------------------------------------------------------
/images/shoppingcart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/shoppingcart.png
--------------------------------------------------------------------------------
/images/skateramp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/skateramp.png
--------------------------------------------------------------------------------
/images/sleepingbag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/sleepingbag.png
--------------------------------------------------------------------------------
/images/snowman1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/snowman1.png
--------------------------------------------------------------------------------
/images/snowman2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/snowman2.png
--------------------------------------------------------------------------------
/images/snowman3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/snowman3.png
--------------------------------------------------------------------------------
/images/snowman4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/snowman4.png
--------------------------------------------------------------------------------
/images/soccerball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/soccerball.png
--------------------------------------------------------------------------------
/images/stepladder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/stepladder.png
--------------------------------------------------------------------------------
/images/stretcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/stretcher.png
--------------------------------------------------------------------------------
/images/teacart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/teacart.png
--------------------------------------------------------------------------------
/images/tent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/tent.png
--------------------------------------------------------------------------------
/images/toolchest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/toolchest.png
--------------------------------------------------------------------------------
/images/trafficdevice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/trafficdevice.png
--------------------------------------------------------------------------------
/images/trashbin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/trashbin.png
--------------------------------------------------------------------------------
/images/trifinishbanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/trifinishbanner.png
--------------------------------------------------------------------------------
/images/tristartbanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/tristartbanner.png
--------------------------------------------------------------------------------
/images/tristarttable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/tristarttable.png
--------------------------------------------------------------------------------
/images/warehousetrolly1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/warehousetrolly1.png
--------------------------------------------------------------------------------
/images/warehousetrolly2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/warehousetrolly2.png
--------------------------------------------------------------------------------
/images/waterbarrel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/waterbarrel.png
--------------------------------------------------------------------------------
/images/wheelbarrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/wheelbarrow.png
--------------------------------------------------------------------------------
/images/woodramp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/woodramp.png
--------------------------------------------------------------------------------
/images/woodtable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/woodtable.png
--------------------------------------------------------------------------------
/images/worklight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/worklight.png
--------------------------------------------------------------------------------
/images/worklight2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/worklight2.png
--------------------------------------------------------------------------------
/images/worklight3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/worklight3.png
--------------------------------------------------------------------------------
/images/xmaspresent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/xmaspresent.png
--------------------------------------------------------------------------------
/images/xmastree1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/xmastree1.png
--------------------------------------------------------------------------------
/images/xmastree2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WaypointRP/wp-placeables/39b9b303369406aad9058fc576ba1d430830b8d1/images/xmastree2.png
--------------------------------------------------------------------------------
/server/server.lua:
--------------------------------------------------------------------------------
 1 | -- Setup all the placeable props as useable items
 2 | for _, prop in pairs(Config.PlaceableProps) do
 3 |     CreateUseableItem(prop.item, function(source, item)
 4 |         TriggerClientEvent("wp-placeables:client:placeItem", source, prop)
 5 |     end)
 6 | end
 7 | 
 8 | -- Checks the config to see if the item is valid
 9 | local function containsItem(item)
10 |     for i = 1, #Config.PlaceableProps do
11 |         local v = Config.PlaceableProps[i]
12 |         if v.item == item then
13 |             return true
14 |         end
15 |     end
16 | end
17 | 
18 | -- This function is to handle the syncing of deleting world props between all clients
19 | RegisterServerEvent("wp-placeables:server:deleteWorldObject", function(object)
20 |     TriggerClientEvent("wp-placeables:client:deleteWorldObject", -1, object)
21 | end)
22 | 
23 | RegisterNetEvent("wp-placeables:server:RemoveItem", function(itemName)
24 |     local src = source
25 |     RemoveItem(src, itemName, 1)
26 | end)
27 | 
28 | RegisterNetEvent("wp-placeables:server:AddItem", function(itemName)
29 |     local src = source
30 | 
31 |     if not containsItem(itemName) then
32 |         print(string.format("%s - tried to spawn an item that does not exist in the config (%s)", src, itemName))
33 |         return
34 |     end
35 | 
36 |     AddItem(src, itemName, 1)
37 | end)
38 | 
39 | AddEventHandler("onResourceStart", function(resourceName)
40 |     if resourceName == GetCurrentResourceName() then
41 |         -- Give the script some time to start
42 |         Wait(100)
43 | 
44 |         ValidateOxLibUsage()
45 |     end
46 | end)
47 | 
--------------------------------------------------------------------------------
/shared/config.lua:
--------------------------------------------------------------------------------
  1 | Config = {}
  2 | ------------------------------------
  3 | -- FRAMEWORK / SCRIPT CONFIGURATION
  4 | -- Adjust these settings to match the framework and scripts you are using
  5 | -- Note: If using ox for any option, enable @ox_lib/init.lua in the manifest!
  6 | ------------------------------------
  7 | 
  8 | --- @type "qb" | "qbx" | "esx"
  9 | Config.Framework = "qb"
 10 | 
 11 | -- The notification script you are using.
 12 | --- @type "qb" | "esx" | "ox" | "none"
 13 | Config.Notify = "qb"
 14 | 
 15 | -- The target script you are using.
 16 | --- @type "qb" | "ox"
 17 | Config.Target = "qb"
 18 | 
 19 | -- The inventory script you are using.
 20 | --- @type "qb" | "esx" | "ox"
 21 | Config.Inventory = "qb"
 22 | 
 23 | -- The progress bar script you are using.
 24 | --- @type "qb" | "ox" | "none"
 25 | Config.ProgessBar = "qb"
 26 | 
 27 | -- The logging script you are using.
 28 | --- @type "qb" | "none"
 29 | Config.Log = "qb"
 30 | 
 31 | ------------------------------------
 32 | --- END FRAMEWORK / SCRIPT CONFIGURATION
 33 | ------------------------------------
 34 | 
 35 | Config.ItemPlacementModeRadius = 10.0 -- Object can only be placed within this radius of the player
 36 | 
 37 | -- These are necessary so people can't place props far away
 38 | Config.minZOffset = -2.0 -- The min z offset for placing objects
 39 | Config.maxZOffset = 2.0  -- The max z offset for placing objects
 40 | 
 41 | -- Creates a deep copy of the table
 42 | -- This is necessary for getting around luas pass by reference of tables
 43 | --- @param orig table The table to be copied
 44 | --- @return table The deep copied table
 45 | local function deepcopy(orig) -- modified the deep copy function from http://lua-users.org/wiki/CopyTable
 46 |     local orig_type = type(orig)
 47 |     local copy
 48 |     if orig_type == "table" then
 49 |         if not orig.canOpen or orig.canOpen() then
 50 |             local toRemove = {}
 51 |             copy = {}
 52 |             for orig_key, orig_value in next, orig, nil do
 53 |                 if type(orig_value) == "table" then
 54 |                     if not orig_value.canOpen or orig_value.canOpen() then
 55 |                         copy[deepcopy(orig_key)] = deepcopy(orig_value)
 56 |                     else
 57 |                         toRemove[orig_key] = true
 58 |                     end
 59 |                 else
 60 |                     copy[deepcopy(orig_key)] = deepcopy(orig_value)
 61 |                 end
 62 |             end
 63 |             for i = 1, #toRemove do table.remove(copy, i) --[[ Using this to make sure all indexes get re-indexed and no empty spaces are in the radialmenu ]] end
 64 |             if copy and next(copy) then setmetatable(copy, deepcopy(getmetatable(orig))) end
 65 |         end
 66 |     elseif orig_type ~= "function" then
 67 |         copy = orig
 68 |     end
 69 |     return copy
 70 | end
 71 | 
 72 | -- Helper function to combine default options with custom target options for items
 73 | --- @param targetOptions table[] The default target options for the item (e.g., pushTargetOptions for push-only objects and pushAndSitTargetOptions for push-and-sit objects)
 74 | --- @param animationPushOptions? table The custom animation options for pushing the object (modifying offsets, rotations, animations)
 75 | --- @param animationSitOptions? table The custom animation options for sitting on the object (modifying offsets, rotations, animations)
 76 | --- @param otherOptions? table[] Any other custom target options you want to add to the item
 77 | --- @return table[] The combined target options with customizations applied
 78 | local function setCustomTargetOptions(targetOptions, animationPushOptions, animationSitOptions, otherOptions)
 79 |     local customTargetOptions = deepcopy(targetOptions)
 80 | 
 81 |     if animationPushOptions then
 82 |         customTargetOptions[1].animationPushOptions = animationPushOptions
 83 |     end
 84 | 
 85 |     if animationSitOptions then
 86 |         customTargetOptions[2].animationSitOptions = animationSitOptions
 87 |     end
 88 | 
 89 |     if otherOptions then
 90 |         for i = 1, #otherOptions do
 91 |             customTargetOptions[#customTargetOptions + 1] = otherOptions[i]
 92 |         end
 93 |     end
 94 | 
 95 |     return customTargetOptions
 96 | end
 97 | 
 98 | -- Default target options
 99 | 
100 | local pushAndSitTargetOptions = {
101 |     {
102 |         event = "wp-placeables:client:pushObject",
103 |         icon = "fas fa-shopping-cart",
104 |         label = "Push object",
105 |         animationPushOptions = {
106 |             offset = { x = -0.4, y = -1.7, z = -0.3, },
107 |             rotation = { x = 0.0, y = 0.0, z = 180.0, },
108 |             animationDict = "missfinale_c2ig_11",
109 |             animationName = "pushcar_offcliff_f",
110 |         },
111 |     },
112 |     {
113 |         event = "wp-placeables:client:sitOnObject",
114 |         icon = "fas fa-chair",
115 |         label = "Sit on object",
116 |         animationSitOptions = {
117 |             offset = { x = 0.0, y = 0.15, z = 0.85, },
118 |             rotation = { x = 0.0, y = 10.0, z = 175.0, },
119 |             animationDict = "anim@amb@business@bgen@bgen_no_work@",
120 |             animationName = "sit_phone_phoneputdown_idle_nowork",
121 |         },
122 |     },
123 | }
124 | 
125 | local pushTargetOptions = {
126 |     {
127 |         event = "wp-placeables:client:pushObject",
128 |         icon = "fas fa-shopping-cart",
129 |         label = "Push cart",
130 |         animationPushOptions = {
131 |             offset = { x = -0.4, y = -1.7, z = -0.3, },
132 |             rotation = { x = 0.0, y = 0.0, z = 180.0, },
133 |             animationDict = "missfinale_c2ig_11",
134 |             animationName = "pushcar_offcliff_f",
135 |         },
136 |     },
137 | }
138 | 
139 | -- Define custom target options here for addon items
140 | 
141 | -- Uncomment this line if you are using wp-seats
142 | local chairCustomTargetOptions = {
143 |     {
144 |         event = "wp-seats:client:sitOnChair",
145 |         icon = "fas fa-chair",
146 |         label = "Sit down",
147 |     },
148 | }
149 | 
150 | -- Uncomment this line if you are using wp-yogamats
151 | -- local yogaCustomTargetOptions = {
152 | --     {
153 | --         event = "wp-yogamats:client:useYogaMat",
154 | --         icon = "fas fa-pray",
155 | --         label = "Do yoga",
156 | --     },
157 | -- }
158 | 
159 | -- Uncomment this line if you are using wp-printer
160 | -- local printerCustomTargetOptions = {
161 | --     {
162 | --         event = "wp-printer:client:UsePrinter",
163 | --         icon = "fas fa-print",
164 | --         label = "Use printer",
165 | --     },
166 | -- }
167 | 
168 | -- Uncomment this line if you are using wp-fireworks
169 | -- local fireworkCustomTargetOptions = {
170 | --     {
171 | --         event = 'wp-fireworks:client:lightFireworkFuse',
172 | --         icon = "fa-solid fa-fire",
173 | --         label = "Light fuse"
174 | --     },
175 | --     {
176 | --         event = 'wp-fireworks:client:buildFireworkSequence',
177 | --         icon = 'fa-solid fa-link',
178 | --         label = 'Add to sequence'
179 | --     }
180 | -- }
181 | 
182 | -- Uncomment this line if you are using wp-trafficlights
183 | -- local trafficLightCustomTargetOptions = {
184 | --     {
185 | --         event = "wp-trafficlights:client:OpenMenu",
186 | --         icon = "fas fa-traffic-light",
187 | --         label = "Remote control traffic light",
188 | --     },
189 | -- }
190 | 
191 | -- Uncomment this line if you are using wp-trafficlights
192 | -- local trafficLightCustomPickupEvent = "wp-trafficlights:RemoveTrafficLight"
193 | 
194 | --- @class PlaceableProp
195 | --- @field item string The item name as defined in your items.lua
196 | --- @field label string The label to be used for this item (displayed in the progress bar)
197 | --- @field model string The prop model to be used for this item
198 | --- @field isFrozen boolean Whether or not the prop should be frozen in place when placed
199 | --- @field customTargetOptions? table[] Optional - Custom target options for this item, if it should do more than just pickup
200 | --- @field customPickupEvent? string Optional - If you want to override the default pickup event, set this to the event you want to be called when the "pickup" target option is used
201 | 
202 | -- Add the props you want to be placeable here
203 | -- Every prop will have the "pickup" target option added by default (to override use customPickupEvent)
204 | --- @type PlaceableProp[]
205 | Config.PlaceableProps = {
206 |     -- Constructions props
207 |     { item = "roadworkbarrier",       label = "Road Work Ahead Barrier",   model = "prop_barrier_work04a",           isFrozen = true, },
208 |     { item = "roadclosedbarrier",     label = "Road Closed Barrier",       model = "xm3_prop_xm3_road_barrier_01a",  isFrozen = true, },
209 |     { item = "constructionbarrier",   label = "Fold-out Barrier",          model = "prop_barrier_work01a",           isFrozen = false, },
210 |     { item = "constructionbarrier2",  label = "Construction Barrier",      model = "prop_barrier_work06a",           isFrozen = true, },
211 |     { item = "constructionbarrier3",  label = "Construction Barrier",      model = "prop_mp_barrier_02b",            isFrozen = true, },
212 |     { item = "roadconebig",           label = "Big Road Cone",             model = "prop_barrier_wat_03a",           isFrozen = false, },
213 |     { item = "roadcone",              label = "Road Cone",                 model = "prop_roadcone01a",               isFrozen = false, },
214 |     { item = "roadpole",              label = "Road Pole",                 model = "prop_roadpole_01a",              isFrozen = false, },
215 |     { item = "worklight",             label = "Work Light",                model = "prop_worklight_01a",             isFrozen = false, },
216 |     { item = "worklight2",            label = "Work Light",                model = "prop_worklight_04b",             isFrozen = false, },
217 |     { item = "worklight3",            label = "Work Light",                model = "prop_worklight_02a",             isFrozen = false, },
218 |     { item = "constructiongenerator", label = "Construction Generator",    model = "prop_generator_03b",             isFrozen = true, },
219 |     { item = "trafficdevice",         label = "Traffic Device (Left)",     model = "prop_trafficdiv_01",             isFrozen = true, },
220 |     { item = "trafficdevice2",        label = "Traffic Device (Right)",    model = "prop_trafficdiv_02",             isFrozen = true, },
221 |     { item = "meshfence1",            label = "Mesh Fence (Small)",        model = "prop_fnc_omesh_01a",             isFrozen = true, },
222 |     { item = "meshfence2",            label = "Mesh Fence (Medium)",       model = "prop_fnc_omesh_02a",             isFrozen = true, },
223 |     { item = "meshfence3",            label = "Mesh Fence (Large)",        model = "prop_fnc_omesh_03a",             isFrozen = true, },
224 |     { item = "waterbarrel",           label = "Water Barrel",              model = "prop_barrier_wat_04a",           isFrozen = false, },
225 | 
226 |     -- Camping + Hobo props
227 |     { item = "tent",                  label = "Old Tent",                  model = "prop_skid_tent_03",              isFrozen = true, },
228 |     { item = "tent2",                 label = "Tent",                      model = "prop_skid_tent_01",              isFrozen = true, },
229 |     { item = "tent3",                 label = "Large Tent",                model = "ba_prop_battle_tent_02",         isFrozen = true, },
230 |     { item = "hobostove",             label = "Hobo Stove",                model = "prop_hobo_stove_01",             isFrozen = true, },
231 |     { item = "campfire",              label = "Campfire",                  model = "prop_beach_fire",                isFrozen = true, },
232 |     { item = "hobomattress",          label = "Hobo Mattress",             model = "prop_rub_matress_01",            isFrozen = true, },
233 |     { item = "hoboshelter",           label = "Hobo Shelter",              model = "prop_homeles_shelter_01",        isFrozen = true, },
234 |     { item = "sleepingbag",           label = "Sleeping Bag",              model = "prop_skid_sleepbag_1",           isFrozen = true, },
235 |     { item = "canopy1",               label = "Canopy (Green)",            model = "prop_gazebo_01",                 isFrozen = true, },
236 |     { item = "canopy2",               label = "Canopy (Blue)",             model = "prop_gazebo_02",                 isFrozen = true, },
237 |     { item = "canopy3",               label = "Canopy (White)",            model = "prop_gazebo_03",                 isFrozen = true, },
238 |     { item = "cot",                   label = "Cot",                       model = "gr_prop_gr_campbed_01",          isFrozen = true, },
239 | 
240 |     -- Triathlon props
241 |     { item = "tristarttable",         label = "Triathlon Start Table",     model = "prop_tri_table_01",              isFrozen = true, },
242 |     { item = "tristartbanner",        label = "Triathlon Start Banner",    model = "prop_tri_start_banner",          isFrozen = true, },
243 |     { item = "trifinishbanner",       label = "Triathlon Finish Banner",   model = "prop_tri_finish_banner",         isFrozen = true, },
244 | 
245 |     -- Table props
246 |     { item = "plastictable",          label = "Plastic Table",             model = "prop_ven_market_table1",         isFrozen = true, },
247 |     { item = "plastictable2",         label = "Plastic Table",             model = "prop_table_03",                  isFrozen = true, },
248 |     { item = "woodtable",             label = "Small Wood Table",          model = "prop_rub_table_01",              isFrozen = true, },
249 |     { item = "woodtable2",            label = "Wood Table",                model = "prop_rub_table_02",              isFrozen = true, },
250 | 
251 |     -- Beach props
252 |     { item = "beachtowel",            label = "Beach Towel",               model = "prop_cs_beachtowel_01",          isFrozen = true, },
253 |     { item = "beachumbrella",         label = "Beach Umbrella",            model = "prop_parasol_04b",               isFrozen = true, },
254 |     { item = "beachumbrella2",        label = "Beach Umbrella",            model = "prop_beach_parasol_02",          isFrozen = true, },
255 |     { item = "beachumbrella3",        label = "Beach Umbrella",            model = "prop_beach_parasol_06",          isFrozen = true, },
256 |     { item = "beachumbrella4",        label = "Beach Umbrella",            model = "prop_beach_parasol_10",          isFrozen = true, },
257 |     { item = "beachball",             label = "Beach Ball",                model = "prop_beachball_02",              isFrozen = false, },
258 | 
259 |     -- Ramp props
260 |     { item = "ramp1",                 label = "Wood Ramp (Gradual)",       model = "prop_mp_ramp_01",                isFrozen = true, },
261 |     { item = "ramp2",                 label = "Wood Ramp (Moderate)",      model = "prop_mp_ramp_02",                isFrozen = true, },
262 |     { item = "ramp3",                 label = "Wood Ramp (Steep)",         model = "prop_mp_ramp_03",                isFrozen = true, },
263 |     { item = "ramp4",                 label = "Metal Ramp (Large)",        model = "xs_prop_arena_pipe_ramp_01a",    isFrozen = true, },
264 |     { item = "ramp5",                 label = "Metal Trailer Ramp",        model = "xs_prop_x18_flatbed_ramp",       isFrozen = true, },
265 |     { item = "skateramp",             label = "Skate Ramp",                model = "prop_skate_flatramp",            isFrozen = true, },
266 |     { item = "stuntramp1",            label = "Stunt Ramp S",              model = "stt_prop_ramp_adj_flip_s",       isFrozen = true, },
267 |     { item = "stuntramp2",            label = "Stunt Ramp M",              model = "stt_prop_ramp_adj_flip_m",       isFrozen = true, },
268 |     { item = "stuntramp3",            label = "Stunt Ramp L",              model = "stt_prop_ramp_jump_l",           isFrozen = true, },
269 |     { item = "stuntramp4",            label = "Stunt Ramp XL",             model = "stt_prop_ramp_jump_xl",          isFrozen = true, },
270 |     { item = "stuntramp5",            label = "Stunt Ramp XXL",            model = "stt_prop_ramp_jump_xxl",         isFrozen = true, },
271 |     { item = "stuntloop1",            label = "Stunt Half Loop",           model = "stt_prop_ramp_adj_hloop",        isFrozen = true, },
272 |     { item = "stuntloop2",            label = "Stunt Loop",                model = "stt_prop_ramp_adj_loop",         isFrozen = true, },
273 |     { item = "stuntloop3",            label = "Stunt Spiral",              model = "stt_prop_ramp_spiral_s",         isFrozen = true, },
274 | 
275 |     -- EMS/Hospital props
276 |     { item = "medbag",                label = "Medical Bag",               model = "xm_prop_x17_bag_med_01a",        isFrozen = true, },
277 |     { item = "examlight",             label = "Exam Light",                model = "v_med_examlight",                isFrozen = true, },
278 |     { item = "hazardbin",             label = "Hazard Bin",                model = "v_med_medwastebin",              isFrozen = true, },
279 |     { item = "microscope",            label = "Microscope",                model = "v_med_microscope",               isFrozen = true, },
280 |     { item = "oscillator",            label = "Oscillator",                model = "v_med_oscillator3",              isFrozen = true, },
281 |     { item = "medmachine",            label = "Medical Machine",           model = "v_med_oscillator4",              isFrozen = true, },
282 |     { item = "bodybag",               label = "Body Bag",                  model = "xm_prop_body_bag",               isFrozen = true, },
283 | 
284 |     -- Chairs
285 |     { item = "camp_chair_green",      label = "Camp Chair (Green)",        model = "prop_skid_chair_01",             isFrozen = true,  customTargetOptions = chairCustomTargetOptions, },
286 |     { item = "camp_chair_blue",       label = "Camp Chair (Blue)",         model = "prop_skid_chair_02",             isFrozen = true,  customTargetOptions = chairCustomTargetOptions, },
287 |     { item = "camp_chair_plaid",      label = "Camp Chair (Plaid)",        model = "prop_skid_chair_03",             isFrozen = true,  customTargetOptions = chairCustomTargetOptions, },
288 |     { item = "plastic_chair",         label = "Plastic Chair",             model = "prop_chair_08",                  isFrozen = true,  customTargetOptions = chairCustomTargetOptions, },
289 |     { item = "folding_chair",         label = "Folding Chair",             model = "xm3_prop_xm3_folding_chair_01a", isFrozen = true,  customTargetOptions = chairCustomTargetOptions, },
290 | 
291 |     -- Cargo props
292 |     { item = "cargobox1",             label = "Large cardboardbox pallet", model = "prop_mb_cargo_03a",              isFrozen = false, },
293 |     { item = "cargobox2",             label = "Large mixed pallet",        model = "prop_mb_cargo_02a",              isFrozen = false, },
294 |     { item = "cargobox3",             label = "Tall wrapped pallet",       model = "hei_prop_carrier_cargo_04b",     isFrozen = false, },
295 |     { item = "cargobox4",             label = "Cardboardboxes pallet",     model = "prop_boxpile_02c",               isFrozen = false, },
296 |     { item = "cargobox5",             label = "Sprunk boxes pallet",       model = "prop_boxpile_03a",               isFrozen = false, },
297 |     { item = "cargobox6",             label = "Cardboardboxes wrapped",    model = "prop_boxpile_04a",               isFrozen = false, },
298 |     { item = "cargobox7",             label = "Cardboardboxes fragile",    model = "prop_boxpile_06a",               isFrozen = false, },
299 |     { item = "cargobox8",             label = "Cardboardboxes + keg",      model = "prop_boxpile_09a",               isFrozen = false, },
300 |     { item = "pallet1",               label = "Empty pallet",              model = "prop_pallet_01a",                isFrozen = false, },
301 |     { item = "pallet2",               label = "Fertilizer pallet",         model = "bkr_prop_fertiliser_pallet_01a", isFrozen = false, },
302 |     { item = "pallet3",               label = "Weed bricks pallet",        model = "hei_prop_heist_weed_pallet",     isFrozen = false, },
303 |     { item = "pallet4",               label = "Barrell pallet",            model = "xm3_prop_xm3_pallet_ch_01a",     isFrozen = false, },
304 |     { item = "pallet5",               label = "Slotmachine pallet",        model = "sf_prop_sf_slot_pallet_01a",     isFrozen = false, },
305 |     { item = "crate1",                label = "Gopostal crate",            model = "prop_box_wood03a",               isFrozen = false, },
306 |     { item = "crate2",                label = "Wood crate",                model = "prop_box_wood04a",               isFrozen = false, },
307 |     { item = "crate3",                label = "Cluckinbell crate",         model = "vw_prop_vw_boxwood_01a",         isFrozen = false, },
308 |     { item = "crate4",                label = "Water crate",               model = "prop_watercrate_01",             isFrozen = false, },
309 |     { item = "crate5",                label = "Animal cage",               model = "v_med_apecrate",                 isFrozen = false, },
310 | 
311 |     -- Xmas props
312 |     { item = "snowman1",              label = "Snowman (Red)",             model = "xm3_prop_xm3_snowman_01a",       isFrozen = true, },
313 |     { item = "snowman2",              label = "Snowman (Blue)",            model = "xm3_prop_xm3_snowman_01b",       isFrozen = true, },
314 |     { item = "snowman3",              label = "Snowman (Green)",           model = "xm3_prop_xm3_snowman_01c",       isFrozen = true, },
315 |     { item = "snowman4",              label = "Snowman",                   model = "prop_prlg_snowpile",             isFrozen = true, },
316 |     { item = "xmastree1",             label = "Giant Xmas Tree",           model = "prop_xmas_ext",                  isFrozen = true, },
317 |     { item = "xmastree2",             label = "Xmas Tree",                 model = "prop_xmas_tree_int",             isFrozen = true, },
318 |     { item = "candycane",             label = "Candy Cane",                model = "w_me_candy_xm3",                 isFrozen = true, },
319 |     { item = "xmaspresent",           label = "Xmas Present",              model = "xm3_prop_xm3_present_01a",       isFrozen = true, },
320 | 
321 |     -- Misc props
322 |     { item = "greenscreen",           label = "Green Screen",              model = "prop_ld_greenscreen_01",         isFrozen = true, },
323 |     { item = "ropebarrier",           label = "Rope Barrier",              model = "vw_prop_vw_barrier_rope_01a",    isFrozen = false, },
324 |     { item = "largesoccerball",       label = "Large Soccer Ball",         model = "stt_prop_stunt_soccer_ball",     isFrozen = false, },
325 |     { item = "soccerball",            label = "Soccer Ball",               model = "p_ld_soc_ball_01",               isFrozen = false, },
326 |     { item = "stepladder",            label = "Step Ladder",               model = "v_med_cor_stepladder",           isFrozen = true, },
327 |     { item = "sexdoll",               label = "Sex Doll",                  model = "prop_defilied_ragdoll_01",       isFrozen = true, },
328 | 
329 |     -- Pushable items
330 |     { item = "shoppingcart1",         label = "Shopping Cart (Empty)",     model = "prop_rub_trolley01a",            isFrozen = false, customTargetOptions = pushAndSitTargetOptions, },
331 |     { item = "shoppingcart2",         label = "Shopping Cart (Full)",      model = "prop_skid_trolley_2",            isFrozen = false, customTargetOptions = pushTargetOptions, },
332 |     { item = "shoppingcart3",         label = "Shopping Cart (Empty)",     model = "prop_rub_trolley02a",            isFrozen = false, customTargetOptions = pushAndSitTargetOptions, },
333 |     { item = "shoppingcart4",         label = "Shopping Cart (Full)",      model = "prop_skid_trolley_1",            isFrozen = false, customTargetOptions = pushTargetOptions, },
334 |     {
335 |         item = "wheelbarrow",
336 |         label = "Wheelbarrow",
337 |         model = "prop_wheelbarrow01a",
338 |         isFrozen = false,
339 |         customTargetOptions = setCustomTargetOptions(
340 |             pushAndSitTargetOptions, {
341 |                 offset = { x = -0.4, y = -1.8, z = -0.6, },
342 |                 rotation = { x = 0.0, y = 20.0, z = 90.0, },
343 |                 animationDict = "missfinale_c2ig_11",
344 |                 animationName = "pushcar_offcliff_f",
345 |             }, {
346 |                 offset = { x = -0.25, y = 0.0, z = 1.4, },
347 |                 rotation = { x = 13.0, y = 0.0, z = 255.0, },
348 |                 animationDict = "anim@amb@business@bgen@bgen_no_work@",
349 |                 animationName = "sit_phone_phoneputdown_idle_nowork",
350 |             }
351 |         ),
352 |     },
353 |     {
354 |         item = "warehousetrolly1",
355 |         label = "Warehouse Trolly (Empty)",
356 |         model = "hei_prop_hei_warehousetrolly_02",
357 |         isFrozen = false,
358 |         customTargetOptions = setCustomTargetOptions(
359 |             pushAndSitTargetOptions, {
360 |                 offset = { x = -0.4, y = -1.5, z = -0.9, },
361 |                 rotation = { x = 0.0, y = 0.0, z = 180.0, },
362 |                 animationDict = "missfinale_c2ig_11",
363 |                 animationName = "pushcar_offcliff_f",
364 |             }, {
365 |                 offset = { x = -0.15, y = 0.15, z = 1.25, },
366 |                 rotation = { x = 0.0, y = 10.0, z = 175.0, },
367 |                 animationDict = "anim@amb@business@bgen@bgen_no_work@",
368 |                 animationName = "sit_phone_phoneputdown_idle_nowork",
369 |             }
370 |         ),
371 |     },
372 |     {
373 |         item = "warehousetrolly2",
374 |         label = "Warehouse Trolly (Full)",
375 |         model = "prop_flattruck_01d",
376 |         isFrozen = false,
377 |         customTargetOptions = setCustomTargetOptions(
378 |             pushTargetOptions, {
379 |                 offset = { x = -0.4, y = -1.5, z = -0.9, },
380 |                 rotation = { x = 0.0, y = 0.0, z = 180.0, },
381 |                 animationDict = "missfinale_c2ig_11",
382 |                 animationName = "pushcar_offcliff_f",
383 |             }
384 |         ),
385 |     },
386 |     {
387 |         item = "roomtrolly",
388 |         label = "Room Trolly",
389 |         model = "ch_prop_ch_room_trolly_01a",
390 |         isFrozen = false,
391 |         customTargetOptions = setCustomTargetOptions(
392 |             pushTargetOptions, {
393 |                 offset = { x = -0.4, y = -1.75, z = -0.8, },
394 |                 rotation = { x = 0.0, y = 0.0, z = 90.0, },
395 |                 animationDict = "missfinale_c2ig_11",
396 |                 animationName = "pushcar_offcliff_f",
397 |             }
398 |         ),
399 |     },
400 |     {
401 |         item = "janitorcart1",
402 |         label = "Janitor Cart",
403 |         model = "prop_cleaning_trolly",
404 |         isFrozen = false,
405 |         customTargetOptions = setCustomTargetOptions(
406 |             pushTargetOptions, {
407 |                 offset = { x = -0.3, y = -1.6, z = -0.9, },
408 |                 rotation = { x = 0.0, y = 0.0, z = 180.0, },
409 |                 animationDict = "missfinale_c2ig_11",
410 |                 animationName = "pushcar_offcliff_f",
411 |             }
412 |         ),
413 |     },
414 |     {
415 |         item = "janitorcart2",
416 |         label = "Janitor Cart",
417 |         model = "ch_prop_ch_trolly_01a",
418 |         isFrozen = false,
419 |         customTargetOptions = setCustomTargetOptions(
420 |             pushTargetOptions, {
421 |                 offset = { x = -0.3, y = -1.75, z = -0.3, },
422 |                 rotation = { x = 0.0, y = 0.0, z = 270.0, },
423 |                 animationDict = "missfinale_c2ig_11",
424 |                 animationName = "pushcar_offcliff_f",
425 |             }
426 |         ),
427 |     },
428 |     {
429 |         item = "mopbucket",
430 |         label = "Mop Bucket",
431 |         model = "prop_tool_mopbucket",
432 |         isFrozen = false,
433 |         customTargetOptions = setCustomTargetOptions(
434 |             pushTargetOptions, {
435 |                 offset = { x = -0.3, y = -1.9, z = -0.8, },
436 |                 rotation = { x = 0.0, y = 0.0, z = 270.0, },
437 |                 animationDict = "missfinale_c2ig_11",
438 |                 animationName = "pushcar_offcliff_f",
439 |             }
440 |         ),
441 |     },
442 |     {
443 |         item = "metalcart",
444 |         label = "Metal Cart",
445 |         model = "prop_gold_trolly",
446 |         isFrozen = false,
447 |         customTargetOptions = setCustomTargetOptions(
448 |             pushTargetOptions, {
449 |                 offset = { x = -0.4, y = -1.75, z = -0.35, },
450 |                 rotation = { x = 0.0, y = 0.0, z = 270.0, },
451 |                 animationDict = "missfinale_c2ig_11",
452 |                 animationName = "pushcar_offcliff_f",
453 |             }
454 |         ),
455 |     },
456 |     {
457 |         item = "teacart",
458 |         label = "Tea Cart",
459 |         model = "prop_tea_trolly",
460 |         isFrozen = false,
461 |         customTargetOptions = setCustomTargetOptions(
462 |             pushTargetOptions, {
463 |                 offset = { x = -0.4, y = -1.75, z = -0.4, },
464 |                 rotation = { x = 0.0, y = 0.0, z = 90.0, },
465 |                 animationDict = "missfinale_c2ig_11",
466 |                 animationName = "pushcar_offcliff_f",
467 |             }
468 |         ),
469 |     },
470 |     {
471 |         item = "drinkcart",
472 |         label = "Drink Cart",
473 |         model = "h4_int_04_drink_cart",
474 |         isFrozen = false,
475 |         customTargetOptions = setCustomTargetOptions(
476 |             pushTargetOptions, {
477 |                 offset = { x = -0.4, y = -1.75, z = -0.4, },
478 |                 rotation = { x = 0.0, y = 0.0, z = 90.0, },
479 |                 animationDict = "missfinale_c2ig_11",
480 |                 animationName = "pushcar_offcliff_f",
481 |             }
482 |         ),
483 |     },
484 |     {
485 |         item = "handtruck1",
486 |         label = "Hand Truck",
487 |         model = "prop_sacktruck_02a",
488 |         isFrozen = false,
489 |         customTargetOptions = setCustomTargetOptions(
490 |             pushTargetOptions, {
491 |                 offset = { x = -0.4, y = -1.4, z = -0.8, },
492 |                 rotation = { x = -35.0, y = 0.0, z = 180.0, },
493 |                 animationDict = "missfinale_c2ig_11",
494 |                 animationName = "pushcar_offcliff_f",
495 |             }
496 |         ),
497 |     },
498 |     {
499 |         item = "handtruck2",
500 |         label = "Hand Truck (Boxes)",
501 |         model = "prop_sacktruck_02b",
502 |         isFrozen = false,
503 |         customTargetOptions = setCustomTargetOptions(
504 |             pushTargetOptions, {
505 |                 offset = { x = -0.4, y = -1.4, z = -0.75, },
506 |                 rotation = { x = -35.0, y = 0.0, z = 180.0, },
507 |                 animationDict = "missfinale_c2ig_11",
508 |                 animationName = "pushcar_offcliff_f",
509 |             }
510 |         ),
511 |     },
512 |     {
513 |         item = "trashbin",
514 |         label = "Trash Bin",
515 |         model = "prop_cs_bin_01_skinned",
516 |         isFrozen = false,
517 |         customTargetOptions = setCustomTargetOptions(
518 |             pushAndSitTargetOptions, {
519 |                 offset = { x = -0.4, y = -1.62, z = -0.8, },
520 |                 rotation = { x = -15.0, y = 0.0, z = 180.0, },
521 |                 animationDict = "missfinale_c2ig_11",
522 |                 animationName = "pushcar_offcliff_f",
523 |             }, {
524 |                 offset = { x = 0.02, y = 0.15, z = 1.25, },
525 |                 rotation = { x = 0.0, y = 0.0, z = 175.0, },
526 |                 animationDict = "anim@model_kylie_insta",
527 |                 animationName = "kylie_insta_clip",
528 |             }
529 |         ),
530 |     },
531 |     {
532 |         item = "lawnmower",
533 |         label = "Lawn Mower",
534 |         model = "prop_lawnmower_01",
535 |         isFrozen = false,
536 |         customTargetOptions = setCustomTargetOptions(
537 |             pushTargetOptions, {
538 |                 offset = { x = -0.43, y = -1.6, z = -0.83, },
539 |                 rotation = { x = 0.0, y = 0.0, z = 180.0, },
540 |                 animationDict = "missfinale_c2ig_11",
541 |                 animationName = "pushcar_offcliff_f",
542 |             }
543 |         ),
544 |     },
545 |     {
546 |         item = "toolchest",
547 |         label = "Tool Chest",
548 |         model = "prop_toolchest_03",
549 |         isFrozen = false,
550 |         customTargetOptions = setCustomTargetOptions(
551 |             pushTargetOptions, {
552 |                 offset = { x = -0.35, y = -1.95, z = -0.83, },
553 |                 rotation = { x = 0.0, y = 0.0, z = 90.0, },
554 |                 animationDict = "missfinale_c2ig_11",
555 |                 animationName = "pushcar_offcliff_f",
556 |             }
557 |         ),
558 |     },
559 |     {
560 |         item = "carjack",
561 |         label = "Car Jack",
562 |         model = "prop_carjack",
563 |         isFrozen = false,
564 |         customTargetOptions = setCustomTargetOptions(
565 |             pushTargetOptions, {
566 |                 offset = { x = -0.35, y = -1.75, z = -0.83, },
567 |                 rotation = { x = 0.0, y = 0.0, z = 180.0, },
568 |                 animationDict = "missfinale_c2ig_11",
569 |                 animationName = "pushcar_offcliff_f",
570 |             }
571 |         ),
572 |     },
573 |     {
574 |         item = "hospitalbedtable",
575 |         label = "Hospital Bedside Table",
576 |         model = "v_med_bedtable",
577 |         isFrozen = false,
578 |         customTargetOptions = setCustomTargetOptions(
579 |             pushTargetOptions, {
580 |                 offset = { x = -0.35, y = -1.7, z = -0.35, },
581 |                 rotation = { x = 0.0, y = 0.0, z = 180.0, },
582 |                 animationDict = "missfinale_c2ig_11",
583 |                 animationName = "pushcar_offcliff_f",
584 |             }
585 |         ),
586 |     },
587 |     {
588 |         item = "medtable",
589 |         label = "Medical Table",
590 |         model = "v_med_trolley2",
591 |         isFrozen = false,
592 |         customTargetOptions = setCustomTargetOptions(
593 |             pushTargetOptions, {
594 |                 offset = { x = -0.35, y = -1.75, z = -0.83, },
595 |                 rotation = { x = 0.0, y = 0.0, z = 90.0, },
596 |                 animationDict = "missfinale_c2ig_11",
597 |                 animationName = "pushcar_offcliff_f",
598 |             }
599 |         ),
600 |     },
601 | 
602 |     -- ADDON ITEMS
603 | 
604 |     -- Yogamats
605 |     -- Uncomment this line if you are using wp-yogamats
606 |     -- {item = "yogamat_blue", label = "Yoga mat (Blue)", model = "prop_yoga_mat_01", isFrozen = true, customTargetOptions = yogaCustomTargetOptions},
607 |     -- {item = "yogamat_black", label = "Yoga mat (Black)", model = "prop_yoga_mat_02", isFrozen = true, customTargetOptions = yogaCustomTargetOptions},
608 |     -- {item = "yogamat_red", label = "Yoga mat (Red)", model = "prop_yoga_mat_03", isFrozen = true, customTargetOptions = yogaCustomTargetOptions},
609 | 
610 |     -- Printers
611 |     -- Uncomment this line if you are using wp-printer
612 |     -- {item = "printer", label = "Printer", model = "prop_printer_01", isFrozen = true, customTargetOptions = printerCustomTargetOptions},
613 |     -- {item = "printer2", label = "Printer", model = "prop_printer_02", isFrozen = true, customTargetOptions = printerCustomTargetOptions},
614 |     -- {item = "printer3", label = "Printer", model = "v_res_printer", isFrozen = true, customTargetOptions = printerCustomTargetOptions},
615 |     -- {item = "printer4", label = "Printer", model = "v_ret_gc_print", isFrozen = true, customTargetOptions = printerCustomTargetOptions},
616 |     -- {item = "photocopier", label = "Photocopier", model = "v_med_cor_photocopy", isFrozen = true, customTargetOptions = printerCustomTargetOptions},
617 | 
618 |     -- Fireworks
619 |     -- Uncomment this line if you are using wp-fireworks
620 |     -- {item = "finalefirework1",   label = "Finale Firework (White)", model = "bzzz_prop_fireworks_a", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
621 |     -- {item = "finalefirework2",   label = "Finale Firework (Colored)", model = "bzzz_prop_fireworks_a", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
622 |     -- {item = "finalefirework3",   label = "Finale Firework (USA)", model = "bzzz_prop_fireworks_a", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
623 |     -- {item = "finalefireworklong1",   label = "Long Finale Firework (White)", model = "bzzz_prop_fireworks_a", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
624 |     -- {item = "finalefireworklong2",   label = "Long Finale Firework (Colored)", model = "bzzz_prop_fireworks_a", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
625 |     -- {item = "finalefireworklong3",   label = "Long Finale Firework (USA)", model = "bzzz_prop_fireworks_a", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
626 |     -- {item = "fountainfirework1", label = "Fountain Firework (White)", model = "ind_prop_firework_03", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
627 |     -- {item = "fountainfirework2", label = "Fountain Firework (Colored)", model = "ind_prop_firework_03", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
628 |     -- {item = "fountainfirework3", label = "Fountain Firework (USA)", model = "ind_prop_firework_03", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
629 |     -- {item = "missilefirework1",  label = "Missile Firework (White)", model = "ind_prop_firework_04", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
630 |     -- {item = "missilefirework2",  label = "Missile Firework (Colored)", model = "ind_prop_firework_04", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
631 |     -- {item = "missilefirework3",  label = "Missile Firework (USA)", model = "ind_prop_firework_04", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
632 |     -- {item = "mortarfirework",    label = "Mortar Firework", model = "prop_poster_tube_02", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
633 |     -- {item = "strobefirework",    label = "Strobe Firework", model = "bzzz_prop_fireworks_b", isFrozen = true, customTargetOptions = fireworkCustomTargetOptions},
634 | 
635 |     -- Traffic lights
636 |     -- Uncomment this line if you are using wp-trafficlights
637 |     -- {item = "trafficlight", label= "Traffic light", model = "prop_traffic_03a", isFrozen = true, customTargetOptions = trafficLightCustomTargetOptions, customPickupEvent = trafficLightCustomPickupEvent},
638 | 
639 |     -- ADD YOUR CUSTOM PROPS HERE
640 | }
641 | 
--------------------------------------------------------------------------------
/shared/framework.lua:
--------------------------------------------------------------------------------
  1 | -- IsDuplicityVersion - is used to determine if the function is called by the server or the client (true == from server)
  2 | 
  3 | --------------------- SHARED FUNCTIONS ---------------------
  4 | local Core = nil
  5 | --- @return table Core The core object of the framework
  6 | function GetCoreObject()
  7 |     if not Core then
  8 |         if Config.Framework == "esx" then
  9 |             Core = exports["es_extended"]:getSharedObject()
 10 |         elseif Config.Framework == "qb" or Config.Framework == "qbx" then
 11 |             Core = exports["qb-core"]:GetCoreObject()
 12 |         end
 13 |     end
 14 |     return Core
 15 | end
 16 | 
 17 | Core = Config.Framework ~= "none" and GetCoreObject() or nil
 18 | 
 19 | --- @param text string The text to show in the notification
 20 | --- @param notificationType string The type of notification to show ex: 'success', 'error', 'info'
 21 | --- @param src - number|nil The source of the player - only required when called from server side
 22 | function Notify(text, notificationType, src)
 23 |     if IsDuplicityVersion() then
 24 |         if Config.Notify == "esx" then
 25 |             TriggerClientEvent("esx:showNotification", src, text)
 26 |         elseif Config.Notify == "qb" then
 27 |             TriggerClientEvent("QBCore:Notify", src, text, notificationType)
 28 |         elseif Config.Notify == "ox" then
 29 |             TriggerClientEvent("ox_lib:notify", src, {
 30 |                 description = text,
 31 |                 type = notificationType,
 32 |             })
 33 |         end
 34 |     else
 35 |         if Config.Notify == "esx" then
 36 |             Core.ShowNotification(text)
 37 |         elseif Config.Notify == "qb" then
 38 |             Core.Functions.Notify(text, notificationType)
 39 |         elseif Config.Notify == "ox" then
 40 |             lib.notify({
 41 |                 description = text,
 42 |                 type = notificationType,
 43 |             })
 44 |         end
 45 |     end
 46 | end
 47 | 
 48 | --- @param source number|nil The source of the player
 49 | --- @return table PlayerData The player data of the player
 50 | function GetPlayerData(source)
 51 |     local Core = GetCoreObject()
 52 |     if IsDuplicityVersion() then
 53 |         if Config.Framework == "esx" then
 54 |             -- TODO: does esx not use citizenId? need to find what it uses
 55 |             return Core.GetPlayerFromId(source)
 56 |         elseif Config.Framework == "qb" then
 57 |             return Core.Functions.GetPlayer(source).PlayerData
 58 |         elseif Config.Framework == "qbx" then
 59 |             return exports.qbx_core:GetPlayer(source).PlayerData
 60 |         end
 61 |     else
 62 |         if Config.Framework == "esx" then
 63 |             return Core.GetPlayerData()
 64 |         elseif Config.Framework == "qb" then
 65 |             return Core.Functions.GetPlayerData()
 66 |         elseif Config.Framework == "qbx" then
 67 |             return exports.qbx_core:GetPlayerData()
 68 |         end
 69 |     end
 70 | end
 71 | 
 72 | --------------------- CLIENT FUNCTIONS ---------------------
 73 | 
 74 | -- Triggers a progressbar on the client
 75 | -- This uses the same method signature as QBCore Progressbar
 76 | --- @param name string The name of the progressbar
 77 | --- @param label string The label to show on the progressbar
 78 | --- @param duration number The duration of the progressbar
 79 | --- @param useWhileDead boolean Whether or not the progressbar should be used while dead
 80 | --- @param canCancel boolean Whether or not the progressbar can be cancelled
 81 | --- @param disableControls table Contains the controls to disable while the progressbar is active (disableMovement, disableCarMovement, disableMouse, disableCombat)
 82 | --- @param animation table Contains the animation to play while the progressbar is active (animDict, anim, flags)
 83 | --- @param prop table Contains the prop to show while the progressbar is active (model, bone, coords, rotation)
 84 | --- @param propTwo table Contains the prop to show while the progressbar is active (model, bone, coords, rotation)
 85 | --- @param onFinish function The callback function to call when the progressbar finishes
 86 | --- @param onCancel function The callback function to call when the progressbar is cancelled
 87 | function Progressbar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
 88 |     if IsDuplicityVersion() then return end
 89 |     if Config.ProgessBar == "none" then
 90 |         onFinish()
 91 |     elseif Config.ProgessBar == "qb" then
 92 |         return Core.Functions.Progressbar(name, label, duration, useWhileDead, canCancel, disableControls, animation, prop, propTwo, onFinish, onCancel)
 93 |     elseif Config.ProgessBar == "ox" then
 94 |         local props = {}
 95 |         if prop then
 96 |             props[1] = {
 97 |                 model = prop.model,
 98 |                 bone = prop.bone,
 99 |                 coords = prop.coords,
100 |                 rotation = prop.rotation,
101 |             }
102 |         end
103 |         if propTwo then
104 |             props[2] = {
105 |                 model = propTwo.model,
106 |                 bone = propTwo.bone,
107 |                 coords = propTwo.coords,
108 |                 rotation = propTwo.rotation,
109 |             }
110 |         end
111 | 
112 |         if lib.progressBar({
113 |                 name = name,
114 |                 label = label,
115 |                 duration = duration,
116 |                 useWhileDead = useWhileDead,
117 |                 canCancel = canCancel,
118 |                 disable = {
119 |                     move = disableControls.disableMovement,
120 |                     car = disableControls.disableCarMovement,
121 |                     mouse = disableControls.disableMouse,
122 |                     combat = disableControls.disableCombat,
123 |                 },
124 |                 anim = {
125 |                     dict = animation.animDict,
126 |                     clip = animation.anim,
127 |                     flag = animation.flags,
128 |                 },
129 |                 prop = props,
130 |             }) then
131 |             onFinish()
132 |         else
133 |             onCancel()
134 |         end
135 |     else
136 |         warn("Invalid Config.ProgessBar: <" .. tostring(Config.ProgessBar) .. ">. Update Progressbar in framework.lua.")
137 |     end
138 | end
139 | 
140 | -- Takes a prop model and the targetOptions and adds it to the target script so that this prop model is targetable
141 | --- @param model string The name of the prop model to target
142 | --- @param targetOptions table The options to pass to the target script
143 | ---        ex: targetOptions = { distance = num, options: { offset = { x = 0.0, y = 0.0, z = 0.0 }, rotation = { x = 0.0, y = 0.0, z = 0.0 } , animationDict = "", animationName = "" } }
144 | function AddTargetModel(modelName, targetOptions)
145 |     if IsDuplicityVersion() then return end
146 |     if Config.Target == "qb" then
147 |         exports["qb-target"]:AddTargetModel(modelName, targetOptions)
148 |     elseif Config.Target == "ox" then
149 |         -- ox target expects each option to have a distance on it,
150 |         -- Append the distance value to each option
151 |         for _, option in pairs(targetOptions.options) do
152 |             option.distance = targetOptions.distance
153 |         end
154 |         exports.ox_target:addModel(modelName, targetOptions.options)
155 |     else
156 |         warn("Invalid Config.Target: <" .. tostring(Config.Target) .. ">. Update AddTargetModel in framework.lua.")
157 |     end
158 | end
159 | 
160 | -- Creates a log entry when an item is placed or picked up
161 | --- @param itemName string The name of the item that was placed or picked up
162 | --- @param isItemPlaced boolean True if the item was placed, false if the item was picked up
163 | function CreateLog(itemName, isItemPlaced)
164 |     if IsDuplicityVersion() or Config.Log == "none" then return end
165 | 
166 |     local playerId = PlayerId()
167 |     local playerName = GetPlayerName(playerId)
168 |     local PlayerData = GetPlayerData()
169 |     local color = isItemPlaced and "red" or "green"
170 |     local action = isItemPlaced and "Placed" or "Picked up"
171 |     local logMessage = "**" .. playerName .. "** (" .. PlayerData.charinfo.firstname .. " " .. PlayerData.charinfo.lastname .. ") | CitizenId: " .. PlayerData.citizenid .. "\n Item Name: " .. itemName
172 | 
173 |     if Config.Log == "none" then
174 |         return
175 |     elseif Config.Log == "qb" then
176 |         -- Be sure to add the 'itemplacement' entry to the qb-log config
177 |         TriggerServerEvent("qb-log:server:CreateLog", "itemplacement", "Item " .. action .. " By:", color, logMessage)
178 |     else
179 |         warn("Invalid Config.Log: <" .. tostring(Config.Log) .. ">. Update CreateLog in framework.lua.")
180 |     end
181 | end
182 | 
183 | --------------------- SERVER FUNCTIONS ---------------------
184 | 
185 | -- Registers a useable item
186 | --- @param itemName string The name of the item to register
187 | --- @param callbackFn function The function to call when the item is used
188 | function CreateUseableItem(itemName, callbackFn)
189 |     if not IsDuplicityVersion() then return end
190 |     if Config.Framework == "esx" then
191 |         -- ESX returns the itemName as the second parameter, itemdata as the third parameter when calling the callback function
192 |         -- We are interested in the itemData for our callback
193 |         local function ESXCallback(source, itemName, itemData)
194 |             callbackFn(source, itemData)
195 |         end
196 | 
197 |         return Core.RegisterUsableItem(itemName, ESXCallback)
198 |     elseif Config.Framework == "qb" then
199 |         return Core.Functions.CreateUseableItem(itemName, callbackFn)
200 |     elseif Config.Framework == "qbx" then
201 |         return exports.qbx_core:CreateUseableItem(itemName, callbackFn)
202 |     else
203 |         warn("Invalid Config.Framework: <" .. tostring(Config.Framework) .. ">. Update CreateUseableItem in framework.lua.")
204 |     end
205 | end
206 | 
207 | -- Adds item to the players inventory
208 | --- @param source number The source of the player
209 | --- @param itemName string The name of the item to add
210 | --- @param amount number The amount of the item to add
211 | --- @param info table Metadata to add to the item
212 | function AddItem(source, itemName, amount, info)
213 |     if not IsDuplicityVersion() then return end
214 |     if Config.Inventory == "esx" then
215 |         local xPlayer = Core.GetPlayerFromId(source)
216 |         return xPlayer.addInventoryItem(itemName, amount)
217 |     elseif Config.Inventory == "qb" then
218 |         local Player = Core.Functions.GetPlayer(source)
219 |         TriggerClientEvent("inventory:client:ItemBox", source, Core.Shared.Items[itemName], "add")
220 |         return Player.Functions.AddItem(itemName, amount, nil, info)
221 |     elseif Config.Inventory == "ox" then
222 |         exports.ox_inventory:AddItem(source, itemName, amount, info)
223 |     else
224 |         warn("Invalid Config.Inventory: <" .. tostring(Config.Inventory) .. ">. Update AddItem in framework.lua.")
225 |     end
226 | end
227 | 
228 | -- Removes item from the players inventory
229 | --- @param source number The source of the player
230 | --- @param itemName string The name of the item to remove
231 | --- @param amount number The number of items to remove
232 | --- @param slot number The slot of the item to remove from
233 | function RemoveItem(source, itemName, amount, slot)
234 |     if not IsDuplicityVersion() then return end
235 |     if Config.Inventory == "esx" then
236 |         local xPlayer = Core.GetPlayerFromId(source)
237 |         return xPlayer.removeInventoryItem(itemName, amount)
238 |     elseif Config.Inventory == "qb" then
239 |         local Player = Core.Functions.GetPlayer(source)
240 |         TriggerClientEvent("inventory:client:ItemBox", source, Core.Shared.Items[itemName], "remove")
241 |         return Player.Functions.RemoveItem(itemName, amount, slot)
242 |     elseif Config.Inventory == "ox" then
243 |         exports.ox_inventory:RemoveItem(source, itemName, amount, nil, slot)
244 |     else
245 |         warn("Invalid Config.Inventory: <" .. tostring(Config.Inventory) .. ">. Update RemoveItem in framework.lua.")
246 |     end
247 | end
248 | 
249 | --- This function checks if any OX scripts are being used in the configuration
250 | --- and throws an error if `ox_lib` is not properly enabled in the `fxmanifest.lua`.
251 | --- 
252 | --- **Usage**: Call this function on the server inside an `onResourceStart` event handler.
253 | --- 
254 | --- **Example**:
255 | --- ```lua
256 | --- AddEventHandler("onResourceStart", function(resourceName)
257 | ---     if GetCurrentResourceName() == resourceName then
258 | ---         Wait(100) -- Give the script some time to start
259 | ---         ValidateOxLibUsage()
260 | ---     end
261 | --- end)
262 | --- ```
263 | function ValidateOxLibUsage()
264 |     if not IsDuplicityVersion() then return end
265 | 
266 |     local isUsingOxScripts =
267 |         Config.Notify == "ox"
268 |         or Config.Target == "ox"
269 |         or Config.Inventory == "ox"
270 |         or Config.ProgessBar == "ox"
271 | 
272 |     -- Ensure ox_lib is not commented out in the fxmanifest/shared_script section if any OX scripts are used in the Config.
273 |     -- If ox_lib is commented out, display an error as the script will not function correctly.
274 |     if isUsingOxScripts then
275 |         local filePath = GetResourcePath(GetCurrentResourceName()) .. "/fxmanifest.lua"
276 |         local file, _errorMsg = io.open(filePath, "r")
277 |         if not file then return end
278 | 
279 |         -- Read through the fxmanifest file
280 |         -- Find the line with "@ox_lib/init.lua" and check if it is commented out
281 |         local isOxLibCommentedOut = false
282 |         for line in file:lines() do
283 |             if line:find("@ox_lib/init.lua") then
284 |                 -- Check if the line is commented out
285 |                 if line:match("^%s*%-%-") then
286 |                     isOxLibCommentedOut = true
287 |                 end
288 |                 break
289 |             end
290 |         end
291 | 
292 |         file:close()
293 | 
294 |         if isOxLibCommentedOut then
295 |             error(
296 |                 "\n=====================================\n\n" ..
297 | 
298 |                 "YOU ARE USING OX SCRIPTS AND DID NOT UNCOMMENT OX_LIB IN THE FXMANIFEST!\n\n" ..
299 | 
300 |                 "The script will not work until you uncomment it from the fxmanifest.\n\n" ..
301 | 
302 |                 "=====================================\n"
303 |             )
304 |         end
305 |     end
306 | end
--------------------------------------------------------------------------------