diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..3d90694ade --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/data/CPUChip/amount.txt b/data/CPUChip/amount.txt new file mode 100644 index 0000000000..f8250dfb3b --- /dev/null +++ b/data/CPUChip/amount.txt @@ -0,0 +1,44 @@ +//Usage (in & out): +//Port0 - Air amount (output in percent) +//Port1 - Coolant amount (output in percent) +//Port2 - Energy amount (output in percent) +//Port3 - Pressure (output in KPA) +//Port4 - Temperature (output in Celsiums) +//Change these values +define MAXAIR,12000; +define MAXCOOLANT,12000; +define MAXENERGY,12000; +mainloop: + //Air% = (Air / MaxAmount) * 100 + mov eax,port0; + div eax,MAXAIR; //MAXIMUM AMOUNT OF AIR + mul eax,100; + int eax; inc eax; + mov port0,eax; + + //Coolant% = (Coolant / MaxAmount) * 100 + mov eax,port1; + div eax,MAXCOOLANT; //MAXIMUM AMOUNT OF COOLANT + mul eax,100; + int eax; inc eax; + mov port1,eax; + + //Energy% = (Energy / MaxAmount) * 100 + mov eax,port2; + div eax,MAXENERGY; //MAXIMUM AMOUNT OF ENERGY + mul eax,100; + int eax; inc eax; + mov port2,eax; + + //Pressure(KPA) = (Pressure * 101325) / 1000 + mov eax,port3; + mul eax,101325; + div eax,1000; + mov port3,eax; + + //Temperature C* = K - 273 + mov eax,port4; + sub eax,273; + mov port4,eax; + + jmp mainloop; diff --git a/data/CPUChip/bball.txt b/data/CPUChip/bball.txt new file mode 100644 index 0000000000..76015dfab1 --- /dev/null +++ b/data/CPUChip/bball.txt @@ -0,0 +1,78 @@ +//Bouncing ball for digital screen (Hi-speed) +DATA; + alloc bx; + alloc by; + alloc vx; + alloc vy; +CODE; + mov #bx,15; + mov #by,15; + //Experiment with velocity: + mov #vx,1; + mov #vy,1; + call bScreenErase; + //Main loop + mainloop: + //Erase old ball + mov ecx,0; + call bPaintBall; + + //Move ball using velocity + add #bx,#vx; + add #by,#vy; + //Bounce against X walls + cmp #bx,0; + cle bReverseX; + cmp #bx,31; + cge bReverseX; + //Bounce against Y walls + cmp #by,0; + cle bReverseY; + cmp #by,31; + cge bReverseY; + + //Paint new ball + mov ecx,255; + call bPaintBall; + jmp mainloop; +//---------------------------------------------------- +// Calculates ball VRAM address +// Result in EAX +//---------------------------------------------------- +bCalcAddress: + mov eax,#by; + mul eax,32; + add eax,#bx; + add eax,65536; //VRAM Offset + ret +//---------------------------------------------------- +// Erases the screen +//---------------------------------------------------- +bScreenErase: + mov edi,65536; + scrEraseLoop: + mov #edi,0; + inc edi; + cmp edi,66559; + jle scrEraseLoop; + ret +//---------------------------------------------------- +// Reverse ball X (Short, but easy) +//---------------------------------------------------- +bReverseX: + neg #vx; + ret +//---------------------------------------------------- +// Reverse ball Y (Short, but easy) +//---------------------------------------------------- +bReverseY: + neg #vy; + ret +//---------------------------------------------------- +// Paint ball +// Paints ball in color stored in ECX +//---------------------------------------------------- +bPaintBall: + call bCalcAddress; + mov #eax,ecx; + ret diff --git a/data/CPUChip/digicam.txt b/data/CPUChip/digicam.txt new file mode 100644 index 0000000000..5c96e11dc2 --- /dev/null +++ b/data/CPUChip/digicam.txt @@ -0,0 +1,37 @@ +//Digital camera using ports +//Outputs: +//PORT0 - Ranger X +//PORT1 - Ranger Y +//PORT5 - Digital screen X +//PORT6 - Digital screen Y +//PORT7 - Digital screen G (gray) +//Inputs: +//PORT0 - Ranger data +define MAXRANGE,1024; //Ranger range +mov eax,0; //EAX = 0 +mov ebx,0; //EBX = 0 +loopx: //Loop X value + mov ebx,0; //EBX = 0 + loopy: //Loop Y value + mov port5,eax; //Port5 = EAX (X) + mov ecx,port0; //ECX = PORT0 + div ecx,MAXRANGE; //ECX = ECX / MAXRANGE + mul ecx,255; //ECX = ECX * 255 + neg ecx; //ECX = -ECX + add ecx,255; //ECX = ECX + 255 + mov port7,ecx; //PORT7 = ECX (ECX = 255-(PORT0 / MAXRANGE)*255) + mov port6,ebx; //PORT6 = EBX (Y) + mov ecx,eax; //ECX = EAX + div ecx,16; //ECX = ECX / 16 - 1 + sub ecx,1; + mov edx,ebx; //ECX = EBX + div edx,16; //EDX = EDX / 16 - 1 + sub edx,1; + mov port0,ecx; //Ranger X = ECX + mov port1,edx; //Ranger Y = EDX + inc ebx; //Increase Y + cmp ebx,32; //Compare to 32 + jl loopy; //If less then loop + inc eax; //Increase X + cmp eax,32; //Compare to 32 + jl loopx; //If less then loop diff --git a/data/CPUChip/elev.txt b/data/CPUChip/elev.txt new file mode 100644 index 0000000000..46e03a489a --- /dev/null +++ b/data/CPUChip/elev.txt @@ -0,0 +1,69 @@ +//Working multifloor elevator +//Ports: +//IN: +//PORT0 - Button pressed, reversed +//PORT1 - Elevator speed +//OUT: +//PORT0 - Motor +//PORT5 - Current floor number +//PORT6 - Moving LED, and elevator door status +//PORT7 - Floor door open status + +//////// Data segment +DATA; //Allocate data segment (Everything after it + // and before CODE; is allocated in data segment) + alloc floor; //Create variable named "floor" and set it to 0 + alloc z; //Create variable named "z" and set it to 0 + alloc destz; //Create variable named "destz" and set it to 0 +//////// Code segment & initialization routine +CODE; //Initialize code segment (Everything below is code) + mov #z,0; //Store 0 into variable named Z (# means we write to memory) + mov port0,#z; //Store variable Z into port0 (# means we read from memory) + mov port5,1; //Store 1 into port 5 (we are on floor 1) + mov port7,1; //Store 1 into port 7 (doors are open) + mov #floor,1; //Store 1 into floor counter +//////// Read for button presses + btnloop: //Create label named "btnloop" + mov eax,port0; //Store port0 into EAX general-purpose register + cmp eax,0; //Compare EAX with 0 + je btnloop; //If EAX is greater than 0 then jump to label "btnloop" +//////// Choose movement direction + mov port6,1; //EAX > 0 means some buttons was pressed. Store 1 into moving led port + mov port7,0; //Store 0 into port 7 (close doors) + mul eax,256; //Multiply pressed button number by 256 + sub eax,256; //Subtract 256 from pressed button number + mov #destz,eax; //Target elevator height - (ButtonNumber - 1) * 256 + cmp #destz,#z; //Compare value in "destz" with value in "z" + je dontmove; //If its equal, then dont move. Jump over movement + jl movedown; //If its not equal, and less then jump to move down code +//////// Move up + moveup: //If its greater than jump to move up code + add #z,port1; //Add elevator speed to Z value + mov port0,#z; //Set elevator height to the new Z value + call calcfloor; //Calculate & show floor number + cmp #destz,#z; //Compare, are we done moving yet + jg moveup; //We are moving up, and destanation > current Z. Continue moving + jmp dontmove; //We are on the floor. Jump over move down code +//////// Move down + movedown: //Move down code + sub #z,port1; //Subtract speed from Z value + mov port0,#z; //Set the new elevator height + call calcfloor; //Calculate & show floor number + cmp #destz,#z; //Compare destanation Z with our Z + jl movedown; //If its under us then continue moving +//////// Finished moving + dontmove: //We finished moving/already at floor + mov #z,#destz; //Store destz into z to prevent mistakes with floor height + mov port7,1; //Open doors on floor (previously calculated with calcfloor + mov port6,0; //Turn off moving LED and open elevator doors + jmp btnloop; //Jump and wait for keypress again + +//////// public CalcFloor() +calcfloor: //FloorNumber = CalcFloor() + mov eax,#z; //Store current height into EAX + add eax,256; //Floor number = math.Round((z+256) / 256) + div eax,256; //Divide... + rnd eax; //Round up value in EAX, and store it back into EAX + mov #floor,eax; //Store floor number + mov port5,eax; //Display the floor number + ret //Return from function \ No newline at end of file diff --git a/data/CPUChip/helloworld.txt b/data/CPUChip/helloworld.txt new file mode 100644 index 0000000000..74b4e331f9 --- /dev/null +++ b/data/CPUChip/helloworld.txt @@ -0,0 +1,42 @@ +//Wired Hello World! +//Connect CPU membus input to console screen +//Connect CPUs CLK input to button (toggle) +//Notice how you can store your +//subroutines/calls in DATA area +DATA; +message: + db 'Hello World!',0; +WriteString: //ESI - String pointer, EDX - Param + mov eax,65536; + AWriteLoop: + cmp #esi,0; //Terminate on char 0 + je AEnd; + mov #eax,#esi; //Output char + inc eax; + mov #eax,edx; //Output char param + inc eax; + inc esi; + jmp AWriteLoop; + AEnd: + ret //Return from call + +CODE; + mov esi,message; + mov edx,000999; //White foreground on black background + call WriteString; + +//More about colors: +//Lower 3 digits are foreground, +//and higher 3 digits are background +//Each of 3 digits shows amount of +//RED, GREEN, and BLUE (in order) +//Each color has 10 shades - from 0 to 9 +// +//For example, 999044 will be dark yellow (044) on +//a white background (999) +// +//Experiment with colors! +// +//Also, the 7th digit (if its not equal to 0) will +//cause the character to blink by changing foreground and +//background places (actual data in memory wont change) \ No newline at end of file diff --git a/data/CPUChip/screentest.txt b/data/CPUChip/screentest.txt new file mode 100644 index 0000000000..3365b10fbd --- /dev/null +++ b/data/CPUChip/screentest.txt @@ -0,0 +1,22 @@ +// Screen test using ports +// Spawn 1 cpu, 1 digital screen, and 1 input. +// Attach CPUs clock input to keypad input. +// Attach digital screens clock input to keypad input. +// Attach digital screens pixelg input to CPUs port2 output. +// Attach digital screens pixelx input to CPUs port0 output. +// Attach digital screens pixely input to CPUs port1 output. +mov eax,0; // EAX = 0 +loopx: // Loop #1 (X-axis) + mov ebx,0; // EBX = 0 + loopy: // Loop #2 (Y-axis) + mov port0,eax; // Port[0] = EAX + mov port1,ebx; // Port[1] = EBX + mov edx,eax; // EDX = EAX + mul edx,ebx; // EDX = EDX*EBX (aka EDX = EAX*EBX) + mov port2,edx; // Port[2] = EDX (aka Port[2] = EAX*EBX) + inc ebx; // EBX = EBX + 1 + cmp ebx,32; // Compare EBX and 32 + jl loopy; // Jump if less than 32 + inc eax; // EAX = EAX + 1 + cmp eax,32; // Compare EAX and 32 + jl loopx; // Jump if less than 32 \ No newline at end of file diff --git a/data/CPUChip/screentest2.txt b/data/CPUChip/screentest2.txt new file mode 100644 index 0000000000..2c4a225fa8 --- /dev/null +++ b/data/CPUChip/screentest2.txt @@ -0,0 +1,22 @@ +// Screen test using hi-speed link +// Spawn 1 cpu, 1 digital screen, and 1 button (toggle). +// Attach CPUs clock input to button. +// Attach digital screens clock input to button. +// Attach CPU's MemBus input to digital screen +mov eax,0; // EAX = 0 +loopx: // Loop #1 (X-axis) + mov ebx,0; // EBX = 0 + loopy: // Loop #2 (Y-axis) + mov edi,ebx; // EDI - Screen Byte Number + mul edi,32; // EDI = Y * 32 + X + add edi,eax; // EDI = EDI + 65536 (Because screen is linked + add edi,65536; // to top of CPU ram, i.e. 65536 and so on) + mov edx,eax; // EDX = EAX + mul edx,ebx; // EDX = EDX*EBX (aka EDX = EAX*EBX) + mov #edi,edx; // Screen[EDI] = EDX (Screen's byte EDI) + inc ebx; // EBX = EBX + 1 + cmp ebx,32; // Compare EBX and 32 + jl loopy; // Jump if less than 32 + inc eax; // EAX = EAX + 1 + cmp eax,32; // Compare EAX and 32 + jl loopx; // Jump if less than 32 \ No newline at end of file diff --git a/data/CPUChip/speedtest.txt b/data/CPUChip/speedtest.txt new file mode 100644 index 0000000000..b57416766c --- /dev/null +++ b/data/CPUChip/speedtest.txt @@ -0,0 +1,8 @@ +//SpeedTest - Outputs average number of +//CPU clocks passed +//Attach data port to IOBus of CPU +mov eax,1; +infloop: + add eax,3; //3 cycles for the loop + mov port0,eax; +jmp infloop; \ No newline at end of file diff --git a/data/Expression2/_helloworld_.txt b/data/Expression2/_helloworld_.txt new file mode 100644 index 0000000000..173bfcb3c0 --- /dev/null +++ b/data/Expression2/_helloworld_.txt @@ -0,0 +1,24 @@ +@name Hello World +@inputs A B +@outputs Add Sub Mul Div +@outputs GreaterThan Highest Lowest +@outputs Vector:vector +@persist D +@trigger all + +Add = A + B +Sub = A - B +Mul = A * B +Div = A / B + +GreaterThan = A > B + +if(A > B) { + Highest = A, Lowest = B +} else { + Highest = B, Lowest = A +} + +Vector = vec(A, B, 0) +Vector = Vector + vec(0, 0, A + B) +Vector = Vector:normalized() \ No newline at end of file diff --git a/data/ExpressionGate/examples/advsmoother.txt b/data/ExpressionGate/examples/advsmoother.txt new file mode 100644 index 0000000000..45706154a3 --- /dev/null +++ b/data/ExpressionGate/examples/advsmoother.txt @@ -0,0 +1,29 @@ +N@Advanced Smoother +I@Target Speed Acceleration +O@Value Active +# Activate when target has been set +~Target -> Active = 1; + +# Main computation loop +first() | clk() -> +# Precomputation of inputs + !Acceleration -> Acceleration = Speed * 50 * 2; + AccLength = Speed^2 / Acceleration / 2 + AccRate = min(Speed, Acceleration / 50) +# Precomputation of variables + Distance = Target - Value + Direction = Distance >= 0 ? 1 : -1 +# Calculate ideal speed modifier + IdealRate = Speed * Direction + abs(Distance) <= AccLength -> + IdealRate *= sqrt(abs(Distance - Rate / 50) / AccLength); +# Calculate final speed for iteration + Rate += clamp(IdealRate - Rate, -Acceleration / 50, Acceleration / 50) + Value += Rate / 50 +# Check if value has reached target + Active = abs(Rate) > AccRate + | abs(Distance) > AccRate / 50 + !Active -> Rate = 0, Value = Target;; + +# Schedule the main loop if active +Active -> interval(20); diff --git a/data/ExpressionGate/examples/convert.txt b/data/ExpressionGate/examples/convert.txt new file mode 100644 index 0000000000..66593e3cae --- /dev/null +++ b/data/ExpressionGate/examples/convert.txt @@ -0,0 +1,14 @@ +N@Speed Converter +I@Units +O@Kmh Mph Ms Kt +# Kilometers per hour +Kmh = Units / 10.936133 + +# Miles per hour +Mph = Units / 17.600000 + +# Meters per second +Ms = Units / 39.370079 + +# Knots +Kt = Units / 20.253718 \ No newline at end of file diff --git a/data/ExpressionGate/examples/duplex/duplexer.txt b/data/ExpressionGate/examples/duplex/duplexer.txt new file mode 100644 index 0000000000..b908551b28 --- /dev/null +++ b/data/ExpressionGate/examples/duplex/duplexer.txt @@ -0,0 +1,29 @@ +N@Duplexer +I@A B C D E F G H Link +O@A B C D E F G H Link +# Merge multiple values into one wire +!~Link -> + TmpLink = send(A, B, C, D, E, F, G, H); + +# Split a wire coming from a merger +~Link -> + TmpA = recv(Link, 1) + TmpB = recv(Link, 2) + TmpC = recv(Link, 3) + TmpD = recv(Link, 4) + TmpE = recv(Link, 5) + TmpF = recv(Link, 6) + TmpG = recv(Link, 7) + TmpH = recv(Link, 8); + +# Refresh the output variables +A = TmpA +B = TmpB +C = TmpC +D = TmpD +E = TmpE +F = TmpF +G = TmpG +H = TmpH + +Link = TmpLink diff --git a/data/ExpressionGate/examples/duplex/merger.txt b/data/ExpressionGate/examples/duplex/merger.txt new file mode 100644 index 0000000000..8e0dcb82c8 --- /dev/null +++ b/data/ExpressionGate/examples/duplex/merger.txt @@ -0,0 +1,5 @@ +N@Merger +I@A B C D E F G H +O@Link +# Merge multiple values into one wire +Link = send(A, B, C, D, E, F, G, H) diff --git a/data/ExpressionGate/examples/duplex/splitter.txt b/data/ExpressionGate/examples/duplex/splitter.txt new file mode 100644 index 0000000000..d25264b74a --- /dev/null +++ b/data/ExpressionGate/examples/duplex/splitter.txt @@ -0,0 +1,12 @@ +N@Splitter +I@Link +O@A B C D E F G H +# Split a wire coming from a merger +A = recv(Link, 1) +B = recv(Link, 2) +C = recv(Link, 3) +D = recv(Link, 4) +E = recv(Link, 5) +F = recv(Link, 6) +G = recv(Link, 7) +H = recv(Link, 8) diff --git a/data/ExpressionGate/examples/helloworld.txt b/data/ExpressionGate/examples/helloworld.txt new file mode 100644 index 0000000000..ab53567bc9 --- /dev/null +++ b/data/ExpressionGate/examples/helloworld.txt @@ -0,0 +1,12 @@ +N@Hello World +I@A B +O@Plus Minus Times Divided PowerOf Remainder Logarithm +# Very basic example for beginners +# Documentation at wiremod.com +Plus = A + B +Minus = A - B +Times = A * B +Divided = A / B +PowerOf = A ^ B +Remainder = A % B +Logarithm = log(A, B) diff --git a/data/ExpressionGate/examples/outdated/clock.txt b/data/ExpressionGate/examples/outdated/clock.txt new file mode 100644 index 0000000000..3a36433975 --- /dev/null +++ b/data/ExpressionGate/examples/outdated/clock.txt @@ -0,0 +1,10 @@ +N@clock +I@Tick Step Reset +O@Hours Minutes Seconds +# Wire a Pulser with TickTime 1 to Tick +~Tick & Tick -> Clock += 1; +~Step & Step -> Clock += Step; +~Reset & Reset -> Clock = 0; +Seconds = Clock % 60 +Minutes = floor(Clock / 60) % 60 +Hours = floor(Clock / 3600) % 24 diff --git a/data/ExpressionGate/examples/outdated/conditional.txt b/data/ExpressionGate/examples/outdated/conditional.txt new file mode 100644 index 0000000000..6ea595d865 --- /dev/null +++ b/data/ExpressionGate/examples/outdated/conditional.txt @@ -0,0 +1,12 @@ +N@Example Conditional +I@Speed Turbo +O@Speed Limited +Limited = 0 +Speed < 0 -> + Speed = 0, Limited = 1; +Speed > 200 -> + !Turbo -> + Speed = 200, Limited = 1; + Turbo & Speed > 400 -> + Speed = 400, Limited = 2; + ; diff --git a/data/ExpressionGate/examples/outdated/interface.txt b/data/ExpressionGate/examples/outdated/interface.txt new file mode 100644 index 0000000000..79f7d90e1d --- /dev/null +++ b/data/ExpressionGate/examples/outdated/interface.txt @@ -0,0 +1,4 @@ +N@Interface +I@Speed Reset Distance +# Example use of the gate to prettify a module +Speed /= 60 diff --git a/data/ExpressionGate/examples/outdated/latch.txt b/data/ExpressionGate/examples/outdated/latch.txt new file mode 100644 index 0000000000..8535d4ac48 --- /dev/null +++ b/data/ExpressionGate/examples/outdated/latch.txt @@ -0,0 +1,4 @@ +N@Latch +I@Hold Data +O@Out +!Hold -> Out = Data; diff --git a/data/ExpressionGate/examples/outdated/vehicle.txt b/data/ExpressionGate/examples/outdated/vehicle.txt new file mode 100644 index 0000000000..57747a3ded --- /dev/null +++ b/data/ExpressionGate/examples/outdated/vehicle.txt @@ -0,0 +1,10 @@ +N@Vehicle Stats +I@X Y Z Reset +O@Speed Acceleration TotalDistance +# Wire a Speedometer to the inputs +SMOOTH = 10 +Speed = sqrt(X^2 + Y^2 + Z^2) +Acceleration = Acceleration * (1 - 1 / SMOOTH) + (Speed - SpeedPrev) * (1 / SMOOTH) +TotalDistance += Speed +SpeedPrev = Speed +Reset -> TotalDistance = 0; diff --git a/data/ExpressionGate/examples/stopwatch.txt b/data/ExpressionGate/examples/stopwatch.txt new file mode 100644 index 0000000000..e859ba0921 --- /dev/null +++ b/data/ExpressionGate/examples/stopwatch.txt @@ -0,0 +1,26 @@ +N@Stopwatch +I@Start Stop Reset +O@Current Lap Record +# Increment time for each clock pulse +clk() -> Time += 0.02, schedule(20); + +# Start the clock, also restart +~Start & Start -> + Running & (Time < Record | !Record) -> + Record = Time; + Lap = Time, Time = 0 + Running = 1, schedule(20); + +# Stop the clock and update times +~Stop & Stop & Running -> + Lap = Time + Time < Record | !Record -> + Record = Time; + Running = 0, schedule(0); + +# Reset all values and stop the clock +Reset -> + Time = Lap = Record = 0 + Running = 0, schedule(0); + +Current = Time diff --git a/data/ExpressionGate/examples/uptime.txt b/data/ExpressionGate/examples/uptime.txt new file mode 100644 index 0000000000..b9f22e3e92 --- /dev/null +++ b/data/ExpressionGate/examples/uptime.txt @@ -0,0 +1,14 @@ +N@Server Uptime +O@Days Hours Minutes Seconds +# Retrieve server uptime +Time = curtime() + +# Synchronize timing with clock edge +interval(1000) +!Time | !$Time -> interval(1); + +# Compute time output parts +Seconds = floor(Time % 60) +Minutes = floor(Time / 60) % 60 +Hours = floor(Time / 3600) % 24 +Days = floor(Time / 86400) diff --git a/data/ExpressionGate/selfaware/angular.txt b/data/ExpressionGate/selfaware/angular.txt new file mode 100644 index 0000000000..910406e3e5 --- /dev/null +++ b/data/ExpressionGate/selfaware/angular.txt @@ -0,0 +1,6 @@ +N@Angular Speedometer +O@Roll Yaw Pitch +interval(1000/60) +Roll = extangvelr() +Yaw = extangvely() +Pitch = extangvelp() diff --git a/data/ExpressionGate/selfaware/color.txt b/data/ExpressionGate/selfaware/color.txt new file mode 100644 index 0000000000..f467184b99 --- /dev/null +++ b/data/ExpressionGate/selfaware/color.txt @@ -0,0 +1,7 @@ +N@Color Outputter +O@R G B A +interval(1000/60) +R = extcolorr() +G = extcolorg() +B = extcolorb() +A = extcolora() diff --git a/data/ExpressionGate/selfaware/direction.txt b/data/ExpressionGate/selfaware/direction.txt new file mode 100644 index 0000000000..bc8b1baab8 --- /dev/null +++ b/data/ExpressionGate/selfaware/direction.txt @@ -0,0 +1,15 @@ +N@Vectorized Gyroscope +O@FX FY FZ RX RY RZ UX UY UZ +interval(1000/60) +# Forward vector +FX = extdirfwx() +FY = extdirfwy() +FZ = extdirfwz() +# Right vector +RX = extdirrtx() +RY = extdirrty() +RZ = extdirrtz() +# Up vector +UX = extdirupx() +UY = extdirupy() +UZ = extdirupz() \ No newline at end of file diff --git a/data/ExpressionGate/selfaware/fade.txt b/data/ExpressionGate/selfaware/fade.txt new file mode 100644 index 0000000000..a1d01f25e8 --- /dev/null +++ b/data/ExpressionGate/selfaware/fade.txt @@ -0,0 +1,5 @@ +N@Color Fade +O@NULL +interval(1000/60) +Color += 12 +extcolor(abs(Color % 510 - 255), 0, 0, 255) diff --git a/data/ExpressionGate/selfaware/gps.txt b/data/ExpressionGate/selfaware/gps.txt new file mode 100644 index 0000000000..4ff26d36cd --- /dev/null +++ b/data/ExpressionGate/selfaware/gps.txt @@ -0,0 +1,6 @@ +N@GPS +O@X Y Z +interval(1000/60) +X = extposx() +Y = extposy() +Z = extposz() diff --git a/data/ExpressionGate/selfaware/gyro.txt b/data/ExpressionGate/selfaware/gyro.txt new file mode 100644 index 0000000000..f728b06c89 --- /dev/null +++ b/data/ExpressionGate/selfaware/gyro.txt @@ -0,0 +1,6 @@ +N@Gyroscope +O@Roll Yaw Pitch +interval(1000/60) +Roll = extangr() +Yaw = extangy() +Pitch = extangp() diff --git a/data/ExpressionGate/selfaware/speedo.txt b/data/ExpressionGate/selfaware/speedo.txt new file mode 100644 index 0000000000..eb4ab6bc20 --- /dev/null +++ b/data/ExpressionGate/selfaware/speedo.txt @@ -0,0 +1,6 @@ +N@Speedometer +O@X Y Z +interval(1000/60) +X = extvelx() +Y = extvely() +Z = extvelz() diff --git a/data/GPUChip/bounce.txt b/data/GPUChip/bounce.txt new file mode 100644 index 0000000000..84c4896b3e --- /dev/null +++ b/data/GPUChip/bounce.txt @@ -0,0 +1,65 @@ +////////////////////////////////// +// BOUNCING BALL GPU EXAMPLE // +////////////////////////////////// +dentrypoint 0,_draw; // Set draw start entrypoint to "_draw" + // +rand #ball.x; // Set random ball start point +rand #ball.y; // + // +dexit; // Exit the initialization routine... +////////////////////////////////// +_draw: // Entrypoint for the drawing function + // +dcvxpipe 2; // Set coordinate pipe to 2 (to use coordinates 0...1) +dclrscr bg_color; // Clear screen with background color + // +dmuldt eax,#d.x; // EAX = Direction Vector * Delta (change of coords per frame) +add #ball.x,eax; // Move the ball +dmuldt eax,#d.y; // +add #ball.y,eax; // + // +cmp #ball.x,0.9; // Check hits against walls +cge bounce.x; // Call bounce routine... +cmp #ball.x,0.0; // +cle bounce.x; // + // +cmp #ball.y,0.9; // Bounce on other axis +cge bounce.y; // +cmp #ball.y,0.0; // +cle bounce.y; // + // +dcolor ball_color; // Set color to color of ball +drectwh ball,ball_wh; // Draw the ball + // +dsetsize 24; // Set font size +dwrite textpos,text; + // +dexit; // Exit the draw function +////////////////////////////////// +bounce.x: // Bounce function (change X speed) + neg #d.x; // + min #ball.x,0.9; // + max #ball.x,0.0; // +ret // + // +bounce.y: // Bounce function (change Y speed) + neg #d.y; // + min #ball.y,0.9; // + max #ball.y,0.0; // +ret // +////////////////////////////////// +// Data and resources // +////////////////////////////////// + // +color ball_color,255,255,255; // Ball color (white) +color bg_color, 64, 32,128; // Background color (neon violet) + // +vector2f ball; // Ball position +vector2f ball_wh,0.1,0.1; // Ball width/height + // +vector2f textpos,0.1,0.1; // Text position + // +vector2f d,1.0,1.0; // Movement direction & speed + // +string text,'Bouncing ball!'; // "Bouncing ball!" +////////////////////////////////// \ No newline at end of file diff --git a/data/GPUChip/cube.txt b/data/GPUChip/cube.txt new file mode 100644 index 0000000000..aca3a6c6a8 --- /dev/null +++ b/data/GPUChip/cube.txt @@ -0,0 +1,138 @@ +//timer EAX;// div EAX,8; +//fsin EAX,EAX; +//mul EAX,512; +//fabs EAX,EAX; +//neg EAX; +//add EAX,512; + +dcvxpipe 3; //-1..1 (opengl screen) +dvxpipe 5; //matrix projection + +//Initialize transform +mperspective mProjectionMatrix,vPerspective; + +//Render starts +dclrscr bg_color; +mlookat mViewMatrix,vLookAt; //View matrix + +timer eax; +mov #vRotate.w,eax; + +//Rotate translate +mrotate mRotateMatrix,vRotate; +mtranslate mTranslateMatrix,vTranslate; + +//Create model matrix +mmov mModelMatrix,mTranslateMatrix; +mmul mModelMatrix,mRotateMatrix; + +//modelViewMatrix = ViewMatrix * modelMatrx +mmov mModelViewMatrix,mViewMatrix; +mmul mModelViewMatrix,mModelMatrix; + +//load matrix +mload mModelViewMatrix; +mloadproj mProjectionMatrix; + +//setup light +dsetlight 0,lightdata; + +//setup buffer +denable 0; //Vertex buffer +denable 1; //ZSorting +denable 2; //Lighting +denable 3; //Face culling + +//render cube +dcolor fg_color; +dvxdata_3f cube2,12; +dvxflush; + +ddisable 0; //Disable everything! +ddisable 1; +ddisable 2; +ddisable 3; + +dcvxpipe 0; +dvxpipe 0; + +//You can write some text here now +// +dexit; + +//======== +cube2: +db -1,-1,-1; +db 1,-1,-1; +db 1,1,-1; +cube3: +db -1,-1,-1; +db 1,1,-1; +db -1,1,-1; +cube4: +db 1,-1,1; +db -1,-1,1; +db 1,1,1; +cube5: +db -1,-1,1; +db -1,1,1; +db 1,1,1; +cube6: +db 1,-1,-1; +db -1,-1,-1; +db 1,-1,1; +cube7: +db -1,-1,-1; +db -1,-1,1; +db 1,-1,1; +cube8: +db -1,1,-1; +db 1,1,-1; +db 1,1,1; +cube9: +db -1,1,1; +db -1,1,-1; +db 1,1,1; +cube10: +db -1,-1,-1; +db -1,1,-1; +db -1,1,1; +cube11: +db -1,-1,1; +db -1,-1,-1; +db -1,1,1; +cube12: +db 1,1,-1; +db 1,-1,-1; +db 1,1,1; +cube13: +db 1,-1,-1; +db 1,-1,1; +db 1,1,1; + +lightdata: +vector4f lightpos, 0,50,-50, 0; //x y z +color lightcol,255,255,255, 1; //R G B Brightness +//======== + +matrix mRotateMatrix; +matrix mTranslateMatrix; + +matrix mProjectionMatrix; //This defines our projection to screen +matrix mViewMatrix; //This defines our camera transformations + +matrix mModelMatrix; //This is our model transformations +matrix mModelViewMatrix; //This is our model relatively to camera transform + + +vector4f vRotate, 1, 1, 1, 0; // +vector4f vTranslate, 0, 0, 0, 0; // <0> +vector4f vPerspective, 30, 1.6, 1, 20; // + +vLookAt: +vector3f vLookAt_Eye, 0, 0, -5; //Where our camera is +vector3f vLookAt_Center, 0, 0, 0; //What we look at +vector3f vLookAt_Up, 0, 1, 0; //Where our matt-hat is + +color fg_color,255,255,25; +color bg_color,64,32,12; \ No newline at end of file diff --git a/data/GPUChip/foxlogo.txt b/data/GPUChip/foxlogo.txt new file mode 100644 index 0000000000..13205fefae --- /dev/null +++ b/data/GPUChip/foxlogo.txt @@ -0,0 +1,84 @@ +//Fox game console logo (also example on how to work with polygons) + +dclrscr chassis; + +dcolor fox1c; +dvxdata_2f fox1a,16; //16 max!! +dvxdata_2f fox2a,3; +dvxdata_2f fox3a,3; +dvxdata_2f fox4a,3; +dvxdata_2f fox5a,3; +dvxdata_2f fox6a,3; +dvxdata_2f fox7a,6; +dvxdata_2f fox8a,3; + +dcolor fox2c; +dvxdata_2f fox2,4; + +dexit; + +//=========================================== +color chassis,0,0,0; + +color fox1c,60,60,60; +color fox2c,100,100,100; +//=========================================== +fox1a: //N=16 +db 60,218 +db 62,173 +db 32,36 +db 214,119 +db 268,128 +db 318,168 +db 352,233 +db 494,243 +db 499,254 +db 496,266 +db 478,321 +db 335,374 +db 265,408 +db 223,419 +db 95,430 +db 109,408 + +fox2a: //N = 3 +db 109,408 +db 57,432 +db 69,376 +fox3a: +db 69,376 +db 33,394 +db 59,327 +fox4a: +db 59,327 +db 24,348 +db 54,273 +fox5a: +db 54,273 +db 29,286 +db 57,240 +fox6a: +db 57,240 +db 26,245 +db 60,218 + +fox7a: //N=6 +db 109,408 +db 69,376 +db 59,327 +db 54,273 +db 57,240 +db 60,218 + +fox8a: //N=3 +db 177,150; +db 269,150; +db 190,47; + +//=========================================== +fox2: //N=4 +db 340,238 +db 286,257 +db 274,203 +db 311,213 +//=========================================== \ No newline at end of file diff --git a/data/GPUChip/hud_engine.txt b/data/GPUChip/hud_engine.txt new file mode 100644 index 0000000000..be58b31b6e --- /dev/null +++ b/data/GPUChip/hud_engine.txt @@ -0,0 +1,101 @@ +//mov #65522,1; +//mov #65525,0.66; +//port0 & port1 - engine left/right throttle (0..1) +//port2 & port3 - delta (not used) + +//This displays engine window in PhoenixWings airplane + +mov #65485,16; //set circle quality + +dclrscr hud_border; + +dcolor hud_text; +dcircle hud_engine1gauge,68; +dcircle hud_engine2gauge,68; +dcolor hud_border; +dcircle hud_engine1gauge,64; +dcircle hud_engine2gauge,64; + +dcolor hud_text; +dsetwidth 1; +dline hud_engine1gauge_start,hud_engine1gauge; +dline hud_engine2gauge_start,hud_engine2gauge; + +dsetwidth 2; + +//=== +mov eax,port0; mul eax,100; +mul eax,0.1; +mul #left_power,1.9; +add #left_power,eax; +div #left_power,2; + +mov eax,#left_power; div eax,100; +mul eax,6.00; +add eax,1.57; + +drotatescale eax,1; +dmove hud_engine1gauge; + +dline gauge_base,gauge_needle; +//== +mov #right_power,#left_power; //comment this and.. +//uncomment if your left/right engines are not synchronized +//mov eax,port1; mul eax,100; +//mul eax,0.1; +//mul #right_power,1.9; +//add #right_power,eax; +//div #right_power,2; + +//mov eax,#right_power; div eax,100; +//mul eax,6.00; +//add eax,1.57; + +drotatescale eax,1; +dmove hud_engine2gauge; + +dline gauge_base,gauge_needle; +//== + +//use this for whatever you wanna +//mov #left_delta,port2; sub #left_delta,7.6; mul #left_delta,10; +//mov #right_delta,port3; sub #right_delta,7.6; mul #right_delta,10; + +drotatescale 0,1; //reset! +dmove 0; + +dsetfont 4; +dsetsize 28; +dwritefmt hud_text1pos,hud_text1; +dwritefmt hud_text2pos,hud_text2; + +cmp port4,1; +dcolor hud_yellow; +je _nsh; + dshade 0.25; +_nsh: +dwrite hud_text3pos,hud_text3; +dexit; + +vector2f hud_text1pos,70,212; +vector2f hud_text2pos,310,212; +vector2f hud_text3pos,20,460; + +string hud_text1,'Left Engine',10,10,'N1 = %i%%',10,'Delta = %i%%'; +float left_power; float left_delta; +string hud_text2,'Right Engine',10,10,'N1 = %i%%',10,'Delta = %i%%'; +float right_power; float right_delta; +string hud_text3,''; + +vector2f hud_engine1gauge,128,128; +vector2f hud_engine1gauge_start,128,64; + +vector2f hud_engine2gauge,384,128; +vector2f hud_engine2gauge_start,384,64; + +vector2f gauge_base,0,0; +vector2f gauge_needle,0,-48; + +color hud_text,64,255,64; +color hud_yellow,255,255,64; +color hud_border,30,30,30; \ No newline at end of file diff --git a/data/GPUChip/hud_fighter.txt b/data/GPUChip/hud_fighter.txt new file mode 100644 index 0000000000..1ddc1f7e08 --- /dev/null +++ b/data/GPUChip/hud_fighter.txt @@ -0,0 +1,134 @@ +//Aircraft hud +//port0 - ROLL +//port1 - PITCH +//port2 - YAW (heading) +//port3 - speed (units/sec) +//port4 - altitude (units) +//port5 - radar altitude (put ranger under your plane, and attach to this) +//port6 - flaps active, 1 or 0 +//port7 - go to "Gates - Time", and find "Derivative". Attach this to derivative, and derivative to altitude (vertical speed) + +//Artiftical horizon +in eax,0; //Roll +in ebx,1; //Pitch + +//mul ebx,0.017453292; +mul eax,0.017453292; +add eax,1.57; + +div ebx,90; +mul ebx,512; +add ebx,256; + +mov #horizon_moveoffset.y,ebx; + +drotatescale eax,1; +dmove horizon_moveoffset; + +dcolor art_sky; +drectwh horizon_sky_offset,horizon_size; +dcolor art_grnd; +drectwh horizon_grnd_offset,horizon_size; + +dcolor hud_text; +dsetsize 20; +mov eax,-45; +_horizon_text: + mov ebx,eax; + mul ebx,5.68; + sub ebx,10; + mov #horizon_textpos1.y,ebx; + mov #horizon_textpos2.y,ebx; add ebx,9; + mov #horizon_rectpos1.y,ebx; add ebx,2; + mov #horizon_rectpos2.y,ebx; + + drect horizon_rectpos1,horizon_rectpos2; + dwritei horizon_textpos1,eax; + dwritei horizon_textpos2,eax; + + add eax,15; + cmp eax,45; + jle _horizon_text; + +//Reset +dmove 0; +drotatescale 0,1; + +//Border around art horizon +dcolor border_color; +drect border_p1,border_p2; +drect border_p3,border_p4; +drect border_p5,border_p6; +drect border_p7,border_p8; +dcolor border_color2; +drect border_p9,border_p10; + +//Draw hud stuff +mov #roll,port0; +mov #pitch,port1; +mov #hdg,port2; add #hdg,180; +mov #spd,port3; div #spd,17.6; +mov #alt,port4; +add #alt,12000; +div #alt,12; +mov #ralt,port5; div #ralt,12; +mov #vspd,port7; div #vspd,17.6; +dcolor hud_text; +dwritefmt hud_pos1,hud_text1; +dsetsize 16; +dwritefmt hud_pos2,hud_text2; + +dcolor hud_text; +mov eax,port6; mul eax,0.75; add eax,0.25; +dshade eax; +dwritefmt hud_pos3,hud_text3; + + +dexit; + +vec2f hud_pos1,50,20; +string hud_text1,'ROLL %i %tPITCH %i%tHDG %i'; +float roll; +float pitch; +float hdg; + +vec2f hud_pos2,45,120; +string hud_text2,'SPD',10,'%ikt',10,10,'ALT',10,'%ift',10,10,'RALT',10,'%ift',10,10,'VSPD',10,'%ift/s'; +float spd; +float alt; +float ralt; +float vspd; + +vec2f hud_pos3,45,400; +string hud_text3,'FLAPS'; + + +vec2f horizon_textpos1,96,0; +vec2f horizon_textpos2,-64,0; +vec2f horizon_rectpos1,-50,0; +vec2f horizon_rectpos2,50,0; +color hud_text,64,255,64; + +color border_color2,255,255,255; +color border_color,30,30,30; +vec2f border_p1,0,0; +vec2f border_p2,128,512; +vec2f border_p3,384,0; +vec2f border_p4,512,512; + +vec2f border_p5,128,0; +vec2f border_p6,384,64; +vec2f border_p7,128,448; +vec2f border_p8,384,512; + +vec2f border_p9,128,254; +vec2f border_p10,384,258; + +vec2f horizon_sky_offset,-256,-512; +vec2f horizon_grnd_offset,-256,0; +vec2f horizon_size,512,512; + +vec2f horizon_moveoffset,256,256; + +color art_sky,24,144,255; +color art_grnd,192,72,0; \ No newline at end of file diff --git a/data/GPUChip/mt2.txt b/data/GPUChip/mt2.txt new file mode 100644 index 0000000000..466e1db967 --- /dev/null +++ b/data/GPUChip/mt2.txt @@ -0,0 +1,46 @@ +dcvxpipe 3; +mov #regHWClear,0; //Stop hardware clearing +dsetwidth 0.05; + +timer EAX; +mov EDX,EAX; sub EDX,#PrevTime; //EDX = Delta time +mov #PrevTime,EAX; + +mov EBP,0.4; //Speed of rotation + +mov ECX,8; +DrawLoop: + mov EAX,#Angle; mul EAX,1; + fsin #EndPoint.X,EAX; mul EAX,2; + fcos #EndPoint.Y,EAX; + + //HSL coloring + fsin #HSL.R,EAX; mul #HSL.R,127; add #HSL.R,128; add EAX,1.57;// mul EAX,2; + fsin #HSL.G,EAX; mul #HSL.G,127; add #HSL.G,128; add EAX,1.57;// mul EAX,2; + fsin #HSL.B,EAX; mul #HSL.B,127; add #HSL.B,128; + + dcolor HSL; + + //Looks very nice + dline StartPoint1,EndPoint; + dline StartPoint2,EndPoint; + dline StartPoint3,EndPoint; + dline StartPoint4,EndPoint; + + mul EDX,EBP; + add #Angle,EDX; +loop DrawLoop; + +dexit; + +alloc Angle; +alloc PrevTime; + +color HSL; + +vector2f EndPoint,0,0; +vector2f StartPoint0,0,0; +vector2f StartPoint1,1,1; +vector2f StartPoint2,1,-1; +vector2f StartPoint3,-1,-1; +vector2f StartPoint4,-1,1; diff --git a/data/GPUChip/mt3.txt b/data/GPUChip/mt3.txt new file mode 100644 index 0000000000..391c1156e8 --- /dev/null +++ b/data/GPUChip/mt3.txt @@ -0,0 +1,106 @@ +//== 3D Graphics begin here ==================================================== + dvxpipe 3; + dcvxpipe 3; + + //Calc depth here + mov #Background.MinDepth, 0.8; //Near distance + mov #Background.MaxDepth, 6.0; //Far distance + mov #Background.ShadeStart,1.0; + mov #Background.DepthStep ,0.3; //Depth step. The lower, the higher quality is + + timer #Time; mul #Time,3; + + mov EAX,#Time; mod EAX,#Background.DepthStep; + + sub #Background.MinDepth,EAX; + sub #Background.MaxDepth,EAX; + + //Initialize depth range + mov #Background.deltaDepth,#Background.MaxDepth; + sub #Background.deltaDepth,#Background.MinDepth; + + //Compute background stuff + mov #Background.ShadeStep,#Background.deltaDepth; + div #Background.ShadeStep,#Background.DepthStep; + frnd #Background.ShadeStep; + finv #Background.ShadeStep; + mul #Background.ShadeStep,#Background.ShadeStepMul; + + //Brightness too + mov EAX,#Time; mod EAX,#Background.ShadeStep; + sub #Background.ShadeStart,EAX; + + mov #_rect.color.r,200; + mov #_rect.color.b,200; + +// Uncomment this for trippy camera +// timer EAX; div EAX,8; fsin EBX,EAX; mul EBX,2; +// drotatescale EAX,EBX; mul EBX,2; + + dsetwidth 0.8; + call Draw.Background; +dexit; + +alloc Time; + +//============================================================================== +Draw.Background: + //Draw all the rectangles + mov EAX,#Background.MinDepth; mov ECX,#Background.ShadeStart; + BackgroundLoop: + mov EDX,#Time; add EDX,EAX; + mov EBP,#Time; div EBP,6.28; fcos EBP,EBP; + + fsin EDI,EDX; mul EDI,EBP; mul EDI,0.8; sub EDI,1; + mov #_rect.offset.x,EDI; + + fcos ESI,EDX; mul ESI,EBP; mul ESI,0.4; sub ESI,1; + mov #_rect.offset.y,ESI; + + mov EDX,ECX; fpwr EDX,2; + mov #regZOffset,EAX; + + dcolor _rect.color; +// Uncomment this for trippy HSL color +// mov ESI,#Time; add ESI,EAX; +// fsin #HSL.R,ESI; mul #HSL.R,127; add #HSL.R,128; add ESI,1.57;// mul EAX,2; +// fsin #HSL.G,ESI; mul #HSL.G,127; add #HSL.G,128; add ESI,1.57;// mul EAX,2; +// fsin #HSL.B,ESI; mul #HSL.B,127; add #HSL.B,128; +// +// dcolor HSL; + dshade EDX; + dorectwh _rect.offset,_rect.wh; + + sub ECX,#Background.ShadeStep; + add EAX,#Background.DepthStep; + + cmp EAX,#Background.MaxDepth; + jl BackgroundLoop; +ret + +//============================================================================== +//Drawing parameters +float Background.MinDepth; +float Background.MaxDepth; +float Background.deltaDepth ; +float Background.DepthStep; +float Background.ShadeStart ; +float Background.ShadeStep ; +float Background.ShadeStepMul,0.5; + +color HSL; + +//Generic rectangle +vector2f _rect.offset,-1,-1; +vector2f _rect.wh,2,2; + +vector2f _pad1.offset; +vector2f _pad2.offset; +vector2f _pad.wh; + +//Color scheme +color _rect.color, 200,200,200; +color _rect.color2,200,200,000; + +color _pad1.color, 000,200,000; +color _pad2.color, 200,000,000; \ No newline at end of file diff --git a/data/GPUChip/stargate.txt b/data/GPUChip/stargate.txt new file mode 100644 index 0000000000..7873b9d52f --- /dev/null +++ b/data/GPUChip/stargate.txt @@ -0,0 +1,211 @@ +//STARGATE DIAL COMPUTER MAIN DISPLAY (realistic colors) +// +//How to connect: +//GPU IOBus to Data Port +//Port0 to "Open" +//Port1 to "Active" +//Port2 to "Chevron" +//Port3 to "Inbound" +//Port4 to iris +// +//That's all! + +div #65525,1.33; +mov #65485,16; //65485 is the circle quality register + +//24 means circles have 24 sides +//You can have up to 128 sides, but that LAGS +//32 sides is not even noticable comparing to 128 + +//= Misc decorations ================== + +dcolor stargate_out_ring; +dcircle center,250; +dcolor stargate_middle_ring; +dcircle center,240; +dcolor stargate_out_ring; +dcircle center,223; + +//= Rotating ring ===================== +mov #65485,12; +dcolor stargate_inner_ring; + +in ecx,2; //This block checks if chevron 7 is engaged +cmp ecx,7; //If yes, dont spin +mov eax,0; +jge _norotate; + timer eax; +_norotate: + +in ebx,1; //This one checks if stargate is active +mul eax,ebx; + +in ebx,3; neg ebx; add ebx,1; //This one checks if its inbound +mul eax,ebx; //wormhole + +drotatescale eax,1; //rotate by EAX radians +dmove center; +dcircle 0,220; + +drotatescale 0,1; //Reset scale/movment +dmove 0; + +//= Inner ring around EH ============== +mov #65485,24; +dcolor stargate_out_ring; +dcircle center,190; + + +//= EH ================================ +dcolor black; +dcircle center,180; //draw black hole instead of event horizon + +dcolor stargate_eventhorizon; + +in ebx,0; //Stargate active? +cmp ebx,0; +mov eax,0; +je _active; + rand eax; + mul eax,0.1; + add eax,0.9; +_active: + +in ebx,0; mul ebx,180; + +mul #eventhorizon_radius,0.99; +mul ebx,1.01; +add #eventhorizon_radius,ebx; +div #eventhorizon_radius,2; + + +dshade eax; +dcircle center,#eventhorizon_radius; + +//= Iris ============================== +mov edx,port4; +neg edx; add edx,1; + +mov eax,#iris_status; +sub eax,edx; +fabs eax,eax; + +dmuldt ecx,8; + +cmp eax,0.02; +jl _donothing; + cmp #iris_status,edx; + jl _lower; + sub #iris_status,ecx; + jmp _donothing; + _lower: + add #iris_status,ecx; +_donothing: + +mov #iris1.y,#iris_status; +mul #iris1.y,#iris2.y; + +dmove center; + +mov ecx,12; +_iris: + fsin ebx,ecx; fabs ebx,ebx; div ebx,10; add ebx,0.7; + + mov eax,ecx; mul eax,0.490; add eax,0.01; //0.697 + add eax,#iris_status; + + drotatescale eax,1; + + dcolor iris_color; + dshade ebx; + + drect iris1,iris2; +loop _iris; + +dmove 0; + +//= Chevrons ========================== +mov eax,1; //Chevron ID +in ebx,2; +dmove center; +_chevron_loop: + mov edx,eax; //Compute chevron angle in radians + mul edx,0.69815; + sub edx,1.23333; + + drotatescale edx,1; //Rotate chevron polygon + dcolor stargate_chevron; + + mov edx,eax:#chevron_triggers; + + cmp edx,ebx; //Check if chevron is light up + jle _noshade; + dshade 0.25; + _noshade: + + dvxpoly chevron_polygon,4; //draw chevron polygon + + inc eax; + cmp eax,9; + jle _chevron_loop; + +//= Computer text ===================== +drotatescale 0,1; //reset movement and scale +dmove 0; + +in eax,3; //Is inbound? +cmp eax,0; +je _dexit; + + timer eax; mul eax,2; fint eax; mod eax,2; + dcolor sgc_text; + dshade eax; + + dsetsize 64; //draw message + dwrite sgc_inboundpos,sgc_inbound; + +_dexit: +dexit; + +//= Helpers =========================== + +chevron_triggers: +db 9,4,5,6,7,1,2,3,8; +// 1 2 3 4 5 6 7 8 9 +// Order in which chevrons light up +// Only 1-7 are used though + +//===================================== + +color sgc_text,255,255,255; + +vector2f sgc_inboundpos,120,215; +string sgc_inbound,'INBOUND'; + +color stargate_out_ring, 116,105, 76; +color stargate_middle_ring, 93 , 85, 60; +color stargate_inner_ring, 138,137,108; +color stargate_eventhorizon, 93,114,162; +color stargate_chevron, 250,162, 54; +color iris_color, 192,192,192; + +color black,0,0,0; + +vector2f center,256,256; + +vector2f iris1,-44,0; +vector2f iris2,44,175; + +vector2f chevcenter,-16,-256; +vector2f chevsize,32,32; + +float eventhorizon_radius; +float iris_status; + +//raw chevron poly data +//format: +chevron_polygon: //n=4 +db -16,-251; +db 16,-251; +db 10,-230; +db -10,-230; \ No newline at end of file diff --git a/data/GPUChip/verynice1.txt b/data/GPUChip/verynice1.txt new file mode 100644 index 0000000000..809889c7e0 --- /dev/null +++ b/data/GPUChip/verynice1.txt @@ -0,0 +1,54 @@ +dcolor c1; +drect p1,p2; +dcolor c2; +drect p3,p4; +dcolor c3; +drect p5,p6; + +mov #textpos1.y,80; + +dcolor c4; +dsetsize 12; +dwrite textpos1,text1; + +mov ecx,0; +port_loop: + add #textpos1.y,18; + mov #textpos2.y,#textpos1.y; + + mov #textpos2.x,#textpos1.x; + add #textpos2.x,90; + dwrite textpos1,text2; + dwritei textpos2,ecx; + + in eax,ecx; + + mov #textpos2.x,#textpos1.x; + add #textpos2.x,192; + dwritef textpos2,eax; + + inc ecx; + cmp ecx,18; + jl port_loop; + +dexit; + +string text1,'VERYNICE HUD SYSTEM INITIALIZED... VER 1.0'; +string text2,'INPUT PORT VALUE'; + +vec2f textpos1,80,80; +vec2f textpos2,80,80; + +color c1,0,0,255; +color c2,0,0,127; +color c3,0,0,64; +color c4,255,255,255; + +vec2f p1,50,50; +vec2f p2,450,450; + +vec2f p3,60,60; +vec2f p4,430,430; + +vec2f p5,70,70; +vec2f p6,440,440; \ No newline at end of file diff --git a/data/GPUChip/verynice2.txt b/data/GPUChip/verynice2.txt new file mode 100644 index 0000000000..7c8cbefe7f --- /dev/null +++ b/data/GPUChip/verynice2.txt @@ -0,0 +1,123 @@ +//Generated by WGUI tool. Get it at wiremod.com +_page_0: +dsetsize 16 +dcolor _c_0 +drect _a_1,_a_2 +dcolor _c_1 +drect _a_4,_a_5 +dcolor _c_2 +drect _a_7,_a_8 +dcolor _c_3 +drect _a_10,_a_11 +dcolor _c_3 +drect _a_13,_a_14 +dcolor _c_2 +mov #_f_17,port0 +dwrite _a_16,_s_17 +dcolor _c_4 +drect _a_19,_a_20 +dcolor _c_2 +mov #_f_23,port0 +dwrite _a_22,_s_23 +dcolor _c_4 +dwritefmt _a_25,_s_26 +dcolor _c_4 +dwritefmt _a_28,_s_29 +dcolor _c_4 +dwritefmt _a_31,_s_32 +dcolor _c_4 +dwritefmt _a_34,_s_35 +dcolor _c_4 +dwritefmt _a_37,_s_38 +dcolor _c_4 +dwritefmt _a_40,_s_41 +dcolor _c_4 +dwritefmt _a_43,_s_44 +dcolor _c_4 +dwritefmt _a_46,_s_47 +dcolor _c_3 +drect _a_49,_a_50 +dcolor _c_2 +mov #_f_53,port0 +dwrite _a_52,_s_53 +dcolor _c_3 +drect _a_55,_a_56 +dcolor _c_2 +mov #_f_59,port0 +dwrite _a_58,_s_59 +dcolor _c_2 +dwritefmt _a_61,_s_62 +dcolor _c_2 +dwritefmt _a_64,_s_65 +dcolor _c_2 +dwritefmt _a_67,_s_68 +dcolor _c_2 +dwritefmt _a_70,_s_71 +dcolor _c_2 +dwritefmt _a_73,_s_74 +dcolor _c_2 +dwritefmt _a_76,_s_77 +dexit + +color _c_0,0,0,160 +vector2f _a_1,8,8 +vector2f _a_2,504,504 +color _c_1,7,51,122 +vector2f _a_4,16,16 +vector2f _a_5,496,496 +color _c_2,0,0,0 +vector2f _a_7,24,24 +vector2f _a_8,488,488 +color _c_3,192,192,192 +vector2f _a_10,32,32 +vector2f _a_11,480,216 +vector2f _a_13,32,224 +vector2f _a_14,480,392 +vector2f _a_16,40,40 +string _s_17,'VERYNICE GUI V2.1 Initialized...' +float _f_17,0 +color _c_4,128,128,128 +vector2f _a_19,32,64 +vector2f _a_20,480,72 +vector2f _a_22,40,232 +string _s_23,'Raw data feed:' +float _f_23,0 +vector2f _a_25,88,256 +string _s_26,'Input port 0: %f' +vector2f _a_28,88,272 +string _s_29,'Input port 1: %f' +vector2f _a_31,88,288 +string _s_32,'Input port 2: %f' +vector2f _a_34,88,304 +string _s_35,'Input port 3: %f' +vector2f _a_37,88,320 +string _s_38,'Input port 4: %f' +vector2f _a_40,88,336 +string _s_41,'Input port 5: %f' +vector2f _a_43,88,352 +string _s_44,'Input port 6: %f' +vector2f _a_46,88,368 +string _s_47,'Input port 7: %f' +vector2f _a_49,32,400 +vector2f _a_50,248,480 +vector2f _a_52,40,408 +string _s_53,'Vector feed 1:' +float _f_53,0 +vector2f _a_55,264,400 +vector2f _a_56,480,480 +vector2f _a_58,272,408 +string _s_59,'Vector feed 2:' +float _f_59,0 +vector2f _a_61,40,424 +string _s_62,'X: %f' +vector2f _a_64,40,440 +string _s_65,'Y: %f' +vector2f _a_67,40,456 +string _s_68,'Z: %f' +vector2f _a_70,272,424 +string _s_71,'X: %f' +vector2f _a_73,272,440 +string _s_74,'Y: %f' +vector2f _a_76,272,456 +string _s_77,'Z: %f' + diff --git a/data/WireModelPacks/default.txt b/data/WireModelPacks/default.txt new file mode 100644 index 0000000000..c5112ffc13 --- /dev/null +++ b/data/WireModelPacks/default.txt @@ -0,0 +1,181 @@ +"0" +{ + "Gate Chip" + { + "categories" "gate,chip" + "model" "models/jaanus/wiretool/wiretool_gate.mdl" + + "files" + { + "0" "models/jaanus/wiretool/wiretool_gate.dx80.vtx" + "1" "models/jaanus/wiretool/wiretool_gate.dx90.vtx" + "2" "models/jaanus/wiretool/wiretool_gate.phy" + "3" "models/jaanus/wiretool/wiretool_gate.sw.vtx" + "4" "models/jaanus/wiretool/wiretool_gate.vvd" + "5" "models/jaanus/wiretool/wiretool_gate.xbox.vtx" + } + } + + "Value Chip" + { + "categories" "value" + "model" "models/kobilica/value.mdl" + + "files" + { + "0" "models/kobilica/value.dx80.vtx" + "1" "models/kobilica/value.dx90.vtx" + "2" "models/kobilica/value.phy" + "3" "models/kobilica/value.sw.vtx" + "4" "models/kobilica/value.vvd" + "5" "models/kobilica/value.xbox.vtx" + "6" "materials/models/wire/chipnum.vmt" + "7" "materials/models/wire/chipnum.vtf" + } + } + + "Speaker" + { + "categories" "speaker" + "model" "models/cheeze/wires/speaker.mdl" + + "files" + { + "0" "models/cheeze/wires/speaker.dx80.vtx" + "1" "models/cheeze/wires/speaker.dx90.vtx" + "2" "models/cheeze/wires/speaker.phy" + "3" "models/cheeze/wires/speaker.sw.vtx" + "4" "models/cheeze/wires/speaker.vvd" + "5" "materials/models/cheeze/wires/leather2.vmt" + "6" "materials/models/cheeze/wires/leather2.vtf" + "7" "materials/models/cheeze/wires/metal_texture.vmt" + "8" "materials/models/cheeze/wires/metal_texture.vtf" + "9" "materials/models/cheeze/wires/speaker_base.vmt" + "10" "materials/models/cheeze/wires/speaker_base.vtf" + "11" "materials/models/cheeze/wires/speaker_blck.vmt" + "12" "materials/models/cheeze/wires/speaker_blck.vtf" + "13" "materials/models/cheeze/wires/speaker_red.vmt" + "14" "materials/models/cheeze/wires/speaker_base.vtf" + } + } + + "Metal Can" + { + "categories" "speaker" + "model" "models/props_junk/garbage_metalcan002a.mdl" + } + + "Do Not Press" + { + "categories" "button" + "model" "models/dav0r/buttons/button.mdl" + } + + "Switch" + { + "categories" "button" + "model" "models/dav0r/buttons/switch.mdl" + } + + "Clock" + { + "categories" "button" + "model" "models/props_c17/clock01.mdl" + } + + "Breen Clock" + { + "categories" "detonator" + "model" "models/props_combine/breenclock.mdl" + } + + "Blue Binder" + { + "categories" "radio" + "model" "models/props_lab/binderblue.mdl" + } + + "Green Binder" + { + "categories" "radio2" + "model" "models/props_lab/bindergreen.mdl" + } + + "Siren" + { + "categories" "pixel,indicator,podctrlr" + "model" "models/jaanus/wiretool/wiretool_siren.mdl" + } + + "Medium 7-seg bar" + { + "categories" "pixel,indicator" + "model" "models/segment2.mdl" + } + + "Small 7-seg bar" + { + "categories" "pixel,indicator" + "model" "models/segment.mdl" + } + + "LED small" + { + "categories" "pixel,indicator" + "model" "models/led.mdl" + } + + "LED large" + { + "categories" "pixel,indicator" + "model" "models/led2.mdl" + } + + "Pop can" + { + "categories" "pixel,indicator" + "model" "models/props_junk/PopCan01a.mdl" + } + + "Barrel" + { + "categories" "indicator" + "model" "models/props_borealis/bluebarrel001.mdl" + } + + "Grave stone" + { + "categories" "indicator" + "model" "models/props_c17/gravestone004a.mdl" + } + + "Traffic Cone" + { + "categories" "indicator" + "model" "models/props_junk/TrafficCone001a.mdl" + } + + "Big Clock" + { + "categories" "indicator" + "model" "models/props_trainstation/trainstation_clock001.mdl" + } + + "Ranger" + { + "categories" "ranger,podctrlr" + "model" "models/jaanus/wiretool/wiretool_range.mdl" + } + + "Weight" + { + "categories" "weight" + "model" "models/props_interiors/pot01a.mdl" + } + + "Huladoll" + { + "categories" "weight" + "model" "models/props_lab/huladoll.mdl" + } +} diff --git a/data/WireModelPacks/expression2.txt b/data/WireModelPacks/expression2.txt new file mode 100644 index 0000000000..fcf161798f --- /dev/null +++ b/data/WireModelPacks/expression2.txt @@ -0,0 +1,62 @@ +"ModelPack" +{ + "Expression" + { + "categories" "expr2" + "model" "models/expression 2/cpu_expression.mdl" + "files" + { + "0" "models/expression 2/cpu_expression.dx80.vtx" + "1" "models/expression 2/cpu_expression.dx90.vtx" + "2" "models/expression 2/cpu_expression.phy" + "3" "models/expression 2/cpu_expression.sw.vtx" + "4" "models/expression 2/cpu_expression.vvd" + "5" "materials/models/expression 2/pins.vmt" + "6" "materials/models/expression 2/pins.vtf" + "7" "materials/models/expression 2/exprssn.vmt" + "8" "materials/models/expression 2/exprssn.vtf" + "9" "materials/models/expression 2/encasing.vmt" + "10" "materials/models/expression 2/encasing.vtf" + } + } + + "Controller" + { + "categories" "expr2" + "model" "models/expression 2/cpu_controller.mdl" + "files" + { + "0" "models/expression 2/cpu_controller.dx80.vtx" + "1" "models/expression 2/cpu_controller.dx90.vtx" + "2" "models/expression 2/cpu_controller.phy" + "3" "models/expression 2/cpu_controller.sw.vtx" + "4" "models/expression 2/cpu_controller.vvd" + "5" "materials/models/expression 2/pins.vmt" + "6" "materials/models/expression 2/pins.vtf" + "7" "materials/models/expression 2/cntrllr.vmt" + "8" "materials/models/expression 2/cntrllr.vtf" + "9" "materials/models/expression 2/encasing.vmt" + "10" "materials/models/expression 2/encasing.vtf" + } + } + + "Microchip" + { + "categories" "expr2" + "model" "models/expression 2/cpu_microchip.mdl" + "files" + { + "0" "models/expression 2/cpu_microchip.dx80.vtx" + "1" "models/expression 2/cpu_microchip.dx90.vtx" + "2" "models/expression 2/cpu_microchip.phy" + "3" "models/expression 2/cpu_microchip.sw.vtx" + "4" "models/expression 2/cpu_microchip.vvd" + "5" "materials/models/expression 2/pins.vmt" + "6" "materials/models/expression 2/pins.vtf" + "7" "materials/models/expression 2/mcrochp.vmt" + "8" "materials/models/expression 2/mcrochp.vtf" + "9" "materials/models/expression 2/encasing.vmt" + "10" "materials/models/expression 2/encasing.vtf" + } + } +} \ No newline at end of file diff --git a/info.txt b/info.txt new file mode 100644 index 0000000000..1687cc10fc --- /dev/null +++ b/info.txt @@ -0,0 +1,9 @@ +"AddonInfo" +{ + "name" "Wiremod" + "version" "SVN" + "author_name" "Wire Team" + "author_url" "http://www.wiremod.com/" + "info" "A set of SEnts and STools that communicate through wires." + "override" "0" +} diff --git a/lua/autorun/Wire_Load.lua b/lua/autorun/Wire_Load.lua new file mode 100644 index 0000000000..a64471c1a3 --- /dev/null +++ b/lua/autorun/Wire_Load.lua @@ -0,0 +1,70 @@ +-- $Rev: 1663 $ +-- $LastChangedDate: 2009-09-12 03:34:53 -0700 (Sat, 12 Sep 2009) $ +-- $LastChangedBy: TomyLobo $ + +if VERSION < 34 then Error("WireMod: Your GMod is years too old. Load aborted.\n") end + +if SERVER then + -- this file + AddCSLuaFile("autorun/Wire_Load.lua") + + -- shared includes + AddCSLuaFile("wire/WireShared.lua") + AddCSLuaFile("wire/UpdateCheck.lua") + AddCSLuaFile("wire/Beam_NetVars.lua") + AddCSLuaFile("wire/WireGates.lua") + AddCSLuaFile("wire/WireMonitors.lua") + AddCSLuaFile("wire/opcodes.lua") + + -- client includes + AddCSLuaFile("wire/client/cl_wirelib.lua") + AddCSLuaFile("wire/client/cl_modelplug.lua") + AddCSLuaFile("wire/client/cl_gpulib.lua") + AddCSLuaFile("wire/client/WireDermaExts.lua") + AddCSLuaFile("wire/client/WireMenus.lua") + AddCSLuaFile("wire/client/TextEditor.lua") + AddCSLuaFile("wire/client/toolscreen.lua") + AddCSLuaFile("wire/client/wire_expression2_browser.lua") + AddCSLuaFile("wire/client/wire_expression2_editor.lua") + AddCSLuaFile("wire/client/e2helper.lua") + AddCSLuaFile("wire/client/e2descriptions.lua") + + -- resource files + for i=1,16 do + resource.AddFile("settings/render_targets/WireGPU_RT_"..i..".txt") + end + resource.AddFile("materials/expression 2/cog.vtf") + resource.AddFile("materials/expression 2/cog.vmt") + print("Ignore the following error message about cog_world.vtf:") + resource.AddFile("materials/expression 2/cog_world.vmt") +end + +-- shared includes +include("wire/WireShared.lua") +include("wire/UpdateCheck.lua") +include("wire/Beam_NetVars.lua") +include("wire/WireGates.lua") +include("wire/WireMonitors.lua") +include("wire/opcodes.lua") + +-- server includes +if SERVER then + include("wire/server/WireLib.lua") + include("wire/server/ModelPlug.lua") + include("wire/server/radiolib.lua") +end + +-- client includes +if CLIENT then + include("wire/client/cl_wirelib.lua") + include("wire/client/cl_modelplug.lua") + include("wire/client/cl_gpulib.lua") + include("wire/client/WireDermaExts.lua") + include("wire/client/WireMenus.lua") + include("wire/client/toolscreen.lua") + include("wire/client/TextEditor.lua") + include("wire/client/wire_expression2_browser.lua") + include("wire/client/wire_expression2_editor.lua") + include("wire/client/e2helper.lua") + include("wire/client/e2descriptions.lua") +end diff --git a/lua/effects/jump_in/init.lua b/lua/effects/jump_in/init.lua new file mode 100644 index 0000000000..553d190ebc --- /dev/null +++ b/lua/effects/jump_in/init.lua @@ -0,0 +1,60 @@ +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local TargetEntity = data:GetEntity() + + if ( !TargetEntity || !TargetEntity:IsValid() ) then return end + + //local vOffset = TargetEntity:GetPos() + + local Low, High = TargetEntity:WorldSpaceAABB() + local Center = data:GetOrigin() //High - (( High - Low ) * 0.5) + + local NumParticles = TargetEntity:BoundingRadius() + NumParticles = NumParticles * 2 + + NumParticles = math.Clamp( NumParticles, 10, 500 ) + + local emitter = ParticleEmitter( Center ) + + for i=0, NumParticles do + + local vPos = Vector( math.Rand(Low.x,High.x), math.Rand(Low.y,High.y), math.Rand(Low.z,High.z) ) + local vVel = (vPos - Center) * 6 + local particle = emitter:Add( "effects/spark", Center ) + if (particle) then + particle:SetVelocity( vVel ) + particle:SetLifeTime( 0 ) + particle:SetDieTime( math.Rand( 0.1, 0.4 ) ) + particle:SetStartAlpha( 0 ) + particle:SetEndAlpha( math.Rand( 200, 255 ) ) + particle:SetStartSize( 0 ) + particle:SetEndSize( 20 ) + particle:SetRoll( math.Rand(0, 360) ) + particle:SetRollDelta( 0 ) + end + + end + + emitter:Finish() + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + return false +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render() +end diff --git a/lua/effects/jump_out/init.lua b/lua/effects/jump_out/init.lua new file mode 100644 index 0000000000..a93ef280f5 --- /dev/null +++ b/lua/effects/jump_out/init.lua @@ -0,0 +1,62 @@ + + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local TargetEntity = data:GetEntity() + + if ( !TargetEntity || !TargetEntity:IsValid() ) then return end + + local vOffset = TargetEntity:GetPos() + + local Low, High = TargetEntity:WorldSpaceAABB() + local Center = data:GetOrigin() //High - (( High - Low ) * 0.5) + + local NumParticles = TargetEntity:BoundingRadius() + NumParticles = NumParticles * 2 + + NumParticles = math.Clamp( NumParticles, 10, 500 ) + + local emitter = ParticleEmitter( vOffset ) + + for i=0, NumParticles do + + local vPos = Vector( math.Rand(Low.x,High.x), math.Rand(Low.y,High.y), math.Rand(Low.z,High.z) ) + local vVel = (Center - vPos) * 6 + local particle = emitter:Add( "effects/spark", vPos ) + if (particle) then + particle:SetVelocity( vVel ) + particle:SetLifeTime( 0 ) + particle:SetDieTime( math.Rand( 0.1, 0.3 ) ) + particle:SetStartAlpha( math.Rand( 200, 255 ) ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 20 ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand(0, 360) ) + particle:SetRollDelta( 0 ) + end + + end + + emitter:Finish() + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + return false +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render() +end diff --git a/lua/effects/thruster_ring/init.lua b/lua/effects/thruster_ring/init.lua new file mode 100644 index 0000000000..ae0d601892 --- /dev/null +++ b/lua/effects/thruster_ring/init.lua @@ -0,0 +1,64 @@ + + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + + render.DrawQuadEasy( self.Entity:GetPos(), + self.Entity:GetAngles():Forward(), + self.Size, self.Size, + Color( math.Rand( 10, 100), math.Rand( 100, 220), math.Rand( 240, 255), self.Alpha ) + ) + +end diff --git a/lua/effects/thruster_ring_grow/init.lua b/lua/effects/thruster_ring_grow/init.lua new file mode 100644 index 0000000000..7fa61f2564 --- /dev/null +++ b/lua/effects/thruster_ring_grow/init.lua @@ -0,0 +1,64 @@ + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Size = self.Size + (255 - self.Alpha)*0.08 + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + + render.DrawQuadEasy( self.Entity:GetPos(), + self.Entity:GetAngles():Forward(), + self.Size, self.Size, + Color( math.Rand( 10, 100), math.Rand( 100, 220), math.Rand( 240, 255), self.Alpha ) + ) + +end diff --git a/lua/effects/thruster_ring_grow1/init.lua b/lua/effects/thruster_ring_grow1/init.lua new file mode 100644 index 0000000000..b650bda7ec --- /dev/null +++ b/lua/effects/thruster_ring_grow1/init.lua @@ -0,0 +1,64 @@ + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Size = self.Size + (255 - self.Alpha)*0.06 + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + + render.DrawQuadEasy( self.Entity:GetPos(), + self.Entity:GetAngles():Forward(), + self.Size, self.Size, + Color( math.Rand( 10, 100), math.Rand( 100, 220), math.Rand( 240, 255), self.Alpha ) + ) + +end diff --git a/lua/effects/thruster_ring_grow2/init.lua b/lua/effects/thruster_ring_grow2/init.lua new file mode 100644 index 0000000000..545b2717fc --- /dev/null +++ b/lua/effects/thruster_ring_grow2/init.lua @@ -0,0 +1,64 @@ + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Size = self.Size + (255 - self.Alpha)*0.04 + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + + render.DrawQuadEasy( self.Entity:GetPos(), + self.Entity:GetAngles():Forward(), + self.Size, self.Size, + Color( math.Rand( 10, 100), math.Rand( 100, 220), math.Rand( 240, 255), self.Alpha ) + ) + +end diff --git a/lua/effects/thruster_ring_grow3/init.lua b/lua/effects/thruster_ring_grow3/init.lua new file mode 100644 index 0000000000..ff656d4a6d --- /dev/null +++ b/lua/effects/thruster_ring_grow3/init.lua @@ -0,0 +1,64 @@ + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Size = self.Size + (255 - self.Alpha)*0.02 + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + + render.DrawQuadEasy( self.Entity:GetPos(), + self.Entity:GetAngles():Forward(), + self.Size, self.Size, + Color( math.Rand( 10, 100), math.Rand( 100, 220), math.Rand( 240, 255), self.Alpha ) + ) + +end diff --git a/lua/effects/thruster_ring_shrink/init.lua b/lua/effects/thruster_ring_shrink/init.lua new file mode 100644 index 0000000000..59fcda0374 --- /dev/null +++ b/lua/effects/thruster_ring_shrink/init.lua @@ -0,0 +1,64 @@ + +EFFECT.Mat = Material( "effects/select_ring" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Size = self.Size - (255 - self.Alpha)*0.02 + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + + render.DrawQuadEasy( self.Entity:GetPos(), + self.Entity:GetAngles():Forward(), + self.Size, self.Size, + Color( math.Rand( 10, 100), math.Rand( 100, 220), math.Rand( 240, 255), self.Alpha ) + ) + +end diff --git a/lua/effects/thruster_sperm/init.lua b/lua/effects/thruster_sperm/init.lua new file mode 100644 index 0000000000..2fcbd05d4f --- /dev/null +++ b/lua/effects/thruster_sperm/init.lua @@ -0,0 +1,66 @@ + +EFFECT.Mat = Material( "thrusteraddon/sperm" ) + +/*--------------------------------------------------------- + Initializes the effect. The data is a table of data + which was passed from the server. +---------------------------------------------------------*/ +function EFFECT:Init( data ) + + local size = 16 + self.Entity:SetCollisionBounds( Vector( -size,-size,-size ), Vector( size,size,size ) ) + + local Pos = data:GetOrigin() + data:GetNormal() * 2 + + self.Entity:SetPos( Pos ) + self.Entity:SetAngles( data:GetNormal():Angle() + Angle( 0.01, 0.01, 0.01 ) ) + + self.Pos = data:GetOrigin() + self.Normal = data:GetNormal() + + self.Speed = 2 + self.Size = 16 + self.Alpha = 255 + +end + + +/*--------------------------------------------------------- + THINK +---------------------------------------------------------*/ +function EFFECT:Think( ) + + local speed = FrameTime() * self.Speed + + //if (self.Speed > 100) then self.Speed = self.Speed - 1000 * speed end + + //self.Size = self.Size + speed * self.Speed + self.Alpha = self.Alpha - 250.0 * speed + + self.Entity:SetPos( self.Entity:GetPos() + self.Normal * speed * 128 ) + + if (self.Alpha < 0 ) then return false end + if (self.Size < 0) then return false end + return true + +end + +/*--------------------------------------------------------- + Draw the effect +---------------------------------------------------------*/ +function EFFECT:Render( ) + + if (self.Alpha < 1 ) then return end + + render.SetMaterial( self.Mat ) + local ang = self.Entity:GetAngles():Forward() + ang.yaw = ang.yaw + 90 + ang.roll = ang.roll + 90 + + render.DrawQuadEasy( self.Entity:GetPos(), + ang, + self.Size, self.Size, + Color( 255,255,255, self.Alpha ) + ) + +end diff --git a/lua/entities/base_wire_entity/cl_init.lua b/lua/entities/base_wire_entity/cl_init.lua new file mode 100644 index 0000000000..cc1048e6d4 --- /dev/null +++ b/lua/entities/base_wire_entity/cl_init.lua @@ -0,0 +1,40 @@ +include("shared.lua") + +ENT.RenderGroup = RENDERGROUP_TRANSLUCENT//RENDERGROUP_OPAQUE//RENDERGROUP_BOTH + +local wire_drawoutline = CreateClientConVar("wire_drawoutline", 1, true, false) + +function ENT:Draw() + self:DoNormalDraw() + Wire_Render(self.Entity) +end + +function ENT:DoNormalDraw() + local looked_at = LocalPlayer():GetEyeTrace().Entity == self.Entity and EyePos():Distance(self:GetPos()) < 256 + if wire_drawoutline:GetBool() and looked_at then + if ( self.RenderGroup == RENDERGROUP_OPAQUE) then + self.OldRenderGroup = self.RenderGroup + self.RenderGroup = RENDERGROUP_TRANSLUCENT + end + if wire_drawoutline:GetBool() then self:DrawEntityOutline(1.0) end + self:DrawModel() + else + if(self.OldRenderGroup) then + self.RenderGroup = self.OldRenderGroup + self.OldRenderGroup = nil + end + self:DrawModel() + end + if looked_at then + if(self:GetOverlayText() ~= "") then + AddWorldTip(self:EntIndex(),self:GetOverlayText(),0.5,self:GetPos(),e) + end + end +end + +function ENT:Think() + if (CurTime() >= (self.NextRBUpdate or 0)) then + self.NextRBUpdate = CurTime() + math.random(30,100)/10 --update renderbounds every 3 to 10 seconds + Wire_UpdateRenderBounds(self.Entity) + end +end diff --git a/lua/entities/base_wire_entity/init.lua b/lua/entities/base_wire_entity/init.lua new file mode 100644 index 0000000000..39952a193b --- /dev/null +++ b/lua/entities/base_wire_entity/init.lua @@ -0,0 +1,93 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +ENT.WireDebugName = "No Name" +function ENT:Think() + if not self.is_looked_at or Wire_DisableOverlayTextUpdate then return end + if not self.NextOverlayText then return end + if self.NextOverlayTextTime and CurTime() < self.NextOverlayTextTime then return end + + //self.BaseClass.BaseClass.SetOverlayText(self, self.NextOverlayText) + self.Entity:SetNetworkedBeamString("GModOverlayText",self.NextOverlayText) + self.NextOverlayText = nil + self.NextOverlayTextTime = CurTime() + (self.OverlayDelay or 0.4) + math.random()*(self.OverlayRandom or 0.2) + if Wire_SlowerOverlayTextUpdate then + self.NextOverlayTextTime = self.NextOverlayTextTime + 1 //add a sec between updates + end +end + +function ENT:SetOverlayText(txt) + if Wire_DisableOverlayTextUpdate then return end + if Wire_FastOverlayTextUpdate then + self.Entity:SetNetworkedBeamString("GModOverlayText",txt,true) //send it now, damn it! + else + if self.NextOverlayTextTime or self.is_looked_at == false then + self.NextOverlayText = txt + else + self.Entity:SetNetworkedBeamString("GModOverlayText",txt) + self.NextOverlayText = nil + if not self.OverlayDelay or self.OverlayDelay > 0 or Wire_SlowerOverlayTextUpdate or not SinglePlayer() or Wire_ForceDelayOverlayTextUpdate then + self.NextOverlayTextTime = CurTime() + (self.OverlayDelay or 0.6) + math.random()*(self.OverlayRandom or 0.2) + end + end + end +end + +local lookat = {} +hook.Add("EntityRemoved", "IsLookedAt", function(ent) + lookat[ent] = nil +end) + +hook.Add("Think", "IsLookedAt", function() + for ent,_ in pairs(lookat) do + if ent.WasLookedAt then ent:WasLookedAt(lookat[ent]) end + lookat[ent] = nil + end + for _,ply in ipairs(player.GetAll()) do + local ent = ply:GetEyeTrace().Entity + if not lookat[ent] then + if ent.IsLookedAt then ent:IsLookedAt(ply) end + lookat[ent] = ply + end + end +end) + +function ENT:WasLookedAt(ply) + self.is_looked_at = false +end + +function ENT:IsLookedAt(ply) + self.is_looked_at = true +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function ENT:BuildDupeInfo() + return WireLib.BuildDupeInfo(self.Entity) +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + WireLib.ApplyDupeInfo( ply, ent, info, GetEntByID ) +end + +function ENT:PreEntityCopy() + //build the DupeInfo table and save it as an entity mod + local DupeInfo = self:BuildDupeInfo() + if(DupeInfo) then + duplicator.StoreEntityModifier(self.Entity,"WireDupeInfo",DupeInfo) + end +end + +function ENT:PostEntityPaste(Player,Ent,CreatedEntities) + //apply the DupeInfo + if(Ent.EntityMods and Ent.EntityMods.WireDupeInfo) then + Ent:ApplyDupeInfo(Player, Ent, Ent.EntityMods.WireDupeInfo, function(id) return CreatedEntities[id] end) + end +end diff --git a/lua/entities/base_wire_entity/shared.lua b/lua/entities/base_wire_entity/shared.lua new file mode 100644 index 0000000000..445063f62d --- /dev/null +++ b/lua/entities/base_wire_entity/shared.lua @@ -0,0 +1,30 @@ +ENT.Type = "anim" +ENT.Base = "base_gmodentity" + +ENT.PrintName = "Wire Entity" +ENT.Author = "Erkle" +ENT.Contact = "ErkleMad@gmail.com" +ENT.Purpose = "Base for all wired SEnts" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +ENT.IsWire = true + +function ENT:GetOverlayText() + local name = self.Entity:GetNetworkedString("WireName") + //local txt = self.BaseClass.BaseClass.GetOverlayText(self) or "" + local txt = self.Entity:GetNetworkedBeamString("GModOverlayText") or "" + if (not SinglePlayer()) then + local PlayerName = self:GetPlayerName() + txt = txt .. "\n(" .. PlayerName .. ")" + end + if(name and name ~= "") then + if (txt == "") then + return "- "..name.." -" + end + return "- "..name.." -\n"..txt + end + return txt +end diff --git a/lua/entities/gmod_iballoon/cl_init.lua b/lua/entities/gmod_iballoon/cl_init.lua new file mode 100644 index 0000000000..036305acbd --- /dev/null +++ b/lua/entities/gmod_iballoon/cl_init.lua @@ -0,0 +1 @@ +include("shared.lua") diff --git a/lua/entities/gmod_iballoon/init.lua b/lua/entities/gmod_iballoon/init.lua new file mode 100644 index 0000000000..edc688b098 --- /dev/null +++ b/lua/entities/gmod_iballoon/init.lua @@ -0,0 +1,33 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") +--same as normal balloon but invincible +local MODEL = Model("models/dav0r/balloon/balloon.mdl") + +function ENT:Initialize() + self.Entity:SetModel(MODEL) + self.Entity:PhysicsInit(SOLID_VPHYSICS) + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:SetMass(100) + phys:Wake() + phys:EnableGravity(false) + end + self:SetForce(1) + self.Entity:StartMotionController() +end + +function ENT:SetForce(force) + self.Force = force*5000 + self.Entity:SetNetworkedFloat(0,self.Force) + self:SetOverlayText("Force: " .. math.floor(force)) +end + +function ENT:PhysicsSimulate(phys,deltatime) + -- Angular,Linear,globalforce + return Vector(0,0,0),Vector(0,0,self.Force)*deltatime,SIM_GLOBAL_FORCE +end + +function ENT:OnRestore( ) + self.Force = self.Entity:GetNetworkedFloat(0) +end diff --git a/lua/entities/gmod_iballoon/shared.lua b/lua/entities/gmod_iballoon/shared.lua new file mode 100644 index 0000000000..2652464d6e --- /dev/null +++ b/lua/entities/gmod_iballoon/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_gmodentity" + +ENT.PrintName = "Indestructible Balloon" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_addressbus/cl_init.lua b/lua/entities/gmod_wire_addressbus/cl_init.lua new file mode 100644 index 0000000000..9d004eb17e --- /dev/null +++ b/lua/entities/gmod_wire_addressbus/cl_init.lua @@ -0,0 +1,4 @@ +include("shared.lua") +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_addressbus/init.lua b/lua/entities/gmod_wire_addressbus/init.lua new file mode 100644 index 0000000000..3ba82614b7 --- /dev/null +++ b/lua/entities/gmod_wire_addressbus/init.lua @@ -0,0 +1,89 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "AddressBus" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:SetUseType( SIMPLE_USE ) + self.Outputs = Wire_CreateOutputs(self.Entity, {"Memory"}) + self.Inputs = Wire_CreateInputs(self.Entity,{"Memory1","Memory2","Memory3","Memory4"}) + self.Memory = {} + self.MemStart = {} + self.MemEnd = {} + self.DataRate = 0 + self.DataBytes = 0 + for i = 1,4 do + self.Memory[i] = nil + self.MemStart[i] = 0 + self.MemEnd[i] = 0 + end + self:SetOverlayText( "Address bus\nData rate: 0 bps" ) +end + +function ENT:Think() + self.BaseClass.Think(self) + + self.DataRate = (self.DataRate*1.2 + self.DataBytes * 4 * 0.8) / 2 + self.DataBytes = 0 + + Wire_TriggerOutput(self.Entity, "Memory", self.DataRate) + + self:SetOverlayText("Address bus\nData rate: "..math.floor(self.DataRate).." bps") + self.Entity:NextThink(CurTime()+0.25) +end + +function ENT:ReadCell( Address ) + for i = 1,4 do + if (Address >= self.MemStart[i]) && (Address <= self.MemEnd[i]) then + if (self.Memory[i]) then + if (self.Memory[i].ReadCell) then + self.DataBytes = self.DataBytes + 1 + local val = self.Memory[i]:ReadCell( Address - self.MemStart[i] ) + if (val) then + return val + else + return 0 + end + end + else + return 0 + end + end + end + return nil +end + +function ENT:WriteCell( Address, value ) + local res = false + for i = 1,4 do + if (Address >= self.MemStart[i]) && (Address <= self.MemEnd[i]) then + if (self.Memory[i]) then + if (self.Memory[i].WriteCell) then + self.Memory[i]:WriteCell( Address - self.MemStart[i], value ) + end + end + self.DataBytes = self.DataBytes + 1 + res = true + end + end + return res +end + + +function ENT:TriggerInput(iname, value) + if (iname == "Memory1") then + self.Memory[1] = self.Inputs.Memory1.Src + elseif (iname == "Memory2") then + self.Memory[2] = self.Inputs.Memory2.Src + elseif (iname == "Memory3") then + self.Memory[3] = self.Inputs.Memory3.Src + elseif (iname == "Memory4") then + self.Memory[4] = self.Inputs.Memory4.Src + end +end diff --git a/lua/entities/gmod_wire_addressbus/shared.lua b/lua/entities/gmod_wire_addressbus/shared.lua new file mode 100644 index 0000000000..09cd7ca519 --- /dev/null +++ b/lua/entities/gmod_wire_addressbus/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Address Bus" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_adv_input/cl_init.lua b/lua/entities/gmod_wire_adv_input/cl_init.lua new file mode 100644 index 0000000000..9de824c262 --- /dev/null +++ b/lua/entities/gmod_wire_adv_input/cl_init.lua @@ -0,0 +1,2 @@ +include('shared.lua') +ENT.RenderGroup = RENDERGROUP_OPAQUE diff --git a/lua/entities/gmod_wire_adv_input/init.lua b/lua/entities/gmod_wire_adv_input/init.lua new file mode 100644 index 0000000000..5f6bfda82f --- /dev/null +++ b/lua/entities/gmod_wire_adv_input/init.lua @@ -0,0 +1,129 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +ENT.WireDebugName = "Adv. Input" +ENT.OverlayDelay = 0.1 +ENT.OverlayRandom = 0.025 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity,{"Reset"}) + self.Outputs = Wire_CreateOutputs(self.Entity,{"Out"}) +end + +function ENT:Setup(key_more,key_less,toggle,value_min,value_max,value_start,speed) + self.keymore = key_more + self.keyless = key_less + self.toggle = (toggle == 1 || toggle == true) + self.value_min = value_min + self.value_max = value_max + self.Value = value_start + self.value_start = value_start + self.speed = speed + self:ShowOutput() + Wire_TriggerOutput(self.Entity,"Out",self.Value) +end + +function ENT:TriggerInput(iname, value) + if(iname == "Reset")then + if(value != 0)then + self.Value = self.value_start + self:ShowOutput() + Wire_TriggerOutput(self.Entity,"Out",self.Value) + end + end +end + +function ENT:InputActivate(mul) + if (self.toggle) then + return self:Switch( !self.On, mul ) + end + return self:Switch( true, mul ) +end + +function ENT:InputDeactivate( mul ) + if (self.toggle) then return true end + return self:Switch( false, mul ) +end + +function ENT:Switch( on, mul ) + if (!self.Entity:IsValid()) then return false end + self.On = on + if(on) then + self.dir = mul + else + self.dir = 0 + end + return true +end + +function ENT:Think() + self.BaseClass.Think(self) + local timediff = CurTime()-(self.LastThink or 0) + self.LastThink = (self.LastThink or 0)+timediff + if (self.On == true) then + self.Value = self.Value + self.speed * timediff * self.dir + if (self.Value < self.value_min) then + self.Value = self.value_min + elseif (self.Value > self.value_max) then + self.Value = self.value_max + end + self:ShowOutput() + Wire_TriggerOutput(self.Entity,"Out",self.Value) + self.Entity:NextThink(CurTime()+0.02) + return true + end +end + +function ENT:ShowOutput() + self:SetOverlayText("(" .. self.value_min .. " - " .. self.value_max .. ") = " .. self.Value) +end + +local function On( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputActivate( mul ) +end + +local function Off( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputDeactivate( mul ) +end +numpad.Register( "WireAdvInput_On",On) +numpad.Register( "WireAdvInput_Off",Off) + + +function MakeWireAdvInput( pl, Pos, Ang, model, keymore, keyless, toggle, value_min, value_max, value_start, speed, frozen ) + if ( !pl:CheckLimit( "wire_adv_inputs" ) ) then return false end + + local wire_adv_input = ents.Create( "gmod_wire_adv_input" ) + if (!wire_adv_input:IsValid()) then return false end + + wire_adv_input:SetAngles( Ang ) + wire_adv_input:SetPos( Pos ) + wire_adv_input:SetModel( Model(model or "models/jaanus/wiretool/wiretool_input.mdl") ) + wire_adv_input:Spawn() + + if wire_adv_input:GetPhysicsObject():IsValid() then + local Phys = wire_adv_input:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + wire_adv_input:Setup( keymore, keyless, toggle, value_min, value_max, value_start, speed ) + wire_adv_input:SetPlayer(pl) + wire_adv_input. pl = pl + + numpad.OnDown( pl, keymore, "WireAdvInput_On", wire_adv_input, 1 ) + numpad.OnUp( pl, keymore, "WireAdvInput_Off", wire_adv_input, 1 ) + + numpad.OnDown( pl, keyless, "WireAdvInput_On", wire_adv_input, -1 ) + numpad.OnUp( pl, keyless, "WireAdvInput_Off", wire_adv_input, -1 ) + + pl:AddCount( "wire_adv_inputs", wire_adv_input ) + + return wire_adv_input +end + +duplicator.RegisterEntityClass("gmod_wire_adv_input", MakeWireAdvInput, "Pos", "Ang", "Model", "keymore", "keyless", "toggle", "value_min", "value_max", "value_start", "speed", "frozen") diff --git a/lua/entities/gmod_wire_adv_input/shared.lua b/lua/entities/gmod_wire_adv_input/shared.lua new file mode 100644 index 0000000000..ff6c92e702 --- /dev/null +++ b/lua/entities/gmod_wire_adv_input/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Advanced Input" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_adv_pod/cl_init.lua b/lua/entities/gmod_wire_adv_pod/cl_init.lua new file mode 100644 index 0000000000..8548a79603 --- /dev/null +++ b/lua/entities/gmod_wire_adv_pod/cl_init.lua @@ -0,0 +1,18 @@ +include("shared.lua") +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end + +local bindlist = ENT.bindlist +ENT.bindlist = nil + +hook.Add("PlayerBindPress", "wire_adv_pod", function(ply, bind, pressed) + if ply:InVehicle() then + if bindlist[bind] then + RunConsoleCommand("wire_adv_pod_bind", bind) + end + end +end) diff --git a/lua/entities/gmod_wire_adv_pod/init.lua b/lua/entities/gmod_wire_adv_pod/init.lua new file mode 100644 index 0000000000..8330ccc477 --- /dev/null +++ b/lua/entities/gmod_wire_adv_pod/init.lua @@ -0,0 +1,403 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +ENT.WireDebugName = "Advanced Pod Controller" +local MODEL = Model("models/jaanus/wiretool/wiretool_siren.mdl") + +local keys, bindlist = ENT.keys, ENT.bindlist +ENT.keys, ENT.bindlist = nil, nil + +concommand.Add("wire_adv_pod_bind", function(ply, command, args) + local bind = args[1] + if not bind then return end + + local output = bindlist[bind] + if not output then return end + + local allpods = ents.FindByClass("gmod_wire_adv_pod") + for _,apod in ipairs(allpods) do + if (ply:GetVehicle() == apod.Pod) then + Wire_TriggerOutput(apod, output, 1) + timer.Simple(0.03, Wire_TriggerOutput, apod, output, 0) + end + end +end) + + +function ENT:Initialize() + --self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:SetUseType(SIMPLE_USE) + + self.lockvar = 0 + self.disablevar = false + self.crossvar = 0 + self.Entity:SetColor( 255, 0, 0, 255 ) + + local outputs = { + --aim + "X", "Y", "Z", "AimPos [VECTOR]", + + "Active", "ThirdPerson", + "Team", "Health", "Armor", + "Distance", "Bearing", "Elevation", + --"AimVector [VECTOR]", --TODO: place holder for later + } + for index,output,bind in ipairs_map(bindlist, unpack) do + table.insert(outputs, index, output) + end + for index,output,inkey in ipairs_map(keys, unpack) do + table.insert(outputs, index, output) + end + + self.VPos = Vector(0, 0, 0) + + -- Create "Entity" output - should be at the end + table.insert(outputs, "Entity [ENTITY]") + + local outputtypes = {} + for i = 1,#outputs do + local name, tp = outputs[i]:match("^(.*) %[(.*)%]$") + if name then + outputs[i] = name + outputtypes[i] = tp + else + outputtypes[i] = "NORMAL" + end + end + + //self.Outputs = Wire_CreateOutputs( self.Entity, outputs ) + self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, outputs, outputtypes) + self.Inputs = Wire_CreateInputs( self.Entity, { "Lock", "Terminate", "Strip weapons", "Eject", "Disable", "Crosshairs", "Brake", "Allow Buttons", "Relative", "Damage Health", "Damage Armor"} ) + self:SetOverlayText( "Adv. Pod Controller" ) + + self.pushbuttons = false + self.LastPressed = 0 + self.BE_rel = false +end + +function ENT:Link(pod,RC) + if !pod then return false end + self.Pod = pod + self.RC = RC + return true +end + +local function RCEject(Ply) + Ply.Active = false + Ply:SetMoveType(2) + Ply:DrawViewModel(true) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Lock") then + if self.RC then return end + if !(self.Pod && self.Pod:IsValid()) then return end + if (value > 0) then + if (self.lockvar == 0) then + self.Pod:Fire("Lock", "1", 0) + self.lockvar = 1 + else + self.Pod:Fire("Unlock", "1", 0) + self.lockvar = 0 + end + end + elseif (iname == "Terminate") then + if self.Ply and self.Ply:IsValid() then + if (value > 0) then + if self.RC then + RCEject(self.Ply) + self.Ply.Linked = false + end + self.Ply:Kill() + end + end + elseif (iname == "Strip weapons") then + if self.Ply and self.Ply:IsValid() then + if (value > 0) then + if self.RC then + RCEject(self.Ply) + self.Ply.Linked = false + self.Ply:PrintMessage(HUD_PRINTTALK,"Your control has been terminated, and your weapons stripped!\n") + end + self.Ply:StripWeapons( ) + self.Ply:PrintMessage(HUD_PRINTTALK,"Your weapons have been stripped!\n") + end + end + elseif (iname == "Eject") then + if self.Ply and self.Ply:IsValid() then + if (value > 0) then + if self.RC then + RCEject(self.Ply) + else + self.Ply:ExitVehicle( ) + end + end + end + elseif (iname == "Disable") then + self.disablevar = (value >= 1) + elseif (iname == "Crosshairs") then + if (value > 0) then + if self.Ply and self.Ply:IsValid() then + if (self.crossvar == 0) then + self.Ply:CrosshairEnable() + self.crossvar = 1 + else + self.Ply:CrosshairDisable() + self.crossvar = 0 + end + end + end + elseif (iname == "Brake") then + if self.RC then return end + if value > 0 then + self.Pod:Fire("TurnOff", "1", 0) + self.Pod:Fire("HandBrakeOn", "1", 0) + else + self.Pod:Fire("TurnOn", "1", 0) + self.Pod:Fire("HandBrakeOff", "1", 0) + end + elseif (iname == "Damage Health") then + if self.Ply and self.Ply:IsValid() then + if value > 0 then + if value > 100 then value = 100 end + self.Ply:TakeDamage(value) + end + end + elseif (iname == "Damage Armor") then + if self.Ply and self.Ply:IsValid() then + if value > 0 then + if value > 100 then value = 100 end + local armordam = self.Ply:Armor() - value + if armordam < 0 then armordam = 0 end + self.Ply:SetArmor(armordam) + end + end + elseif (iname == "Allow Buttons") then + if value > 0 then + self.pushbuttons = true + else + self.pushbuttons = false + end + elseif (iname == "Relative") then + if value > 0 then + self.BE_rel = true + else + self.BE_rel = false + end + end +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) +end + +local function fixupangle(angle) + if angle > 180 then angle = angle - 360 end + if angle < -180 then angle = angle + 360 end + return angle +end + +function ENT:Think() + local _,_,_,coloralpha = self.Entity:GetColor() + if self.Pod and self.Pod:IsValid() then + Wire_TriggerOutput( self.Entity, "Entity", self.Pod ) + local Ply = nil + if self.RC then + if !self.Pod:Alive() then self.Pod.Active = false end + if self.Pod.Active then + Ply = self.Pod + end + else + Ply = self.Pod:GetPassenger() + end + if Ply and Ply:IsValid() then + local temp = false + if self.Ply == nil then + if !self.RC then + self.junkBE = CurTime() + 2 + else + self.junkBE = nil + temp = true + end + end + self.Ply = Ply + if temp then self.Ply.Initial = self.Ply:GetAimVector():Angle() end + self.Entity:SetColor( 0, 255, 0, coloralpha ) + Wire_TriggerOutput(self.Entity, "Active", 1) + Wire_TriggerOutput(self.Entity, "ThirdPerson", self.Ply:GetInfoNum("gmod_vehicle_viewmode", 0)) + + if not self.disablevar then + for index,output,inkey in ipairs_map(keys, unpack) do + if not self.disablevar and self.Ply:KeyDownLast( inkey ) then + Wire_TriggerOutput( self.Entity, output, 1 ) + else + Wire_TriggerOutput( self.Entity, output, 0 ) + end + end + end + + --player info + Wire_TriggerOutput(self.Entity, "Team", self.Ply:Team()) + Wire_TriggerOutput(self.Entity, "Health", self.Ply:Health()) + Wire_TriggerOutput(self.Entity, "Armor", self.Ply:Armor()) + + if self.junkBE then --all this info is garbage while the player is entering the pod, junk it for the first 2 second + if self.junkBE < CurTime() then self.junkBE = nil end + else + local trace = util.GetPlayerTrace( self.Ply ) + trace.filter = {self.Ply,self.Pod} + local EyeTrace = util.TraceLine( trace ) + self.VPos = EyeTrace.HitPos + local dist = (EyeTrace.HitPos-self.Ply:GetShootPos()):Length() + Wire_TriggerOutput(self.Entity, "Distance", dist) + + Wire_TriggerOutput(self.Entity, "X", EyeTrace.HitPos.x ) + Wire_TriggerOutput(self.Entity, "Y", EyeTrace.HitPos.y ) + Wire_TriggerOutput(self.Entity, "Z", EyeTrace.HitPos.z ) + Wire_TriggerOutput(self.Entity, "AimPos", EyeTrace.HitPos ) + + local AimVectorAngle = self.Ply:GetAimVector():Angle() + + if self.BE_rel then + local PodAngle + if !self.RC then + PodAngle = self.Pod:GetAngles() + if self.Pod:GetClass() != "prop_vehicle_prisoner_pod" then + PodAngle.y = PodAngle.y + 90 + end + else + PodAngle = self.Ply.Initial + end + Wire_TriggerOutput(self.Entity, "Bearing", fixupangle((AimVectorAngle.y - PodAngle.y))) + Wire_TriggerOutput(self.Entity, "Elevation", fixupangle(-(AimVectorAngle.p - PodAngle.p))) + else + Wire_TriggerOutput(self.Entity, "Bearing", fixupangle((AimVectorAngle.y))) + Wire_TriggerOutput(self.Entity, "Elevation", fixupangle(-AimVectorAngle.p)) + end + + if self.pushbuttons then + if EyeTrace.Entity and EyeTrace.Entity:IsValid() and EyeTrace.Entity:GetClass() == "gmod_wire_button" and dist < 256 and self.Ply:KeyDownLast( IN_ATTACK ) then + if EyeTrace.Entity.Toggle then + if self.LastPressed + 0.5 < CurTime() then + EyeTrace.Entity:Switch(not EyeTrace.Entity:IsOn()) + self.LastPressed = CurTime() + end + elseif not EyeTrace.Entity:IsOn() then + EyeTrace.Entity:Switch(true) + EyeTrace.Entity.PrevUser = self.Ply + EyeTrace.Entity.podpress = true + end + end + end + end + else -- if ValidEntity(Ply) + if self.Ply then --clear outputs + Wire_TriggerOutput(self.Entity, "Active", 0) + Wire_TriggerOutput(self.Entity, "ThirdPerson", 0) + self.Entity:SetColor( 255, 0, 0, coloralpha ) + for index,output,inkey in ipairs_map(keys, unpack) do + Wire_TriggerOutput(self.Entity, output, 0) + end + self.Pod.Initial = nil + Wire_TriggerOutput(self.Entity, "Team", 0) + Wire_TriggerOutput(self.Entity, "Health", 0) + Wire_TriggerOutput(self.Entity, "Armor", 0) + Wire_TriggerOutput(self.Entity, "Distance", 0) + Wire_TriggerOutput(self.Entity, "X", 0) + Wire_TriggerOutput(self.Entity, "Y", 0) + Wire_TriggerOutput(self.Entity, "Z", 0) + Wire_TriggerOutput(self.Entity, "AimPos", Vector(0, 0, 0)) + Wire_TriggerOutput(self.Entity, "Bearing", 0) + Wire_TriggerOutput(self.Entity, "Elevation", 0) + --self.Ply.Initial = nil + end + self.Ply = nil + end -- if ValidEntity(Ply) + if self.disablevar then + for index,output,inkey in ipairs_map(keys, unpack) do + Wire_TriggerOutput( self.Entity, output, 0 ) + end + Wire_TriggerOutput(self.Entity, "Disabled", 1) + else + Wire_TriggerOutput(self.Entity, "Disabled", 0) + end + end + self.Entity:NextThink(CurTime() + 0.01) + return true +end + +function ENT:GetBeaconPos(sensor) + return self.VPos +end + +--Duplicator support to save pod link (TAD2020) +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if (self.Pod) and (self.Pod:IsValid()) and (!self.RC) then + info.pod = self.Pod:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + if (info.pod) then + self.Pod = GetEntByID(info.pod) + if (!self.Pod) then + self.Pod = ents.GetByIndex(info.pod) + end + if not self.Pod.GetPassenger then + self.Pod = nil + end + end +end + +function ENT:Use( User, caller ) + User:PrintMessage(HUD_PRINTTALK, "Hold down your use key for 2 seconds to get and link a Remote Controller.") + timer.Create("adv_pod_use_"..self:EntIndex(), 2, 1, function(self, User) + if not User then return end + if not User:IsValid() then return end + if not User:IsPlayer() then return end + if not User:KeyDown(IN_USE) then return end + + if not User:GetWeapon("RemoteController"):IsValid() then + User:Give("RemoteController") + end + + if self:Link(User, true) then + User:PrintMessage(HUD_PRINTTALK, "You are now linked!") + User.Linked = true + else + User:PrintMessage(HUD_PRINTTALK, "Link failed!") + end + end, self, User) +end + + +function MakeWireAdvPod(pl, Pos, Ang, model, frozen) + if not pl:CheckLimit("wire_pods") then return false end + + local wire_pod = ents.Create("gmod_wire_adv_pod") + if not wire_pod:IsValid() then return false end + wire_pod:SetModel( model or MODEL ) + wire_pod:SetAngles(Ang) + wire_pod:SetPos(Pos) + wire_pod:Spawn() + + if wire_pod:GetPhysicsObject():IsValid() then + wire_pod:GetPhysicsObject():EnableMotion(!frozen) + end + + wire_pod:SetPlayer(pl) + wire_pod.pl = pl + + pl:AddCount("wire_pods", wire_pod) + pl:AddCleanup( "gmod_wire_adv_pod", wire_pod ) + + return wire_pod +end +duplicator.RegisterEntityClass("gmod_wire_adv_pod", MakeWireAdvPod, "Pos", "Ang", "Model", "frozen") diff --git a/lua/entities/gmod_wire_adv_pod/shared.lua b/lua/entities/gmod_wire_adv_pod/shared.lua new file mode 100644 index 0000000000..b87afc7565 --- /dev/null +++ b/lua/entities/gmod_wire_adv_pod/shared.lua @@ -0,0 +1,69 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Advanced Pod Controller" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +-- Output keys. Format: keys[index] = { "Output", IN_* } +ENT.keys = { + { "W", IN_FORWARD }, + { "A", IN_MOVELEFT }, + { "S", IN_BACK }, + { "D", IN_MOVERIGHT }, + { "Mouse1", IN_ATTACK }, + { "Mouse2", IN_ATTACK2 }, + { "R", IN_RELOAD }, + { "Space", IN_JUMP }, + --{ "Duck", IN_DUCK }, -- Doesn't work with pods, apparently + { "Shift", IN_SPEED }, + { "Zoom", IN_ZOOM }, + { "Alt", IN_WALK }, + { "TurnLeftKey", IN_LEFT }, + { "TurnRightKey", IN_RIGHT }, +} + +-- Output client-side binds. Format: keys[index] = { "Output", "bind" } +ENT.bindlist = { + { "PrevWeapon", "invprev" }, + { "NextWeapon", "invnext" }, + { "Light", "impulse 100" }, +} + +-- prepare for lookup +for index,output,bind in ipairs_map(ENT.bindlist, unpack) do + ENT.bindlist[bind] = output +end + + +function ENT:SetEffect(name) + self.Entity:SetNetworkedString("Effect",name) +end + +function ENT:GetEffect(name) + return self.Entity:GetNetworkedString("Effect") +end + + +function ENT:SetOn(boolon) + self.Entity:SetNetworkedBool("On",boolon,true) +end + +function ENT:IsOn(name) + return self.Entity:GetNetworkedBool("On") +end + + +function ENT:SetOffset(v) + self.Entity:SetNetworkedVector("Offset",v,true) +end + +function ENT:GetOffset(name) + return self.Entity:GetNetworkedVector("Offset") +end diff --git a/lua/entities/gmod_wire_button/cl_init.lua b/lua/entities/gmod_wire_button/cl_init.lua new file mode 100644 index 0000000000..2f96e30fee --- /dev/null +++ b/lua/entities/gmod_wire_button/cl_init.lua @@ -0,0 +1,27 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + +/*--------------------------------------------------------- + Name: DrawTranslucent + Desc: Draw translucent +---------------------------------------------------------*/ +function ENT:DrawTranslucent() + + if ( LocalPlayer():GetEyeTrace().Entity == self.Entity && EyePos():Distance( self.Entity:GetPos() ) < 512 ) then + + if ( self:IsOn() ) then + self:DrawEntityOutline( 1.05 + math.sin( CurTime() * 60 ) * 0.05 ) + else + self:DrawEntityOutline( 1.0 ) + end + + end + + self:Draw() + +end + diff --git a/lua/entities/gmod_wire_button/init.lua b/lua/entities/gmod_wire_button/init.lua new file mode 100644 index 0000000000..943e8ca755 --- /dev/null +++ b/lua/entities/gmod_wire_button/init.lua @@ -0,0 +1,166 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Button" +ENT.OverlayDelay = 0 +ENT.OutputEntID = false +ENT.EntToOutput = NULL + +local anims = { + -- ["model"] = { on_anim, off_anim } + ["models/props/switch001.mdl"] = { 2, 1 }, + ["models/props_combine/combinebutton.mdl"] = { 3, 2 }, + ["models/props_mining/control_lever01.mdl"] = { 1, 4 }, + ["models/props_mining/freightelevatorbutton01.mdl"] = { 1, 2 }, + ["models/props_mining/freightelevatorbutton02.mdl"] = { 1, 2 }, + ["models/props_mining/switch01.mdl"] = { 1, 2 }, + ["models/props_mining/switch_updown01.mdl"] = { 2, 3 }, +} + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:SetUseType( SIMPLE_USE ) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) + self.Inputs = Wire_CreateInputs(self.Entity, { "Set" }) + local anim = anims[self:GetModel()] + if anim then self:SetSequence(anim[2]) end +end + +function ENT:TriggerInput(iname, value) + if iname == "Set" then + if (self.toggle) then + self:Switch(value ~= 0) + end + end +end + +function ENT:Use(ply) + if (not ply:IsPlayer()) then return end + if (self.PrevUser) and (self.PrevUser:IsValid()) then return end + if self.OutputEntID then + self.EntToOutput = ply + end + if (self:IsOn()) then + if (self.toggle) then self:Switch(false) end + + return + end + + self:Switch(true) + self.PrevUser = ply +end + +function ENT:Think() + self.BaseClass.Think(self) + + if ( self:IsOn() ) then + if (not self.PrevUser) + or (not self.PrevUser:IsValid()) + or (not self.podpress and not self.PrevUser:KeyDown(IN_USE)) + or (self.podpress and not self.PrevUser:KeyDown( IN_ATTACK )) then + if (not self.toggle) then + self:Switch(false) + end + + self.PrevUser = nil + self.podpress = nil + end + + self.Entity:NextThink(CurTime()+0.05) + return true + end +end + +function ENT:Setup(toggle, value_off, value_on, entityout) + self.toggle = toggle + self.value_off = value_off + self.value_on = value_on + self.Value = value_off + self.entityout = entityout + self:SetOn( false ) + + self:ShowOutput(self.value_off) + Wire_TriggerOutput(self.Entity, "Out", self.value_off) + + if entityout then + WireLib.AdjustSpecialOutputs(self.Entity, { "Out", "EntID" , "Entity" }, { "NORMAL", "NORMAL" , "ENTITY" }) + Wire_TriggerOutput(self.Entity, "EntID", 0) + Wire_TriggerOutput(self.Entity, "Entity", nil) + self.OutputEntID=true + else + Wire_AdjustOutputs(self.Entity, { "Out" }) + self.OutputEntID=false + end + + if toggle then + Wire_AdjustInputs(self.Entity, { "Set" }) + else + Wire_AdjustInputs(self.Entity, {}) + end +end + +function ENT:Switch(on) + if (not self.Entity:IsValid()) then return end + + self:SetOn( on ) + + if (on) then + self:ShowOutput(self.value_on) + self.Value = self.value_on + + local anim = anims[self:GetModel()] + if anim then self:SetSequence(anim[1]) end + else + self:ShowOutput(self.value_off) + self.Value = self.value_off + + local anim = anims[self:GetModel()] + if anim then self:SetSequence(anim[2]) end + + if self.OutputEntID then self.EntToOutput = NULL end + end + + Wire_TriggerOutput(self.Entity, "Out", self.Value) + if self.OutputEntID then + Wire_TriggerOutput(self.Entity, "EntID", self.EntToOutput:EntIndex()) + Wire_TriggerOutput(self.Entity, "Entity", self.EntToOutput) + end + return true +end + +function ENT:ShowOutput(value) + self:SetOverlayText( "(" .. self.value_off .. " - " .. self.value_on .. ") = " .. value ) +end + + +function MakeWireButton( pl, Pos, Ang, model, toggle, value_off, value_on, description, entityout, frozen ) + if ( !pl:CheckLimit( "wire_buttons" ) ) then return false end + + local wire_button = ents.Create( "gmod_wire_button" ) + if (!wire_button:IsValid()) then return false end + + wire_button:SetModel(model) + wire_button:SetAngles(Ang) + wire_button:SetPos(Pos) + wire_button:Spawn() + + if wire_button:GetPhysicsObject():IsValid() then + local Phys = wire_button:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + wire_button:Setup(toggle, value_off, value_on, entityout ) + wire_button:SetPlayer(pl) + wire_button.pl = pl + + pl:AddCount( "wire_buttons", wire_button ) + + return wire_button +end + +duplicator.RegisterEntityClass("gmod_wire_button", MakeWireButton, "Pos", "Ang", "Model", "toggle", "value_off", "value_on", "description", "entityout", "frozen" ) diff --git a/lua/entities/gmod_wire_button/shared.lua b/lua/entities/gmod_wire_button/shared.lua new file mode 100644 index 0000000000..262bd46836 --- /dev/null +++ b/lua/entities/gmod_wire_button/shared.lua @@ -0,0 +1,19 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Button" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetOn( bOn ) + self.Entity:SetNetworkedBool( "OnOff", bOn, true ) +end +function ENT:IsOn() + return self.Entity:GetNetworkedBool( "OnOff" ) +end diff --git a/lua/entities/gmod_wire_cam/cl_init.lua b/lua/entities/gmod_wire_cam/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_cam/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_cam/init.lua b/lua/entities/gmod_wire_cam/init.lua new file mode 100644 index 0000000000..011d30ff23 --- /dev/null +++ b/lua/entities/gmod_wire_cam/init.lua @@ -0,0 +1,92 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Camera" + +function ENT:Initialize() + self.phys = self:GetPhysicsObject() + if not self.phys:IsValid() then self.phys = self end + + self.IdealPos = self:GetPos() + self.IdealAng = self:GetAngles() + self.IdealVel = self.phys:GetVelocity() +end + +--[[ +function ENT:Think() + --if ValidEntity(self:GetParent()) then return end + if self:GetPos() ~= self.IdealPos then + self:SetPos(self.IdealPos) + end + if self:GetAngles() ~= self.IdealAng then + self:SetAngles(self.IdealAng) + end + if self.phys:GetVelocity() ~= self.IdealVel then + self.phys:SetVelocity(self.IdealVel) + end + if self:GetColor() ~= Color(0, 0, 0, 0) then + self:SetColor(0, 0, 0, 0) + end + self:NextThink(CurTime()+0.1) +end +]] + +function ENT:ReceiveInfo(iname, value) + self.IdealAng = self:GetAngles() + if iname == "X" then + self.IdealPos.x = value + self:SetPos(self.IdealPos) + elseif iname == "Y" then + self.IdealPos.y = value + self:SetPos(self.IdealPos) + elseif iname == "Z" then + self.IdealPos.z = value + self:SetPos(self.IdealPos) + elseif iname == "Position" then + self.IdealPos = value + self:SetPos(self.IdealPos) + + elseif iname == "Pitch" then + self.IdealAng.p = value + --self:SetAngles(self.IdealAng) + elseif iname == "Yaw" then + self.IdealAng.y = value + --self:SetAngles(self.IdealAng) + elseif iname == "Roll" then + self.IdealAng.r = value + --self:SetAngles(self.IdealAng) + elseif iname == "Angle" then + self.IdealAng = value + --self:SetAngles(self.IdealAng) + + elseif iname == "Direction" then + self.IdealAng = value:Angle() + --self:SetAngles(self.IdealAng) + elseif iname == "Velocity" then + self.IdealVel = value + self.phys:SetVelocity(self.IdealVel) + elseif iname == "Parent" then + if ValidEntity(value) then + self:SetParent(value) + else + self:SetParent(nil) + end + end + if self:GetAngles() ~= self.IdealAng then + local parent = self:GetParent() + self:SetParent(nil) + self:SetAngles(self.IdealAng) + self:SetParent(parent) + end +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end diff --git a/lua/entities/gmod_wire_cam/shared.lua b/lua/entities/gmod_wire_cam/shared.lua new file mode 100644 index 0000000000..b5e91c9c85 --- /dev/null +++ b/lua/entities/gmod_wire_cam/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Camera" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamRange(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamRange() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_cameracontroller/cl_init.lua b/lua/entities/gmod_wire_cameracontroller/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_cameracontroller/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_cameracontroller/init.lua b/lua/entities/gmod_wire_cameracontroller/init.lua new file mode 100644 index 0000000000..6643f53ddc --- /dev/null +++ b/lua/entities/gmod_wire_cameracontroller/init.lua @@ -0,0 +1,153 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Camera Controller" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Outputs = Wire_CreateOutputs(self.Entity, {"On", "X", "Y", "Z"}) + self.Active = false + self.OriginalOwner = nil + self.CamEnt = nil + self.CamPlayer = nil + self.CamPod = nil + self.ZoomAmount = 0 + self.OriginalFOV = 0 + self.Static = 0 +end + +function ENT:Setup(Player, Static) + if Player and Player:IsValid() and Player:IsPlayer() then + self.CamPlayer = Player + self.OriginalOwner = Player + self.OriginalFOV = self.CamPlayer:GetFOV() + end + + if Static == 0 then + local cam = ents.Create("gmod_wire_cam") -- TODO: RT camera + if not cam:IsValid() then return false end + + cam:SetAngles( Vector(0, 0, 0) ) + cam:SetPos( self:GetPos() ) + cam:SetModel( Model("models/props_junk/PopCan01a.mdl") ) + cam:SetColor(0, 0, 0, 0) + cam:Spawn() + + self.CamEnt = cam + self.Inputs = WireLib.CreateSpecialInputs(self.Entity, {"Activated", "Zoom", "X", "Y", "Z", "Pitch", "Yaw", "Roll", "Angle", "Position", "Direction", "Velocity", "Parent"}, {"NORMAL", "NORMAL", "NORMAL", "NORMAL", "NORMAL", "NORMAL", "NORMAL", "NORMAL", "ANGLE", "VECTOR", "VECTOR", "VECTOR", "ENTITY"}) + else + self.Inputs = Wire_CreateInputs(self.Entity, {"Activated", "Zoom"}) + self.Static = 1 + end +end + +function ENT:Think() + self.BaseClass.Think(self) + + local vStart = self.CamEnt:GetPos() + local vForward = self.CamEnt:GetForward() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * 100000) + trace.filter = { self.CamEnt } + local trace = util.TraceLine( trace ) + + if trace.HitPos then + Wire_TriggerOutput(self.Entity, "X", trace.HitPos.x) + Wire_TriggerOutput(self.Entity, "Y", trace.HitPos.y) + Wire_TriggerOutput(self.Entity, "Z", trace.HitPos.z) + else + Wire_TriggerOutput(self.Entity, "X", 0) + Wire_TriggerOutput(self.Entity, "Y", 0) + Wire_TriggerOutput(self.Entity, "Z", 0) + end + + self.Entity:NextThink(CurTime()+0.1) + return true +end + +function ENT:OnRemove() + if self.CamEnt and self.CamEnt:IsValid() then + self.CamEnt:Remove() + end + + if self.Active == 1 then + self.CamPlayer:SetViewEntity(self.CamPlayer) + end + Wire_Remove(self.Entity) +end + +function ENT:TriggerInput(iname, value) + if iname == "Activated" then + if value == 0 then + self.Active = 0 + Wire_TriggerOutput(self.Entity, "On", 0) + if not ValidEntity(self.CamPlayer) then return end + self.CamPlayer:SetViewEntity(self.CamPlayer) + self.CamPlayer:SetFOV(self.OrginialFOV, 0.01) + else + if self.CamPod then + if self.CamPod:GetDriver() and self.CamPod:GetDriver():IsValid() then + self.CamPlayer = self.CamPod:GetDriver() + else + self.CamPlayer = self.OriginalOwner + end + end + self.CamPlayer:SetViewEntity(self.CamEnt) + self.CamPlayer:SetFOV(self.ZoomAmount, 0.01) + self.Active = 1 + Wire_TriggerOutput(self.Entity, "On", 1) + end + elseif iname == "Zoom" /* and not self.RT */ then + self.ZoomAmount = math.Clamp(value, 1, self.OriginalFOV) + if self.Active == 1 then + self.CamPlayer:SetFOV(self.ZoomAmount, 0.01) -- TODO: RT camera + end + else + if self.CamEnt then + self.CamEnt:ReceiveInfo(iname, value) + end + end +end + +function ENT:ShowOutput() + local text = "Wired Camera" + self:SetOverlayText( text ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if self.CamPod and self.CamPod:IsValid() then + info.pod = self.CamPod:EntIndex() + end + if self.CamEnt and self.CamEnt:IsValid() and self.Static ~= 0 then + info.cam = self.CamEnt:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + if info.pod then + self.CamPod = GetEntByID(info.pod) + if not self.CamPod then + self.CamPod = ents.GetByIndex(info.pod) + end + end + if info.cam then + self.CamEnt = GetEntByID(info.cam) + if not self.CamEnt then + self.CamEnt = ents.GetByIndex(info.cam) + end + end +end diff --git a/lua/entities/gmod_wire_cameracontroller/shared.lua b/lua/entities/gmod_wire_cameracontroller/shared.lua new file mode 100644 index 0000000000..d78da5ca69 --- /dev/null +++ b/lua/entities/gmod_wire_cameracontroller/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Camera Controller" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamRange(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamRange() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_cd_disk/cl_init.lua b/lua/entities/gmod_wire_cd_disk/cl_init.lua new file mode 100644 index 0000000000..48d2275a21 --- /dev/null +++ b/lua/entities/gmod_wire_cd_disk/cl_init.lua @@ -0,0 +1,10 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_cd_disk/init.lua b/lua/entities/gmod_wire_cd_disk/init.lua new file mode 100644 index 0000000000..d258e1464d --- /dev/null +++ b/lua/entities/gmod_wire_cd_disk/init.lua @@ -0,0 +1,107 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + +include('shared.lua') + +ENT.WireDebugName = "CD" + +local MODEL = Model("models/jaanus/wiretool/wiretool_range.mdl") + +function ENT:Initialize() +// self.Entity:SetModel(MODEL) + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.DiskMemory = {} + self.Precision = 1 //1 unit + self.IRadius = 12 //units + + //Use Z axis for Sector address + //Use XY radius for Track address + //Use Z height for Stack address + self:Setup() +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + info.Precision = self.Precision + info.IRadius = self.IRadius + info["DiskMemory"] = {} + + local dataptr = 0 + for k,v in pairs(self.DiskMemory) do + info["DiskMemory"][k] = dataptr + info["DiskData"..dataptr] = {} + for k2,v2 in pairs(self.DiskMemory[k]) do + info["DiskData"..dataptr][k2] = v2 + end + dataptr = dataptr + 1 + end + + return info +end + + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + self.Precision = info.Precision + self.IRadius = info.IRadius + self.DiskMemory = {} + + for k,v in pairs(info["DiskMemory"]) do + local dataptr = info["DiskMemory"][k] + self.DiskMemory[k] = {} + for k2,v2 in pairs(info["DiskData"..dataptr]) do + self.DiskMemory[k][k2] = v2 + end + end + + self:Setup() +end + +function ENT:Setup() + local min = self:OBBMins() + local max = self:OBBMaxs() + + self.Precision = math.floor(math.Clamp(self.Precision,1,64)) + self.IRadius = math.max(self.IRadius,0) + + self.StackStartHeight = -min.z + + self.DiskStacks = math.max(1,math.floor((max.z - min.z) / self.Precision)+1) + self.DiskTracks = math.floor(0.5*math.min(max.x - min.x,max.y - min.y) / self.Precision) + + self.DiskSectors = 0 + self.TrackSectors = {} + self.FirstTrack = math.floor((self.IRadius) / self.Precision) + for i=self.FirstTrack,self.DiskTracks-1 do + self.TrackSectors[i] = self.DiskSectors + self.DiskSectors = self.DiskSectors + math.floor(2*3.1415926*i) + 1 + end + + self.DiskVolume = self.DiskSectors*self.DiskStacks + self.BytesPerBlock = 512//*self.Precision + self.DiskSize = self.DiskSectors*self.BytesPerBlock + +// print("Precision: "..(self.Precision)) +// print("H: "..(max.z - min.z)) +// print("R: "..(0.5*((max.x - min.x)^2+(max.y - min.y)^2)^0.5)) +// print("Disk stacks: "..self.DiskStacks) +// print("Disk tracks: "..self.DiskTracks) +// print("Disk sectors total: "..self.DiskSectors) +// print("Disk volume "..self.DiskVolume) + + self:ShowOutput() +end + +function ENT:ShowOutput() + self:SetOverlayText("CD disk\nEffective size (per stack): "..self.DiskSize.." bytes ("..math.floor(self.DiskSize/1024).." kb)\n".. + "Tracks: "..self.DiskTracks.."\nSectors: "..self.DiskSectors.."\nStacks: "..self.DiskStacks) +end diff --git a/lua/entities/gmod_wire_cd_disk/shared.lua b/lua/entities/gmod_wire_cd_disk/shared.lua new file mode 100644 index 0000000000..03e9ca251c --- /dev/null +++ b/lua/entities/gmod_wire_cd_disk/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire CD Disk" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_cd_lock/cl_init.lua b/lua/entities/gmod_wire_cd_lock/cl_init.lua new file mode 100644 index 0000000000..48d2275a21 --- /dev/null +++ b/lua/entities/gmod_wire_cd_lock/cl_init.lua @@ -0,0 +1,10 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_cd_lock/init.lua b/lua/entities/gmod_wire_cd_lock/init.lua new file mode 100644 index 0000000000..262e0a34b9 --- /dev/null +++ b/lua/entities/gmod_wire_cd_lock/init.lua @@ -0,0 +1,121 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + +include('shared.lua') + +ENT.WireDebugName = "CD Lock" + +//Time after loosing one disk to search for another +local NEW_DISK_WAIT_TIME = 2 +local DISK_IN_SOCKET_CONSTRAINT_POWER = 5000 +local DISK_IN_ATTACH_RANGE = 16 + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Const = nil + self.Disk = nil + self.DisableLinking = 0 + + self.Inputs = Wire_CreateOutputs(self.Entity, { "Disable" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Locked" }) + self:SetOverlayText("CD lock") + + self.Entity:NextThink(CurTime() + 0.25) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Disable") then + self.DisableLinking = value + if (value >= 1) and (self.Const) then + self.Const:Remove() + //self.NoCollideConst:Remove() + + self.Const = nil + self.Disk.Lock = nil + self.Disk = nil + //self.NoCollideConst = nil + + Wire_TriggerOutput(self.Entity, "Locked", 0) + self.Entity:NextThink(CurTime() + NEW_DISK_WAIT_TIME) + end + end +end + +function ENT:Think() + self.BaseClass.Think(self) + + // If we were undiskged, reset the disk and socket to accept new ones. + if (self.Const) and (not self.Const:IsValid()) then + self.Const = nil + self.Disk.Lock = nil + self.Disk = nil + self.NoCollideConst = nil + + Wire_TriggerOutput(self.Entity, "Locked", 0) + + self.Entity:NextThink(CurTime() + NEW_DISK_WAIT_TIME) //Give time before next grabbing a disk. + return true + else + if (self.DisableLinking < 1) and (self.Disk == nil) then + // Find entities near us + local lockCenter = self:LocalToWorld(Vector(0, 0, 0)) + local local_ents = ents.FindInSphere(lockCenter, DISK_IN_ATTACH_RANGE) + for key, disk in pairs(local_ents) do + // If we find a disk, try to attach it to us + if (disk:IsValid() && disk:GetClass() == "gmod_wire_cd_disk") then + if (disk.Lock == nil) then + self:AttachDisk(disk) + end + end + end + end + end + self.Entity:NextThink(CurTime() + 0.25) +end + +function ENT:AttachDisk(disk) + //Position disk + local min = disk:OBBMins() + local max = disk:OBBMaxs() + + local newpos = self:LocalToWorld(Vector(0, 0, 0)) + local lockAng = self.Entity:GetAngles() + disk:SetPos(newpos) + disk:SetAngles(lockAng) + + self.NoCollideConst = constraint.NoCollide(self.Entity, disk, 0, 0) + if (not self.NoCollideConst) then + Wire_TriggerOutput(self.Entity, "Locked", 0) + return + end + + //Constrain together + self.Const = constraint.Weld(self.Entity, disk, 0, 0, DISK_IN_SOCKET_CONSTRAINT_POWER, true) + if (not self.Const) then + self.NoCollideConst:Remove() + self.NoCollideConst = nil + Wire_TriggerOutput(self.Entity, "Locked", 0) + return + end + + //Prepare clearup incase one is removed + disk:DeleteOnRemove(self.Const) + self.Entity:DeleteOnRemove(self.Const) + self.Const:DeleteOnRemove(self.NoCollideConst) + + disk.Lock = self + self.Disk = disk + Wire_TriggerOutput(self.Entity, "Locked", 1) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + diff --git a/lua/entities/gmod_wire_cd_lock/shared.lua b/lua/entities/gmod_wire_cd_lock/shared.lua new file mode 100644 index 0000000000..d8c40f343a --- /dev/null +++ b/lua/entities/gmod_wire_cd_lock/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire CD Lock" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_cd_ray/cl_init.lua b/lua/entities/gmod_wire_cd_ray/cl_init.lua new file mode 100644 index 0000000000..73cf67dfbb --- /dev/null +++ b/lua/entities/gmod_wire_cd_ray/cl_init.lua @@ -0,0 +1,9 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_cd_ray/init.lua b/lua/entities/gmod_wire_cd_ray/init.lua new file mode 100644 index 0000000000..19de502867 --- /dev/null +++ b/lua/entities/gmod_wire_cd_ray/init.lua @@ -0,0 +1,285 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "CD Ray" + + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, {"Write","Read","Value"}) + self.Outputs = Wire_CreateOutputs(self.Entity, {"Data","Sector","LocalSector","Track","Stack","Address"}) + + self.Command = {} + self.Command[0] = 0 //[W] Write ray on + self.Command[1] = 0 //[W] Read ray on + self.Command[2] = 0 //[R] Current sector (global) + self.Command[3] = 0 //[R] Current sector (on track) + self.Command[4] = 0 //[R] Current track + self.Command[5] = 0 //[R] Current stack + self.Command[6] = 0 //[R] Current address (global) + self.Command[7] = 0 //[R] Current address (in current stack) + + self.Command[8] = 0 //[W] Buffer ready (read or write - pick the ray) + self.Command[9] = 0 //[W] Continious mode + self.Command[10] = 0 //[W] Wait for address mode + self.Command[11] = 0 //[W] Target address (in current stack) + self.Command[12] = 0 //[W] Wait for track§or mode + self.Command[13] = 0 //[W] Target sector + self.Command[14] = 0 //[W] Target track + + self.Command[21] = 0 //[R] Raw disk spin velocity + self.Command[22] = 0 //[R] Raw disk spin angle + self.Command[23] = 0 //[R] Raw distance from disk center + self.Command[24] = 0 //[R] Raw stack index + + self.Command[25] = 0 //[R] Disk precision (Inches Per Block) + self.Command[26] = 0 //[R] Disk sectors (total) + self.Command[27] = 0 //[R] Disk tracks (total) + self.Command[28] = 0 //[R] First track number + self.Command[29] = 0 //[R] Bytes per block + self.Command[30] = 0 //[R] Disk size (per stack) + self.Command[31] = 0 //[R] Disk volume (bytes total) + + self.WriteBuffer = {} + self.PrevDiskEnt = nil + + self:SetBeamRange(64) + self:ShowOutput() +end + +function ENT:ReadCell(Address) + if (Address >= 0) && (Address < 512) then + if (self.Command[Address]) then + return self.Command[Address] + else + return 0 + end + end + if (Address >= 512) && (Address < 1024) then + if (self.WriteBuffer[Address-512]) then + return self.WriteBuffer[Address-512] + else + return 0 + end + end + return nil +end + +function ENT:WriteCell(Address, value) + if (Address >= 0) && (Address < 512) then + self.Command[Address] = value + if (Address == 8) then + self:DoJob() + end + return true + end + if (Address >= 512) && (Address < 1024) then + if (value ~= 0) then + self.WriteBuffer[Address-512] = value + else + self.WriteBuffer[Address-512] = nil + end + return true + end + return false +end + +function ENT:Setup(Range,DefaultZero) + self.DefaultZero = DefaultZero + self:SetBeamRange(Range) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Write") then + self.Command[0] = value + self.Command[8] = 1 + self.Command[9] = 1 + elseif (iname == "Read") then + self.Command[1] = value + self.Command[8] = 1 + self.Command[9] = 1 + elseif (iname == "Value") then + self.Command[8] = 1 + self.Command[9] = 1 + self.WriteBuffer[0] = value + end +end + +function ENT:DoJob() + if (not self.Disk) then return end + local disk = self.Disk + if (self.Command[8] ~= 0) then + local dojob = true + if (self.Command[9] == 0) then self.Command[8] = 0 end + if (self.Command[10] ~= 0) then + if (self.Command[11] ~= self.Command[7]) then dojob = false end + end + if (self.Command[12] ~= 0) then + if (self.Command[13] ~= self.Command[3]) || (self.Command[14] ~= self.Command[4]) then + dojob = false + end + end + + local sector_addr = self.Sector.."."..self.Track.."."..self.Stack//{s=sector,t=track,st=stack} + if (self.Command[0] ~= 0) then //write ray + disk.DiskMemory[sector_addr] = self.WriteBuffer + else //read ray + if (disk.DiskMemory[sector_addr]) then + self.WriteBuffer = disk.DiskMemory[sector_addr] + else + self.WriteBuffer = {} + self.WriteBuffer[0] = 0 + end + end + end +end + +function ENT:Think() + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamRange()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + if ((self.Command[0] ~= 0) or (self.Command[1] ~= 0)) then + if (self.Command[0] == 1) then //write ray (blue) + if (Color(self.Entity:GetColor()) != Color(0,0,255,255)) then + self.Entity:SetColor(0, 0, 255, 255) + end + else //read ray (red) + if (Color(self.Entity:GetColor()) != Color(255,0,0,255)) then + self.Entity:SetColor(255, 0, 0, 255) + end + end + else + if (Color(self.Entity:GetColor()) != Color(255,255,255,255)) then + self.Entity:SetColor(255, 255, 255, 255) + end + end + + if ((trace.Entity) and + (trace.Entity:IsValid()) and + (trace.Entity:GetClass() == "gmod_wire_cd_disk")) then + local pos = trace.HitPos + local disk = trace.Entity + local lpos = disk:WorldToLocal(pos) + + local vel = disk.Entity:GetPhysicsObject():GetAngleVelocity().z + + local r = (lpos.x^2+lpos.y^2)^0.5 //radius + local a = math.fmod(3.1415926+math.atan2(lpos.x,lpos.y),2*3.1415926) //angle + local h = lpos.z-disk.StackStartHeight //stack + + local track = math.floor(r / disk.Precision) + local sector = math.floor(a*track)//*disk.Precision) + local stack = math.floor(h/disk.Precision) + if (disk.DiskStacks == 1) then stack = 0 end + + if (self.PrevDiskEnt ~= disk) then + self.PrevDiskEnt = disk + + self.Command[25] = disk.Precision + self.Command[26] = disk.DiskSectors + self.Command[27] = disk.DiskTracks + self.Command[28] = disk.FirstTrack + self.Command[29] = disk.BytesPerBlock + self.Command[30] = disk.DiskSize + self.Command[31] = disk.DiskVolume + end + + if ((track >= disk.FirstTrack) and (stack >= 0) and (sector >= 0) and + //(track < disk.DiskTracks) and + (stack < disk.DiskStacks)) then + self.Command[21] = vel //[R] Raw disk spin velocity + self.Command[22] = a //[R] Raw disk spin angle + self.Command[23] = r //[R] Raw distance from disk center + self.Command[24] = h //[R] Raw stack index + + if (not disk.TrackSectors[track]) then disk.TrackSectors[track] = 0 end + + self.Command[2] = disk.DiskSectors*stack+disk.TrackSectors[track]+sector //[R] Current sector (global) + self.Command[3] = sector //[R] Current sector (on track) + self.Command[4] = track //[R] Current track + self.Command[5] = stack //[R] Current stack + self.Command[6] = self.Command[2]*disk.BytesPerBlock //[R] Current address (global) + self.Command[7] = (disk.TrackSectors[track]+sector)*disk.BytesPerBlock //[R] Current address (in current stack) + + if ((self.Command[0] ~= 0) or (self.Command[1] ~= 0)) then + self.Sector = sector + self.Track = track + self.Stack = stack + self.Disk = disk + self:DoJob() + end + else + self.Command[21] = 0 + self.Command[22] = 0 + self.Command[23] = 0 + self.Command[24] = 0 + + self.Command[2] = 0 + self.Command[3] = 0 + self.Command[4] = 0 + self.Command[5] = 0 + self.Command[6] = 0 + self.Command[7] = 0 + end + else + self.PrevDiskEnt = nil + self.Disk = nil + + self.Command[2] = 0 + self.Command[3] = 0 + self.Command[4] = 0 + self.Command[5] = 0 + self.Command[6] = 0 + self.Command[7] = 0 + + self.Command[21] = 0 + self.Command[22] = 0 + self.Command[23] = 0 + self.Command[24] = 0 + self.Command[25] = 0 + self.Command[26] = 0 + self.Command[27] = 0 + self.Command[28] = 0 + self.Command[29] = 0 + self.Command[30] = 0 + self.Command[31] = 0 + end + + //Update output + if (self.WriteBuffer[0]) then + Wire_TriggerOutput(self.Entity, "Data",self.WriteBuffer[0]) + else + Wire_TriggerOutput(self.Entity, "Data",0) + end + Wire_TriggerOutput(self.Entity, "Sector", self.Command[2]) + Wire_TriggerOutput(self.Entity, "LocalSector", self.Command[3]) + Wire_TriggerOutput(self.Entity, "Track", self.Command[4]) + Wire_TriggerOutput(self.Entity, "Stack", self.Command[5]) + Wire_TriggerOutput(self.Entity, "Address", self.Command[6]) + + self.Entity:NextThink(CurTime()+0.01) + return true +end + +function ENT:ShowOutput() + self:SetOverlayText("CD Ray") +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end diff --git a/lua/entities/gmod_wire_cd_ray/shared.lua b/lua/entities/gmod_wire_cd_ray/shared.lua new file mode 100644 index 0000000000..3cfbcc2c91 --- /dev/null +++ b/lua/entities/gmod_wire_cd_ray/shared.lua @@ -0,0 +1,49 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire CD Ray" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamRange(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamRange() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_colorer/cl_init.lua b/lua/entities/gmod_wire_colorer/cl_init.lua new file mode 100644 index 0000000000..32f8433103 --- /dev/null +++ b/lua/entities/gmod_wire_colorer/cl_init.lua @@ -0,0 +1,6 @@ +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_colorer/init.lua b/lua/entities/gmod_wire_colorer/init.lua new file mode 100644 index 0000000000..633de7db14 --- /dev/null +++ b/lua/entities/gmod_wire_colorer/init.lua @@ -0,0 +1,119 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Colorer" + + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = WireLib.CreateSpecialInputs(self.Entity, { "Fire", "R", "G", "B", "A", "RGB" }, {"NORMAL", "NORMAL", "NORMAL", "NORMAL", "NORMAL", "VECTOR"}) + self.Outputs = Wire_CreateOutputs(self.Entity, {"Out"}) + self.ValueR = 255 + self.ValueG = 255 + self.ValueB = 255 + self.ValueA = 255 + self:SetBeamLength(2048) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Setup(outColor,Range) + Msg("setup\n") + if(outColor)then + local onames = {} + table.insert(onames, "R") + table.insert(onames, "G") + table.insert(onames, "B") + table.insert(onames, "A") + Wire_AdjustOutputs(self.Entity, onames) + end + self:SetBeamLength(Range) + self:ShowOutput() +end + +function ENT:TriggerInput(iname, value) + if (iname == "Fire") then + if (value ~= 0) then + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamLength()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + if (!trace.Entity) then return false end + if (!trace.Entity:IsValid() ) then return false end + if (trace.Entity:IsWorld()) then return false end + if ( CLIENT ) then return true end + trace.Entity:SetColor( self.ValueR, self.ValueG, self.ValueB, self.ValueA ) + end + elseif(iname == "R") then + self.ValueR = math.max(math.min(255,value),0) + elseif(iname == "G") then + self.ValueG = math.max(math.min(255,value),0) + elseif(iname == "B") then + self.ValueB = math.max(math.min(255,value),0) + elseif(iname == "A") then + self.ValueA = math.max(math.min(255,value),0) + elseif(iname == "RGB") then + self.ValueR, self.ValueG, self.ValueB = value[1], value[2], value[3] + end +end + +function ENT:ShowOutput() + local text = "Colorer" + if(self.Outputs["R"])then + text = text .. "\nColor = " + .. math.Round(self.Outputs["R"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["G"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["B"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["A"].Value*1000)/1000 + end + self:SetOverlayText( text ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function ENT:Think() + self.BaseClass.Think(self) + if(self.Outputs["R"])then + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamLength()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + if (!trace.Entity) then return false end + if (!trace.Entity:IsValid() ) then return false end + if (trace.Entity:IsWorld()) then return false end + if ( CLIENT ) then return true end + + local r,g,b,a = trace.Entity:GetColor() + //Msg("color check\n") + //Msg("R-"..tostring(r).."\nG-"..tostring(g).."\nB-"..tostring(b).."\nA-"..tostring(a).."\n") + + Wire_TriggerOutput(self.Entity,"R",r) + Wire_TriggerOutput(self.Entity,"G",g) + Wire_TriggerOutput(self.Entity,"B",b) + Wire_TriggerOutput(self.Entity,"A",a) + + self:ShowOutput() + + end + self.Entity:NextThink(CurTime()+0.25) +end + diff --git a/lua/entities/gmod_wire_colorer/shared.lua b/lua/entities/gmod_wire_colorer/shared.lua new file mode 100644 index 0000000000..f3ea8e5e51 --- /dev/null +++ b/lua/entities/gmod_wire_colorer/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Colorer" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect() + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn() + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset() + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamLength( length ) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_consolescreen/cl_init.lua b/lua/entities/gmod_wire_consolescreen/cl_init.lua new file mode 100644 index 0000000000..8baaa8299e --- /dev/null +++ b/lua/entities/gmod_wire_consolescreen/cl_init.lua @@ -0,0 +1,568 @@ +if (not EmuFox) then + include('shared.lua') +end + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Initialize() + self.Memory1 = {} + self.Memory2 = {} + for i = 0, 2047 do + self.Memory1[i] = 0 + end + + //Caching control: + //[2020] - Force cache refresh + //[2021] - Cached blocks size (up to 28, 0 if disabled) + //Hardware image control: + //[2019] - Clear viewport defined by 2031-2034 + //[2022] - Screen ratio (read only) + //[2023] - Hardware scale + //[2024] - Rotation (0 - 0*, 1 - 90*, 2 - 180*, 3 - 270*) + //[2025] - Brightness White + //[2026] - Brightness B + //[2027] - Brightness G + //[2028] - Brightness R + //[2029] - Vertical scale (1) + //[2030] - Horizontal scale (1) + // + //Shifting control: + //[2031] - Low shift column + //[2032] - High shift column + //[2033] - Low shift row + //[2034] - High shift row + // + //Character output control: + //[2035] - Charset, always 0 + //[2036] - Brightness (additive) + // + //Control registers: + //[2037] - Shift cells (number of cells, >0 right, <0 left) + //[2038] - Shift rows (number of rows, >0 shift up, <0 shift down) + //[2039] - Hardware Clear Row (Writing clears row) + //[2040] - Hardware Clear Column (Writing clears column) + //[2041] - Hardware Clear Screen + //[2042] - Hardware Background Color (000) + // + //Cursor control: + //[2043] - Cursor Blink Rate (0.50) + //[2044] - Cursor Size (0.25) + //[2045] - Cursor Address + //[2046] - Cursor Enabled + // + //[2047] - Clk + + self.Memory1[2022] = 3/4 + self.Memory1[2023] = 0 + self.Memory1[2024] = 0 + self.Memory1[2025] = 1 + self.Memory1[2026] = 1 + self.Memory1[2027] = 1 + self.Memory1[2028] = 1 + self.Memory1[2029] = 1 + self.Memory1[2030] = 1 + self.Memory1[2031] = 0 + self.Memory1[2032] = 29 + self.Memory1[2033] = 0 + self.Memory1[2034] = 17 + self.Memory1[2035] = 0 + self.Memory1[2036] = 0 + + self.Memory1[2042] = 0 + self.Memory1[2043] = 0.5 + self.Memory1[2044] = 0.25 + self.Memory1[2045] = 0 + self.Memory1[2046] = 0 + + for i = 0, 2047 do + self.Memory2[i] = self.Memory1[i] + end + + self.LastClk = false + + self.PrevTime = CurTime() + self.IntTimer = 0 + + self.NeedRefresh = true + self.Flash = false + self.FrameNeedsFlash = false + + self.FramesSinceRedraw = 0 + self.NewClk = true + + WireGPU_NeedRenderTarget(self:EntIndex()) +end + +function ENT:OnRemove() + WireGPU_ReturnRenderTarget(self:EntIndex()) +end + +local function calcoffset(offset, Address) + offset = -100 - offset+Address + if offset >= 1048576 then offset = offset - 1048576 end + return offset +end + +usermessage.Hook("hispeed_datastream", function(um) + local ent = ents.GetByIndex(um:ReadShort()) + + if not ValidEntity(ent) then return end + if not ent.Memory1 then return end + if not ent.Memory2 then return end + + local Address = 0 + while true do + local value = um:ReadFloat() + if value < -100 then + Address = calcoffset(value, Address) + elseif value == -100 then + break + else + ent:WriteCell(Address, value) + Address = Address + 1 + end + end +end) + +function ENT:ReadCell(Address,value) + if Address < 0 then return nil end + if Address >= 2048 then return nil end + + return self.Memory2[Address] +end + +function ENT:WriteCell(Address,value) + if Address < 0 then return false end + if Address >= 2048 then return false end + + if (Address == 2047) then self.NewClk = value ~= 0 end + + if (self.NewClk) then + self.Memory1[Address] = value //Vis mem + self.NeedRefresh = true + end + self.Memory2[Address] = value //Invis mem + + //2038 - Shift rows (number of rows, >0 shift down, <0 shift up) + //2039 - Hardware Clear Row (Writing clears row) + //2040 - Hardware Clear Column (Writing clears column) + //2041 - Hardware Clear Screen + + if (Address == 2019) then + local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) + for j = low, high do + for i = 2*lowc, 2*highc+1 do + self.Memory1[60*j+i] = 0 + self.Memory2[60*j+i] = 0 + end + end + self.NeedRefresh = true + end + if (Address == 2037) then + local delta = math.abs(value) + local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) + if (value > 0) then + for j = low,high do + for i = highc,lowc+delta,-1 do + if (self.NewClk) then + self.Memory1[j*60+i*2] = self.Memory1[j*60+(i-delta)*2] + self.Memory1[j*60+i*2+1] = self.Memory1[j*60+(i-delta)*2+1] + end + self.Memory2[j*60+i*2] = self.Memory2[j*60+(i-delta)*2] + self.Memory2[j*60+i*2+1] = self.Memory2[j*60+(i-delta)*2+1] + end + end + for j = low,high do + for i = lowc, lowc+delta-1 do + if (self.NewClk) then + self.Memory1[j*60+i*2] = 0 + self.Memory1[j*60+i*2+1] = 0 + end + self.Memory2[j*60+i*2] = 0 + self.Memory2[j*60+i*2+1] = 0 + end + end + else + for j = low,high do + for i = lowc,highc-delta do + if (self.NewClk) then + self.Memory1[j*60+i*2] = self.Memory1[j*60+i*2+delta*2] + self.Memory1[j*60+i*2+1] = self.Memory1[j*60+i*2+1+delta*2] + end + self.Memory2[j*60+i*2] = self.Memory2[j*60+i*2+delta*2] + self.Memory2[j*60+i*2+1] = self.Memory2[j*60+i*2+1+delta*2] + end + end + for j = low,high do + for i = highc-delta+1,highc do + if (self.NewClk) then + self.Memory1[j*60+i*2] = 0 + self.Memory1[j*60+i*2+1] = 0 + end + self.Memory2[j*60+i*2] = 0 + self.Memory2[j*60+i*2+1] = 0 + end + end + end + end + if (Address == 2038) then + local delta = math.abs(value) + local low = math.floor(math.Clamp(self.Memory1[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory1[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory1[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory1[2032],0,29)) + if (value > 0) then + for j = low, high-delta do + for i = 2*lowc,2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = self.Memory1[(j+delta)*60+i] + end + self.Memory2[j*60+i] = self.Memory2[(j+delta)*60+i] + end + end + for j = high-delta+1,high do + for i = 2*lowc, 2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = 0 + end + self.Memory2[j*60+i] = 0 + end + end + else + for j = high,low+delta,-1 do + for i = 2*lowc, 2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = self.Memory1[(j-delta)*60+i] + end + self.Memory2[j*60+i] = self.Memory2[(j-delta)*60+i] + end + end + for j = low,low+delta-1 do + for i = 2*lowc, 2*highc+1 do + if (self.NewClk) then + self.Memory1[j*60+i] = 0 + end + self.Memory2[j*60+i] = 0 + end + end + end + end + if (Address == 2039) then + for i = 0, 59 do + self.Memory1[value*60+i] = 0 + self.Memory2[value*60+i] = 0 + end + self.NeedRefresh = true + end + if (Address == 2040) then + for i = 0, 17 do + self.Memory1[i*60+value] = 0 + self.Memory2[i*60+value] = 0 + end + self.NeedRefresh = true + end + if (Address == 2041) then + for i = 0, 18*30*2-1 do + self.Memory1[i] = 0 + self.Memory2[i] = 0 + end + self.NeedRefresh = true + end + + if (self.LastClk ~= self.NewClk) then + self.LastClk = self.NewClk + self.Memory1 = table.Copy(self.Memory2) //swap the memory if clock changes + + self.NeedRefresh = true + end + return true +end + +function ENT:DrawGraphicsChar(c,x,y,w,h,r,g,b) + surface.SetDrawColor(r,g,b,255) + surface.SetTexture(0) + + if (c == 128) then + vertex = {} + + //Generate vertex data + vertex[1] = {} + vertex[1]["x"] = x + vertex[1]["y"] = y+h + + vertex[2] = {} + vertex[2]["x"] = x+w + vertex[2]["y"] = y+h + + vertex[3] = {} + vertex[3]["x"] = x+w + vertex[3]["y"] = y + surface.DrawPoly(vertex) + end + + if (c == 129) then + vertex = {} + + //Generate vertex data + vertex[1] = {} + vertex[1]["x"] = x + vertex[1]["y"] = y+h + + vertex[2] = {} + vertex[2]["x"] = x + vertex[2]["y"] = y + + vertex[3] = {} + vertex[3]["x"] = x+w + vertex[3]["y"] = y+h + surface.DrawPoly(vertex) + end + + if (c == 130) then + vertex = {} + + //Generate vertex data + vertex[1] = {} + vertex[1]["x"] = x + vertex[1]["y"] = y+h + + vertex[2] = {} + vertex[2]["x"] = x+w + vertex[2]["y"] = y + + vertex[3] = {} + vertex[3]["x"] = x + vertex[3]["y"] = y + surface.DrawPoly(vertex) + end + + if (c == 131) then + vertex = {} + + //Generate vertex data + vertex[1] = {} + vertex[1]["x"] = x + vertex[1]["y"] = y + + vertex[2] = {} + vertex[2]["x"] = x+w + vertex[2]["y"] = y + + vertex[3] = {} + vertex[3]["x"] = x+w + vertex[3]["y"] = y+h + surface.DrawPoly(vertex) + end +end + +function ENT:Draw() + self.Entity:DrawModel() + + local DeltaTime = CurTime()-(self.PrevTime or CurTime()) + self.PrevTime = (self.PrevTime or CurTime())+DeltaTime + self.IntTimer = self.IntTimer + DeltaTime + + self.RTTexture = WireGPU_GetMyRenderTarget(self:EntIndex()) + + local NewRT = self.RTTexture + local OldRT = render.GetRenderTarget() + + local OldTex = WireGPU_matScreen:GetMaterialTexture("$basetexture") + WireGPU_matScreen:SetMaterialTexture("$basetexture",self.RTTexture) + + self.FramesSinceRedraw = self.FramesSinceRedraw + 1 + + if (self.NeedRefresh == true) then + self.FramesSinceRedraw = 0 + self.NeedRefresh = false + self.FrameNeedsFlash = false + + if (self.Memory1[2046] >= 1) then self.FrameNeedsFlash = true end + + local oldw = ScrW() + local oldh = ScrH() + + render.SetRenderTarget(NewRT) + render.SetViewPort(0,0,512,512) + cam.Start2D() + //Draw terminal here + //W/H = 16 + local szx = 512/31 + local szy = 512/19 + + local ch = self.Memory1[2042] + + local hb = 28*math.fmod(ch,10) + local hg = 28*math.fmod(math.floor(ch / 10),10) + local hr = 28*math.fmod(math.floor(ch / 100),10) + surface.SetDrawColor(hr,hg,hb,255) + surface.DrawRect(0,0,512,512) + + for ty = 0, 17 do + for tx = 0, 29 do + local a = tx + ty*30 + local c1 = self.Memory1[2*a] + local c2 = self.Memory1[2*a+1] + + local cback = math.floor(c2 / 1000) + local cfrnt = c2 - math.floor(c2 / 1000)*1000 + + local fb = math.Clamp(28*math.fmod(cfrnt,10) + self.Memory1[2036],0,255) + local fg = math.Clamp(28*math.fmod(math.floor(cfrnt / 10),10) + self.Memory1[2036],0,255) + local fr = math.Clamp(28*math.fmod(math.floor(cfrnt / 100),10) + self.Memory1[2036],0,255) + local bb = math.Clamp(28*math.fmod(cback,10) + self.Memory1[2036],0,255) + local bg = math.Clamp(28*math.fmod(math.floor(cback / 10),10) + self.Memory1[2036],0,255) + local br = math.Clamp(28*math.fmod(math.floor(cback / 100),10) + self.Memory1[2036],0,255) + + if (self.Flash == true) && (cback > 999) then + fb,bb = bb,fb + fg,bg = bg,fg + fr,br = br,fr + end + + if (cback > 999) then + self.FrameNeedsFlash = true + end + + if (c1 > 255) then c1 = 0 end + if (c1 < 0) then c1 = 0 end + + if (cback ~= 0) then + surface.SetDrawColor(br,bg,bb,255) + surface.DrawRect(tx*szx+szx/2,ty*szy+szy/2,szx*1.2,szy*1.2) + else + surface.SetDrawColor(hr,hg,hb,255) + surface.DrawRect(tx*szx+szx/2,ty*szy+szy/2,szx*1.2,szy*1.2) + end + + if (c1 ~= 0) && (cfrnt ~= 0) then + if (c1 <= 127) then + draw.DrawText(string.char(c1),"WireGPU_ConsoleFont", + tx*szx+szx/8+szx/2,ty*szy+szy/4+szy/2, + Color(math.Clamp(fr*self.Memory1[2028]*self.Memory1[2025],0,255), + math.Clamp(fg*self.Memory1[2027]*self.Memory1[2025],0,255), + math.Clamp(fb*self.Memory1[2026]*self.Memory1[2025],0,255), + 255),0) + else + self:DrawGraphicsChar(c1,tx*szx+szx/2,ty*szy+szy/2,szx,szy, + math.Clamp(fr*self.Memory1[2028]*self.Memory1[2025],0,255), + math.Clamp(fg*self.Memory1[2027]*self.Memory1[2025],0,255), + math.Clamp(fb*self.Memory1[2026]*self.Memory1[2025],0,255)) + end + end + end + end + + if (self.Memory1[2045] > 1080) then self.Memory1[2045] = 1080 end + if (self.Memory1[2045] < 0) then self.Memory1[2045] = 0 end + if (self.Memory1[2044] > 1) then self.Memory1[2044] = 1 end + if (self.Memory1[2044] < 0) then self.Memory1[2044] = 0 end + + if (self.Memory1[2046] >= 1) then + if (self.Flash == true) then + local a = math.floor(self.Memory1[2045] / 2) + + local tx = a - math.floor(a / 30)*30 + local ty = math.floor(a / 30) + + local c = self.Memory1[2*a+1] + local cback = 999-math.floor(c / 1000) + local bb = 28*math.fmod(cback,10) + local bg = 28*math.fmod(math.floor(cback / 10),10) + local br = 28*math.fmod(math.floor(cback / 100),10) + + surface.SetDrawColor( + math.Clamp(br*self.Memory1[2028]*self.Memory1[2025],0,255), + math.Clamp(bg*self.Memory1[2027]*self.Memory1[2025],0,255), + math.Clamp(bb*self.Memory1[2026]*self.Memory1[2025],0,255), + 255 + ) + surface.DrawRect( + tx*szx+szx/2, + ty*szy+szy/2+szy*1.2*(1-self.Memory1[2044]), + szx*1.2, + szy*1.2*self.Memory1[2044] + ) + end + end + cam.End2D() + render.SetViewPort(0,0,oldw,oldh) + render.SetRenderTarget(OldRT) + end + + if (self.FrameNeedsFlash == true) then + if (self.IntTimer < self.Memory1[2043]) then + if (self.Flash == false) then + self.NeedRefresh = true + end + self.Flash = true + end + + if (self.IntTimer >= self.Memory1[2043]) then + if (self.Flash == true) then + self.NeedRefresh = true + end + self.Flash = false + end + + if (self.IntTimer >= self.Memory1[2043]*2) then + self.IntTimer = 0 + end + end + + if (EmuFox) then + return + end + + if (WireGPU_Monitors[self.Entity:GetModel()]) && (WireGPU_Monitors[self.Entity:GetModel()].OF) then + OF = WireGPU_Monitors[self.Entity:GetModel()].OF + OU = WireGPU_Monitors[self.Entity:GetModel()].OU + OR = WireGPU_Monitors[self.Entity:GetModel()].OR + Res = WireGPU_Monitors[self.Entity:GetModel()].RS + RatioX = WireGPU_Monitors[self.Entity:GetModel()].RatioX + else + OF = 0 + OU = 0 + OR = 0 + Res = 1 + RatioX = 1 + end + + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + + local pos = self.Entity:GetPos()+(self.Entity:GetForward()*OF)+(self.Entity:GetUp()*OU)+(self.Entity:GetRight()*OR) + + cam.Start3D2D(pos,ang,Res) + local w = 512*math.Clamp(self.Memory1[2030],0,1) + local h = 512*math.Clamp(self.Memory1[2029],0,1) + local x = -w/2 + local y = -h/2 + + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(-256,-256,512/RatioX,512) + + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(WireGPU_texScreen) + WireGPU_DrawScreen(x,y,w/RatioX,h,self.Memory1[2024],self.Memory1[2023]) + cam.End3D2D() + + WireGPU_matScreen:SetMaterialTexture("$basetexture",OldTex) + Wire_Render(self.Entity) +end + +function ENT:IsTranslucent() + return true +end diff --git a/lua/entities/gmod_wire_consolescreen/init.lua b/lua/entities/gmod_wire_consolescreen/init.lua new file mode 100644 index 0000000000..32f6e7d49a --- /dev/null +++ b/lua/entities/gmod_wire_consolescreen/init.lua @@ -0,0 +1,285 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include('shared.lua') + +ENT.WireDebugName = "ConsoleScreen" + +function ENT:Initialize() + + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Inputs = Wire_CreateInputs(self.Entity, { "CharX", "CharY", "Char", "CharParam", "Clk", "Reset" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Memory" }) + + self.Memory = {} + + for i = 0, 2047 do + self.Memory[i] = 0 + end + + self.CharX = 0 + self.CharY = 0 + self.Char = 0 + self.CharParam = 0 + + self.Memory[2022] = 3/4 + self.Memory[2023] = 0 + self.Memory[2024] = 0 + self.Memory[2025] = 1 + self.Memory[2026] = 1 + self.Memory[2027] = 1 + self.Memory[2028] = 1 + self.Memory[2029] = 1 + self.Memory[2030] = 1 + self.Memory[2031] = 0 + self.Memory[2032] = 29 + self.Memory[2033] = 0 + self.Memory[2034] = 17 + self.Memory[2035] = 0 + self.Memory[2036] = 0 + + self.Memory[2042] = 000 + self.Memory[2043] = 0.5 + self.Memory[2044] = 0.25 + self.Memory[2045] = 0 + self.Memory[2046] = 0 + self.Memory[2047] = 1 -- CLK + + self.IgnoreDataTransfer = false + self:ResetCacheSystem() +end + +function ENT:ResetCacheSystem() + self.MemoryCache = {} +end + +function ENT:SendToClient(Address, value) + value = math.max(-99,value or 0) + table.insert(self.MemoryCache, { Address, value }) +end + +local function calcoffset(offset) + if offset < 0 then offset = 1048576 + offset end + return -100-offset +end + +function ENT:FlushCache() + if not next(self.MemoryCache) then return end + + local bytes = 4+4 + umsg.Start("hispeed_datastream") + umsg.Short(self:EntIndex()) + local last_address = -1 + + for _,Address,value in ipairs_map(self.MemoryCache,unpack) do + local gap = Address - (last_address+1) + if gap ~= 0 then + bytes = bytes + 8 + else + bytes = bytes + 4 + end + if bytes >= 200 then + umsg.Float(-100) + umsg.End() + bytes = 4+4 + umsg.Start("hispeed_datastream") + umsg.Short(self:EntIndex()) + + last_address = -1 + gap = Address - (last_address+1) + end + if gap ~= 0 then + umsg.Float(calcoffset(gap)) + end + + umsg.Float(value) + last_address = Address + end + umsg.Float(-100) + umsg.End() + + self:ResetCacheSystem() +end + +function ENT:SendPixel() + if (self.Memory[2047] ~= 0) && (self.CharX >= 0) && (self.CharX < 30) && + (self.CharY >= 0) && (self.CharY < 18) then + local pixelno = math.floor(self.CharY)*30+math.floor(self.CharX) + + self:WriteCell(pixelno*2, self.Char) + self:WriteCell(pixelno*2+1, self.CharParam) + end +end + +function ENT:ReadCell(Address) + if Address < 0 then return nil end + if Address >= 2048 then return nil end + + if Address == 2022 then return WireGPU_Monitors[self.Entity:GetModel()].RatioX end + + return self.Memory[Address] +end + +function ENT:WriteCell(Address, value) + if Address < 0 then return false end + if Address >= 2048 then return false end + + if Address < 2000 then -- text/attribute data + if self.Memory[Address] == value then return true end + else + self:ClientWriteCell(Address, value) + end + + self.Memory[Address] = value + + self:SendToClient(Address, value) + + return true +end + +function ENT:Think() + if (self.IgnoreDataTransfer == true) then + self.IgnoreDataTransfer = false + self.Entity:NextThink(CurTime()+0.2) + else + self:FlushCache() + self.Entity:NextThink(CurTime()+0.1) + end + return true +end + +function ENT:TriggerInput(iname, value) + if (iname == "CharX") then + self.CharX = value + self:SendPixel() + elseif (iname == "CharY") then + self.CharY = value + self:SendPixel() + elseif (iname == "Char") then + self.Char = value + self:SendPixel() + elseif (iname == "CharParam") then + self.CharParam = value + self:SendPixel() + elseif (iname == "Clk") then + self:WriteCell(2047, value) + self:SendPixel() + elseif (iname == "Reset") then + self:WriteCell(2041,0) + self:WriteCell(2046,0) + self:WriteCell(2042,0) + end +end + +function ENT:ClientWriteCell(Address, value) + if (Address == 2019) then -- Hardware Clear Viewport + local low = math.floor(math.Clamp(self.Memory[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) + for j = low, high do + for i = 2*lowc, 2*highc+1 do + self.Memory[i*60+value] = 0 + end + end + elseif (Address == 2037) then -- Shift cells (number of cells, >0 right, <0 left) + local delta = math.abs(value) + local low = math.floor(math.Clamp(self.Memory[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) + if (value > 0) then + for j = low,high do + for i = highc,lowc+delta,-1 do + self.Memory[j*60+i*2] = self.Memory[j*60+i*2-delta*2] + self.Memory[j*60+i*2+1] = self.Memory[j*60+i*2+1-delta*2] + end + end + for j = low,high do + for i = lowc, lowc+delta-1 do + self.Memory[j*60+i*2] = 0 + self.Memory[j*60+i*2+1] = 0 + end + end + else + for j = low,high do + for i = lowc,highc-delta do + self.Memory[j*60+i*2] = self.Memory[j*60+i*2+delta*2] + self.Memory[j*60+i*2+1] = self.Memory[j*60+i*2+1+delta*2] + end + end + for j = low,high do + for i = highc-delta+1,highc do + self.Memory[j*60+i*2] = 0 + self.Memory[j*60+i*2+1] = 0 + end + end + end + elseif (Address == 2038) then -- Shift rows (number of rows, >0 shift down, <0 shift up) + local delta = math.abs(value) + local low = math.floor(math.Clamp(self.Memory[2033],0,17)) + local high = math.floor(math.Clamp(self.Memory[2034],0,17)) + local lowc = math.floor(math.Clamp(self.Memory[2031],0,29)) + local highc = math.floor(math.Clamp(self.Memory[2032],0,29)) + if (value > 0) then + for j = low, high-delta do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = self.Memory[(j+delta)*60+i] + end + end + for j = high-delta+1,high do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = 0 + end + end + else + for j = high,low+delta,-1 do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = self.Memory[(j-delta)*60+i] + end + end + for j = low,low+delta-1 do + for i = 2*lowc, 2*highc+1 do + self.Memory[j*60+i] = 0 + end + end + end + elseif (Address == 2039) then -- Hardware Clear Row (Writing clears row) + for i = 0, 59 do + self.Memory[value*60+i] = 0 + end + elseif (Address == 2040) then -- Hardware Clear Column (Writing clears column) + for i = 0, 17 do + self.Memory[i*60+value] = 0 + end + elseif (Address == 2041) then -- Hardware Clear Screen + for i = 0, 18*30*2-1 do + self.Memory[i] = 0 + end + self:ResetCacheSystem() -- optimization + end +end + + +function MakeWireconsoleScreen(pl, Pos, Ang, model) + + if (!pl:CheckLimit("wire_consolescreens")) then return false end + + local wire_consolescreen = ents.Create("gmod_wire_consolescreen") + if (!wire_consolescreen:IsValid()) then return false end + wire_consolescreen:SetModel(model) + + wire_consolescreen:SetAngles(Ang) + wire_consolescreen:SetPos(Pos) + wire_consolescreen:Spawn() + + wire_consolescreen:SetPlayer(pl) + + pl:AddCount("wire_consolescreens", wire_consolescreen) + + return wire_consolescreen +end + +duplicator.RegisterEntityClass("gmod_wire_consolescreen", MakeWireconsoleScreen, "Pos", "Ang", "Model") diff --git a/lua/entities/gmod_wire_consolescreen/shared.lua b/lua/entities/gmod_wire_consolescreen/shared.lua new file mode 100644 index 0000000000..57508ab02d --- /dev/null +++ b/lua/entities/gmod_wire_consolescreen/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Console Screen" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_cpu/cl_init.lua b/lua/entities/gmod_wire_cpu/cl_init.lua new file mode 100644 index 0000000000..3fd534c3c8 --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cl_init.lua @@ -0,0 +1,5 @@ +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE diff --git a/lua/entities/gmod_wire_cpu/compiler_asm.lua b/lua/entities/gmod_wire_cpu/compiler_asm.lua new file mode 100644 index 0000000000..0aa3bdec8c --- /dev/null +++ b/lua/entities/gmod_wire_cpu/compiler_asm.lua @@ -0,0 +1,1184 @@ +function ENT:InitializeRegisterNames() + self.RegisterName = {} + self.RegisterName["eax"] = 0 + self.RegisterName["ebx"] = 1 + self.RegisterName["ecx"] = 2 + self.RegisterName["edx"] = 3 + self.RegisterName["esi"] = 4 + self.RegisterName["edi"] = 5 + self.RegisterName["esp"] = 6 + self.RegisterName["ebp"] = 7 + + self.RegisterName["cs"] = 8 + self.RegisterName["ss"] = 9 + self.RegisterName["ds"] = 10 + self.RegisterName["es"] = 11 + self.RegisterName["gs"] = 12 + self.RegisterName["fs"] = 13 + self.RegisterName["ks"] = 14 + self.RegisterName["ls"] = 15 + + for i=0,1023 do + self.RegisterName["port"..i] = 1000+i-1 + end + + self.SegmentName = {} + self.SegmentName["eax"] = -10 + self.SegmentName["ebx"] = -11 + self.SegmentName["ecx"] = -12 + self.SegmentName["edx"] = -13 + self.SegmentName["esi"] = -14 + self.SegmentName["edi"] = -15 + self.SegmentName["esp"] = -16 + self.SegmentName["ebp"] = -17 + + self.SegmentName["cs"] = -2 + self.SegmentName["ss"] = -3 + self.SegmentName["ds"] = -4 + self.SegmentName["es"] = -5 + self.SegmentName["gs"] = -6 + self.SegmentName["fs"] = -7 + self.SegmentName["ks"] = -8 + self.SegmentName["ls"] = -9 + + self.GeneralRegister = {} + self.GeneralRegister["eax"] = true + self.GeneralRegister["ebx"] = true + self.GeneralRegister["ecx"] = true + self.GeneralRegister["edx"] = true + self.GeneralRegister["esi"] = true + self.GeneralRegister["edi"] = true + self.GeneralRegister["esp"] = true + self.GeneralRegister["ebp"] = true +end + +function ENT:InitializeOptimizer() + // +end + +function ENT:Message(msg) + self.Player:PrintMessage(HUD_PRINTCONSOLE,"-> "..msg) + self.Player:ConCommand("wire_cpu_editor_addlog \""..msg.."\"") +end + +function ENT:Error(msg) + if (EmuFox) then //Override for EmuFox + print("-> Error at line "..self.Line..": "..msg) + end + + local errmsg + if (self.CurrentFile == "") then + errmsg = "Error: "..msg..", at line "..self.Line + else + errmsg = self.CurrentFile..": Error: "..msg..", at line "..(self.Line-self.FileStartLine) + end + if ValidEntity(self.Player) then + self.Player:PrintMessage(HUD_PRINTCONSOLE,"-> "..errmsg) + + -- last error thing for the editor (temporary) + umsg.Start("wire_cpu_error", self.Player) + umsg.String(errmsg) + umsg.End() + end + + --self.Player:ConCommand("wire_cpu_editor_addlog \"".."-> Error at line "..self.Line..": "..msg.."\"") FIXME + self.FatalError = true +end + +function ENT:_whitespace() + while ((string.sub(self.CurrentLine,1,1) == " ") || + (string.sub(self.CurrentLine,1,1) == "\t")) do + self.CurrentLine = string.sub(self.CurrentLine,2) + end +end + +function ENT:_need(char) + if (string.sub(self.CurrentLine,1,1) ~= char) then + return false + else + self.CurrentLine = string.sub(self.CurrentLine,2) + self:_whitespace() + return true + end +end + +function ENT:_need_with_whitespace(char) + if (string.sub(self.CurrentLine,1,1) ~= char) then + return false + else + self.CurrentLine = string.sub(self.CurrentLine,2) + return true + end +end + +function ENT:_char() + local char = string.sub(self.CurrentLine,1,1) + self.CurrentLine = string.sub(self.CurrentLine,2) + return char +end + +function ENT:_peek() + return string.sub(self.CurrentLine,2,2) +end + +function ENT:_getc() + return string.sub(self.CurrentLine,1,1) +end + +function ENT:_getstring(sepchar) + local str = "" + self:_whitespace() + while ((self.CurrentLine ~= "") && (self:_getc() ~= sepchar) && (self:_getc() ~= ")")) do //fixme isalphanum + str = str .. self:_char() + end + return string.lower(str) +end + +function ENT:_word() + local word = "" + while self.CurrentLine:match("^[^ ();#:,'\"!%^&*\t]") do //FIXME: isalphanum + word = word .. string.sub(self.CurrentLine,1,1) + self.CurrentLine = string.sub(self.CurrentLine,2) + end + return word +end + + +function ENT:_keyword() + return string.lower(self:_word()) +end + +function ENT:Compiler_Stage0(pl) + self.Player = pl + + self.FatalError = false + self.Compiling = false + self.MakeDump = false + + self.PrecompileData = {} + self.DebugLines = {} + self.DebugData = {} + + self.Dump = "" + + self.LocalVarRange = 128 + self.ReturnVariable = "eax" +end + +function ENT:Compiler_SetExtraLabels() + self:SetLabel("__date_year__", tonumber(os.date("%Y"))) + self:SetLabel("__date_month__", tonumber(os.date("%m"))) + self:SetLabel("__date_day__", tonumber(os.date("%d"))) + + self:SetLabel("__date_hour__", tonumber(os.date("%H"))) + self:SetLabel("__date_minute__",tonumber(os.date("%M"))) + self:SetLabel("__date_second__",tonumber(os.date("%S"))) + + if (self.IsGPU) then + self:SetLabel("regclk", 65535) + self:SetLabel("regreset", 65534) + self:SetLabel("reghwclear", 65533) + self:SetLabel("regvertexmode", 65532) + self:SetLabel("reghalt", 65531) + self:SetLabel("regram_reset", 65530) + + self:SetLabel("reghscale", 65525) + self:SetLabel("regvscale", 65524) + self:SetLabel("reghwscale", 65523) + self:SetLabel("regrotation", 65522) + self:SetLabel("regsprsize", 65521) + self:SetLabel("regtexdataptr", 65520) + self:SetLabel("regtexdatasz", 65519) + self:SetLabel("regrasterq", 65518) + + self:SetLabel("regwidth", 65515) + self:SetLabel("regheight", 65514) + self:SetLabel("regratio", 65513) + self:SetLabel("regparamlist", 65512) + + self:SetLabel("regcursorx", 65505) + self:SetLabel("regcursory", 65504) + self:SetLabel("regcursor", 65503) + + self:SetLabel("regbrightnessw", 65495) + self:SetLabel("regbrightnessr", 65494) + self:SetLabel("regbrightnessg", 65493) + self:SetLabel("regbrightnessb", 65492) + self:SetLabel("regcontrastw", 65491) + self:SetLabel("regcontrastr", 65490) + self:SetLabel("regcontrastg", 65489) + self:SetLabel("regcontrastb", 65488) + + self:SetLabel("regcirclequality",65485) + self:SetLabel("regoffsetx", 65484) + self:SetLabel("regoffsety", 65483) + self:SetLabel("regrotation", 65482) + self:SetLabel("regscale", 65481) + self:SetLabel("regcenterx", 65480) + self:SetLabel("regcentery", 65479) + self:SetLabel("regcirclestart", 65478) + self:SetLabel("regcircleend", 65477) + self:SetLabel("reglinewidth", 65476) + self:SetLabel("regscalex", 65475) + self:SetLabel("regscaley", 65474) + self:SetLabel("regfontalign", 65473) + self:SetLabel("regzoffset", 65472) + end +end + +function ENT:Compiler_Stage1() + self.WIP = 0 + self.OffsetWIP = 0 + self.Labels = {} + self.FunctionParams = {} + self.FirstPass = true + + self.LastKeyword = "" + self.Dump = "" + self.CurrentFunction = nil + + self.CurrentFile = "" + self.FileStartLine = 0 + + self:SetLabel("programsize",0) + self:Compiler_SetExtraLabels() +end + +function ENT:Compiler_Stage2() + iterator = function (labelk,labelv) + self.Labels[labelk].Ignore = nil + end + table.foreach(self.Labels,iterator) + + self:SetLabel("programsize",self.WIP) + self:Compiler_SetExtraLabels() + + self.WIP = 0 + self.OffsetWIP = 0 + self.FirstPass = false + + self.LastKeyword = "" + self.Dump = "" + self.CurrentFunction = nil + + self.CurrentFile = "" + self.FileStartLine = 0 +end + +function ENT:ParseProgram_ASM(programtext,programline) + if (programtext == "") then + return + end + + self.CurrentLine = programtext + self.Line = programline + + local comment = string.find(self.CurrentLine,"//", 1, true) + if (comment) then self.CurrentLine = string.sub(self.CurrentLine,1,comment-1) end + + self:Compile() +end + +function ENT:GenerateASM(code) + local templine = self.CurrentLine + self.CurrentLine = code + self:GenerateCode() + self.CurrentLine = templine +end + +function ENT:ParseOpcodeParameter(keyword) + local result = {} + + if (keyword == "") then //#EAX + if (self:_need("#")) then + keyword = self:_keyword() + if (self.RegisterName[keyword]) then //#EAX + if (self.GeneralRegister[keyword]) then + result.RM = 17+self.RegisterName[keyword] + else + self:Error("Expected general register for memory reference, got '"..keyword.."' instead!") + end + else + if ((self.FunctionParams[keyword]) && (self.Labels[self.CurrentFunction.Name].Param[keyword])) then //#functparam + result.RM = 49 + result.Byte = self.CurrentFunction.ArgCount - self.Labels[self.CurrentFunction.Name].Param[keyword].Arg + 2 + else + result.RM = 25 + result.Byte = self:GetValidValue(keyword) //#123 + end + end + else + --[[ + if (self:_need("(")) then //(1,2,3,4) FIXME + self:_whitespace() + while (not self:_peek(')')) do + result.RM = 25 + result.Byte = self:GetValidValue("programsize") + + if (self:_peek(',')) then + self:_need(',') + end + self:_whitespace() + end + else + ]] + self:Error("Expected '#' for memory reference") + --end + end + else + if (self:_need(":")) then //Segment prefix + if (self:_need("#")) then //EAX:#EBX + if (self.RegisterName[keyword]) then //EAX:#EBX + local register = self:_keyword() + if (self.RegisterName[register]) then + if (self.GeneralRegister[register]) then + result.RM = 17+self.RegisterName[register] + result.Segment = self.SegmentName[keyword] + else + self:Error("Expected general register for parameter with offset") + end + else + result.RM = 25 + result.Byte = self:GetValidValue(register) //EAX:#123 + result.Segment = self.SegmentName[keyword] + end + else + local register = self:_keyword() + if (self.RegisterName[register]) then //123:#EBX + if (self.GeneralRegister[register]) then + result.RM = 34+self.RegisterName[register] + result.Byte = self:GetValidValue(keyword) + else + self:Error("Expected general register name parameter with offset") + end + else + self:Error("Expected register name for parameter with offset, got '"..keyword.."' instead!") + end + end + else //EAX:EBX + if (self.RegisterName[keyword]) then //EAX:EBX + local register = self:_keyword() + if (self.RegisterName[register]) then + if (self.GeneralRegister[register]) then + result.RM = 26+self.RegisterName[register] + result.Segment = self.SegmentName[keyword] + else + self:Error("Expected general register for parameter with offset") + end + else + if (tonumber(register)) then //EAX:123 + if (self.GeneralRegister[keyword]) then + result.RM = 42+self.RegisterName[keyword] + result.Byte = self:GetValidValue(register) + else + self:Error("Expected general register name parameter with offset") + end + else + self:Error("Expected register name for parameter with offset, got '"..keyword.."' instead!") + end + end + else + local register = self:_keyword() + if (self.RegisterName[register]) then //123:EBX + if (self.GeneralRegister[register]) then + result.RM = 42+self.RegisterName[register] + result.Byte = self:GetValidValue(keyword) + else + self:Error("Expected general register name parameter with offset") + end + else + self:Error("Expected register name for parameter with offset, got '"..keyword.."' instead!") + end + end + end + else //No segment prefix, no memory reference + if (self.RegisterName[keyword]) then //EAX + result.RM = self.RegisterName[keyword]+1 + else + if ((self.FunctionParams[keyword]) && (self.Labels[self.CurrentFunction.Name].Param[keyword])) then //functparam + result.RM = 49 + result.Byte = self.CurrentFunction.ArgCount - self.Labels[self.CurrentFunction.Name].Param[keyword].Arg + 2 + else + result.RM = 0 + result.Byte = self:GetValidValue(keyword) //123 + end + end + end + end + + return result +end + +function ENT:GenerateCode(keyword) + //#EBX< >,< >EAX:#EBX< > + local dRM1 = {} + local dRM2 = {} + if (keyword == nil) then + self:_whitespace() + keyword = self:_keyword() + self:_whitespace() + end + + if (self.OpcodeCount[self.DecodeOpcode[keyword]] > 0) then + dRM1 = self:ParseOpcodeParameter(self:_keyword()) + if (self.FatalError) then return end + end + if (self.OpcodeCount[self.DecodeOpcode[keyword]] > 1) then + self:_whitespace() + if (not self:_need(",")) then + self:Error("Expected second operand for opcode '"..keyword.."'!") + end + self:_whitespace() + dRM2 = self:ParseOpcodeParameter(self:_keyword()) + if (self.FatalError) then return end + end + + local XEIP = self.WIP + local RM = 0 + local Opcode = self.DecodeOpcode[keyword] + + if (dRM1.RM) then RM = RM + dRM1.RM end + if (dRM2.RM) then RM = RM + dRM2.RM*10000 end + if (dRM1.Segment) then Opcode = Opcode + 1000 end + if (dRM2.Segment) then Opcode = Opcode + 10000 end + + self:Write(Opcode) + self:Write(RM) + + if (dRM1.Segment) then self:Write(dRM1.Segment) end + if (dRM2.Segment) then self:Write(dRM2.Segment) end + + if (dRM1.Byte) then self:Write(dRM1.Byte) end + if (dRM2.Byte) then self:Write(dRM2.Byte) end + + if (self.FirstPass == false) then + if (not self.IsGPU) then + self:Precompile(XEIP) + end + end +end + +function ENT:ParseDB() + local ParsingString = false + while (self.FatalError == false) and (self.CurrentLine ~= "") and not ((not ParsingString) and (self:_need(";"))) do + if (self:_need_with_whitespace("'")) then + if (ParsingString == true) then + if (self:_peek() == "'") then + self:_char() + self:Write(string.byte("'")) + else + ParsingString = false + self:_whitespace() + if (self:_need(",") == false) then return end + self:_whitespace() + end + else + ParsingString = true + end + end + if (ParsingString == false) then + if (self:_need("$")) then //Offset... + local value = self:_keyword() + self:Write(self.WIP+self:GetValidValue(value)) + else + local value = self:_keyword() + self:Write(self:GetValidValue(value)) + end + self:_whitespace() + if (self:_need(",") == false) then return end + self:_whitespace() + else + local char = self:_char() + if (char == "\\") then + local char2 = self:_char() + if (char2 == "n") then + self:Write(10) + end + if (char2 == "\\") then + self:Write(string.byte('\\')) + end + if (char2 == "r") then + self:Write(10) + end + if (char2 == "0") then + self:Write(0) + end + else + self:Write(string.byte(char)) + end + + end + end +end + +function ENT:Compile() + if (self.Debug) && (not self.FirstPass) then + self.DebugLines[self.Line] = "["..self.Line.."]"..self.CurrentLine + self.DebugData[self.WIP] = self.Line + end + self.Dump = self.Dump.."["..(self.WIP+self.OffsetWIP).."]["..self.Line.."]["..self.WIP.."]"..self.CurrentLine.."\n" + + local prevline = self.CurrentLine.."_" + while (self.FatalError == false) && (self.CurrentLine ~= "") do + //< >MOV< > + if (self.WIP < 0) then + self:Error("Write pointer out of range") + end + if (self.CurrentLine == prevline) then + self:Error("Infinite loop in parser, you must have done something wrong") + return + end + prevline = self.CurrentLine + + self:_whitespace() + local word = self:_word() + local keyword = string.lower(word) + self:_whitespace() + + if (self.DecodeOpcode[keyword]) then + self:GenerateCode(keyword) + elseif (keyword == "db") then + self:ParseDB() + elseif (keyword == "alloc") then + local aword = self:_keyword() + self:_whitespace() + if (self:_need(",")) then + if (aword == "") then + self.Error("Missing first parameter for 'alloc' macro!") + return + end + + local bword = self:_keyword() + self:_whitespace() + if (self:_need(",")) then + local cword = self:_keyword() + + if (bword == "") then + self.Error("Missing second parameter for 'alloc' macro!") + return + end + + if (cword ~= "") then + local size = 0 + local value = 0 + + size = self:GetAlwaysValidValue(bword) + value = self:GetValidValue(cword) + + self:AddLabel(aword) + for i=0,size-1 do + self:Write(value) + end + else + self:Error("Missing third parameter for 'alloc' macro!") + end + else + if (bword ~= "") then //alloc mylabel,123; + self:AddLabel(aword) + self:Write(self:GetValidValue(bword)) + else + self:Error("Missing second parameter for 'alloc' macro!") + end + end + else + if (aword ~= "") then //alloc mylabel; + if (tonumber(aword)) then + for i=0,aword-1 do + self:Write(0) + end + else + self:AddLabel(aword) + self:Write(0) + end + else //alloc; + self:Write(0) + end + end + elseif (keyword == "define") then + local definename = self:_keyword() + self:_whitespace() + if (self:_need(",")) then + local definevalue = self:_keyword() + if (self.FirstPass) then + if (self.Labels[definename]) then + self:Error("Label '"..definename.."' already exists (previously defined at line "..self.Labels[definename].DefineLine..")") + else + self.Labels[definename] = {} + self.Labels[definename].WIP = self:GetValidValue(definevalue) + self.Labels[definename].DefineLine = self.Line + end + end + else + self:Error("Error in 'define' macro syntax: missing second parameter (define value)") + end + elseif (keyword == "string") then + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + if (self:_need(",")) then + self:ParseDB() + end + self:Write(0) + elseif (keyword == "matrix") then + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + self:Write(1) self:Write(0) self:Write(0) self:Write(0) + self:Write(0) self:Write(1) self:Write(0) self:Write(0) + self:Write(0) self:Write(0) self:Write(1) self:Write(0) + self:Write(0) self:Write(0) self:Write(0) self:Write(1) + elseif (keyword == "float") || (keyword == "scalar") || (keyword == "vector1f") || (keyword == "vec1f") then + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + if (self:_need(",")) then + local x = self:_keyword() + self:_whitespace() + self:AddLabel(name..".x") + self:Write(self:GetValidValue(x)) + else + self:AddLabel(name..".x") + self:Write(0) + end + elseif (keyword == "vector2f") || (keyword == "uv") || (keyword == "vector") || (keyword == "vec2f") then + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + if (self:_need(",")) then + local x = self:_keyword() + self:_whitespace() + self:AddLabel(name..".x") + self:AddLabel(name..".u") + self:Write(self:GetValidValue(x)) + if (self:_need(",")) then + local y = self:_keyword() + self:_whitespace() + self:AddLabel(name..".y") + self:AddLabel(name..".v") + self:Write(self:GetValidValue(y)) + else + self:AddLabel(name..".v") + self:Write(0) + end + else + self:AddLabel(name..".x") + self:AddLabel(name..".u") + self:Write(0) + self:AddLabel(name..".y") + self:AddLabel(name..".v") + self:Write(0) + end + elseif (keyword == "vector3f") || (keyword == "vec3f") then + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + if (self:_need(",")) then + local x = self:_keyword() + self:_whitespace() + self:AddLabel(name..".x") + self:Write(self:GetValidValue(x)) + if (self:_need(",")) then + local y = self:_keyword() + self:_whitespace() + self:AddLabel(name..".y") + self:Write(self:GetValidValue(y)) + if (self:_need(",")) then + local z = self:_keyword() + self:_whitespace() + self:AddLabel(name..".z") + self:Write(self:GetValidValue(z)) + else + self:AddLabel(name..".z") + self:Write(0) + end + else + self:AddLabel(name..".y") + self:Write(0) + self:AddLabel(name..".z") + self:Write(0) + end + else + self:AddLabel(name..".x") + self:Write(0) + self:AddLabel(name..".y") + self:Write(0) + self:AddLabel(name..".z") + self:Write(0) + end + elseif (keyword == "vector4f") || (keyword == "vec4f") then + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + if (self:_need(",")) then + local x = self:_keyword() + self:_whitespace() + self:AddLabel(name..".x") + self:AddLabel(name..".r") + self:Write(self:GetValidValue(x)) + if (self:_need(",")) then + local y = self:_keyword() + self:_whitespace() + self:AddLabel(name..".y") + self:AddLabel(name..".g") + self:Write(self:GetValidValue(y)) + if (self:_need(",")) then + local z = self:_keyword() + self:_whitespace() + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(self:GetValidValue(z)) + if (self:_need(",")) then + local w = self:_keyword() + self:_whitespace() + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(self:GetValidValue(w)) + else + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(0) + end + else + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(0) + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(0) + end + else + self:AddLabel(name..".y") + self:AddLabel(name..".g") + self:Write(0) + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(0) + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(0) + end + else + self:AddLabel(name..".x") + self:AddLabel(name..".r") + self:Write(0) + self:AddLabel(name..".y") + self:AddLabel(name..".g") + self:Write(0) + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(0) + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(0) + end + elseif (keyword == "color") then //copypasta from vector4f + local name = self:_keyword() + self:_whitespace() + self:AddLabel(name) + if (self:_need(",")) then + local x = self:_keyword() + self:_whitespace() + self:AddLabel(name..".x") + self:AddLabel(name..".r") + self:Write(self:GetValidValue(x)) + if (self:_need(",")) then + local y = self:_keyword() + self:_whitespace() + self:AddLabel(name..".y") + self:AddLabel(name..".g") + self:Write(self:GetValidValue(y)) + if (self:_need(",")) then + local z = self:_keyword() + self:_whitespace() + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(self:GetValidValue(z)) + if (self:_need(",")) then + local w = self:_keyword() + self:_whitespace() + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(self:GetValidValue(w)) + else + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(255) + end + else + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(0) + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(255) + end + else + self:AddLabel(name..".y") + self:AddLabel(name..".g") + self:Write(0) + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(0) + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(255) + end + else + self:AddLabel(name..".x") + self:AddLabel(name..".r") + self:Write(0) + self:AddLabel(name..".y") + self:AddLabel(name..".g") + self:Write(0) + self:AddLabel(name..".z") + self:AddLabel(name..".b") + self:Write(0) + self:AddLabel(name..".w") + self:AddLabel(name..".a") + self:Write(255) + end + elseif (keyword == "code") then + self:AddLabel("codestart") + if (not self.FirstPass) && (not self.Labels["datastart"]) then + self:Error("No matching 'data' macro was found!") + end + elseif (keyword == "data") then + self:AddLabel("datastart") + if (not self.FirstPass) && (not self.Labels["codestart"]) then + self:Error("No matching 'code' macro was found!") + end + + self:Write(2) + self:Write(0) + self:Write(self:GetValidValue("codestart")) + elseif (keyword == "org") then + local value = self:_keyword() + self.WIP = self:GetValidValue(value) + elseif (keyword == "offset") then + local value = self:_keyword() + self.OffsetWIP = self:GetValidValue(value) + elseif (keyword == "wipe_locals") then + self:WipeLocals() + elseif (keyword == "wipe_labels") then + self:WipeLabels() + elseif (keyword == "setvar") then + local varname = self:_keyword() + if (varname ~= "") then + self:_whitespace() + if (self:_need(",")) then + local varvalue = self:_keyword() + if (varvalue ~= "") then + //Set compiler variables + if (varname == "localrange") then + if tonumber(varvalue) then self.LocalVarRange = tonumber(varvalue) end + end + if (varname == "returnregister") then + if self.GeneralRegister[varvalue] then self.ReturnVariable = varvalue end + end + else + self:Error("Missing variable value for 'setvar' macro") + end + end + else + self:Error("Missing variable name for 'setvar' macro") + end + elseif (keyword == "asmfile") then + local filename = self:_getstring(";") + + self.CurrentFile = filename + self.FileStartLine = self.Line + elseif (keyword == "asmend") then + self.CurrentFile = "" + elseif (keyword == "function") then + if (self.CurrentFunction) then + self:Error("Can't have function inside function!") + end + + local fname = self:_keyword() + local argscnt = 0 + + self:AddLabel(fname) + self:GenerateASM("push ebp") + self:GenerateASM("mov ebp,esp") + self:GenerateASM("inc ebp") + + if (self:_need("(")) then + local argument = self:_getstring(",") + while (argument ~= "") do + self:AddFunctionArgument(fname,argument,argscnt) + + argscnt = argscnt + 1 + if (self:_need(",")) then + argument = self:_getstring(",") + else + argument = "" + end + end + end + + self.CurrentFunction = {} + self.CurrentFunction.ArgCount = argscnt + self.CurrentFunction.Name = fname + elseif (keyword == "return") then + local retval = self:_getstring(";") + if (retval ~= "") then + if (retval ~= "eax") then + self:GenerateASM("mov "..self.ReturnVariable..","..retval) + end + end + self:GenerateASM("add esp,2") + self:GenerateASM("pop ebp") + self:GenerateASM("ret") + elseif (keyword == "end") then + if (not self.CurrentFunction) then + self:Error("END must be inside function") + end + + if (self.LastKeyword ~= "return") then + self:GenerateASM("add esp,2") + self:GenerateASM("pop ebp") + self:GenerateASM("ret") + end + self.CurrentFunction = nil + elseif (keyword == "getarg") then + if (not self.CurrentFunction) then + self:Error("GETARG must be inside function") + end + + if (self:_need("(")) then + local where = self:_getstring(",") + self:_need(",") + local argno = self:GetValidValue(self:_keyword()) + self:GenerateASM("mov "..where..","..(self.CurrentFunction.ArgCount - argno + 2)) + else + self:Error("GETARG: syntax error") + end + elseif (self:_need("(")) then //High-level function call + //Function call + local address = self:GetValidValue(keyword) + local argscnt = 0 + + local argument = self:_getstring(",") + while (argument ~= "") do + self:GenerateASM("push "..argument) + + argscnt = argscnt + 1 + if (self:_need(",")) then + argument = self:_getstring(",") + else + argument = "" + end + end + + self:GenerateASM("mov ecx,"..argscnt) + self:GenerateASM("call "..address) + if (argscnt ~= 0) then + self:GenerateASM("add esp,"..argscnt) + end + + if (not self:_need(")")) then + self:Error("Error in function call syntax") + end + else + if (self:_need(":")) then + if (string.sub(keyword,1,1) == "@") then + self:AddLocalLabel(keyword) + elseif (string.sub(keyword,1,1) == "$") then + self:AddGlobalLabel(keyword) + else + self:AddLabel(keyword) + end + else + if (keyword ~= "") then + local peek = self:_peek() + if (peek == ";") then + self:Error("Invalid label definition or unknown keyword '"..keyword.."' (expecting ':' rather than ';')") + else + self:Error("Unknown keyword '"..keyword.."'") + end + else + if (self.CurrentLine == "") then + return + else + self:Error("Syntax error. This should not be here: \""..self.CurrentLine.."\"") + end + end + end + end + + self.LastKeyword = keyword + + self:_whitespace() + self:_need(";") + end +end + +function ENT:GetLabel(labelname) + local foundlabel = nil + //for labelk,labelv in pairs(self.Labels) do + iterator = function (labelk,labelv) + if (self.Labels[labelk].Ignore == nil) then + if (labelk == labelname) then + if (labelv.Local == true) then + if (math.abs(self.WIP - labelv.WIP) < self.LocalVarRange) then + foundlabel = labelv + return foundlabel + end + else + foundlabel = labelv + return foundlabel + end + end + end + end + table.foreach(self.Labels,iterator) + return foundlabel +end + +function ENT:GetValidValue(labelname) + local sign = 1 + if (labelname == "") then return 0 end + if (string.sub(labelname,1,1) == "-") then + labelname = string.sub(labelname,2) + sign = -1 + end + if (tonumber(labelname)) then + return sign*(tonumber(labelname)) + else + local foundlabel = self:GetLabel(labelname) + + if (foundlabel) then + return sign*(foundlabel.WIP+self.OffsetWIP) + else + if (not self.FirstPass) then + self:Error("Expected number or a valid label") + end + return 0 + end + end +end + +function ENT:GetAlwaysValidValue(labelname) + local sign = 1 + if (labelname == "") then return 0 end + if (string.sub(labelname,1,1) == "-") then + labelname = string.sub(labelname,2) + sign = -1 + end + if (tonumber(labelname)) then + return sign*(tonumber(labelname)) + else + local foundlabel = self:GetLabel(labelname) + + if (foundlabel) then + return sign*(foundlabel.WIP+self.OffsetWIP) + else + self:Error("Expected number or a valid label, defined BEFORE this line") + return 0 + end + end +end + +function ENT:SetLabel(labelname,value) + if (self.Labels[labelname]) then + self.Labels[labelname].WIP = value + else + self.Labels[labelname] = {} + self.Labels[labelname].WIP = self.WIP + self.Labels[labelname].DefineLine = self.Line + end +end + +function ENT:AddLabel(labelname) + if (self.FirstPass) then + if (self:GetLabel(labelname)) then + self:Error("Label '"..labelname.."' already exists (previously defined at line "..self.Labels[labelname].DefineLine..")") + else + self.Labels[labelname] = {} + self.Labels[labelname].WIP = self.WIP + self.Labels[labelname].DefineLine = self.Line + end + else + if (self.Labels[labelname]) then + if (self.Labels[labelname].WIP ~= self.WIP) then + self.Labels[labelname].WIP = self.WIP + self:Error("Label pointer changed between stages - report this to Black Phoenix!") + end + end + end +end + +function ENT:AddFunctionArgument(functionname,labelname,argno) + if (self.FirstPass) then + if (not self:GetLabel(functionname)) then + self:Error("Internal error - report to black phoenix! (code AF8828Z8A)") + end + + if (self.Labels[functionname].Param[labelname]) then + self:Error("Function parameter '"..labelname.."' already exists)") + else + if (not self.Labels[functionname].Param) then + self.Labels[functionname].Param = {} + end + self.Labels[functionname].Param[labelname] = {} + self.Labels[functionname].Param[labelname].Arg = argno + self.FunctionParams[labelname] = true + end + end +end + +function ENT:AddLocalLabel(labelname) + if (self.FirstPass) then + if (self:GetLabel(labelname)) then + self:Error("Label '"..labelname.."' already exists (previously defined at line "..self.Labels[labelname].DefineLine..")") + else + self.Labels[labelname] = {} + self.Labels[labelname].WIP = self.WIP + self.Labels[labelname].DefineLine = self.Line + self.Labels[labelname].Local = true + end + else + if (self.Labels[labelname]) then + if (self.Labels[labelname].WIP ~= self.WIP) && (math.abs(self.WIP - self.Labels[labelname].WIP) < self.LocalVarRange) then + self.Labels[labelname].WIP = self.WIP + self:Error("Label pointer changed between stages - report this to Black Phoenix!") + end + end + end +end + +function ENT:AddGlobalLabel(labelname) + if (self.FirstPass) then + if (self:GetLabel(labelname)) then + self:Error("Label '"..labelname.."' already exists (previously defined at line "..self.Labels[labelname].DefineLine..")") + else + self.Labels[labelname] = {} + self.Labels[labelname].WIP = self.WIP + self.Labels[labelname].DefineLine = self.Line + self.Labels[labelname].Global = true + end + else + if (self.Labels[labelname]) then + if (self.Labels[labelname].WIP ~= self.WIP) then + self.Labels[labelname].WIP = self.WIP + self:Error("Label pointer changed between stages - report this to Black Phoenix!") + end + end + end +end + +function ENT:WipeLocals() + for labelk,labelv in pairs(self.Labels) do + if (labelv.Local) then + self.Labels[labelk] = nil + end + end +end + +function ENT:WipeLabels() + //for labelk,labelv in pairs(self.Labels) do + iterator = function (labelk,labelv) + self.Labels[labelk].Ignore = true + end + table.foreach(self.Labels,iterator) +end diff --git a/lua/entities/gmod_wire_cpu/cpu_advmath.lua b/lua/entities/gmod_wire_cpu/cpu_advmath.lua new file mode 100644 index 0000000000..41e0b661b4 --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cpu_advmath.lua @@ -0,0 +1,538 @@ +function ENT:InitializeAdvMathASMOpcodes() + //- Vector math ------------------------------------------------------------------------------------------------------- + self.DecodeOpcode["vadd"] = 250 //VADD X,Y : X = X + Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vsub"] = 251 //VSUB X,Y : X = X - Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vmul"] = 252 //VMUL X,Y : X = X * SCALAR Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vdot"] = 253 //VDOT X,Y : X = X . Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vcross"] = 254 //VCROSS X,Y : X = X x Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vmov"] = 255 //VMOV X,Y : X = Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vnorm"] = 256 //VNORM X,Y : X = NORMALIZE(Y) [MODEF,MODEF] 7.00 + self.DecodeOpcode["vcolornorm"] = 257 //VCOLORNORM X,Y : X = COLOR_NORMALIZE(Y) [MODEF,MODEF] 7.00 + //- Matrix math ------------------------------------------------------------------------------------------------------- + self.DecodeOpcode["madd"] = 260 //MADD X,Y : X = X + Y [MATRIX,MATRIX] 7.00 + self.DecodeOpcode["msub"] = 261 //MSUB X,Y : X = X - Y [MATRIX,MATRIX] 7.00 + self.DecodeOpcode["mmul"] = 262 //MMUL X,Y : X = X * Y [MATRIX,MATRIX] 7.00 + self.DecodeOpcode["mrotate"] = 263 //MROTATE X,Y : X = ROT(Y) [MATRIX,4F] 7.00 + self.DecodeOpcode["mscale"] = 264 //MSCALE X,Y : X = SCALE(Y) [MATRIX,4F] 7.00 + self.DecodeOpcode["mperspective"] = 265 //MPERSPECTIVE X,Y : X = PERSP(Y) [MATRIX,4F] 7.00 + self.DecodeOpcode["mtranslate"] = 266 //MTRANSLATE X,Y : X = TRANS(Y) [MATRIX,4F] 7.00 + self.DecodeOpcode["mlookat"] = 267 //MLOOKAT X,Y : X = LOOKAT(Y) [MATRIX,4F] 7.00 + self.DecodeOpcode["mmov"] = 268 //MMOV X,Y : X = Y [MATRIX,MATRIX] 7.00 + self.DecodeOpcode["vlen"] = 269 //VLEN X,Y : X = Sqrt(Y . Y) [F,MODEF] 7.00 + self.DecodeOpcode["mident"] = 270 //MIDENT X : Load identity matrix into X [MATRIX] 7.00 + self.DecodeOpcode["vmode"] = 273 //VMODE X : Vector mode = Y [INT] 7.00 + self.DecodeOpcode["vdiv"] = 295 //VDIV X,Y : X = X / Y [MODEF,MODEF] 7.00 + self.DecodeOpcode["vtransform"] = 296 //VTRANSFORM X,Y : X = X * Y [MODEF,MATRIX] 8.00 + //--------------------------------------------------------------------------------------------------------------------- +end + +function ENT:InitializeAdvMathOpcodeTable() + //------------------------------------------------------------ + self.OpcodeTable[250] = function (Param1, Param2) //VADD + if (self.VMODE == 2) then + local vec1 = self:Read2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local vec2 = self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = 0}) + else + local vec1 = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local vec2 = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + {x = vec1.x + vec2.x, y = vec1.y + vec2.y, z = vec1.z + vec2.z}) + end + end + self.OpcodeTable[251] = function (Param1, Param2) //VSUB + if (self.VMODE == 2) then + local vec1 = self:Read2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local vec2 = self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = 0}) + else + local vec1 = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local vec2 = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + {x = vec1.x - vec2.x, y = vec1.y - vec2.y, z = vec1.z - vec2.z}) + end + end + self.OpcodeTable[252] = function (Param1, Param2) //VMUL + if (self.VMODE == 2) then + local vec = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = vec.x*Param2, y = vec.y*Param2, z = 0}) + else + local vec = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + {x = vec.x*Param2, y = vec.y*Param2, z = vec.z*Param2}) + end + end + self.OpcodeTable[253] = function (Param1, Param2) //VDOT + if (self.VMODE == 2) then + local v1 = self:Read2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local v2 = self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:WriteCell(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + v1.x * v2.x + v1.y * v2.y) + else + local v1 = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local v2 = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:WriteCell(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + v1.x*v2.x + v1.y*v2.y + v1.z*v2.z) + end + end + self.OpcodeTable[254] = function (Param1, Param2) //VCROSS + if (self.VMODE == 2) then + local v1 = self:Read2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local v2 = self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + self:WriteCell(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + v1.x * v2.y - v1.y * v2.x) + else + local vec1 = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local vec2 = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment1]) + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + {x = v1.y * v2.z - v1.z * v2.y, y = v1.z * v2.x - v1.x * v2.z, z = v1.x * v2.y - v1.y * v2.x}) + end + end + self.OpcodeTable[255] = function (Param1, Param2) //VMOV + if (self.VMODE == 2) then + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment1])) + else + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2])) + end + end + self.OpcodeTable[256] = function (Param1, Param2) //VNORM + if (self.VMODE == 2) then + local vec = self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment1]) + local d = (vec.x * vec.x + vec.y * vec.y)^0.5 + 10e-9 + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = vec.x / d, y = vec.y / d}) + else + local vec = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local d = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z)^0.5 + 10e-9 + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + {x = vec.x / d, y = vec.y / d, z = vec.z / d}) + end + end + self.OpcodeTable[257] = function (Param1, Param2) //VCOLORNORM + + end + //------------------------------------------------------------ + self.OpcodeTable[260] = function (Param1, Param2) //MADD + local mx1 = self:ReadMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local mx2 = self:ReadMatrix(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rmx = {} + + for i=0,15 do + rmx[i] = mx1[i] + mx2[i] + end + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rmx) + + end + self.OpcodeTable[261] = function (Param1, Param2) //MSUB + local mx1 = self:ReadMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local mx2 = self:ReadMatrix(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rmx = {} + + for i=0,15 do + rmx[i] = mx1[i] - mx2[i] + end + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rmx) + + end + self.OpcodeTable[262] = function (Param1, Param2) //MMUL + local mx1 = self:ReadMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local mx2 = self:ReadMatrix(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rmx = {} + + for i=0,3 do + for j=0,3 do + rmx[i*4+j] = mx1[i*4+0] * mx2[0*4+j] + + mx1[i*4+1] * mx2[1*4+j] + + mx1[i*4+2] * mx2[2*4+j] + + mx1[i*4+3] * mx2[3*4+j] + end + end + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rmx) + + end + self.OpcodeTable[263] = function (Param1, Param2) //MROTATE + local vec = self:Read4f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rm = {} + + local axis = {} + axis[0] = vec.x + axis[1] = vec.y + axis[2] = vec.z + + local angle = vec.w; + + local mag = math.sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]) + if (mag > 0) then + axis[0] = axis[0] / mag + axis[1] = axis[1] / mag + axis[2] = axis[2] / mag + end + + local sine = math.sin(angle) + local cosine = math.cos(angle) + + local ab = axis[0] * axis[1] * (1 - cosine) + local bc = axis[1] * axis[2] * (1 - cosine) + local ca = axis[2] * axis[0] * (1 - cosine) + local tx = axis[0] * axis[0] + local ty = axis[1] * axis[1] + local tz = axis[2] * axis[2] + + rm[0] = tx + cosine * (1 - tx) + rm[1] = ab + axis[2] * sine + rm[2] = ca - axis[1] * sine + rm[3] = 0 + rm[4] = ab - axis[2] * sine + rm[5] = ty + cosine * (1 - ty) + rm[6] = bc + axis[0] * sine + rm[7] = 0 + rm[8] = ca + axis[1] * sine + rm[9] = bc - axis[0] * sine + rm[10] = tz + cosine * (1 - tz) + rm[11] = 0 + rm[12] = 0 + rm[13] = 0 + rm[14] = 0 + rm[15] = 1 + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rm) + + end + self.OpcodeTable[264] = function (Param1, Param2) //MSCALE + local vec = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rm = {} + + + rm[0] = vec.x + rm[1] = 0 + rm[2] = 0 + rm[3] = 0 + + rm[4] = 0 + rm[5] = vec.y + rm[6] = 0 + rm[7] = 0 + + rm[8] = 0 + rm[9] = 0 + rm[10] = vec.z + rm[11] = 0 + + rm[12] = 0 + rm[13] = 0 + rm[14] = 0 + rm[15] = 1 + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rm) + + end + self.OpcodeTable[265] = function (Param1, Param2) //MPERSPECTIVE + local vec = self:Read4f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rm = {} + + local sine + local cotangent + local deltaZ + local radians = vec.x / 2.0 * 3.1415 / 180.0 + + deltaZ = vec.w - vec.z + sine = math.sin(radians) + //Should be non-zero to avoid division by zero + + cotangent = math.cos(radians) / sine + + rm[0*4+0] = cotangent / vec.y + rm[1*4+0] = 0.0; + rm[2*4+0] = 0.0; + rm[3*4+0] = 0.0; + + rm[0*4+1] = 0.0; + rm[1*4+1] = cotangent; + rm[2*4+1] = 0.0; + rm[3*4+1] = 0.0; + + rm[0*4+2] = 0.0; + rm[1*4+2] = 0.0; + rm[2*4+2] = -(vec.z + vec.w) / deltaZ; + rm[3*4+2] = -2 * vec.z * vec.w / deltaZ; + + rm[0*4+3] = 0.0; + rm[1*4+3] = 0.0; + rm[2*4+3] = -1; + rm[3*4+3] = 0; + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rm) + end + self.OpcodeTable[266] = function (Param1, Param2) //MTRANSLATE + local vec = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + local rm = {} + + + rm[0] = 1 + rm[1] = 0 + rm[2] = 0 + rm[3] = vec.x + + rm[4] = 0 + rm[5] = 1 + rm[6] = 0 + rm[7] = vec.y + + rm[8] = 0 + rm[9] = 0 + rm[10] = 1 + rm[11] = vec.z + + rm[12] = 0 + rm[13] = 0 + rm[14] = 0 + rm[15] = 1 + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rm) + end + self.OpcodeTable[267] = function (Param1, Param2) //MLOOKAT + local eye = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]+0) + local center = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]+3) + local up = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]+6) + local rm = {} + + local x = {} + local y = {} + local z = {} + + // Difference eye and center vectors to make Z vector + z[0] = eye.x - center.x + z[1] = eye.y - center.y + z[2] = eye.z - center.z + + // Normalize Z + local mag = math.sqrt(z[0]*z[0] + z[1]*z[1] + z[2]*z[2]) + if (mag > 0) then + z[0] = z[0] / mag + z[1] = z[1] / mag + z[2] = z[2] / mag + end + + // Up vector makes Y vector + y[0] = up.x + y[1] = up.y + y[2] = up.z + + // X vector = Y cross Z. + x[0] = y[1]*z[2] - y[2]*z[1] + x[1] = -y[0]*z[2] + y[2]*z[0] + x[2] = y[0]*z[1] - y[1]*z[0] + + // Recompute Y = Z cross X + y[0] = z[1]*x[2] - z[2]*x[1] + y[1] = -z[0]*x[2] + z[2]*x[0] + y[2] = z[0]*x[1] - z[1]*x[0] + + // Normalize X + mag = math.sqrt(x[0]*x[0] + x[1]*x[1] + x[2]*x[2]) + if (mag > 0) then + x[0] = x[0] / mag + x[1] = x[1] / mag + x[2] = x[2] / mag + end + + // Normalize Y + mag = math.sqrt(y[0]*y[0] + y[1]*y[1] + y[2]*y[2]) + if (mag > 0) then + y[0] = y[0] / mag + y[1] = y[1] / mag + y[2] = y[2] / mag + end + + // Build resulting view matrix. + rm[0*4+0] = x[0]; rm[0*4+1] = x[1]; + rm[0*4+2] = x[2]; rm[0*4+3] = -x[0]*eye.x + -x[1]*eye.y + -x[2]*eye.z; + + rm[1*4+0] = y[0]; rm[1*4+1] = y[1]; + rm[1*4+2] = y[2]; rm[1*4+3] = -y[0]*eye.x + -y[1]*eye.y + -y[2]*eye.z; + + rm[2*4+0] = z[0]; rm[2*4+1] = z[1]; + rm[2*4+2] = z[2]; rm[2*4+3] = -z[0]*eye.x + -z[1]*eye.y + -z[2]*eye.z; + + rm[3*4+0] = 0.0; rm[3*4+1] = 0.0; rm[3*4+2] = 0.0; rm[3*4+3] = 1.0; + + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1],rm) + end + self.OpcodeTable[268] = function (Param1, Param2) //MMOV + self:WriteMatrix(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + self:ReadMatrix(Param2 + self[self.PrecompileData[self.XEIP].Segment2])) + end + self.OpcodeTable[269] = function (Param1, Param2) //VLEN + if (self.VMODE == 2) then + local vec = self:Read2f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + return (vec.x * vec.x + vec.y * vec.y)^(0.5) + else + local vec = self:Read3f(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + return (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z)^(0.5) + end + end + self.OpcodeTable[270] = function (Param1, Param2) //MIDENT + local rm = {} + + rm[0] = 1 + rm[1] = 0 + rm[2] = 0 + rm[3] = 0 + + rm[4] = 0 + rm[5] = 1 + rm[6] = 0 + rm[7] = 0 + + rm[8] = 0 + rm[9] = 0 + rm[10] = 1 + rm[11] = 0 + + rm[12] = 0 + rm[13] = 0 + rm[14] = 0 + rm[15] = 1 + + self:WriteMatrix(Param1,rm) + end + self.OpcodeTable[273] = function (Param1, Param2) //VMODE + self.VMODE = math.Clamp(math.floor(Param1),2,3) + end + self.OpcodeTable[295] = function (Param1, Param2) //VDIV + if (math.abs(Param2) < 1e-12) then + self:Interrupt(3,0) + else + if (self.VMODE == 2) then + local vec = self:Read2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = vec.x/Param2, y = vec.y/Param2, z = 0}) + else + local vec = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment2], + {x = vec.x/Param2, y = vec.y/Param2, z = vec.z/Param2}) + end + end + end + self.OpcodeTable[296] = function (Param1, Param2) //VTRANSFORM + if (self.VMODE == 2) then + local vec = self:Read2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local mx = self:ReadMatrix(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + + local tmp = {} + for i=0,3 do + tmp[i] = mx[i*4+0] * vec.x + + mx[i*4+1] * vec.y + + mx[i*4+2] * 0 + + mx[i*4+3] * 1 + end + + + self:Write2f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = tmp[0], y = tmp[1], z = 0}) + else + local vec = self:Read3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1]) + local mx = self:ReadMatrix(Param2 + self[self.PrecompileData[self.XEIP].Segment2]) + + local tmp = {} + for i=0,3 do + tmp[i] = mx[i*4+0] * vec.x + + mx[i*4+1] * vec.y + + mx[i*4+2] * vec.z + + mx[i*4+3] * 1 + end + + + self:Write3f(Param1 + self[self.PrecompileData[self.XEIP].Segment1], + {x = tmp[0], y = tmp[1], z = tmp[2]}) + end + end +end + +function ENT:Read2f(addr) + local resultcoord = {} + if (addr == 0) then + resultcoord.x = 0 + resultcoord.y = 0 + resultcoord.z = 0 + resultcoord.w = 0 + else + resultcoord.x = self:ReadCell(addr+0) + resultcoord.y = self:ReadCell(addr+1) + resultcoord.z = 0 + resultcoord.w = 0 + end + return resultcoord +end + +function ENT:Read3f(addr) + local resultcoord = {} + if (addr == 0) then + resultcoord.x = 0 + resultcoord.y = 0 + resultcoord.z = 0 + resultcoord.w = 0 + else + resultcoord.x = self:ReadCell(addr+0) + resultcoord.y = self:ReadCell(addr+1) + resultcoord.z = self:ReadCell(addr+2) + resultcoord.w = 0 + end + return resultcoord +end + +function ENT:Read4f(addr) + local resultcoord = {} + if (addr == 0) then + resultcoord.x = 0 + resultcoord.y = 0 + resultcoord.z = 0 + resultcoord.w = 0 + else + resultcoord.x = self:ReadCell(addr+0) + resultcoord.y = self:ReadCell(addr+1) + resultcoord.z = self:ReadCell(addr+2) + resultcoord.w = self:ReadCell(addr+3) + end + return resultcoord +end + +function ENT:ReadMatrix(addr) + local resultmx = {} + for i=0,15 do + resultmx[i] = self:ReadCell(addr+i) + end + return resultmx +end + +function ENT:WriteMatrix(addr,resultmx) + for i=0,15 do + self:WriteCell(addr+i,resultmx[i]) + end +end + +function ENT:Write2f(addr,coord) + self:WriteCell(addr+0,coord.x) + self:WriteCell(addr+1,coord.y) +end + +function ENT:Write3f(addr,coord) + self:WriteCell(addr+0,coord.x) + self:WriteCell(addr+1,coord.y) + self:WriteCell(addr+2,coord.z) +end + +function ENT:Write4f(addr,coord) + self:WriteCell(addr+0,coord.x) + self:WriteCell(addr+1,coord.y) + self:WriteCell(addr+2,coord.z) + self:WriteCell(addr+3,coord.w) +end diff --git a/lua/entities/gmod_wire_cpu/cpu_bitwise.lua b/lua/entities/gmod_wire_cpu/cpu_bitwise.lua new file mode 100644 index 0000000000..7fad055b74 --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cpu_bitwise.lua @@ -0,0 +1,143 @@ +// Previously LuaBit v0.4, by hanzhao (abrash_han@hotmail.com) +// Rewrote most of operations to be at least a little bit faster, and work with custom amount of bits + +function ENT:ForceInteger(n) + return math.floor(n or 0) +end + +function ENT:Is48bitInteger(n) + if ((math.floor(n) == n) and (n <= 140737488355328) and (n >= -140737488355327)) then + return true + else + return false + end +end + +function ENT:IntegerToBinary(n) + n = self:ForceInteger(n) + if (n < 0) then + local tbl = self:IntegerToBinary(-n-1) + tbl[self.IPREC] = 1 + return tbl + end + + local tbl = {} + local cnt = 0 + while ((n > 0) and (cnt < self.IPREC)) do + local last = math.fmod(n,2) + if(last == 1) then + tbl[cnt] = 1 + else + tbl[cnt] = 0 + end + n = (n-last)/2 + cnt = cnt + 1 + end + while (cnt < self.IPREC) do + tbl[cnt] = 0 + cnt = cnt + 1 + end + + return tbl +end + +function ENT:BinaryToInteger(tbl) + local n = table.getn(tbl) + local sign = 0 + if (n > self.IPREC-1) then + sign = tbl[self.IPREC-1] + n = self.IPREC + end + + local rslt = 0 + local power = 1 + for i = 0, n-1 do + rslt = rslt + tbl[i]*power + power = power*2 + end + + if (sign == 1) then + return -rslt-1 + else + return rslt + end +end + +function ENT:BinaryOr(m,n) + local tbl_m = self:IntegerToBinary(m) + local tbl_n = self:IntegerToBinary(n) + local tbl = {} + + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 0, rslt-1 do + tbl[i] = math.min(1,tbl_m[i]+tbl_n[i]) + end + + return self:BinaryToInteger(tbl) +end + +function ENT:BinaryAnd(m,n) + local tbl_m = self:IntegerToBinary(m) + local tbl_n = self:IntegerToBinary(n) + local tbl = {} + + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 0, rslt-1 do + tbl[i] = tbl_m[i]*tbl_n[i] + end + + return self:BinaryToInteger(tbl) +end + +function ENT:BinaryNot(n) + local tbl_n = self:IntegerToBinary(n) + local tbl = {} + + local rslt = table.getn(tbl_n) + for i = 0, rslt-1 do + tbl[i] = 1-tbl_n[i] + end + return self:BinaryToInteger(tbl) +end + +function ENT:BinaryXor(m,n) + local tbl_m = self:IntegerToBinary(m) + local tbl_n = self:IntegerToBinary(n) + local tbl = {} + + local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n)) + for i = 0, rslt-1 do + tbl[i] = (tbl_m[i]+tbl_n[i]) % 2 + end + + return self:BinaryToInteger(tbl) +end + +function ENT:BinarySHR(n,bits) + local tbl_n = self:IntegerToBinary(n) + local tbl = {} + + local rslt = table.getn(tbl_n) + for i = 0, self.IPREC-bits-1 do + tbl[i] = tbl_n[i+bits] + end + for i = self.IPREC-bits,rslt-1 do + tbl[i] = 0 + end + + return self:BinaryToInteger(tbl) +end + +function ENT:BinarySHL(n,bits) + local tbl_n = self:IntegerToBinary(n) + local tbl = {} + + for i = bits,self.IPREC-1 do + tbl[i] = tbl_n[i-bits] + end + for i = 0,bits-1 do + tbl[i] = 0 + end + + return self:BinaryToInteger(tbl) +end diff --git a/lua/entities/gmod_wire_cpu/cpu_bus.lua b/lua/entities/gmod_wire_cpu/cpu_bus.lua new file mode 100644 index 0000000000..bb46bafbae --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cpu_bus.lua @@ -0,0 +1,235 @@ +function ENT:Write(value) + if (tonumber(value) ~= nil) && (value) then + if (self.UseROM == true) then + if (self.WIP < 65536) then + self.ROMMemory[self.WIP] = value + end + end + + self:WriteCell(self.WIP,value) + if (self.Debug) && (value != 0) then + Msg("-> ZyeliosASM: Wrote "..value.." at ["..self.WIP.."]\n") + end + end + self.WIP = self.WIP + 1 +end + +function ENT:ReadCell(Address) + if (not Address) then + if (self.Debug) then + print("Non-existant address fed into address bus (read)!") + end + return nil + end + + Page = math.floor(Address / 128) + if (not self:Is48bitInteger(Address)) then + self:Interrupt(15,Address) + return nil + end + + if (self.BusLock == 1) then + if (self.Debug) then self:DebugMessage("Warning: Bus was read while locked") end + return nil + end + + //Map address + if ((self.Page[Page]) and (self.Page[Page].MappedTo) and (self.Page[Page].MappedTo ~= Page)) then + Address = Address % 128 + self.Page[Page].MappedTo*128 + end + + if (Address < 0) then + return self:ReadPort(-Address-1) + end + + if ((self.EF == 1) && (self.Page[Page]) + && (self.Page[Page].Read == 0) && (self.CurrentPage.RunLevel > self.Page[Page].RunLevel)) then //Page protection + self:Interrupt(12,Address) + return nil + end + + if (Address < 65536) then + if (self.Memory[Address]) then + return self.Memory[Address] + else + return 0 + end + else + if (self.Inputs.MemBus.Src) then + if (self.Inputs.MemBus.Src.ReadCell) then + local var = self.Inputs.MemBus.Src:ReadCell(Address-65536) + if (var) then + return var + else + self:Interrupt(7,Address) + return nil + end + else + self:Interrupt(8,Address) + return nil + end + else + self:Interrupt(7,Address) + return nil + end + end +end + +function ENT:WriteCell(Address, value) + if (not Address) then + if (self.Debug) then + print("Non-existant address fed into address bus (write)!") + end + return nil + end + + Page = math.floor(Address / 128) + if (not self:Is48bitInteger(Address)) then + self:Interrupt(15,Address) + return false + end + + if (self.BusLock == 1) then + if (self.Debug) then self:DebugMessage("Warning: Bus was written while locked") end + return false + end + + //Map address + if ((self.Page[Page]) and (self.Page[Page].MappedTo) and (self.Page[Page].MappedTo ~= Page)) then + Address = Address % 128 + self.Page[Page].MappedTo*128 + end + + if (Address < 0) then + return self:WritePort(-Address-1,value) + end + + if (self.PrecompileMemory[Address]) then //If this byte was precompiled + local xeip = self.PrecompileMemory[Address] + self.PrecompileMemory[Address] = nil //Void precompile information + self.PrecompileData[xeip] = nil + end + + if ((self.EF == 1) && (self.Page[Page]) + && (self.Page[Page].Write == 0) && (self.CurrentPage.RunLevel > self.Page[Page].RunLevel)) then //Page protection + self:Interrupt(9,Address) + return false + end + + if (Address < 65536) then + self.Memory[Address] = value + return true + else + if (self.Inputs.MemBus.Src) then + if (self.Inputs.MemBus.Src.WriteCell) then + if (self.Inputs.MemBus.Src:WriteCell(Address-65536,value)) then + return true + else + self:Interrupt(7,Address) + return false + end + else + self:Interrupt(8,Address) + return false + end + else + self:Interrupt(7,Address) + return false + end + end + return true +end + +//TODO: Move readport and writeport in write and read + +function ENT:ReadPort(Address) + if (self.BusLock == 1) then + if (self.Debug) then self:DebugMessage("Warning: IOBus was read while locked") end + return nil + end + + if (Address < 0) then + self:Interrupt(10,Address) + return nil + end + if (self.Inputs.IOBus.Src) then + if (self.Inputs.IOBus.Src.ReadCell) then + local var = self.Inputs.IOBus.Src:ReadCell(math.floor(Address)) + if (var) then + return var + else + self:Interrupt(10,Address) + return nil + end + else + self:Interrupt(8,-Address) + return nil + end + else + return 0 + end +end + +function ENT:WritePort(Address, value) + if (self.BusLock == 1) then + if (self.Debug) then self:DebugMessage("Warning: IOBus was written while locked") end + return false + end + + if (Address < 0) then + self:Interrupt(10,Address) + return false + end + if (self.Inputs.IOBus.Src) then + if (self.Inputs.IOBus.Src.WriteCell) then + if (self.Inputs.IOBus.Src:WriteCell(math.floor(Address),value)) then + return true + else + self:Interrupt(10,Address) + return false + end + else + self:Interrupt(8,-Address) + return false + end + else + return true + end +end + +function ENT:Push(value) + if (self.BusLock == 1) then + if (self.Debug) then self:DebugMessage("Warning: Stack write while locked") end + return false + end + + self:WriteCell(self.ESP+self.SS,value) + self.ESP = self.ESP - 1 + if (self.ESP < 0) then + self.ESP = 0 + self:Interrupt(6,self.ESP) + return false + end + return true +end + +function ENT:Pop() + if (self.BusLock == 1) then + if (self.Debug) then self:DebugMessage("Warning: Stack read while locked") end + return nil + end + + self.ESP = self.ESP + 1 + if (self.ESP > self.ESZ) then + self.ESP = self.ESZ + self:Interrupt(6,self.ESP) + return 0 + end + + local popvalue = self:ReadCell(self.ESP+self.SS) + if (popvalue) then + return popvalue + else + self:Interrupt(6,self.ESP) + return 0 + end +end diff --git a/lua/entities/gmod_wire_cpu/cpu_interrupt.lua b/lua/entities/gmod_wire_cpu/cpu_interrupt.lua new file mode 100644 index 0000000000..96b5699938 --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cpu_interrupt.lua @@ -0,0 +1,209 @@ +//INTERRUPTS TABLE +//Value | Meaning +//--------------------------------------------------------------------- +//2 | End of program execution +//3 | Division by zero +//4 | Unknown opcode +//5 | Internal processor error +//6 | Stack error (overflow/underflow) +//7 | Memory read/write fault +//8 | MemBus fault +//9 | Write access violation (page protection) +//10 | Port read/write fault +//11 | Page acccess violation (page protection) +//12 | Read access violation (page protection) +//13 | General processor fault +//14 | Execute access violation (page protection) +//15 | Address space violation +//31 | Debug trap +//---------------------------------------------------------------------- + +function ENT:NMIInterrupt(intnumber) + if ((self.IF == 1) && + self:Push(self.LS) && + self:Push(self.KS) && + self:Push(self.ES) && + self:Push(self.GS) && + self:Push(self.FS) && + self:Push(self.DS) && + self:Push(self.SS) && + self:Push(self.CS) && + + self:Push(self.EDI) && + self:Push(self.ESI) && + self:Push(self.ESP) && + self:Push(self.EBP) && + self:Push(self.EDX) && + self:Push(self.ECX) && + self:Push(self.EBX) && + self:Push(self.EAX) && + + self:Push(self.CMPR) && + self:Push(self.IP)) then + self:Interrupt(intnumber,0,1) + end +end + +function ENT:Interrupt(intnumber,intparam,isNMI) + if (self.Compiling) then + self.FatalError = true + return + end + if (self.AuxIO == 1) then + if (self.Debug) then + self:DebugMessage("EXTERNAL INTERRUPT: #"..intnumber.."\nADDRESS: "..self.XEIP.."\nPARAM="..intparam.."\nLastReadAddress="..self.LADD) + end + return + end + if (self.Debug) then + if (self.INTR == 0) then + self:DebugMessage("INTERRUPT: #"..intnumber.."\nADDRESS: "..self.XEIP.."\nPARAM="..intparam.."\nLastReadAddress="..self.LADD) + else + self:DebugMessage("CASCADE INTERRUPT: #"..intnumber.."\nADDRESS: "..self.XEIP.."\nLastReadAddress="..self.LADD) + end + end + + + if (self.INTR == 1) then return end + self.INTR = 1 + self.BusLock = 1 + self.LINT = intnumber + if (intparam) then self.LADD = intparam else self.LADD = self.XEIP end + + local fracparam = intparam + if (intparam ~= 0) then + while (math.floor(fracparam) >= 1) do + fracparam = fracparam / 10 + end + end + Wire_TriggerOutput(self.Entity, "Error", intnumber+fracparam) + + if (self.IF == 1) then + if (self.PF == 0) && (self.EF == 0) then + if (intnumber < 0) or (intnumber > 255) or (intnumber > self.NIDT-1) then + //Not handled + return + end + if (intnumber == 0) then + self:Reset() + return + end + + if (intnumber ~= 31) then //Don't die on debug trap + self.Clk = 0 + end + return + else + if (self.EF == 1) then //4 bytes interrupt table + if (intnumber < 0) or (intnumber > 255) then + self.INTR = 0 + self:Interrupt(13,3) + return + end + + if (intnumber > self.NIDT-1) then + if (intnumber == 0) then + self:Reset() + end + if (intnumber == 1) then + self.Clk = 0 + end + return + end + + local intaddress = self.IDTR + intnumber*4 + + self.BusLock = 0 + self:SetCurrentPage(intaddress) + local int_ip = self:ReadCell(intaddress+0) + local int_cs = self:ReadCell(intaddress+1) + local int_ = self:ReadCell(intaddress+2) + local int_flags = self:IntegerToBinary(self:ReadCell(intaddress+3)) + self:SetCurrentPage(self.XEIP) + self.BusLock = 1 + + //Flags: + //3 [8 ] = CMPR shows if interrupt occured + //4 [16] = Interrupt does not set CS + //5 [32] = Interrupt enabled + //6 [64] = NMI interrupt + + if ((isNMI) and (int_flags[6] ~= 1)) then + self.INTR = 0 + self:Interrupt(13,4) + return + end + + if (int_flags[5] == 1) then + if (self.Debug) then self:DebugMessage("INTERRUPT: #"..intnumber.." HANDLED\nJumpOffset="..int_ip..":"..int_cs.."\n") end + + self.BusLock = 0 + self:Push(self.IP) + self:Push(self.CS) + self.BusLock = 1 + + self.IP = int_ip + if (int_flags[4] == 0) then + self.CS = int_cs + end + + if (int_flags[3] == 1) then + self.CMPR = 1 + end + else + if (intnumber == 0) then + self:Reset() + end + if (intnumber == 1) then + self.Clk = 0 + end + if (int_flags[3] == 1) then + self.CMPR = -1 + end + end + else //2 bytes interrupt table + if (intnumber < 0) or (intnumber > 255) then + self.INTR = 0 + self:Interrupt(13,3) + return + end + + local intaddress = self.IDTR + intnumber*2 + if (intaddress > 65535) then intaddress = 65535 end + if (intaddress < 0) then intaddress = 0 end + local intoffset = self.Memory[intaddress] + local intprops = self.Memory[intaddress+1] + if ((intprops == 32) || (intprops == 96)) then //Interrupt active, temp fix + if (self.Debug) then + self:DebugMessage("INTERRUPT: #"..intnumber.." HANDLED\nJumpOffset="..intoffset.."\n") + end + + self.BusLock = 0 + if (intnumber == 4 ) || + (intnumber == 7 ) || + (intnumber == 9 ) || + (intnumber == 10) then + self:Push(self.LADD) + end + if (intnumber == 4 ) || + (intnumber == 31) then //If wrong opcode or debug trap, then store + self:Push(self.ILTC) + end + if self:Push(self.IP) then //Store IRET + self:Push(self.XEIP) + self.IP = intoffset + end + self.CMPR = 0 + self.BusLock = 1 + else + if (intnumber == 1) then + self.Clk = 0 + end + self.CMPR = 1 + end + end + end + end + self.BusLock = 0 +end + diff --git a/lua/entities/gmod_wire_cpu/cpu_opcodes.lua b/lua/entities/gmod_wire_cpu/cpu_opcodes.lua new file mode 100644 index 0000000000..26e76a434c --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cpu_opcodes.lua @@ -0,0 +1,1260 @@ +if (EmuFox) then + include('gmod_wire_cpu/cpu_advmath.lua') +else + include('cpu_advmath.lua') +end + +function ENT:InitializeOpcodeNames() + self.DecodeOpcode = {} + + //----------------------------------------------------------------------------- ------ + self.DecodeOpcode["jne"] = 1 //JNE X : IP = X, IF CMPR ~= 0 + self.DecodeOpcode["jnz"] = 1 //JNZ X : IP = X, IF CMPR ~= 0 + self.DecodeOpcode["jmp"] = 2 //JMP X : IP = X + self.DecodeOpcode["jg"] = 3 //JG X : IP = X, IF CMPR > 0 + self.DecodeOpcode["jnle"] = 3 //JNLE X : IP = X, IF !(CMPR <= 0) + self.DecodeOpcode["jge"] = 4 //JGE X : IP = X, IF CMPR >= 0 + self.DecodeOpcode["jnl"] = 4 //JNL X : IP = X, IF !(CMPR < 0) + self.DecodeOpcode["jl"] = 5 //JL X : IP = X, IF CMPR < 0 + self.DecodeOpcode["jnge"] = 5 //JNGE X : IP = X, IF !(CMPR >= 0) + self.DecodeOpcode["jle"] = 6 //JLE X : IP = X, IF CMPR <= 0 + self.DecodeOpcode["jng"] = 6 //JNG X : IP = X, IF !(CMPR > 0) + self.DecodeOpcode["je"] = 7 //JE X : IP = X, IF CMPR = 0 + self.DecodeOpcode["jz"] = 7 //JZ X : IP = X, IF CMPR = 0 + self.DecodeOpcode["cpuid"] = 8 //CPUID X : EAX -> CPUID[X] + self.DecodeOpcode["push"] = 9 //PUSH X : WRITE(ESP+SS,X); ESP = ESP - 1 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["add"] = 10 //ADD X,Y : X = X + Y + self.DecodeOpcode["sub"] = 11 //SUB X,Y : X = X - Y + self.DecodeOpcode["mul"] = 12 //MUL X,Y : X = X * Y + self.DecodeOpcode["div"] = 13 //DIV X,Y : X = X / Y + self.DecodeOpcode["mov"] = 14 //MOV X,Y : X = y + self.DecodeOpcode["cmp"] = 15 //CMP X,Y : CMPR = X - Y + self.DecodeOpcode["rd"] = 16 //RD X,Y : X = MEMORY[Y] + self.DecodeOpcode["wd"] = 17 //WD X,Y : MEMORY[X] = Y + self.DecodeOpcode["min"] = 18 //MIN X,Y : MIN(X,Y) + self.DecodeOpcode["max"] = 19 //MAX X,Y : MAX(X,Y) + //----------------------------------------------------------------------------------- + self.DecodeOpcode["inc"] = 20 //INC X : X = X + 1 + self.DecodeOpcode["dec"] = 21 //DEC X : X = X - 1 + self.DecodeOpcode["neg"] = 22 //NEG X : X = -X + self.DecodeOpcode["rand"] = 23 //RAND X : X = Random(0..1) + self.DecodeOpcode["loop"] = 24 //LOOP X : IF ECX ~= 0 THEN JUMP X 2.00 + self.DecodeOpcode["loopa"] = 25 //LOOPA X : IF EAX ~= 0 THEN JUMP X 2.00 + self.DecodeOpcode["loopb"] = 26 //LOOPB X : IF EBX ~= 0 THEN JUMP X 2.00 + self.DecodeOpcode["loopd"] = 27 //LOOPD X : IF EDX ~= 0 THEN JUMP X 2.00 + self.DecodeOpcode["spg"] = 28 //SPG X : PAGE(X) = READ ONLY 2.00 + self.DecodeOpcode["cpg"] = 29 //CPG X : PAGE(X) = READ AND WRITE 2.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["pop"] = 30 //POP X : X = READ(ESP+SS); ESP = ESP + 1 + self.DecodeOpcode["call"] = 31 //CALL X : IP -> STACK; IP = X + self.DecodeOpcode["bnot"] = 32 //BNOT X : X = BINARY NOT X + self.DecodeOpcode["fint"] = 33 //FINT X : X = FLOOR(X) + self.DecodeOpcode["frnd"] = 34 //FRND X : X = ROUND(X) + self.DecodeOpcode["ffrac"] = 35 //FFRAC X : X = X - FLOOR(X) + self.DecodeOpcode["finv"] = 36 //FINV X : X = 1 / X + self.DecodeOpcode["halt"] = 37 //HALT X : HALT UNTIL PORT[X] + self.DecodeOpcode["fshl"] = 38 //FSHL X : X = X * 2 2.00 + self.DecodeOpcode["fshr"] = 39 //FSHR X : X = X / 2 2.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["ret"] = 40 //RET : IP <- STACK + self.DecodeOpcode["iret"] = 41 //IRET : IP <- STACK 2.00 + self.DecodeOpcode["sti"] = 42 //STI : IF = TRUE 2.00 + self.DecodeOpcode["cli"] = 43 //CLI : IF = FALSE 2.00 + self.DecodeOpcode["stp"] = 44 //STP : PF = TRUE 2.00 + self.DecodeOpcode["clp"] = 45 //CLP : PF = FALSE 2.00 + //self.DecodeOpcode[""] = 46 //RESERVED : + self.DecodeOpcode["retf"] = 47 //RETF : IP,CS <- STACK 2.00 + self.DecodeOpcode["stef"] = 48 //STEF : EF = TRUE 4.00 + self.DecodeOpcode["clef"] = 49 //CLEF : EF = FALSE 4.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["and"] = 50 //FAND X,Y : X = X AND Y + self.DecodeOpcode["or"] = 51 //FOR X,Y : X = X OR Y + self.DecodeOpcode["xor"] = 52 //FXOR X,Y : X = X XOR Y + self.DecodeOpcode["fsin"] = 53 //FSIN X,Y : X = SIN Y + self.DecodeOpcode["fcos"] = 54 //FCOS X,Y : X = COS Y + self.DecodeOpcode["ftan"] = 55 //FTAN X,Y : X = TAN Y + self.DecodeOpcode["fasin"] = 56 //FASIN X,Y : X = ASIN Y + self.DecodeOpcode["facos"] = 57 //FACOS X,Y : X = ACOS Y + self.DecodeOpcode["fatan"] = 58 //FATAN X,Y : X = ATAN Y + self.DecodeOpcode["mod"] = 59 //MOD X,Y : X = X MOD Y 2.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["bit"] = 60 //BIT X,Y : CMPR = BIT(X,Y) 2.00 + self.DecodeOpcode["sbit"] = 61 //SBIT X,Y : BIT(X,Y) = 1 2.00 + self.DecodeOpcode["cbit"] = 62 //CBIT X,Y : BIT(X,Y) = 0 2.00 + self.DecodeOpcode["tbit"] = 63 //TBIT X,Y : BIT(X,Y) = ~BIT(X,Y) 2.00 + self.DecodeOpcode["band"] = 64 //BAND X,Y : X = X BAND Y 2.00 + self.DecodeOpcode["bor"] = 65 //BOR X,Y : X = X BOR Y 2.00 + self.DecodeOpcode["bxor"] = 66 //BXOR X,Y : X = X BXOR Y 2.00 + self.DecodeOpcode["bshl"] = 67 //BSHL X,Y : X = X BSHL Y 2.00 + self.DecodeOpcode["bshr"] = 68 //BSHR X,Y : X = X BSHR Y 2.00 + self.DecodeOpcode["jmpf"] = 69 //JMPF X,Y : CS = Y; IP = X 2.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["nmiint"] = 70 //NMIINT X : NMIINTERRUPT(X); 4.00 + self.DecodeOpcode["cne"] = 71 //CNE X : CALL(X), IF CMPR ~= 0 2.00 + self.DecodeOpcode["cnz"] = 71 //CNZ X : CALL(X), IF CMPR ~= 0 2.00 + //self.DecodeOpcode[""] = 72 //RESERVED : + self.DecodeOpcode["cg"] = 73 //CG X : CALL(X), IF CMPR > 0 2.00 + self.DecodeOpcode["cnle"] = 73 //CNLE X : CALL(X), IF !(CMPR <= 0) 2.00 + self.DecodeOpcode["cge"] = 74 //CGE X : CALL(X), IF CMPR >= 0 2.00 + self.DecodeOpcode["cnl"] = 74 //CNL X : CALL(X), IF !(CMPR < 0) 2.00 + self.DecodeOpcode["cl"] = 75 //CL X : CALL(X), IF CMPR < 0 2.00 + self.DecodeOpcode["cnge"] = 75 //CNGE X : CALL(X), IF !(CMPR >= 0) 2.00 + self.DecodeOpcode["cle"] = 76 //CLE X : CALL(X), IF CMPR <= 0 2.00 + self.DecodeOpcode["cng"] = 76 //CNG X : CALL(X), IF !(CMPR > 0) 2.00 + self.DecodeOpcode["ce"] = 77 //CE X : CALL(X), IF CMPR = 0 2.00 + self.DecodeOpcode["cz"] = 77 //CZ X : CALL(X), IF CMPR = 0 2.00 + self.DecodeOpcode["mcopy"] = 78 //MCOPY X : X BYTES(ESI) -> EDI 2.00 + self.DecodeOpcode["mxchg"] = 79 //MXCHG X : X BYTES(ESI) <> EDI 2.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["fpwr"] = 80 //FPWR X,Y : X = X ^ Y 2.00 + self.DecodeOpcode["xchg"] = 81 //XCHG X,Y : X,Y = Y,X 2.00 + self.DecodeOpcode["flog"] = 82 //FLOG X,Y : X = LOG(Y) 2.00 + self.DecodeOpcode["flog10"] = 83 //FLOG10 X,Y : X = LOG10(Y) 2.00 + self.DecodeOpcode["in"] = 84 //IN X,Y : X = PORT[Y] 2.00 + self.DecodeOpcode["out"] = 85 //OUT X,Y : PORT[X] = Y 2.00 + self.DecodeOpcode["fabs"] = 86 //FABS X,Y : X = ABS(Y) 2.00 + self.DecodeOpcode["fsgn"] = 87 //FSGN X,Y : X = SIGN(Y) 2.00 + self.DecodeOpcode["fexp"] = 88 //FEXP X,Y : X = EXP(Y) 2.00 + self.DecodeOpcode["callf"] = 89 //CALLF X,Y : CS = Y; CALL(X) 2.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["fpi"] = 90 //FPI X : X = PI 2.00 + self.DecodeOpcode["fe"] = 91 //FE X : X = E 2.00 + self.DecodeOpcode["int"] = 92 //INT X : INTERRUPT(X) 2.00 + self.DecodeOpcode["tpg"] = 93 //TPG X : CMPR = 1 IF PAGE READS, ELSE 0 2.00 + self.DecodeOpcode["fceil"] = 94 //FCEIL X : X = CEIL(X) 2.00 + self.DecodeOpcode["erpg"] = 95 //ERPG X : ERASE ROM PAGE(X) 2.00 + self.DecodeOpcode["wrpg"] = 96 //WRPG X : WRITE ROM PAGE(X) 2.00 + self.DecodeOpcode["rdpg"] = 97 //RDPG X : READ ROM PAGE(X) 2.00 + self.DecodeOpcode["timer"] = 98 //TIMER X : X = TIMER 2.00 + self.DecodeOpcode["lidtr"] = 99 //LIDTR X : IDTR = X 2.00 + //self.DecodeOpcode[""] = 100 //RESERVED + //----------------------------------------------------------------------------------- + self.DecodeOpcode["jner"] = 101 //JNER X : IP = IP+X, IF CMPR ~= 0 + self.DecodeOpcode["jnzr"] = 101 //JNZR X : IP = IP+X, IF CMPR ~= 0 + self.DecodeOpcode["jmpr"] = 102 //JMPR X : IP = IP+X + self.DecodeOpcode["jgr"] = 103 //JGR X : IP = IP+X, IF CMPR > 0 + self.DecodeOpcode["jnler"] = 103 //JNLER X : IP = IP+X, IF !(CMPR <= 0) + self.DecodeOpcode["jger"] = 104 //JGER X : IP = IP+X, IF CMPR >= 0 + self.DecodeOpcode["jnlr"] = 104 //JNLR X : IP = IP+X, IF !(CMPR < 0) + self.DecodeOpcode["jlr"] = 105 //JLR X : IP = IP+X, IF CMPR < 0 + self.DecodeOpcode["jnger"] = 105 //JNGER X : IP = IP+X, IF !(CMPR >= 0) + self.DecodeOpcode["jler"] = 106 //JLER X : IP = IP+X, IF CMPR <= 0 + self.DecodeOpcode["jngr"] = 106 //JNGR X : IP = IP+X, IF !(CMPR > 0) + self.DecodeOpcode["jer"] = 107 //JER X : IP = IP+X, IF CMPR = 0 + self.DecodeOpcode["jzr"] = 107 //JZR X : IP = IP+X, IF CMPR = 0 + self.DecodeOpcode["lneg"] = 108 //LNEG X : X = LOGic NEGATE(X) 3.00 + //self.DecodeOpcode[""] = 109 //RESERVED + //----------------------------------------------------------------------------------- + self.DecodeOpcode["nmiret"] = 110 //NMIRET : NMIRESTORE; 2.00 + self.DecodeOpcode["idle"] = 111 //IDLE : FORCE_CPU_IDLE; 4.00 + self.DecodeOpcode["nop"] = 112 //NOP : 5.00 + //self.DecodeOpcode[""] = 113 //RESERVED + self.DecodeOpcode["pusha"] = 114 //PUSHA : Push all GP registers 8.00 + self.DecodeOpcode["popa"] = 115 //POPA : Pop all GP registers 8.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["cpuget"] = 120 //CPUGET X,Y : X = CPU[Y] 5.00 + self.DecodeOpcode["cpuset"] = 121 //CPUSET X,Y : CPU[X] = Y 5.00 + self.DecodeOpcode["spp"] = 122 //SPP X,Y : PAGE[X].Y = 1 5.00 [BLOCK] + self.DecodeOpcode["cpp"] = 123 //CPP X,Y : PAGE[X].Y = 0 5.00 [BLOCK] + self.DecodeOpcode["srl"] = 124 //SRL X,Y : PAGE[X].RunLevel = Y 5.00 [BLOCK] + self.DecodeOpcode["grl"] = 125 //GRL X,Y : X = PAGE[Y].RunLevel 5.00 + self.DecodeOpcode["lea"] = 126 //LEA X,Y : X = ADDRESS(Y) 5.00 + self.DecodeOpcode["block"] = 127 //BLOCK X,Y : SETUP_DATA_BLOCK([X..X+Y-1]) 6.00 + self.DecodeOpcode["cmpand"] = 128 //CMPAND X,Y : CMPR = CMPR AND (X - Y) 6.00 + self.DecodeOpcode["cmpor"] = 129 //CMPOR X,Y : CMPR = CMPR OR (X - Y) 6.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["mshift"] = 130 //MSHIFT X : SHIFT DATA (look in lua) 7.00 + self.DecodeOpcode["smap"] = 131 //SMAP X,Y : PAGE[X].MappedTo = Y 8.00 [BLOCK] + self.DecodeOpcode["gmap"] = 132 //GMAP X,Y : X = PAGE[Y].MappedTo 8.00 + self.DecodeOpcode["rstack"] = 133 //RSTACK X,Y : X = STACK[Y] 9.00 + self.DecodeOpcode["sstack"] = 134 //SSTACK X,Y : STACK[X] = Y 9.00 + //----------------------------------------------------------------------------------- + self.DecodeOpcode["breakpoint"] = 138 //BREAKPOINT (EmuFox only) + + self:InitializeAdvMathASMOpcodes() +end + +function ENT:InitializeASMOpcodes() + self.OpcodeCount = {} + self.OpcodeCount[0] = 0 + for i=1,300 do + if ((i >= 1) && (i <= 9)) then + self.OpcodeCount[i] = 1 + elseif (i >= 10) && (i <= 19) then + self.OpcodeCount[i] = 2 + elseif (i >= 20) && (i <= 29) then + self.OpcodeCount[i] = 1 + elseif (i >= 30) && (i <= 39) then + self.OpcodeCount[i] = 1 + elseif (i >= 40) && (i <= 49) then + self.OpcodeCount[i] = 0 + elseif (i >= 50) && (i <= 59) then + self.OpcodeCount[i] = 2 + elseif (i >= 60) && (i <= 69) then + self.OpcodeCount[i] = 2 + elseif (i >= 70) && (i <= 79) then + self.OpcodeCount[i] = 1 + elseif (i >= 80) && (i <= 89) then + self.OpcodeCount[i] = 2 + elseif (i >= 90) && (i <= 99) then + self.OpcodeCount[i] = 1 + elseif (i >= 100) && (i <= 109) then + self.OpcodeCount[i] = 1 + elseif (i >= 110) && (i <= 119) then + self.OpcodeCount[i] = 0 + elseif (i >= 120) && (i <= 129) then + self.OpcodeCount[i] = 2 + elseif (i >= 130) && (i <= 137) then + self.OpcodeCount[i] = 2 + elseif (i >= 138) && (i <= 139) then + self.OpcodeCount[i] = 0 + + //GPU OPCODES + elseif (i >= 200) && (i <= 209) then + self.OpcodeCount[i] = 0 + elseif (i >= 210) && (i <= 219) then + self.OpcodeCount[i] = 1 + elseif (i >= 220) && (i <= 229) then + self.OpcodeCount[i] = 2 + elseif (i >= 230) && (i <= 249) then + self.OpcodeCount[i] = 2 + elseif (i >= 240) && (i <= 249) then + self.OpcodeCount[i] = 2 + elseif (i >= 250) && (i <= 259) then + self.OpcodeCount[i] = 2 + elseif (i >= 260) && (i <= 269) then + self.OpcodeCount[i] = 2 + elseif (i >= 270) && (i <= 279) then + self.OpcodeCount[i] = 1 + elseif (i >= 280) && (i <= 289) then + self.OpcodeCount[i] = 1 + elseif (i >= 290) && (i <= 299) then + self.OpcodeCount[i] = 2 + end + end + self.OpcodeCount[280] = 1 //DDFRAME +end + +function ENT:InitializeOpcodeTable() + self.OpcodeTable = {} + + self.OpcodeTable[0] = function (Param1,Param2) //END + self:Interrupt(2,0) + end + self.OpcodeTable[1] = function (Param1,Param2) //JNE + if (self.CMPR ~= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[2] = function (Param1,Param2) //JMP + self.IP = Param1 + end + self.OpcodeTable[3] = function (Param1,Param2) //JG + if (self.CMPR > 0) then + self.IP = Param1 + end + end + self.OpcodeTable[4] = function (Param1,Param2) //JGE + if (self.CMPR >= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[5] = function (Param1,Param2) //JL + if (self.CMPR < 0) then + self.IP = Param1 + end + end + self.OpcodeTable[6] = function (Param1,Param2) //JLE + if (self.CMPR <= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[7] = function (Param1,Param2) //JE + if (self.CMPR == 0) then + self.IP = Param1 + end + end + //============================================================ + self.OpcodeTable[8] = function (Param1,Param2) //CPUID + if (Param1 == 0) then //CPU REVISION/VERSION + self.EAX = self:CPUID_Version() //= 8.00 OPC REV 2 + elseif (Param1 == 1) then //AMOUNT OF RAM + self.EAX = 65536 //= 64KB + elseif (Param1 == 2) then //TYPE (0 - ZCPU; 1 - ZGPU) + self.EAX = 0 //= ZCPU + end + end + //============================================================ + self.OpcodeTable[9] = function (Param1,Param2) //PUSH + self:Push(Param1) + end + //------------------------------------------------------------ + self.OpcodeTable[10] = function (Param1,Param2) //ADD + return Param1 + Param2 + end + self.OpcodeTable[11] = function (Param1,Param2) //SUB + return Param1 - Param2 + end + self.OpcodeTable[12] = function (Param1,Param2) //MUL + return Param1 * Param2 + end + self.OpcodeTable[13] = function (Param1,Param2) //DIV + if (math.abs(Param2) < 1e-12) then + self:Interrupt(3,0) + else + return Param1 / Param2 + end + end + self.OpcodeTable[14] = function (Param1,Param2) //MOV + return Param2 + end + self.OpcodeTable[15] = function (Param1,Param2) //CMP + self.CMPR = Param1 - Param2 + end + self.OpcodeTable[16] = function (Param1,Param2) //RD + if (Param2 < 0) then return 0 end + if (self.Memory[Param2]) then + return self.Memory[Param2] + else + return 0 + end + end + self.OpcodeTable[17] = function (Param1,Param2) //WD + if (Param1 < 0) then return end + self.Memory[Param1] = Param2 + end + self.OpcodeTable[18] = function (Param1,Param2) //MIN + if (Param2 < Param1) then + return Param2 + else + return Param1 + end + end + self.OpcodeTable[19] = function (Param1,Param2) //MAX + if (Param2 > Param1) then + return Param2 + else + return Param1 + end + end + //------------------------------------------------------------ + self.OpcodeTable[20] = function (Param1,Param2) //INC + return Param1 + 1 + end + self.OpcodeTable[21] = function (Param1,Param2) //DEC + return Param1 - 1 + end + self.OpcodeTable[22] = function (Param1,Param2) //NEG + return -Param1 + end + self.OpcodeTable[23] = function (Param1,Param2) //RAND + return math.random() + end + self.OpcodeTable[24] = function (Param1,Param2) //LOOP + self.ECX = self.ECX-1 + if (self.ECX ~= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[25] = function (Param1,Param2) //LOOPA + self.EAX = self.EAX-1 + if (self.EAX ~= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[26] = function (Param1,Param2) //LOOPB + self.EBX = self.EBX-1 + if (self.EBX ~= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[27] = function (Param1,Param2) //LOOPD + self.EDX = self.EDX-1 + if (self.EDX ~= 0) then + self.IP = Param1 + end + end + self.OpcodeTable[28] = function (Param1,Param2) //SPG + local page = math.floor(Param1 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 0 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + else + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + self.Page[page].Read = 1 + self.Page[page].Write = 0 + else + self:Interrupt(11,page) + end + end + end + self.OpcodeTable[29] = function (Param1,Param2) //CPG + local page = math.floor(Param1 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + else + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + self.Page[page].Read = 1 + self.Page[page].Write = 1 + else + self:Interrupt(11,page) + end + end + end + //------------------------------------------------------------ + self.OpcodeTable[30] = function (Param1,Param2) //POP + return self:Pop() + end + self.OpcodeTable[31] = function (Param1,Param2) //CALL + if self:Push(self.IP) then + self.IP = Param1 + end + end + self.OpcodeTable[32] = function (Param1,Param2) //BNOT + return self:BinaryNot(Param1) + end + self.OpcodeTable[33] = function (Param1,Param2) //FINT + return math.floor(Param1) + end + self.OpcodeTable[34] = function (Param1,Param2) //RND + return math.Round(Param1) + end + self.OpcodeTable[35] = function (Param1,Param2) //FLOOR + return Param1 - math.floor(Param1) + end + self.OpcodeTable[36] = function (Param1,Param2) //INV + if (math.abs(Param1) < 1e-12) then + self:Interrupt(3,1) + else + return 1 / Param1 + end + end + self.OpcodeTable[37] = function (Param1,Param2) //HALT + self.HaltPort = math.floor(Param1) + end + self.OpcodeTable[38] = function (Param1,Param2) //FSHL + return math.floor(Param1 * 2) + end + self.OpcodeTable[39] = function (Param1,Param2) //FSHR + return math.floor(Param1 / 2) + end + //------------------------------------------------------------ + self.OpcodeTable[40] = function (Param1,Param2) //RET + local newIP = self:Pop() + self.IP = newIP + end + self.OpcodeTable[41] = function (Param1,Param2) //IRET + if (self.EF == 1) then + local newCS = self:Pop() + self.CS = newCS + local newIP = self:Pop() + self.IP = newIP + else + self:Pop() //XEIP + local newIP = self:Pop() + self.IP = newIP + end + Wire_TriggerOutput(self.Entity, "Error", 0) + end + self.OpcodeTable[42] = function (Param1,Param2) //STI + self.NextIF = 1 + end + self.OpcodeTable[43] = function (Param1,Param2) //CLI + self.IF = 0 + end + self.OpcodeTable[44] = function (Param1,Param2) //STP + self.PF = 1 + end + self.OpcodeTable[45] = function (Param1,Param2) //CLP + self.PF = 0 + end + self.OpcodeTable[46] = function (Param1,Param2) //STD + self.Debug = true + end + self.OpcodeTable[47] = function (Param1,Param2) //RETF + local newIP = self:Pop() + local newCS = self:Pop() + + self.IP = newIP + if (newCS) then + self.CS = newCS + end + end + self.OpcodeTable[48] = function (Param1,Param2) //STEF + self.EF = 1 + end + self.OpcodeTable[49] = function (Param1,Param2) //CLEF + self.EF = 0 + end + //------------------------------------------------------------ + self.OpcodeTable[50] = function (Param1,Param2) //AND + if (Param1 > 0) && (Param2 > 0) then + return 1 + else + return 0 + end + end + self.OpcodeTable[51] = function (Param1,Param2) //OR + if (Param1 > 0) || (Param2 > 0) then + return 1 + else + return 0 + end + end + self.OpcodeTable[52] = function (Param1,Param2) //XOR + if ((Param1 > 0) && (Param2 <= 0)) || + ((Param1 <= 0) && (Param2 > 0)) then + return 1 + else + return 0 + end + end + self.OpcodeTable[53] = function (Param1,Param2) //FSIN + return math.sin(Param2) + end + self.OpcodeTable[54] = function (Param1,Param2) //FCOS + return math.cos(Param2) + end + self.OpcodeTable[55] = function (Param1,Param2) //FTAN + return math.tan(Param2) + end + self.OpcodeTable[56] = function (Param1,Param2) //FASIN + return math.asin(Param2) + end + self.OpcodeTable[57] = function (Param1,Param2) //FACOS + return math.acos(Param2) + end + self.OpcodeTable[58] = function (Param1,Param2) //FATAN + return math.atan(Param2) + end + self.OpcodeTable[59] = function (Param1,Param2) //MOD + return math.fmod(Param1,Param2) + end + //------------------------------------------------------------ + self.OpcodeTable[60] = function (Param1,Param2) //BIT + local bits = self:IntegerToBinary(Param1) + self.CMPR = bits[math.floor(math.Clamp(Param2,0,self.IPREC-1))] + end + self.OpcodeTable[61] = function (Param1,Param2) //SBIT + local bits = self:IntegerToBinary(math.floor(Param1)) + bits[math.floor(math.Clamp(Param2,0,self.IPREC-1))] = 1 + return self:BinaryToInteger(bits) + end + self.OpcodeTable[62] = function (Param1,Param2) //CBIT + local bits = self:IntegerToBinary(math.floor(Param1)) + bits[math.floor(math.Clamp(Param2,0,self.IPREC-1))] = 0 + return self:BinaryToInteger(bits) + end + self.OpcodeTable[63] = function (Param1,Param2) //TBIT + local bits = self:IntegerToBinary(math.floor(Param1)) + if (bits[math.floor(math.Clamp(Param2,0,self.IPREC-1))]) then + bits[math.floor(math.Clamp(Param2,0,self.IPREC-1))] = 1-bits[math.floor(math.Clamp(Param2,0,self.IPREC-1))] + end + return self:BinaryToInteger(bits) + end + self.OpcodeTable[64] = function(Param1,Param2) //BAND + return self:BinaryAnd(Param1,Param2) + end + self.OpcodeTable[65] = function(Param1,Param2) //BOR + return self:BinaryOr(Param1,Param2) + end + self.OpcodeTable[66] = function(Param1,Param2) //BXOR + return self:BinaryXor(Param1,Param2) + end + self.OpcodeTable[67] = function(Param1,Param2) //BSHL + return self:BinarySHL(Param1,Param2) + end + self.OpcodeTable[68] = function(Param1,Param2) //BSHR + return self:BinarySHR(Param1,Param2) + end + self.OpcodeTable[69] = function (Param1,Param2) //JMPF + self.CS = Param2 + self.IP = Param1 + end + //------------------------------------------------------------ + self.OpcodeTable[70] = function (Param1,Param2) //NMIINT + self:NMIInterrupt(math.floor(Param1)) + end + self.OpcodeTable[71] = function (Param1,Param2) //CNE + if (self.CMPR ~= 0) then + if self:Push(self.IP) then + self.IP = Param1 + end + end + end + self.OpcodeTable[72] = function (Param1,Param2) //CJMP + if self:Push(self.IP) then + self.IP = Param1 + end + end + self.OpcodeTable[73] = function (Param1,Param2) //CG + if (self.CMPR > 0) then + if self:Push(self.IP) then + self.IP = Param1 + end + end + end + self.OpcodeTable[74] = function (Param1,Param2) //CGE + if (self.CMPR >= 0) then + if self:Push(self.IP) then + self.IP = Param1 + end + end + end + self.OpcodeTable[75] = function (Param1,Param2) //CL + if (self.CMPR < 0) then + if self:Push(self.IP) then + self.IP = Param1 + end + end + end + self.OpcodeTable[76] = function (Param1,Param2) //CLE + if (self.CMPR <= 0) then + if self:Push(self.IP) then + self.IP = Param1 + end + end + end + self.OpcodeTable[77] = function (Param1,Param2) //CE + if (self.CMPR == 0) then + if self:Push(self.IP) then + self.IP = Param1 + end + end + end + self.OpcodeTable[78] = function (Param1,Param2) //MCOPY + if (Param1 == 0) then return end + for i = 1,math.Clamp(Param1,0,8192) do + local val + if (self.PrecompileData[self.XEIP]) then + val = self:ReadCell(self.ESI) + if (val == nil) then return end + if (self:WriteCell(self.EDI,val) == false) then return end + else + return + end + self.EDI = self.EDI + 1 + self.ESI = self.ESI + 1 + end + end + self.OpcodeTable[79] = function (Param1,Param2) //MXCHG + if (Param1 == 0) then return end + for i = 1,math.Clamp(Param1,0,8192) do + local val + if (self.PrecompileData[self.XEIP]) then + val1 = self:ReadCell(self.ESI) + val2 = self:ReadCell(self.EDI) + if (val1 == nil) || (val2 == nil) then return end + if (self:WriteCell(self.EDI,val1) == false) || (self:WriteCell(self.ESI,val2) == false) then return end + else + return + end + self.EDI = self.EDI + 1 + self.ESI = self.ESI + 1 + end + end + //------------------------------------------------------------ + self.OpcodeTable[80] = function (Param1,Param2) //FPWR + return Param1^Param2 + end + self.OpcodeTable[81] = function (Param1,Param2) //XCHG + self.PrecompileData[self.XEIP].WriteBack2(Param1) + return Param2 + end + self.OpcodeTable[82] = function (Param1,Param2) //FLOG + return math.log(Param2) + end + self.OpcodeTable[83] = function (Param1,Param2) //FLOG10 + return math.log10(Param2) + end + self.OpcodeTable[84] = function (Param1,Param2) //IN + return self:ReadPort(Param2) + end + self.OpcodeTable[85] = function (Param1,Param2) //OUT + self:WritePort(Param1,Param2) + end + self.OpcodeTable[86] = function (Param1,Param2) //FABS + return math.abs(Param2) + end + self.OpcodeTable[87] = function (Param1,Param2) //FSGN + if (Param2 > 0) then + return 1 + elseif (Param2 < 0) then + return -1 + else + return 0 + end + end + self.OpcodeTable[88] = function (Param1,Param2) //FEXP + return math.exp(Param2) + end + self.OpcodeTable[89] = function (Param1,Param2) //CALLF + if self:Push(self.CS) && self:Push(self.IP) then + self.IP = Param1 + self.CS = Param2 + end + end + //------------------------------------------------------------ + self.OpcodeTable[90] = function (Param1,Param2) //FPI + return 3.141592653589793 + end + self.OpcodeTable[91] = function (Param1,Param2) //FE + return 2.718281828459045 + end + self.OpcodeTable[92] = function (Param1,Param2) //INT + self:Interrupt(Param1,0) + end + self.OpcodeTable[93] = function (Param1,Param2) //TPG + local tadd = Param1*128 + local oldint = self.IF + self.IF = 0 + self.CMPR = 0 + while (tadd < Param1*128+128) do + local val = self:ReadCell(tadd) + if (val == nil) then + self.CMPR = tadd + tadd = Param1*128+128 + end + tadd = tadd + 1 + end + self.IF = oldint + end + self.OpcodeTable[94] = function (Param1,Param2) //FCEIL + return math.ceil(Param1) + end + self.OpcodeTable[95] = function (Param1,Param2) //ERPG + if (Param1 >= 0) && (Param1 < 512) then + local tadd = Param1*128 + while (tadd < Param1*128+128) do + self.ROMMemory[tadd] = 0 + tadd = tadd + 1 + end + else + self:Interrupt(12,0) + end + end + self.OpcodeTable[96] = function (Param1,Param2) //WRPG + if (Param1 >= 0) && (Param1 < 512) then + local tadd = Param1*128 + while (tadd < Param1*128+128) do + self.ROMMemory[tadd] = self.Memory[tadd] + tadd = tadd + 1 + end + else + self:Interrupt(12,0) + end + end + self.OpcodeTable[97] = function (Param1,Param2) //RDPG + if (Param1 >= 0) && (Param1 < 512) then + local tadd = Param1*128 + while (tadd < Param1*128+128) do + self.Memory[tadd] = self.ROMMemory[tadd] + tadd = tadd + 1 + end + else + self:Interrupt(12,0) + end + end + self.OpcodeTable[98] = function (Param1,Param2) //TIMER + return self.TIMER + end + self.OpcodeTable[99] = function (Param1,Param2) //LIDTR + self.IDTR = Param1 + end + self.OpcodeTable[100] = function (Param1,Param2) //STATESTORE + self:WriteCell(Param1 + 00,self.IP) + + self:WriteCell(Param1 + 01,self.EAX) + self:WriteCell(Param1 + 02,self.EBX) + self:WriteCell(Param1 + 03,self.ECX) + self:WriteCell(Param1 + 04,self.EDX) + + self:WriteCell(Param1 + 05,self.ESI) + self:WriteCell(Param1 + 06,self.EDI) + self:WriteCell(Param1 + 07,self.ESP) + self:WriteCell(Param1 + 08,self.EBP) + + self:WriteCell(Param1 + 09,self.CS) + self:WriteCell(Param1 + 10,self.SS) + self:WriteCell(Param1 + 11,self.DS) + self:WriteCell(Param1 + 12,self.ES) + self:WriteCell(Param1 + 13,self.GS) + self:WriteCell(Param1 + 14,self.FS) + + self:WriteCell(Param1 + 15,self.CMPR) + end + //------------------------------------------------------------ + self.OpcodeTable[101] = function (Param1,Param2) //JNER + if (self.CMPR ~= 0) then + self.IP = self.IP + Param1 + end + end + self.OpcodeTable[102] = function (Param1,Param2) //JMPR + self.IP = self.IP + Param1 + end + self.OpcodeTable[103] = function (Param1,Param2) //JGR + if (self.CMPR > 0) then + self.IP = self.IP + Param1 + end + end + self.OpcodeTable[104] = function (Param1,Param2) //JGER + if (self.CMPR >= 0) then + self.IP = self.IP + Param1 + end + end + self.OpcodeTable[105] = function (Param1,Param2) //JLR + if (self.CMPR < 0) then + self.IP = self.IP + Param1 + end + end + self.OpcodeTable[106] = function (Param1,Param2) //JLER + if (self.CMPR <= 0) then + self.IP = self.IP + Param1 + end + end + self.OpcodeTable[107] = function (Param1,Param2) //JER + if (self.CMPR == 0) then + self.IP = self.IP + Param1 + end + end + self.OpcodeTable[108] = function (Param1,Param2) //LNEG + return 1-math.Clamp(Param1,0,1) + end + self.OpcodeTable[109] = function (Param1,Param2) //STATERESTORE + //self.IP = self:ReadCell(Param1 + 00) + self:ReadCell(Param1 + 00) + + self.EAX = self:ReadCell(Param1 + 01) + self.EBX = self:ReadCell(Param1 + 02) + self.ECX = self:ReadCell(Param1 + 03) + self.EDX = self:ReadCell(Param1 + 04) + + self.ESI = self:ReadCell(Param1 + 05) + self.EDI = self:ReadCell(Param1 + 06) + self.ESP = self:ReadCell(Param1 + 07) + self.EBP = self:ReadCell(Param1 + 08) + + self.CS = self:ReadCell(Param1 + 09) + self.SS = self:ReadCell(Param1 + 10) + self.DS = self:ReadCell(Param1 + 11) + self.ES = self:ReadCell(Param1 + 12) + self.GS = self:ReadCell(Param1 + 13) + self.FS = self:ReadCell(Param1 + 14) + + self.CMPR = self:ReadCell(Param1 + 15) + end + //------------------------------------------------------------ + self.OpcodeTable[110] = function (Param1,Param2) //NMIRET + local newval + + //Interrupt data: + newval = self:Pop() //Interrupt return CS + newval = self:Pop() //Interrupt return EIP + + newval = self:Pop() if (newval) then self.IP = newval else return end + newval = self:Pop() if (newval) then self.CMPR = newval else return end + + newval = self:Pop() if (newval) then self.EAX = newval else return end + newval = self:Pop() if (newval) then self.EBX = newval else return end + newval = self:Pop() if (newval) then self.ECX = newval else return end + newval = self:Pop() if (newval) then self.EDX = newval else return end + newval = self:Pop() if (newval) then self.EBP = newval else return end + newval = self:Pop() if (newval) then else return end //ESP - not now + newval = self:Pop() if (newval) then self.ESI = newval else return end + newval = self:Pop() if (newval) then self.EDI = newval else return end + + newval = self:Pop() if (newval) then self.CS = newval else return end + newval = self:Pop() if (newval) then else return end //SS - not now + newval = self:Pop() if (newval) then self.DS = newval else return end + newval = self:Pop() if (newval) then self.FS = newval else return end + newval = self:Pop() if (newval) then self.GS = newval else return end + newval = self:Pop() if (newval) then self.ES = newval else return end + newval = self:Pop() if (newval) then self.KS = newval else return end + newval = self:Pop() if (newval) then self.LS = newval else return end + end + self.OpcodeTable[111] = function (Param1,Param2) //IDLE + self.Idle = 1 + end + self.OpcodeTable[112] = function (Param1,Param2) //NOP + end + self.OpcodeTable[113] = function (Param1,Param2) //RLADD + self.EAX = self.LADD + end + self.OpcodeTable[114] = function (Param1,Param2) //PUSHA + self:Push(self.EDI) + self:Push(self.ESI) + self:Push(self.EBP) + self:Push(self.ESP) + + self:Push(self.EDX) + self:Push(self.ECX) + self:Push(self.EBX) + self:Push(self.EAX) + end + self.OpcodeTable[115] = function (Param1,Param2) //POPA + local newval + + newval = self:Pop() if (newval) then self.EAX = newval else return end + newval = self:Pop() if (newval) then self.EBX = newval else return end + newval = self:Pop() if (newval) then self.ECX = newval else return end + newval = self:Pop() if (newval) then self.EDX = newval else return end + newval = self:Pop() if (newval) then self.EBP = newval else return end + newval = self:Pop() if (newval) then else return end //ESP - not now + newval = self:Pop() if (newval) then self.ESI = newval else return end + newval = self:Pop() if (newval) then self.EDI = newval else return end + end + //------------------------------------------------------------ + self.OpcodeTable[120] = function (Param1,Param2) //CPUGET + if (self.CPUVariable[Param2]) then + return self[self.CPUVariable[Param2]] + else + return 0 + end + end + self.OpcodeTable[121] = function (Param1,Param2) //CPUSET + if (self.CPUVariable[Param1]) then + if (not self.CPUVariableReadonly[Param1]) then + self[self.CPUVariable[Param1]] = Param2 + end + end + end + self.OpcodeTable[122] = function (Param1,Param2) //SPP + if (self.BlockSize > 0) then + local addr = self.BlockStart + self.BlockSize = math.Clamp(self.BlockSize,0,8192) + while (addr < self.BlockStart + self.BlockSize) do + local page = math.floor(addr / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + if (Param2 == 0) then + self.Page[page].Read = 1 + elseif (Param2 == 1) then + self.Page[page].Write = 1 + elseif (Param2 == 2) then + self.Page[page].Execute = 1 + elseif (Param2 == 3) then + self.Page[page].RunLevel = 1 + end + else + self:Interrupt(11,page) + return + end + addr = addr + 128 + end + self.BlockSize = 0 + else + local page = math.floor(Param1 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + if (Param2 == 0) then + self.Page[page].Read = 1 + elseif (Param2 == 1) then + self.Page[page].Write = 1 + elseif (Param2 == 2) then + self.Page[page].Execute = 1 + elseif (Param2 == 3) then + self.Page[page].RunLevel = 1 + end + else + self:Interrupt(11,page) + end + end + end + self.OpcodeTable[123] = function (Param1,Param2) //CPP + if (self.BlockSize > 0) then + local addr = self.BlockStart + self.BlockSize = math.Clamp(self.BlockSize,0,8192) + while (addr < self.BlockStart + self.BlockSize) do + local page = math.floor(addr / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + if (Param2 == 0) then + self.Page[page].Read = 0 + elseif (Param2 == 1) then + self.Page[page].Write = 0 + elseif (Param2 == 2) then + self.Page[page].Execute = 0 + elseif (Param2 == 3) then + self.Page[page].RunLevel = 0 + end + else + self:Interrupt(11,page) + return + end + addr = addr + 128 + end + self.BlockSize = 0 + else + local page = math.floor(Param1 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + if (Param2 == 0) then + self.Page[page].Read = 0 + elseif (Param2 == 1) then + self.Page[page].Write = 0 + elseif (Param2 == 2) then + self.Page[page].Execute = 0 + elseif (Param2 == 3) then + self.Page[page].RunLevel = 0 + end + else + self:Interrupt(11,page) + end + end + end + self.OpcodeTable[124] = function (Param1,Param2) //SRL + if (self.BlockSize > 0) then + local addr = self.BlockStart + self.BlockSize = math.Clamp(self.BlockSize,0,8192) + while (addr < self.BlockStart + self.BlockSize) do + local page = math.floor(addr / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + self.Page[page].RunLevel = Param2 + else + self:Interrupt(11,page) + return + end + addr = addr + 128 + end + self.BlockSize = 0 + else + local page = math.floor(Param1 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + self.Page[page].RunLevel = Param2 + else + self:Interrupt(11,page) + end + end + end + self.OpcodeTable[125] = function (Param1,Param2) //GRL + local page = math.floor(Param2 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + return self.Page[page].RunLevel + + end + self.OpcodeTable[126] = function (Param1,Param2) //LEA + if (self.PrecompileData[self.XEIP].EffectiveAddress2) then + return self.PrecompileData[self.XEIP]:EffectiveAddress2() + else + return Param2 + end + end + self.OpcodeTable[127] = function (Param1,Param2) //BLOCK + self.BlockStart = Param1 + self.BlockSize = Param2 + end + self.OpcodeTable[128] = function (Param1,Param2) //CMPAND + if (self.CMPR ~= 0) then + self.CMPR = Param1 - Param2 + end + end + self.OpcodeTable[129] = function (Param1,Param2) //CMPOR + if (self.CMPR == 0) then + self.CMPR = Param1 - Param2 + end + end + //------------------------------------------------------------ + self.OpcodeTable[130] = function (Param1,Param2) //MSHIFT + if (Param1 == 0) then return end + + local Buffer = {} + local Count = math.Clamp(Param1,0,8192)-1 + + if (Param2 > 0) then + for i = 0,Count-Param2 do //Shifted part + Buffer[i] = self:ReadCell(self.ESI+i+Param2) + end + for i = Count-Param2+1,Count do //Remaining part + Buffer[i] = self:ReadCell(self.ESI+i-(Count-Param2+1)) + end + elseif (Param2 < 0) then + for i = Param2,Count do //Shifted part + Buffer[i] = self:ReadCell(self.ESI+i-Param2) + end + for i = 0,Param2-1 do //Remaining part + Buffer[i] = self:ReadCell(self.ESI+i+Count-Param2) + end + end + + for i = 0,Count-1 do + self:WriteCell(self.ESI+i,Buffer[i]) + end + self.ESI = self.ESI + math.Clamp(Param1,0,8192) + end + self.OpcodeTable[131] = function (Param1,Param2) //SMAP + if (self.BlockSize > 0) then + local addr = self.BlockStart + self.BlockSize = math.Clamp(self.BlockSize,0,8192) + while (addr < self.BlockStart + self.BlockSize) do + local page = math.floor(addr / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + self.Page[page].MappedTo = Param2 + //Invalidate precompile data + for tmpaddr=page*128,page*128+127 do + self.PrecompileMemory[tmpaddr] = nil + self.PrecompileData[tmpaddr] = nil + end + for tmpaddr=Param2*128,Param2*128+127 do + self.PrecompileMemory[tmpaddr] = nil + self.PrecompileData[tmpaddr] = nil + end + else + self:Interrupt(11,page) + return + end + addr = addr + 128 + Param2 = Param2 + 1 + end + self.BlockSize = 0 + else + local page = math.floor(Param1 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + if (self.CurrentPage.RunLevel <= self.Page[page].RunLevel) then + self.Page[page].MappedTo = Param2 + //Invalidate precompile data + for tmpaddr=page*128,page*128+127 do + self.PrecompileMemory[tmpaddr] = nil + self.PrecompileData[tmpaddr] = nil + end + for tmpaddr=Param2*128,Param2*128+127 do + self.PrecompileMemory[tmpaddr] = nil + self.PrecompileData[tmpaddr] = nil + end + else + self:Interrupt(11,page) + end + end + end + self.OpcodeTable[132] = function (Param1,Param2) //GMAP + local page = math.floor(Param2 / 128) + if (not self.Page[page]) then + self.Page[page] = {} + self.Page[page].Read = 1 + self.Page[page].Write = 1 + self.Page[page].Execute = 1 + self.Page[page].RunLevel = self.CurrentPage.RunLevel + end + + return self.Page[page].MappedTo + + end + self.OpcodeTable[133] = function (Param1,Param2) //RSTACK + local val = self:ReadCell(self.SS+Param2) + return val or 0 + end + self.OpcodeTable[134] = function (Param1,Param2) //SSTACK + self:WriteCell(self.SS+Param1,Param2) + end + self.OpcodeTable[138] = function (Param1,Param2) //BREAKPOINT + if (EmuFox) then + print("CPU BREAKPOINT AT "..self.XEIP) + self.Clk = 0 + end + end + self.OpcodeTable[139] = function (Param1,Param2) //CLD + self.Debug = false + end + //------------------------------------------------------------ + + self:InitializeAdvMathOpcodeTable() +end + +function ENT:InitializeOpcodeRunlevels() + self.OpcodeRunLevel = {} + + //Priviliegied opcodes: + self.OpcodeRunLevel[16] = 0 //RD + self.OpcodeRunLevel[17] = 0 //WD + self.OpcodeRunLevel[42] = 0 //STI + self.OpcodeRunLevel[43] = 0 //CLI + self.OpcodeRunLevel[44] = 0 //STP + self.OpcodeRunLevel[45] = 0 //CLP + self.OpcodeRunLevel[48] = 0 //STE + self.OpcodeRunLevel[49] = 0 //CLE + self.OpcodeRunLevel[70] = 0 //NMIINT + self.OpcodeRunLevel[95] = 0 //ERPG + self.OpcodeRunLevel[96] = 0 //WRPG + self.OpcodeRunLevel[97] = 0 //RRPG + self.OpcodeRunLevel[99] = 0 //LIDTR + self.OpcodeRunLevel[110] = 0 //NMIRET + self.OpcodeRunLevel[111] = 0 //IDLE + self.OpcodeRunLevel[121] = 0 //CPUSET + self.OpcodeRunLevel[122] = 0 //CPP + self.OpcodeRunLevel[123] = 0 //SPP + self.OpcodeRunLevel[124] = 0 //SRL + self.OpcodeRunLevel[131] = 0 //SMAP +end diff --git a/lua/entities/gmod_wire_cpu/cpu_vm.lua b/lua/entities/gmod_wire_cpu/cpu_vm.lua new file mode 100644 index 0000000000..20f12da908 --- /dev/null +++ b/lua/entities/gmod_wire_cpu/cpu_vm.lua @@ -0,0 +1,750 @@ +function ENT:Reset() + self.IP = 0 //Instruction pointer + + self.Page = {} + + self.EAX = 0 //General purpose registers + self.EBX = 0 + self.ECX = 0 + self.EDX = 0 + self.ESI = 0 + self.EDI = 0 + self.ESP = 65535 + self.EBP = 0 + + self.CS = 0 //Segment pointer registers + self.SS = 0 + self.DS = 0 + self.ES = 0 + self.GS = 0 + self.FS = 0 + self.KS = 0 + self.LS = 0 + + self.ESZ = 65535 //Stack size register + + self.IDTR = 0 //Interrupt descriptor table register + self.NIDT = 256 //Size of interrupt descriptor table + self.EF = 0 //Enhanced mode flag + self.PF = 0 //Protected mode flag + self.IF = 1 //Interrupts enabled flag + self.NextIF = nil + + self.CMPR = 0 //Compare register + self.XEIP = 0 //Current instruction address register + self.LADD = 0 //Last interrupt parameter + self.LINT = 0 //Last interrupt number + self.TMR = 0 //Internal timer + self.TIMER = 0 //Internal clock + self.CPAGE = 0 //Current page ID + + self.BPREC = 48 //Binary precision for integer emulation mode + self.IPREC = 48 //Integer precision + self.VMODE = 2 //Vector mode (2D) + + self.CODEBYTES = 0 //Executed size of code + + self.INTR = 0 + self.BusLock = 0 + self.Idle = 0 + + self.BlockStart = 0 + self.BlockSize = 0 + self.XTRL = 1 //Runlevel for external IO + + self.CurrentPage = nil + + self.Clk = self.InputClk + + if (not self.IsGPU) then + if (self.UseROM == true) then + for i = 0, 65535 do + if (self.ROMMemory[i]) then + self:WriteCell(i,self.ROMMemory[i]) + end + end + end + end + + self.HaltPort = -1 + + if (self.Debug) then self:DebugMessage("CPU RESET") end + if (not self.IsGPU) then Wire_TriggerOutput(self.Entity, "Error", 0.0) end +end + +function ENT:InitializeCPUVariableSet() + self.CPUVariable = {} + self.CPUVariableReadonly = {} + + self.CPUVariable[0 ] = "IP" + + self.CPUVariable[1 ] = "EAX" + self.CPUVariable[2 ] = "EBX" + self.CPUVariable[3 ] = "ECX" + self.CPUVariable[4 ] = "EDX" + self.CPUVariable[5 ] = "ESI" + self.CPUVariable[6 ] = "EDI" + self.CPUVariable[7 ] = "ESP" + self.CPUVariable[8 ] = "EBP" + + self.CPUVariable[9 ] = "ESZ" + + self.CPUVariable[16] = "CS" + self.CPUVariable[17] = "SS" + self.CPUVariable[18] = "DS" + self.CPUVariable[19] = "ES" + self.CPUVariable[20] = "GS" + self.CPUVariable[21] = "FS" + self.CPUVariable[22] = "KS" + self.CPUVariable[23] = "LS" + + self.CPUVariable[24] = "IDTR" + self.CPUVariable[25] = "CMPR" + self.CPUVariable[26] = "XEIP" self.CPUVariableReadonly[26] = true + self.CPUVariable[27] = "LADD" + self.CPUVariable[28] = "LINT" + self.CPUVariable[29] = "TMR" + self.CPUVariable[30] = "TIMER" + self.CPUVariable[31] = "CPAGE" self.CPUVariableReadonly[31] = true + + self.CPUVariable[32] = "IF" + self.CPUVariable[33] = "PF" + self.CPUVariable[34] = "EF" + + self.CPUVariable[45] = "BusLock" + self.CPUVariable[46] = "Idle" + self.CPUVariable[47] = "INTR" + + self.CPUVariable[48] = "SerialNo" self.CPUVariableReadonly[48] = true + self.CPUVariable[49] = "CODEBYTES" self.CPUVariableReadonly[49] = true + self.CPUVariable[50] = "BPREC" + self.CPUVariable[51] = "IPREC" + self.CPUVariable[52] = "NIDT" + self.CPUVariable[53] = "BlockStart" + self.CPUVariable[54] = "BlockSize" + self.CPUVariable[55] = "VMODE" + self.CPUVariable[56] = "XTRL" + self.CPUVariable[57] = "HaltPort" +end + +function ENT:InitializeLookupTables() + self.SegmentType = {} + self.SegmentType[-2 ] = "CS" + self.SegmentType[-3 ] = "SS" + self.SegmentType[-4 ] = "DS" + self.SegmentType[-5 ] = "ES" + self.SegmentType[-6 ] = "GS" + self.SegmentType[-7 ] = "FS" + self.SegmentType[-8 ] = "KS" + self.SegmentType[-9 ] = "LS" + self.SegmentType[-10] = "EAX" + self.SegmentType[-11] = "EBX" + self.SegmentType[-12] = "ECX" + self.SegmentType[-13] = "EDX" + self.SegmentType[-14] = "ESI" + self.SegmentType[-15] = "EDI" + self.SegmentType[-16] = "ESP" + self.SegmentType[-17] = "EBP" + + self.ParamFunctions_1 = {} + self.ParamFunctions_1[0] = function() return self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[1] = function() return self.EAX end + self.ParamFunctions_1[2] = function() return self.EBX end + self.ParamFunctions_1[3] = function() return self.ECX end + self.ParamFunctions_1[4] = function() return self.EDX end + self.ParamFunctions_1[5] = function() return self.ESI end + self.ParamFunctions_1[6] = function() return self.EDI end + self.ParamFunctions_1[7] = function() return self.ESP end + self.ParamFunctions_1[8] = function() return self.EBP end + + self.ParamFunctions_1[9] = function() return self.CS end + self.ParamFunctions_1[10] = function() return self.SS end + self.ParamFunctions_1[11] = function() return self.DS end + self.ParamFunctions_1[12] = function() return self.ES end + self.ParamFunctions_1[13] = function() return self.GS end + self.ParamFunctions_1[14] = function() return self.FS end + self.ParamFunctions_1[15] = function() return self.KS end + self.ParamFunctions_1[16] = function() return self.LS end + + self.ParamFunctions_1[17] = function() return self:ReadCell(self.EAX + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[18] = function() return self:ReadCell(self.EBX + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[19] = function() return self:ReadCell(self.ECX + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[20] = function() return self:ReadCell(self.EDX + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[21] = function() return self:ReadCell(self.ESI + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[22] = function() return self:ReadCell(self.EDI + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[23] = function() return self:ReadCell(self.ESP + self[self.PrecompileData[self.XEIP].Segment1]) end + self.ParamFunctions_1[24] = function() return self:ReadCell(self.EBP + self[self.PrecompileData[self.XEIP].Segment1]) end + + self.ParamFunctions_1[25] = function() return self:ReadCell(self.PrecompileData[self.XEIP].PeekByte1 + self[self.PrecompileData[self.XEIP].Segment1]) end + + self.ParamFunctions_1[26] = function() return self.EAX + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[27] = function() return self.EBX + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[28] = function() return self.ECX + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[29] = function() return self.EDX + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[30] = function() return self.ESI + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[31] = function() return self.EDI + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[32] = function() return self.ESP + self[self.PrecompileData[self.XEIP].Segment1] end + self.ParamFunctions_1[33] = function() return self.EBP + self[self.PrecompileData[self.XEIP].Segment1] end + + self.ParamFunctions_1[34] = function() return self:ReadCell(self.EAX + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[35] = function() return self:ReadCell(self.EBX + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[36] = function() return self:ReadCell(self.ECX + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[37] = function() return self:ReadCell(self.EDX + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[38] = function() return self:ReadCell(self.ESI + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[39] = function() return self:ReadCell(self.EDI + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[40] = function() return self:ReadCell(self.ESP + self.PrecompileData[self.XEIP].PeekByte1) end + self.ParamFunctions_1[41] = function() return self:ReadCell(self.EBP + self.PrecompileData[self.XEIP].PeekByte1) end + + self.ParamFunctions_1[42] = function() return self.EAX + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[43] = function() return self.EBX + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[44] = function() return self.ECX + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[45] = function() return self.EDX + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[46] = function() return self.ESI + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[47] = function() return self.EDI + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[48] = function() return self.ESP + self.PrecompileData[self.XEIP].PeekByte1 end + self.ParamFunctions_1[49] = function() return self.EBP + self.PrecompileData[self.XEIP].PeekByte1 end + for i=1000,2024 do + self.ParamFunctions_1[i] = function() return self:ReadPort(self.PrecompileData[self.XEIP].dRM1-1000) end + end + + self.NeedPeekByte = {} + self.NeedPeekByte[0] = true + self.NeedPeekByte[25] = true + self.NeedPeekByte[34] = true + self.NeedPeekByte[35] = true + self.NeedPeekByte[36] = true + self.NeedPeekByte[37] = true + self.NeedPeekByte[38] = true + self.NeedPeekByte[39] = true + self.NeedPeekByte[40] = true + self.NeedPeekByte[41] = true + self.NeedPeekByte[42] = true + self.NeedPeekByte[43] = true + self.NeedPeekByte[44] = true + self.NeedPeekByte[45] = true + self.NeedPeekByte[46] = true + self.NeedPeekByte[47] = true + self.NeedPeekByte[48] = true + self.NeedPeekByte[49] = true + + self.ParamFunctions_2 = {} + self.ParamFunctions_2[0] = function() return self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[1] = function() return self.EAX end + self.ParamFunctions_2[2] = function() return self.EBX end + self.ParamFunctions_2[3] = function() return self.ECX end + self.ParamFunctions_2[4] = function() return self.EDX end + self.ParamFunctions_2[5] = function() return self.ESI end + self.ParamFunctions_2[6] = function() return self.EDI end + self.ParamFunctions_2[7] = function() return self.ESP end + self.ParamFunctions_2[8] = function() return self.EBP end + self.ParamFunctions_2[9] = function() return self.CS end + self.ParamFunctions_2[10] = function() return self.SS end + self.ParamFunctions_2[11] = function() return self.DS end + self.ParamFunctions_2[12] = function() return self.ES end + self.ParamFunctions_2[13] = function() return self.GS end + self.ParamFunctions_2[14] = function() return self.FS end + self.ParamFunctions_2[15] = function() return self.KS end + self.ParamFunctions_2[16] = function() return self.LS end + + self.ParamFunctions_2[17] = function() return self:ReadCell(self.EAX + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[18] = function() return self:ReadCell(self.EBX + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[19] = function() return self:ReadCell(self.ECX + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[20] = function() return self:ReadCell(self.EDX + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[21] = function() return self:ReadCell(self.ESI + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[22] = function() return self:ReadCell(self.EDI + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[23] = function() return self:ReadCell(self.ESP + self[self.PrecompileData[self.XEIP].Segment2]) end + self.ParamFunctions_2[24] = function() return self:ReadCell(self.EBP + self[self.PrecompileData[self.XEIP].Segment2]) end + + self.ParamFunctions_2[25] = function() return self:ReadCell(self.PrecompileData[self.XEIP].PeekByte2 + self[self.PrecompileData[self.XEIP].Segment2]) end + + self.ParamFunctions_2[26] = function() return self.EAX + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[27] = function() return self.EBX + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[28] = function() return self.ECX + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[29] = function() return self.EDX + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[30] = function() return self.ESI + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[31] = function() return self.EDI + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[32] = function() return self.ESP + self[self.PrecompileData[self.XEIP].Segment2] end + self.ParamFunctions_2[33] = function() return self.EBP + self[self.PrecompileData[self.XEIP].Segment2] end + + self.ParamFunctions_2[34] = function() return self:ReadCell(self.EAX + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[35] = function() return self:ReadCell(self.EBX + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[36] = function() return self:ReadCell(self.ECX + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[37] = function() return self:ReadCell(self.EDX + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[38] = function() return self:ReadCell(self.ESI + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[39] = function() return self:ReadCell(self.EDI + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[40] = function() return self:ReadCell(self.ESP + self.PrecompileData[self.XEIP].PeekByte2) end + self.ParamFunctions_2[41] = function() return self:ReadCell(self.EBP + self.PrecompileData[self.XEIP].PeekByte2) end + + self.ParamFunctions_2[42] = function() return self.EAX + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[43] = function() return self.EBX + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[44] = function() return self.ECX + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[45] = function() return self.EDX + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[46] = function() return self.ESI + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[47] = function() return self.EDI + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[48] = function() return self.ESP + self.PrecompileData[self.XEIP].PeekByte2 end + self.ParamFunctions_2[49] = function() return self.EBP + self.PrecompileData[self.XEIP].PeekByte2 end + + for i=1000,2024 do + self.ParamFunctions_2[i] = function() return self:ReadPort(self.PrecompileData[self.XEIP].dRM2-1000) end + end + + self.WriteBackFunctions = {} + self.WriteBackFunctions[0] = function(Result) end + self.WriteBackFunctions[1] = function(Result) self.EAX = Result end + self.WriteBackFunctions[2] = function(Result) self.EBX = Result end + self.WriteBackFunctions[3] = function(Result) self.ECX = Result end + self.WriteBackFunctions[4] = function(Result) self.EDX = Result end + self.WriteBackFunctions[5] = function(Result) self.ESI = Result end + self.WriteBackFunctions[6] = function(Result) self.EDI = Result end + self.WriteBackFunctions[7] = function(Result) self.ESP = Result end + self.WriteBackFunctions[8] = function(Result) self.EBP = Result end + self.WriteBackFunctions[9] = function(Result) self:Interrupt(13,1) end + self.WriteBackFunctions[10] = function(Result) self.SS = Result end + self.WriteBackFunctions[11] = function(Result) self.DS = Result end + self.WriteBackFunctions[12] = function(Result) self.ES = Result end + self.WriteBackFunctions[13] = function(Result) self.GS = Result end + self.WriteBackFunctions[14] = function(Result) self.FS = Result end + self.WriteBackFunctions[15] = function(Result) self.KS = Result end + self.WriteBackFunctions[16] = function(Result) self.LS = Result end + self.WriteBackFunctions[17] = function(Result) self:WriteCell(self.EAX + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[18] = function(Result) self:WriteCell(self.EBX + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[19] = function(Result) self:WriteCell(self.ECX + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[20] = function(Result) self:WriteCell(self.EDX + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[21] = function(Result) self:WriteCell(self.ESI + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[22] = function(Result) self:WriteCell(self.EDI + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[23] = function(Result) self:WriteCell(self.ESP + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[24] = function(Result) self:WriteCell(self.EBP + self[self.PrecompileData[self.XEIP].Segment1],Result) end + self.WriteBackFunctions[25] = function(Result) self:WriteCell(self.PrecompileData[self.XEIP].PeekByte1 + self[self.PrecompileData[self.XEIP].Segment1],Result) end + + for i=1000,2024 do + self.WriteBackFunctions[i] = function(Result) self:WritePort(self.PrecompileData[self.XEIP].dRM1-1000,Result) end + end + + self.WriteBackFunctions2 = {} + self.WriteBackFunctions2[0] = function(Result) end + self.WriteBackFunctions2[1] = function(Result) self.EAX = Result end + self.WriteBackFunctions2[2] = function(Result) self.EBX = Result end + self.WriteBackFunctions2[3] = function(Result) self.ECX = Result end + self.WriteBackFunctions2[4] = function(Result) self.EDX = Result end + self.WriteBackFunctions2[5] = function(Result) self.ESI = Result end + self.WriteBackFunctions2[6] = function(Result) self.EDI = Result end + self.WriteBackFunctions2[7] = function(Result) self.ESP = Result end + self.WriteBackFunctions2[8] = function(Result) self.EBP = Result end + self.WriteBackFunctions2[9] = function(Result) self:Interrupt(13,1) end + self.WriteBackFunctions2[10] = function(Result) self.SS = Result end + self.WriteBackFunctions2[11] = function(Result) self.DS = Result end + self.WriteBackFunctions2[12] = function(Result) self.ES = Result end + self.WriteBackFunctions2[13] = function(Result) self.GS = Result end + self.WriteBackFunctions2[14] = function(Result) self.FS = Result end + self.WriteBackFunctions2[15] = function(Result) self.KS = Result end + self.WriteBackFunctions2[16] = function(Result) self.LS = Result end + self.WriteBackFunctions2[17] = function(Result) self:WriteCell(self.EAX + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[18] = function(Result) self:WriteCell(self.EBX + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[19] = function(Result) self:WriteCell(self.ECX + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[20] = function(Result) self:WriteCell(self.EDX + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[21] = function(Result) self:WriteCell(self.ESI + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[22] = function(Result) self:WriteCell(self.EDI + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[23] = function(Result) self:WriteCell(self.ESP + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[24] = function(Result) self:WriteCell(self.EBP + self[self.PrecompileData[self.XEIP].Segment2],Result) end + self.WriteBackFunctions2[25] = function(Result) self:WriteCell(self.PrecompileData[self.XEIP].PeekByte2 + self[self.PrecompileData[self.XEIP].Segment2],Result) end + for i=1000,2024 do + self.WriteBackFunctions2[i] = function(Result) self:WritePort(self.PrecompileData[self.XEIP].dRM2-1000,Result) end + end + + self.EffectiveAddress1 = {} + self.EffectiveAddress1[17] = function() return self.EAX + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[18] = function() return self.EBX + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[19] = function() return self.ECX + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[20] = function() return self.EDX + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[21] = function() return self.ESI + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[22] = function() return self.EDI + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[23] = function() return self.ESP + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[24] = function() return self.EBP + self[self.PrecompileData[self.XEIP].Segment1] end + self.EffectiveAddress1[24] = function() return self.EBP + self[self.PrecompileData[self.XEIP].Segment1] end + + self.EffectiveAddress1[25] = function() return self.PrecompileData[self.XEIP].PeekByte1 + self[self.PrecompileData[self.XEIP].Segment1] end + + self.EffectiveAddress1[34] = function() return self.EAX + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[35] = function() return self.EBX + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[36] = function() return self.ECX + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[37] = function() return self.EDX + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[38] = function() return self.ESI + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[39] = function() return self.EDI + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[40] = function() return self.ESP + self.PrecompileData[self.XEIP].PeekByte1 end + self.EffectiveAddress1[41] = function() return self.EBP + self.PrecompileData[self.XEIP].PeekByte1 end + + for i=1000,2024 do + self.EffectiveAddress1[i] = function(Result) return -(self.PrecompileData[self.XEIP].dRM2-1000)-1 end + end + + + self.EffectiveAddress2 = {} + self.EffectiveAddress2[17] = function() return self.EAX + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[18] = function() return self.EBX + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[19] = function() return self.ECX + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[20] = function() return self.EDX + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[21] = function() return self.ESI + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[22] = function() return self.EDI + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[23] = function() return self.ESP + self[self.PrecompileData[self.XEIP].Segment2] end + self.EffectiveAddress2[24] = function() return self.EBP + self[self.PrecompileData[self.XEIP].Segment2] end + + self.EffectiveAddress2[25] = function() return self.PrecompileData[self.XEIP].PeekByte2 + self[self.PrecompileData[self.XEIP].Segment2] end + + self.EffectiveAddress2[34] = function() return self.EAX + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[35] = function() return self.EBX + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[36] = function() return self.ECX + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[37] = function() return self.EDX + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[38] = function() return self.ESI + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[39] = function() return self.EDI + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[40] = function() return self.ESP + self.PrecompileData[self.XEIP].PeekByte2 end + self.EffectiveAddress2[41] = function() return self.EBP + self.PrecompileData[self.XEIP].PeekByte2 end + + for i=1000,2024 do + self.EffectiveAddress2[i] = function(Result) return -(self.PrecompileData[self.XEIP].dRM2-1000)-1 end + end +end + +function ENT:PRead(IP) + self.PrecompileMemory[self.TempIP] = IP + self.TempIP = self.TempIP + 1 + return self:ReadCell(self.TempIP-1) +end + +function ENT:Precompile(IP) + self.SkipIterations = true + + self.TempIP = IP + if (self.Debug) then self:DebugMessage("Precompiling instruction at address "..IP) end + + self.PrecompileData[IP] = {} + self.PrecompileData[IP].Size = 0 + + local Opcode = self:PRead(IP) + local RM = self:PRead(IP) + self.PrecompileData[IP].Size = self.PrecompileData[IP].Size + 2 + + local Disp1 = 0 + local Disp2 = 0 + + self.PrecompileData[IP].Valid = true + self.PrecompileData[IP].UnknownOpcode = false + self.PrecompileData[IP].ErrorCode = 0 + + if (Opcode == nil) || (RM == nil) then + if (self.Debug) then Msg("Precompile failed (invalid opcode/RM)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 1 + return + end + + Opcode = tonumber(Opcode) + + local dRM2 = math.floor(RM / 10000) + local dRM1 = RM - dRM2*10000 + + local Segment1 = -4 + local Segment2 = -4 + + if (Opcode > 1000) then + if (Opcode > 10000) then + Segment2 = self:PRead(IP) + self.PrecompileData[IP].Size = self.PrecompileData[IP].Size + 1 + + Opcode = Opcode-10000 + if (Opcode > 1000) then + Segment1 = self:PRead(IP) + self.PrecompileData[IP].Size = self.PrecompileData[IP].Size + 1 + + Opcode = Opcode-1000 + + local Temp = Segment2 + Segment2 = Segment1 + Segment1 = Temp + end + else + Segment1 = self:PRead(IP) + self.PrecompileData[IP].Size = self.PrecompileData[IP].Size + 1 + + Opcode = Opcode-1000 + end + end + + self.PrecompileData[IP].Opcode = Opcode + + Segment1 = self.SegmentType[Segment1] + Segment2 = self.SegmentType[Segment2] + + self.PrecompileData[IP].Segment1 = Segment1 + self.PrecompileData[IP].Segment2 = Segment2 + + if (self.OpcodeCount[Opcode] && (self.OpcodeCount[Opcode] > 0)) then + if self.NeedPeekByte[dRM1] then + self.PrecompileData[IP].PeekByte1 = self:PRead(IP) + self.PrecompileData[IP].Size = self.PrecompileData[IP].Size + 1 + + if (self.PrecompileData[IP].PeekByte1 == nil) then + if (self.Debug) then Msg("Precompile failed (could not peek next byte)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 2 + return + end + end + end + + if (self.OpcodeCount[Opcode] && (self.OpcodeCount[Opcode] > 1)) then + if self.NeedPeekByte[dRM2] then + self.PrecompileData[IP].PeekByte2 = self:PRead(IP) + self.PrecompileData[IP].Size = self.PrecompileData[IP].Size + 1 + + if (self.PrecompileData[IP].PeekByte2 == nil) then + if (self.Debug) then Msg("Precompile failed (could not peek next byte)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 3 + return + end + end + end + + local Param1 = nil + local Param2 = nil + + self.PrecompileData[IP].dRM1 = dRM1 + self.PrecompileData[IP].dRM2 = dRM2 + + if (self.OpcodeCount[Opcode] && (self.OpcodeCount[Opcode] > 0)) then + Param1 = self.ParamFunctions_1[dRM1] + self.PrecompileData[IP].Param1 = Param1 + self.PrecompileData[IP].EffectiveAddress1 = self.EffectiveAddress1[dRM1] + + if (not Param1) then + if (self.Debug) then Msg("Precompile failed (Parameter 1 calling function invalid)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 4 + return + end + end + + if (self.OpcodeCount[Opcode] && (self.OpcodeCount[Opcode] > 1)) then + Param2 = self.ParamFunctions_2[dRM2] + self.PrecompileData[IP].Param2 = Param2 + self.PrecompileData[IP].EffectiveAddress2 = self.EffectiveAddress2[dRM2] + + if (not Param2) then + if (self.Debug) then Msg("Precompile failed (Parameter 2 calling function invalid)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 5 + return + end + end + + + if (self.OpcodeTable[Opcode]) then + self.PrecompileData[IP].Execute = function() //Most of magic is done here + if (self.OpcodeTable[self.PrecompileData[self.XEIP].Opcode]) then + if (self.PrecompileData[self.XEIP].Param1) then + if (self.PrecompileData[self.XEIP].Param2) then + local param1 = tonumber(self.PrecompileData[self.XEIP].Param1()) + local param2 = tonumber(self.PrecompileData[self.XEIP].Param2()) + if (param1 and param2) then + return self.OpcodeTable[self.PrecompileData[self.XEIP].Opcode](param1,param2) + else + return "Read error" + end + else + local param1 = tonumber(self.PrecompileData[self.XEIP].Param1()) + if (param1) then + return self.OpcodeTable[self.PrecompileData[self.XEIP].Opcode](param1,0) + else + return "Read error" + end + end + else + return self.OpcodeTable[self.PrecompileData[self.XEIP].Opcode](0,0) + end + else + if (self.Debug) then Msg("Error: something gone terribly wrong, trying to call non-existing opcode ("..self.PrecompileData[self.XEIP].Opcode..") function without interrupt 4 triggered\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 8 + return + end + end + else + if (self.Debug) then Msg("Precompile almost failed (Unknown opcode "..Opcode..")\n") end + self.PrecompileData[IP].UnknownOpcode = true + self.PrecompileData[IP].Valid = false + return + end + + //First destanation + if (self.OpcodeCount[Opcode] && (self.OpcodeCount[Opcode] > 0)) then + self.PrecompileData[IP].WriteBack = self.WriteBackFunctions[dRM1] + + if (self.PrecompileData[IP].WriteBack == nil) then + if (self.Debug) then Msg("Precompile failed (Writeback function invalid)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 6 + return + end + end + + //Second destanation (for XCHG) + //if (self.OpcodeCount[Opcode] && (self.OpcodeCount[Opcode] > 1)) then + if (Opcode == 81) then //Special case for XCHG - double writeback + self.PrecompileData[IP].WriteBack2 = self.WriteBackFunctions2[dRM2] + + if (self.PrecompileData[IP].WriteBack2 == nil) then + if (self.Debug) then Msg("Precompile failed (Writeback2 function invalid)\n") end + self.PrecompileData[IP].Valid = false + self.PrecompileData[IP].ErrorCode = 7 + return + end + end + + if (self.Debug) then Msg("Precompile successful\n") end +end + +function ENT:PrintState() + Msg("TMR="..self.TMR.." TIMER="..self.TIMER.." XEIP="..self.XEIP.." CMPR="..self.CMPR.."\n") + Msg("EAX="..self.EAX.." EBX="..self.EBX.." ECX="..self.ECX.." EDX="..self.EDX.."\n") + Msg("ESI="..self.ESI.." EDI="..self.EDI.." ESP="..self.ESP.." EBP="..self.EBP.." ESZ="..self.ESZ.."\n") + + Msg("CS="..self.CS) + Msg(" SS="..self.SS) + Msg(" DS="..self.DS) + Msg(" FS="..self.FS) + Msg(" GS="..self.GS) + Msg(" ES="..self.ES) + Msg(" KS="..self.KS) + Msg(" LS="..self.LS.."\n") +end + +function ENT:SetCurrentPage(address) + self.CPAGE = math.floor(address / 128) + + if (not self.Page[self.CPAGE]) then + self.Page[self.CPAGE] = {} + self.Page[self.CPAGE].Read = 1 + self.Page[self.CPAGE].Write = 1 + self.Page[self.CPAGE].Execute = 1 + self.Page[self.CPAGE].RunLevel = 0 + self.Page[self.CPAGE].MappedTo = self.CPAGE + end + self.CurrentPage = self.Page[self.CPAGE] +end + +function ENT:Execute() + self.AuxIO = 0 + self.DeltaTime = CurTime()-(self.PrevTime or CurTime()) + self.PrevTime = (self.PrevTime or CurTime())+self.DeltaTime + + self.TIMER = self.TIMER + self.DeltaTime + self.TMR = self.TMR + 1 + + if (self.BusLock == 1) then + if (self.Debug) then + print("Warning: execution while bus is locked") + end + end + + if (not self.IP) then + self:Reset() + Wire_TriggerOutput(self.Entity, "Error", 5.0) + return + end + + self.XEIP = self.IP+self.CS + self:SetCurrentPage(self.XEIP) + + if (self.CurrentPage.Execute == 0) then + self:Interrupt(14,self.CPAGE) + return + end + + if (self.NextIF) then + self.IF = self.NextIF + self.NextIF = nil + end + + if (self.Debug) then + self:DebugMessage("CPU EXECUTION STEP") + end + + //Dynamic precompiler: check if opcode was precompiled + if (self.PrecompileData[self.XEIP]) then + //Simulate read + self.IP = self.IP + self.PrecompileData[self.XEIP].Size + self.CODEBYTES = self.CODEBYTES + self.PrecompileData[self.XEIP].Size + + //Verify opcode + if (self.PrecompileData[self.XEIP].Valid) then + if (self.OpcodeRunLevel[self.PrecompileData[self.XEIP].Opcode]) then + if (self.OpcodeRunLevel[self.PrecompileData[self.XEIP].Opcode] == 0) then + if (self.Page[self.CPAGE].RunLevel ~= 0) then + self:Interrupt(13,self.PrecompileData[self.XEIP].Opcode) + end + end + end + + //Execute + local Result = self.PrecompileData[self.XEIP].Execute() + if (Result) then + if (Result == "Read error") then + self:Interrupt(5,1) //Read error during execute + else + self.PrecompileData[self.XEIP].WriteBack(Result) + end + end + else + if (self.PrecompileData[self.XEIP].UnknownOpcode) then + self:Interrupt(4,self.PrecompileData[self.XEIP].Opcode) //Unknown Opcode + else + self:Interrupt(5,2+(self.PrecompileData[self.XEIP].ErrorCode or 0)*10) //Internal/opcode read error + end + end + else + self:Precompile(self.XEIP) + self.TMR = self.TMR + 29 + end + + if (self.Debug) then + if (self.INTR == 0) then + self:DebugMessage("") + end + self:PrintState() + + Msg("Memory at XEIP: ") + for i=self.XEIP,self.XEIP+6 do + local oldlock = self.BusLock + self.BusLock = 0 + local val = self:ReadCell(i) + self.BusLock = oldlock + if (val) then + Msg("["..val.."] ") + end + end + Msg("\n") + + if (self.DebugData[self.XEIP]) then + print("") + if (self.DebugLines[self.DebugData[self.XEIP]-2]) then + Msg(self.DebugLines[self.DebugData[self.XEIP]-2].."\n") + end + if (self.DebugLines[self.DebugData[self.XEIP]-1]) then + Msg(self.DebugLines[self.DebugData[self.XEIP]-1].."\n") + end + if (self.DebugLines[self.DebugData[self.XEIP]]) then + print(self.DebugLines[self.DebugData[self.XEIP]]) + end + if (self.DebugLines[self.DebugData[self.XEIP]+1]) then + Msg(self.DebugLines[self.DebugData[self.XEIP]+1].."\n") + end + if (self.DebugLines[self.DebugData[self.XEIP]+2]) then + Msg(self.DebugLines[self.DebugData[self.XEIP]+2].."\n") + end + print("") + end + end + + self.INTR = 0 + self.AuxIO = 1 +end diff --git a/lua/entities/gmod_wire_cpu/init.lua b/lua/entities/gmod_wire_cpu/init.lua new file mode 100644 index 0000000000..931ab20aad --- /dev/null +++ b/lua/entities/gmod_wire_cpu/init.lua @@ -0,0 +1,211 @@ +if (EmuFox) then + include('gmod_wire_cpu/compiler_asm.lua') + + include('gmod_wire_cpu/cpu_bitwise.lua') + include('gmod_wire_cpu/cpu_vm.lua') + include('gmod_wire_cpu/cpu_opcodes.lua') + include('gmod_wire_cpu/cpu_bus.lua') + include('gmod_wire_cpu/cpu_interrupt.lua') +else + AddCSLuaFile("cl_init.lua") + AddCSLuaFile("shared.lua") + + include('shared.lua') + include('compiler_asm.lua') + include('cpu_bitwise.lua') + include('cpu_vm.lua') + include('cpu_opcodes.lua') + include('cpu_bus.lua') + include('cpu_interrupt.lua') +end +ENT.WireDebugName = "CPU" + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Inputs = Wire_CreateInputs(self.Entity, { "MemBus", "IOBus", "Frequency", "Clk", "Reset", "NMI"}) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Error" }) + + self.Debug = false //!!!GLOBAL DEBUG MODE SWITCH!!! + + //Debug mode is cool. All cool guys use it to debug their programs + //It spams your console with step-by-step description of what your CPU does + + self.DebugLines = {} + self.DebugData = {} + + self.Memory = {} + self.ROMMemory = {} + self.PrecompileData = {} + self.PrecompileMemory = {} + + self.SerialNo = math.floor(math.random()*1000000) + + self.UseROM = false + + self.Clk = 0 + self.InputClk = 0 + self.AuxIO = 0 + + self:Reset() + + self.DeltaTime = 0 + self.Freq = 2000 + self.PrevThinkTime = CurTime() + + self.PrevTime = CurTime() + self.SkipIterations = false + + self:SetOverlayText("CPU") + self:InitializeOpcodeTable() + self:InitializeLookupTables() + self:InitializeOpcodeRunlevels() + self:InitializeOpcodeNames() + self:InitializeRegisterNames() + self:InitializeOptimizer() + self:InitializeCPUVariableSet() + self:InitializeASMOpcodes() +end + +function ENT:CPUID_Version() + //SVN shit doesnt want to work!! + local SVNString = "$Revision: 643 $" + return 900//tonumber(string.sub(SVNString,12,14)) +end + +function ENT:DebugMessage(msg) + if (self.CPUName) then + Msg(self.CPUName.." ") + end + Msg("============================================\n") + Msg(msg.."\n") +end + + +//CPUID +//Value | EAX +//-------------------------------------------- +//0 | CPU Version +//1 | RAM Size +//-------------------------------------------- + +function ENT:RunExecute(Iterations) + while (Iterations > 0) && (self.Clk >= 1.0) && (self.Idle == 0) do + self:Execute() + if (self.SkipIterations == true) then + self.SkipIterations = false + Iterations = Iterations - 30 + else + Iterations = Iterations - 1 + end + end + + //Restore current page for external bus reading + self.CurrentPage = {} + self.CurrentPage.Read = 1 + self.CurrentPage.Write = 1 + self.CurrentPage.Execute = 1 + self.CurrentPage.RunLevel = self.XTRL //External reads have runlevel 1 + + if (self.Idle == 1) then + self.Idle = 0 + end +end//self.Freq + +function ENT:Think() + local DeltaTime = CurTime() - self.PrevThinkTime + local Iterations = math.floor(self.Freq*DeltaTime*0.5) + self:RunExecute(Iterations) + + self.PrevThinkTime = CurTime() + + //Run every tick (or at least attempt to) + if (self.Clk >= 1.0) then self.Entity:NextThink(CurTime()) end + return true +end + +//FIXME: remove this: +function ENT:SingleThink() + if (self.Clk >= 1.0) then + self:Execute() + end + + //Restore current page for external bus reading + self.CurrentPage = {} + self.CurrentPage.Read = 1 + self.CurrentPage.Write = 1 + self.CurrentPage.Execute = 1 + self.CurrentPage.RunLevel = self.XTRL //External reads have runlevel 1 + + if (self.Idle == 1) then + self.Idle = 0 + end +end + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + info.UseROM = self.UseROM + info.SerialNo = self.SerialNo + if (self.UseROM) then + info.Memory = {} + for i=0,65535 do + if ((self.ROMMemory[i]) && (self.ROMMemory[i] ~= 0)) then + info.Memory[i] = self.ROMMemory[i] + end + end + end + + return info +end + + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + self.SerialNo = info.SerialNo + if ((info.UseROM) && (info.UseROM == true)) then + self.UseROM = info.UseROM + self.ROMMemory = {} + for i=0,65535 do + if (info.Memory[i]) then + self.ROMMemory[i] = info.Memory[i] + end + end + + + self:Reset() + end +end + +function ENT:TriggerInput(iname, value) + if (iname == "Clk") then + self.Clk = value + self.InputClk = value + self.PrevTime = CurTime() + self.PrevThinkTime = CurTime() + self.Entity:NextThink(CurTime()) + elseif (iname == "Frequency") then + if (not SinglePlayer() && (value > 120000)) then + self.Freq = 120000 + return + end + if (value > 0) then + self.Freq = math.floor(value) + end + elseif (iname == "Reset") then + if (value >= 1.0) then + self:Reset() + end + elseif (iname == "NMI") then + if (value >= 32) && (value < 256) then + if (self.Clk >= 1.0) then + self.AuxIO = 0 + self:NMIInterrupt(math.floor(value)) + self.AuxIO = 1 + end + end + end +end diff --git a/lua/entities/gmod_wire_cpu/shared.lua b/lua/entities/gmod_wire_cpu/shared.lua new file mode 100644 index 0000000000..e055e983d8 --- /dev/null +++ b/lua/entities/gmod_wire_cpu/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire CPU" +ENT.Author = "Black Phoenix" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_damage_detector/cl_init.lua b/lua/entities/gmod_wire_damage_detector/cl_init.lua new file mode 100644 index 0000000000..c3a06f3110 --- /dev/null +++ b/lua/entities/gmod_wire_damage_detector/cl_init.lua @@ -0,0 +1,3 @@ +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_damage_detector/init.lua b/lua/entities/gmod_wire_damage_detector/init.lua new file mode 100644 index 0000000000..a22ab639d4 --- /dev/null +++ b/lua/entities/gmod_wire_damage_detector/init.lua @@ -0,0 +1,218 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include( 'shared.lua' ) + +ENT.WireDebugName = "Damage Detector" + +local damage_detectors = {} + +function ENT:Initialize() + local self = self.Entity + + self:PhysicsInit( SOLID_VPHYSICS ) + self:SetMoveType( MOVETYPE_VPHYSICS ) + self:SetSolid( SOLID_VPHYSICS ) + + self.Outputs = WireLib.CreateSpecialOutputs(self, { "Damage", "Attacker", "Inflictor", "Victim" } , { "NORMAL", "ENTITY", "ENTITY", "ENTITY" } ) + self.Inputs = WireLib.CreateSpecialInputs(self, { "On", "Entity", "Entities" }, { "NORMAL", "ENTITY", "ARRAY" } ) + + self.on = 0 + self.updated = false + + self.firsthit_dmginfo = {} -- Stores damage info representing damage during an interval + self.output_dmginfo = {} -- Stores the current damage info outputs + self.damage = 0 + + damage_detectors[self:EntIndex()] = true +end + +/****************************** + How entities are stored: + self.linked_entities Array: Entities the detector is directly linked to. Contains entIDs as values + self.key_ents KeyTable: Entities to check damage on (including constrained ents), only updated when the damage hook is called,.Contains entIDs as table keys +******************************/ + +function ENT:OnRemove() + damage_detectors[self.Entity:EntIndex()] = nil + Wire_Remove(self.Entity) +end + +function ENT:ShowOutput() + if self.includeconstrained == 0 then + self:SetOverlayText( "Damage Detector\n" .. + "(Individual Props)" ) + else + self:SetOverlayText( "Damage Detector\n" .. + "(Constrained Props)" ) + end +end + +function ENT:SetOverlayText(txt) + self.Entity:SetNetworkedBeamString("GModOverlayText", txt) +end + +function ENT:Setup( includeconstrained ) + self.includeconstrained = includeconstrained + self:ShowOutput() +end + +function ENT:LinkEntity( ent ) + self.linked_entities = {} + self.linked_entities[0] = ent:EntIndex() -- [0] is used to store single links (e.g. manual links) +end + +function ENT:TriggerInput( iname, value ) + if (iname == "On") then + if value > 0 then self.on = true + else self.on = false end + elseif (iname == "Entities") then -- Populate linked_entities from "Array" + if value then + self.linked_entities = {} + for _,v in pairs(value) do + if ValidEntity(v) then + table.insert(self.linked_entities, v:EntIndex()) + end + end + end + elseif (iname == "Entity") then + if value then + self.linked_entities = {} + if ValidEntity(value) then + self.linked_entities[1] = value:EntIndex() + end + end + end +end + +function ENT:TriggerOutput() -- Entity outputs won't trigger again until they change + local attacker = self.firsthit_dmginfo[1] + if ValidEntity(attacker) then + if self.output_dmginfo[1] != attacker then + self.output_dmginfo[1] = attacker + Wire_TriggerOutput( self.Entity, "Attacker", attacker ) + end + end + + local inflictor = self.firsthit_dmginfo[2] + if ValidEntity(inflictor) then + if self.output_dmginfo[2] != inflictor then + self.output_dmginfo[2] = inflictor + Wire_TriggerOutput( self.Entity, "Inflictor", inflictor ) + end + end + + local victim = self.firsthit_dmginfo[3] + if ValidEntity( ents.GetByIndex(victim) ) then + if self.output_dmginfo[3] != victim then + self.output_dmginfo[3] = victim + Wire_TriggerOutput( self.Entity, "Victim", ents.GetByIndex(victim) ) + end + end + + Wire_TriggerOutput( self.Entity, "Damage", self.damage ) + Wire_TriggerOutput( self.Entity, "Damage", 0 ) -- Set damage back to 0 after it's been dealt +end + +function ENT:UpdateLinkedEnts() -- Check to see if prop is registered by the detector + if !self.linked_entities then return nil end + + self.key_ents = {} + + if self.includeconstrained == 1 then -- Don't update constrained entities unless we have to + self:UpdateConstrainedEnts() + end + + for _,v in pairs (self.linked_entities) do -- include linked_entities + if ValidEntity( ents.GetByIndex(v) ) then + self.key_ents[v] = true + end + end +end + +function ENT:UpdateConstrainedEnts() -- Finds all entities constrained to linked_entities + for _,v in pairs (self.linked_entities) do + local ent = ents.GetByIndex(v) + if ValidEntity(ent) and constraint.HasConstraints(ent) and !self.key_ents[v] then + for _,w in pairs( constraint.GetAllConstrainedEntities(ent) ) do + if ValidEntity(w) then + self.key_ents[w:EntIndex()] = true + end + end + end + end +end + +function ENT:UpdateDamage( dmginfo, entID ) + if !self.updated then -- Only register the first target's damage info + self.firsthit_dmginfo = { + dmginfo:GetAttacker(), + dmginfo:GetInflictor(), + entID + } + end + + local damage = dmginfo:GetDamage() + + if dmginfo:IsExplosionDamage() then -- Explosives will affect the entity that receives the most damage + if self.damage < damage then + self.damage = damage + self.firsthit_dmginfo[3] = entID + end + else + self.damage = self.damage + damage + end +end + +function ENT:Think() + self.updated = false + if self.damage > 0 then + self:TriggerOutput() + self.damage = 0 + end + return true +end + +local function CheckWireDamageDetectors( ent, inflictor, attacker, amount, dmginfo ) + if amount > 0 then + local entID = ent:EntIndex() + for k,_ in pairs(damage_detectors) do + local detector = ents.GetByIndex(k) + if ValidEntity(detector) and detector.on then + if !detector.updated then + detector:UpdateLinkedEnts() + detector:NextThink(CurTime()+0.001) -- Update link info once per tick per detector at most + end + if detector.key_ents[entID] then + detector:UpdateDamage( dmginfo, entID ) + end + detector.updated = true + end + end + end +end +hook.Add( "EntityTakeDamage", "CheckWireDamageDetectors", CheckWireDamageDetectors ) + +// Advanced Duplicator Support + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if ValidEntity( ents.GetByIndex(self.linked_entities[0]) ) then + info.linked_entities = self.linked_entities[0] + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if IsValid( GetEntByID(info.linked_entities) ) then + self.linked_entities = {} + self.linked_entities[0] = GetEntByID(info.linked_entities):EntIndex() + elseif IsValid( ents.GetByIndex(info.linked_entities) ) then + self.linked_entities = {} + self.linked_entities[0] = info.linked_entities + end + self:ShowOutput() +end diff --git a/lua/entities/gmod_wire_damage_detector/shared.lua b/lua/entities/gmod_wire_damage_detector/shared.lua new file mode 100644 index 0000000000..fd3d1d564d --- /dev/null +++ b/lua/entities/gmod_wire_damage_detector/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Damage Detector" +ENT.Author = "Jimlad" +ENT.Contact = "" +ENT.Purpose = "Detects damage inflicted on props." +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_data_satellitedish/cl_init.lua b/lua/entities/gmod_wire_data_satellitedish/cl_init.lua new file mode 100644 index 0000000000..48d2275a21 --- /dev/null +++ b/lua/entities/gmod_wire_data_satellitedish/cl_init.lua @@ -0,0 +1,10 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_data_satellitedish/init.lua b/lua/entities/gmod_wire_data_satellitedish/init.lua new file mode 100644 index 0000000000..6e69945a4c --- /dev/null +++ b/lua/entities/gmod_wire_data_satellitedish/init.lua @@ -0,0 +1,53 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Satellite Dish" + + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self:ShowOutput() +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:ShowOutput() + if IsValid(self.Transmitter) then + self:SetOverlayText( "Satellite Dish: Linked" ) + else + self:SetOverlayText( "Satellite Dish: Unlinked" ) + end +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +// Advanced Duplicator Support + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if ( self.Transmitter ) and ( self.Transmitter:IsValid() ) then + info.Transmitter = self.Transmitter:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.Transmitter) then + self.Transmitter = GetEntByID(info.Transmitter) + if (!self.Transmitter) then + self.Transmitter = ents.GetByIndex(info.Transmitter) + end + end + self:ShowOutput() +end diff --git a/lua/entities/gmod_wire_data_satellitedish/shared.lua b/lua/entities/gmod_wire_data_satellitedish/shared.lua new file mode 100644 index 0000000000..d22e024f28 --- /dev/null +++ b/lua/entities/gmod_wire_data_satellitedish/shared.lua @@ -0,0 +1,37 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Data Satellite Dish" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_data_store/cl_init.lua b/lua/entities/gmod_wire_data_store/cl_init.lua new file mode 100644 index 0000000000..48d2275a21 --- /dev/null +++ b/lua/entities/gmod_wire_data_store/cl_init.lua @@ -0,0 +1,10 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_data_store/init.lua b/lua/entities/gmod_wire_data_store/init.lua new file mode 100644 index 0000000000..05e62b7ee6 --- /dev/null +++ b/lua/entities/gmod_wire_data_store/init.lua @@ -0,0 +1,45 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Data Store" + +local MODEL = Model("models/jaanus/wiretool/wiretool_range.mdl") + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Values = {}; + self.Values["A"] = 0 + self.Values["B"] = 0 + self.Values["C"] = 0 + self.Values["D"] = 0 + self.Values["E"] = 0 + self.Values["F"] = 0 + self.Values["G"] = 0 + self.Values["H"] = 0 + self:ShowOutput() +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Think() +end + +function ENT:Setup() +end + +function ENT:ShowOutput() + self:SetOverlayText( "Data Store" ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + diff --git a/lua/entities/gmod_wire_data_store/shared.lua b/lua/entities/gmod_wire_data_store/shared.lua new file mode 100644 index 0000000000..98e3715289 --- /dev/null +++ b/lua/entities/gmod_wire_data_store/shared.lua @@ -0,0 +1,37 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Data Storer" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_data_transferer/cl_init.lua b/lua/entities/gmod_wire_data_transferer/cl_init.lua new file mode 100644 index 0000000000..73cf67dfbb --- /dev/null +++ b/lua/entities/gmod_wire_data_transferer/cl_init.lua @@ -0,0 +1,9 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_data_transferer/init.lua b/lua/entities/gmod_wire_data_transferer/init.lua new file mode 100644 index 0000000000..cb1678bf72 --- /dev/null +++ b/lua/entities/gmod_wire_data_transferer/init.lua @@ -0,0 +1,158 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Data Transferer" + + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, {"Send","A","B","C","D","E","F","G","H"}) + self.Outputs = Wire_CreateOutputs(self.Entity, {"A","B","C","D","E","F","G","H"}) + self.Sending = false + self.Activated = false + self.ActivateTime = 0 + self.DefaultZero = true + self.IgnoreZero = false + self.Values = {}; + self.Values["A"] = 0 + self.Values["B"] = 0 + self.Values["C"] = 0 + self.Values["D"] = 0 + self.Values["E"] = 0 + self.Values["F"] = 0 + self.Values["G"] = 0 + self.Values["H"] = 0 + + self:SetBeamRange(25000) + self:ShowOutput() +end + +function ENT:Setup(Range,DefaultZero,IgnoreZero) + self.IgnoreZero = IgnoreZero + self.DefaultZero = DefaultZero + self:SetBeamRange(Range) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:TriggerInput(iname, value) + if(iname == "Send")then + if(value > 0)then + self.Sending = true + else + self.Sending = false + end + else + self.Values[iname] = value + end +end + +function ENT:Think() + if(self.Activated == false && self.DefaultZero)then + Wire_TriggerOutput(self.Entity,"A",0) + Wire_TriggerOutput(self.Entity,"B",0) + Wire_TriggerOutput(self.Entity,"C",0) + Wire_TriggerOutput(self.Entity,"D",0) + Wire_TriggerOutput(self.Entity,"E",0) + Wire_TriggerOutput(self.Entity,"F",0) + Wire_TriggerOutput(self.Entity,"G",0) + Wire_TriggerOutput(self.Entity,"H",0) + else + if(CurTime() > self.ActivateTime + 0.5)then + self.Activated = false + end + end + + + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamRange()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + local ent = trace.Entity + + if not (ent && ent:IsValid() && + (trace.Entity:GetClass() == "gmod_wire_data_transferer" || + trace.Entity:GetClass() == "gmod_wire_data_satellitedish" || + trace.Entity:GetClass() == "gmod_wire_data_store" ))then + if(Color(self.Entity:GetColor()) != Color(255,255,255,255))then + self.Entity:SetColor(255, 255, 255, 255) + end + return false + end + + if(Color(self.Entity:GetColor()) != Color(0,255,0,255))then + self.Entity:SetColor(0, 255, 0, 255) + end + + if(trace.Entity:GetClass() == "gmod_wire_data_transferer")then + ent:RecieveValue("A",self.Values.A) + ent:RecieveValue("B",self.Values.B) + ent:RecieveValue("C",self.Values.C) + ent:RecieveValue("D",self.Values.D) + ent:RecieveValue("E",self.Values.E) + ent:RecieveValue("F",self.Values.F) + ent:RecieveValue("G",self.Values.G) + ent:RecieveValue("H",self.Values.H) + elseif(trace.Entity:GetClass() == "gmod_wire_data_satellitedish")then + if(ent.Transmitter && ent.Transmitter:IsValid())then + ent.Transmitter:RecieveValue("A",self.Values.A) + ent.Transmitter:RecieveValue("B",self.Values.B) + ent.Transmitter:RecieveValue("C",self.Values.C) + ent.Transmitter:RecieveValue("D",self.Values.D) + ent.Transmitter:RecieveValue("E",self.Values.E) + ent.Transmitter:RecieveValue("F",self.Values.F) + ent.Transmitter:RecieveValue("G",self.Values.G) + ent.Transmitter:RecieveValue("H",self.Values.H) + else + self.Entity:SetColor(255, 0, 0, 255) + end + elseif(trace.Entity:GetClass() == "gmod_wire_data_store")then + Wire_TriggerOutput(self.Entity,"A",ent.Values.A) + Wire_TriggerOutput(self.Entity,"B",ent.Values.B) + Wire_TriggerOutput(self.Entity,"C",ent.Values.C) + Wire_TriggerOutput(self.Entity,"D",ent.Values.D) + Wire_TriggerOutput(self.Entity,"E",ent.Values.E) + Wire_TriggerOutput(self.Entity,"F",ent.Values.F) + Wire_TriggerOutput(self.Entity,"G",ent.Values.G) + Wire_TriggerOutput(self.Entity,"H",ent.Values.H) + if(self.Sending)then + ent.Values.A = self.Entity.Inputs["A"].Value + ent.Values.B = self.Entity.Inputs["B"].Value + ent.Values.C = self.Entity.Inputs["C"].Value + ent.Values.D = self.Entity.Inputs["D"].Value + ent.Values.E = self.Entity.Inputs["E"].Value + ent.Values.F = self.Entity.Inputs["F"].Value + ent.Values.G = self.Entity.Inputs["G"].Value + ent.Values.H = self.Entity.Inputs["H"].Value + end + end + self.Entity:NextThink(CurTime()+0.125) +end + +function ENT:ShowOutput() + self:SetOverlayText( "Data Transferer" ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function ENT:RecieveValue(output,value) + self.Activated = true + self.ActivateTime = CurTime() + if value ~= 0 or not self.IgnoreZero then + Wire_TriggerOutput(self.Entity,output,value) + end +end diff --git a/lua/entities/gmod_wire_data_transferer/shared.lua b/lua/entities/gmod_wire_data_transferer/shared.lua new file mode 100644 index 0000000000..4146a23c78 --- /dev/null +++ b/lua/entities/gmod_wire_data_transferer/shared.lua @@ -0,0 +1,49 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Data Transferer" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamRange(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamRange() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_dataplug/cl_init.lua b/lua/entities/gmod_wire_dataplug/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_dataplug/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_dataplug/init.lua b/lua/entities/gmod_wire_dataplug/init.lua new file mode 100644 index 0000000000..8a374625c1 --- /dev/null +++ b/lua/entities/gmod_wire_dataplug/init.lua @@ -0,0 +1,74 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +local MODEL = Model( "models/hammy/pci_card.mdl" ) + +ENT.WireDebugName = "DataPlug" + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.MySocket = nil + self.Memory = nil + + self.Inputs = Wire_CreateInputs(self.Entity, { "Memory" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Connected" }) + self:SetOverlayText( "Data plug" ) + Wire_TriggerOutput(self.Entity, "Connected", 0) +end + +function ENT:OnRemove() + self.BaseClass.Think(self) + + if (self.MySocket) and (self.MySocket:IsValid()) then + self.MySocket.MyPlug = nil + end +end + +function ENT:Setup(a,ar,ag,ab,aa) + self.A = a or 0 + self.AR = ar or 255 + self.AG = ag or 0 + self.AB = ab or 0 + self.AA = aa or 255 + self.Entity:SetColor(ar, ag, ab, aa) +end + +function ENT:TriggerInput(iname, value, iter) + if (iname == "Memory") then + self.Memory = self.Inputs.Memory.Src + if (self.MySocket) and (self.MySocket:IsValid()) then + self.MySocket:SetMemory(self.Memory) + end + end +end + +function ENT:SetSocket(socket) + self.MySocket = socket + if (self.MySocket) and (self.MySocket:IsValid()) then + self.MySocket:SetMemory(self.Memory) + else + Wire_TriggerOutput(self.Entity, "Connected", 0) + end +end + +function ENT:AttachedToSocket(socket) + socket:SetMemory(self.Memory) + Wire_TriggerOutput(self.Entity, "Connected", 1) +end + +function ENT:OnRestore() + self.A = self.A or 0 + self.AR = self.AR or 255 + self.AG = self.AG or 0 + self.AB = self.AB or 0 + self.AA = self.AA or 255 + + self.BaseClass.OnRestore(self) +end diff --git a/lua/entities/gmod_wire_dataplug/shared.lua b/lua/entities/gmod_wire_dataplug/shared.lua new file mode 100644 index 0000000000..ddf0ed6150 --- /dev/null +++ b/lua/entities/gmod_wire_dataplug/shared.lua @@ -0,0 +1,7 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Plug" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_dataport/cl_init.lua b/lua/entities/gmod_wire_dataport/cl_init.lua new file mode 100644 index 0000000000..9dca17e409 --- /dev/null +++ b/lua/entities/gmod_wire_dataport/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_dataport/init.lua b/lua/entities/gmod_wire_dataport/init.lua new file mode 100644 index 0000000000..3161d3d8f6 --- /dev/null +++ b/lua/entities/gmod_wire_dataport/init.lua @@ -0,0 +1,58 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "DataPort" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:SetUseType( SIMPLE_USE ) + + //makeoutputs = {} + //for i = 0,7 do + // makeoutputs[i] = "Port"..i + //end + //self.Outputs = Wire_CreateOutputs(self.Entity, makeoutputs) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Port0","Port1","Port2","Port3","Port4","Port5","Port6","Port7" }) + self.Inputs = Wire_CreateInputs(self.Entity, { "Port0","Port1","Port2","Port3","Port4","Port5","Port6","Port7" }) + + self.Ports = {} + for i = 0,7 do + self.Ports[i] = 0 + end + self:SetOverlayText( "Data port" ) +end + +/*function ENT:Think() + self.BaseClass.Think(self) +end*/ + +function ENT:ReadCell( Address ) + if (Address >= 0) && (Address <= 7) then + return self.Ports[Address] + else + return nil + end +end + +function ENT:WriteCell( Address, value ) + if (Address >= 0) && (Address <= 7) then + Wire_TriggerOutput(self.Entity, "Port"..Address, value) + return true + else + return false + end +end + +function ENT:TriggerInput(iname, value) + for i = 0,7 do + if (iname == "Port"..i) then + self.Ports[i] = value + end + end +end diff --git a/lua/entities/gmod_wire_dataport/shared.lua b/lua/entities/gmod_wire_dataport/shared.lua new file mode 100644 index 0000000000..2971da6d2e --- /dev/null +++ b/lua/entities/gmod_wire_dataport/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Data Port" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_datarate/cl_init.lua b/lua/entities/gmod_wire_datarate/cl_init.lua new file mode 100644 index 0000000000..9d004eb17e --- /dev/null +++ b/lua/entities/gmod_wire_datarate/cl_init.lua @@ -0,0 +1,4 @@ +include("shared.lua") +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_datarate/init.lua b/lua/entities/gmod_wire_datarate/init.lua new file mode 100644 index 0000000000..329f01d20a --- /dev/null +++ b/lua/entities/gmod_wire_datarate/init.lua @@ -0,0 +1,87 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "DataTransfer" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:SetUseType( SIMPLE_USE ) + self.Outputs = Wire_CreateOutputs(self.Entity, {"Output","HiSpeed_DataRate","Wire_DataRate"}) + self.Inputs = Wire_CreateInputs(self.Entity,{"Input","Smooth", "Interval"}) + + self.Memory = nil + self.Smooth = 0.5 + self.Interval = 0.25 + + self.WDataRate = 0 + self.WDataBytes = 0 + self.HDataRate = 0 + self.HDataBytes = 0 + + self:SetOverlayText("Data transferrer\nHi-Speed data rate: 0 bps\nWire data rate: 0 bps") +end + +function ENT:Think() + self.BaseClass.Think(self) + + self.WDataRate = (self.WDataRate*(2-self.Smooth) + self.WDataBytes * (1/self.Interval) * (self.Smooth)) / 2 + self.WDataBytes = 0 + + self.HDataRate = (self.HDataRate*(2-self.Smooth) + self.HDataBytes * (1/self.Interval) * (self.Smooth)) / 2 + self.HDataBytes = 0 + + Wire_TriggerOutput(self.Entity, "HiSpeed_DataRate", self.HDataRate) + Wire_TriggerOutput(self.Entity, "Wire_DataRate", self.WDataRate) + + self:SetOverlayText("Data transferrer\nHi-Speed data rate: "..math.floor(self.HDataRate).." bps\nWire data rate: "..math.floor(self.WDataRate).." bps") + self.Entity:NextThink(CurTime()+self.Interval) + + return true +end + +function ENT:ReadCell( Address ) + if (self.Memory) then + if (self.Memory.LatchStore && self.Memory.LatchStore[math.floor(Address)]) then + self.HDataBytes = self.HDataBytes + 1 + return self.Memory.LatchStore[math.floor(Address)] + elseif (self.Memory.ReadCell) then + self.HDataBytes = self.HDataBytes + 1 + local val = self.Memory:ReadCell(Address) + if (val) then return val + else return 0 end + end + end + return nil +end + +function ENT:WriteCell( Address, value ) + if (self.Memory) then + if (self.Memory.LatchStore && self.Memory.LatchStore[math.floor(Address)]) then + self.Memory.LatchStore[math.floor(Address)] = value + self.HDataBytes = self.HDataBytes + 1 + return true + elseif (self.Memory.WriteCell) then + local res = self.Memory:WriteCell(Address, value) + self.HDataBytes = self.HDataBytes + 1 + return res + end + end + return false +end + +function ENT:TriggerInput(iname, value) + if (iname == "Input") then + self.Memory = self.Inputs.Input.Src + self.WDataBytes = self.WDataBytes + 1 + Wire_TriggerOutput(self.Entity, "Output", value) + elseif (iname == "Smooth") then + self.Smooth = 2*(1-math.Clamp(value,0,1)) + elseif (iname == "Interval") then + self.Interval = math.Clamp(value,0.1,2) + end +end diff --git a/lua/entities/gmod_wire_datarate/shared.lua b/lua/entities/gmod_wire_datarate/shared.lua new file mode 100644 index 0000000000..7e61b1b21c --- /dev/null +++ b/lua/entities/gmod_wire_datarate/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Data Transferrer" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_datasocket/cl_init.lua b/lua/entities/gmod_wire_datasocket/cl_init.lua new file mode 100644 index 0000000000..4bdf6213ce --- /dev/null +++ b/lua/entities/gmod_wire_datasocket/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE diff --git a/lua/entities/gmod_wire_datasocket/init.lua b/lua/entities/gmod_wire_datasocket/init.lua new file mode 100644 index 0000000000..f52e271c32 --- /dev/null +++ b/lua/entities/gmod_wire_datasocket/init.lua @@ -0,0 +1,160 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Socket" + +local MODEL = Model( "models/hammy/pci_slot.mdl" ) + +//Time after loosing one plug to search for another +local NEW_PLUG_WAIT_TIME = 2 +local PLUG_IN_SOCKET_CONSTRAINT_POWER = 5000 +local PLUG_IN_ATTACH_RANGE = 3 + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.MyPlug = nil + self.Memory = nil + self.Const = nil + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Memory" }) + self:SetOverlayText( "Data socket" ) + Wire_TriggerOutput(self.Entity, "Memory", 0) +end + +function ENT:SetMemory(mement) + self.Memory = mement + Wire_TriggerOutput(self.Entity, "Memory", 1) +end + +function ENT:Setup(a,ar,ag,ab,aa) + self.A = a or 0 + self.AR = ar or 255 + self.AG = ag or 0 + self.AB = ab or 0 + self.AA = aa or 255 + + self.Entity:SetColor(ar, ag, ab, aa) +end + +function ENT:ReadCell( Address ) + if (self.Memory) then + if (self.Memory.ReadCell) then + return self.Memory:ReadCell( Address ) + else + return nil + end + else + return nil + end +end + +function ENT:WriteCell( Address, value ) + if (self.Memory) then + if (self.Memory.WriteCell) then + return self.Memory:WriteCell( Address, value ) + else + return false + end + else + return false + end +end + +function ENT:Think() + self.BaseClass.Think(self) + + // If we were unplugged, reset the plug and socket to accept new ones. + if (self.Const) and (not self.Const:IsValid()) then + self.Const = nil + self.NoCollideConst = nil + if (self.MyPlug) and (self.MyPlug:IsValid()) then + self.MyPlug:SetSocket(nil) + self.MyPlug = nil + end + + self.Memory = nil //We're now getting no signal + Wire_TriggerOutput(self.Entity, "Memory", 0) + + self.Entity:NextThink( CurTime() + NEW_PLUG_WAIT_TIME ) //Give time before next grabbing a plug. + return true + end + + // If we have no plug in us + if (not self.MyPlug) or (not self.MyPlug:IsValid()) then + + // Find entities near us + local sockCenter = self:GetOffset( Vector(-1.75, 0, 0) ) + local local_ents = ents.FindInSphere( sockCenter, PLUG_IN_ATTACH_RANGE ) + for key, plug in pairs(local_ents) do + + // If we find a plug, try to attach it to us + if ( plug:IsValid() && plug:GetClass() == "gmod_wire_dataplug" ) then + + // If no other sockets are using it + if plug.MySocket == nil then + local plugpos = plug:GetPos() + local dist = (sockCenter-plugpos):Length() + + self:AttachPlug(plug) + end + end + end + end +end + +function ENT:AttachPlug( plug ) + // Set references between them + plug:SetSocket(self.Entity) + self.MyPlug = plug + + // Position plug + local newpos = self:GetOffset( Vector(-1.75, 0, 0) ) + local socketAng = self.Entity:GetAngles() + plug:SetPos( newpos ) + plug:SetAngles( socketAng ) + + self.NoCollideConst = constraint.NoCollide(self.Entity, plug, 0, 0) + if (not self.NoCollideConst) then + self.MyPlug = nil + plug:SetSocket(nil) + self.Memory = nil + Wire_TriggerOutput(self.Entity, "Memory", 0) + return + end + + // Constrain together + self.Const = constraint.Weld( self.Entity, plug, 0, 0, PLUG_IN_SOCKET_CONSTRAINT_POWER, true ) + if (not self.Const) then + self.NoCollideConst:Remove() + self.NoCollideConst = nil + self.MyPlug = nil + plug:SetSocket(nil) + self.Memory = nil + Wire_TriggerOutput(self.Entity, "Memory", 0) + return + end + + // Prepare clearup incase one is removed + plug:DeleteOnRemove( self.Const ) + self.Entity:DeleteOnRemove( self.Const ) + self.Const:DeleteOnRemove( self.NoCollideConst ) + + plug:AttachedToSocket(self.Entity) +end + +function ENT:OnRestore() + self.A = self.A or 0 + self.AR = self.AR or 255 + self.AG = self.AG or 0 + self.AB = self.AB or 0 + self.AA = self.AA or 255 + + self.BaseClass.OnRestore(self) +end diff --git a/lua/entities/gmod_wire_datasocket/shared.lua b/lua/entities/gmod_wire_datasocket/shared.lua new file mode 100644 index 0000000000..ba35abb8ca --- /dev/null +++ b/lua/entities/gmod_wire_datasocket/shared.lua @@ -0,0 +1,18 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Socket" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:GetOffset( vec ) + local offset = vec + + local ang = self.Entity:GetAngles() + local stackdir = ang:Up() + offset = ang:Up() * offset.X + ang:Forward() * -1 * offset.Z + ang:Right() * offset.Y + + return self.Entity:GetPos() + stackdir * 2 + offset +end diff --git a/lua/entities/gmod_wire_detonator/cl_init.lua b/lua/entities/gmod_wire_detonator/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_detonator/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_detonator/init.lua b/lua/entities/gmod_wire_detonator/init.lua new file mode 100644 index 0000000000..cc90ecdd2a --- /dev/null +++ b/lua/entities/gmod_wire_detonator/init.lua @@ -0,0 +1,118 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Detonator" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs( self.Entity, { "Trigger" } ) + self.Trigger = 0 +end + +function ENT:TriggerInput(iname, value) + if iname == "Trigger" then + self:ShowOutput( value ) + end +end + +function ENT:Setup(damage) + self.Damage = damage + self:ShowOutput( 0 ) +end + +function ENT:ShowOutput( Trigger ) + if Trigger ~= self.Trigger then + self:SetOverlayText( "Detonator " .. self.damage .. " = " .. Trigger ) + self.Trigger = Trigger + if Trigger > 0 then + self:DoDamage() + end + end +end + +function ENT:DoDamage() + if self.target and self.target:IsValid() and self.target:Health() > 0 then + if self.target:Health() <= self.Damage then + self.target:SetHealth(0) + self.target:Fire( "break", "", 0 ) + self.target:Fire( "kill", "", 0.2 ) + else + self.target:SetHealth( self.target:Health() - self.Damage ) + end + end + + local effectdata = EffectData() + effectdata:SetOrigin( self.Entity:GetPos() ) + util.Effect( "Explosion", effectdata, true, true ) + self.Entity:Remove() +end + +-- Dupe info functions added by TheApathetic +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if self.target and self.target:IsValid() then + info.target = self.target:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if info.target then + local target = GetEntByID(info.target) + if not target then + target = ents.GetByIndex(info.target) + end + self.target = target + end +end + + +-- "target" is now handled by TOOL:LeftClick() for STool-spawned +-- detonators and ENT:Build/ApplyDupeInfo() for duplicated ones +-- It's done this way because MakeWireDetonator() cannot distinguish whether +-- detonator was made by the STool or the duplicator; the duplicator-made +-- detonator tries to reference a non-existent target (TheApathetic) +function MakeWireDetonator(pl, Pos, Ang, model, damage, nocollide, frozen) + if not pl:CheckLimit( "wire_detonators" ) then return false end + + local wire_detonator = ents.Create("gmod_wire_detonator") + if not wire_detonator:IsValid() then return false end + wire_detonator:SetAngles(Ang) + wire_detonator:SetPos(Pos) + wire_detonator:SetModel(model) + wire_detonator:Spawn() + + wire_detonator:Setup(damage) + wire_detonator:SetPlayer(pl) + + if nocollide == true then wire_detonator:GetPhysicsObject():EnableCollisions(false) end + if wire_detonator:GetPhysicsObject():IsValid() then + local Phys = wire_detonator:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + local ttable = { + pl = pl, + damage = damage, + nocollide = nocollide + } + table.Merge(wire_detonator, ttable) + + pl:AddCount("wire_detonators", wire_detonator) + pl:AddCleanup("gmod_wire_detonator", wire_detonator) + + return wire_detonator +end + +duplicator.RegisterEntityClass("gmod_wire_detonator", MakeWireDetonator, "Pos", "Ang", "Model", "damage", "nocollide", "frozen") + diff --git a/lua/entities/gmod_wire_detonator/shared.lua b/lua/entities/gmod_wire_detonator/shared.lua new file mode 100644 index 0000000000..56f106e86b --- /dev/null +++ b/lua/entities/gmod_wire_detonator/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Detonator" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_digitalscreen/cl_init.lua b/lua/entities/gmod_wire_digitalscreen/cl_init.lua new file mode 100644 index 0000000000..e54c48a3de --- /dev/null +++ b/lua/entities/gmod_wire_digitalscreen/cl_init.lua @@ -0,0 +1,306 @@ +if (not EmuFox) then + include('shared.lua') +end + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Initialize() + self.Memory1 = {} + self.Memory2 = {} + + self.LastClk = true + self.NewClk = true + self.Memory1[1048575] = 1 + self.Memory2[1048575] = 1 + self.NeedRefresh = true + self.RefreshPixels = {} + self.RefreshRows = {} + + self.ScreenWidth = 32 + self.ScreenHeight = 32 + + for i=1,self.ScreenHeight do + self.RefreshRows[i] = i-1 + end + for i=1,self.ScreenHeight do + self.RefreshRows[i] = i-1 + end + + //0..786431 - RGB data + + //1048569 - Color mode (0: RGBXXX; 1: R G B) + //1048570 - Clear row + //1048571 - Clear column + //1048572 - Screen Height + //1048573 - Screen Width + //1048574 - Hardware Clear Screen + //1048575 - CLK + + WireGPU_NeedRenderTarget(self:EntIndex()) +end + +function ENT:OnRemove() + WireGPU_ReturnRenderTarget(self:EntIndex()) +end + +usermessage.Hook("hispeed_datamessage", function(um) + local ent = ents.GetByIndex(um:ReadShort()) + local datasize = um:ReadChar() + + if ValidEntity(ent) and ent.Memory1 and ent.Memory2 then + for i = 1,datasize do + local address = um:ReadLong() + local value = um:ReadLong() + ent:WriteCell(address,value) + end + end +end) + +function ENT:ReadCell(Address,value) + if Address < 0 then return nil end + if Address >= 1048576 then return nil end + + return self.Memory2[Address] +end + +function ENT:WriteCell(Address,value) + if Address < 0 then return false end + if Address >= 1048576 then return false end + + if (Address == 1048575) then + self.NewClk = value ~= 0 + end + --print("recv: "..Address.." pixs: "..#self.RefreshPixels) + if (self.NewClk) then + self.Memory1[Address] = value -- visible buffer + self.NeedRefresh = true + if self.Memory1[1048569] == 1 then -- R G B mode + local pixelno = math.floor(Address/3) + if self.RefreshPixels[#self.RefreshPixels] ~= pixelno then + self.RefreshPixels[#self.RefreshPixels+1] = pixelno + end + else -- other modes + self.RefreshPixels[#self.RefreshPixels+1] = Address + end + end + self.Memory2[Address] = value -- invisible buffer + + if Address == 1048574 then + local mem1,mem2 = {},{} + for addr = 1048500,1048575 do + mem1[addr] = self.Memory1[addr] + mem2[addr] = self.Memory2[addr] + end + self.Memory1,self.Memory2 = mem1,mem2 + self.NeedRefresh = true + for i = 1,self.ScreenHeight do + self.RefreshRows[i] = i-1 + end + elseif Address == 1048572 then + self.ScreenHeight = value + self.NeedRefresh = true + for i = 1,self.ScreenHeight do + self.RefreshRows[i] = i-1 + end + elseif Address == 1048573 then + self.ScreenWidth = value + self.NeedRefresh = true + for i = 1,self.ScreenHeight do + self.RefreshRows[i] = i-1 + end + end + + if self.LastClk ~= self.NewClk then + -- swap the memory if clock changes + self.LastClk = self.NewClk + self.Memory1 = table.Copy(self.Memory2) + + self.NeedRefresh = true + for i=1,self.ScreenHeight do + self.RefreshRows[i] = i-1 + end + end + return true +end + +local transformcolor = {} +transformcolor[0] = function(c) -- RGBXXX + local crgb = math.floor(c / 1000) + local cgray = c - math.floor(c / 1000)*1000 + + cb = cgray+28*math.fmod(crgb, 10) + cg = cgray+28*math.fmod(math.floor(crgb / 10), 10) + cr = cgray+28*math.fmod(math.floor(crgb / 100), 10) + + return cr, cg, cb +end +transformcolor[2] = function(c) -- 24 bit mode + cb = math.fmod(c, 256) + cg = math.fmod(math.floor(c / 256), 256) + cr = math.fmod(math.floor(c / 65536), 256) + + return cr, cg, cb +end +transformcolor[3] = function(c) -- RRRGGGBBB + cb = math.fmod(c, 1000) + cg = math.fmod(math.floor(c / 1e3), 1000) + cr = math.fmod(math.floor(c / 1e6), 1000) + + return cr, cg, cb +end + +function ENT:RedrawPixel(a) + if a >= self.ScreenWidth*self.ScreenHeight then return end + + local cr,cg,cb + + local x = a % self.ScreenWidth + local y = math.floor(a / self.ScreenWidth) + + local colormode = self.Memory1[1048569] or 0 + + if colormode == 1 then + cr = self.Memory1[a*3+0] or 0 + cg = self.Memory1[a*3+1] or 0 + cb = self.Memory1[a*3+2] or 0 + else + local c = self.Memory1[a] or 0 + cr, cg, cb = (transformcolor[colormode] or transformcolor[0])(c) + end + + local xstep = (512/self.ScreenWidth) + local ystep = (512/self.ScreenHeight) + + surface.SetDrawColor(cr,cg,cb,255) + surface.DrawRect(x*xstep,y*ystep,xstep,ystep) +end + +function ENT:RedrawRow(y) + local xstep = (512/self.ScreenWidth) + local ystep = (512/self.ScreenHeight) + local a = y*self.ScreenWidth + if (a >= self.ScreenWidth*self.ScreenHeight) then return end + + local colormode = self.Memory1[1048569] or 0 + + for x = 0,self.ScreenWidth-1 do + local cr,cg,cb + + if (colormode == 1) then + cr = self.Memory1[(a+x)*3 ] or 0 + cg = self.Memory1[(a+x)*3+1] or 0 + cb = self.Memory1[(a+x)*3+2] or 0 + else + local c = self.Memory1[a+x] or 0 + cr, cg, cb = (transformcolor[colormode] or transformcolor[0])(c) + end + + surface.SetDrawColor(cr,cg,cb,255) + surface.DrawRect(x*xstep,y*ystep,xstep,ystep) + end +end + +function ENT:Draw() + self.Entity:DrawModel() + + self.RTTexture = WireGPU_GetMyRenderTarget(self:EntIndex()) + + local NewRT = self.RTTexture + local OldRT = render.GetRenderTarget() + + local OldTex = WireGPU_matScreen:GetMaterialTexture("$basetexture") + WireGPU_matScreen:SetMaterialTexture("$basetexture",self.RTTexture) + + if self.NeedRefresh then + self.NeedRefresh = false + + local oldw = ScrW() + local oldh = ScrH() + + render.SetRenderTarget(NewRT) + render.SetViewPort(0,0,512,512) + cam.Start2D() + local pixels = 0 + local idx = 1 + + if (#self.RefreshRows > 0) then + idx = #self.RefreshRows + while ((idx > 0) and (pixels < 8192)) do + self:RedrawRow(self.RefreshRows[idx]) + self.RefreshRows[idx] = nil + idx = idx - 1 + pixels = pixels + self.ScreenWidth + end + if (idx == 0) then + self.RefreshRows = {} + end + else + idx = #self.RefreshPixels + while ((idx > 0) and (pixels < 8192)) do + self:RedrawPixel(self.RefreshPixels[idx]) + self.RefreshPixels[idx] = nil + idx = idx - 1 + pixels = pixels + 1 + end + if (idx == 0) then + self.RefreshRows = {} + end + end + cam.End2D() + render.SetViewPort(0,0,oldw,oldh) + render.SetRenderTarget(OldRT) + end + + if EmuFox then return end + + local OF, OU, OR, Res, RatioX, Rot90 + if (WireGPU_Monitors[self.Entity:GetModel()]) && (WireGPU_Monitors[self.Entity:GetModel()].OF) then + OF = WireGPU_Monitors[self.Entity:GetModel()].OF + OU = WireGPU_Monitors[self.Entity:GetModel()].OU + OR = WireGPU_Monitors[self.Entity:GetModel()].OR + Res = WireGPU_Monitors[self.Entity:GetModel()].RS + RatioX = WireGPU_Monitors[self.Entity:GetModel()].RatioX + Rot90 = WireGPU_Monitors[self.Entity:GetModel()].rot90 + else + OF = 0 + OU = 0 + OR = 0 + Res = 1 + RatioX = 1 + end + + local ang = self.Entity:GetAngles() + local rot = Angle(-90,90,0) + if Rot90 then + rot = Angle(0,90,0) + end + + ang:RotateAroundAxis(ang:Right(), rot.p) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.r) + --ang = self:LocalToWorldAngles(rot) + + local pos = self.Entity:GetPos()+(self.Entity:GetForward()*OF)+(self.Entity:GetUp()*OU)+(self.Entity:GetRight()*OR) + + cam.Start3D2D(pos,ang,Res) + local w = 512 + local h = 512 + local x = -w/2 + local y = -h/2 + + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(-256,-256,512/RatioX,512) + + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(WireGPU_texScreen) + WireGPU_DrawScreen(x,y,w/RatioX,h,0,0) + cam.End3D2D() + + WireGPU_matScreen:SetMaterialTexture("$basetexture",OldTex) + Wire_Render(self.Entity) +end + +function ENT:IsTranslucent() + return true +end diff --git a/lua/entities/gmod_wire_digitalscreen/init.lua b/lua/entities/gmod_wire_digitalscreen/init.lua new file mode 100644 index 0000000000..ac72a33415 --- /dev/null +++ b/lua/entities/gmod_wire_digitalscreen/init.lua @@ -0,0 +1,191 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "DigitalScreen" + +function ENT:Initialize() + + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Inputs = Wire_CreateInputs(self.Entity, { "PixelX", "PixelY", "PixelG", "Clk", "FillColor", "ClearRow", "ClearCol" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Memory" }) + + self.Memory = {} + + self.PixelX = 0 + self.PixelY = 0 + self.PixelG = 0 + self.Memory[1048575] = 1 + + self.ScreenWidth = 32 + self.ScreenHeight = 32 + + self.DataCache = WireLib.containers.new(WireLib.containers.deque) + + self.IgnoreDataTransfer = false +end + +function ENT:SetDigitalSize(ScreenWidth, ScreenHeight) + self:WriteCell(1048572, ScreenHeight) + self:WriteCell(1048573, ScreenWidth) +end + +function ENT:SendPixel() + if self.Memory[1048575] == 0 then return end -- why? + if self.PixelX < 0 then return end + if self.PixelY < 0 then return end + if self.PixelX >= self.ScreenWidth then return end + if self.PixelY >= self.ScreenHeight then return end + + local address = self.PixelY*self.ScreenWidth + self.PixelX + self:WriteCell(address, self.PixelG) +end + +function ENT:ReadCell(Address) + if Address < 0 then return nil end + if Address >= 1048576 then return nil end + + return self.Memory[Address] or 0 +end + +local per_tick = 8 +hook.Add("Think", "resetdigitickrate", function() + per_tick = math.min(per_tick+2,8) +end) + +function ENT:FlushCache() + if per_tick <= 0 then return end + if self.DataCache:size() == 0 then return end + + per_tick = per_tick - 1 + umsg.Start("hispeed_datamessage") + umsg.Short(self:EntIndex()) + local bytes = math.min(self.DataCache:size(), 31) + umsg.Char(bytes) + for i = 1,bytes do + local element = self.DataCache:shift() + umsg.Long(element[1]) + umsg.Long(element[2]) + end + + umsg.End() + self:FlushCache() -- re-flush until the quota is used up or the cache is empty +end + +function ENT:ClearPixel(i) + if self.Memory[1048569] == 1 then + -- R G B mode + self.Memory[i*3] = 0 + self.Memory[i*3+1] = 0 + self.Memory[i*3+2] = 0 + return + end + + -- other modes + self.Memory[i] = 0 +end + +function ENT:WriteCell(Address, value) + if Address < 0 then return false end + if Address >= 1048576 then return false end + + if Address < 1048500 then -- RGB data + if self.Memory[Address] == value then return true end + else + if Address == 1048569 then -- Color mode (0: RGBXXX; 1: R G B; 2: 24 bit RGB; 3: RRRGGGBBB) + -- not needed (yet) + elseif Address == 1048570 then -- Clear row + local row = math.Clamp(value, 0, self.ScreenHeight-1) + for i = row*self.ScreenWidth,(row+1)*self.ScreenWidth-1 do + self:ClearPixel(i) + end + elseif Address == 1048571 then -- Clear column + local col = math.Clamp(value, 0, self.ScreenWidth-1) + for i = col,col+self.ScreenWidth*(self.ScreenHeight-1),self.ScreenWidth do + self:ClearPixel(i) + end + elseif Address == 1048572 then -- Height + self.ScreenHeight = math.Clamp(math.floor(value), 1, 512) + elseif Address == 1048573 then -- Width + self.ScreenWidth = math.Clamp(math.floor(value), 1, 512) + elseif Address == 1048574 then -- Hardware Clear Screen + for i = 0,self.ScreenWidth*self.ScreenHeight-1 do + self:ClearPixel(i) + end + elseif Address == 1048575 then -- CLK + -- not needed atm + end + end + + self.Memory[Address] = value + + self.DataCache:push({ Address, value }) + + if per_tick > 0 and self.DataCache:size() >= 31 then + self:FlushCache() + self.IgnoreDataTransfer = true + end + return true +end + +function ENT:Think() + if (self.IgnoreDataTransfer == true) then + self.IgnoreDataTransfer = false + self.Entity:NextThink(CurTime()+0.2) + else + self:FlushCache() + self.Entity:NextThink(CurTime()+0.1) + end + return true +end + +function ENT:TriggerInput(iname, value) + if (iname == "PixelX") then + self.PixelX = math.floor(value) + self:SendPixel() + elseif (iname == "PixelY") then + self.PixelY = math.floor(value) + self:SendPixel() + elseif (iname == "PixelG") then + self.PixelG = math.floor(value) + self:SendPixel() + elseif (iname == "Clk") then + self:WriteCell(1048575, value) + self:SendPixel() + elseif (iname == "FillColor") then + self:WriteCell(1048574,value) + elseif (iname == "ClearCol") then + self:WriteCell(1048571,math.Clamp( value, 0, 31 )) + elseif (iname == "ClearRow") then + self:WriteCell(1048570,math.Clamp( value, 0, 31 )) + end +end + + +function MakeWireDigitalScreen( pl, Pos, Ang, model, ScreenWidth, ScreenHeight ) + + if ( !pl:CheckLimit( "wire_digitalscreens" ) ) then return false end + + local wire_digitalscreen = ents.Create( "gmod_wire_digitalscreen" ) + if (!wire_digitalscreen:IsValid()) then return false end + wire_digitalscreen:SetModel(model) + + if (not ScreenWidth) then ScreenWidth = 32 end + if (not ScreenHeight) then ScreenHeight = 32 end + + wire_digitalscreen:SetAngles( Ang ) + wire_digitalscreen:SetPos( Pos ) + wire_digitalscreen:Spawn() + wire_digitalscreen:SetDigitalSize(ScreenWidth,ScreenHeight) + + wire_digitalscreen:SetPlayer(pl) + + pl:AddCount( "wire_digitalscreens", wire_digitalscreen ) + + return wire_digitalscreen +end + +duplicator.RegisterEntityClass("gmod_wire_digitalscreen", MakeWireDigitalScreen, "Pos", "Ang", "Model", "ScreenWidth", "ScreenHeight") diff --git a/lua/entities/gmod_wire_digitalscreen/shared.lua b/lua/entities/gmod_wire_digitalscreen/shared.lua new file mode 100644 index 0000000000..cbcd87878a --- /dev/null +++ b/lua/entities/gmod_wire_digitalscreen/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Digital Screen" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_dual_input/cl_init.lua b/lua/entities/gmod_wire_dual_input/cl_init.lua new file mode 100644 index 0000000000..e719186ab9 --- /dev/null +++ b/lua/entities/gmod_wire_dual_input/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_dual_input/init.lua b/lua/entities/gmod_wire_dual_input/init.lua new file mode 100644 index 0000000000..c388d6f4ca --- /dev/null +++ b/lua/entities/gmod_wire_dual_input/init.lua @@ -0,0 +1,116 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "DualInput" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) +end + +function ENT:Setup(keygroup, keygroup2, toggle, value_off, value_on, value_on2) + self.keygroup = keygroup + self.keygroup2 = keygroup2 + self.toggle = (toggle == 1 || toggle == true) + self.value_off = value_off + self.value_on = value_on + self.value_on2 = value_on2 + self.Value = value_off + self.Select = 0 + + self:ShowOutput(self.value_off) + Wire_TriggerOutput(self.Entity, "Out", self.value_off) +end + +function ENT:InputActivate( mul ) + if ( self.toggle && self.Select == mul ) then + return self:Switch( !self.On, mul ) + end + + return self:Switch( true, mul ) +end + +function ENT:InputDeactivate( mul ) + if ( self.toggle ) then return true end + + return self:Switch( false, mul ) +end + +function ENT:Switch( on, mul ) + if (!self.Entity:IsValid()) then return false end + + self.On = on + self.Select = mul + + if (on && mul == 1) then + self:ShowOutput(self.value_on) + self.Value = self.value_on + elseif (on && mul == -1) then + self:ShowOutput(self.value_on2) + self.Value = self.value_on2 + else + self:ShowOutput(self.value_off) + self.Value = self.value_off + end + + Wire_TriggerOutput(self.Entity, "Out", self.Value) + + return true +end + +function ENT:ShowOutput(value) + self:SetOverlayText( "(" .. self.value_on2 .. " - " .. self.value_off .. " - " .. self.value_on .. ") = " .. value ) +end + +local function On( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputActivate( mul ) +end + +local function Off( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputDeactivate( mul ) +end + +numpad.Register( "WireDualInput_On", On ) +numpad.Register( "WireDualInput_Off", Off ) + + +function MakeWireDualInput( pl, Pos, Ang, model, keygroup, keygroup2, toggle, value_off, value_on, value_on2, frozen ) + if ( !pl:CheckLimit( "wire_dual_inputs" ) ) then return false end + + local wire_dual_input = ents.Create( "gmod_wire_dual_input" ) + if (!wire_dual_input:IsValid()) then return false end + + wire_dual_input:SetAngles( Ang ) + wire_dual_input:SetPos( Pos ) + wire_dual_input:SetModel( Model(model or "models/jaanus/wiretool/wiretool_input.mdl") ) + wire_dual_input:Spawn() + + if wire_dual_input:GetPhysicsObject():IsValid() then + local Phys = wire_dual_input:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + wire_dual_input:Setup( keygroup, keygroup2, toggle, value_off, value_on, value_on2 ) + wire_dual_input:SetPlayer( pl ) + + numpad.OnDown( pl, keygroup, "WireDualInput_On", wire_dual_input, 1 ) + numpad.OnUp( pl, keygroup, "WireDualInput_Off", wire_dual_input, 1 ) + + numpad.OnDown( pl, keygroup2, "WireDualInput_On", wire_dual_input, -1 ) + numpad.OnUp( pl, keygroup2, "WireDualInput_Off", wire_dual_input, -1 ) + + pl:AddCount( "wire_dual_inputs", wire_dual_input ) + + return wire_dual_input +end + +duplicator.RegisterEntityClass("gmod_wire_dual_input", MakeWireDualInput, "Pos", "Ang", "Model", "keygroup", "keygroup2", "toggle", "value_off", "value_on", "value_on2", "frozen") diff --git a/lua/entities/gmod_wire_dual_input/shared.lua b/lua/entities/gmod_wire_dual_input/shared.lua new file mode 100644 index 0000000000..2e1bf63622 --- /dev/null +++ b/lua/entities/gmod_wire_dual_input/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Dual Input" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_emarker/cl_init.lua b/lua/entities/gmod_wire_emarker/cl_init.lua new file mode 100644 index 0000000000..b611a89fdd --- /dev/null +++ b/lua/entities/gmod_wire_emarker/cl_init.lua @@ -0,0 +1,5 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_emarker/init.lua b/lua/entities/gmod_wire_emarker/init.lua new file mode 100644 index 0000000000..5a3569a56d --- /dev/null +++ b/lua/entities/gmod_wire_emarker/init.lua @@ -0,0 +1,56 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "EMarker" +ENT.OverlayDelay = 0 + +local MODEL = Model( "models/jaanus/wiretool/wiretool_siren.mdl" ) + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + Add_EntityMarker(self.Entity) + + self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, { "Entity" }, { "ENTITY" }) + self:SetOverlayText( "No Mark selected" ) +end + +function ENT:LinkEMarker(mark) + if mark then self.mark = mark end + if (!self.mark || !self.mark:IsValid()) then self:SetOverlayText( "No Mark selected" ) return end + Wire_TriggerOutput(self.Entity, "Entity", self.mark) + self:SetOverlayText( "Linked - " .. self.mark:GetModel() ) +end + +function ENT:UnLinkEMarker() + self.mark = null + Wire_TriggerOutput(self.Entity, "Entity", null) + self:SetOverlayText( "No Mark selected" ) +end + +// Advanced Duplicator Support + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if ( self.mark ) and ( self.mark:IsValid() ) then + info.mark = self.mark:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.mark) then + self.mark = GetEntByID(info.mark) + if (!self.mark) then + self.mark = ents.GetByIndex(info.mark) + end + end + self:LinkEMarker() +end diff --git a/lua/entities/gmod_wire_emarker/shared.lua b/lua/entities/gmod_wire_emarker/shared.lua new file mode 100644 index 0000000000..f58285572f --- /dev/null +++ b/lua/entities/gmod_wire_emarker/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Entity Marker" +ENT.Author = "Shandolum" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_explosive/cl_init.lua b/lua/entities/gmod_wire_explosive/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_explosive/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_explosive/init.lua b/lua/entities/gmod_wire_explosive/init.lua new file mode 100644 index 0000000000..2f01d30125 --- /dev/null +++ b/lua/entities/gmod_wire_explosive/init.lua @@ -0,0 +1,326 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Explosive" +ENT.OverlayDelay = 0 + +/*--------------------------------------------------------- + Name: Initialize + Desc: First function called. Use to set up your entity +---------------------------------------------------------*/ +function ENT:Initialize() + + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + self.exploding = false + self.reloading = false + self.NormInfo = "" + self.count = 0 + self.ExplodeTime = 0 + self.ReloadTime = 0 + self.CountTime = 0 + + self.Inputs = Wire_CreateInputs(self.Entity, { "Detonate", "ResetHealth" }) + + self.Entity:SetMaxHealth(100) + self.Entity:SetHealth(100) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Health" }) +end + +/*--------------------------------------------------------- + Name: TriggerInput + Desc: the inputs +---------------------------------------------------------*/ +function ENT:TriggerInput(iname, value) + if (iname == "Detonate") then + if ( !self.exploding && !self.reloading ) then + if ( math.abs(value) == self.key ) then + self:Trigger() + end + end + elseif (iname == "ResetHealth") then + self:ResetHealth() + end +end + +/*--------------------------------------------------------- + Name: Setup + Desc: does a whole lot of setting up +---------------------------------------------------------*/ +function ENT:Setup( damage, delaytime, removeafter, doblastdamage, radius, affectother, notaffected, delayreloadtime, maxhealth, bulletproof, explosionproof, fallproof, explodeatzero, resetatexplode, fireeffect, coloreffect, invisibleatzero, nocollide ) + + self.Damage = math.Clamp( damage, 0, 1500 ) + self.Delaytime = delaytime + self.Removeafter = removeafter + self.DoBlastDamage = doblastdamage + self.Radius = math.min(512,math.max(radius, 1)) + self.Affectother = affectother + self.Notaffected = notaffected + self.Delayreloadtime = delayreloadtime + + self.BulletProof = bulletproof + self.ExplosionProof = explosionproof + self.FallProof = fallproof + + self.ExplodeAtZero = explodeatzero + self.ResetAtExplode = resetatexplode + + self.FireEffect = fireeffect + self.ColorEffect = coloreffect + self.NoCollide = nocollide + self.InvisibleAtZero = invisibleatzero + + self.Entity:SetMaxHealth(maxhealth) + self:ResetHealth() + + + //self.Entity:SetHealth(maxhealth) + //Wire_TriggerOutput(self.Entity, "Health", maxhealth) + + //reset everthing back and try to stop exploding + //self.exploding = false + //self.reloading = false + //self.count = 0 + //self.Entity:Extinguish() + //if (self.ColorEffect) then self.Entity:SetColor(255, 255, 255, 255) end + + self.NormInfo = "Explosive" + if (self.DoBlastDamage) then self.NormInfo = self.NormInfo.." (Damage: "..self.Damage..")" end + if (self.Radius > 0 || self.Delaytime > 0) then self.NormInfo = self.NormInfo.."\n" end + if (self.Radius > 0 ) then self.NormInfo = self.NormInfo.." Rad: "..self.Radius end + if (self.Delaytime > 0) then self.NormInfo = self.NormInfo.." Delay: "..self.Delaytime end + + self:ShowOutput() + +end + +function ENT:ResetHealth( ) + self.Entity:SetHealth( self.Entity:GetMaxHealth() ) + Wire_TriggerOutput(self.Entity, "Health", self.Entity:GetMaxHealth()) + + //put the fires out and try to stop exploding + self.exploding = false + self.reloading = false + self.count = 0 + self.Entity:Extinguish() + + if (self.ColorEffect) then self.Entity:SetColor(255, 255, 255, 255) end + + self.Entity:SetNoDraw( false ) + + if (self.NoCollide) then + self.Entity:SetCollisionGroup(COLLISION_GROUP_WORLD) + else + self.Entity:SetCollisionGroup(COLLISION_GROUP_NONE) + end + + self:ShowOutput() +end + + +/*--------------------------------------------------------- + Name: OnTakeDamage + Desc: Entity takes damage +---------------------------------------------------------*/ +function ENT:OnTakeDamage( dmginfo ) + + if ( dmginfo:GetInflictor():GetClass() == "gmod_wire_explosive" && !self.Affectother ) then return end + + if ( !self.Notaffected ) then self.Entity:TakePhysicsDamage( dmginfo ) end + + if (dmginfo:IsBulletDamage() && self.BulletProof) || + (dmginfo:IsExplosionDamage() && self.ExplosionProof) || + (dmginfo:IsFallDamage() && self.FallProof) then return end //fix fall damage, it doesn't happen + + if (self.Entity:Health() > 0) then //don't need to beat a dead horse + local dammage = dmginfo:GetDamage() + local h = self.Entity:Health() - dammage + if (h < 0) then h = 0 end + self.Entity:SetHealth(h) + Wire_TriggerOutput(self.Entity, "Health", h) + self:ShowOutput() + if (self.ColorEffect) then + if (h == 0) then c = 0 else c = 255 * (h / self.Entity:GetMaxHealth()) end + self.Entity:SetColor(255, c, c, 255) + end + if (h == 0) then + if (self.ExplodeAtZero) then self:Trigger() end //oh shi-- + end + end + +end + +/*--------------------------------------------------------- + Name: Trigger + Desc: Start exploding +---------------------------------------------------------*/ +function ENT:Trigger() + if ( self.Delaytime > 0 ) then + //self.exploding = true + self.ExplodeTime = CurTime() + self.Delaytime + if (self.FireEffect) then self.Entity:Ignite((self.Delaytime + 3),0) end +/* timer.Simple( self.Delaytime, self.Explode, self ) + //self.count = self.Delaytime + //self:Countdown() + //else + //self.exploding = true + //self:Explode() +*/ + end + self.exploding = true + // Force reset of counter + self.CountTime = 0 +end + +/*--------------------------------------------------------- + Name: Think + Desc: Thinks :P +---------------------------------------------------------*/ +function ENT:Think() + self.BaseClass.Think(self) + + if (self.exploding) then + if (self.ExplodeTime < CurTime()) then + self:Explode() + end + elseif (self.reloading) then + if (self.ReloadTime < CurTime()) then + self.reloading = false + if (self.ResetAtExplode) then + self:ResetHealth() + else + self:ShowOutput() + end + end + end + + // Do count check to ensure that + // ShowOutput() is called every second + // when exploding or reloading + if ((self.CountTime or 0) < CurTime()) then + local temptime = 0 + if (self.exploding) then + temptime = self.ExplodeTime + elseif (self.reloading) then + temptime = self.ReloadTime + end + + if (temptime > 0) then + self.count = math.ceil(temptime - CurTime()) + self:ShowOutput() + end + + self.CountTime = CurTime() + 1 + end + + self.Entity:NextThink(CurTime() + 0.05) + return true +end + +/*--------------------------------------------------------- + Name: Explode + Desc: is one needed? +---------------------------------------------------------*/ +function ENT:Explode( ) + + if ( !self.Entity:IsValid() ) then return end + + self.Entity:Extinguish() + + if (!self.exploding) then return end //why are we exploding if we shouldn't be + + ply = self:GetPlayer() or self.Entity + if(not ValidEntity(ply)) then ply = self.Entity end; + + if (self.InvisibleAtZero) then + ply:SetCollisionGroup(COLLISION_GROUP_DEBRIS) + ply:SetNoDraw( true ) + ply:SetColor(255, 255, 255, 0) + end + + if ( self.DoBlastDamage ) then + util.BlastDamage( self.Entity, ply, self.Entity:GetPos(), self.Radius, self.Damage ) + end + + local effectdata = EffectData() + effectdata:SetOrigin( self.Entity:GetPos() ) + util.Effect( "Explosion", effectdata, true, true ) + + if ( self.Removeafter ) then + self.Entity:Remove() + return + end + + self.exploding = false + + self.reloading = true + self.ReloadTime = CurTime() + math.max(1, self.Delayreloadtime) + // Force reset of counter + self.CountTime = 0 + self:ShowOutput() + + /*if ( self.Delayreloadtime > 0 ) then + //t = self.Delayreloadtime + 1 + self.reloading = true + //timer.Simple( self.Delayreloadtime, self.Reloaded, self ) + //self.count = self.Delayreloadtime + //self:Countdown() + else //keep it from going off again for at least another second + self.reloading = true + timer.Simple( 1, self.Reloaded, self ) + self:ShowOutput(0) + end*/ + +end + +/* Don't need these anymore +function ENT:Reloaded( ) + self.reloading = false + if (self.ResetAtExplode) then self:ResetHealth() end +end + +function ENT:Countdown( ) + self:ShowOutput() + self.count = self.count - 1 + if ( self.count > 0 ) then //theres still time left + timer.Simple( 1, self.Countdown, self ) + else //will be done after this second + timer.Simple( 1, self.ShowOutput, self ) + end +end +*/ + +/*--------------------------------------------------------- + Name: ShowOutput + Desc: don't foreget to call this when changes happen +---------------------------------------------------------*/ +function ENT:ShowOutput( ) + local txt = "" + if (self.reloading && self.Delayreloadtime > 0) then + txt = "Rearming... "..self.count + if (self.ColorEffect && !self.InvisibleAtZero) then + if (self.count == 0) then c = 255 else c = 255 * ((self.Delayreloadtime - self.count) / self.Delayreloadtime) end + self.Entity:SetColor(255, c, c, 255) + end + if (self.InvisibleAtZero) then + ply:SetNoDraw( false ) + if (self.count == 0) then c = 255 else c = 255 * ((self.Delayreloadtime - self.count) / self.Delayreloadtime) end + self.Entity:SetColor(255, 255, 255, c) + end + elseif (self.exploding) then + txt = "Triggered... "..self.count + else + txt = self.NormInfo.."\nHealth: "..self.Entity:Health().."/"..self.Entity:GetMaxHealth() + end + self:SetOverlayText(txt) +end diff --git a/lua/entities/gmod_wire_explosive/shared.lua b/lua/entities/gmod_wire_explosive/shared.lua new file mode 100644 index 0000000000..5abfa6398e --- /dev/null +++ b/lua/entities/gmod_wire_explosive/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Explosive" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_expression/cl_init.lua b/lua/entities/gmod_wire_expression/cl_init.lua new file mode 100644 index 0000000000..eaa0d52403 --- /dev/null +++ b/lua/entities/gmod_wire_expression/cl_init.lua @@ -0,0 +1,7 @@ +// Written by Syranide, me@syranide.com + +include('shared.lua') +include('parser.lua') -- should be removed, or? + +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_expression/init.lua b/lua/entities/gmod_wire_expression/init.lua new file mode 100644 index 0000000000..f43c10ce38 --- /dev/null +++ b/lua/entities/gmod_wire_expression/init.lua @@ -0,0 +1,448 @@ +// Written by Syranide, me@syranide.com + +// Found some extremely rare bug at line 170 (delta), probably got set to nil for some reason. + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') +include('parser.lua') + +ENT.Delta = 0.001 +ENT.OverlayDelay = 0 +ENT.WireDebugName = "Expression" + +if !WireModPacket then + WireModPacket = {} + WireModPacketIndex = 0 +end + +if !WireModVector then + WireModVector = {} + WireModVectorIndex = 0 +end + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Xinputs = {} + self.Xoutputs = {} + self.Xlocals = {} + + self.deltavars = {} + self.inputvars = {} + self.triggvars = {} + self.variables = {} + + self.Inputs = Wire_CreateInputs(self.Entity, {}) + self.Outputs = Wire_CreateOutputs(self.Entity, {}) +end + +function ENT:TriggerInput(key, value) + if key then + self.deltavars[key] = self.inputvars[key] + self.inputvars[key] = value + self.triggvars[key] = true + self:Update() + --self.Entity:NextThink(CurTime()+0.001) + end +end + +function ENT:Think() + if !self.scheduled then return true end + + self.curtime = self.curtime + self.scheduled / 1000 + if math.abs(self.curtime - CurTime()) > 0.1 then self.curtime = CurTime() end + + self.scheduled = nil + self.clocked = true + self:Update() + self.clocked = nil + + if !self.scheduled then self.curtime = nil end + + return true +end + +function ENT:Update() + for _,key in pairs(self.Xinputs) do + self.variables[key] = self.inputvars[key] + end + + self.schedule = nil + + local tbl = self.instructions + self["_"..tbl[1]](self,tbl) + self.triggvars = {} + + if self.schedule and math.abs(self.schedule) >= self.Delta then + if !self.clocked and self.schedule > 0 or !self.curtime then self.curtime = CurTime() end + if self.schedule < 0 then self.schedule = -self.schedule end + if self.schedule < 20 then self.schedule = 20 end + self.scheduled = self.schedule + self.Entity:NextThink(self.curtime + self.schedule / 1000) + elseif self.scheduled and self.schedule and math.abs(self.schedule) < self.Delta then + self.scheduled = nil + end + + for _,key in ipairs(self.Xoutputs) do + Wire_TriggerOutput(self.Entity, key, self.variables[key]) --major overhead, add lazy updates? + end +end + +function ENT:Reset() + for _,key in ipairs(self.Xlocals) do self.variables[key] = 0 self.deltavars[key] = 0 end + for _,key in ipairs(self.Xoutputs) do self.variables[key] = 0 self.deltavars[key] = 0 end + + self.scheduled = nil + self.schedule = nil + self.curtime = nil + self.initialized = false + + self:Update() + + self.initialized = true +end + +function ENT:Setup(name, parser) + local inputs = parser:GetInputs() + local outputs = parser:GetOutputs() + local locals = parser:GetLocals() + + local inputvars = {} + local deltavars = {} + local triggvars = {} + local variables = {} + + for _,key in ipairs(inputs) do + if !self.inputvars[key] then + inputvars[key] = 0 + deltavars[key] = 0 + else + inputvars[key] = self.inputvars[key] + deltavars[key] = self.deltavars[key] + end + end + + for _,key in ipairs(outputs) do + if !self.variables[key] then + variables[key] = 0 + deltavars[key] = 0 + else + variables[key] = self.variables[key] + deltavars[key] = self.deltavars[key] + end + end + + for _,key in ipairs(locals) do + if !self.variables[key] then + variables[key] = 0 + deltavars[key] = 0 + else + variables[key] = self.variables[key] + deltavars[key] = self.deltavars[key] + end + end + + self.inputvars = inputvars + self.deltavars = deltavars + self.triggvars = triggvars + self.variables = variables + + self.Xinputs = inputs + self.Xoutputs = outputs + self.Xlocals = locals + + self.instructions = parser:GetInstructions() + + Wire_AdjustInputs(self.Entity, inputs) + Wire_AdjustOutputs(self.Entity, outputs) + + if name == "" then name = "generic" end + self:SetOverlayText("Expression (" .. name .. ")") + + self.scheduled = nil + self.schedule = nil + self.curtime = nil + self.initialized = false + + self:Update() + + self.initialized = true + return true +end + +--function ENT:Compile(tbl) +-- if type(tbl) == "table" then +-- tbl[1] = self["_" .. tbl[1]] +-- for i=2,#tbl do tbl[i] = self:Compile(tbl[i]) end +-- end +-- return tbl +--end + +function ENT:_end(tbl) return false end + +function ENT:_num(tbl) return tbl[2] end +function ENT:_var(tbl) return self.variables[tbl[2]] end +function ENT:_dlt(tbl) return self.variables[tbl[2]] - self.deltavars[tbl[2]] end +function ENT:_trg(tbl) if self.triggvars[tbl[2]] then return 1 else return 0 end end + +function ENT:_seq(tbl) if self["_"..tbl[2][1]](self,tbl[2]) == false then return false elseif self["_"..tbl[3][1]](self,tbl[3]) == false then return false end end + +function ENT:_fun(tbl) local prm = self["_"..tbl[3][1]](self,tbl[3]) local fun = self["_"..tbl[2].."_"..#prm] if !fun and #prm > 0 then fun = self["_"..tbl[2].."_x"] end if fun == nil then return -1 end return fun(self,unpack(prm)) end +function ENT:_prm(tbl) local prm = self["_"..tbl[2][1]](self,tbl[2]) table.insert(prm, self["_"..tbl[3][1]](self,tbl[3])) return prm end +function ENT:_nil(tbl) return {} end + +function ENT:_con(tbl) + /*if self:GetPlayer() then + local instr = tbl[2] + local outstr = "" + + while true do + local pos = string.find(instr, "$", 1, true) + if pos then + outstr = outstr .. string.sub(instr, 0, pos - 1) + instr = string.sub(instr, pos + 1) + + local pos = string.find(instr, "$", 1, true) + if pos then + local var = string.sub(instr, 0, pos - 1) + if self.variables[var] then + outstr = outstr .. tostring(self.variables[var]) + else + outstr = outstr .. "0" + end + + instr = string.sub(instr, pos + 1) + end + else + outstr = outstr .. instr + break + end + end + + self:GetPlayer():ConCommand(outstr) + end*/ +end + +function ENT:_imp(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2])) >= self.Delta then self.elser = 0 if self["_"..tbl[3][1]](self,tbl[3]) == false then return false end else self.elser = 1 end end +function ENT:_cnd(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2])) >= self.Delta then return self["_"..tbl[3][1]](self,tbl[3]) else return self["_"..tbl[4][1]](self,tbl[4]) end end + +function ENT:_and(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2])) >= self.Delta and math.abs(self["_"..tbl[3][1]](self,tbl[3])) >= self.Delta then return 1 else return 0 end end +function ENT:_or(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2])) >= self.Delta or math.abs(self["_"..tbl[3][1]](self,tbl[3])) >= self.Delta then return 1 else return 0 end end + +function ENT:_ass(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end +function ENT:_aadd(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self.variables[tbl[2][2]] + self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end +function ENT:_asub(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self.variables[tbl[2][2]] - self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end +function ENT:_amul(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self.variables[tbl[2][2]] * self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end +function ENT:_adiv(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self.variables[tbl[2][2]] / self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end +function ENT:_amod(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self.variables[tbl[2][2]] % self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end +function ENT:_aexp(tbl) self.triggvars[tbl[2][2]] = true self.deltavars[tbl[2][2]] = self.variables[tbl[2][2]] self.variables[tbl[2][2]] = self.variables[tbl[2][2]] ^ self["_"..tbl[3][1]](self,tbl[3]) return self.variables[tbl[2][2]] end + +function ENT:_eq(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3])) < self.Delta then return 1 else return 0 end end +function ENT:_neq(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3])) >= self.Delta then return 1 else return 0 end end + +function ENT:_gth(tbl) if self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3]) >= self.Delta then return 1 else return 0 end end +function ENT:_lth(tbl) if self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3]) <= -self.Delta then return 1 else return 0 end end +function ENT:_geq(tbl) if self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3]) > -self.Delta then return 1 else return 0 end end +function ENT:_leq(tbl) if self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3]) < self.Delta then return 1 else return 0 end end + +function ENT:_neg(tbl) return - self["_"..tbl[2][1]](self,tbl[2]) end +function ENT:_add(tbl) return self["_"..tbl[2][1]](self,tbl[2]) + self["_"..tbl[3][1]](self,tbl[3]) end +function ENT:_sub(tbl) return self["_"..tbl[2][1]](self,tbl[2]) - self["_"..tbl[3][1]](self,tbl[3]) end +function ENT:_mul(tbl) return self["_"..tbl[2][1]](self,tbl[2]) * self["_"..tbl[3][1]](self,tbl[3]) end +function ENT:_div(tbl) return self["_"..tbl[2][1]](self,tbl[2]) / self["_"..tbl[3][1]](self,tbl[3]) end +function ENT:_mod(tbl) return self["_"..tbl[2][1]](self,tbl[2]) % self["_"..tbl[3][1]](self,tbl[3]) end +function ENT:_exp(tbl) return self["_"..tbl[2][1]](self,tbl[2]) ^ self["_"..tbl[3][1]](self,tbl[3]) end + +function ENT:_not(tbl) if math.abs(self["_"..tbl[2][1]](self,tbl[2])) < self.Delta then return 1 else return 0 end end + +ENT._else_0 = function (self, n) return self.elser end +ENT._abs_1 = function (self, n) return math.abs(n) end +ENT._ceil_1 = function (self, n) return math.ceil(n) end +ENT._ceil_2 = function (self, n, d) return math.ceil(n * 10 ^ d) / 10 ^ d end +ENT._clamp_3 = function (self, v, l, u) if v < l then return l elseif v > u then return u else return v end end +ENT._inrange_3 = function (self, v, l, u) if v >= l and v <= u then return 1 else return 0 end end +ENT._exp_1 = function (self, n) return math.exp(n) end +ENT._floor_1 = function (self, n) return math.floor(n) end +ENT._floor_2 = function (self, n, d) return math.floor(n * 10 ^ d) / 10 ^ d end +ENT._frac_1 = function (self, n) return math.fmod(n, 1) end +ENT._int_1 = function (self, n) return math.modf(n, 1) end +ENT._ln_1 = function (self, n) return math.log(n) / math.log(math.exp(1)) end +ENT._log_2 = function (self, n, k) return math.log(n) / math.log(k) end +ENT._log2_1 = function (self, n) return math.log(n) end +ENT._log10_1 = function (self, n) return math.log10(n) end +ENT._mod_2 = function (self, x, y) return math.fmod(x, y) end +ENT._sgn_1 = function (self, n) if n > 0 then return 1 elseif n < 0 then return -1 else return 0 end end +ENT._sqrt_1 = function (self, n) return math.sqrt(n) end +ENT._cbrt_1 = function (self, n) if n > 0 then return n ^ (1 / 3) else return math.abs(n) ^ (1 / 3) end end +ENT._root_1 = function (self, n, k) if n > 0 || k % 2 == 0 then return n ^ (1 / k) else return math.abs(n) ^ (1 / k) end end +ENT._round_1 = function (self, n) return math.Round(n) end +ENT._round_2 = function (self, n, d) return math.Round(n * 10 ^ d) / 10 ^ d end +ENT._e_0 = function (self) return math.exp(1) end + +ENT._max_x = function (self, ...) return math.max(...) end +ENT._min_x = function (self, ...) return math.min(...) end +ENT._avg_x = function (self, ...) local n = 0 for _,v in ipairs({...}) do n = n + v end return n / #{...} end +ENT._sel_x = function (self, i, ...) if ({...})[i] == nil then return -1 else return ({...})[i] end end + +ENT._random_0 = function (self) return math.random() end +ENT._random_2 = function (self, l, u) return math.random() * (u - l) + l end +ENT._curtime_0 = function (self) return CurTime() end + +ENT._deg_1 = function (self, r) return math.deg(r) end +ENT._rad_1 = function (self, d) return math.rad(d) end +ENT._pi_0 = function (self) return math.pi end + +ENT._acosr_1 = function (self, x) return math.acos(x) end +ENT._asinr_1 = function (self, x) return math.asin(x) end +ENT._atanr_2 = function (self, x, y) return math.atan2(x, y) end +ENT._atan2r_2 = function (self, x, y) return math.atan2(x, y) end +ENT._atanr_1 = function (self, x) return math.atan(x) end +ENT._coshr_1 = function (self, r) return math.cosh(r) end +ENT._cosr_1 = function (self, r) return math.cos(r) end +ENT._sinr_1 = function (self, r) return math.sin(r) end +ENT._sinhr_1 = function (self, r) return math.sinh(r) end +ENT._tanr_1 = function (self, r) return math.tan(r) end +ENT._tanhr_1 = function (self, r) return math.tanh(r) end + +ENT._acos_1 = function (self, x) return math.deg(math.acos(x)) end +ENT._asin_1 = function (self, x) return math.deg(math.asin(x)) end +ENT._atan_2 = function (self, x, y) return math.deg(math.atan2(x, y)) end +ENT._atan2_2 = function (self, x, y) return math.deg(math.atan2(x, y)) end +ENT._atan_1 = function (self, x) return math.deg(math.atan(x)) end +ENT._cosh_1 = function (self, d) return math.cosh(math.rad(d)) end +ENT._cos_1 = function (self, d) return math.cos(math.rad(d)) end +ENT._sin_1 = function (self, d) return math.sin(math.rad(d)) end +ENT._sinh_1 = function (self, d) return math.sinh(math.rad(d)) end +ENT._tan_1 = function (self, d) return math.tan(math.rad(d)) end +ENT._tanh_1 = function (self, d) return math.tanh(math.rad(d)) end + +ENT._angnorm_1 = function (self, d) return (d + 180) % 360 - 180 end +ENT._angnormr_1 = function (self, d) return (d + math.pi) % (math.pi * 2) - math.pi end + +ENT._first_0 = function (self, n) if self.initialized then return 0 else return 1 end end + +ENT._clk_0 = function (self, n) if self.clocked then return 1 else return 0 end end +ENT._schedule_1 = function (self, n) self.schedule = n return 0 end +ENT._interval_1 = function (self, n) self.schedule = -n return 0 end + +ENT._send_x = function (self, ...) WireModPacketIndex = WireModPacketIndex % 90 + 1 WireModPacket[WireModPacketIndex] = {...} return WireModPacketIndex + 9 end +ENT._recv_2 = function (self, id, p) id = id - 9 if WireModPacket[id] and WireModPacket[id][p] then return WireModPacket[id][p] else return -1 end end + +function WireGateExpressionSendPacket(...) + WireModPacketIndex = WireModPacketIndex % 90 + 1 + WireModPacket[WireModPacketIndex] = {...} + return WireModPacketIndex + 9 +end + +function WireGateExpressionRecvPacket(packet, index) + packet = packet - 9 + if WireModPacket[packet] and WireModPacket[packet][index] then + return WireModPacket[packet][index] + else + return nil + end +end + +-- HIGHLY EXPERIMENTAL FUNCTIONALITY -- + +function ENT:GetVector(id) + return WireModVector[id - 9] +end + +function ENT:PutVector(v) + WireModVectorIndex = WireModVectorIndex % 90 + 1 + WireModVector[WireModVectorIndex] = v + return WireModVectorIndex + 9 +end + +function ENT:CloneVector(v) + return Vector(v.x, v.y, v.z) +end + +function ENT:IsVector(id1, id2) + id1 = id1 - 9 + if !WireModVector[id1] then return false end + + if id2 then + id2 = id2 - 9 + if !WireModVector[id2] then return false end + end + + return true +end + + +ENT._vector_2 = function (self, x, y) return self:PutVector(Vector(x, y, 0)) end +ENT._vector_3 = function (self, x, y, z) return self:PutVector(Vector(x, y, z)) end + +ENT._vecx_1 = function (self, v) if self:IsVector(v) then return self:GetVector(v).x else return -1 end end +ENT._vecy_1 = function (self, v) if self:IsVector(v) then return self:GetVector(v).y else return -1 end end +ENT._vecz_1 = function (self, v) if self:IsVector(v) then return self:GetVector(v).z else return -1 end end + +ENT._vecpitch_1 = function (self, v) if self:IsVector(v) then return (self:GetVector(v):Angle().p + 180) % 360 - 180 else return -1 end end +ENT._vecyaw_1 = function (self, v) if self:IsVector(v) then return (self:GetVector(v):Angle().y + 180) % 360 - 180 else return -1 end end + +ENT._veclength_1 = function (self, v) if self:IsVector(v) then return self:GetVector(v):Length() else return -1 end end +ENT._vecnormalize_1 = function (self, v) if self:IsVector(v) then return self:PutVector(self:GetVector(v):GetNormalized()) else return -1 end end + +ENT._vecdot_2 = function (self, v1, v2) if self:IsVector(v1, v2) then return self:GetVector(v1):Dot(self:GetVector(v2)) else return -1 end end +ENT._veccross_2 = function (self, v1, v2) if self:IsVector(v1, v2) then return self:PutVector(self:GetVector(v1):Cross(self:GetVector(v2))) else return -1 end end +ENT._vecdistance_2 = function (self, v1, v2) if self:IsVector(v1, v2) then return self:GetVector(v1):Distance(self:GetVector(v2)) else return -1 end end +ENT._vecadd_2 = function (self, v1, v2) if self:IsVector(v1, v2) then return self:PutVector(self:GetVector(v1) + self:GetVector(v2)) else return -1 end end +ENT._vecsub_2 = function (self, v1, v2) if self:IsVector(v1, v2) then return self:PutVector(self:GetVector(v1) - self:GetVector(v2)) else return -1 end end +ENT._vecmul_2 = function (self, v1, v2) if self:IsVector(v1, v2) then return self:PutVector(self:GetVector(v1) * self:GetVector(v2)) else return -1 end end +ENT._vecsmul_2 = function (self, v, n) if self:IsVector(v) then return self:PutVector(self:GetVector(v) * n) else return -1 end end +ENT._vecsdiv_2 = function (self, v, n) if self:IsVector(v) then return self:PutVector(self:GetVector(v) / n) else return -1 end end + +ENT._vecrotate_4 = function (self, v, p, y, r) if self:IsVector(v) then local vec = self:CloneVector(self:GetVector(v)) vec:Rotate(Angle(p, y, r)) return self:PutVector(vec) else return -1 end end + +-- EXTERNAL INPUT EXTENSION + +ENT._extcolor_3 = function (self, r, g, b) self:SetColor(math.Clamp(r, 0, 255), math.Clamp(g, 0, 255), math.Clamp(b, 0, 255), 255) return 0 end +ENT._extcolor_4 = function (self, r, g, b, a) self:SetColor(math.Clamp(r, 0, 255), math.Clamp(g, 0, 255), math.Clamp(b, 0, 255), math.Clamp(a, 0, 255)) return 0 end + +ENT._extcolorr_0 = function (self) local tbl = { self:GetColor() } return tbl[0] end +ENT._extcolorg_0 = function (self) local tbl = { self:GetColor() } return tbl[1] end +ENT._extcolorb_0 = function (self) local tbl = { self:GetColor() } return tbl[2] end +ENT._extcolora_0 = function (self) local tbl = { self:GetColor() } return tbl[3] end + +ENT._extdirfwx_0 = function (self) return self:GetForward().x end +ENT._extdirfwy_0 = function (self) return self:GetForward().y end +ENT._extdirfwz_0 = function (self) return self:GetForward().z end +ENT._extdirfw_0 = function (self) return self:PutVector(self:GetForward()) end + +ENT._extdirrtx_0 = function (self) return self:GetRight().x end +ENT._extdirrty_0 = function (self) return self:GetRight().y end +ENT._extdirrtz_0 = function (self) return self:GetRight().z end +ENT._extdirrt_0 = function (self) return self:PutVector(self:GetRight()) end + +ENT._extdirupx_0 = function (self) return self:GetUp().x end +ENT._extdirupy_0 = function (self) return self:GetUp().y end +ENT._extdirupz_0 = function (self) return self:GetUp().z end +ENT._extdirup_0 = function (self) return self:PutVector(self:GetUp()) end + +ENT._extposx_0 = function (self) return self:GetPos().x end +ENT._extposy_0 = function (self) return self:GetPos().y end +ENT._extposz_0 = function (self) return self:GetPos().z end +ENT._extpos_0 = function (self) return self:PutVector(self:GetPos()) end + +ENT._extvelabsx_0 = function (self) return self:GetVelocity().x end +ENT._extvelabsy_0 = function (self) return self:GetVelocity().y end +ENT._extvelabsz_0 = function (self) return self:GetVelocity().z end +ENT._extvelabs_0 = function (self) return self:PutVector(self:GetVelocity()) end + +ENT._extvelx_0 = function (self) return -(self:WorldToLocal(self:GetVelocity() + self:GetPos())).y end +ENT._extvely_0 = function (self) return (self:WorldToLocal(self:GetVelocity() + self:GetPos())).x end +ENT._extvelz_0 = function (self) return (self:WorldToLocal(self:GetVelocity() + self:GetPos())).z end +ENT._extvel_0 = function (self) local v = (self:WorldToLocal(self:GetVelocity() + self:GetPos())) return self:PutVector(Vector(-v.y, v.x, v.z)) end + +ENT._extangp_0 = function (self) return self:GetAngles().p end +ENT._extangy_0 = function (self) return self:GetAngles().y end +ENT._extangr_0 = function (self) return self:GetAngles().r end + +ENT._extangvelp_0 = function (self) return self:GetPhysicsObject():GetAngleVelocity().x end +ENT._extangvely_0 = function (self) return self:GetPhysicsObject():GetAngleVelocity().y end +ENT._extangvelr_0 = function (self) return self:GetPhysicsObject():GetAngleVelocity().z end diff --git a/lua/entities/gmod_wire_expression/parser.lua b/lua/entities/gmod_wire_expression/parser.lua new file mode 100644 index 0000000000..38c14f3718 --- /dev/null +++ b/lua/entities/gmod_wire_expression/parser.lua @@ -0,0 +1,575 @@ +// Written by Syranide, me@syranide.com + +AddCSLuaFile("parser.lua") + +WireGateExpressionParser = {} + +WireGateExpressionParser.toktable = { + ["add"] = "+", + ["sub"] = "-", + ["mul"] = "*", + ["div"] = "/", + ["mod"] = "%", + ["exp"] = "^", + + ["aadd"] = "+=", + ["asub"] = "-=", + ["amul"] = "*=", + ["adiv"] = "/=", + ["amod"] = "%=", + ["aexp"] = "^=", + + ["imp"] = "->", + + ["ass"] = "=", + ["not"] = "!", + ["gth"] = ">", + ["lth"] = "<", + ["eq"] = "==", + ["neq"] = "!=", + ["geq"] = ">=", + ["leq"] = "<=", + + ["and"] = "&", + ["or"] = "|", + + ["qst"] = "?", + ["col"] = ":", + ["sem"] = ";", + ["com"] = ",", + + ["lpa"] = "(", + ["rpa"] = ")", + + ["trg"] = "~", + ["dlt"] = "$", +} + +WireGateExpressionParser.optable = { + ["+"] = {"add", {["="] = {"aadd"}}}, + ["-"] = {"sub", {["="] = {"asub"}, [">"] = {"imp"}}}, + ["*"] = {"mul", {["="] = {"amul"}}}, + ["/"] = {"div", {["="] = {"adiv"}}}, + ["%"] = {"mod", {["="] = {"amod"}}}, + ["^"] = {"exp", {["="] = {"aexp"}}}, + + ["="] = {"ass", {["="] = {"eq"}}}, + ["!"] = {"not", {["="] = {"neq"}}}, + [">"] = {"gth", {["="] = {"geq"}}}, + ["<"] = {"lth", {["="] = {"leq"}}}, + + ["&"] = {"and"}, + ["|"] = {"or"}, + + ["?"] = {"qst"}, + [":"] = {"col"}, + [";"] = {"sem"}, + [","] = {"com"}, + + ["("] = {"lpa"}, + [")"] = {"rpa"}, + + ["~"] = {"trg"}, + ["$"] = {"dlt"}, +} + +function WireGateExpressionParser:New(code, inputs, outputs) + local object = { + symtok = nil, + symarg = nil, + locals = {}, + symbols = {}, + symbindex = 1, + line = 1, + } + + self.__index = self + self = setmetatable(object, self) + + self:ParseSymbols(code) + + self:NextSymbol() + self.instructions = self:expr1() + + self.inputs = self:ParsePorts(inputs) + self.outputs = self:ParsePorts(outputs) + + if #self.outputs == 0 then + self.outputs = self.inputs + end + + if #self.outputs == 0 then + self:Error('No outputs defined') + end + + local inputkeys = self.GetIndexTable(self.inputs) + local outputkeys = self.GetIndexTable(self.outputs) + + local _locals = self.locals self.locals = {} + for key,_ in pairs(_locals) do + if not inputkeys[key] and not outputkeys[key] then + table.insert(self.locals, key) + end + end + + return self +end + +function WireGateExpressionParser:GetError() + return self.error +end + +function WireGateExpressionParser:GetInstructions() + if self.error then return nil end + return self.instructions +end + +function WireGateExpressionParser:GetLocals() + if self.error then return nil end + return self.locals +end + +function WireGateExpressionParser:GetInputs() + if self.error then return nil end + return self.inputs +end + +function WireGateExpressionParser:GetOutputs() + if self.error then return nil end + return self.outputs +end + + +function WireGateExpressionParser.GetIndexTable(tbl) + local keys = {} + for _,key in ipairs(tbl) do keys[key] = true end + return keys +end + +function WireGateExpressionParser:Error(str) + if not self.error then self.error = str end +end + +function WireGateExpressionParser:NextCharacter() + if self.position <= self.length then + self.character = string.sub(self.expression, self.position, self.position) + self.charvalue = string.byte(self.character) + self.position = self.position + 1 + else + self.character = nil + self.charvalue = nil + end +end + +function WireGateExpressionParser:ReadCharacter() + self.buffer = self.buffer .. self.character + self:NextCharacter() +end + +function WireGateExpressionParser:ParseSymbols(str) + --[[ + 97 = a, 122 = z + 65 = A, 90 = Z + --]] + + if str == "" then str = "0" end + + self.expression = str + self.position = 1 + self.length = string.len(str) + self.buffer = "" + + self:NextCharacter() + + while true do + if self.character then + while self.character == " " or self.character == "\t" or self.character == "\n" or self.character == "\r" do + if self.character == "\n" then self.line = self.line + 1 end + self:NextCharacter() + end + + if not self.character then break end + + self.symarg = "" + self.buffer = "" + + if self.character >= "0" and self.character <= "9" then + while self.character and self.character >= "0" and self.character <= "9" do self:ReadCharacter() end + if self.character and self.character == "." then + self:ReadCharacter() + -- error otherwise self:Error("Improper number layout") + while self.character and self.character >= "0" and self.character <= "9" do self:ReadCharacter() end + end + self.symtok = "num"; + elseif self.charvalue >= 97 and self.charvalue <= 122 then + while self.character and (self.charvalue >= 97 and self.charvalue <= 122 or self.charvalue >= 65 and self.charvalue <= 90 or self.character >= "0" and self.character <= "9" or self.character == "_") do self:ReadCharacter() end + self.symtok = "fun" + elseif self.charvalue >= 65 and self.charvalue <= 90 or self.character == "_" then + while self.character and (self.charvalue >= 65 and self.charvalue <= 90 or self.charvalue >= 97 and self.charvalue <= 122 or self.character >= "0" and self.character <= "9" or self.character == "_") do self:ReadCharacter() end + self.symtok = "var" + elseif self.character == "'" then + self:NextCharacter() + while self.character and self.character ~= "'" do self:ReadCharacter() end + self:NextCharacter() + self.symtok = "str" + elseif self.character == "" then + break + else + if !self:ParseOperator() then + self:Error("Unexpected character (" .. self.character .. ") at line " .. self.line) + self:NextCharacter() + end + end + + self.symarg = self.buffer + else + break + end + + table.insert(self.symbols, { self.symtok, self.symarg, self.line }) + end +end + +function WireGateExpressionParser:ParseOperator() + -- should be extended to automatically backtrack if failed (and is at nil, but passed a token that wasn't nil) + -- this is not entirely correct either + local op = self.optable + + if op[self.character] then + while true do + op = op[self.character] + self:ReadCharacter() + + if self.character then + if op[2] then + + if op[2][self.character] then + op = op[2] + else + self.symtok = op[1] + self.symarg = self.buffer + return true + end + else + self.symtok = op[1] + self.symarg = self.buffer + return true + end + else + if op[1] then + self.symtok = op[1] + self.symarg = self.buffer + else + return false + end + end + end + else + return false + end +end + +function WireGateExpressionParser:ParsePorts(ports) + local vals = {} + local keys = {} + ports = string.Explode(" ", string.Trim(ports)) + + for _,key in ipairs(ports) do + key = string.Trim(key) + if key ~= "" then + character = string.sub(key, 1, 1) + charvalue = string.byte(character) + if charvalue >= 65 and charvalue <= 90 or character == "_" then + for i=2,string.len(key) do + character = string.sub(key, i, i) + charvalue = string.byte(character) + if character and charvalue >= 65 and charvalue <= 90 or charvalue >= 97 and charvalue <= 122 or character >= "0" and character <= "9" or character == "_" then + else + self:Error("Invalid port name: " .. key) + end + end + else + self:Error("Invalid port name: " .. key) + end + + if keys[key] then + self:Error("Duplicate port: " .. key) + else + keys[key] = true + table.insert(vals, key) + end + end + end + + return vals +end + + + +function WireGateExpressionParser:Accept(token) + if self.symtok == token then + self:NextSymbol() + return true; + else + return false; + end +end + +function WireGateExpressionParser:Expect(token) -- outputs bad errors (lpa) etc + if self:Accept(token) then + return true; + else + if self.symtok then + if self.symtok == "fun" then + self:Error("Expected symbol (" .. self.toktable[token] .. ") near function (" .. self.symarg .. ") at line " .. self.line); + elseif self.symtok == "var" then + self:Error("Expected symbol (" .. self.toktable[token] .. ") near variable (" .. self.symarg .. ") at line " .. self.line); + elseif self.symtok == "num" then + self:Error("Expected symbol (" .. self.toktable[token] .. ") near number (" .. self.symarg .. ") at line " .. self.line); + elseif self.symtok == "str" then + self:Error("Expected symbol (" .. self.toktable[token] .. ") near string (" .. self.symarg .. ") at line " .. self.line); + else + self:Error("Expected symbol (" .. self.toktable[token] .. ") near (" .. self.toktable[self.symtok] .. ") at line " .. self.line); + end + else + self:Error("Expected symbol (" .. self.toktable[token] .. ") at line " .. self.line); + end + return false; + end +end + +function WireGateExpressionParser:NextSymbol() + if self.symbindex <= #self.symbols then + self.symtok = self.symbols[self.symbindex][1] + self.symarg = self.symbols[self.symbindex][2] + self.line = self.symbols[self.symbindex][3] + self.symbindex = self.symbindex + 1 + else + self.symtok = nil + self.symarg = nil + end +end + +function WireGateExpressionParser:RecurseLeft(expr, tbl) + local expression = expr(self) + local ins = false + while true do + for key,value in pairs(tbl) do + if self:Accept(key) then + ins = true + expression = {value, expression, expr(self)} + break + end + end + if !ins then break end + ins = false + end + return expression +end + +function WireGateExpressionParser:RecurseRight(expr, tbl) + local expression = expr(self) + for key,value in pairs(tbl) do + if self:Accept(key) then + return {value, expression, self:RecurseRight(expr, tbl)} + end + end + return expression +end + +--[[ 1 : exp2 , exp2 exp1 ]] +function WireGateExpressionParser:expr1() + local expression = self:expr2() + + if self:Accept("com") or self.symtok then + return {"seq", expression, self:expr1()} + else + return expression + end +end + +--[[ 2 : exp4 , exp4->exp3; ]] +function WireGateExpressionParser:expr2() + if self.symtok and self.symtok == "fun" and (self.symarg == "concommand" or self.symarg == "concmd") then + self:NextSymbol() + self:Expect("lpa") + local arg = self.symarg + self:Expect("str") + self:Expect("rpa") + return {"con", arg} + end + + local expression = self:expr4() + + if self:Accept("imp") then + local expression = {"imp", expression, self:expr3()} + self:Expect("sem") + return expression + else + return expression + end +end + +--[[ 3 : exp2 , exp2 exp3 ]] +function WireGateExpressionParser:expr3() + if self.symtok and self.symtok == "fun" and self.symarg == "end" then + self:NextSymbol() + return {"end"} + end + + local expression = self:expr2() + if self:Accept("com") or self.symtok and self.symtok != "sem" then + return {"seq", expression, self:expr3()} + else + return expression + end +end + +--[[ 4 : exp5 , var=exp4 , var+=exp4 ]] -- force for first level! +function WireGateExpressionParser:expr4() + local expression = self:expr5() + if expression and expression[1] == "var" then + local arg = expression + if self:Accept("ass") then + return {"ass", arg, self:expr4()} + elseif self:Accept("aadd") then + return {"aadd", arg, self:expr4()} + elseif self:Accept("asub") then + return {"asub", arg, self:expr4()} + elseif self:Accept("amul") then + return {"amul", arg, self:expr4()} + elseif self:Accept("adiv") then + return {"adiv", arg, self:expr4()} + elseif self:Accept("amod") then + return {"amod", arg, self:expr4()} + elseif self:Accept("aexp") then + return {"aexp", arg, self:expr4()} + end + end + return expression +end + +--[[ 5 : exp6 , exp6?exp4:exp4 ]] +function WireGateExpressionParser:expr5() + local expression = self:expr6() + if self:Accept("qst") then + local exprtrue = self:expr4() + self:Expect("col") + return {"cnd", expression, exprtrue, self:expr4()} + else + return expression + end +end + +--[[ 6 : exp7 , exp7|exp6 ]] +function WireGateExpressionParser:expr6() + return self:RecurseLeft(self.expr7, {["or"] = "or"}) +end + +--[[ 7 : exp8 , exp7&exp8 ]] +function WireGateExpressionParser:expr7() + return self:RecurseLeft(self.expr8, {["and"] = "and"}) +end + +--[[ 8 : exp9 , exp8==exp9 , exp8!=exp9 ]] +function WireGateExpressionParser:expr8() + return self:RecurseLeft(self.expr9, {["eq"] = "eq", ["neq"] = "neq"}) +end + +--[[ 9 : exp10, exp9>=exp10 , exp9<=exp10 , exp9>exp10 , exp9 e7, e6 <= e7, e6 >= e7 + 7 : e7 + e8, e7 - e8 + 8 : e8 * e9, e8 / e9, e8 % e9 + 9 : e9 ^ e10 +10 : +e11, -e11, !e10 +11 : e11:fun([e1, ...]), e11[var,type] +12 : (e1), fun([e1, ...]) +13 : string, num, ~var, $var +14 : var++, var-- [ERROR] +15 : var + +*/ +/******************************************************************************/ + +Parser = {} +Parser.__index = Parser + +function Parser.Execute(...) + -- instantiate Parser + local instance = setmetatable({}, Parser) + + -- and pcall the new instance's Process method. + return pcall(Parser.Process, instance, ...) +end + +function Parser:Error(message, token) + if token then + error(message .. " at line " .. token[4] .. ", char " .. token[5], 0) + else + error(message .. " at line " .. self.token[4] .. ", char " .. self.token[5], 0) + end +end + +function Parser:Process(tokens, params) + self.tokens = tokens + + self.index = 0 + self.count = #tokens + self.delta = {} + + self:NextToken() + local tree = self:Root() + + return tree, self.delta +end + +/******************************************************************************/ + +function Parser:GetToken() + return self.token +end + +function Parser:GetTokenData() + return self.token[2] +end + +function Parser:GetTokenTrace() + return {self.token[4], self.token[5]} +end + + +function Parser:Instruction(trace, name, ...) + return {name, trace, ...} // +end + + +function Parser:HasTokens() + return self.readtoken != nil +end + +function Parser:NextToken() + if self.index <= self.count then + if self.index > 0 then + self.token = self.readtoken + else + self.token = {"", "", false, 1, 1} + end + + self.index = self.index + 1 + self.readtoken = self.tokens[self.index] + else + self.readtoken = nil + end +end + +function Parser:TrackBack() + self.index = self.index - 2 + self:NextToken() +end + + +function Parser:AcceptRoamingToken(name) + local token = self.readtoken + if !token or token[1] != name then return false end + + self:NextToken() + return true +end + +function Parser:AcceptTailingToken(name) + local token = self.readtoken + if !token or token[3] then return false end + + return self:AcceptRoamingToken(name) +end + +function Parser:AcceptLeadingToken(name) + local token = self.tokens[self.index + 1] + if !token or token[3] then return false end + + return self:AcceptRoamingToken(name) +end + + +function Parser:RecurseLeft(func, tbl) + local expr = func(self) + local hit = true + + while hit do + hit = false + for i=1,#tbl do + if self:AcceptRoamingToken(tbl[i]) then + local trace = self:GetTokenTrace() + + hit = true + expr = self:Instruction(trace, tbl[i], expr, func(self)) + break + end + end + end + + return expr +end + +/******************************************************************************/ + +local loopdepth + +function Parser:Root() + loopdepth = 0 + return self:Stmts() +end + + +function Parser:Stmts() + local trace = self:GetTokenTrace() + local stmts = self:Instruction(trace, "seq") + + if !self:HasTokens() then return stmts end + + while true do + if self:AcceptRoamingToken("com") then + self:Error("Statement separator (,) must not appear multiple times") + end + + stmts[#stmts + 1] = self:Stmt1() + + if !self:HasTokens() then break end + + if !self:AcceptRoamingToken("com") then + if self.readtoken[3] == false then + self:Error("Statements must be separated by comma (,) or whitespace") + end + end + end + + return stmts +end + + +function Parser:Stmt1() + if self:AcceptRoamingToken("if") then + local trace = self:GetTokenTrace() + return self:Instruction(trace, "if", self:Cond(), self:Block("if condition"), self:IfElseIf()) + end + + return self:Stmt2() +end + +function Parser:Stmt2() + if self:AcceptRoamingToken("whl") then + local trace = self:GetTokenTrace() + loopdepth = loopdepth + 1 + local whl = self:Instruction(trace, "whl", self:Cond(), self:Block("while condition")) + loopdepth = loopdepth - 1 + return whl + end + + return self:Stmt3() +end + +function Parser:Stmt3() + if self:AcceptRoamingToken("for") then + local trace = self:GetTokenTrace() + loopdepth = loopdepth + 1 + + if !self:AcceptRoamingToken("lpa") then + self:Error("Left parenthesis (() must appear before condition") + end + + if !self:AcceptRoamingToken("var") then + self:Error("Variable expected for the numeric index") + end + + local var = self:GetTokenData() + + if !self:AcceptRoamingToken("ass") then + self:Error("Assignment operator (=) expected to preceed variable") + end + + local estart = self:Expr1() + + if !self:AcceptRoamingToken("com") then + self:Error("Comma (,) expected after start value") + end + + local estop = self:Expr1() + + local estep + if self:AcceptRoamingToken("com") then + estep = self:Expr1() + end + + if !self:AcceptRoamingToken("rpa") then + self:Error("Right parenthesis ()) missing, to close condition") + end + + local sfor = self:Instruction(trace, "for", var, estart, estop, estep, self:Block("for statement")) + + loopdepth = loopdepth - 1 + return sfor + end + + return self:Stmt4() +end + +function Parser:Stmt4() + if self:AcceptRoamingToken("brk") then + if loopdepth > 0 then + local trace = self:GetTokenTrace() + return self:Instruction(trace, "brk") + else + self:Error("Break may not exist outside of a loop") + end + elseif self:AcceptRoamingToken("cnt") then + if loopdepth > 0 then + local trace = self:GetTokenTrace() + return self:Instruction(trace, "cnt") + else + self:Error("Continue may not exist outside of a loop") + end + end + + return self:Stmt5() +end + +function Parser:Stmt5() + if self:AcceptRoamingToken("var") then + local trace = self:GetTokenTrace() + local var = self:GetTokenData() + + if self:AcceptTailingToken("inc") then + return self:Instruction(trace, "inc", var) + elseif self:AcceptRoamingToken("inc") then + self:Error("Increment operator (++) must not be preceded by whitespace") + end + + if self:AcceptTailingToken("dec") then + return self:Instruction(trace, "dec", var) + elseif self:AcceptRoamingToken("dec") then + self:Error("Decrement operator (--) must not be preceded by whitespace") + end + + self:TrackBack() + end + + return self:Stmt6() +end + +function Parser:Stmt6() + if self:AcceptRoamingToken("var") then + local trace = self:GetTokenTrace() + local var = self:GetTokenData() + + if self:AcceptRoamingToken("aadd") then + return self:Instruction(trace, "ass", var, self:Instruction(trace, "add", self:Instruction(trace, "var", var), self:Expr1())) + elseif self:AcceptRoamingToken("asub") then + return self:Instruction(trace, "ass", var, self:Instruction(trace, "sub", self:Instruction(trace, "var", var), self:Expr1())) + elseif self:AcceptRoamingToken("amul") then + return self:Instruction(trace, "ass", var, self:Instruction(trace, "mul", self:Instruction(trace, "var", var), self:Expr1())) + elseif self:AcceptRoamingToken("adiv") then + return self:Instruction(trace, "ass", var, self:Instruction(trace, "div", self:Instruction(trace, "var", var), self:Expr1())) + end + + self:TrackBack() + end + + return self:Stmt7() +end + +function Parser:Stmt7() + if self:AcceptRoamingToken("var") then + local tbpos = self.index + local trace = self:GetTokenTrace() + local var = self:GetTokenData() + + if self:AcceptTailingToken("lsb") then + if self:AcceptRoamingToken("rsb") then + self:Error("Indexing operator ([]) requires an index [X]") + end + + local expr = self:Expr1() + if self:AcceptRoamingToken("com") then + if !self:AcceptRoamingToken("fun") then + self:Error("Indexing operator ([]) requires a lower case type [X,t]") + end + + local longtp = self:GetTokenData() + + if !self:AcceptRoamingToken("rsb") then + self:Error("Right square bracket (]) missing, to close indexing operator [X,t]") + end + + if self:AcceptRoamingToken("ass") then + if longtp == "number" then longtp = "normal" end + if wire_expression_types[string.upper(longtp)] == nil then + self:Error("Indexing operator ([]) does not support the type [" .. longtp .. "]") + end + + local tp = wire_expression_types[string.upper(longtp)][1] + return self:Instruction(trace, "set", var, expr, self:Stmt7(), tp) + end + elseif self:AcceptRoamingToken("rsb") then + if self:AcceptRoamingToken("ass") then + return self:Instruction(trace, "set", var, expr, self:Stmt7()) + end + else + self:Error("Indexing operator ([]) needs to be closed with comma (,) or right square bracket (])") + end + elseif self:AcceptRoamingToken("lsb") then + self:Error("Indexing operator ([]) must not be preceded by whitespace") + elseif self:AcceptRoamingToken("ass") then + return self:Instruction(trace, "ass", var, self:Stmt7()) + end + + self.index = tbpos - 2 + self:NextToken() + end + + return self:Stmt8() +end + +function Parser:Stmt8() + return self:Expr1() +end + + +function Parser:IfElseIf() + if self:AcceptRoamingToken("eif") then + local trace = self:GetTokenTrace() + return self:Instruction(trace, "if", self:Cond(), self:Block("elseif condition"), self:IfElseIf()) + end + + return self:IfElse() +end + +function Parser:IfElse() + if self:AcceptRoamingToken("els") then + return self:Block("else") + end + + local trace = self:GetTokenTrace() + return self:Instruction(trace, "seq") +end + +function Parser:Cond() + if !self:AcceptRoamingToken("lpa") then + self:Error("Left parenthesis (() must appear before condition") + end + + local expr = self:Expr1() + + if !self:AcceptRoamingToken("rpa") then + self:Error("Right parenthesis ()) missing, to close condition") + end + + return expr +end + + +function Parser:Block(block_type) + local trace = self:GetTokenTrace() + local stmts = self:Instruction(trace, "seq") + + if !self:AcceptRoamingToken("lcb") then + self:Error("Left curly bracket ({) must appear after "..(block_type or "condition")) + end + + local token = self:GetToken() + + if self:AcceptRoamingToken("rcb") then + return stmts + end + + if self:HasTokens() then + while true do + if self:AcceptRoamingToken("com") then + self:Error("Statement separator (,) must not appear multiple times") + elseif self:AcceptRoamingToken("rcb") then + self:Error("Statement separator (,) must be suceeded by statement") + end + + stmts[#stmts + 1] = self:Stmt1() + + if self:AcceptRoamingToken("rcb") then + return stmts + end + + if !self:AcceptRoamingToken("com") then + if !self:HasTokens() then break end + + if self.readtoken[3] == false then + self:Error("Statements must be separated by comma (,) or whitespace") + end + end + end + end + + self:Error("Right curly bracket (}) missing, to close statement block", token) +end + + +function Parser:Expr1() + self.exprtoken = self:GetToken() + + if self:AcceptRoamingToken("var") then + if self:AcceptRoamingToken("ass") then + self:Error("Assignment operator (=) must not be part of equation") + end + + if self:AcceptRoamingToken("aadd") then + self:Error("Additive assignment operator (+=) must not be part of equation") + elseif self:AcceptRoamingToken("asub") then + self:Error("Subtractive assignment operator (-=) must not be part of equation") + elseif self:AcceptRoamingToken("amul") then + self:Error("Multiplicative assignment operator (*=) must not be part of equation") + elseif self:AcceptRoamingToken("adiv") then + self:Error("Divisive assignment operator (/=) must not be part of equation") + end + + self:TrackBack() + end + + return self:Expr2() +end + +function Parser:Expr2() + local expr = self:Expr3() + + if self:AcceptRoamingToken("qsm") then + local trace = self:GetTokenTrace() + local exprtrue = self:Expr1() + + if !self:AcceptRoamingToken("col") then -- perhaps we want to make sure there is space around this (method bug) + self:Error("Conditional operator (:) must appear after expression to complete conditional", token) + end + + return self:Instruction(trace, "cnd", expr, exprtrue, self:Expr1()) + end + + return expr +end + +function Parser:Expr3() + return self:RecurseLeft(self.Expr4, {"or"}) +end + +function Parser:Expr4() + return self:RecurseLeft(self.Expr5, {"and"}) +end + +function Parser:Expr5() + return self:RecurseLeft(self.Expr6, {"eq", "neq"}) +end + +function Parser:Expr6() + return self:RecurseLeft(self.Expr7, {"gth", "lth", "geq", "leq"}) +end + +function Parser:Expr7() + return self:RecurseLeft(self.Expr8, {"add", "sub"}) +end + +function Parser:Expr8() + return self:RecurseLeft(self.Expr9, {"mul", "div", "mod"}) +end + +function Parser:Expr9() + return self:RecurseLeft(self.Expr10, {"exp"}) +end + +function Parser:Expr10() + if self:AcceptLeadingToken("add") then + return self:Expr11() + elseif self:AcceptRoamingToken("add") then + self:Error("Identity operator (+) must not be succeeded by whitespace") + end + + if self:AcceptLeadingToken("sub") then + local trace = self:GetTokenTrace() + return self:Instruction(trace, "neg", self:Expr11()) + elseif self:AcceptRoamingToken("sub") then + self:Error("Negation operator (-) must not be succeeded by whitespace") + end + + if self:AcceptLeadingToken("not") then + local trace = self:GetTokenTrace() + return self:Instruction(trace, "not", self:Expr10()) + elseif self:AcceptRoamingToken("not") then + self:Error("Logical not operator (-) must not be succeeded by whitespace") + end + + return self:Expr11() +end + +function Parser:Expr11() + local expr = self:Expr12() + + while true do + if self:AcceptTailingToken("col") then + if !self:AcceptTailingToken("fun") then + if self:AcceptRoamingToken("fun") then + self:Error("Method operator (:) must not be preceded by whitespace") + else + self:Error("Method operator (:) must be followed by method name") + end + end + + local trace = self:GetTokenTrace() + local fun = self:GetTokenData() + + if !self:AcceptTailingToken("lpa") then + if self:AcceptRoamingToken("lpa") then + self:Error("Left parenthesis (() must not be preceded by whitespace") + else + self:Error("Left parenthesis (() must appear after method name") + end + end + + local token = self:GetToken() + + if self:AcceptRoamingToken("rpa") then + expr = self:Instruction(trace, "mto", fun, expr, {}) + else + local exprs = {self:Expr1()} + + while self:AcceptRoamingToken("com") do + exprs[#exprs + 1] = self:Expr1() + end + + if !self:AcceptRoamingToken("rpa") then + self:Error("Right parenthesis ()) missing, to close method argument list", token) + end + + expr = self:Instruction(trace, "mto", fun, expr, exprs) + end + --elseif self:AcceptRoamingToken("col") then + -- self:Error("Method operator (:) must not be preceded by whitespace") + elseif self:AcceptTailingToken("lsb") then + local trace = self:GetTokenTrace() + + if self:AcceptRoamingToken("rsb") then + self:Error("Indexing operator ([]) requires an index [X]") + end + + local aexpr = self:Expr1() + if self:AcceptRoamingToken("com") then + if !self:AcceptRoamingToken("fun") then + self:Error("Indexing operator ([]) requires a lower case type [X,t]") + end + + local longtp = self:GetTokenData() + + if !self:AcceptRoamingToken("rsb") then + self:Error("Right square bracket (]) missing, to close indexing operator [X,t]") + end + + if longtp == "number" then longtp = "normal" end + if wire_expression_types[string.upper(longtp)] == nil then + self:Error("Indexing operator ([]) does not support the type [" .. longtp .. "]") + end + + local tp = wire_expression_types[string.upper(longtp)][1] + expr = self:Instruction(trace, "get", expr, aexpr, tp) + elseif self:AcceptRoamingToken("rsb") then + expr = self:Instruction(trace, "get", expr, aexpr) + else + self:Error("Indexing operator ([]) needs to be closed with comma (,) or right square bracket (])") + end + elseif self:AcceptRoamingToken("lsb") then + self:Error("Indexing operator ([]) must not be preceded by whitespace") + else + break + end + end + + return expr +end + +function Parser:Expr12() + if self:AcceptRoamingToken("lpa") then + local token = self:GetToken() + + local expr = self:Expr1() + + if !self:AcceptRoamingToken("rpa") then + self:Error("Right parenthesis ()) missing, to close grouped equation", token) + end + + return expr + end + + if self:AcceptRoamingToken("fun") then + local trace = self:GetTokenTrace() + local fun = self:GetTokenData() + + if !self:AcceptTailingToken("lpa") then + if self:AcceptRoamingToken("lpa") then + self:Error("Left parenthesis (() must not be preceded by whitespace") + else + self:Error("Left parenthesis (() must appear after function name, variables must start with uppercase letter,") + end + end + + local token = self:GetToken() + + if self:AcceptRoamingToken("rpa") then + return self:Instruction(trace, "fun", fun, {}) + else + local exprs = {self:Expr1()} + while self:AcceptRoamingToken("com") do + exprs[#exprs + 1] = self:Expr1() + end + + if !self:AcceptRoamingToken("rpa") then + self:Error("Right parenthesis ()) missing, to close function argument list", token) + end + + return self:Instruction(trace, "fun", fun, exprs) + end + end + + return self:Expr13() +end + +function Parser:Expr13() + if self:AcceptRoamingToken("num") then + local trace = self:GetTokenTrace() + local num,tp = self:GetTokenData():match("^([-+e0-9.]*)(.*)$") + return self:Instruction(trace, "num"..tp, num) + end + + if self:AcceptRoamingToken("str") then + local trace = self:GetTokenTrace() + local str = self:GetTokenData() + return self:Instruction(trace, "str", str) + end + + if self:AcceptRoamingToken("trg") then + local trace = self:GetTokenTrace() + + if !self:AcceptTailingToken("var") then + if self:AcceptRoamingToken("var") then + self:Error("Triggered operator (~) must not be succeeded by whitespace") + else + self:Error("Triggered operator (~) must be preceded by variable") + end + end + + local var = self:GetTokenData() + return self:Instruction(trace, "trg", var) + end + + if self:AcceptRoamingToken("dlt") then + local trace = self:GetTokenTrace() + + if !self:AcceptTailingToken("var") then + if self:AcceptRoamingToken("var") then + self:Error("Delta operator ($) must not be succeeded by whitespace") + else + self:Error("Delta operator ($) must be preceded by variable") + end + end + + local var = self:GetTokenData() + self.delta[var] = true + + return self:Instruction(trace, "dlt", var) + end + + return self:Expr14() +end + +function Parser:Expr14() + if self:AcceptRoamingToken("var") then + if self:AcceptTailingToken("inc") then + self:Error("Increment operator (++) must not be part of equation") + elseif self:AcceptRoamingToken("inc") then + self:Error("Increment operator (++) must not be preceded by whitespace") + end + + if self:AcceptTailingToken("dec") then + self:Error("Decrement operator (--) must not be part of equation") + elseif self:AcceptRoamingToken("dec") then + self:Error("Decrement operator (--) must not be preceded by whitespace") + end + + self:TrackBack() + end + + return self:Expr15() +end + +function Parser:Expr15() + if self:AcceptRoamingToken("var") then + local trace = self:GetTokenTrace() + local var = self:GetTokenData() + return self:Instruction(trace, "var", var) + end + + return self:ExprError() +end + +function Parser:ExprError() + if self:HasTokens() then + if self:AcceptRoamingToken("add") then + self:Error("Addition operator (+) must be preceded by equation or value") + elseif self:AcceptRoamingToken("sub") then -- can't occur + self:Error("Subtraction operator (-) must be preceded by equation or value") + elseif self:AcceptRoamingToken("mul") then + self:Error("Multiplication operator (*) must be preceded by equation or value") + elseif self:AcceptRoamingToken("div") then + self:Error("Division operator (/) must be preceded by equation or value") + elseif self:AcceptRoamingToken("mod") then + self:Error("Modulo operator (%) must be preceded by equation or value") + elseif self:AcceptRoamingToken("exp") then + self:Error("Exponentiation operator (%) must be preceded by equation or value") + + elseif self:AcceptRoamingToken("ass") then + self:Error("Assignment operator (=) must be preceded by variable") + elseif self:AcceptRoamingToken("aadd") then + self:Error("Additive assignment operator (+=) must be preceded by variable") + elseif self:AcceptRoamingToken("asub") then + self:Error("Subtractive assignment operator (-=) must be preceded by variable") + elseif self:AcceptRoamingToken("amul") then + self:Error("Multiplicative assignment operator (*=) must be preceded by variable") + elseif self:AcceptRoamingToken("adiv") then + self:Error("Divisive assignment operator (/=) must be preceded by variable") + + elseif self:AcceptRoamingToken("and") then + self:Error("Logical and operator (&) must be preceded by equation or value") + elseif self:AcceptRoamingToken("or") then + self:Error("Logical or operator (|) must be preceded by equation or value") + + elseif self:AcceptRoamingToken("eq") then + self:Error("Equality operator (==) must be preceded by equation or value") + elseif self:AcceptRoamingToken("neq") then + self:Error("Inequality operator (!=) must be preceded by equation or value") + elseif self:AcceptRoamingToken("gth") then + self:Error("Greater than or equal to operator (>=) must be preceded by equation or value") + elseif self:AcceptRoamingToken("lth") then + self:Error("Less than or equal to operator (<=) must be preceded by equation or value") + elseif self:AcceptRoamingToken("geq") then + self:Error("Greater than operator (>) must be preceded by equation or value") + elseif self:AcceptRoamingToken("leq") then + self:Error("Less than operator (<) must be preceded by equation or value") + + elseif self:AcceptRoamingToken("inc") then + self:Error("Increment operator (++) must be preceded by variable") + elseif self:AcceptRoamingToken("dec") then + self:Error("Decrement operator (--) must be preceded by variable") + + elseif self:AcceptRoamingToken("rpa") then + self:Error("Right parenthesis ()) without matching left parenthesis") + elseif self:AcceptRoamingToken("lcb") then + self:Error("Left curly bracket ({) must be part of an if-statement block") + elseif self:AcceptRoamingToken("rcb") then + self:Error("Right curly bracket (}) without matching left curly bracket") + elseif self:AcceptRoamingToken("lsb") then + self:Error("Left square bracket ([) must be preceded by variable") + elseif self:AcceptRoamingToken("rsb") then + self:Error("Right square bracket (]) without matching left square bracket") + + elseif self:AcceptRoamingToken("com") then + self:Error("Comma (,) not expected here, missing an argument?") + elseif self:AcceptRoamingToken("col") then + self:Error("Method operator (:) must not be preceded by whitespace") + + elseif self:AcceptRoamingToken("if") then + self:Error("If keyword (if) must not appear inside an equation") + elseif self:AcceptRoamingToken("eif") then + self:Error("Else-if keyword (elseif) must be part of an if-statement") + elseif self:AcceptRoamingToken("els") then + self:Error("Else keyword (else) must be part of an if-statement") + + else + self:Error("Unexpected token found (" .. self.readtoken[1] .. "), please report this to me@syranide.com") + end + else + self:Error("Further input required at end of code, incomplete expression", self.exprtoken) + end +end diff --git a/lua/entities/gmod_wire_expression2/base/preprocessor.lua b/lua/entities/gmod_wire_expression2/base/preprocessor.lua new file mode 100644 index 0000000000..a46df92b7d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/base/preprocessor.lua @@ -0,0 +1,316 @@ +/******************************************************************************\ + Expression 2 Pre-Processor for Garry's Mod + Andreas "Syranide" Svensson, me@syranide.com +\******************************************************************************/ + +AddCSLuaFile("preprocessor.lua") + +PreProcessor = {} +PreProcessor.__index = PreProcessor + +function PreProcessor.Execute(...) + -- instantiate PreProcessor + local instance = setmetatable({}, PreProcessor) + + -- and pcall the new instance's Process method. + return pcall(instance.Process, instance, ...) +end + +function PreProcessor:Error(message, column) + error(message .. " at line " .. self.readline .. ", char " .. (column or 1), 0) +end + +local type_map = { + v4 = "xv4", + v2 = "xv2", + m4 = "xm4", + m2 = "xm2", + rd = "xrd", + wl = "xwl", + number = "n", +} +local function gettype(tp) + tp = tp:Trim():lower() + local up = tp:upper() + return type_map[tp] or (wire_expression_types[up] and wire_expression_types[up][1]) or tp +end + +function PreProcessor:HandlePPCommand(comment) + local command,args = comment:match("^([^ ]*) ?(.*)$") + local handler = self["PP_"..command] + if handler then return handler(self, args) end +end + +function PreProcessor:RemoveComments(line) + -- Search for string literals and comments + local comment = string.find(line, '[#"]') + + -- While the current match is not a comment... + while (comment and (string.sub(line, comment, comment) != '#')) do + -- ...skip the string literal... + -- condition: comment points to a " + comment = string.find(line, '[\\"]', comment+1) + -- condition: comment points to a \ or a " + while (comment and (string.sub(line, comment, comment) == '\\')) do + -- comment points to a \ -> skip 2 characters + comment = string.find(line, '[\\"]', comment+2) + -- comment points to a \ or a " or nil + if (comment == nil) then break end -- syntax error: missing closing quote -> break + -- comment points to a \ or a " + end + -- condition: comment points to a " or nil + if (comment == nil) then break end -- syntax error: missing closing quote -> break + -- condition: comment points to a " + -- ...and look for the next string literal or comment. + comment = string.find(line, '[#"]', comment+1) + end + + local prev_disabled = self.disabled + if comment then + self:HandlePPCommand(line:sub(comment+1)) + line = string.sub(line, 1, comment - 1) + end + + if prev_disabled then return "" end + return line +end + +function PreProcessor:ParseDirectives(line) + -- parse directive + local directive, value = line:match("^@([^ ]*) ?(.*)$") + + -- not a directive? + if not directive then + -- flag as "in code", if that is the case + if string.Trim(line) ~= "" then + self.incode = true + end + -- don't handle as a directive. + return line + end + + local col = directive:find("[A-Z]") + if col then self:Error("Directive (@" .. E2Lib.limitString(directive, 10) .. ") must be lowercase", col+1) end + if self.incode then self:Error("Directive (@" .. E2Lib.limitString(directive, 10) .. ") must appear before code") end + + -- evaluate directive + if directive == "name" then + if self.directives.name == nil then + self.directives.name = value + else + self:Error("Directive (@name) must not be specified twice") + end + elseif directive == "inputs" then + local retval, columns = self:ParsePorts(value,#directive+2) + + for i,key in ipairs(retval[1]) do + if self.directives.inputs[3][key] then + self:Error("Directive (@input) contains multiple definitions of the same variable", columns[i]) + else + local index = #self.directives.inputs[1] + 1 + self.directives.inputs[1][index] = key + self.directives.inputs[2][index] = retval[2][i] + self.directives.inputs[3][key] = retval[2][i] + end + end + elseif directive == "outputs" then + local retval, columns = self:ParsePorts(value,#directive+2) + + for i,key in ipairs(retval[1]) do + if self.directives.outputs[3][key] then + self:Error("Directive (@output) contains multiple definitions of the same variable", columns[i]) + else + local index = #self.directives.outputs[1] + 1 + self.directives.outputs[1][index] = key + self.directives.outputs[2][index] = retval[2][i] + self.directives.outputs[3][key] = retval[2][i] + end + end + elseif directive == "persist" then + local retval, columns = self:ParsePorts(value,#directive+2) + + for i,key in ipairs(retval[1]) do + if self.directives.persist[3][key] then + self:Error("Directive (@persist) contains multiple definitions of the same variable", columns[i]) + else + local index = #self.directives.persist[1] + 1 + self.directives.persist[1][index] = key + self.directives.persist[2][index] = retval[2][i] + self.directives.persist[3][key] = retval[2][i] + end + end + elseif directive == "trigger" then + local trimmed = string.Trim(value) + if trimmed == "" then + elseif trimmed == "all" then + if self.directives.trigger[1] != nil then + self:Error("Directive (@trigger) conflicts with previous directives") + end + self.directives.trigger[1] = true + elseif trimmed == "none" then + if self.directives.trigger[1] != nil then + self:Error("Directive (@trigger) conflicts with previous directives") + end + self.directives.trigger[1] = false + else + if self.directives.trigger[1] != nil and #self.directives.trigger[2] == 0 then + self:Error("Directive (@trigger) conflicts with previous directives") + end + + self.directives.trigger[1] = false + local retval, columns = self:ParsePorts(value,#directive+2) + + for i,key in ipairs(retval[1]) do + if self.directives.trigger[key] then + self:Error("Directive (@trigger) contains multiple definitions of the same variable", columns[i]) + else + self.directives.trigger[2][key] = true + end + end + end + else + self:Error("Unknown directive found (@" .. E2Lib.limitString(directive, 10) .. ")", 2) + end + + -- remove line from output + return "" +end + +function PreProcessor:Process(buffer, params) + local lines = string.Explode("\n", buffer) + + self.directives = { + name = nil, + inputs = { {}, {}, {} }, + outputs = { {}, {}, {} }, + persist = { {}, {}, {} }, + delta = { {}, {}, {} }, + trigger = { nil, {} }, + } + + for i,line in ipairs(lines) do + self.readline = i + line = string.TrimRight(line) + + line = self:RemoveComments(line) + line = self:ParseDirectives(line) + + lines[i] = line + end + + if self.directives.trigger[1] == nil then self.directives.trigger[1] = true end + if !self.directives.name then self.directives.name = "" end + + return self.directives, string.Implode("\n", lines) +end + +function PreProcessor:ParsePorts(ports, startoffset) + local names = {} + local types = {} + local columns = {} + ports = ports:gsub("%[.-%]", function(s) + return s:gsub(" ",",") + end) -- preprocess multi-variable definitions. + + for column,key in ports:gmatch("()([^ ]+)") do + column = startoffset+column + key = key:Trim() + + -------------------------------- variable names -------------------------------- + + -- single-variable definition? + local _,i,namestring = key:find("^([A-Z][A-Za-z0-9_]*)") + if i then + -- yes -> add the variable + names[#names + 1] = namestring + else + -- no -> maybe a multi-variable definition? + _,i,namestring = key:find("^%[([^]]+)%]") + if not i then + -- no -> malformed variable name + self:Error("Variable name (" .. E2Lib.limitString(key, 10) .. ") must start with an uppercase letter", column) + end + -- yes -> add all variables. + for column2,var in namestring:gmatch("()([^,]+)") do + column2 = column+column2 + var = string.Trim(var) + -- skip empty entries + if var ~= "" then + -- error on malformed variable names + if not var:match("^[A-Z]") then self:Error("Variable name (" .. E2Lib.limitString(var, 10) .. ") must start with an uppercase letter", column2) end + local errcol = var:find("[^A-Za-z0-9_]") + if errcol then self:Error("Variable declaration (" .. E2Lib.limitString(var, 10) .. ") contains invalid characters", column2+errcol-1) end + -- and finally add the variable. + names[#names + 1] = var + end + end + end + + -------------------------------- variable types -------------------------------- + + local vtype + local character = key:sub(i+1, i+1) + if character == ":" then + -- type is specified -> check for validity + vtype = key:sub(i + 2) + + if vtype ~= vtype:lower() then + self:Error("Variable type [" .. E2Lib.limitString(vtype, 10) .. "] must be lowercase", column+i+1) + end + + if not wire_expression_types[vtype:upper()] then + self:Error("Unknown variable type [" .. E2Lib.limitString(vtype, 10) .. "] specified for variable(s) (" .. E2Lib.limitString(namestring, 10) .. ")", column+i+1) + end + elseif character == "" then + -- type is not specified -> default to NORMAL + vtype = "NORMAL" + else + -- invalid -> raise an error + self:Error("Variable declaration (" .. E2Lib.limitString(key, 10) .. ") contains invalid characters", column+i) + end + + -- fill in the missing types + for i = #types+1,#names do + types[i] = vtype:upper() + columns[i] = column + end + end + + return { names, types }, columns +end + +function PreProcessor:PP_ifdef(args) + if self.disabled ~= nil then self:Error("Found nested #ifdef") end + local thistype,colon,name,argtypes = args:match("([^:]-)(:?)([^:(]+)%(([^)]*)%)") + if not thistype or (thistype ~= "") ~= (colon ~= "") then self:Error("Malformed #ifdef argument "..args) end + + thistype = gettype(thistype) + + local tps = { thistype..colon } + for i,argtype in ipairs(string.Explode(",", argtypes)) do + argtype = gettype(argtype) + table.insert(tps, argtype) + end + local pars = table.concat(tps) + local a = wire_expression2_funcs[name .. "(" .. pars .. ")"] + + self.disabled = not a +end + +function PreProcessor:PP_ifndef(args) + local ret = self:PP_ifdef(args) + self.disabled = not self.disabled + return ret +end + +function PreProcessor:PP_else(args) + if self.disabled == nil then self:Error("Found #else outside #ifdef block") end + if args:Trim() ~= "" then self:Error("Must not pass an argument to #else") end + self.disabled = not self.disabled +end + +function PreProcessor:PP_endif(args) + if self.disabled == nil then self:Error("Found #endif outside #ifdef block") end + if args:Trim() ~= "" then self:Error("Must not pass an argument to #endif") end + self.disabled = nil +end diff --git a/lua/entities/gmod_wire_expression2/base/tokenizer.lua b/lua/entities/gmod_wire_expression2/base/tokenizer.lua new file mode 100644 index 0000000000..b93025fa1c --- /dev/null +++ b/lua/entities/gmod_wire_expression2/base/tokenizer.lua @@ -0,0 +1,239 @@ +/******************************************************************************\ + Expression 2 Tokenizer for Garry's Mod + Andreas "Syranide" Svensson, me@syranide.com +\******************************************************************************/ + +AddCSLuaFile("tokenizer.lua") + +Tokenizer = {} +Tokenizer.__index = Tokenizer + +function Tokenizer.Execute(...) + -- instantiate Tokenizer + local instance = setmetatable({}, Tokenizer) + + -- and pcall the new instance's Process method. + return pcall(Tokenizer.Process, instance, ...) +end + +function Tokenizer:Error(message, offset) + error(message .. " at line " .. self.tokenline .. ", char " .. (self.tokenchar+(offset or 0)), 0) +end + +function Tokenizer:Process(buffer, params) + self.buffer = buffer + self.length = buffer:len() + self.position = 0 + + self:SkipCharacter() + + local tokens = {} + local tokenname, tokendata, tokenspace + self.tokendata = "" + + while self.character do + tokenspace = self:NextPattern("%s+") and true or false + + if !self.character then break end + + self.tokenline = self.readline + self.tokenchar = self.readchar + self.tokendata = "" + + tokenname, tokendata = self:NextSymbol() + + if tokenname == nil then + tokenname, tokendata = self:NextOperator() + + if tokenname == nil then + self:Error("Unknown character found (" .. self.character .. ")") + end + end + + tokens[#tokens + 1] = { tokenname, tokendata, tokenspace, self.tokenline, self.tokenchar } + end + + return tokens +end + +/******************************************************************************/ + +function Tokenizer:SkipCharacter() + if self.position < self.length then + if self.position > 0 then + if self.character == "\n" then + self.readline = self.readline + 1 + self.readchar = 1 + else + self.readchar = self.readchar + 1 + end + else + self.readline = 1 + self.readchar = 1 + end + + self.position = self.position + 1 + self.character = self.buffer:sub(self.position, self.position) + else + self.character = nil + end +end + +function Tokenizer:NextCharacter() + self.tokendata = self.tokendata .. self.character + self:SkipCharacter() +end + +-- Returns true on success, nothing if it fails. +function Tokenizer:NextPattern(pattern) + if not self.character then return false end + local startpos,endpos,text = self.buffer:find(pattern, self.position) + + if startpos ~= self.position then return false end + local buf = self.buffer:sub(startpos, endpos) + if not text then text = buf end + + self.tokendata = self.tokendata .. text + + + self.position = endpos + 1 + if self.position <= self.length then + self.character = self.buffer:sub(self.position, self.position) + else + self.character = nil + end + + buf = string.Explode("\n", buf) + if #buf > 1 then + self.readline = self.readline+#buf-1 + self.readchar = #buf[#buf]+1 + else + self.readchar = self.readchar + #buf[#buf] + end + return true +end + +function Tokenizer:NextSymbol() + local tokenname + + if self:NextPattern("^[0-9]+%.?[0-9]*") then + -- real/imaginary/quaternion number literals + local errorpos = self.tokendata:match("^0()[0-9]") or self.tokendata:find("%.$") + if self:NextPattern("^[eE][+-]?[0-9][0-9]*") then + errorpos = errorpos or self.tokendata:match("[eE][+-]?()0[0-9]") + end + + self:NextPattern("^[ijk]") + if self:NextPattern("^[a-zA-Z_]") then + errorpos = errorpos or self.tokendata:len() + end + + if errorpos then + self:Error("Invalid number format (" .. E2Lib.limitString(self.tokendata, 10) .. ")", errorpos-1) + end + + tokenname = "num" + + elseif self:NextPattern("^[a-z][a-zA-Z0-9_]*") then + -- keywords/functions + if self.tokendata == "if" then + tokenname = "if" + elseif self.tokendata == "elseif" then + tokenname = "eif" + elseif self.tokendata == "else" then + tokenname = "els" + elseif self.tokendata == "while" then + tokenname = "whl" + elseif self.tokendata == "for" then + tokenname = "for" + elseif self.tokendata == "break" then + tokenname = "brk" + elseif self.tokendata == "continue" then + tokenname = "cnt" + elseif self.tokendata:match("^[ijk]$") and self.character ~= "(" then + tokenname, self.tokendata = "num", "1"..self.tokendata + else + tokenname = "fun" + end + + elseif self:NextPattern("^[A-Z][a-zA-Z0-9_]*") then + -- variables + tokenname = "var" + + elseif self.character == "_" then + -- constants + self:NextCharacter() + self:NextPattern("^[A-Z0-9_]*") + + local value = wire_expression2_constants[self.tokendata] + + if type(value) == "number" then + tokenname = "num" + self.tokendata = value + elseif type(value) == "string" then + tokenname = "str" + self.tokendata = value + elseif type(value) == "nil" then + self:Error("Unknown constant found (_"..self.tokendata..")") + else + self:Error("Constant (_"..self.tokendata..") has invalid data type ("..type(value)..")") + end + + elseif self.character == "\"" then + -- strings + + -- skip opening quotation mark + self:SkipCharacter() + + -- loop until the closing quotation mark + while self.character != "\"" do + -- check for line/file endings + if self.character == "\n" or !self.character then + self:Error("Unterminated string (\"" .. E2Lib.limitString(self.tokendata, 10) .. ")") + end + + if self.character == "\\" then + self:SkipCharacter() + if self.character == "n" then + self.character = "\n" + elseif self.character == "t" then + self.character = "\t" + end + end + + self:NextCharacter() + end + -- skip closing quotation mark + self:SkipCharacter() + + tokenname = "str" + + else + -- nothing + return + end + + return tokenname, self.tokendata +end + +function Tokenizer:NextOperator() + local op = E2Lib.optable[self.character] + + if not op then return end + + while true do + self:NextCharacter() + + -- Check for the end of the string. + if not self.character then return op[1] end + + -- Check whether we are at a leaf and can't descend any further. + if not op[2] then return op[1] end + + -- Check whether we are at a node with no matching branches. + if not op[2][self.character] then return op[1] end + + -- branch + op = op[2][self.character] + end +end diff --git a/lua/entities/gmod_wire_expression2/cl_init.lua b/lua/entities/gmod_wire_expression2/cl_init.lua new file mode 100644 index 0000000000..554b17eae1 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/cl_init.lua @@ -0,0 +1,36 @@ +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_OPAQUE + +function wire_expression2_validate(buffer) + if CLIENT and not e2_function_data_received then return "Loading extensions. Please try again in a few seconds..." end + + -- invoke preprocessor + local status, directives, buffer = PreProcessor.Execute(buffer) + if not status then return directives end + + -- decompose directives + local name, inports, outports, persists = directives.name, directives.inputs, directives.outputs, directives.persist + if name == "" then name = "generic" end + + -- invoke tokenizer (=lexer) + local status, tokens = Tokenizer.Execute(buffer) + if not status then return tokens end + + -- invoke parser + local status, tree, dvars = Parser.Execute(tokens) + if not status then return tree end + + -- invoke compiler + local status, result = Compiler.Execute(tree, inports[3], outports[3], persists[3], dvars) + if not status then return result end +end + +-- On the server we need errors instead of return values, so we wrap and throw errors. +if SERVER then + local _wire_expression2_validate = wire_expression2_validate + function wire_expression2_validate(...) + local msg = _wire_expression2_validate(...) + if msg then error(msg,0) end + end +end diff --git a/lua/entities/gmod_wire_expression2/core/angle.lua b/lua/entities/gmod_wire_expression2/core/angle.lua new file mode 100644 index 0000000000..adc4829451 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/angle.lua @@ -0,0 +1,404 @@ +/******************************************************************************\ +Angle support +\******************************************************************************/ + +// wow... this is basically just vector-support, but renamed angle-support :P +// pitch, yaw, roll +registerType("angle", "a", { 0, 0, 0 }, + function(self, input) return { input.p, input.y, input.r } end, + function(self, output) return Angle(output[1], output[2], output[3]) end, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 3 then error("Return value does not have exactly 3 entries!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("ang", "", "a", function(self, args) + return { 0, 0, 0 } +end) + +__e2setcost(3) -- temporary + +registerFunction("ang", "nnn", "a", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + return { rv1, rv2, rv3 } +end) + +// Convert Vector -> Angle +registerFunction("ang", "v", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[1],rv1[2],rv1[3]} +end) + +/******************************************************************************/ + +registerOperator("ass", "a", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ + +registerOperator("is", "a", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] != 0 || rv1[2] != 0 || rv1[3] != 0 + then return 1 else return 0 end +end) + +registerOperator("eq", "aa", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta && + rv1[3] - rv2[3] <= delta && rv2[3] - rv1[3] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "aa", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta || rv2[1] - rv1[1] > delta || + rv1[2] - rv2[2] > delta || rv2[2] - rv1[2] > delta || + rv1[3] - rv2[3] > delta || rv2[3] - rv1[3] > delta + then return 1 else return 0 end +end) + +registerOperator("geq", "aa", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv2[1] - rv1[1] <= delta && + rv2[2] - rv1[2] <= delta && + rv2[3] - rv1[3] <= delta + then return 1 else return 0 end +end) + +registerOperator("leq", "aa", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && + rv1[2] - rv2[2] <= delta && + rv1[3] - rv2[3] <= delta + then return 1 else return 0 end +end) + +registerOperator("gth", "aa", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta && + rv1[2] - rv2[2] > delta && + rv1[3] - rv2[3] > delta + then return 1 else return 0 end +end) + +registerOperator("lth", "aa", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv2[1] - rv1[1] > delta && + rv2[2] - rv1[2] > delta && + rv2[3] - rv1[3] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ + +registerOperator("dlt", "a", "a", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3] } +end) + +registerOperator("neg", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2], -rv1[3] } +end) + +registerOperator("add", "aa", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2], rv1[3] + rv2[3] } +end) + +registerOperator("sub", "aa", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3] } +end) + +registerOperator("mul", "aa", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1], rv1[2] * rv2[2], rv1[3] * rv2[3] } +end) + +registerOperator("mul", "na", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2], rv1 * rv2[3] } +end) + +registerOperator("mul", "an", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2, rv1[3] * rv2 } +end) + +registerOperator("div", "na", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 / rv2[1], rv1 / rv2[2], rv1 / rv2[3] } +end) + +registerOperator("div", "an", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2, rv1[3] / rv2 } +end) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +registerFunction("angnorm", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {(rv1[1] + 180) % 360 - 180,(rv1[2] + 180) % 360 - 180,(rv1[3] + 180) % 360 - 180} +end) + +registerFunction("angnorm", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return (rv1 + 180) % 360 - 180 +end) + +registerFunction("pitch", "a:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] +end) + +registerFunction("yaw", "a:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[2] +end) + +registerFunction("roll", "a:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[3] +end) + +// SET methods that returns angles +registerFunction("setPitch", "a:n", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv2, rv1[2], rv1[3] } +end) + +registerFunction("setYaw", "a:n", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv2, rv1[3] } +end) + +registerFunction("setRoll", "a:n", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv2 } +end) + +/******************************************************************************/ + +registerFunction("round", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local p = rv1[1] - (rv1[1] + 0.5) % 1 + 0.5 + local y = rv1[2] - (rv1[2] + 0.5) % 1 + 0.5 + local r = rv1[3] - (rv1[3] + 0.5) % 1 + 0.5 + return {p, y, r} +end) + +registerFunction("round", "an", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local p = rv1[1] - ((rv1[1] * shf + 0.5) % 1 + 0.5) / shf + local y = rv1[2] - ((rv1[2] * shf + 0.5) % 1 + 0.5) / shf + local r = rv1[3] - ((rv1[3] * shf + 0.5) % 1 + 0.5) / shf + return {p, y, r} +end) + +// ceil/floor on p,y,r separately +registerFunction("ceil", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local p = rv1[1] - rv1[1] % -1 + local y = rv1[2] - rv1[2] % -1 + local r = rv1[3] - rv1[3] % -1 + return {p, y, r} +end) + +registerFunction("ceil", "an", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local p = rv1[1] - ((rv1[1] * shf) % -1) / shf + local y = rv1[2] - ((rv1[2] * shf) % -1) / shf + local r = rv1[3] - ((rv1[3] * shf) % -1) / shf + return {p, y, r} +end) + +registerFunction("floor", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local p = rv1[1] - rv1[1] % 1 + local y = rv1[2] - rv1[2] % 1 + local r = rv1[3] - rv1[3] % 1 + return {p, y, r} +end) + +registerFunction("floor", "an", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local p = rv1[1] - ((rv1[1] * shf) % 1) / shf + local y = rv1[2] - ((rv1[2] * shf) % 1) / shf + local r = rv1[3] - ((rv1[3] * shf) % 1) / shf + return {p, y, r} +end) + +// Performs modulo on p,y,r separately +registerFunction("mod", "an", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local p,y,r + if rv1[1] >= 0 then + p = rv1[1] % rv2 + else p = rv1[1] % -rv2 end + if rv1[2] >= 0 then + y = rv1[2] % rv2 + else y = rv1[2] % -rv2 end + if rv1[3] >= 0 then + r = rv1[3] % rv2 + else r = rv1[3] % -rv2 end + return {p, y, r} +end) + +// Modulo where divisors are defined as an angle +registerFunction("mod", "aa", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local p,y,r + if rv1[1] >= 0 then + p = rv1[1] % rv2[1] + else p = rv1[1] % -rv2[1] end + if rv1[2] >= 0 then + y = rv1[2] % rv2[2] + else y = rv1[2] % -rv2[2] end + if rv1[3] >= 0 then + y = rv1[3] % rv2[3] + else y = rv1[3] % -rv2[3] end + return {p, y, r} +end) + +// Clamp each p,y,r separately +registerFunction("clamp", "ann", "a", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local p,y,r + + if rv1[1] < rv2 then p = rv2 + elseif rv1[1] > rv3 then p = rv3 + else p = rv1[1] end + + if rv1[2] < rv2 then y = rv2 + elseif rv1[2] > rv3 then y = rv3 + else y = rv1[2] end + + if rv1[3] < rv2 then r = rv2 + elseif rv1[3] > rv3 then r = rv3 + else r = rv1[3] end + + return {p, y, r} +end) + +// Clamp according to limits defined by two min/max angles +registerFunction("clamp", "aaa", "a", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local p,y,r + + if rv1[1] < rv2[1] then p = rv2[1] + elseif rv1[1] > rv3[1] then p = rv3[1] + else p = rv1[1] end + + if rv1[2] < rv2[2] then y = rv2[2] + elseif rv1[2] > rv3[2] then y = rv3[2] + else y = rv1[2] end + + if rv1[3] < rv2[3] then r = rv2[3] + elseif rv1[3] > rv3[3] then r = rv3[3] + else r = rv1[3] end + + return {p, y, r} +end) + +// Mix two angles by a given proportion (between 0 and 1) +registerFunction("mix", "aan", "a", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local n + if rv3 < 0 then n = 0 + elseif rv3 > 1 then n = 1 + else n = rv3 end + local p = rv1[1] * n + rv2[1] * (1-n) + local y = rv1[2] * n + rv2[2] * (1-n) + local r = rv1[3] * n + rv2[3] * (1-n) + return {p, y, r} +end) + +// Circular shift function: shiftr( p,y,r ) = ( r,p,y ) +registerFunction("shiftR", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[3], rv1[1], rv1[2]} +end) + +registerFunction("shiftL", "a", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[2], rv1[3], rv1[1]} +end) + +/******************************************************************************/ + +e2function vector angle:forward() + return Angle(this[1], this[2], this[3]):Forward() +end + +e2function vector angle:right() + return Angle(this[1], this[2], this[3]):Right() +end + +e2function vector angle:up() + return Angle(this[1], this[2], this[3]):Up() +end + +e2function string toString(angle a) + return "[" .. tostring(a[1]) .. "," .. tostring(a[2]) .. "," .. tostring(a[3]) .. "]" +end + +e2function string angle:toString() = e2function string toString(angle a) + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/array.lua b/lua/entities/gmod_wire_expression2/core/array.lua new file mode 100644 index 0000000000..278a4fc650 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/array.lua @@ -0,0 +1,1030 @@ +/******************************************************************************\ + Array support +\******************************************************************************/ + +E2_MAX_ARRAY_SIZE = 1024*1024 // 1MB + +/******************************************************************************/ + +registerType("array", "r", {}, + function(self, input) + local ret = {} + self.prf = self.prf + #input / 3 + for k,v in pairs(input) do ret[k] = v end + return ret + end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + return retval + end +) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +registerFunction("array", "", "r", function(self, args) + return {} +end) + +--- Constructs an array with the given values as elements. If you specify types that are not supported by the array data type, the behaviour is undefined. +e2function array array(...) + local ret = { ... } + for i,v in ipairs(ret) do + if typeids[i] == "r" or typeids[i] == "t" then ret[i] = nil end + end + return ret +end + +/******************************************************************************/ + +e2function array operator=(array lhs, array rhs) + local lookup = self.data.lookup + + -- remove old lookup entry + if lookup[rhs] then lookup[rhs][lhs] = nil end + + -- add new lookup entry + local lookup_entry = lookup[rhs] + if not lookup_entry then + lookup_entry = {} + lookup[rhs] = lookup_entry + end + lookup_entry[lhs] = true + + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +/******************************************************************************/ + +registerOperator("is", "r", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return (type(rv1) == "table") and 1 or 0 +end) + +registerCallback("postinit", function() + -- retrieve information about all registered types + local types = table.Copy(wire_expression_types) + + -- change the name for numbers from "NORMAL" to "NUMBER" + types["NUMBER"] = types["NORMAL"] + types["NORMAL"] = nil + + -- we don't want tables and arrays as array elements, so get rid of them + types["TABLE"] = nil + types["ARRAY"] = nil + + -- generate op[] for all types + for name,id in pairs_map(types, unpack) do + + -- R:number() etc + local getter = name:lower() + + -- R:setNumber() etc + local setter = "set"..name:sub(1,1):upper()..name:sub(2):lower() + + local getf = wire_expression2_funcs[getter.."(r:n)"] + local setf = wire_expression2_funcs[setter.."(r:n"..id..")"] + + if getf then + local f = getf.oldfunc or getf[3] -- use oldfunc if present, else func + if getf then + registerOperator("idx", id.."=rn", id, f, getf[4], getf[5]) + end + end + if setf then + local f = setf.oldfunc or setf[3] -- use oldfunc if present, else func + if setf then + registerOperator("idx", id.."=rn"..id, id, f, setf[4], setf[5]) + end + end + end +end) + +/******************************************************************************/ + +registerFunction("count", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return #rv1 +end) + +/******************************************************************************/ + +registerFunction("clear", "r:", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + + self.prf = self.prf + #rv1 / 3 + + table.Empty(rv1) +end) + +registerFunction("clone", "r:", "r", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = {} + + self.prf = self.prf + #rv1 / 3 + + for k,v in pairs(rv1) do + ret[k] = v + end + + return ret +end) + +/******************************************************************************/ + +registerFunction("number", "r:n", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if ret then return tonumber(ret) or 0 end + return 0 +end) + +registerFunction("setNumber", "r:nn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end +// if rv3 == 0 then rv3 = nil end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("vector2", "r:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if type(ret) == "table" and table.getn(ret) == 2 then return ret end + return { 0, 0 } +end) + +registerFunction("setVector2", "r:nxv2", "xv2", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("vector", "r:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if (type(ret) == "table" and table.getn(ret) == 3) or type(ret) == "Vector" then return ret end + return { 0, 0, 0 } +end) + +registerFunction("setVector", "r:nv", "v", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end +// if rv3[1] == 0 and rv3[2] == 0 and rv3[3] == 0 then rv3 = nil end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("vector4", "r:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, 0, 0 } +end) + +registerFunction("setVector4", "r:nxv4", "xv4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("angle", "r:n", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("setAngle", "r:na", "a", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end +// if rv3[1] == 0 and rv3[2] == 0 and rv3[3] == 0 then rv3 = nil end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("matrix2", "r:n", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, + 0, 0 } +end) + +registerFunction("setMatrix2", "r:nxm2", "xm2", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("matrix", "r:n", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if type(ret) == "table" and table.getn(ret) == 9 then return ret end + return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } +end) + +registerFunction("setMatrix", "r:nm", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("matrix4", "r:n", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if type(ret) == "table" and table.getn(ret) == 16 then return ret end + return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } +end) + +registerFunction("setMatrix4", "r:nxm4", "xm4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("string", "r:n", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if ret then return tostring(ret) end + return "" +end) + +registerFunction("setString", "r:ns", "s", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end +// if rv3 == "" then rv3 = nil end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +registerFunction("entity", "r:n", "e", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[rv2] + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("setEntity", "r:ne", "e", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if(rv2 >= E2_MAX_ARRAY_SIZE) then return end + rv1[rv2] = rv3 + self.vclk[rv1] = true + return rv3 +end) + +/******************************************************************************/ + +registerFunction("pushNumber", "r:n", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if (rv2 == 0) then rv2 = nil end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popNumber", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if ret then return tonumber(ret) or 0 end + return 0 +end) + +registerFunction("pushVector2", "r:xv2", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popVector2", "r:", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 2 then return ret end + return { 0, 0 } +end) + +registerFunction("pushVector", "r:v", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popVector", "r:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if (type(ret) == "table" and table.getn(ret) == 3) or type(ret) == "Vector" then return ret end + return { 0, 0, 0 } +end) + +registerFunction("pushVector4", "r:xv4", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popVector4", "r:", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, 0, 0 } +end) + +registerFunction("pushAngle", "r:a", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popAngle", "r:", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("pushMatrix2", "r:xm2", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popMatrix2", "r:", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, + 0, 0 } +end) + +registerFunction("pushMatrix", "r:m", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popMatrix", "r:", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 9 then return ret end + return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } +end) + +registerFunction("pushMatrix4", "r:xm4", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popMatrix4", "r:", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 16 then return ret end + return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } +end) + +registerFunction("pushString", "r:s", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if (rv2 == "") then rv2 = nil end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popString", "r:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if ret then return tostring(ret) end + return "" +end) + +registerFunction("pushEntity", "r:e", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("popEntity", "r:", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1) + self.vclk[rv1] = true + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("pop", "r:", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + table.remove(rv1) + self.vclk[rv1] = true +end) + +/******************************************************************************/ + +registerFunction("insertNumber", "r:nn", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if (rv3 == 0) then rv3 = nil end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeNumber", "r:n", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if ret then return tonumber(ret) or 0 end + return 0 +end) + +registerFunction("insertVector2", "r:nxv2", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeVector2", "r:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 2 then return ret end + return { 0, 0 } +end) + +registerFunction("insertVector", "r:nv", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if rv3[1] == 0 and rv3[2] == 0 and rv3[3] == 0 then rv3 = nil end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeVector", "r:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if (type(ret) == "table" and table.getn(ret) == 3) or type(ret) == "Vector" then return ret end + return { 0, 0, 0 } +end) + +registerFunction("insertVector4", "r:nxv4", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeVector4", "r:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, 0, 0 } +end) + +registerFunction("insertAngle", "r:na", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if rv3[1] == 0 and rv3[2] == 0 and rv3[3] == 0 then rv3 = nil end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeAngle", "r:n", "a", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("insertMatrix2", "r:nxm2", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeMatrix2", "r:n", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, + 0, 0 } +end) + +registerFunction("insertMatrix", "r:nm", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeMatrix", "r:n", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 9 then return ret end + return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } +end) + +registerFunction("insertMatrix4", "r:nxm4", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeMatrix4", "r:n", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 16 then return ret end + return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } +end) + +registerFunction("insertString", "r:ns", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if (rv3 == "") then rv3 = nil end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeString", "r:n", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if ret then return tostring(ret) end + return "" +end) + +registerFunction("insertEntity", "r:ne", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,rv2,rv3) + self.vclk[rv1] = true +end) + +registerFunction("removeEntity", "r:n", "e", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = table.remove(rv1,rv2) + self.vclk[rv1] = true + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("remove", "r:n", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + table.remove(rv1,rv2) + self.vclk[rv1] = true +end) + +/******************************************************************************/ + +registerFunction("unshiftNumber", "r:n", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if (rv2 == 0) then rv2 = nil end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftNumber", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if ret then return tonumber(ret) or 0 end + return 0 +end) + +registerFunction("unshiftVector2", "r:xv2", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftVector2", "r:", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 2 then return ret end + return { 0, 0 } +end) + +registerFunction("unshiftVector", "r:v", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftVector", "r:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if (type(ret) == "table" and table.getn(ret) == 3) or type(ret) == "Vector" then return ret end + return { 0, 0, 0 } +end) + +registerFunction("unshiftVector4", "r:xv4", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftVector4", "r:", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, 0, 0 } +end) + +registerFunction("unshiftAngle", "r:a", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftAngle", "r:", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("unshiftMatrix2", "r:xm2", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftMatrix2", "r:", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 4 then return ret end + return { 0, 0, + 0, 0 } +end) + +registerFunction("unshiftMatrix", "r:m", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftMatrix", "r:", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 9 then return ret end + return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } +end) + +registerFunction("unshiftMatrix4", "r:xm4", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftMatrix4", "r:", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if type(ret) == "table" and table.getn(ret) == 16 then return ret end + return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } +end) + +registerFunction("unshiftString", "r:s", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end +// if (rv2 == "") then rv2 = nil end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftString", "r:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if ret then return tostring(ret) end + return "" +end) + +registerFunction("unshiftEntity", "r:e", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if ((table.getn(rv1)+1) >= E2_MAX_ARRAY_SIZE) then return end + table.insert(rv1,1,rv2) + self.vclk[rv1] = true +end) + +registerFunction("shiftEntity", "r:", "e", function(self, args) + local op1 = args[2] + local rv1= op1[1](self, op1) + local ret = table.remove(rv1,1) + self.vclk[rv1] = true + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("shift", "r:", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + table.remove(rv1,1) + self.vclk[rv1] = true +end) + +/******************************************************************************/ + +registerFunction("sum", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local out = 0 + + self.prf = self.prf + #rv1 / 2 + + for _,value in ipairs(rv1) do + out = out + (tonumber(value) or 0) + end + return out +end) + +registerFunction("average", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local totalValue = 0 + local totalIndex = 0 + local averageValue = 0 + + self.prf = self.prf + #rv1 / 2 + + for k,v in ipairs(rv1) do + if type( v ) == "number" then + totalValue = totalValue + rv1[k] + totalIndex = totalIndex + 1 + end + end + averageValue = totalValue / totalIndex + return averageValue +end) + +registerFunction("min", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local min = nil + + self.prf = self.prf + #rv1 / 2 + + for k,v in ipairs(rv1) do + if type( v ) == "number" then + if min == nil || v < min then + min = rv1[k] + end + end + end + if min == nil then min = 0 end + local ret = min + min = nil + return ret +end) + +registerFunction("minIndex", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local minIndex = 0 + local min = nil + + self.prf = self.prf + #rv1 / 2 + + for k,v in ipairs(rv1) do + if type( v ) == "number" then + if min == nil || v < min then + min = rv1[k] + minIndex = k + end + end + end + if min == nil then min = 0 end + local ret = minIndex + min = nil + return ret +end) + +registerFunction("max", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local ret = 0 + + self.prf = self.prf + #rv1 / 2 + + for k,v in ipairs(rv1) do + if type( v ) == "number" then + if v > ret then + ret = rv1[k] + end + end + end + return ret +end) + +registerFunction("maxIndex", "r:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local retIndex = 0 + local ret = 0 + + self.prf = self.prf + #rv1 / 2 + + for k,v in ipairs(rv1) do + if type( v ) == "number" then + if v > ret then + ret = rv1[k] + retIndex = k + end + end + end + return retIndex +end) + +/******************************************************************************/ + +registerFunction("concat", "r:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local out = "" + + self.prf = self.prf + #rv1 + + for _,value in ipairs(rv1) do + out = out .. tostring(value) + end + return out +end) + +registerFunction("concat", "r:s", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), tostring(op2[1](self, op2)) + local out = "" + + self.prf = self.prf + #rv1 + + for _,value in ipairs(rv1) do + out = out .. tostring(value) .. rv2 + end + return string.Left(out, string.len(out) - string.len(rv2)) +end) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/bitwise.lua b/lua/entities/gmod_wire_expression2/core/bitwise.lua new file mode 100644 index 0000000000..dbed779685 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/bitwise.lua @@ -0,0 +1,27 @@ +-- By asiekierka, 2009 -- +--Non-luabit XOR by TomyLobo-- +e2function number bAnd(a, b) + return (a & b) +end +e2function number bOr(a, b) + return (a | b) +end +e2function number bXor(a, b) + return (a | b) & (-1-(a & b)) +end +e2function number bShr(a, b) + return (a >> b) +end +e2function number bShl(a, b) + return (a << b) +end +e2function number bNot(n) + return (-1)-n +end +e2function number bNot(n,bits) + if bits >= 32 || bits < 1 then + return (-1)-n + else + return (math.pow(2,bits)-1)-n + end +end diff --git a/lua/entities/gmod_wire_expression2/core/bone.lua b/lua/entities/gmod_wire_expression2/core/bone.lua new file mode 100644 index 0000000000..391496d3a3 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/bone.lua @@ -0,0 +1,482 @@ +local isOwner = E2Lib.isOwner +local validEntity = E2Lib.validEntity +registerCallback("e2lib_replace_function", function(funcname, func, oldfunc) + if funcname == "isOwner" then + isOwner = func + elseif funcname == "validEntity" then + validEntity = func + end +end) + +local bone2entity = {} +local bone2index = {} +local entity2bone = {} + +hook.Add("EntityRemoved", "wire_expression2_bone", function(ent) + if not entity2bone[ent] then return end + for index,bone in pairs(entity2bone[ent]) do + bone2entity[bone] = nil + bone2index[bone] = nil + end + entity2bone[ent] = nil +end) + +registerType("bone", "b", nil, + nil, + nil, + function(retval) + if retval == nil then return end + if type(retval) ~= "PhysObj" then error("Return value is neither nil nor a PhysObj, but a "..type(retval).."!",0) end + if not bone2entity[retval] then error("Return value is not a registered bone!",0) end + end +) + +-- faster access to some math library functions +local abs = math.abs +local atan2 = math.atan2 +local sqrt = math.sqrt +local asin = math.asin +local Clamp = math.Clamp + +local rad2deg = 180 / math.pi + +function getBone(entity, index) + if not entity2bone[entity] then entity2bone[entity] = {} end + local bone = entity2bone[entity][index] + if not bone then + bone = entity:GetPhysicsObjectNum(index) + entity2bone[entity][index] = bone + end + if not bone then return nil end + if not bone:IsValid() then return nil end + + bone2entity[bone] = entity + bone2index[bone] = index + + return bone +end +E2Lib.getBone = getBone + +local function removeBone(bone) + bone2entity[bone] = nil + bone2index[bone] = nil +end + +-- checks whether the bone is valid. if yes, returns the bone's entity; otherwise, returns nil. +local function isValidBone(b) + if not b then return nil end + if not b:IsValid() then return nil end + local ent = bone2entity[b] + if not validEntity(ent) then + removeBone(b) + return nil + end + return ent +end +E2Lib.isValidBone = isValidBone + +-- Same as isValidBone, except that it returns the bone index as well. +local function isValidBone2(b) + if not b then return nil end + if not b:IsValid() then return nil end + local ent = bone2entity[b] + if not validEntity(ent) then + removeBone(b) + return nil, 0 + end + return ent, bone2index[b] +end +E2Lib.isValidBone2 = isValidBone2 + +-- checks whether the bone is valid. if yes, returns false; otherwise, returns true. +local function isInvalidBone(b) + if not b then return true end + if not b:IsValid() then return true end + if not validEntity(bone2entity[b]) then + removeBone(b) + return true + end + return false +end +E2Lib.isInvalidBone = isInvalidBone + +--- if (B) +e2function number operator_is(bone b) + if isInvalidBone(b) then return 0 else return 1 end +end + +--- B = B +e2function bone operator=(bone lhs, bone rhs) + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +--- B == B +e2function number operator==(bone lhs, bone rhs) + if lhs == rhs then return 1 else return 0 end +end + +--- B != B +e2function number operator!=(bone lhs, bone rhs) + if lhs ~= rhs then return 1 else return 0 end +end + +--[[************************************************************************]]-- + +--- Returns 's th bone. +e2function bone entity:bone(index) + if not validEntity(this) then return nil end + if index < 0 then return nil end + if index >= this:GetPhysicsObjectCount() then return nil end + return getBone(this, index) +end + +--- Returns an array containing all of 's bones. This array's first element has the index 0! +e2function array entity:bones() + if not validEntity(this) then return {} end + local ret = {} + local maxn = this:GetPhysicsObjectCount()-1 + for i = 0,maxn do + ret[i] = getBone(this, i) + end + return ret +end + +--- Returns 's number of bones. +e2function number entity:boneCount() + if not validEntity(this) then return 0 end + return this:GetPhysicsObjectCount() +end + +--- Returns an invalid bone. +e2function bone nobone() + return nil +end + +--- Returns the entity belongs to +e2function entity bone:entity() + return isValidBone(this) +end + +--- Returns 's index in the entity it belongs to. Returns -1 if the bone is invalid or an error occured. +e2function number bone:index() + if isInvalidBone(this) then return -1 end + --[[local ent = this:GetEntity() + if not validEntity(ent) then return -1 end + local maxn = ent:GetPhysicsObjectCount()-1 + for i = 0,maxn do + if this == ent:GetPhysicsObjectNum(i) then return i end + end + return -1]] + return bone2index[this] or -1 +end + +--[[************************************************************************]]-- + +--- Returns 's position. +e2function vector bone:pos() + if isInvalidBone(this) then return {0, 0, 0} end + return this:GetPos() +end + +--- Returns a vector describing 's forward direction. +e2function vector bone:forward() + if isInvalidBone(this) then return {0, 0, 0} end + return this:LocalToWorld(Vector(1,0,0))-this:GetPos() +end + +--- Returns a vector describing 's right direction. +e2function vector bone:right() + if isInvalidBone(this) then return {0, 0, 0} end + return this:LocalToWorld(Vector(0,-1,0))-this:GetPos() -- the y coordinate in local coords is left, not right. hence -1 +end + +--- Returns a vector describing 's up direction. +e2function vector bone:up() + if isInvalidBone(this) then return {0, 0, 0} end + return this:LocalToWorld(Vector(0,0,1))-this:GetPos() +end + +--- Returns 's velocity. +e2function vector bone:vel() + if isInvalidBone(this) then return {0, 0, 0} end + return this:GetVelocity() +end + +--- Returns 's velocity in local coordinates. +e2function vector bone:velL() + if isInvalidBone(this) then return {0, 0, 0} end + return this:WorldtoLocal(this:GetVelocity() + this:GetPos()) +end + +--[[************************************************************************]]-- + +--- Transforms from local coordinates (as seen from ) to world coordinates. +e2function vector bone:toWorld(vector pos) + if isInvalidBone(this) then return {0, 0, 0} end + return this:LocalToWorld(Vector(pos[1],pos[2],pos[3])) +end + +--- Transforms from world coordinates to local coordinates (as seen from ). +e2function vector bone:toLocal(vector pos) + if isInvalidBone(this) then return {0, 0, 0} end + return this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) +end + +--[[************************************************************************]]-- + +--- Returns 's angular velocity. +e2function angle bone:angVel() + if isInvalidBone(this) then return {0, 0, 0} end + local vec = this:GetAngleVelocity() + return { vec.y, vec.z, vec.x } +end + +--- Returns a vector describing rotation axis, magnitude and sense given as the vector's direction, magnitude and orientation. +e2function vector bone:angVelVector() + if isInvalidBone(this) then return {0, 0, 0} end + return this:GetAngleVelocity() +end + +--- Returns 's pitch, yaw and roll angles. +e2function angle bone:angles() + if isInvalidBone(this) then return {0, 0, 0} end + local ang = this:GetAngles() + return { ang.p, ang.y, ang.r } +end + +--[[************************************************************************]]-- + +--- Returns the bearing (yaw) from to . +e2function number bone:bearing(vector pos) + if isInvalidBone(this) then return 0 end + + pos = this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) + + return rad2deg*-atan2(pos.y, pos.x) +end + +--- Returns the elevation (pitch) from to . +e2function number bone:elevation(vector pos) + if isInvalidBone(this) then return 0 end + pos = this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) + + local len = pos:Length() + if len < delta then return 0 end + return rad2deg*asin(pos.z / len) +end + +--- Returns the elevation (pitch) and bearing (yaw) from to +e2function angle bone:heading(vector pos) + if isInvalidBone(this) then return {0, 0, 0} end + + pos = this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) + + -- bearing + local bearing = rad2deg*-atan2(pos.y, pos.x) + + -- elevation + local len = pos:Length()--sqrt(x*x + y*y + z*z) + if len < delta then return { 0, bearing, 0 } end + local elevation = rad2deg*asin(pos.z / len) + + return { elevation, bearing, 0 } +end + +--- Returns 's mass. +e2function number bone:mass() + if isInvalidBone(this) then return 0 end + return this:GetMass() +end + +--- Returns 's Center of Mass. +e2function vector bone:massCenter() + if isInvalidBone(this) then return {0, 0, 0} end + return this:LocalToWorld(this:GetMassCenter()) +end + +--- Returns 's Center of Mass in local coordinates. +e2function vector bone:massCenterL() + if isInvalidBone(this) then return {0, 0, 0} end + return this:GetMassCenter() +end + +--- Sets 's mass (between 0.001 and 50,000) +e2function void bone:setMass(mass) + local ent = isValidBone(this) + if not ent then return end + if not isOwner(self, ent) then return end + mass = Clamp(mass, 0.001, 50000) + this:SetMass(mass) +end + +--- Gets the principal components of 's inertia tensor in the form vec(Ixx, Iyy, Izz) +e2function vector bone:inertia() + if isInvalidBone(this) then return {0, 0, 0} end + return this:GetInertia() +end + +--[[************************************************************************]]-- + +--- Applies force to according to 's direction and magnitude +e2function void bone:applyForce(vector force) + local ent = isValidBone(this) + if not ent then return end + if not isOwner(self, ent) then return end + this:ApplyForceCenter(Vector(force[1], force[2], force[3])) +end + +--- Applies force to according to from the location of +e2function void bone:applyOffsetForce(vector force, vector pos) + local ent = isValidBone(this) + if not ent then return end + if not isOwner(self, ent) then return end + this:ApplyForceOffset(Vector(force[1], force[2], force[3]), Vector(pos[1], pos[2], pos[3])) +end + +--- Applies torque to according to +e2function void bone:applyAngForce(angle forceA) + local ent = isValidBone(this) + if not ent then return end + if not isOwner(self, ent) then return end + + -- assign vectors + local pos = this:LocalToWorld(this:GetMassCenter()) + local forward = this:LocalToWorld(Vector(1,0,0))-this:GetPos() + local left = this:LocalToWorld(Vector(0,1,0))-this:GetPos() -- the y coordinate in local coords is left, not right + local up = this:LocalToWorld(Vector(0,0,1))-this:GetPos() + + local pitch = up * (forceA[1]*0.5) + local yaw = forward * (forceA[2]*0.5) + local roll = left * (forceA[3]*0.5) + + -- apply pitch force + this:ApplyForceOffset( forward, pos + pitch ) + this:ApplyForceOffset( forward * -1, pos - pitch ) + + -- apply yaw force + this:ApplyForceOffset( left, pos + yaw ) + this:ApplyForceOffset( left * -1, pos - yaw ) + + -- apply roll force + this:ApplyForceOffset( up, pos + roll ) + this:ApplyForceOffset( up * -1, pos - roll ) +end + +--- Applies torque according to the axis, magnitude and sense given by the vector's direction, magnitude and orientation. +e2function void bone:applyTorque(vector torque) + local ent = isValidBone(this) + if not ent then return end + if not isOwner(self, ent) then return end + local phys = this + + local tq = Vector(torque[1], torque[2], torque[3]) + local torqueamount = tq:Length() + local off + if abs(torque[3]) > torqueamount*0.1 or abs(torque[1]) > torqueamount*0.1 then + off = Vector(-torque[3], 0, torque[1]) + else + off = Vector(-torque[2], torque[1], 0) + end + off:Normalize() + local dir = tq:Cross(off) + + dir = phys:LocalToWorld(dir)-phys:GetPos() + local masscenter = phys:GetMassCenter() + phys:ApplyForceOffset( dir * 0.5, phys:LocalToWorld(masscenter+off) ) + phys:ApplyForceOffset( dir * -0.5, phys:LocalToWorld(masscenter-off) ) +end + +--[[************************************************************************]]-- + +--- Returns 1 if is frozen, 0 otherwise +e2function number bone:isFrozen() + if isInvalidBone(this) then return end + if this:IsMoveable() then return 0 else return 1 end +end + +-- helper function for invert(T) in table.lua +function e2_tostring_bone(b) + local ent = isValidBone(b) + if not ent then return "(null)" end + return string.format("%s:bone(%d)", tostring(ent), bone2index[b]) +end + +--- Returns formatted as a string. Returns "(null)" for invalid bones. +e2function string toString(bone b) + local ent = isValidBone(b) + if not ent then return "(null)" end + return string.format("%s:bone(%d)", tostring(ent), bone2index[b]) +end + +--[[************************************************************************]]-- + +--- Returns the th element of . Returns nobone() if it's not a '''bone'''. +--- @nodoc +e2function bone array:bone(index) + local ret = this[index] + if isInvalidBone(ret) then return nil end + return ret +end + +--- Sets as the th element of . +--- @nodoc +e2function void array:setBone(index, bone value) + if index >= E2_MAX_ARRAY_SIZE then return nil end + this[index] = value +end + +--- Inserts as the last element of . +--- @nodoc +e2function void array:pushBone(bone value) + if table.getn(this)+1 >= E2_MAX_ARRAY_SIZE then return end + table.insert(this, value) +end + +--- Removes the last element of and returns it. Returns nobone() if it's not a '''bone'''. +--- @nodoc +e2function bone array:popBone() + local ret = table.remove(this) + self.vclk[args[2][2]] = true + if isInvalidBone(ret) then return nil end + return ret +end + +--- Inserts as the th element of . +--- @nodoc +e2function void array:insertBone(index, bone value) + if table.getn(this)+1 >= E2_MAX_ARRAY_SIZE then return end + table.insert(this, index, value) + self.vclk[args[2][2]] = true +end + +--- Removes the th element of and returns it. Returns nobone() if it's not a '''bone'''. +--- @nodoc +e2function bone array:removeBone(index) + local ret = table.remove(this, index) + self.vclk[args[2][2]] = true + if isInvalidBone(ret) then return nil end + return ret +end + +--- Inserts as the first element of . +--- @nodoc +e2function void array:unshiftBone(bone value) + if table.getn(this)+1 >= E2_MAX_ARRAY_SIZE then return end + table.insert(this, 1, value) + self.vclk[args[2][2]] = true +end + +--- Removes the first element of and returns it. Returns nobone() if it's not a '''bone'''. +--- @nodoc +e2function bone array:shiftBone() + local ret = table.remove(this, 1) + self.vclk[args[2][2]] = true + if isInvalidBone(ret) then return nil end + return ret +end + +--[[************************************************************************]]-- + +-- TODO: constraints diff --git a/lua/entities/gmod_wire_expression2/core/chat.lua b/lua/entities/gmod_wire_expression2/core/chat.lua new file mode 100644 index 0000000000..b3b91986d7 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/chat.lua @@ -0,0 +1,145 @@ +//Original author: ZeikJT +//Modified by Gwahir and TomyLobo + +local validEntity = validEntity + +local TextList = { + last = { "", 0, nil } +} +local ChatAlert = {} +local runByChat = 0 +local chipHideChat = false + +--[[************************************************************************]]-- + +registerCallback("destruct",function(self) + ChatAlert[self.entity] = nil +end) + +hook.Add("PlayerSay","Exp2TextReceiving", function(ply, text, toall) + local entry = { text, CurTime(), ply, toall } + TextList[ply:EntIndex()] = entry + TextList.last = entry + + runByChat = 1 + chipHideChat = false + local hideCurrent = false + for e,_ in pairs(ChatAlert) do + if validEntity(e) then + chipHideChat = nil + e:Execute() + --if chipHideChat ~= nil and ply == e.player then + if chipHideChat and ply == e.player then + hideCurrent = chipHideChat + end + else + ChatAlert[e] = nil + end + end + runByChat = 0 + + if hideCurrent then return "" end +end) + +hook.Add("EntityRemoved","Exp2ChatPlayerDisconnect", function(ply) + TextList[ply:EntIndex()] = nil +end) + +--[[************************************************************************]]-- + +--- If == 0, the chip will no longer run on chat events, otherwise it makes this chip execute when someone chats. Only needs to be called once, not in every execution. +e2function void runOnChat(activate) + if activate ~= 0 then + ChatAlert[self.entity] = true + else + ChatAlert[self.entity] = nil + end +end + +--- Returns 1 if the chip is being executed because of a chat event. Returns 0 otherwise. +e2function number chatClk() + return runByChat +end + +--- Returns 1 if the chip is being executed because of a chat event by player . Returns 0 otherwise. +e2function number chatClk(entity ply) + if not validEntity(ply) then return 0 end + if ply ~= TextList.last[3] then return 0 end + return runByChat +end + +--- If != 0, hide the chat message that is currently being processed. +e2function void hideChat(hide) + chipHideChat = hide ~= 0 +end + +--[[************************************************************************]]-- + +--- Returns the last player to speak. +e2function entity lastSpoke() + local entry = TextList.last + if not entry then return nil end + + local ply = entry[3] + if not validEntity(ply) then return nil end + if not ply:IsPlayer() then return nil end + + return ply +end + +--- Returns the last message in the chat log. +e2function string lastSaid() + local entry = TextList.last + if not entry then return "" end + + return entry[1] +end + +--- Returns the time the last message was sent. +e2function number lastSaidWhen() + local entry = TextList.last + if not entry then return 0 end + + return entry[2] +end + +--- Returns 1 if the last message was sent in the team chat, 0 otherwise. +e2function number lastSaidTeam() + local entry = TextList.last + if not entry then return 0 end + + return entry[4] and 0 or 1 +end + +--- Returns what the player last said. +e2function string entity:lastSaid() + if not validEntity(this) then return "" end + if not this:IsPlayer() then return "" end + + local entry = TextList[this:EntIndex()] + if not entry then return "" end + + return entry[1] +end + +--- Returns when the given player last said something. +e2function number entity:lastSaidWhen() + if not validEntity(this) then return 0 end + if not this:IsPlayer() then return 0 end + + local entry = TextList[this:EntIndex()] + if not entry then return 0 end + + return entry[2] +end + +--- Returns 1 if the last message was sent in the team chat, 0 otherwise. +e2function number entity:lastSaidTeam() + if not validEntity(this) then return 0 end + if not this:IsPlayer() then return 0 end + + local entry = TextList[this:EntIndex()] + if not entry then return 0 end + + return entry[4] and 0 or 1 +end diff --git a/lua/entities/gmod_wire_expression2/core/cl_console.lua b/lua/entities/gmod_wire_expression2/core/cl_console.lua new file mode 100644 index 0000000000..4f66fc256c --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/cl_console.lua @@ -0,0 +1 @@ +CreateClientConVar("wire_expression2_concmd", 0, true, true) diff --git a/lua/entities/gmod_wire_expression2/core/cl_debug.lua b/lua/entities/gmod_wire_expression2/core/cl_debug.lua new file mode 100644 index 0000000000..7e48286c6a --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/cl_debug.lua @@ -0,0 +1,3 @@ +datastream.Hook("wire_expression2_printColor", function( ply, handle, id, printinfo ) + chat.AddText(unpack(printinfo)) +end) diff --git a/lua/entities/gmod_wire_expression2/core/cl_files.lua b/lua/entities/gmod_wire_expression2/core/cl_files.lua new file mode 100644 index 0000000000..928ac5138f --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/cl_files.lua @@ -0,0 +1,31 @@ +-------------------------------------------------------------------------------- +-- File extension by {iG} I_am_McLovin -- +-------------------------------------------------------------------------------- + +if not datastream then require( "datastream" ) end + +usermessage.Hook( "wire_expression2_fileload", function( um ) + local raw_filename = um:ReadString() + local filename = "e2files/"..raw_filename + if string.find(filename, "..", 1, true) then return end + + if file.Exists( filename ) and file.Size( filename ) <= 102400 then + local filedata = file.Read( filename ) + datastream.StreamToServer( "wire_expression2_filedata", { filename = raw_filename, filedata = filedata } ) + end +end ) + +datastream.Hook( "wire_expression2_filewrite", function( handler, id, encoded, decoded ) + local file_name = "e2files/"..decoded.name + if string.find(file_name, "..", 1, true) then return end + + local old_file = "" + + if decoded.append then + if file.Exists( file_name ) then + old_file = file.Read( file_name ) + end + end + + file.Write( file_name, old_file .. decoded.data ) +end ) diff --git a/lua/entities/gmod_wire_expression2/core/cl_hologram.lua b/lua/entities/gmod_wire_expression2/core/cl_hologram.lua new file mode 100644 index 0000000000..b61330a75e --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/cl_hologram.lua @@ -0,0 +1,28 @@ +if not datastream then require( "datastream" ) end + +local hologram_owners = {} + +datastream.Hook( "wire_holograms_owners", function( ply, handler, id, encoded, decoded ) + + for k,v in pairs( encoded[1] ) do + if v.owner and v.owner:IsValid() and v.hologram and v.hologram:IsValid() then + table.insert( hologram_owners, { ["owner"] = v.owner, ["hologram"] = v.hologram } ) + end + end + + hook.Add( "HUDPaint", "draw_wire_hologram_owners", function( ) + for k,v in pairs( hologram_owners ) do + if v.owner and v.owner:IsValid() and v.owner:IsPlayer() and v.hologram and v.hologram:IsValid() then + local vec = v.hologram:GetPos():ToScreen() + draw.DrawText(v.owner:Name(), "ScoreboardText", vec.x, vec.y, Color(255,0,0,255), 1) + end + end + end ) + +end) + +-- this function is called from the client side +function wire_holograms_remove_owners_display() + hook.Remove( "HUDPaint", "draw_wire_hologram_owners" ) + hologram_owners = {} +end diff --git a/lua/entities/gmod_wire_expression2/core/color.lua b/lua/entities/gmod_wire_expression2/core/color.lua new file mode 100644 index 0000000000..3f78b1fb88 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/color.lua @@ -0,0 +1,149 @@ +/******************************************************************************\ + Colour support +\******************************************************************************/ + +local Clamp = math.Clamp + +local function ColorClamp(col1, col2, col3, col4) + return Clamp(col1, 0, 255), Clamp(col2, 0, 255), Clamp(col3, 0, 255), Clamp(col4, 0, 255) +end + +/******************************************************************************/ + +registerFunction("getColor", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if !validEntity(rv1) then return {0,0,0} end + + local r,g,b = rv1:GetColor() + return { r, g, b } +end) + +registerFunction("getColor4", "e:", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if !validEntity(rv1) then return {0,0,0,0} end + + return { rv1:GetColor() } +end) + +registerFunction("getAlpha", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if !validEntity(rv1) then return 0 end + + local _,_,_,alpha = rv1:GetColor() + return alpha +end) + +registerFunction("setColor", "e:nnn", "", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + + local _,_,_,alpha = rv1:GetColor() + rv1:SetColor(ColorClamp(rv2, rv3, rv4, alpha)) +end) + +registerFunction("setColor", "e:nnnn", "", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + + if rv1:IsPlayer() or rv1:IsWeapon() then rv5 = 255 end + + rv1:SetColor(ColorClamp(rv2, rv3, rv4, rv5)) +end) + +registerFunction("setColor", "e:v", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + + local _,_,_,alpha = rv1:GetColor() + rv1:SetColor(ColorClamp(rv2[1], rv2[2], rv2[3], alpha)) +end) + +registerFunction("setColor", "e:vn", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + + if rv1:IsPlayer() or rv1:IsWeapon() then rv3 = 255 end + + rv1:SetColor(ColorClamp(rv2[1], rv2[2], rv2[3], rv3)) +end) + +registerFunction("setColor", "e:xv4", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + + local alpha + if rv1:IsPlayer() or rv1:IsWeapon() then + alpha = 255 + else + alpha = rv2[4] + end + + rv1:SetColor(ColorClamp(rv2[1], rv2[2], rv2[3], alpha)) +end) + +registerFunction("setAlpha", "e:n", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + + if rv1:IsPlayer() or rv1:IsWeapon() then return end + + local r,g,b = rv1:GetColor() + rv1:SetColor(r, g, b, Clamp(rv2, 0, 255)) +end) + +--- Converts from the [http://en.wikipedia.org/wiki/HSV_color_space HSV color space] to the [http://en.wikipedia.org/wiki/RGB_color_space RGB color space] +e2function vector hsv2rgb(vector hsv) + local col = HSVToColor(hsv[1], hsv[2], hsv[3]) + return { col.r, col.g, col.b } +end + +--- Converts from the [http://en.wikipedia.org/wiki/RGB_color_space RGB color space] to the [http://en.wikipedia.org/wiki/HSV_color_space HSV color space] +e2function vector rgb2hsv(vector rgb) + return { ColorToHSV(Color(rgb[1], rgb[2], rgb[3])) } +end + +local floor = math.floor +local Clamp = math.Clamp +local converters = {} +converters[0] = function(r, g, b) + local r = Clamp(floor(r/28),0,9) + local g = Clamp(floor(g/28),0,9) + local b = Clamp(floor(b/28),0,9) + + return floor(r)*100000+floor(g)*10000+floor(b)*1000 +end +converters[2] = function(r, g, b) + return floor(r)*65536+floor(g)*256+floor(b) +end +converters[3] = function(r, g, b) + return floor(r)*1000000+floor(g)*1000+floor(b) +end + +--- Converts an RGB vector to a number in digital screen format. Specifies a mode, either 0, 2 or 3, corresponding to Digital Screen color modes. +e2function number rgb2digi(vector rgb, mode) + conv = converters[mode] + if not conv then return 0 end + return conv(rgb[1], rgb[2], rgb[3]) +end + +--- Converts the RGB color (,,) to a number in digital screen format. Specifies a mode, either 0, 2 or 3, corresponding to Digital Screen color modes. +e2function number rgb2digi(r, g, b, mode) + conv = converters[mode] + if not conv then return 0 end + return conv(r, g, b) +end diff --git a/lua/entities/gmod_wire_expression2/core/compat.lua b/lua/entities/gmod_wire_expression2/core/compat.lua new file mode 100644 index 0000000000..926282134a --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/compat.lua @@ -0,0 +1,33 @@ +-- Functions for backwards-compatibility. Might be removed at any time... + +registerFunction("teamName", "n:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local str = team.GetName(rv1) + if not str then return "" end + return str +end) + +registerFunction("teamScore", "n:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.GetScore(rv1) +end) + +registerFunction("teamPlayers", "n:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.NumPlayers(rv1) +end) + +registerFunction("teamDeaths", "n:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.TotalDeaths(rv1) +end) + +registerFunction("teamFrags", "n:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.TotalFrags(rv1) +end) diff --git a/lua/entities/gmod_wire_expression2/core/complex.lua b/lua/entities/gmod_wire_expression2/core/complex.lua new file mode 100644 index 0000000000..aeb8943f1b --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/complex.lua @@ -0,0 +1,339 @@ +/******************************************************************************\ + Complex numbers support +\******************************************************************************/ + +-- faster access to some math library functions +local abs = math.abs +local Round = math.Round +local sqrt = math.sqrt +local exp = math.exp +local log = math.log +local sin = math.sin +local cos = math.cos +local sinh = math.sinh +local cosh = math.cosh +local acos = math.acos + +local function format(value) + local dbginfo + + if abs(value[1]) < delta then + if abs(value[2]) < delta then + dbginfo = "0" + else + dbginfo = Round(value[2]*1000)/1000 .. "i" + end + else + if value[2] > delta then + dbginfo = Round(value[1]*1000)/1000 .. "+" .. Round(value[2]*1000)/1000 .. "i" + elseif abs(value[2]) <= delta then + dbginfo = Round(value[1]*1000)/1000 + elseif value[2] < -delta then + dbginfo = Round(value[1]*1000)/1000 .. Round(value[2]*1000)/1000 .. "i" + end + end + return dbginfo +end +WireLib.registerDebuggerFormat("COMPLEX", format) + +/******************************************************************************/ + +registerType("complex", "c", { 0, 0 }, + function(self, input) return { input[1], input[2] } end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 2 then error("Return value does not have exactly 2 entries!",0) end + end +) + +/******************************************************************************/ + +local function cexp(x,y) + return {exp(x)*cos(y), exp(x)*sin(y)} +end + +local function clog(x,y) + local r,i,l + + l = x*x+y*y + + if l==0 then return {0, 0} end + + r = log(sqrt(l)) + + local c,s + c = x/sqrt(l) + + i = acos(c) + if y<0 then i = -i end + + return {r, i} +end + +local function cdiv(a,b) + local l=b[1]*b[1]+b[2]*b[2] + + return {(a[1]*b[1]+a[2]*b[2])/l, (a[2]*b[1]-a[1]*b[2])/l} +end + +/******************************************************************************/ + +e2function complex operator=(complex lhs, complex rhs) + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +e2function number operator_is(complex z) + if (z[1]==0) && (z[2]==0) then return 0 else return 1 end +end + +e2function number operator==(complex lhs, complex rhs) + if abs(lhs[1]-rhs[1])<=delta && + abs(lhs[2]-rhs[2])<=delta then + return 1 + else return 0 end +end + +e2function number operator==(complex lhs, number rhs) + if abs(lhs[1]-rhs)<=delta && + abs(lhs[2])<=delta then + return 1 + else return 0 end +end + +e2function number operator==(number lhs, complex rhs) + if abs(lhs-rhs[1])<=delta && + abs(rhs[2])<=delta then + return 1 + else return 0 end +end + +e2function number operator!=(complex lhs, complex rhs) + if abs(lhs[1]-rhs[1])>delta || + abs(lhs[2]-rhs[2])>delta then + return 1 + else return 0 end +end + +e2function number operator!=(complex lhs, number rhs) + if abs(lhs[1]-rhs)>delta || + abs(lhs[2])>delta then + return 1 + else return 0 end +end + +e2function number operator!=(number lhs, complex rhs) + if abs(lhs-rhs[1])>delta || + abs(rhs[2])>delta then + return 1 + else return 0 end +end + +/******************************************************************************/ + +e2function complex operator_neg(complex z) + return {-z[1], -z[2]} +end + +e2function complex operator+(complex lhs, complex rhs) + return {lhs[1]+rhs[1], lhs[2]+rhs[2]} +end + +e2function complex operator+(number lhs, complex rhs) + return {lhs+rhs[1], rhs[2]} +end + +e2function complex operator+(complex lhs, number rhs) + return {lhs[1]+rhs, lhs[2]} +end + +e2function complex operator-(complex lhs, complex rhs) + return {lhs[1]-rhs[1], lhs[2]-rhs[2]} +end + +e2function complex operator-(number lhs, complex rhs) + return {lhs-rhs[1], -rhs[2]} +end + +e2function complex operator-(complex lhs, number rhs) + return {lhs[1]-rhs, lhs[2]} +end + +e2function complex operator*(complex lhs, complex rhs) + return {lhs[1]*rhs[1]-lhs[2]*rhs[2], lhs[2]*rhs[1]+lhs[1]*rhs[2]} +end + +e2function complex operator*(number lhs, complex rhs) + return {lhs*rhs[1], lhs*rhs[2]} +end + +e2function complex operator*(complex lhs, number rhs) + return {lhs[1]*rhs, lhs[2]*rhs} +end + +e2function complex operator/(complex lhs, complex rhs) + local z = rhs[1]*rhs[1] + rhs[2]*rhs[2] + return {(lhs[1]*rhs[1]+lhs[2]*rhs[2])/z, (lhs[2]*rhs[1]-lhs[1]*rhs[2])/z} +end + +e2function complex operator/(number lhs, complex rhs) + local z = rhs[1]*rhs[1] + rhs[2]*rhs[2] + return {lhs*rhs[1]/z, -lhs*rhs[2]/z} +end + +e2function complex operator/(complex lhs, number rhs) + return {lhs[1]/rhs, lhs[2]/rhs} +end + +e2function complex operator^(complex lhs, complex rhs) + local l = clog(lhs[1], lhs[2]) + local e = {rhs[1]*l[1] - rhs[2]*l[2], rhs[1]*l[2] + rhs[2]*l[1]} + return cexp(e[1], e[2]) +end + +e2function complex operator^(complex lhs, number rhs) + local l = clog(lhs[1], lhs[2]) + return cexp(rhs*l[1], rhs*l[2]) +end + +/******************************** constructors ********************************/ + +--- Returns complex zero +e2function complex comp() + return {0, 0} +end + +--- Converts a real number to complex (returns complex number with real part and imaginary part 0) +e2function complex comp(a) + return {a, 0} +end + +--- Returns +*i +e2function complex comp(a, b) + return {a, b} +end + +--- Returns the imaginary unit i +e2function complex i() + return {0, 1} +end + +--- Returns *i +e2function complex i(b) + return {0, b} +end + +/****************************** helper functions ******************************/ + +--- Returns the absolute value of +e2function number abs(complex z) + return sqrt(z[1]*z[1] + z[2]*z[2]) +end + +--- Returns the argument of +e2function number arg(complex z) + local l = z[1]*z[1]+z[2]*z[2] + if l==0 then return 0 end + local c = z[1]/sqrt(l) + local p = acos(c) + if z[2]<0 then p = -p end + return p +end + +--- Returns the conjugate of +e2function complex conj(complex z) + return {z[1], -z[2]} +end + +--- Returns the real part of +e2function number real(complex z) + return z[1] +end + +--- Returns the imaginary part of +e2function number imag(complex z) + return z[2] +end + +/***************************** exp and logarithms *****************************/ + +--- Raises Euler's constant e to the power of +e2function complex exp(complex z) + return cexp(z[1], z[2]) +end + +--- Calculates the natural logarithm of +e2function complex log(complex z) + return clog(z[1], z[2]) +end + +--- Calculates the logarithm of to a complex base +e2function complex log(complex base, complex z) + return cdiv(clog(z),clog(base)) +end + +--- Calculates the logarithm of to a real base +e2function complex log(number base, complex z) + local l=clog(z) + return {l[1]/log(base), l[2]/log(base)} +end + +--- Calculates the logarithm of to base 2 +e2function complex log2(complex z) + local l=clog(z) + return {l[1]/log(2), l[2]/log(2)} +end + +--- Calculates the logarithm of to base 10 +e2function complex log10(complex z) + local l=clog(z) + return {l[1]/log(10), l[2]/log(10)} +end + +/******************************************************************************/ + +--- Calculates the square root of +e2function complex sqrt(complex z) + local l = clog(z[1], z[2]) + return cexp(0.5*l[1], 0.5*l[2]) +end + +--- Calculates the complex square root of the real number +e2function complex csqrt(n) + if n<0 then + return {0, sqrt(-n)} + else + return {sqrt(n), 0} + end +end + +/******************* trigonometric and hyperbolic functions *******************/ + +--- Calculates the sine of +e2function complex sin(complex z) + return {sin(z[1])*cosh(z[2]), sinh(z[2])*cos(z[1])} +end + +--- Calculates the cosine of +e2function complex cos(complex z) + return {cos(z[1])*cosh(z[2]), -sin(z[1])*sinh(z[2])} +end + +--- Calculates the hyperbolic sine of +e2function complex sinh(complex z) + return {sinh(z[1])*cos(z[2]), sin(z[2])*cosh(z[1])} +end + +--- Calculates the hyperbolic cosine of +e2function complex cosh(complex z) + return {cosh(z[1])*cos(z[2]), sinh(z[1])*sin(z[2])} +end + +/******************************************************************************/ + +--- Formats as a string. +e2function string toString(complex z) + return format(z) +end diff --git a/lua/entities/gmod_wire_expression2/core/console.lua b/lua/entities/gmod_wire_expression2/core/console.lua new file mode 100644 index 0000000000..9638b13cc4 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/console.lua @@ -0,0 +1,34 @@ +/******************************************************************************\ + Console support +\******************************************************************************/ + +local function validConCmd(self) + if not self.player:IsValid() then return false end + return self.player:GetInfoNum("wire_expression2_concmd") ~= 0 +end + +--[[ +e2function trace(string message) + self.player:Msg(message .. "\n") +end +]] + +e2function number concmd(string command) + if not validConCmd(self) then return 0 end + self.player:ConCommand(command) + return 1 +end + +e2function string convar(string cvar) + if not validConCmd(self) then return "" end + local ret = self.player:GetInfo(cvar) + if not ret then return "" end + return ret +end + +e2function number convarnum(string cvar) + if not validConCmd(self) then return 0 end + local ret = self.player:GetInfoNum(cvar) + if not ret then return 0 end + return ret +end diff --git a/lua/entities/gmod_wire_expression2/core/constraint.lua b/lua/entities/gmod_wire_expression2/core/constraint.lua new file mode 100644 index 0000000000..e616b4ae30 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/constraint.lua @@ -0,0 +1,138 @@ +/******************************************************************************\ + Constraint support V1.5 +\******************************************************************************/ + +//---------------------------// +//--Helper Functions--// +//---------------------------// + +-- caps("heLlO") == "Hello" +local function caps(text) + local capstext = text:sub(1,1):upper() .. text:sub(2):lower() + if capstext == "Nocollide" then return "NoCollide" end + if capstext == "Advballsocket" then return "AdvBallsocket" end + return capstext +end + +-- Returns con.Ent1 or con.Ent2, whichever is not equivalent to ent. Optionally subscripts con with num beforehand. +local function ent1or2(ent,con,num) + if not con then return nil end + if num then + con = con[num] + if not con then return nil end + end + if con.Ent1==ent then return con.Ent2 end + return con.Ent1 +end + +/******************************************************************************/ + +--- Returns an '''array''' containing all entities directly or indirectly constrained to , except itself. +e2function array entity:getConstraints() + if not validEntity(this) then return {} end + if not constraint.HasConstraints(this) then return {} end + + local keytable = constraint.GetAllConstrainedEntities(this) + local array = {} + local count = 0 + for _,ent in pairs(keytable) do + if validEntity(ent) and ent ~= this then + table.insert(array, ent) + end + end + return array +end + +--- Returns the number of constraints on . +e2function number entity:hasConstraints() + if not validEntity(this) then return 0 end + + return #constraint.GetTable(this) +end + +--- Returns the number of constraints of type on . +e2function number entity:hasConstraints(string constraintType) + if not validEntity(this) then return 0 end + + local constype = caps(constraintType) + local ConTable = constraint.GetTable(this) + local count = 0 + for k, con in ipairs(ConTable) do + if con.Type == constype then + count = count + 1 + end + end + return count +end + +--- Returns 1 if is constrained to anything, 0 otherwise. +e2function number entity:isConstrained() + if not validEntity(this) then return 0 end + if not constraint.HasConstraints(this) then return 0 end + + return 1 +end + +--- Returns the first entity was welded to. +e2function entity entity:isWeldedTo() + if not validEntity(this) then return nil end + if not constraint.HasConstraints(this) then return nil end + + return ent1or2(this,constraint.FindConstraint(this, "Weld")) +end + +--- Returns the th entity was welded to. +e2function entity entity:isWeldedTo(index) + if not validEntity(this) then return nil end + if not constraint.HasConstraints(this) then return nil end + + return ent1or2(this,constraint.FindConstraints(this, "Weld"), math.floor(index)) +end + +--- Returns the first entity was constrained to. +e2function entity entity:isConstrainedTo() + if not validEntity(this) then return 0 end + if not constraint.HasConstraints(this) then return nil end + + return ent1or2(this,constraint.GetTable(this),1) +end + +--- Returns the th entity was constrained to. +e2function entity entity:isConstrainedTo(index) + if not validEntity(this) then return 0 end + if not constraint.HasConstraints(this) then return nil end + + return ent1or2(this,constraint.GetTable(this), math.floor(index)) +end + +--- Returns the first entity was constrained to with the given constraint type . +e2function entity entity:isConstrainedTo(string constraintType) + if not validEntity(this) then return nil end + if not constraint.HasConstraints(this) then return nil end + + return ent1or2(this,constraint.FindConstraint(this, caps(constraintType))) +end + +--- Returns the th entity was constrained to with the given constraint type . +e2function entity entity:isConstrainedTo(string constraintType, index) + if not validEntity(this) then return nil end + if not constraint.HasConstraints(this) then return nil end + + return ent1or2(this,constraint.FindConstraints(this, caps(constraintType)), math.floor(index)) +end + +--- Returns the '''entity''' is parented to. +e2function entity entity:parent() + if not validEntity(this) then return nil end + return this:GetParent() +end + +--- Returns the '''bone''' is parented to. +e2function bone entity:parentBone() + if not validEntity(this) then return nil end + + local ent = this:GetParent() + if not validEntity(ent) then return nil end + local bonenum = this:GetParentPhysNum() + return getBone(ent, bonenum) +end diff --git a/lua/entities/gmod_wire_expression2/core/core.lua b/lua/entities/gmod_wire_expression2/core/core.lua new file mode 100644 index 0000000000..204f2c8830 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/core.lua @@ -0,0 +1,317 @@ +/******************************************************************************\ + Core language support +\******************************************************************************/ + +local delta = wire_expression2_delta + +__e2setcost(1) -- approximation + +registerOperator("dat", "", "", function(self, args) + return args[2] +end) + +__e2setcost(2) -- approximation + +registerOperator("var", "", "", function(self, args) + return self.vars[args[2]] +end) + +/******************************************************************************/ + +__e2setcost(0) + +registerOperator("seq", "", "", function(self, args) + local n = #args + if n == 2 then return end + + self.prf = self.prf + args[2] + if self.prf > e2_tickquota then error("perf", 0) end + + for i=3,n-1 do + local op = args[i] + op[1](self, op) + end + + local op = args[n] + return op[1](self, op) +end) + +/******************************************************************************/ + +__e2setcost(0) -- approximation + +registerOperator("whl", "", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + + op3[1](self, op3) + + self.prf = self.prf + args[5] + 3 + while op1[1](self, op1) != 0 do + local ok, msg = pcall(op2[1], self, op2) + if !ok then + if msg == "break" then break + elseif msg == "continue" then + else error(msg, 0) end + end + + self.prf = self.prf + args[5] + 3 + end +end) + +registerOperator("for", "", "", function(self, args) + local var, op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6], args[7] + + op5[1](self, op5) + + local rstart, rend, rstep + rstart = op1[1](self, op1) + rend = op2[1](self, op2) + local rdiff = rend - rstart + local rdelta = delta + + self.vars[var] = rstart + self.vclk[var] = true + + if op3 then + rstep = op3[1](self, op3) + + if rdiff > -delta then + if rstep < delta then return end + elseif rdiff < delta then + if rstep > -delta then return end + else + return + end + + if rstep < 0 then + rdelta = -delta + end + else + if rdiff > -delta then + rstep = 1 + else + return + end + end + + self.prf = self.prf + 3 + for I=rstart,rend+rdelta,rstep do + self.vars[var] = I + self.vclk[var] = true + + local ok, msg = pcall(op4[1], self, op4) + if !ok then + if msg == "break" then break + elseif msg == "continue" then + else error(msg, 0) end + end + + self.prf = self.prf + 3 + end +end) + +__e2setcost(2) -- approximation + +registerOperator("brk", "", "", function(self, args) + error("break", 0) +end) + +registerOperator("cnt", "", "", function(self, args) + error("continue", 0) +end) + +/******************************************************************************/ + +__e2setcost(3) -- approximation + +registerOperator("if", "n", "", function(self, args) + local op1 = args[3] + self.prf = self.prf + args[2] + if op1[1](self, op1) != 0 then + local op2 = args[4] + op2[1](self, op2) + return + else + local op3 = args[5] + op3[1](self, op3) + return + end +end) + +registerOperator("cnd", "n", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 != 0 then + self.prf = self.prf + args[5] + local op2 = args[3] + return op2[1](self, op2) + else + self.prf = self.prf + args[6] + local op3 = args[4] + return op3[1](self, op3) + end +end) + +/******************************************************************************/ + +__e2setcost(1) -- approximation + +registerOperator("trg", "", "n", function(self, args) + local op1 = args[2] + if self.triggerinput == op1 + then return 1 else return 0 end +end) + +/******************************************************************************/ + +__e2setcost(0) -- cascaded + +registerOperator("is", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 != 0 + then return 1 else return 0 end +end) + +__e2setcost(1) -- approximation + +registerOperator("not", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 == 0 + then return 1 else return 0 end +end) + +registerOperator("and", "nn", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 == 0 + then return 0 end + + local op2 = args[3] + local rv2 = op2[1](self, op2) + if rv2 == 0 + then return 0 else return 1 end +end) + +registerOperator("or", "nn", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 != 0 + then return 1 end + + local op2 = args[3] + local rv2 = op2[1](self, op2) + if rv2 != 0 + then return 1 else return 0 end +end) + +/******************************************************************************/ + +__e2setcost(1) -- approximation + +e2function number first() + if self.entity.first + then return 1 else return 0 end +end + +e2function number duped() + if self.entity.duped + then return 1 else return 0 end +end + +e2function number inputClk() + if self.triggerinput + then return 1 else return 0 end +end + + +/******************************************************************************/ + +__e2setcost(2) -- approximation + +e2function void exit() + error("exit", 0) +end + +/******************************************************************************/ + +__e2setcost(100) -- approximation + +e2function void reset() + if !self.entity.first then + self.data.reset = true + end + error("exit", 0) +end + +registerCallback("postexecute", function(self) + if self.data.reset then + self.entity:Reset() + end +end) + +/******************************************************************************/ + +local floor = math.floor +local ceil = math.ceil +local round = math.Round + +__e2setcost(1) -- approximation + +e2function number ops() + return round(self.prfbench) +end + +e2function number opcounter() + return ceil(self.prf + self.prfcount) +end + +--- If used as a while loop condition, stabilizes the expression around hardquota used. +e2function number perf() + if self.prf + self.prfcount >= e2_hardquota-e2_tickquota then return 0 end + if self.prf >= e2_softquota*2 then return 0 end + return 1 +end + +e2function number minquota() + if self.prf < e2_softquota then + return floor(e2_softquota - self.prf) + else + return 0 + end +end + +e2function number maxquota() + if self.prf < e2_tickquota then + local tickquota = e2_tickquota - self.prf + local hardquota = e2_hardquota - self.prfcount - self.prf + e2_softquota + + if hardquota < tickquota then + return floor(hardquota) + else + return floor(tickquota) + end + else + return 0 + end +end + +__e2setcost(nil) + +registerCallback("postinit", function() + -- Returns the Nth value given after the index, the type's zero element otherwise. If you mix types, all non-matching arguments will be regarded as the 2nd argument's type's zero element. + for name,id,zero in pairs_map(wire_expression_types, unpack) do + registerFunction("select", "n"..id.."...", id, function(self, args) + local index = args[2] + index = index[1](self, index) + + index = math.Clamp(math.floor(index), 1, #args-3) + + if index ~= 1 and args[#args][index+1] ~= id then return zero end + local value = args[index+2] + value = value[1](self, value) + return value + end, 5, { "index", "argument1" }) + end +end) diff --git a/lua/entities/gmod_wire_expression2/core/custom.lua b/lua/entities/gmod_wire_expression2/core/custom.lua new file mode 100644 index 0000000000..fcbcbcfe0d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/custom.lua @@ -0,0 +1,4 @@ +/******************************************************************************\ + User defined support +\******************************************************************************/ + diff --git a/lua/entities/gmod_wire_expression2/core/custom/readme.txt b/lua/entities/gmod_wire_expression2/core/custom/readme.txt new file mode 100644 index 0000000000..faa2170a51 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/custom/readme.txt @@ -0,0 +1,9 @@ +Put your custom expression2 extensions in here. +They will be automatically loaded at runtime. + +For each file, there can be a client part, which must be prefixed with "cl_". +This part will then be transferred to and loaded on the client. + +Example: +If there is a file named "foo.lua" it will be loaded on the server. +If, in addition to that, there is a file named "cl_foo.lua", it will be transferred to and loaded on the client. diff --git a/lua/entities/gmod_wire_expression2/core/debug.lua b/lua/entities/gmod_wire_expression2/core/debug.lua new file mode 100644 index 0000000000..06be2cb31d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/debug.lua @@ -0,0 +1,162 @@ +-- import some e2lib and math functions +local validEntity = E2Lib.validEntity +local isOwner = E2Lib.isOwner +local Clamp = math.Clamp + +/******************************************************************************/ + +--- Posts to the chat area. +e2function void print(string text) + self.player:ChatPrint(text) +end + +--- Posts a string to the chat of 's driver. Returns 1 if the text was printed, 0 if not. +e2function number entity:printDriver(string text) + if not validEntity(this) then return 0 end + if not this:IsVehicle() then return 0 end + if not isOwner(self, this) then return 0 end + if text:find('"', 1, true) then return 0 end + + local driver = this:GetDriver() + if not validEntity(driver) then return 0 end + + driver:ChatPrint(text) + return 1 +end + +/******************************************************************************/ + +--- Displays a hint popup with message for seconds ( being clamped between 0.7 and 7). +e2function void hint(string text, duration) + if not validEntity(self.player) then return end + WireLib.AddNotify(self.player, text, NOTIFY_GENERIC, Clamp(duration,0.7,7)) +end + +--- Displays a hint popup to the driver of vehicle E, with message for seconds ( being clamped between 0.7 and 7). Same return value as printDriver. +e2function number entity:hintDriver(string text, duration) + if not validEntity(this) then return 0 end + if not this:IsVehicle() then return 0 end + if not isOwner(self, this) then return 0 end + + local driver = this:GetDriver() + if not validEntity(driver) then return 0 end + + WireLib.AddNotify(driver, text, NOTIFY_GENERIC, Clamp(duration,0.7,7)) + return 1 +end + +/******************************************************************************/ + +local valid_print_types = {} +for _,cname in ipairs({ "HUD_PRINTCENTER", "HUD_PRINTCONSOLE", "HUD_PRINTNOTIFY", "HUD_PRINTTALK" }) do + local value = _G[cname] + valid_print_types[value] = true + E2Lib.registerConstant(cname, value) +end + +--- Same as print(), but can make the text show up in different places. can be one of the following: _HUD_PRINTCENTER, _HUD_PRINTCONSOLE, _HUD_PRINTNOTIFY, _HUD_PRINTTALK. +e2function void print(print_type, string text) + if not valid_print_types[print_type] then return end + + self.player:PrintMessage(print_type, text) +end + +--- Same as E:printDriver(), but can make the text show up in different places. can be one of the following: _HUD_PRINTCENTER, _HUD_PRINTCONSOLE, _HUD_PRINTNOTIFY, _HUD_PRINTTALK. +e2function number entity:printDriver(print_type, string text) + if not validEntity(this) then return 0 end + if not this:IsVehicle() then return 0 end + if not isOwner(self, this) then return 0 end + if not valid_print_types[print_type] then return 0 end + if text:find('"', 1, true) then return 0 end + + local driver = this:GetDriver() + if not validEntity(driver) then return 0 end + + driver:PrintMessage(print_type, text) + return 1 +end + +/******************************************************************************/ + +-- helper stuff for printTable +local _Msg = Msg +local msgbuf +local function MyMsg(s) + table.insert(msgbuf, s) +end + +--- Prints a table like the lua function [[G.PrintTable|PrintTable]] does, except to the chat area. +e2function void printTable(table tbl) + msgbuf = {} + Msg = MyMsg + PrintTable(tbl) + Msg = _Msg + for _,line in ipairs(string.Explode("\n",table.concat(msgbuf))) do + self.player:ChatPrint(line) + end + msgbuf = nil +end + +--- Prints an array like the lua function [[G.PrintTable|PrintTable]] does, except to the chat area. +e2function void printTable(array arr) + msgbuf = {} + Msg = MyMsg + PrintTable(arr) + Msg = _Msg + for _,line in ipairs(string.Explode("\n",table.concat(msgbuf))) do + self.player:ChatPrint(line) + end + msgbuf = nil +end + +/******************************************************************************/ + +local printColor_typeids = { + n = tostring, + s = tostring, + v = function(v) return Color(v[1],v[2],v[3]) end, + xv4 = function(v) return Color(v[1],v[2],v[3],v[4]) end, + e = function(e) return validEntity(e) and e:IsPlayer() and e or "" end, +} + +--- Works like [[chat.AddText]](...). Parameters can be any amount and combination of numbers, strings, player entities, color vectors (both 3D and 4D). +e2function void printColor(...) + local send_array = { ... } + for i,tp in ipairs(typeids) do + if printColor_typeids[tp] then + send_array[i] = printColor_typeids[tp](send_array[i]) + else + send_array[i] = "" + end + end + datastream.StreamToClients(self.player, "wire_expression2_printColor", send_array) +end + + +local printColor_types = { + number = tostring, + string = tostring, + Vector = function(v) return Color(v[1],v[2],v[3]) end, + table = function(tbl) + for i,v in pairs(tbl) do + if type(i) ~= "number" then return "" end + if type(v) ~= "number" then return "" end + if i < 1 or i > 4 then return "" end + end + return Color(tbl[1] or 0, tbl[2] or 0,tbl[3] or 0,tbl[4]) + end, + Player = function(e) return validEntity(e) and e:IsPlayer() and e or "" end, +} + +--- Like printColor(...), except taking an array containing all the parameters. +e2function void printColor(array arr) + local send_array = {} + for i,tp in ipairs_map(arr,type) do + if printColor_types[tp] then + send_array[i] = printColor_types[tp](arr[i]) + else + send_array[i] = "" + end + end + datastream.StreamToClients(self.player, "wire_expression2_printColor", send_array) +end diff --git a/lua/entities/gmod_wire_expression2/core/e2doc.lua b/lua/entities/gmod_wire_expression2/core/e2doc.lua new file mode 100644 index 0000000000..79907bdd0d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/e2doc.lua @@ -0,0 +1,153 @@ +local eliminate_varname_conflicts = true + + +if not e2_parse_args then include("extpp.lua") end + +local readfile = readfile or function(filename) + return file.Read("../lua/entities/gmod_wire_expression2/core/"..filename) +end +local writefile = writefile or function(filename, contents) + print("--- Writing to file 'data/e2doc/"..filename.."' ---") + return file.Write("e2doc/"..filename, contents) +end +local p_typename = "[a-z][a-z0-9]*" +local p_typeid = "[a-z][a-z0-9]?[a-z0-9]?[a-z0-9]?[a-z0-9]?" +local p_argname = "[a-zA-Z][a-zA-Z0-9]*" +local p_funcname = "[a-z][a-zA-Z0-9]*" +local p_func_operator = "[-a-zA-Z0-9+*/%%^=!><&|$_]*" + +local function ltrim(s) + return string.match(s, "^%s*(.-)$") +end + +local function rtrim(s) + return string.match(s, "^(.-)%s*$") +end + +local function trim(s) + return string.match(s, "^%s*(.-)%s*$") +end +local mess_with_args + +function mess_with_args(args, desc, thistype) + local args_referenced = string.match(desc, "<"..p_argname..">") + local argtable,ellipses = e2_parse_args(args) + local indices = {} + if thistype ~= "" then indices[string.upper(e2_get_typeid(thistype))] = 2 end + args = '' + for i,name in ipairs(argtable.argnames) do + local typeid = string.upper(argtable.typeids[i]) + + local index = "" + if args_referenced then + index = indices[typeid] + if index then + indices[typeid] = index+1 + else + index = "" + indices[typeid] = 2 + end + end + local newname = typeid..""..index.."" + if index == "" then newname = typeid end + + desc = desc:gsub("<"..name..">", "''"..newname.."''") + if i ~= 1 then args = args .. "," end + args = args .. newname + end + if ellipses then + if #argtable.argnames ~= 0 then args = args .. "," end + args = args .. "..." + end + return args,desc +end + +local function e2doc(filename, outfile) + if not outfile then + outfile = string.match(filename,"^.*%.").."txt" + end + local current = {} + local output = { '====Commands====\n:{|style="background:#E6E6FA"\n!align="left" width="150"| Function\n!align="left" width="60"| Returns\n!align="left" width="1000"| Description\n' } + local insert_line = true + for line in string.gmatch(readfile(filename), "%s*(.-)%s*\n") do + if line:sub(1,3) == "---" then + if line:match("[^-%s]") then table.insert(current, ltrim(line:sub(4))) end + elseif line:sub(1,3) == "///" then + table.insert(current, ltrim(line:sub(4))) + elseif line:sub(1,12) == "--[[********" or line:sub(1,9) == "/********" then + if line:find("^%-%-%[%[%*%*%*%*%*%*%*%*+%]%]%-%-$") or line:find("^/%*%*%*%*%*%*%*%*+/$") then + insert_line = true + end + elseif line:sub(1,10) == "e2function" then + local ret, thistype, colon, name, args = line:match("e2function%s+("..p_typename..")%s+([a-z0-9]-)%s*(:?)%s*("..p_func_operator..")%(([^)]*)%)") + if thistype~="" and colon=="" then error("E2doc syntax error: Function names may not start with a number.",0) end + if thistype=="" and colon~="" then error("E2doc syntax error: No type for 'this' given.",0) end + if thistype:sub(1,1):find("[0-9]") then error("E2doc syntax error: Type names may not start with a number.",0) end + + desc = table.concat(current, "
") + current = {} + + if name:sub(1,8) ~= "operator" and not desc:match("@nodoc") then + if insert_line then + table.insert(output, '|-\n| bgcolor="SteelBlue" | || bgcolor="SteelBlue" | || bgcolor="SteelBlue" | \n') + insert_line = false + end + args, desc = mess_with_args(args, desc, thistype) + + if ret == "void" then + ret = "" + else + ret = string.upper(e2_get_typeid(ret)) + end + + if thistype ~= "" then + thistype = string.upper(e2_get_typeid(thistype)) + desc = desc:gsub("", "''"..thistype.."''") + thistype = thistype..":" + end + table.insert(output, string.format("|-\n|%s%s(%s) || %s || ", thistype, name, args, ret)) + --desc = desc:gsub("<([^<>]+)>", "''%1''") + table.insert(output, desc) + table.insert(output, "\n") + end + end + end -- for line + output = table.concat(output).."|}\n" + print(output) + writefile(outfile, output) +end + +-- Add a client-side "e2doc" console command +if SERVER then + AddCSLuaFile("e2doc.lua") + e2doc = nil +elseif CLIENT then + concommand.Add("e2doc", + function(player, command, args) + if not file.IsDir("e2doc") then file.CreateDir("e2doc") end + if not file.IsDir("e2doc/custom") then file.CreateDir("e2doc/custom") end + + local path = string.match(args[2] or args[1],"^%s*(.+)/") + if path and not file.IsDir("e2doc/"..path) then file.CreateDir("e2doc/"..path) end + + e2doc(args[1], args[2]) + end + , + function (commandName,args) -- autocomplete function + args = string.match(args,"^%s*(.-)%s*$") + local path = string.match(args,"^%s*(.+/)") or "" + local files = file.FindInLua("entities/gmod_wire_expression2/core/"..args.."*") + local ret = {} + for _,v in ipairs(files) do + if string.sub(v,1,1) ~= "." then + if file.IsDir('../lua/entities/gmod_wire_expression2/core/'..path..v) then + table.insert(ret, "e2doc "..path..v.."/") + else + table.insert(ret, "e2doc "..path..v) + end + end + end + return ret + end + ) +end diff --git a/lua/entities/gmod_wire_expression2/core/e2lib.lua b/lua/entities/gmod_wire_expression2/core/e2lib.lua new file mode 100644 index 0000000000..11b5d45a1e --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/e2lib.lua @@ -0,0 +1,372 @@ +AddCSLuaFile("e2lib.lua") + +E2Lib = {} + +local type = type +local function checkargtype(argn, value, argtype) + if type(value) ~= argtype then error(string.format("bad argument #%d to 'E2Lib.%s' (%s expected, got %s)", argn, debug.getinfo(2,"n").name, argtype, type(text)),2) end +end + +--[[************************* signature generation ***************************]] + +local function maketype(typeid) + if typeid == "" then return "void" end + if typeid == "n" then return "number" end + + local tp = wire_expression_types2[typeid] + if not tp then error("Type ID '"..typeid.."' not found",2) end + + local typename = tp[1]:lower() + return typename or "unknown" +end + +local function splitargs(args) + local ret = {} + local thistype = nil + local i = 1 + while i <= #args do + letter = args:sub(i,i) + if letter == ":" then + if #ret ~= 1 then error("Misplaced ':' in args",2) end + thistype = ret[1] + ret = {} + elseif letter == "." then + if args:sub(i) ~= "..." then error("Misplaced '.' in args",2) end + table.insert(ret, "...") + i=i+2 + else + local typeid = letter + if letter == "x" then + typeid = args:sub(i,i+2) + i = i+2 + end + table.insert(ret, maketype(typeid)) + end + i = i + 1 + end + return thistype,ret +end + +-- given a function signature like "setNumber(xwl:sn)" and an optional return typeid, generates a nice, readable signature +function E2Lib.generate_signature(signature, rets, argnames) + local funcname, args = string.match(signature, "([^(]+)%(([^)]*)%)") + if not funcname then error("malformed signature") end + + local thistype, args = splitargs(args) + + if argnames then + for i = 1,#args do + if argnames[i] then args[i] = args[i].." "..argnames[i] end + end + end + local new_signature = string.format("%s(%s)", funcname, table.concat(args,",")) + if thistype then new_signature = thistype..":"..new_signature end + + return (not rets or rets == "") and (new_signature) or (maketype(rets).."="..new_signature) +end + +--[[*********************** various entity checkers **************************]] + +-- replaces an E2Lib function (ex.: isOwner) and notifies plugins +function E2Lib.replace_function(funcname, func) + checkargtype(1,funcname,"string") + checkargtype(2,func,"function") + + local oldfunc = E2Lib[funcname] + if type(oldfunc) ~= "function" then error("No E2Lib function by the name "..funcname.." found.",2) end + E2Lib[funcname] = func + wire_expression2_CallHook("e2lib_replace_function", funcname, func, oldfunc) +end + +E2Lib.validEntity = _R.Entity.IsValid -- this covers all cases the old validEntity covered, and more (strings, PhysObjs, etc.). it's also significantly faster. +local validEntity = E2Lib.validEntity + +function E2Lib.validPhysics(entity) + if validEntity(entity) then + if entity:IsWorld() then return false end + if entity:GetMoveType() ~= MOVETYPE_VPHYSICS then return false end + return entity:GetPhysicsObject():IsValid() + end + return false +end + +function E2Lib.getOwner(self, entity) + if(entity == self.entity or entity == self.player) then return self.player end + if entity.GetPlayer then + local ply = entity:GetPlayer() + if validEntity(ply) then return ply end + end + + local OnDieFunctions = entity.OnDieFunctions + if OnDieFunctions then + if OnDieFunctions.GetCountUpdate then + if OnDieFunctions.GetCountUpdate.Args then + if OnDieFunctions.GetCountUpdate.Args[1] then return OnDieFunctions.GetCountUpdate.Args[1] end + end + end + if OnDieFunctions.undo1 then + if OnDieFunctions.undo1.Args then + if OnDieFunctions.undo1.Args[2] then return OnDieFunctions.undo1.Args[2] end + end + end + end + + if entity.GetOwner then + local ply = entity:GetOwner() + if validEntity(ply) then return ply end + end + + return nil +end + +local wire_expression2_restricted = CreateConVar('wire_expression2_restricted', 1) +function E2Lib.isOwner(self, entity) + if wire_expression2_restricted:GetBool() then + return getOwner(self, entity) == self.player + end + return true +end +local isOwner = E2Lib.isOwner + +-- This function is only here for compatibility. Use validEntity() in new code. +function E2Lib.checkEntity(entity) + if validEntity(entity) then return entity end + return nil +end + +-- Checks whether the player is the chip's owner or in a pod owned by the chip's owner. Assumes that ply is really a player. +function E2Lib.canModifyPlayer(self, ply) + if ply == self.player then return true end + + if not validEntity(ply) then return false end + if not ply:IsPlayer() then return false end + + local vehicle = ply:GetVehicle() + if not validEntity(vehicle) then return false end + return isOwner(self, vehicle) +end + +--[[**************************** type guessing *******************************]] + +local type_lookup = { + number = "n", + string = "s", + Vector = "v", + PhysObj = "b", +} +local table_length_lookup = { + [ 2] = "xv2", + [ 3] = "v", + [ 4] = "xv4", + [ 9] = "m", + [16] = "xm4", +} + +function E2Lib.guess_type(value) + if validEntity(value) then return "e" end + if value.EntIndex then return "e" end + local vtype = type(v) + if type_lookup[vtype] then return type_lookup[vtype] end + if vtype == "table" then + if table_length_lookup[#v] then return table_length_lookup[#v] end + if v.HitPos then return "xrd" end + end + + for typeid,v in pairs(wire_expression_types2) do + if v[5] then + local ok = pcall(v[5],value) + if ok then return typeid end + end + end + + -- TODO: more type guessing here + + return "" -- empty string = unknown type, for now. +end + +-- Types that cannot possibly be guessed correctly: + -- angle (will be reported as vector) + -- matrix2 (will be reported as vector4) + -- wirelink (will be reported as entity) + -- complex (will be reported as vector2) + -- quaternion (will be reported as vector4) + -- all kinds of nil stuff + +--[[**************************** list filtering ******************************]] + +local Debug = false +local cPrint +if Debug then + if not console then require("console") end -- only needed if you want fancy-colored output. + function cPrint(color, text) Msg(text) end + if console and console.Print then cPrint = console.Print end +end + +function E2Lib.filterList(list, criterion) + local index = 1 + --if Debug then print("-- filterList: "..#list.." entries --") end + + while index <= #list do + if not criterion(list[index]) then + --if Debug then cPrint(Color(128,128,128), "- "..tostring(list[index]).."\n") end + list[index] = list[#list] + table.remove(list) + else + --if Debug then print(string.format("+%3d %s", index, tostring(list[index]))) end + index = index + 1 + end + end + + --if Debug then print("--------") end + return list +end + +--[[**************************** compiler stuff ******************************]] + +-- TODO: rewrite this! +E2Lib.optable = { + ["+"] = {"add", {["="] = {"aadd"}, ["+"] = {"inc"}}}, + ["-"] = {"sub", {["="] = {"asub"}, ["-"] = {"dec"}}}, + ["*"] = {"mul", {["="] = {"amul"}}}, + ["/"] = {"div", {["="] = {"adiv"}}}, + ["%"] = {"mod"}, + ["^"] = {"exp"}, + + ["="] = {"ass", {["="] = {"eq"}}}, + ["!"] = {"not", {["="] = {"neq"}}}, + [">"] = {"gth", {["="] = {"geq"}}}, + ["<"] = {"lth", {["="] = {"leq"}}}, + + ["&"] = {"and"}, + ["|"] = {"or"}, + + ["?"] = {"qsm"}, + [":"] = {"col"}, + [","] = {"com"}, + + ["("] = {"lpa"}, + [")"] = {"rpa"}, + ["{"] = {"lcb"}, + ["}"] = {"rcb"}, + ["["] = {"lsb"}, + ["]"] = {"rsb"}, + + ["$"] = {"dlt"}, + ["~"] = {"trg"}, +} + +E2Lib.optable_inv = {} + +do + -- TODO: Reverse this and build optable from optable_inv. + local function build_op_index(optable,prefix) + for k,v in pairs(optable) do + if v[1] then E2Lib.optable_inv[v[1]] = prefix..k end + if v[2] then build_op_index(v[2],prefix..k) end + end + end + build_op_index(E2Lib.optable, "") +end + +function E2Lib.printops() + local op_order = {["+"]=1,["-"]=2,["*"]=3,["/"]=4,["%"]=5,["^"]=6,["="]=7,["!"]=8,[">"]=9,["<"]=10,["&"]=11,["|"]=12,["?"]=13,[":"]=14,[","]=15,["("]=16,[")"]=17,["{"]=18,["}"]=19,["["]=20,["]"]=21,["$"]=22,["~"]=23} + print("E2Lib.optable = {") + for k,v in pairs_sortkeys(E2Lib.optable,function(a,b) return (op_order[a] or math.huge)<(op_order[b] or math.huge) end) do + tblstring = table.ToString(v) + tblstring = tblstring:gsub(",}","}") + tblstring = tblstring:gsub("{(.)="," {[\"%1\"] = ") + tblstring = tblstring:gsub(",(.)=",", [\"%1\"] = ") + print(string.format("\t[%q] = %s,",k,tblstring)) + end + print("}") +end + +--[[***************************** string stuff *******************************]] + +-- limits the given string to the given length and adds "..." to the end if too long. +function E2Lib.limitString(text, length) + checkargtype(1,text,"string") + checkargtype(2,length,"number") + + if #text <= length then + return text + else + return string.sub(text, 1, length) .. "..." + end +end + +do + local enctbl = {} + local dectbl = {} + + do + -- generate encode/decode lookup tables + local valid_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 +-*/#^!?~=@&|.,:(){}[]<>" -- list of "normal" chars that can be transferred without problems + local hex = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' } + + + for byte = 1,255 do + dectbl[hex[(byte - byte % 16) / 16 + 1] .. hex[byte % 16 + 1]] = string.char(byte) + enctbl[string.char(byte)] = "%" .. hex[(byte - byte % 16) / 16 + 1] .. hex[byte % 16 + 1] + end + + for i = 1,valid_chars:len() do + local char = valid_chars:sub(i, i) + enctbl[char] = char + end + end + + -- escapes special characters + function E2Lib.encode(str) + return str:gsub(".", enctbl) + end + + -- decodes escaped characters + function E2Lib.decode(encoded) + return encoded:gsub("%%(..)", dectbl) + end +end + +--[[***************************** compatibility ******************************]] + +-- Some functions need to be global for backwards-compatibility. +local makeglobal = { + ["validEntity"] = true, + ["validPhysics"] = true, + ["getOwner"] = true, + ["isOwner"] = true, + ["checkEntity"] = true, +} + +-- Put all these functions into the global scope. +for funcname,_ in pairs(makeglobal) do + _G[funcname] = E2Lib[funcname] +end + +hook.Add("InitPostEntity", "e2lib", function() + -- If changed, put them into the global scope again. + registerCallback("e2lib_replace_function", function(funcname, func, oldfunc) + if makeglobal[funcname] then + _G[funcname] = func + end + if funcname == "validEntity" then validEntity = func + elseif funcname == "isOwner" then isOwner = func + end + end) + + -- check for a CPPI compliant plugin + if SERVER and CPPI and _R.Player.CPPIGetFriends then + E2Lib.replace_function("isOwner", function(self, entity) + local ply = self.player + local owner = getOwner(self, entity) + if not validEntity(owner) then return false end + if ply == owner then return true end + + local friends = owner:CPPIGetFriends() + for _,friend in pairs(friends) do + if ply == friend then return true end + end + return false + end) + end +end) diff --git a/lua/entities/gmod_wire_expression2/core/entity.lua b/lua/entities/gmod_wire_expression2/core/entity.lua new file mode 100644 index 0000000000..c20386e689 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/entity.lua @@ -0,0 +1,862 @@ +/******************************************************************************\ + Entity support +\******************************************************************************/ + +registerType("entity", "e", nil, + nil, + function(self,output) return output or NULL end, + function(retval) + if validEntity(retval) then return end + if retval == nil then return end + if not retval.EntIndex then error("Return value is neither nil nor an Entity, but a "..type(retval).."!",0) end + end +) + +/******************************************************************************/ + +-- import some e2lib functions +local validEntity = E2Lib.validEntity +local validPhysics = E2Lib.validPhysics +local getOwner = E2Lib.getOwner +local isOwner = E2Lib.isOwner + +registerCallback("e2lib_replace_function", function(funcname, func, oldfunc) + if funcname == "isOwner" then + isOwner = func + elseif funcname == "getOwner" then + getOwner = func + elseif funcname == "validEntity" then + validEntity = func + elseif funcname == "validPhysics" then + validPhysics = func + end +end) + +-- faster access to some math library functions +local abs = math.abs +local atan2 = math.atan2 +local sqrt = math.sqrt +local asin = math.asin +local Clamp = math.Clamp + +local rad2deg = 180 / math.pi + + +/******************************************************************************/ +// Functions using operators + +__e2setcost(5) -- temporary + +registerOperator("ass", "e", "e", function(self, args) + local op1, op2 = args[2], args[3] + rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ + +registerOperator("is", "e", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if validEntity(rv1) then return 1 else return 0 end +end) + +registerOperator("eq", "ee", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 == rv2 then return 1 else return 0 end +end) + +registerOperator("neq", "ee", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 != rv2 then return 1 else return 0 end +end) + +/******************************************************************************/ + +registerFunction("entity", "n", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + local ent = ents.GetByIndex(rv1) + if(!validEntity(ent)) then return nil end + return ent +end) + +registerFunction("id", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if(!validEntity(rv1)) then return 0 end + return rv1:EntIndex() +end) + +/******************************************************************************/ +// Functions getting string + +registerFunction("noentity", "", "e", function(self, args) + return nil +end) + +registerFunction("type", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return "" end + return rv1:GetClass() +end) + +registerFunction("model", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return "" end + return rv1:GetModel() +end) + +registerFunction("owner", "e:", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return nil end + return getOwner(self, rv1) +end) + +/******************************************************************************/ +// Functions getting vector +registerFunction("pos", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:GetPos() +end) + +registerFunction("forward", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:GetForward() +end) + +registerFunction("right", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:GetRight() +end) + +registerFunction("up", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:GetUp() +end) + +registerFunction("vel", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:GetVelocity() +end) + +registerFunction("velL", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:WorldToLocal(rv1:GetVelocity() + rv1:GetPos()) +end) + +registerFunction("angVel", "e:", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validPhysics(rv1)) then return {0,0,0} end + local phys = rv1:GetPhysicsObject() + local vec = phys:GetAngleVelocity() + return { vec.y, vec.z, vec.x } +end) + +--- Returns a vector describing rotation axis, magnitude and sense given as the vector's direction, magnitude and orientation. +e2function vector entity:angVelVector() + if not validPhysics(this) then return { 0, 0, 0 } end + local phys = this:GetPhysicsObject() + return phys:GetAngleVelocity() +end + +/******************************************************************************/ +// Functions using vector getting vector +registerFunction("toWorld", "e:v", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:LocalToWorld(Vector(rv2[1],rv2[2],rv2[3])) +end) + +registerFunction("toLocal", "e:v", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:WorldToLocal(Vector(rv2[1],rv2[2],rv2[3])) +end) + +--- Transforms from an angle local to to a world angle. +e2function angle entity:toWorld(angle localAngle) + if not validEntity(this) then return { 0, 0, 0 } end + local worldAngle = this:LocalToWorldAngles(Angle(localAngle[1],localAngle[2],localAngle[3])) + return { worldAngle.p, worldAngle.y, worldAngle.r } +end + +--- Transforms from a world angle to an angle local to . +e2function angle entity:toLocal(angle worldAngle) + if not validEntity(this) then return { 0, 0, 0 } end + local localAngle = this:WorldToLocalAngles(Angle(worldAngle[1],worldAngle[2],worldAngle[3])) + return { localAngle.p, localAngle.y, localAngle.r } +end + +/******************************************************************************/ +// Functions getting number +registerFunction("health", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + return rv1:Health() +end) + +registerFunction("radius", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + return rv1:BoundingRadius() +end) + +// original bearing & elevation thanks to Gwahir +--- Returns the bearing (yaw) from to +e2function number entity:bearing(vector pos) + if not validEntity(this) then return 0 end + + pos = this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) + + return rad2deg*-atan2(pos.y, pos.x) +end + +--- Returns the elevation (pitch) from to +e2function number entity:elevation(vector pos) + if not validEntity(this) then return 0 end + + pos = this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) + + local len = pos:Length() + if len < delta then return 0 end + return rad2deg*asin(pos.z / len) +end + +--- Returns the elevation (pitch) and bearing (yaw) from to +e2function angle entity:heading(vector pos) + if not validEntity(this) then return { 0, 0, 0 } end + + pos = this:WorldToLocal(Vector(pos[1],pos[2],pos[3])) + + -- bearing + local bearing = rad2deg*-atan2(pos.y, pos.x) + + -- elevation + local len = pos:Length()--sqrt(x*x + y*y + z*z) + if len < delta then return { 0, bearing, 0 } end + local elevation = rad2deg*asin(pos.z / len) + + return { elevation, bearing, 0 } +end + +registerFunction("mass", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validPhysics(rv1)) then return 0 end + local phys = rv1:GetPhysicsObject() + return phys:GetMass() +end) + +registerFunction("massCenter", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validPhysics(rv1)) then return {0,0,0} end + local phys = rv1:GetPhysicsObject() + return rv1:LocalToWorld(phys:GetMassCenter()) +end) + +registerFunction("massCenterL", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validPhysics(rv1)) then return {0,0,0} end + local phys = rv1:GetPhysicsObject() + return phys:GetMassCenter() +end) + +registerFunction("setMass", "n", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if(!validPhysics(self.entity)) then return end + local mass = Clamp(rv1, 0.001, 50000) + local phys = self.entity:GetPhysicsObject() + phys:SetMass(mass) +end) + +registerFunction("setMass", "e:n", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if(!validPhysics(rv1)) then return end + if(!isOwner(self, rv1)) then return end + if(rv1:IsPlayer()) then return end + local mass = Clamp(rv2, 0.001, 50000) + local phys = rv1:GetPhysicsObject() + phys:SetMass(mass) +end) + +/******************************************************************************/ +// Functions getting boolean/number +registerFunction("isPlayer", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsPlayer() then return 1 else return 0 end +end) + +registerFunction("isNPC", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsNPC() then return 1 else return 0 end +end) + +registerFunction("isVehicle", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsVehicle() then return 1 else return 0 end +end) + +registerFunction("isWorld", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsWorld() then return 1 else return 0 end +end) + +registerFunction("isOnGround", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsOnGround() then return 1 else return 0 end +end) + +registerFunction("isUnderWater", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:WaterLevel() > 0 then return 1 else return 0 end +end) + +/******************************************************************************/ +// Functions getting angles + +registerFunction("angles", "e:", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + local ang = rv1:GetAngles() + return {ang.p,ang.y,ang.r} +end) + +/******************************************************************************/ + +registerFunction("getMaterial", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if !validEntity(rv1) then return end + return rv1:GetMaterial() +end) + +registerFunction("setMaterial", "e:s", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if !validEntity(rv1) then return end + if !isOwner(self, rv1) then return end + rv1:SetMaterial(rv2) +end) + +--- Gets 's current skin number. +e2function number entity:getSkin() + if validEntity(this) then return this:GetSkin() end + return 0 +end + +--- Sets 's skin number. +e2function void entity:setSkin(skin) + if validEntity(this) then this:SetSkin(skin) end +end + +--- Gets 's number of skins. +e2function number entity:getSkinCount() + if validEntity(this) then return this:SkinCount() end + return 0 +end + +/******************************************************************************/ + +registerFunction("isPlayerHolding", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsPlayerHolding() then return 1 else return 0 end +end) + +registerFunction("isOnFire", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsOnFire() then return 1 else return 0 end +end) + +registerFunction("isWeapon", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if rv1:IsWeapon() then return 1 else return 0 end +end) + +registerFunction("isFrozen", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validPhysics(rv1)) then return 0 end + local phys = rv1:GetPhysicsObject() + if phys:IsMoveable() then return 0 else return 1 end +end) + +registerFunction("inVehicle", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer() and rv1:InVehicle()) then return 1 else return 0 end +end) + +registerFunction("timeConnected", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer()) then return rv1:TimeConnected() else return 0 end +end) + +--- Returns 1 if the player is in noclip mode, 0 if not. +e2function number entity:inNoclip() + if not this or this:GetMoveType() ~= MOVETYPE_NOCLIP then return 0 end + return 1 +end + +/******************************************************************************/ + +__e2setcost(30) -- temporary + +registerFunction("applyForce", "v", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + local phys = self.entity:GetPhysicsObject() + phys:ApplyForceCenter(Vector(rv1[1],rv1[2],rv1[3])) +end) + +registerFunction("applyOffsetForce", "vv", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + local phys = self.entity:GetPhysicsObject() + phys:ApplyForceOffset(Vector(rv1[1],rv1[2],rv1[3]), Vector(rv2[1],rv2[2],rv2[3])) +end) + +/*registerFunction("applyAngVel", "a", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + local phys = self.entity:GetPhysicsObject() + phys:AddAngleVelocity(Angle(rv1[3],rv1[1],rv1[2])) +end)*/ + +registerFunction("applyForce", "e:v", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if(!validPhysics(rv1)) then return nil end + if(!isOwner(self, rv1)) then return nil end + local phys = rv1:GetPhysicsObject() + phys:ApplyForceCenter(Vector(rv2[1],rv2[2],rv2[3])) +end) + +registerFunction("applyOffsetForce", "e:vv", "", function(self,args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3) + if(!validPhysics(rv1)) then return nil end + if(!isOwner(self, rv1)) then return nil end + local phys = rv1:GetPhysicsObject() + phys:ApplyForceOffset(Vector(rv2[1],rv2[2],rv2[3]), Vector(rv3[1],rv3[2],rv3[3])) +end) + +/*registerFunction("applyAngVel", "e:a", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if(!validPhysics(rv1)) then return end + if(!isOwner(self, rv1)) then return nil end + local phys = rv1:GetPhysicsObject() + phys:AddAngleVelocity(Angle(rv2[3],rv2[1],rv2[2])) +end)*/ + +registerFunction("applyAngForce", "a", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + + local ent = self.entity + local phys = ent:GetPhysicsObject() + if not phys:IsValid() then return end + + -- assign vectors + local pos = ent:LocalToWorld(phys:GetMassCenter()) + local up = ent:GetUp() + local left = ent:GetRight() * -1 + local forward = ent:GetForward() + + local pitch = up * (rv1[1]*0.5) + local yaw = forward * (rv1[2]*0.5) + local roll = left * (rv1[3]*0.5) + + -- apply pitch force + phys:ApplyForceOffset( forward, pos + pitch ) + phys:ApplyForceOffset( forward * -1, pos - pitch ) + + -- apply yaw force + phys:ApplyForceOffset( left, pos + yaw ) + phys:ApplyForceOffset( left * -1, pos - yaw ) + + -- apply roll force + phys:ApplyForceOffset( up, pos + roll ) + phys:ApplyForceOffset( up * -1, pos - roll ) +end) + +registerFunction("applyAngForce", "e:a", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if(!validPhysics(rv1)) then return nil end + if(!isOwner(self, rv1)) then return nil end + local phys = rv1:GetPhysicsObject() + + -- assign vectors + local pos = rv1:LocalToWorld(phys:GetMassCenter()) + local up = rv1:GetUp() + local left = rv1:GetRight()*-1 + local forward = rv1:GetForward() + + local pitch = up * (rv2[1]*0.5) + local yaw = forward * (rv2[2]*0.5) + local roll = left * (rv2[3]*0.5) + + -- apply pitch force + phys:ApplyForceOffset( forward, pos + pitch ) + phys:ApplyForceOffset( forward * -1, pos - pitch ) + + -- apply yaw force + phys:ApplyForceOffset( left, pos + yaw ) + phys:ApplyForceOffset( left * -1, pos - yaw ) + + -- apply roll force + phys:ApplyForceOffset( up, pos + roll ) + phys:ApplyForceOffset( up * -1, pos - roll ) +end) + +--- Applies torque according to the axis, magnitude and sense given by the vector's direction, magnitude and orientation. +e2function void entity:applyTorque(vector torque) + if not validEntity(this) then return end + if not isOwner(self, this) then return end + local phys = this:GetPhysicsObject() + if not phys:IsValid() then return end + + local tq = Vector(torque[1], torque[2], torque[3]) + local torqueamount = tq:Length() + local off + if abs(torque[3]) > torqueamount*0.1 or abs(torque[1]) > torqueamount*0.1 then + off = Vector(-torque[3], 0, torque[1]) + else + off = Vector(-torque[2], torque[1], 0) + end + off:Normalize() + local dir = tq:Cross(off) + + dir = phys:LocalToWorld(dir)-phys:GetPos() + local masscenter = phys:GetMassCenter() + phys:ApplyForceOffset( dir * 0.5, phys:LocalToWorld(masscenter+off) ) + phys:ApplyForceOffset( dir * -0.5, phys:LocalToWorld(masscenter-off) ) +end + +registerFunction("inertia", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validPhysics(rv1)) then return {0,0,0} end + local vec = rv1:GetPhysicsObject():GetInertia() + return {vec.x,vec.y,vec.z} +end) + + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +registerFunction("lockPod", "e:n", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if(!validEntity(rv1) || !rv1:IsVehicle()) then return end + if(!isOwner(self, rv1)) then return end + if(rv2 != 0) then + rv1:Fire("Lock", "", 0) + else + rv1:Fire("Unlock", "", 0) + end +end) + +registerFunction("killPod", "e:", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1) || !rv1:IsVehicle()) then return end + if(!isOwner(self, rv1)) then return end + local ply = rv1:GetDriver() + if(ply:IsValid()) then ply:Kill() end +end) + +registerFunction("ejectPod", "e:", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1) || !rv1:IsVehicle()) then return end + if(!isOwner(self, rv1)) then return end + local ply = rv1:GetDriver() + if(ply:IsValid()) then ply:ExitVehicle() end +end) + +/******************************************************************************/ + +registerFunction("aimEntity", "e:", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if not validEntity(rv1) then return nil end + if not rv1:IsPlayer() then return nil end + + local ent = rv1:GetEyeTraceNoCursor().Entity + if not ent:IsValid() then return nil end + return ent +end) + +registerFunction("aimPos", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if not validEntity(rv1) then return {0,0,0} end + if not rv1:IsPlayer() then return {0,0,0} end + + return rv1:GetEyeTraceNoCursor().HitPos +end) + +registerFunction("aimNormal", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if not validEntity(rv1) then return {0,0,0} end + if not rv1:IsPlayer() then return {0,0,0} end + + return rv1:GetEyeTraceNoCursor().HitNormal +end) + +--- Returns the bone the player is currently aiming at. +e2function bone entity:aimBone() + if not validEntity(this) then return nil end + if not this:IsPlayer() then return nil end + + local trace = this:GetEyeTraceNoCursor() + local ent = trace.Entity + if not validEntity(ent) then return nil end + return getBone(ent, trace.PhysicsBone) +end + +--- Equivalent to rangerOffset(16384, :shootPos(), :eye()), but faster (causing less lag) +e2function ranger entity:eyeTrace() + if not validEntity(this) then return nil end + if not this:IsPlayer() then return nil end + + return this:GetEyeTraceNoCursor() +end + +/******************************************************************************/ + +registerFunction("boxSize", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:OBBMaxs() - rv1:OBBMins() +end) + +registerFunction("boxCenter", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:OBBCenter() +end) + +registerFunction("boxMax", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:OBBMaxs() +end) + +registerFunction("boxMin", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + return rv1:OBBMins() +end) + +/******************************************************************************/ + +registerFunction("driver", "e:", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1) || !rv1:IsVehicle()) then return nil end + return rv1:GetDriver() +end) + +registerFunction("passenger", "e:", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1) || !rv1:IsVehicle()) then return nil end + return rv1:GetPassenger() +end) + +--- Returns formatted as a string. Returns "(null)" for invalid entities. +e2function string toString(entity ent) + if not validEntity(ent) then return "(null)" end + return tostring(ent) +end + +/******************************************************************************/ + +local function SetTrails(Player, Entity, Data) + if Entity.SToolTrail then + Entity.SToolTrail:Remove() + Entity.SToolTrail = nil + end + + if not Data then + duplicator.ClearEntityModifier(Entity, "trail") + return + end + + if Data.StartSize <= 0 then Data.StartSize = 0.0001 end + + local trail_entity = util.SpriteTrail( + Entity, //Entity + Data.AttachmentID or 0, //iAttachmentID + Data.Color, //Color + Data.Additive or false, // bAdditive + Data.StartSize, //fStartWidth + Data.EndSize, //fEndWidth + Data.Length, //fLifetime + 2 / (Data.StartSize+Data.EndSize), //fTextureRes + Data.Material .. ".vmt" + ) //strTexture + + Entity.SToolTrail = trail_entity + Player:AddCleanup( "trails", trail_entity ) + + duplicator.StoreEntityModifier( Entity, "trail", Data ) + + return trail_entity +end + +hook.Add("InitPostEntity", "trails", function() + duplicator.RegisterEntityModifier( "trail", SetTrails ) +end) + +--- Removes the trail from . +e2function void entity:removeTrails() + if not validEntity(this) then return end + if not isOwner(self, this) then return end + + SetTrails(self.player, this, nil) +end + +local function composedata(startSize, endSize, length, material, color, alpha) + if string.find(material, '"', 1, true) then return nil end + if not file.Exists("../materials/"..material..".vmt") then return nil end -- check for non-existant materials. + + return { + Color = Color( color[1], color[2], color[3], alpha ), + Length = length, + StartSize = startSize, + EndSize = endSize, + Material = material, + } +end + +--- StartSize, EndSize, Length, Material, Color (RGB), Alpha +--- Adds a trail to with the specified attributes. +e2function void entity:setTrails(startSize, endSize, length, string material, vector color, alpha) + if not validEntity(this) then return end + if not isOwner(self, this) then return end + + local Data = composedata(startSize, endSize, length, material, color, alpha) + if not Data then return end + + SetTrails(self.player, this, Data) +end + +--- StartSize, EndSize, Length, Material, Color (RGB), Alpha, AttachmentID, Additive +--- Adds a trail to with the specified attributes. +e2function void entity:setTrails(startSize, endSize, length, string material, vector color, alpha, attachmentID, additive) + if not validEntity(this) then return end + if not isOwner(self, this) then return end + + local Data = composedata(startSize, endSize, length, material, color, alpha) + if not Data then return end + + Data.AttachmentID = attachmentID + Data.Additive = additive ~= 0 + + SetTrails(self.player, this, Data) +end + +/******************************************************************************/ + +--- Returns 's attachment ID associated with +e2function number entity:lookupAttachment(string attachmentName) + if not validEntity(this) then return 0 end + return this:LookupAttachment(attachmentName) +end + +--- Returns 's attachment position associated with +e2function vector entity:attachmentPos(attachmentID) + if not validEntity(this) then return { 0, 0, 0 } end + local attachment = this:GetAttachment(attachmentID) + if not attachment then return { 0, 0, 0 } end + return attachment.Pos +end + +--- Returns 's attachment angle associated with +e2function angle entity:attachmentAng(attachmentID) + if not validEntity(this) then return { 0, 0, 0 } end + local attachment = this:GetAttachment(attachmentID) + if not attachment then return { 0, 0, 0 } end + local ang = attachment.Ang + return { ang.p, ang.y, ang.r } +end + +--- Same as :attachmentPos(entity:lookupAttachment()) +e2function vector entity:attachmentPos(string attachmentName) + if not validEntity(this) then return { 0, 0, 0 } end + local attachment = this:GetAttachment(this:LookupAttachment(attachmentName)) + if not attachment then return { 0, 0, 0 } end + return attachment.Pos +end + +--- Same as :attachmentAng(entity:lookupAttachment()) +e2function angle entity:attachmentAng(string attachmentName) + if not validEntity(this) then return { 0, 0, 0 } end + local attachment = this:GetAttachment(this:LookupAttachment(attachmentName)) + if not attachment then return { 0, 0, 0 } end + local ang = attachment.Ang + return { ang.p, ang.y, ang.r } +end + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/extloader.lua b/lua/entities/gmod_wire_expression2/core/extloader.lua new file mode 100644 index 0000000000..84f1a84b21 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/extloader.lua @@ -0,0 +1,258 @@ +/******************************************************************************\ + Loading extensions +\******************************************************************************/ + +-- deal with lua_openscript entities/gmod_wire_expression2/core/extloader.lua +local is_reload = e2_processerror ~= nil + +-- Save E2's metatable for wire_expression2_reload +if ENT then + local wire_expression2_ENT = ENT + function wire_expression2_reload(ply) + if validEntity(ply) and ply:IsPlayer() and not ply:IsSuperAdmin() then return end + + Msg("Calling destructors for all Expression2 chips.\n") + local chips = ents.FindByClass("gmod_wire_expression2") + for _,chip in ipairs(chips) do + if not chip.error then + chip:PCallHook('destruct') + end + chip.script = nil + end + Msg("Reloading Expression2 extensions.\n") + + ENT = wire_expression2_ENT + wire_expression2_is_reload = true + include("entities/gmod_wire_expression2/core/extloader.lua") + wire_expression2_is_reload = nil + ENT = nil + + Msg("Calling constructors for all Expression2 chips.\n") + wire_expression2_prepare_functiondata() + wire_expression2_sendfunctions(player.GetAll()) + for _,chip in ipairs(chips) do + pcall(chip.OnRestore, chip) + end + Msg("Done reloading Expression2 extensions.\n") + end + concommand.Add("wire_expression2_reload", wire_expression2_reload) +end + +wire_expression2_reset_extensions() + + +include("extpp.lua") + +-- begin preprocessor stuff -- +-- mostly a huge workaround for the lack of a chunkname parameter in RunString (thanks, garry) + +-- make some wrappers for some commonly used functions that use callbacks. +-- this way we can wrap the callback functions that are passed to them and process their error messages. +local timer_Create = timer.Create +local timer_Adjust = timer.Adjust +local timer_Simple = timer.Simple +local hook_Add = hook.Add + +local function wrap_function(func) + return function(...) + e2_install_hook_fix() + local ok, ret = pcall(func, ...) + e2_remove_hook_fix() + if not ok then error(e2_processerror(ret),0) end + return ret + end +end + +local hook_fix_state = 0 + +function e2_install_hook_fix() + hook_fix_state = hook_fix_state + 1 + function timer.Create(uniqueID, delay, reps, func, ...) + return timer_Create(uniqueID, delay, reps, wrap_function(func), ...) + end + + function timer.Adjust(uniqueID, delay, reps, func, ...) + return timer_Adjust(uniqueID, delay, reps, wrap_function(func), ...) + end + + function timer.Simple(delay, func, ...) + return timer_Simple(delay, wrap_function(func), ...) + end + + function hook.Add(hook, unique_name, hook_func) + return hook_Add(hook, unique_name, wrap_function(hook_func)) + end +end + +function e2_remove_hook_fix() + hook_fix_state = hook_fix_state - 1 + if hook_fix_state == 0 then + timer.Create = timer_Create + timer.Adjust = timer_Adjust + timer.Simple = timer_Simple + hook.Add = hook_Add + end +end + +-- end of wrap stuff + +local function countlines(s) + local _,number_of_newlines = string.gsub(s, "\n", "") + return number_of_newlines+1 +end + +local function luaExists(luaname) + return #file.FindInLua(luaname) ~= 0 +end + +local e2_chunks = { { 1, "unknown chunk" } } +local e2_lastline = 10 + +function e2_processerror(e) + if e:sub(1,1) ~= ":" then return e end + local line, err = string.match(e,"^:([1-9][0-9]*):(.*)$") + if not line then return e end + line = tonumber(line) + local found_chunk = e2_chunks[#e2_chunks] + for i,chunk in ipairs(e2_chunks) do + if chunk[1] > line then + found_chunk = e2_chunks[i-1] + break + end + end + return string.format("%s:%d:%s", found_chunk[2], line - found_chunk[1], err) +end + +local included_files = {} + +-- parses typename/typeid associations from a file and stores info about the file for later use by e2_include_finalize/e2_include_pass2 +local function e2_include(name) + local path,filename = string.match(name, "^(.-/?)([^/]*)$") + + local cl_name = path.."cl_"..filename + if luaExists("entities/gmod_wire_expression2/core/"..cl_name) then + -- If a file of the same name prefixed with cl_ exists, send it to the client and load it there. + AddCSE2File(cl_name) + end + + local luaname = "entities/gmod_wire_expression2/core/"..name + local contents = file.Read("../lua/"..luaname) or "" + e2_extpp_pass1(contents) + table.insert(included_files, {name, luaname, contents}) +end + +-- parses and executes an extension +local function e2_include_pass2(name, luaname, contents) + local ok,ret= pcall(e2_extpp_pass2, contents) + if not ok then + ErrorNoHalt(luaname..ret.."\n") + return + end + + if not ret then + -- e2_extpp_pass2 returned false => file doesn't need preprocessing => use the regular means of inclusion + return include(name) + end + -- file needs preprocessing + + -- This variable receives a table with the results from pcalling the code + e2_includeerror = nil + -- run code with some padding so the error can be matched up with a file name by e2_processerror. + RunString("e2_includeerror = { pcall(function() "..string.rep("\n",e2_lastline)..ret.." end) }") + + -- store info for e2_processerror + table.insert(e2_chunks, { e2_lastline, luaname }) + e2_lastline = e2_lastline + countlines(ret) + + -- evaluate errors + if e2_includeerror then + -- no syntax errors, maybe a runtime error? + ok,ret = unpack(e2_includeerror) + if not ok then + -- runtime error => show and bail out + ErrorNoHalt(e2_processerror(ret).."\n") + return + end + else + -- no error table present -> there must have been a syntax error. Display context information + ErrorNoHalt("...while parsing E2 extension. Precise error:\n"..luaname) + -- evaluate again, displaying the same syntax error, but this time with the proper line number + RunString(ret) + -- This results in something like this: + --[[ + :1234: You forgot a closing ')', noob! + ...while parsing E2 extension. Precise error: + entities/gmod_wire_expression2/core/foo.lua:3: You forgot a closing ')', noob! + ]] + -- Sorry, it's not possible to improve this. + end +end + +local function e2_include_finalize() + e2_install_hook_fix() + for _,info in ipairs(included_files) do + e2_include_pass2(unpack(info)) + end + e2_remove_hook_fix() + included_files = nil + e2_include = nil +end + +-- end preprocessor stuff + +e2_include("core.lua") +e2_include("array.lua") +e2_include("number.lua") +e2_include("vector.lua") +e2_include("string.lua") +e2_include("angle.lua") +e2_include("entity.lua") +e2_include("player.lua") +e2_include("timer.lua") +e2_include("selfaware.lua") +e2_include("unitconv.lua") +e2_include("wirelink.lua") +e2_include("console.lua") +e2_include("find.lua") +e2_include("files.lua") +e2_include("cl_files.lua") +e2_include("globalvars.lua") +e2_include("ranger.lua") +e2_include("sound.lua") +e2_include("color.lua") +e2_include("serverinfo.lua") +e2_include("chat.lua") +e2_include("constraint.lua") +e2_include("weapon.lua") +e2_include("gametick.lua") +e2_include("npc.lua") +e2_include("matrix.lua") +e2_include("vector2.lua") +e2_include("signal.lua") +e2_include("bone.lua") +e2_include("table.lua") +e2_include("glon.lua") +e2_include("hologram.lua") +e2_include("complex.lua") +e2_include("bitwise.lua") +e2_include("quaternion.lua") +e2_include("debug.lua") + +e2_include("compat.lua") +e2_include("custom.lua") + +do + local list = file.FindInLua("entities/gmod_wire_expression2/core/custom/*.lua") + for _,filename in pairs(list) do + if filename:sub(1,3) == "cl_" then + -- If the is prefixed with "cl_", send it to the client and load it there. + AddCSE2File("custom/" .. filename) + else + e2_include("custom/" .. filename) + end + end +end + +e2_include_finalize() + +wire_expression2_CallHook("postinit") diff --git a/lua/entities/gmod_wire_expression2/core/extpp.lua b/lua/entities/gmod_wire_expression2/core/extpp.lua new file mode 100644 index 0000000000..7da00bb0b5 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/extpp.lua @@ -0,0 +1,391 @@ +AddCSLuaFile("entities/gmod_wire_expression2/core/extpp.lua") + +-- some constants -- + +local p_typename = "[a-z][a-z0-9]*" +local p_typeid = "[a-z][a-z0-9]?[a-z0-9]?[a-z0-9]?[a-z0-9]?" +local p_argname = "[a-zA-Z][a-zA-Z0-9_]*" +local p_funcname = "[a-z][a-zA-Z0-9]*" +local p_func_operator = "[-a-zA-Z0-9+*/%%^=!><&|$_%[%]]*" + +local OPTYPE_FUNCTION = nil +local OPTYPE_NORMAL = 0 +local OPTYPE_DONT_FETCH_FIRST = 1 +local OPTYPE_ASSIGN = 2 +local OPTYPE_APPEND_RET = 3 + +local optable = { + ["operator+" ] = "add", + ["operator++"] = {"inc", OPTYPE_DONT_FETCH_FIRST}, + ["operator-" ] = "sub", + ["operator--"] = {"dec", OPTYPE_DONT_FETCH_FIRST}, + ["operator*" ] = "mul", + ["operator/" ] = "div", + ["operator%" ] = "mod", + ["operator^" ] = "exp", + + ["operator=" ] = {"ass", OPTYPE_ASSIGN}, + ["operator=="] = "eq", + ["operator!" ] = "not", + ["operator!="] = "neq", + ["operator>" ] = "gth", + ["operator>="] = "geq", + ["operator<" ] = "lth", + ["operator<="] = "leq", + + ["operator&" ] = "and", + ["operator&&"] = "and", + ["operator|" ] = "or", + ["operator||"] = "or", + + ["operator[]"] = "idx", -- typeless op[] + ["operator[T]"] = {"idx", OPTYPE_APPEND_RET}, -- typed op[] + ["operator$" ] = {"dlt", OPTYPE_DONT_FETCH_FIRST}, -- mostly superfluous now + + ["operator_is" ] = "is", + ["operator_neg"] = "neg", +} + +-- This is an array for types that were parsed from all E2 extensions. We initialize it with an alias "number" for "normal". +local preparsed_types = { ["NUMBER"] = "n" } + +-- This function checks whether its argument is a valid type id. +local function is_valid_typeid(typeid) + return (typeid:match("^[a-wy-zA-WY-Z]$") or typeid:match("^[xX][a-wy-zA-WY-Z0-9][a-wy-zA-WY-Z0-9]$")) and true +end + +-- Returns the typeid associated with the given typename +function e2_get_typeid(typename) + local n = string.upper(typename) + + -- was the type registered with E-2? + if wire_expression_types[n] then return wire_expression_types[n][1] end + + -- was the name found when looking for registerType lines? + if preparsed_types[n] then return preparsed_types[n] end + + -- is the type name a valid typeid? use the type name as the typeid + if is_valid_typeid(typename) then return typename end + return nil +end + +-- parses an argument list +function e2_parse_args(args) + local ellipses = false + local argtable = { typeids = {}, argnames = {}} + if args:find("%S") == nil then return argtable end -- no arguments + local function handle_arg(arg) + -- ellipses before this argument? raise error + if ellipses then error("PP syntax error: Ellipses (...) must be the last argument.",0) end + -- is this argument an ellipsis? + if string.match(arg, "^%s*%.%.%.%s*$") then + -- signal ellipses-ness + ellipses = true + return false + end + + -- assume a type name was given and split up the argument into type name and argument name. + local typename,argname = string.match(arg, "^%s*("..p_typename..")%s+("..p_argname..")%s*$") + + -- the assumption failed + if not typename then + -- try looking for a argument name only and defaulting the type name to "number" + argname = string.match(arg, "^%s*("..p_argname..")%s*$") + typename = "number" + end + + -- this failed as well? give up and print an error + if not argname then error("PP syntax error: Invalid function parameter syntax.",0) end + + local typeid = e2_get_typeid(typename) + + if not typeid then error("PP syntax error: Invalid parameter type '"..typename.."' for argument '"..argname.."'.",0) end + + table.insert(argtable.typeids, typeid) + table.insert(argtable.argnames, argname) + return false + end + -- find the argument before the first comma + local firstarg = args:find(",") or (args:len()+1) + firstarg = args:sub(1, firstarg-1) + -- handle it + handle_arg(firstarg) + + -- find and handle the remaining arguments. + args:gsub(",([^,]*)",handle_arg) + return argtable, ellipses +end + +local function mangle(name, arg_typeids, op_type) + if op_type then name = "operator_"..name end + local ret = "e2_"..name + if arg_typeids == "" then return ret end + return ret.."_"..arg_typeids:gsub("[:=]","_") +end + +local function linenumber(s,i) + local c = 1 + local line = 0 + while c and c < i do + line = line + 1 + c = s:find("\n", c+1, true) + end + return line +end + +-- returns a name and a register function for the given name +-- also optionally returns a flag signaling how to treat the operator in question. +local function handleop(name) + local operator = optable[name] + + if operator then + local op_type = OPTYPE_NORMAL + + -- special treatment is needed for some operators. + if type(operator) == "table" then operator, op_type = unpack(operator) end + + -- return everything. + return operator, "registerOperator", op_type + elseif name:find("^"..p_funcname.."$") then + return name, "registerFunction", OPTYPE_FUNCTION + else + error("PP syntax error: Invalid character in function name.",0) + end +end + +local function makestringtable(tbl, i, j) + if #tbl == 0 then return "{}" end + if not i then i = 1 + elseif i < 0 then i = #tbl + i + 1 + elseif i < 1 then i = 1 + elseif i > #tbl then i = #tbl end + + if not j then j = #tbl + elseif j < 0 then j = #tbl + j + 1 + elseif j < 1 then j = 1 + elseif j > #tbl then j = #tbl end + + --return string.format("{"..string.rep("%q,", math.max(0,j-i+1)).."}", unpack(tbl, i, j)) + local ok, ret = pcall(string.format, "{"..string.rep("%q,", math.max(0,j-i+1)).."}", unpack(tbl, i, j)) + if not ok then + print(i,j,#tbl,"{"..string.rep("%q,", math.max(0,j-i+1)).."}") + error(ret) + end + return ret +end + +function e2_extpp_pass1(contents) + -- look for registerType lines and fill preparsed_types with them + for typename, typeid in string.gmatch("\n"..contents, '%WregisterType%(%s*"('..p_typename..')"%s*,%s*"('..p_typeid..')"') do + preparsed_types[string.upper(typename)] = typeid + end +end + +function e2_extpp_pass2(contents) + -- We add some stuff to both ends of the string so we can look for %W (non-word characters) at the ends of the patterns. + contents = "\nlocal tempcosts={}"..contents.."\n " + + -- this is a list of pieces that make up the final code + local output = {} + -- this is a list of registerFunction lines that will be put at the end of the file. + local function_register = {} + -- We start from position 2, since char #1 is always the \n we added earlier + local lastpos = 2 + + local aliaspos,aliasdata = nil,nil + + -- This flag helps determine whether the preprocessor changed, so we can tell the environment about it. + local changed = false + for h_begin, ret, thistype, colon, name, args, whitespace, equals, h_end in contents:gmatch("()e2function%s+("..p_typename..")%s+([a-z0-9]-)%s*(:?)%s*("..p_func_operator..")%(([^)]*)%)(%s*)(=?)()") do + changed = true + + local function handle_function() + if contents:sub(h_begin-1,h_begin-1):match("%w") then return end + local aliasflag = nil + if equals == "" then + if aliaspos then + if contents:sub(aliaspos,h_begin-1):find("%S") then error ("PP syntax error: Malformed alias definition.",0) end + -- right hand side of an alias assignment + aliasflag = 2 + aliaspos = nil + end + else + if aliaspos then error("PP syntax error: Malformed alias definition.",0) end + -- left hand side of an alias assignment + aliasflag = 1 + aliaspos = h_end + end + + -- check for some obvious errors + if thistype~="" and colon=="" then error("PP syntax error: Function names may not start with a number.",0) end + if thistype=="" and colon~="" then error("PP syntax error: No type for 'this' given.",0) end + if thistype:sub(1,1):find("[0-9]") then error("PP syntax error: Type names may not start with a number.",0) end + + -- append everything since the last function to the output. + table.insert(output, contents:sub(lastpos,h_begin-1)) + -- advance lastpos to the end of the function header + lastpos = h_end + + -- this table contains the arguments in the following form: + -- argtable.argname[n] = "" + -- argtable.typeids[n] = "" + local argtable, ellipses = e2_parse_args(args) + + -- take care of operators: give them a different name and register function + -- op_type is nil if we register a function and a number if it as operator + local name, regfn, op_type = handleop(name) + + -- return type (void means "returns nothing", i.e. "" in registerFunctionese) + local ret_typeid = (ret == "void") and "" or e2_get_typeid(ret) -- ret_typeid = (ret == "void") ? "" : e2_get_typeid(ret) + + -- return type not found => throw an error + if not ret_typeid then error("PP syntax error: Invalid return type: '"..ret.."'",0) end + + -- if "typename:" was found in front of the function name + if thistype ~= "" then + -- evaluate the type name + local this_typeid = e2_get_typeid(thistype) + + -- the type was not found? + if this_typeid == nil then + -- is the type name a valid typeid? + if is_valid_typeid(thistype) then + -- use the type name as the typeid + this_typeid = thistype + else + -- type is not found and not a valid typeid => error + error("PP syntax error: Invalid type for 'this': '"..thistype.."'",0) + end + end + + -- prepend a "this" argument to the list, with the parsed type + if op_type then + -- allow pseudo-member-operators. example: e2function matrix:operator*(factor) + table.insert(argtable.typeids, 1, this_typeid) + else + table.insert(argtable.typeids, 1, this_typeid..":") + end + table.insert(argtable.argnames, 1, "this") + end -- if colon ~= "" + + -- add a sub-table for flagging arguments as "no opfetch" + argtable.no_opfetch = {} + + if op_type == OPTYPE_ASSIGN then -- assignment + -- the assignment operator is registered with only argument typeid, hence we need a special case. + -- we need to make sure the two types match: + if argtable.typeids[1] ~= argtable.typeids[2] then error("PP syntax error: operator= needs two arguments of the same type.", 0) end + + -- remove the typeid of one of the arguments from the list + argtable.typeids[1] = "" + + -- mark the argument as "no opfetch" + argtable.no_opfetch[1] = true + elseif op_type == OPTYPE_DONT_FETCH_FIRST then -- delta/increment/decrement + -- mark the argument as "no opfetch" + argtable.no_opfetch[1] = true + elseif op_type == OPTYPE_APPEND_RET then + table.insert(argtable.typeids, 1, ret_typeid.."=") + end + + -- -- prepare some variables needed to generate the function header and the registerFunction line -- -- + + -- concatenated typeids. example: "s:nn" + local arg_typeids = table.concat(argtable.typeids) + + -- generate a mangled name, which serves as the function's Lua name + local mangled_name = mangle(name, arg_typeids, op_type) + + if aliasflag then + if aliasflag == 1 then + -- left hand side of an alias definition + aliasdata = { regfn, name, arg_typeids, ret_typeid } + elseif aliasflag == 2 then + -- right hand side of an alias definition + regfn, name, arg_typeids, ret_typeid = unpack(aliasdata) + table.insert(function_register, ('if %s then %s(%q, %q, %q, %s) end\n'):format(mangled_name, regfn, name, arg_typeids, ret_typeid, mangled_name)) + end + else + -- save tempcost + table.insert(output, string.format("tempcosts[%q]=__e2getcost() ", mangled_name)) + if ellipses then + -- generate a registerFunction line + table.insert(function_register, string.format('if %s then %s(%q, %q, %q, %s) end\n', mangled_name, regfn, name, arg_typeids.."...", ret_typeid, mangled_name)) + + -- generate a new function header and append it to the output + table.insert(output, 'local function '..mangled_name..'(self, args, typeids, ...)') + table.insert(output, " if not typeids then") + table.insert(output, " local arr,typeids,source_typeids,tmp={},{},args[#args]") + table.insert(output, " for i="..(2+#argtable.typeids)..",#args-1 do") + table.insert(output, " tmp=args[i]") + table.insert(output, " arr[#arr+1]=tmp[1](self,tmp)") + table.insert(output, " typeids[#typeids+1]=source_typeids[i-1]") + table.insert(output, " end") + table.insert(output, " return "..mangled_name.."(self,args,typeids,unpack(arr))") + table.insert(output, " end") + else + -- generate a registerFunction line + table.insert(function_register, string.format('if %s then %s(%q, %q, %q, %s, tempcosts[%q], %s) end\n', mangled_name, regfn, name, arg_typeids, ret_typeid, mangled_name, mangled_name, makestringtable(argtable.argnames,(thistype~="") and 2 or 1))) + + -- generate a new function header and append it to the output + table.insert(output, 'local function '..mangled_name..'(self, args)') + end + + -- if the function has arguments, insert argument fetch code + if #argtable.argnames ~= 0 then + local argfetch, opfetch_l, opfetch_r = '', '', '' + for i,n in ipairs(argtable.argnames) do + if not argtable.no_opfetch[i] then + -- generate opfetch code if not flagged as "no opfetch" + opfetch_l = opfetch_l..n..', ' + opfetch_r = opfetch_r..string.format('%s[1](self, %s), ',n,n) + end + argfetch = argfetch..string.format('args[%d], ',i+1,i+1) + end + + -- remove the trailing commas + argfetch = argfetch:sub(1,-3) + opfetch_l = opfetch_l:sub(1,-3) + opfetch_r = opfetch_r:sub(1,-3) + + -- fetch the ops from the args + table.insert(output, ' local ' .. table.concat(argtable.argnames, ', ') .. ' = ' .. argfetch) + + -- fetch the rvs from the ops + table.insert(output, ' ' .. opfetch_l .. ' = ' .. opfetch_r) + end -- if #argtable.argnames ~= 0 + end -- if aliasflag + table.insert(output, whitespace) + end -- function handle_function() + + -- use pcall, so we can add line numbers to all errors + local ok, msg = pcall(handle_function) + if not ok then + if msg:sub(1,2) == "PP" then + error(":"..linenumber(contents,h_begin)..": "..msg,0) + else + error(": PP internal error: "..msg,0) + end + end + end -- for gmatch(e2function) + + + -- did the preprocessor change anything? + if changed then + -- yes => sweep everything together into a neat pile of hopefully valid lua code + return table.concat(output)..contents:sub(lastpos,-6)..table.concat(function_register) + else + -- no => tell the environment about it, so it can include() the source file instead. + return false + end +end + +if e2_sim then + function e2_extpp(contents) + preparsed_types = { ["NUMBER"] = "n" } + e2_extpp_pass1(contents) + local ret = e2_extpp_pass2(contents) + preparsed_types = nil + return ret + end +end diff --git a/lua/entities/gmod_wire_expression2/core/files.lua b/lua/entities/gmod_wire_expression2/core/files.lua new file mode 100644 index 0000000000..8ed1dcb5b2 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/files.lua @@ -0,0 +1,164 @@ +-------------------------------------------------------------------------------- +-- File extension by {iG} I_am_McLovin -- +-------------------------------------------------------------------------------- + +if not datastream then require( "datastream" ) end + +local uploaded_files = {} +local uploads = {} +local run_on_load = { 0, "" } + +/******************************** File loading ********************************/ + +e2function void fileLoad(string filename) + if timer.IsTimer( "file_load_delay" .. self.player:EntIndex() ) or ( uploaded_files[self.player] and ( uploaded_files[self.player].amt > 100 or ( uploaded_files[self.player][filename] and uploaded_files[self.player][filename].uploaded and uploaded_files[self.player][filename].uploaded == true ) ) ) then return end + + uploaded_files[self.player][filename] = { + uploaded = false, + data = "", + ent = self.entity + } + + umsg.Start( "wire_expression2_fileload", self.player ) + umsg.String( filename ) + umsg.End() + + -- TODO: replace by an entry in a local table. + timer.Create( "file_load_delay"..self.player:EntIndex(), 10, 1, function( ply ) + timer.Remove( "file_load_delay" .. ply:EntIndex() ) + end, self.player ) +end + +e2function number fileLoaded(string filename) + if uploaded_files[self.player] and uploaded_files[self.player][filename] and uploaded_files[self.player][filename].uploaded == true then + return 1 + end + + return 0 +end + +e2function number fileCanLoad() + if timer.IsTimer( "file_load_delay" .. self.player:EntIndex() ) then + return 0 + end + + return 1 +end + +/**************************** File reading/writing ****************************/ + +e2function string fileRead(string filename) + if uploaded_files[self.player] and uploaded_files[self.player][filename] and uploaded_files[self.player][filename].uploaded == true then + return uploaded_files[self.player][filename].data + else + return "" + end +end + +e2function void fileWrite(string filename, string data) + + if uploaded_files[self.player] and uploaded_files[self.player].amt <= 100 then + uploaded_files[self.player][filename] = {} + uploaded_files[self.player][filename].uploaded = true + uploaded_files[self.player][filename].data = data + uploaded_files[self.player][filename].ent = self.entity + end + + datastream.StreamToClients( self.player, "wire_expression2_filewrite", { ["name"] = filename, ["data"] = data, ["append"] = false } ) +end + +e2function void fileAppend(string filename, string data) + + if uploaded_files[self.player] and uploaded_files[self.player].amt <= 100 then + local old_file = "" + + if uploaded_files[self.player][filename] and uploaded_files[self.player][filename].uploaded == true then + old_file = uploaded_files[self.player][filename].data + end + + uploaded_files[self.player][filename] = {} + uploaded_files[self.player][filename].uploaded = true + uploaded_files[self.player][filename].data = old_file .. data + uploaded_files[self.player][filename].ent = self.entity + end + + datastream.StreamToClients( self.player, "wire_expression2_filewrite", { ["name"] = filename, ["data"] = data, ["append"] = true } ) +end + +e2function void fileRemove(string filename) + + if uploaded_files[self.player] and uploaded_files[self.player][filename] then + uploaded_files[self.player][filename] = nil + uploaded_files[self.player].amt = uploaded_files[self.player].amt - 1 + end +end + +/****************************** runOnFile event *******************************/ + +e2function void runOnFile(active) + + if active == 1 then + uploads[self.entity] = true + else + uploads[self.entity] = nil + end +end + +e2function number fileClk() + return run_on_load[1] +end + +e2function number fileClk(string filename) + + if run_on_load[1] == 1 and run_on_load[2] == filename then + return 1 + else + return 0 + end +end + +/******************************** Hooks'n'shit ********************************/ + +registerCallback( "construct", function( self ) + if !uploaded_files[self.player] then + uploaded_files[self.player] = {} + uploaded_files[self.player].amt = 0 + end +end ) + +hook.Add( "EntityRemoved", "wire_expression2_filedata_delete", function( ply ) + uploaded_files[ply] = nil +end ) + +hook.Add( "AcceptStream", "wire_expression2_filedata", function( ply, handler, id ) + if handler == "wire_expression2_filedata" then return true end +end ) + +datastream.Hook( "wire_expression2_filedata", function( ply, handler, id, encoded, decoded ) + if not decoded.filename then return end + if not decoded.filedata then return end + + local plfiles = uploaded_files[ply] + if not plfiles then return end + + local fileentry = plfiles[decoded.filename] + if not fileentry then return end + + + fileentry.uploaded = true + fileentry.data = decoded.filedata + plfiles.amt = plfiles.amt + 1 + + run_on_load[1] = 1 + run_on_load[2] = decoded.filename + + for e,_ in pairs( uploads ) do + if fileentry.ent == e then + e:Execute() + break + end + end + + run_on_load[1] = 0 + run_on_load[2] = "" +end ) diff --git a/lua/entities/gmod_wire_expression2/core/find.lua b/lua/entities/gmod_wire_expression2/core/find.lua new file mode 100644 index 0000000000..96dc4fe235 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/find.lua @@ -0,0 +1,825 @@ +local function table_IsEmpty(t) return not next(t) end + +local filterList = E2Lib.filterList + +-- -- some generic filter criteria -- -- + +local function filter_all() return true end +local function filter_none() return false end + +local forbidden_classes = { + /* + ["info_apc_missile_hint"] = true, + ["info_camera_link"] = true, + ["info_constraint_anchor"] = true, + ["info_hint"] = true, + ["info_intermission"] = true, + ["info_ladder_dismount"] = true, + ["info_landmark"] = true, + ["info_lighting"] = true, + ["info_mass_center"] = true, + ["info_no_dynamic_shadow"] = true, + ["info_node"] = true, + ["info_node_air"] = true, + ["info_node_air_hint"] = true, + ["info_node_climb"] = true, + ["info_node_hint"] = true, + ["info_node_link"] = true, + ["info_node_link_controller"] = true, + ["info_npc_spawn_destination"] = true, + ["info_null"] = true, + ["info_overlay"] = true, + ["info_particle_system"] = true, + ["info_projecteddecal"] = true, + ["info_snipertarget"] = true, + ["info_target"] = true, + ["info_target_gunshipcrash"] = true, + ["info_teleport_destination"] = true, + ["info_teleporter_countdown"] = true, + */ + ["info_player_allies"] = true, + ["info_player_axis"] = true, + ["info_player_combine"] = true, + ["info_player_counterterrorist"] = true, + ["info_player_deathmatch"] = true, + ["info_player_logo"] = true, + ["info_player_rebel"] = true, + ["info_player_start"] = true, + ["info_player_terrorist"] = true, + ["info_player_blu"] = true, + ["info_player_red"] = true, + ["prop_dynamic"] = true, + ["physgun_beam"] = true, + ["player_manager"] = true, + ["predicted_viewmodel"] = true, + ["gmod_ghost"] = true, +} +local function filter_default(self) + local chip = self.entity + return function(ent) + if forbidden_classes[ent:GetClass()] then return false end + + if ent == chip then return false end + return true + end +end + +-- -- some filter criterion generators -- -- + +-- Generates a filter that filters out everything not in a lookup table. +local function filter_in_lookup(lookup) + if table_IsEmpty(lookup) then return filter_none end + + return function(ent) + return lookup[ent] + end +end + +-- Generates a filter that filters out everything in a lookup table. +local function filter_not_in_lookup(lookup) + if table_IsEmpty(lookup) then return filter_all end + + return function(ent) + return not lookup[ent] + end +end + +-- Generates a filter that filters out everything not in a lookup table. +local function filter_function_result_in_lookup(lookup, func) + if table_IsEmpty(lookup) then return filter_none end + + return function(ent) + return lookup[func(ent)] + end +end + +-- Generates a filter that filters out everything in a lookup table. +local function filter_function_result_not_in_lookup(lookup, func) + if table_IsEmpty(lookup) then return filter_all end + + return function(ent) + return not lookup[func(ent)] + end +end + +-- checks if binary_predicate(func(ent), key) matches for any of the keys in the lookup table. Returns false if it does. +local function filter_binary_predicate_match_none(lookup, func, binary_predicate) + if table_IsEmpty(lookup) then return filter_all end + + return function(a) + a = func(a) + for b,_ in pairs(lookup) do + if binary_predicate(a, b) then return false end + end + return true + end +end + +-- checks if binary_predicate(func(ent), key) matches for any of the keys in the lookup table. Returns true if it does. +local function filter_binary_predicate_match_one(lookup, func, binary_predicate) + if table_IsEmpty(lookup) then return filter_none end + + return function(a) + a = func(a) + for b,_ in pairs(lookup) do + if binary_predicate(a, b) then return true end + end + return false + end +end + + +-- -- filter criterion combiners -- -- + +local _filter_and = { + [0] = function() return filter_all end, + function(f1) return f1 end, + function(f1,f2) return function(v) return f1(v) and f2(v) end end, + function(f1,f2,f3) return function(v) return f1(v) and f2(v) and f3(v) end end, + function(f1,f2,f3,f4) return function(v) return f1(v) and f2(v) and f3(v) and f4(v) end end, + function(f1,f2,f3,f4,f5) return function(v) return f1(v) and f2(v) and f3(v) and f4(v) and f5(v) end end, + function(f1,f2,f3,f4,f5,f6) return function(v) return f1(v) and f2(v) and f3(v) and f4(v) and f5(v) and f6(v) end end, + function(f1,f2,f3,f4,f5,f6,f7) return function(v) return f1(v) and f2(v) and f3(v) and f4(v) and f5(v) and f6(v) and f7(v) end end, +} + +-- Usage: filter = filter_and(filter1, filter2, filter3) +local function filter_and(...) + local args = {...} + + -- filter out all filter_all entries + filterList(args, function(f) + if f == filter_none then + args = { filter_none } -- If a filter_none is in the list, we can discard all other filters. + end + return f ~= filter_all + end) + + local combiner = _filter_and[#args] + if not combiner then return nil end -- TODO: write generic combiner + return combiner(unpack(args)) +end + +local _filter_or = { + [0] = function() return filter_none end, + function(f1) return f1 end, + function(f1,f2) return function(v) return f1(v) or f2(v) end end, + function(f1,f2,f3) return function(v) return f1(v) or f2(v) or f3(v) end end, + function(f1,f2,f3,f4) return function(v) return f1(v) or f2(v) or f3(v) or f4(v) end end, + function(f1,f2,f3,f4,f5) return function(v) return f1(v) or f2(v) or f3(v) or f4(v) or f5(v) end end, + function(f1,f2,f3,f4,f5,f6) return function(v) return f1(v) or f2(v) or f3(v) or f4(v) or f5(v) or f6(v) end end, + function(f1,f2,f3,f4,f5,f6,f7) return function(v) return f1(v) or f2(v) or f3(v) or f4(v) or f5(v) or f6(v) or f7(v) end end, +} + +-- Usage: filter = filter_or(filter1, filter2, filter3) +local function filter_or(...) + local args = {...} + + -- filter out all filter_none entries + filterList(args, function(f) + if f == filter_all then + args = { filter_all } -- If a filter_all is in the list, we can discard all other filters. + end + return f ~= filter_none + end) + + local combiner = _filter_or[#args] + if not combiner then return nil end -- TODO: write generic combiner + return combiner(unpack(args)) +end + +local function invalidate_filters(self) + -- Update the filters the next time they are used. + self.data.findfilter = nil +end + +-- This function should be called after the black- or whitelists have changed. +local function update_filters(self) + -- Do not update again until the filters are invalidated the next time. + + local find = self.data.find + + --------------------- + -- blacklist -- + --------------------- + + -- blacklist for single entities + local bl_entity_filter = filter_not_in_lookup(find.bl_entity) + -- blacklist for a player's props + local bl_owner_filter = filter_function_result_not_in_lookup(find.bl_owner, function(ent) return getOwner(self,ent) end) + + -- blacklist for models + local bl_model_filter = filter_binary_predicate_match_none(find.bl_model, function(ent) return string.lower(ent:GetModel() or "") end, string.match) + -- blacklist for classes + local bl_class_filter = filter_binary_predicate_match_none(find.bl_class, function(ent) return string.lower(ent:GetClass()) end, string.match) + + -- combine all blacklist filters (done further down) + --local filter_blacklist = filter_and(bl_entity_filter, bl_owner_filter, bl_model_filter, bl_class_filter) + + --------------------- + -- whitelist -- + --------------------- + + local filter_whitelist = filter_all + + -- if not all whitelists are empty, use the whitelists. + local whiteListInUse = not (table_IsEmpty(find.wl_entity) and table_IsEmpty(find.wl_owner) and table_IsEmpty(find.wl_model) and table_IsEmpty(find.wl_class)) + + if whiteListInUse then + -- blacklist for single entities + local wl_entity_filter = filter_in_lookup(find.wl_entity) + -- blacklist for a player's props + local wl_owner_filter = filter_function_result_in_lookup(find.wl_owner, function(ent) return getOwner(self,ent) end) + + -- blacklist for models + local wl_model_filter = filter_binary_predicate_match_one(find.wl_model, function(ent) return string.lower(ent:GetModel() or "") end, string.match) + -- blacklist for classes + local wl_class_filter = filter_binary_predicate_match_one(find.wl_class, function(ent) return string.lower(ent:GetClass()) end, string.match) + + -- combine all whitelist filters + filter_whitelist = filter_or(wl_entity_filter, wl_owner_filter, wl_model_filter, wl_class_filter) + end + --------------------- + + -- finally combine all filters + --self.data.findfilter = filter_and(find.filter_default, filter_blacklist, filter_whitelist) + self.data.findfilter = filter_and(find.filter_default, bl_entity_filter, bl_owner_filter, bl_model_filter, bl_class_filter, filter_whitelist) +end + +local function applyFindList(self, findlist) + local findfilter = self.data.findfilter + if not findfilter then + update_filters(self) + findfilter = self.data.findfilter + end + filterList(findlist, findfilter) + + self.data.findlist = findlist + return #findlist +end + +--[[************************************************************************]]-- + +local chiplist = {} + +registerCallback("construct", function(self) + self.data.find = { + filter_default = filter_default(self), + bl_entity = {}, + bl_owner = {}, + bl_model = {}, + bl_class = {}, + + wl_entity = {}, + wl_owner = {}, + wl_model = {}, + wl_class = {}, + } + invalidate_filters(self) + self.data.findnext = 0 + self.data.findlist = {} + chiplist[self.data] = true +end) + +registerCallback("destruct", function(self) + chiplist[self.data] = nil +end) + +hook.Add("EntityRemoved", "wire_expression2_find_EntityRemoved", function(ent) + for chip,_ in pairs(chiplist) do + local find = chip.find + find.bl_entity[ent] = nil + find.bl_owner[ent] = nil + find.wl_entity[ent] = nil + find.wl_owner[ent] = nil + + filterList(chip.findlist, function(v) return ent ~= v end) + end +end) + +local wire_exp2_entFindRate = CreateConVar("wire_exp2_entFindRate", ".05") +local wire_exp2_playerFindRate = CreateConVar("wire_exp2_playerFindRate", ".01") + +--[[************************************************************************]]-- + +function query_blocked(self, update) + local time = CurTime() + if time < self.data.findnext then return true end + if update then self.data.findnext = time+wire_exp2_entFindRate:GetFloat()*update end + return false +end + +--- Returns the minimum delay between entity find events on a chip +e2function number findUpdateRate() + return wire_exp2_entFindRate:GetFloat() +end + +--- Returns the minimum delay between entity find events per player +e2function number findPlayerUpdateRate() + return wire_exp2_playerFindRate:GetFloat() +end + +--- Returns 1 if find functions can be used, 0 otherwise. +e2function number findCanQuery() + if query_blocked(self) then return 0 else return 1 end +end + +--[[************************************************************************]]-- + +--- Finds entities in a sphere around V with a radius of N, returns the number found after filtering +e2function number findInSphere(vector center, radius) + if query_blocked(self, 1) then return 0 end + center = Vector(center[1], center[2], center[3]) + + return applyFindList(self,ents.FindInSphere(center, radius)) +end + +--- Like findInSphere but with a [[http://mathworld.wolfram.com/SphericalCone.html Spherical cone]], arguments are for position, direction, length, and degrees (works now) +e2function number findInCone(vector position, vector direction, length, degrees) + if query_blocked(self, 4) then return 0 end + + position = Vector(position[1], position[2], position[3]) + direction = Vector(direction[1], direction[2], direction[3]):Normalize() + + local findlist = ents.FindInSphere(position, length) + + local cosDegrees = math.cos(math.rad(degrees)) + local Dot = direction.Dot + + -- update filter and apply it, together with the cone filter. This is an optimization over applying the two filters in separate passes + if not self.data.findfilter then update_filters(self) end + filterList(findlist, filter_and( + self.data.findfilter, + function(ent) + return Dot(direction, (ent:GetPos() - position):Normalize()) > cosDegrees + end + )) + + self.data.findlist = findlist + return #findlist +end + +--- Like findInSphere but with a globally aligned box, the arguments are the diagonal corners of the box +e2function number findInBox(vector min, vector max) + if query_blocked(self, 1) then return 0 end + min = Vector(min[1], min[2], min[3]) + max = Vector(max[1], max[2], max[3]) + return applyFindList(self, ents.FindInBox(min, max)) +end + +--- Find all entities with the given name +e2function number findByName(string name) + if query_blocked(self, 1) then return 0 end + return applyFindList(self, ents.FindByName(name)) +end + +--- Find all entities with the given model +e2function number findByModel(string model) + if query_blocked(self, 1) then return 0 end + return applyFindList(self, ents.FindByModel(model)) +end + +--- Find all entities with the given class +e2function number findByClass(string class) + if query_blocked(self, 1) then return 0 end + return applyFindList(self, ents.FindByClass(class)) +end + +--[[************************************************************************]]-- + +local function findPlayer(name) + name = string.lower(name) + return filterList(player.GetAll(), function(ent) return string.match(string.lower(ent:GetName()), name) end)[1] +end + +--- Returns the player with the given name, this is an exception to the rule +e2function entity findPlayerByName(string name) + if query_blocked(self, 1) then return nil end + return findPlayer(name) +end + +--[[************************************************************************]]-- + +--- Exclude all entities from from future finds +e2function void findExcludeEntities(array arr) + local bl_entity = self.data.find.bl_entity + local validEntity = validEntity + + for _,ent in ipairs(arr) do + if not validEntity(ent) then return end + bl_entity[ent] = true + end + invalidate_filters(self) +end + +--- Exclude from future finds +e2function void findExcludeEntity(entity ent) + if not validEntity(ent) then return end + self.data.find.bl_entity[ent] = true + invalidate_filters(self) +end + +--- Exclude this player from future finds (put it on the entity blacklist) +e2function void findExcludePlayer(entity ply) = e2function void findExcludeEntity(entity ent) + +--- Exclude this player from future finds (put it on the entity blacklist) +e2function void findExcludePlayer(string name) + local ply = findPlayer(name) + if not ply then return end + self.data.find.bl_entity[ply] = true + invalidate_filters(self) +end + +--- Exclude entities owned by this player from future finds +e2function void findExcludePlayerProps(entity ply) + if not validEntity(ply) then return end + self.data.find.bl_owner[ply] = true + invalidate_filters(self) +end + +--- Exclude entities owned by this player from future finds +e2function void findExcludePlayerProps(string name) + local ply = findPlayer(name) + if not ply then return end + e2_findExcludePlayerProps_e(self, { nil, { function() return ply end } }) +end + +--- Exclude entities with this model (or partial model name) from future finds +e2function void findExcludeModel(string model) + self.data.find.bl_model[string.lower(model)] = true + invalidate_filters(self) +end + +--- Exclude entities with this class (or partial class name) from future finds +e2function void findExcludeClass(string class) + self.data.find.bl_class[string.lower(class)] = true + invalidate_filters(self) +end + +--[[************************************************************************]]-- + +--- Remove all entities from from the blacklist +e2function void findAllowEntities(array arr) + local bl_entity = self.data.find.bl_entity + local validEntity = validEntity + + for _,ent in ipairs(arr) do + if not validEntity(ent) then return end + bl_entity[ent] = nil + end + invalidate_filters(self) +end + +--- Remove from the blacklist +e2function void findAllowEntity(entity ent) + if not validEntity(ent) then return end + self.data.find.bl_entity[ent] = nil + invalidate_filters(self) +end + +--- Remove this player from the entity blacklist +e2function void findAllowPlayer(entity ply) = e2function void findAllowEntity(entity ent) + +--- Remove this player from the entity blacklist +e2function void findAllowPlayer(string name) + local ply = findPlayer(name) + if not ply then return end + self.data.find.bl_entity[ply] = nil + invalidate_filters(self) +end + +--- Remove entities owned by this player from the blacklist +e2function void findAllowPlayerProps(entity ply) + if not validEntity(ply) then return end + self.data.find.bl_owner[ply] = nil + invalidate_filters(self) +end + +--- Remove entities owned by this player from the blacklist +e2function void findAllowPlayerProps(string name) + local ply = findPlayer(name) + if not ply then return end + e2_findAllowPlayerProps_e(self, { nil, { function() return ply end } }) +end + +--- Remove entities with this model (or partial model name) from the blacklist +e2function void findAllowModel(string model) + self.data.find.bl_model[string.lower(model)] = nil + invalidate_filters(self) +end + +--- Remove entities with this class (or partial class name) from the blacklist +e2function void findAllowClass(string class) + self.data.find.bl_class[string.lower(class)] = nil + invalidate_filters(self) +end + +--[[************************************************************************]]-- + +--- Include all entities from in future finds, and remove others not in the whitelist +e2function void findIncludeEntities(array arr) + local wl_entity = self.data.find.wl_entity + local validEntity = validEntity + + for _,ent in ipairs(arr) do + if not validEntity(ent) then return end + wl_entity[ent] = true + end + invalidate_filters(self) +end + +--- Include in future finds, and remove others not in the whitelist +e2function void findIncludeEntity(entity ent) + if not validEntity(ent) then return end + self.data.find.wl_entity[ent] = true + invalidate_filters(self) +end + +--- Include this player in future finds, and remove other entities not in the entity whitelist +e2function void findIncludePlayer(entity ply) = e2function void findIncludeEntity(entity ent) + +--- Include this player in future finds, and remove other entities not in the entity whitelist +e2function void findIncludePlayer(string name) + local ply = findPlayer(name) + if not ply then return end + self.data.find.wl_entity[ply] = true + invalidate_filters(self) +end + +--- Include entities owned by this player from future finds, and remove others not in the whitelist +e2function void findIncludePlayerProps(entity ply) + if not validEntity(ply) then return end + self.data.find.wl_owner[ply] = true + invalidate_filters(self) +end + +--- Include entities owned by this player from future finds, and remove others not in the whitelist +e2function void findIncludePlayerProps(string name) + local ply = findPlayer(name) + if not ply then return end + e2_findIncludePlayerProps_e(self, { nil, { function() return ply end } }) +end + +--- Include entities with this model (or partial model name) in future finds, and remove others not in the whitelist +e2function void findIncludeModel(string model) + self.data.find.wl_model[string.lower(model)] = true + invalidate_filters(self) +end + +--- Include entities with this class (or partial class name) in future finds, and remove others not in the whitelist +e2function void findIncludeClass(string class) + self.data.find.wl_class[string.lower(class)] = true + invalidate_filters(self) +end + +--[[************************************************************************]]-- + +--- Remove all entities from from the whitelist +e2function void findDisallowEntities(array arr) + local wl_entity = self.data.find.wl_entity + local validEntity = validEntity + + for _,ent in ipairs(arr) do + if not validEntity(ent) then return end + wl_entity[ent] = nil + end + invalidate_filters(self) +end + +--- Remove from the whitelist +e2function void findDisallowEntity(entity ent) + if not validEntity(ent) then return end + self.data.find.wl_entity[ent] = nil + invalidate_filters(self) +end + +--- Remove this player from the entity whitelist +e2function void findDisallowPlayer(entity ply) = e2function void findDisallowEntity(entity ent) + +--- Remove this player from the entity whitelist +e2function void findDisallowPlayer(string name) + local ply = findPlayer(name) + if not ply then return end + self.data.find.wl_entity[ply] = nil + invalidate_filters(self) +end + +--- Remove entities owned by this player from the whitelist +e2function void findDisallowPlayerProps(entity ply) + if not validEntity(ply) then return end + self.data.find.wl_owner[ply] = nil + invalidate_filters(self) +end + +--- Remove entities owned by this player from the whitelist +e2function void findDisallowPlayerProps(string name) + local ply = findPlayer(name) + if not ply then return end + e2_findDisallowPlayerProps_e(self, { nil, { function() return ply end } }) +end + +--- Remove entities with this model (or partial model name) from the whitelist +e2function void findDisallowModel(string model) + self.data.find.wl_model[string.lower(model)] = nil + invalidate_filters(self) +end + +--- Remove entities with this class (or partial class name) from the whitelist +e2function void findDisallowClass(string class) + self.data.find.wl_class[string.lower(class)] = nil + invalidate_filters(self) +end + +--[[************************************************************************]]-- + +--- Clear all entries from the entire blacklist +e2function void findClearBlackList() + local find = self.data.find + find.bl_entity = {} + find.bl_owner = {} + find.bl_model = {} + find.bl_class = {} + + invalidate_filters(self) +end + +--- Clear all entries from the entity blacklist +e2function void findClearBlackEntityList() + self.data.find.bl_entity = {} + invalidate_filters(self) +end + +--- Clear all entries from the prop owner blacklist +e2function void findClearBlackPlayerPropList() + self.data.find.bl_owner = {} + invalidate_filters(self) +end + +--- Clear all entries from the model blacklist +e2function void findClearBlackModelList() + self.data.find.bl_model = {} + invalidate_filters(self) +end + +--- Clear all entries from the class blacklist +e2function void findClearBlackClassList() + self.data.find.bl_class = {} + invalidate_filters(self) +end + +--- Clear all entries from the entire whitelist +e2function void findClearWhiteList() + local find = self.data.find + find.wl_entity = {} + find.wl_owner = {} + find.wl_model = {} + find.wl_class = {} + + invalidate_filters(self) +end + +--- Clear all entries from the player whitelist +e2function void findClearWhiteEntityList() + self.data.find.wl_entity = {} + invalidate_filters(self) +end + +--- Clear all entries from the prop owner whitelist +e2function void findClearWhitePlayerPropList() + self.data.find.wl_owner = {} + invalidate_filters(self) +end + +--- Clear all entries from the model whitelist +e2function void findClearWhiteModelList() + self.data.find.wl_model = {} + invalidate_filters(self) +end + +--- Clear all entries from the class whitelist +e2function void findClearWhiteClassList() + self.data.find.wl_class = {} + invalidate_filters(self) +end + +--[[************************************************************************]]-- + +--- Returns the indexed entity from the previous find event (valid parameters are 1 to the number of entities found) +e2function entity findResult(index) + return self.data.findlist[index] +end + +--- Returns the closest entity to the given point from the previous find event +e2function entity findClosest(vector position) + local closest = nil + local dist = math.huge + for _,ent in pairs(self.data.findlist) do + local pos = ent:GetPos() + local xd, yd, zd = pos.x-position[1], pos.y-position[2], pos.z-position[3] + local curdist = xd*xd + yd*yd + zd*zd + if curdist < dist then + closest = ent + dist = curdist + end + end + return closest +end + +--- Formats the query as an array, R:entity(Index) to get a entity, R:string to get a description including the name and entity id. +e2function array findToArray() + local tmp = {} + for k,v in ipairs(self.data.findlist) do + tmp[k] = v + end + return tmp +end + +--- Equivalent to findResult(1) +e2function entity find() + return self.data.findlist[1] +end + +--[[************************************************************************]]-- + +--- Sorts the entities from the last find event, index 1 is the closest to point V, returns the number of entities in the list +e2function number findSortByDistance(vector position) + position = Vector(position[1], position[2], position[3]) + local Distance = position.Distance + local validEntity = validEntity + local findlist = self.data.findlist + table.sort(findlist, function(a, b) + if not validEntity(a) then return false end -- !(invalid < b) <=> (b <= invalid) + if not validEntity(b) then return true end -- (valid < invalid) + + return Distance(position, a:GetPos()) < Distance(position, b:GetPos()) + end) + return #findlist +end + +--[[************************************************************************]]-- + +local function applyClip(self, filter) + local findlist = self.data.findlist + + filterList(findlist, filter) + + return #findlist +end + +--- Filters the list of entities by removing all entities that are NOT of this class +e2function number findClipToClass(string class) + class = string.lower(class) + return applyClip(self, function(ent) return string.match(string.lower(ent:GetClass()), class) end) +end + +--- Filters the list of entities by removing all entities that are of this class +e2function number findClipFromClass(string class) + return applyClip(self, function(ent) return not string.match(string.lower(ent:GetClass()), class) end) +end + +--- Filters the list of entities by removing all entities that do NOT have this model +e2function number findClipToModel(string model) + return applyClip(self, function(ent) return string.match(string.lower(ent:GetModel() or ""), model) end) +end + +--- Filters the list of entities by removing all entities that do have this model +e2function number findClipFromModel(string model) + return applyClip(self, function(ent) return not string.match(string.lower(ent:GetModel() or ""), model) end) +end + +--- Filters the list of entities by removing all entities that do NOT have this name +e2function number findClipToName(string name) + return applyClip(self, function(ent) return string.match(string.lower(ent:GetName()), name) end) +end + +--- Filters the list of entities by removing all entities that do have this name +e2function number findClipFromName(string name) + return applyClip(self, function(ent) return not string.match(string.lower(ent:GetName()), name) end) +end + +--- Filters the list of entities by removing all entities NOT within the specified sphere (center, radius) +e2function number findClipToSphere(vector center, radius) + center = Vector(center[1], center[2], center[3]) + return applyClip(self, function(ent) + return center:Distance(ent:GetPos()) <= radius + end) +end + +--- Filters the list of entities by removing all entities within the specified sphere (center, radius) +e2function number findClipFromSphere(vector center, radius) + center = Vector(center[1], center[2], center[3]) + return applyClip(self, function(ent) + return center:Distance(ent:GetPos()) > radius + end) +end + +--- Filters the list of entities by removing all entities NOT on the positive side of the defined plane. (Plane origin, vector perpendicular to the plane) You can define any convex hull using this. +e2function number findClipToRegion(vector origin, vector perpendicular) + origin = Vector(origin[1], origin[2], origin[3]) + perpendicular = Vector(perpendicular[1], perpendicular[2], perpendicular[3]) + + local perpdot = perpendicular:Dot(origin) + + return applyClip(self, function(ent) + return perpdot < perpendicular:Dot(ent:GetPos()) + end) +end diff --git a/lua/entities/gmod_wire_expression2/core/gametick.lua b/lua/entities/gmod_wire_expression2/core/gametick.lua new file mode 100644 index 0000000000..922f4c12c6 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/gametick.lua @@ -0,0 +1,45 @@ +/******************************************************************************\ + Game tick callback support +\******************************************************************************/ + +local registered_chips = {} +local tickrun = 0 + +registerCallback("destruct",function(self) + registered_chips[self.entity] = nil +end) + +__e2setcost(1) + +--- If != 0 the expression will execute once every game tick +e2function void runOnTick(activate) + if activate ~= 0 then + registered_chips[self.entity] = true + else + registered_chips[self.entity] = nil + end +end + +--- Returns 1 if the current execution was caused by "runOnTick" +e2function number tickClk() + return tickrun +end + +local function Expression2TickClock() + local ents = {} + + -- this additional step is needed because we cant modify registered_chips while it is being iterated. + for entity,_ in pairs(registered_chips) do + if entity:IsValid() then table.insert(ents, entity) end + end + + tickrun = 1 + for _,entity in ipairs(ents) do + entity:Execute() + end + tickrun = 0 +end +hook.Add("Think", "Expression2TickClock", Expression2TickClock) +timer.Create("Expression2TickClock", 5, 0, hook.Add, "Think", "Expression2TickClock", Expression2TickClock) + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/globalvars.lua b/lua/entities/gmod_wire_expression2/core/globalvars.lua new file mode 100644 index 0000000000..f94ad571d0 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/globalvars.lua @@ -0,0 +1,556 @@ +/******************************************************************************\ + Global variable support v1.5 +\******************************************************************************/ + +//--------------------------// +//-- Helper Functions --// +//--------------------------// + +local function glTid(self) + local group = self.data['globavars'] + local uid = "exp2globalshare" + if self.data['globashare']==0 then uid = self.data['globaply'] end + if !_G[uid][group] then + _G[uid][group] = {} + local T = _G[uid][group] + T["s"] = {} + T["n"] = {} + T["v"] = {} + T["a"] = {} + T["e"] = {} + return T + end + return _G[uid][group] +end + +//--------------------------// +//-- Strings --// +//--------------------------// + +__e2setcost(5) -- temporary + +registerFunction("gSetStr", "s", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + T["xs"] = rv1 +end) + +registerFunction("gGetStr", "", "s", function(self, args) + local T = glTid(self) + if T["xs"]==nil then return "" end + return T["xs"] +end) + +registerFunction("gDeleteStr", "", "s", function(self, args) + local T = glTid(self) + if T["xs"]==nil then return "" end + local value = T["xs"] + T["xs"] = nil + return value +end) + +registerFunction("gSetStr", "ss", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local T = glTid(self) + T["s"][rv1] = rv2 +end) + +registerFunction("gGetStr", "s", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if T["s"][rv1]==nil then return "" end + return T["s"][rv1] +end) + +registerFunction("gDeleteStr", "s", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if T["s"][rv1]==nil then return "" end + local value = T["s"][rv1] + T["s"][rv1] = nil + return value +end) + +registerFunction("gSetStr", "ns", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + T["s"][rv1] = rv2 +end) + +registerFunction("gGetStr", "n", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if T["s"][rv1]==nil then return "" end + return T["s"][rv1] +end) + +registerFunction("gDeleteStr", "n", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if T["s"][rv1]==nil then return "" end + local value = T["s"][rv1] + T["s"][rv1] = nil + return value +end) + +//--------------------------// +//-- Numbers --// +//--------------------------// + +registerFunction("gSetNum", "n", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + T["xn"] = rv1 +end) + +registerFunction("gGetNum", "", "n", function(self, args) + local T = glTid(self) + if T["xn"]==nil then return 0 end + return T["xn"] +end) + +registerFunction("gDeleteNum", "", "n", function(self, args) + local T = glTid(self) + if T["xn"]==nil then return 0 end + local value = T["xn"] + T["xn"] = nil + return value +end) + +registerFunction("gSetNum", "sn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local T = glTid(self) + T["n"][rv1] = rv2 +end) + +registerFunction("gGetNum", "s", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if T["n"][rv1]==nil then return 0 end + return T["n"][rv1] +end) + +registerFunction("gDeleteNum", "s", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if T["n"][rv1]==nil then return 0 end + local value = T["n"][rv1] + T["n"][rv1] = nil + return value +end) + +registerFunction("gSetNum", "nn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + T["n"][rv1] = rv2 +end) + +registerFunction("gGetNum", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if T["n"][rv1]==nil then return 0 end + return T["n"][rv1] +end) + +registerFunction("gDeleteNum", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if T["n"][rv1]==nil then return 0 end + local value = T["n"][rv1] + T["n"][rv1] = nil + return value +end) + +//--------------------------// +//-- Vectors --// +//--------------------------// + +registerFunction("gSetVec", "v", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if rv1[1] == 0 and rv1[2] == 0 and rv1[3] == 0 then rv1 = nil end + T["xv"] = rv1 +end) + +registerFunction("gGetVec", "", "v", function(self, args) + local T = glTid(self) + return T["xv"] or { 0, 0, 0 } +end) + +registerFunction("gDeleteVec", "", "v", function(self, args) + local T = glTid(self) + if T["xv"]==nil then return { 0, 0, 0 } end + local value = T["xv"] + T["xv"] = nil + return value +end) + +registerFunction("gSetVec", "sv", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local T = glTid(self) + if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + T["v"][rv1] = rv2 +end) + +registerFunction("gGetVec", "s", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + return T["v"][rv1] or { 0, 0, 0 } +end) + +registerFunction("gDeleteVec", "s", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if T["v"][rv1]==nil then return { 0, 0, 0 } end + local value = T["v"][rv1] + T["v"][rv1] = nil + return value +end) + +registerFunction("gSetVec", "nv", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + T["v"][rv1] = rv2 +end) + +registerFunction("gGetVec", "n", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + return T["v"][rv1] or { 0, 0, 0 } +end) + +registerFunction("gDeleteVec", "n", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if T["v"][rv1]==nil then return { 0, 0, 0 } end + local value = T["v"][rv1] + T["v"][rv1] = nil + return value +end) + +//--------------------------// +//-- Angles --// +//--------------------------// + +registerFunction("gSetAng", "a", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if rv1[1] == 0 and rv1[2] == 0 and rv1[3] == 0 then rv1 = nil end + T["xa"] = rv1 +end) + +registerFunction("gGetAng", "", "a", function(self, args) + local T = glTid(self) + local ret = T["xa"] + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("gDeleteAng", "", "a", function(self, args) + local T = glTid(self) + if T["xa"]==nil then return { 0, 0, 0 } end + local value = T["xa"] + T["xa"] = nil + return value +end) + +registerFunction("gSetAng", "sa", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local T = glTid(self) + if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + T["a"][rv1] = rv2 +end) + +registerFunction("gGetAng", "s", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + local ret = T["a"][rv1] + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("gDeleteAng", "s", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + if T["a"][rv1]==nil then return { 0, 0, 0 } end + local value = T["a"][rv1] + T["a"][rv1] = nil + return value +end) + +registerFunction("gSetAng", "na", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if rv2[1] == 0 and rv2[2] == 0 and rv2[3] == 0 then rv2 = nil end + T["a"][rv1] = rv2 +end) + +registerFunction("gGetAng", "n", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + local ret = T["a"][rv1] + if type(ret) == "table" and table.getn(ret) == 3 then return ret end + return { 0, 0, 0 } +end) + +registerFunction("gDeleteAng", "n", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + if T["a"][rv1]==nil then return { 0, 0, 0 } end + local value = T["a"][rv1] + T["a"][rv1] = nil + return value +end) + +//--------------------------// +//-- Entity --// +//--------------------------// + +registerFunction("gSetEnt", "e", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + T["xe"] = rv1 +end) + +registerFunction("gGetEnt", "", "e", function(self, args) + local T = glTid(self) + local ret = T["xe"] + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("gDeleteEnt", "", "e", function(self, args) + local T = glTid(self) + local ret = T["xe"] + T["xe"] = nil + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("gSetEnt", "se", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local T = glTid(self) + T["e"][rv1] = rv2 +end) + +registerFunction("gGetEnt", "s", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + local ret = T["e"][rv1] + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("gDeleteEnt", "s", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local T = glTid(self) + local ret = T["e"][rv1] + T["e"][rv1] = nil + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("gSetEnt", "ne", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + T["e"][rv1] = rv2 +end) + +registerFunction("gGetEnt", "n", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + local ret = T["e"][rv1] + if validEntity(ret) then return ret end + return nil +end) + +registerFunction("gDeleteEnt", "n", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + local T = glTid(self) + local ret = T["e"][rv1] + T["e"][rv1] = nil + if validEntity(ret) then return ret end + return nil +end) + +//--------------------------// +//-- Clean Up --// +//--------------------------// + +registerFunction("gDeleteAll", "", "", function(self, args) + local group = self.data['globavars'] + local uid = "exp2globalshare" + if self.data['globashare']==0 then uid = self.data['globaply'] end + if _G[uid][group] then + _G[uid][group] = nil + end +end) + +registerFunction("gDeleteAllStr", "", "", function(self, args) + local T = glTid(self) + T["s"] = {} + T["xs"] = nil +end) + +registerFunction("gDeleteAllNum", "", "", function(self, args) + local T = glTid(self) + T["n"] = {} + T["xn"] = nil +end) + +registerFunction("gDeleteAllVec", "", "", function(self, args) + local T = glTid(self) + T["v"] = {} + T["xv"] = nil +end) + +registerFunction("gDeleteAllAng", "", "", function(self, args) + local T = glTid(self) + T["a"] = {} + T["xa"] = nil +end) + +registerFunction("gDeleteAllEnt", "", "", function(self, args) + local T = glTid(self) + T["e"] = {} + T["xe"] = nil +end) + + + +//--------------------------// +//-- Sharing --// +//--------------------------// + +registerFunction("gShare", "n", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1==0 then self.data['globashare'] = 0 + else self.data['globashare'] = 1 end +end) + +//--------------------------// +//-- Group Commands --// +//--------------------------// + +registerFunction("gSetGroup", "s", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local uid = "exp2globalshare" + if self.data['globashare']==0 then uid = self.data['globaply'] end + self.data['globavars'] = rv1 + local group = self.data['globavars'] + if _G[uid][group]==nil then _G[uid][group] = {} end + local T = _G[uid][group] + if !T["s"] then T["s"] = {} end + if !T["n"] then T["n"] = {} end + if !T["v"] then T["v"] = {} end + if !T["a"] then T["a"] = {} end + if !T["e"] then T["e"] = {} end +end) + +registerFunction("gGetGroup", "", "s", function(self, args) + return self.data['globavars'] +end) + +registerFunction("gResetGroup", "", "", function(self, args) + local uid = "exp2globalshare" + if self.data['globashare']==0 then uid = self.data['globaply'] end + self.data['globavars'] = "default" + local group = self.data['globavars'] + if !_G[uid][group] then _G[uid][group] = {} end + local T = _G[uid][group] + if !T["s"] then T["s"] = {} end + if !T["n"] then T["n"] = {} end + if !T["v"] then T["v"] = {} end + if !T["a"] then T["a"] = {} end + if !T["e"] then T["e"] = {} end +end) + +/******************************************************************************/ + +registerCallback("construct", function(self) + if self.data['globavars'] != "default" then + self.data['globavars'] = "default" + end + self.data['globaply'] = self.player:UniqueID() + self.data['globashare'] = 0 +end) + +registerCallback("postexecute", function(self) + if self.data['globavars'] != "default" then + self.data['globavars'] = "default" + end +end) + +//--------------------------// +//-- Server Hooks --// +//--------------------------// + +_G["exp2globalshare"] = {} + +hook.Add( "EntityRemoved", "e2_globalvars_playerdisconnect", function( ply ) + if not ply:IsValid() then return end + if not ply:IsPlayer() then return end + local T = _G[ply:UniqueID()] + for i=0, table.Count(T) do + table.remove(T,table.Count(T)-i) + end +end) + +hook.Add( "PlayerInitialSpawn", "e2_globalvars_playerconnect", function( ply ) + _G[ply:UniqueID()] = {} +end) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/glon.lua b/lua/entities/gmod_wire_expression2/core/glon.lua new file mode 100644 index 0000000000..643e78fe14 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/glon.lua @@ -0,0 +1,50 @@ +if not glon then require("glon") end + +--- Encodes into a string, using [[GLON]]. +e2function string glonEncode(array data) + local ok, ret = pcall(glon.encode, data) + if not ok then + ErrorNoHalt("glon.encode error: "..ret) + return "" + end + return ret or "" +end + +--- Encodes into a string, using [[GLON]]. +e2function string glonEncode(table data) = e2function string glonEncode(array data) + + +--- Decodes into an array, using [[GLON]]. +e2function array glonDecode(string data) + local ok, ret = pcall(glon.decode, data) + if not ok then + ErrorNoHalt("glon.decode error: "..ret) + return {} + end + + return ret or {} +end + +--- Decodes into a table, using [[GLON]]. +e2function table glonDecodeTable(string data) + local ok, ret = pcall(glon.decode, data) + if not ok then + ErrorNoHalt("glon.decode error: "..ret) + return {} + end + + return ret or {} +end + +hook.Add("InitPostEntity", "wire_expression2_glonfix", function() + -- Fixing other people's bugs... + for i = 1,20 do + local name, encode_types = debug.getupvalue(glon.Write, i) + if name == "encode_types" then + for _,tp in ipairs({"NPC","Vehicle","Weapon"}) do + if not encode_types[tp] then encode_types[tp] = encode_types.Entity end + end + break + end + end +end) diff --git a/lua/entities/gmod_wire_expression2/core/hologram.lua b/lua/entities/gmod_wire_expression2/core/hologram.lua new file mode 100644 index 0000000000..08aec942a3 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/hologram.lua @@ -0,0 +1,729 @@ +if not datastream then require( "datastream" ) end + +CreateConVar("wire_holograms_max","50") +local wire_holograms_size_max = CreateConVar("wire_holograms_size_max","50") + +-- context = chip.context = self +-- Holo = { ent = prop, scale = scale, e2owner = context } +-- E2HoloRepo[player][-index] = Holo <-- global holos +-- E2HoloRepo[player][Holo] = Holo <-- local holos +-- context.data.holos[index] = Holo <-- local holos +local E2HoloRepo = {} +local PlayerAmount = {} +local BlockList = {} +local ModelList = { + ["cone"] = true, + ["cube"] = true, + ["dome"] = true, + ["dome2"] = true, + ["cylinder"] = true, + ["hqcone"] = true, + ["hqcylinder"] = true, + ["hqcylinder2"] = true, + ["hqicosphere"] = true, + ["hqicosphere2"] = true, + ["hqsphere"] = true, + ["hqsphere2"] = true, + ["hqtorus"] = true, + ["hqtorus2"] = true, + ["icosphere"] = true, + ["icosphere2"] = true, + ["icosphere3"] = true, + ["prism"] = true, + ["pyramid"] = true, + ["plane"] = true, + ["sphere"] = true, + ["sphere2"] = true, + ["sphere3"] = true, + ["tetra"] = true, + ["torus"] = true, + ["torus2"] = true, + ["torus3"] = true +} + +if not wire_expression2_is_reload and not SinglePlayer() then + print("Ignore the following bunch of error messages:") + for k,_ in pairs(ModelList) do + util.PrecacheModel( "models/Holograms/"..k..".mdl" ) + resource.AddFile( "models/Holograms/"..k..".mdl" ) + end + print("Ignore the preceding bunch of error messages.") +end + +/******************************************************************************/ + +concommand.Add( "wire_holograms_remove_all", function( ply, com, args ) + if not ply:IsAdmin() then return end + + for pl,rep in pairs( E2HoloRepo ) do + for k,Holo in pairs( rep ) do + if Holo and validEntity(Holo.ent) then + Holo.ent:Remove() + PlayerAmount[pl] = PlayerAmount[pl] - 1 + end + end + end + +end ) + +concommand.Add( "wire_holograms_block", function( ply, com, args ) + if not ply:IsAdmin() then return end + + if not args[1] then + ply:PrintMessage( HUD_PRINTCONSOLE, "Command requires a player's name (or part of their name)" ) + ply:PrintMessage( HUD_PRINTCONSOLE, "Usage: wire_holograms_block [name]" ) + return + end + + local name = args[1]:lower() + local players = E2Lib.filterList(player.GetAll(), function(ent) return ent:GetName():lower():match(name) end) + + if #players == 1 then + local v = players[1] + if BlockList[v:SteamID()] == true then + ply:PrintMessage( HUD_PRINTCONSOLE, v:GetName() .. " is already in the holograms blocklist!" ) + else + if E2HoloRepo[v] then + for k2,v2 in pairs( E2HoloRepo[v] ) do + if v2 and validEntity(v2.ent) then + v2.ent:Remove() + PlayerAmount[v] = PlayerAmount[v] - 1 + end + end + end + BlockList[v:SteamID()] = true + for _,player in ipairs( player.GetAll() ) do + player:PrintMessage( HUD_PRINTTALK, "(ADMIN) " .. v:GetName() .. " added to holograms blocklist" ) + end + end + elseif #players > 1 then + ply:PrintMessage( HUD_PRINTCONSOLE, "More than one player matches that name!" ) + else + ply:PrintMessage( HUD_PRINTCONSOLE, "No player names found with " .. args[1] ) + end +end ) + +concommand.Add( "wire_holograms_unblock", function( ply, com, args ) + if not ply:IsAdmin() then return end + + if not args[1] then + ply:PrintMessage( HUD_PRINTCONSOLE, "Command requires a player's name (or part of their name)" ) + ply:PrintMessage( HUD_PRINTCONSOLE, "Usage: wire_holograms_unblock [name]" ) + return + end + + local name = args[1]:lower() + local players = E2Lib.filterList(player.GetAll(), function(ent) return ent:GetName():lower():match(name) end) + + if #players == 1 then + local v = players[1] + if BlockList[v:SteamID()] == true then + BlockList[v:SteamID()] = nil + for _,player in ipairs( player.GetAll() ) do + player:PrintMessage( HUD_PRINTTALK, "(ADMIN) " .. v:GetName() .. " removed from holograms blocklist" ) + end + else + ply:PrintMessage( HUD_PRINTCONSOLE, v:GetName() .. " is not in the holograms blocklist!" ) + end + elseif #players > 1 then + ply:PrintMessage( HUD_PRINTCONSOLE, "More than one player matches that name!" ) + else + ply:PrintMessage( HUD_PRINTCONSOLE, "No player names found with " .. args[1] ) + end +end ) + +concommand.Add( "wire_holograms_block_id", function( ply, com, args ) + if not ply:IsAdmin() then return end + + local steamID = table.concat(args) + + if not steamID:match("STEAM_[0-9]:[0-9]:[0-9]+") then + ply:PrintMessage( HUD_PRINTCONSOLE, "Invalid SteamID format" ) + ply:PrintMessage( HUD_PRINTCONSOLE, "Usage: wire_holograms_block_id STEAM_X:X:XXXXXX" ) + return + end + + if BlockList[steamID] == true then + ply:PrintMessage( HUD_PRINTCONSOLE, steamID .. " is already in the holograms blocklist!" ) + else + BlockList[steamID] = true + for _,player in ipairs( player.GetAll() ) do + player:PrintMessage( HUD_PRINTTALK, "(ADMIN) " .. steamID .. " added to holograms blocklist" ) + end + for _,v in pairs( player.GetAll() ) do + if v:SteamID() == steamID and E2HoloRepo[v] then + for k2,v2 in pairs( E2HoloRepo[v] ) do + if v2 and validEntity(v2.ent) then + v2.ent:Remove() + PlayerAmount[v] = PlayerAmount[v] - 1 + end + end + return + end + end + end +end ) + +concommand.Add( "wire_holograms_unblock_id", function( ply, com, args ) + if not ply:IsAdmin() then return end + + local steamID = table.concat(args) + + if not steamID:match("STEAM_[0-9]:[0-9]:[0-9]+") then + ply:PrintMessage( HUD_PRINTCONSOLE, "Invalid SteamID format" ) + ply:PrintMessage( HUD_PRINTCONSOLE, "Usage: wire_holograms_unblock_id STEAM_X:X:XXXXXX" ) + return + end + + if BlockList[steamID] == true then + BlockList[steamID] = nil + for _,player in ipairs( player.GetAll() ) do + player:PrintMessage( HUD_PRINTTALK, "(ADMIN) " .. steamID .. " removed from holograms blocklist" ) + end + else + ply:PrintMessage( HUD_PRINTCONSOLE, steamID .. " is not in the holograms blocklist!" ) + end +end ) + +/******************************************************************************/ + +local scale_queue = {} + +-- If no recipient is given, the umsg is sent to everyone (umsg.Start does that) +local function flush_scale_queue(queue, recipient) + if not queue then queue = scale_queue end + if #queue == 0 then return end + + local bytes = 4 -- Header(2)+Short(2) + umsg.Start("wire_holograms_set_scale", recipient) + for _,Holo,scale in ipairs_map(queue, unpack) do + bytes = bytes + 14 -- Vector(12)+Short(2) + if bytes > 255 then + umsg.Short(0) -- terminate list + umsg.End() -- end message + umsg.Start("wire_holograms_set_scale", recipient) -- make a new one + bytes = 4+14 -- Header(2)+Short(2)+Vector(12)+Short(2) + end + umsg.Short(Holo.ent:EntIndex()) + umsg.Vector(scale) + end + umsg.Short(0) + umsg.End() +end + +registerCallback("postexecute", function(self) + flush_scale_queue() + scale_queue = {} +end) + +local function rescale(Holo, scale) + local maxval = wire_holograms_size_max:GetInt() + local minval = -maxval + + x = math.Clamp( scale[1], minval, maxval ) + y = math.Clamp( scale[2], minval, maxval ) + z = math.Clamp( scale[3], minval, maxval ) + + local scale = Vector(x, y, z) + if Holo.scale ~= scale then + table.insert(scale_queue, { Holo, scale }) + Holo.scale = scale + end +end + +hook.Add( "PlayerInitialSpawn", "wire_holograms_set_scales", function(ply) + local queue = {} + + for pl,rep in pairs( E2HoloRepo ) do + for k,Holo in pairs( rep ) do + if Holo and validEntity(Holo.ent) then + table.insert(queue, { Holo, Holo.scale }) + end + end + end + + flush_scale_queue(queue, ply) +end) + +/******************************************************************************/ + +local function MakeHolo(Player, Pos, Ang, model) + local prop = ents.Create( "gmod_wire_hologram" ) + prop:SetPos(Pos) + prop:SetAngles(Ang) + prop:SetModel(model) + prop:SetPlayer(Player) + prop:SetNetworkedInt("ownerid", Player:UserID()) + return prop +end + +-- Returns the hologram with the given index or nil if it doesn't exist. +local function CheckIndex(self, index) + index = index - index % 1 + local Holo + if index<0 then + Holo = E2HoloRepo[self.player][-index] + else + Holo = self.data.holos[index] + end + if not Holo or not validEntity(Holo.ent) then return nil end + return Holo +end + +-- Sets the given index to the given hologram. +local function SetIndex(self, index, Holo) + index = index - index % 1 + local rep = E2HoloRepo[self.player] + if index<0 then + rep[-index] = Holo + else + local holos = self.data.holos + if holos[index] then rep[holos[index]] = nil end + holos[index] = Holo + if Holo then rep[Holo] = Holo end + end +end + +--[[ +local function MakePropNoEffect(...) + local backup = DoPropSpawnedEffect + DoPropSpawnedEffect = function() end + local ret = MakeProp(...) + DoPropSpawnedEffect = backup + return ret +end +]] + +local function createcustompropfromE2(self, index, pos, scale, ang, color, model) + if not pos then pos = self.entity:GetPos() end + if not scale then scale = Vector(1,1,1) end + if not ang then ang = self.entity:GetAngles() end + + local Holo = CheckIndex(self, index) + if not Holo then + Holo = {} + SetIndex(self, index, Holo) + end + + model = "models/Holograms/"..(model or "cube")..".mdl" + + local prop + + if validEntity(Holo.ent) then + prop = Holo.ent + prop:SetPos( pos ) + prop:SetAngles( ang ) + prop:SetModel( model ) + else + prop = MakeHolo(self.player, pos, ang, model, {}, {}) + prop:Activate() + prop:Spawn() + prop:SetSolid(SOLID_NONE) + prop:SetMoveType(MOVETYPE_NONE) + PlayerAmount[self.player] = PlayerAmount[self.player]+1 + Holo.ent = prop + Holo.e2owner = self + end + + if not validEntity(prop) then return nil end + if color then prop:SetColor(color[1],color[2],color[3],255) end + + rescale(Holo, scale) + + return prop +end + +/******************************************************************************/ + +local function CheckSpawnTimer( self ) + local holo = self.data.holo + if CurTime() >= holo.nextSpawn then + holo.nextSpawn = CurTime()+1 + if CurTime() >= holo.nextBurst then + holo.remainingSpawns = 30 + elseif holo.remainingSpawns < 10 then + holo.remainingSpawns = 10 + end + end + + holo.nextBurst = CurTime()+10 + + if holo.remainingSpawns > 0 then + holo.remainingSpawns = holo.remainingSpawns - 1 + return true + else + return false + end +end + +-- Removes the hologram with the given index from the given chip. +local function removeholo(self, index) + local Holo = CheckIndex(self, index) + if not Holo then return end + if validEntity(Holo.ent) then + Holo.ent:Remove() + end + PlayerAmount[self.player] = PlayerAmount[self.player] - 1 + SetIndex(self, index, nil) +end + +-- Removes all holograms from the given chip. +local function clearholos(self) + -- delete local holos + for index,Holo in pairs(self.data.holos) do + removeholo(self, index) + end + + -- delete global holos owned by this chip + local rep = E2HoloRepo[self.player] + if not rep then return end + for index,Holo in ipairs(rep) do + if Holo.e2owner == self then + removeholo(self, -index) + end + end +end + +/******************************************************************************/ + +__e2setcost(20) -- temporary + +e2function entity holoCreate(index, vector position, vector scale, angle ang, vector color, string model) + if BlockList[self.player:SteamID()] == true or CheckSpawnTimer( self ) == false then return end + local Holo = CheckIndex(self, index) + if not Holo and PlayerAmount[self.player] >= GetConVar("wire_holograms_max"):GetInt() then return end + + position = Vector(position[1], position[2], position[3]) + ang = Angle(ang[1], ang[2], ang[3]) + local ret = createcustompropfromE2(self, index, position, scale, ang, color, model) + if validEntity(ret) then return ret end +end + +e2function entity holoCreate(index, vector position, vector scale, angle ang, vector color) + if BlockList[self.player:SteamID()] == true or CheckSpawnTimer( self ) == false then return end + local Holo = CheckIndex(self, index) + if not Holo and PlayerAmount[self.player] >= GetConVar("wire_holograms_max"):GetInt() then return end + + position = Vector(position[1], position[2], position[3]) + ang = Angle(ang[1], ang[2], ang[3]) + local ret = createcustompropfromE2(self, index, position, scale, ang, color) + if validEntity(ret) then return ret end +end + +e2function entity holoCreate(index, vector position, vector scale, angle ang) + if BlockList[self.player:SteamID()] == true or CheckSpawnTimer( self ) == false then return end + local Holo = CheckIndex(self, index) + if not Holo and PlayerAmount[self.player] >= GetConVar("wire_holograms_max"):GetInt() then return end + + position = Vector(position[1], position[2], position[3]) + ang = Angle(ang[1], ang[2], ang[3]) + local ret = createcustompropfromE2(self, index, position, scale, ang) + if validEntity(ret) then return ret end +end + +e2function entity holoCreate(index, vector position, vector scale) + if BlockList[self.player:SteamID()] == true or CheckSpawnTimer( self ) == false then return end + local Holo = CheckIndex(self, index) + if not Holo and PlayerAmount[self.player] >= GetConVar("wire_holograms_max"):GetInt() then return end + + position = Vector(position[1],position[2],position[3]) + local ret = createcustompropfromE2(self, index, position, scale) + if validEntity(ret) then return ret end +end + +e2function entity holoCreate(index, vector position) + if BlockList[self.player:SteamID()] == true or CheckSpawnTimer( self ) == false then return end + local Holo = CheckIndex(self, index) + if not Holo and PlayerAmount[self.player] >= GetConVar("wire_holograms_max"):GetInt() then return end + + position = Vector(position[1],position[2],position[3]) + local ret = createcustompropfromE2(self, index, position) + if validEntity(ret) then return ret end +end + +e2function entity holoCreate(index) + if BlockList[self.player:SteamID()] == true or CheckSpawnTimer( self ) == false then return end + local Holo = CheckIndex(self, index) + if not Holo and PlayerAmount[self.player] >= GetConVar("wire_holograms_max"):GetInt() then return end + + local ret = createcustompropfromE2(self, index) + if validEntity(ret) then return ret end +end + +e2function void holoDelete(index) + removeholo(self, index) +end + +e2function void holoDeleteAll() + clearholos(self) +end + +e2function void holoReset(index, string model, vector scale, vector color, string color) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetModel(Model("models/Holograms/"..model..".mdl")) + Holo.ent:SetColor(color[1],color[2],color[3],255) + Holo.ent:SetMaterial(color) + + rescale(Holo, scale) +end + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +e2function void holoScale(index, vector scale) + local Holo = CheckIndex(self, index) + if not Holo then return end + + rescale(Holo, scale) +end + +e2function void holoScaleUnits(index, vector size) + local Holo = CheckIndex(self, index) + if not Holo then return end + + local propsize = Holo.ent:OBBMaxs() - Holo.ent:OBBMins() + x = size[1] / propsize.x + y = size[2] / propsize.y + z = size[3] / propsize.z + + rescale(Holo, Vector(x, y, z)) +end + +e2function void holoPos(index, vector position) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetPos(Vector(position[1],position[2],position[3])) +end + +e2function void holoAng(index, angle ang) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetAngles(Angle(ang[1],ang[2],ang[3])) +end + +/******************************************************************************/ + +e2function void holoColor(index, vector color) + local Holo = CheckIndex(self, index) + if not Holo then return end + + local _,_,_,alpha = Holo.ent:GetColor() + Holo.ent:SetColor(color[1],color[2],color[3],alpha) +end + +e2function void holoColor(index, vector4 color) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetColor(color[1],color[2],color[3],color[4]) +end + +e2function void holoColor(index, vector color, alpha) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetColor(color[1],color[2],color[3],alpha) +end + +e2function void holoAlpha(index, alpha) + local Holo = CheckIndex(self, index) + if not Holo then return end + + local r,g,b = Holo.ent:GetColor() + Holo.ent:SetColor(r,g,b,alpha) +end + +e2function void holoShadow(index, has_shadow) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:DrawShadow( has_shadow ~= 0 ) +end + +/******************************************************************************/ + +e2function void holoModel(index, string model) + if !ModelList[model] then return end + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetModel(Model("models/Holograms/"..model..".mdl")) +end + +e2function void holoModel(index, string model, skin) + if !ModelList[model] then return end + local Holo = CheckIndex(self, index) + if not Holo then return end + + skin = skin - skin % 1 + Holo.ent:SetModel(Model("models/Holograms/"..model..".mdl")) + Holo.ent:SetSkin(skin) +end + +e2function void holoSkin(index, skin) + local Holo = CheckIndex(self, index) + if not Holo then return end + + skin = skin - skin % 1 + Holo.ent:SetSkin(skin) +end + +e2function void holoMaterial(index, string material) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetMaterial(material) +end + +e2function void holoRenderFX(index, effect) + local Holo = CheckIndex(self, index) + if not Holo then return end + + effect = effect - effect % 1 + Holo.ent:SetKeyValue("renderfx",effect) +end + +/******************************************************************************/ + +e2function void holoParent(index, otherindex) + local Holo = CheckIndex(self, index) + if not Holo then return end + + local Holo2 = CheckIndex(self, otherindex) + if not Holo2 then return end + + Holo.ent:SetParent( Holo2.ent ) +end + +e2function void holoParent(index, entity ent) + if not validEntity(ent) then return end + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetParent(ent) + Holo.ent:SetParentPhysNum(0) +end + +e2function void holoParent(index, bone b) + local ent, boneindex = E2Lib.isValidBone2(b) + if not ent then return end + + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetParent(ent) + Holo.ent:SetParentPhysNum(boneindex) +end + +e2function void holoParentAttachment(index, entity ent, string attachmentName) + if not validEntity(ent) then return end + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetParent(ent) + Holo.ent:Fire("SetParentAttachmentMaintainOffset", attachmentName, 0.01) +end + +e2function void holoUnparent(index) + local Holo = CheckIndex(self, index) + if not Holo then return end + + Holo.ent:SetParent(nil) + Holo.ent:SetParentPhysNum(0) +end + +/******************************************************************************/ + +e2function entity holoEntity(index) + local Holo = CheckIndex(self, index) + if Holo and validEntity(Holo.ent) then return Holo.ent end +end + +__e2setcost(30) +--- Gets the hologram index of the given entity, if any. Returns 0 on failure. +e2function number holoIndex(entity ent) + if not validEntity(ent) then return 0 end + if ent:GetClass() ~= "gmod_wire_hologram" then return 0 end + + -- check local holos + for k,Holo in pairs(self.data.holos) do + if(ent == Holo.ent) then return k end + end + + -- check global holos + for k,Holo in pairs(E2HoloRepo[self.player]) do + if type(k) == number and ent == Holo.ent then return -k end + end + return 0 +end +__e2setcost(5) + +/******************************************************************************/ + +registerCallback("construct", function(self) + if not E2HoloRepo[self.player] then + E2HoloRepo[self.player] = {} + PlayerAmount[self.player] = 0 + end + --self.data.HoloEffect = false + self.data.holos = {} + self.data.holo = { + nextSpawn = CurTime()+1, + nextBurst = CurTime()+10, + remainingSpawns = 30 + } +end) + +registerCallback("destruct", function(self) + if not self or not validEntity(self.entity) then return end -- TODO: evaluate necessity + + clearholos(self) +end) + +/******************************************************************************/ + +local DisplayOwners = {} +concommand.Add( "wire_holograms_display_owners", function( ply, com, args ) + + if !DisplayOwners[ply] then + DisplayOwners[ply] = {} + DisplayOwners[ply].bool = false + end + + if DisplayOwners[ply].bool == false then + timer.Create( "wire_holograms_update_owners"..ply:EntIndex(), 0.5, 0, function(ply) + local tbl = {} + for _,owner in ipairs( player.GetAll() ) do + if owner and owner:IsValid() and owner:IsPlayer() and E2HoloRepo[owner] then + for _,Holo in pairs( E2HoloRepo[owner] ) do + if Holo and type( Holo ) == "table" and Holo.ent and Holo.ent:IsValid() then + if !DisplayOwners[ply][Holo.ent] then + DisplayOwners[ply][Holo.ent] = true + table.insert( tbl, { owner = owner, hologram = Holo.ent } ) + end + end + end + end + end + if #tbl > 0 then + datastream.StreamToClients( ply, "wire_holograms_owners", { tbl } ) + end + end, ply ) + DisplayOwners[ply].bool = true + else + if timer.IsTimer( "wire_holograms_update_owners"..ply:EntIndex() ) then -- TODO: is this check necessary? + timer.Remove( "wire_holograms_update_owners"..ply:EntIndex() ) + end + ply:SendLua( "wire_holograms_remove_owners_display()") + DisplayOwners[ply] = {} + DisplayOwners[ply].bool = false + end + +end ) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/init.lua b/lua/entities/gmod_wire_expression2/core/init.lua new file mode 100644 index 0000000000..c7e7e499f0 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/init.lua @@ -0,0 +1,273 @@ +AddCSLuaFile("init.lua") + +/******************************************************************************\ + Expression 2 for Garry's Mod + Andreas "Syranide" Svensson, me@syranide.com +\******************************************************************************/ + +// ADD FUNCTIONS FOR COLOR CONVERSION! +// ADD CONSOLE SUPPORT + +/* +n = numeric +v = vector +s = string +t = table +e = entity +x = non-basic extensions prefix +*/ + +wire_expression2_delta = 0.0000001000000 +delta = wire_expression2_delta + +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ +/******************************************************************************/ + +/******************************************************************************/ +-- functions to type-check function return values. + +local wire_expression2_debug = CreateConVar("wire_expression2_debug", 0, 0) + +cvars.AddChangeCallback("wire_expression2_debug", function(CVar, PreviousValue, NewValue) + if (PreviousValue) == NewValue then return end + wire_expression2_reload() +end) + +-- Removes a typecheck from a function identified by the given signature. +local function removecheck(signature) + local entry = wire_expression2_funcs[signature] + local oldfunc,signature, rets, func,cost = entry.oldfunc,unpack(entry) + + if not oldfunc then return end + func = oldfunc + oldfunc = nil + + entry[3] = func + entry.oldfunc = oldfunc +end + +-- Installs a typecheck in a function identified by the given signature. +local function makecheck(signature) + local entry = wire_expression2_funcs[signature] + local oldfunc,signature, rets, func,cost = entry.oldfunc,unpack(entry) + + if oldfunc then return end + oldfunc = func + + function func(...) + local retval = oldfunc(...) + + local checker = wire_expression_types2[rets][5] + if not checker then return retval end + + local ok, msg = pcall(checker, retval) + if ok then return retval end + debug.Trace() + local full_signature = E2Lib.generate_signature(signature, rets) + error(string.format("Type check for function %q failed: %s\n", full_signature, msg),0) + + return retval + end + + entry[3] = func + entry.oldfunc = oldfunc +end + +/******************************************************************************/ + +function wire_expression2_reset_extensions() + wire_expression_callbacks = { + construct = {}, + destruct = {}, + preexecute = {}, + postexecute = {}, + } + + wire_expression_types = {} + wire_expression_types2 = { + [""] = { + [5] = function() if checker ~= nil then error("Return value of void function is not nil.",0) end end + } + } + wire_expression2_funcs = {} + wire_expression2_funclist = {} + wire_expression2_constants = {} +end + +-- additional args: , , +function registerType(name, id, def, ...) + wire_expression_types[string.upper(name)] = {id, def, ...} + wire_expression_types2[id] = {string.upper(name), def, ...} + if not WireLib.DT[string.upper(name)] then + WireLib.DT[string.upper(name)] = { Zero = def } + end +end + +function wire_expression2_CallHook(hookname, ...) + if not wire_expression_callbacks[hookname] then return end + local ret_array = {} + local errors = {} + local ok, ret + for i,callback in ipairs(wire_expression_callbacks[hookname]) do + e2_install_hook_fix() + ok, ret = pcall(callback, ...) + e2_remove_hook_fix() + if not ok then + table.insert(errors, "\n"..e2_processerror(ret)) + ret_array = nil + else + if ret_array then table.insert(ret_array, ret or false) end + end + end + if not ret_array then error("Error(s) occured while executing '"..hookname.."' hook:"..table.concat(errors),0) end + return ret_array +end + +function registerCallback(event, callback) + if not wire_expression_callbacks[event] then wire_expression_callbacks[event] = {} end + table.insert(wire_expression_callbacks[event], callback) +end + +local tempcost + +function __e2setcost(cost) + tempcost = cost +end +function __e2getcost() + return tempcost +end + +function registerOperator(name, pars, rets, func, cost, argnames) + local signature = "op:" .. name .. "(" .. pars .. ")" + + wire_expression2_funcs[signature] = { signature, rets, func, cost or tempcost, argnames=argnames } + if wire_expression2_debug:GetBool() then makecheck(signature) end +end + +function registerFunction(name, pars, rets, func, cost, argnames) + local signature = name .. "(" .. pars .. ")" + + wire_expression2_funcs[signature] = { signature, rets, func, cost or tempcost, argnames=argnames } + wire_expression2_funclist[name] = true + if wire_expression2_debug:GetBool() then makecheck(signature) end +end + +function E2Lib.registerConstant(name, value, literal) + if name:sub(1,1) ~= "_" then name = "_"..name end + if not value and not literal then value = _G[name] end + + if literal or type(value) == "number" then + wire_expression2_constants[name] = tostring(value) + else + wire_expression2_constants[name] = string.format("%q", value) + end +end + +/******************************************************************************/ + +if not datastream then require( "datastream" ) end + +if SERVER then + + e2_processerror = nil + local clientside_files = {} + + function AddCSE2File(filename) + AddCSLuaFile(filename) + clientside_files[filename] = true + end + include("extloader.lua") + + -- -- Transfer E2 function info to the client for validation and syntax highlighting purposes -- -- + + function _R.CRecipientFilter.IsValid() return true end -- workaround for this bug: http://www.facepunch.com/showpost.php?p=15117600 - thanks Lexi + + do + local functiondata,functiondata2 + + -- prepares a table with information about E2 types and functions + function wire_expression2_prepare_functiondata() + functiondata = { {}, {}, clientside_files, wire_expression2_constants } + functiondata2 = {} + for typename,v in pairs(wire_expression_types) do + functiondata[1][typename] = v[1] -- typeid + end + + for signature,v in pairs(wire_expression2_funcs) do + functiondata[2][signature] = v[2] -- ret + functiondata2[signature] = { v[4], v.argnames } -- cost, argnames + end + end + + wire_expression2_prepare_functiondata() + + + function wire_expression2_sendfunctions(ply) + -- send the prepared function data to the client + datastream.StreamToClients( ply, "wire_expression2_sendfunctions_hook", functiondata ) + datastream.StreamToClients( ply, "wire_expression2_sendfunctions_hook2", functiondata2 ) + end + + -- add a console command the user can use to re-request the function info, in case of errors or updates + concommand.Add("wire_expression2_sendfunctions", wire_expression2_sendfunctions) + + -- send function info once the player first spawns (TODO: find an even earlier hook) + hook.Add("PlayerInitialSpawn", "wire_expression2_sendfunctions", wire_expression2_sendfunctions) + end + +elseif CLIENT then + + e2_function_data_received = nil + -- -- Receive E2 function info from the server for validation and syntax highlighting purposes -- -- + + wire_expression2_reset_extensions() + + datastream.Hook( "wire_expression2_sendfunctions_hook", function( ply, handle, id, functiondata ) + wire_expression2_reset_extensions() + + -- types + for typename,typeid in pairs(functiondata[1]) do + wire_expression_types[typename] = { typeid } + wire_expression_types2[typeid] = { typename } + end + + -- functions + for signature,ret in pairs(functiondata[2]) do + local fname = signature:match("^([^(:]+)%(") + if fname then wire_expression2_funclist[fname] = true end + wire_expression2_funcs[signature] = { signature, ret, false } + end + + -- includes + for filename,_ in pairs(functiondata[3]) do + include("entities/gmod_wire_expression2/core/"..filename) + end + + -- constants + wire_expression2_constants = functiondata[4] + + e2_function_data_received = true + + if wire_expression2_editor then wire_expression2_editor:Validate(false) end + end) + datastream.Hook( "wire_expression2_sendfunctions_hook2", function( ply, handle, id, functiondata2 ) + for signature,v in pairs(functiondata2) do + local entry = wire_expression2_funcs[signature] + if entry then + entry[4] = v[1] -- cost + entry.argnames = v[2] -- argnames + end + end + end) + + if CanRunConsoleCommand() then + RunConsoleCommand("wire_expression2_sendfunctions") + end + +end + +include("e2doc.lua") diff --git a/lua/entities/gmod_wire_expression2/core/matrix.lua b/lua/entities/gmod_wire_expression2/core/matrix.lua new file mode 100644 index 0000000000..4fc86036dc --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/matrix.lua @@ -0,0 +1,1744 @@ +/******************************************************************************\ + Matrix support +\******************************************************************************/ + +local delta = wire_expression2_delta + +local function clone(a) + local b = {} + for k,v in ipairs(a) do + b[k] = v + end + return b +end + + +/******************************************************************************\ + 2x2 Matrices +\******************************************************************************/ + +registerType("matrix2", "xm2", { 0, 0, + 0, 0 }, + function(self, input) + local ret = {} + for k,v in pairs(input) do ret[k] = v end + return ret + end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 4 then error("Return value does not have exactly 4 entries!",0) end + end +) + +/******************************************************************************/ +// Common functions - explicit matrix solvers + +local function det2(a) + return ( a[1] * a[4] - a[3] * a[2] ) +end + +local function inverse2(a) + local det = det2(a) + if det == 0 then return { 0, 0, + 0, 0 } + end + return { a[4]/det, -a[2]/det, + -a[3]/det, a[1]/det } +end + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("matrix2", "", "xm2", function(self, args) + return { 0, 0, + 0, 0 } +end) + +__e2setcost(5) -- temporary + +registerFunction("matrix2", "xv2xv2", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv2[1], + rv1[2], rv2[2] } +end) + +registerFunction("matrix2", "nnnn", "xm2", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + return { rv1, rv2, + rv3, rv4 } +end) + +registerFunction("matrix2", "m", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv2[2], + rv1[4], rv2[5] } +end) + +registerFunction("identity2", "", "xm2", function(self, args) + return { 1, 0, + 0, 1 } +end) + +/******************************************************************************/ + +registerOperator("ass", "xm2", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ +// Comparison + +registerOperator("is", "xm2", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] > delta || -rv1[1] > delta || + rv1[2] > delta || -rv1[2] > delta || + rv1[3] > delta || -rv1[3] > delta || + rv1[4] > delta || -rv1[4] > delta + then return 1 else return 0 end +end) + +registerOperator("eq", "xm2xm2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta && + rv1[3] - rv2[3] <= delta && rv2[3] - rv1[3] <= delta && + rv1[4] - rv2[4] <= delta && rv2[4] - rv1[4] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "xm2xm2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta && rv2[1] - rv1[1] > delta && + rv1[2] - rv2[2] > delta && rv2[2] - rv1[2] > delta && + rv1[3] - rv2[3] > delta && rv2[3] - rv1[3] > delta && + rv1[4] - rv2[4] > delta && rv2[4] - rv1[4] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ +// Basic operations + +registerOperator("dlt", "xm2", "xm2", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2], + rv1[3] - rv2[3], rv1[4] - rv2[4] } +end) + +registerOperator("neg", "xm2", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2], + -rv1[3], -rv1[4] } +end) + +registerOperator("add", "xm2xm2", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2], + rv1[3] + rv2[3], rv1[4] + rv2[4] } +end) + +registerOperator("sub", "xm2xm2", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2], + rv1[3] - rv2[3], rv1[4] - rv2[4] } +end) + +registerOperator("mul", "nxm2", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2], + rv1 * rv2[3], rv1 * rv2[4] } +end) + +registerOperator("mul", "xm2n", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2, + rv1[3] * rv2, rv1[4] * rv2 } +end) + +registerOperator("mul", "xm2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1] + rv1[2] * rv2[2], + rv1[3] * rv2[1] + rv1[4] * rv2[2] } +end) + +registerOperator("mul", "xm2xm2", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1] + rv1[2] * rv2[3], + rv1[1] * rv2[2] + rv1[2] * rv2[4], + rv1[3] * rv2[1] + rv1[4] * rv2[3], + rv1[3] * rv2[2] + rv1[4] * rv2[4] } +end) + +registerOperator("div", "xm2n", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2, + rv1[3] / rv2, rv1[4] / rv2 } +end) + +registerOperator("exp", "xm2n", "xm2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + + if rv2 == -1 then return ( inverse2(rv1) ) + + elseif rv2 == 0 then return { 1, 0, + 0, 1 } + + elseif rv2 == 1 then return rv1 + + elseif rv2 == 2 then + return { rv1[1] * rv1[1] + rv1[2] * rv1[3], + rv1[1] * rv1[2] + rv1[2] * rv1[4], + rv1[3] * rv1[1] + rv1[4] * rv1[3], + rv1[3] * rv1[2] + rv1[4] * rv1[4] } + + else return { 0, 0, + 0, 0 } + end +end) + +/******************************************************************************/ +// Row/column/element manipulation + +registerFunction("row", "xm2:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 2 then k = 2 + else k = rv2 - rv2 % 1 end + + local x = rv1[k * 2 - 1] + local y = rv1[k * 2] + return { x, y } +end) + +registerFunction("column", "xm2:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 2 then k = 2 + else k = rv2 - rv2 % 1 end + + local x = rv1[k] + local y = rv1[k + 2] + return { x, y } +end) + +registerFunction("setRow", "xm2:nnn", "xm2", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local k + + if rv2 < 1 then k = 2 + elseif rv2 > 2 then k = 4 + else k = (rv2 - rv2 % 1)*2 end + + local a = clone(rv1) + a[k - 1] = rv3 + a[k] = rv4 + return a +end) + +registerFunction("setRow", "xm2:nxv2", "xm2", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local k + + if rv2 < 1 then k = 2 + elseif rv2 > 2 then k = 4 + else k = (rv2 - rv2 % 1)*2 end + + local a = clone(rv1) + a[k - 1] = rv3[1] + a[k] = rv3[2] + return a +end) + + +registerFunction("setColumn", "xm2:nnn", "xm2", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 2 then k = 2 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k] = rv3 + a[k + 2] = rv4 + return a +end) + +registerFunction("setColumn", "xm2:nxv2", "xm2", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 2 then k = 2 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k] = rv3[1] + a[k + 2] = rv3[2] + return a +end) + +registerFunction("swapRows", "xm2:", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + + rv1 = { rv1[3], rv1[4], + rv1[1], rv1[2] } + return rv1 +end) + +registerFunction("swapColumns", "xm2:", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + + rv1 = { rv1[2], rv1[1], + rv1[4], rv1[3] } + return rv1 +end) + +registerFunction("element", "xm2:nn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local i, j + + if rv2 < 1 then i = 1 + elseif rv2 > 2 then i = 2 + else i = rv2 - rv2 % 1 end + if rv3 < 1 then j = 1 + elseif rv3 > 2 then j = 2 + else j = rv3 - rv3 % 1 end + + local k = i + (j - 1) * 2 + return rv1[k] +end) + +registerFunction("setElement", "xm2:nnn", "xm2", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local i, j + + if rv2 < 1 then i = 1 + elseif rv2 > 2 then i = 2 + else i = rv2 - rv2 % 1 end + if rv3 < 1 then j = 1 + elseif rv3 > 2 then j = 2 + else j = rv3 - rv3 % 1 end + + local a = clone(rv1) + a[i + (j - 1) * 2] = rv4 + return a +end) + +registerFunction("swapElements", "xm2:nnnn", "xm2", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + local i1, j1, i2, j2 + + if rv2 < 1 then i1 = 1 + elseif rv2 > 3 then i1 = 3 + else i1 = rv2 - rv2 % 1 end + + if rv3 < 1 then j1 = 1 + elseif rv3 > 3 then j1 = 3 + else j1 = rv3 - rv3 % 1 end + + if rv4 < 1 then i2 = 1 + elseif rv4 > 3 then i2 = 3 + else i2 = rv4 - rv4 % 1 end + + if rv5 < 1 then j2 = 1 + elseif rv5 > 3 then j2 = 3 + else j2 = rv5 - rv5 % 1 end + + local k1 = i1 + (j1 - 1) * 2 + local k2 = i2 + (j2 - 1) * 2 + local a = clone(rv1) + a[k1], a[k2] = rv1[k2], rv1[k1] + return a +end) + +/******************************************************************************/ +// Useful matrix maths functions + +registerFunction("diagonal", "xm2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[4] } +end) + +registerFunction("trace", "xm2", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return ( rv1[1] + rv[4] ) +end) + +registerFunction("det", "xm2", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return ( det2(rv1) ) +end) + +registerFunction("transpose", "xm2", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[3], + rv1[2], rv1[4] } +end) + +registerFunction("adj", "xm2", "xm2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[4], -rv1[2], + -rv1[3], rv1[1] } +end) + + +/******************************************************************************\ + 3x3 Matrices +\******************************************************************************/ + +registerType("matrix", "m", { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 }, + function(self, input) + local ret = {} + for k,v in pairs(input) do ret[k] = v end + return ret + end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 9 then error("Return value does not have exactly 9 entries!",0) end + end +) + +/******************************************************************************/ +// Common functions - matrix solvers + +/* +-- Useful functions - may be used in the future? These have been written explicitly in the relevant commands for now. + +local function transpose3(a) + return { a[1], a[4], a[7], + a[2], a[5], a[8], + a[3], a[6], a[9] } +end + +local function adj3(a) + return { a[5] * a[9] - a[8] * a[6], a[8] * a[3] - a[2] * a[9], a[2] * a[6] - a[5] * a[3], + a[7] * a[6] - a[4] * a[9], a[1] * a[9] - a[7] * a[3], a[4] * a[3] - a[1] * a[6], + a[4] * a[8] - a[7] * a[5], a[7] * a[2] - a[1] * a[8], a[1] * a[5] - a[4] * a[2] } +end +*/ + +local function det3(a) + return ( a[1] * (a[5] * a[9] - a[8] * a[6]) - + a[2] * (a[4] * a[9] - a[7] * a[6]) + + a[3] * (a[4] * a[8] - a[7] * a[5]) ) +end + +local function inverse3(a) + local det = det3(a) + if det == 0 then return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } + end + return { (a[5] * a[9] - a[8] * a[6])/det, (a[8] * a[3] - a[2] * a[9])/det, (a[2] * a[6] - a[5] * a[3])/det, + (a[7] * a[6] - a[4] * a[9])/det, (a[1] * a[9] - a[7] * a[3])/det, (a[4] * a[3] - a[1] * a[6])/det, + (a[4] * a[8] - a[7] * a[5])/det, (a[7] * a[2] - a[1] * a[8])/det, (a[1] * a[5] - a[4] * a[2])/det } +end + + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("matrix", "", "m", function(self, args) + return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } +end) + +__e2setcost(5) -- temporary + +registerFunction("matrix", "vvv", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + return { rv1[1], rv2[1], rv3[1], + rv1[2], rv2[2], rv3[2], + rv1[3], rv2[3], rv3[3] } +end) + +registerFunction("matrix", "nnnnnnnnn", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local op4, op5, op6 = args[5], args[6], args[7] + local op7, op8, op9 = args[8], args[9], args[10] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local rv4, rv5, rv6 = op4[1](self, op4), op5[1](self, op5), op6[1](self, op6) + local rv7, rv8, rv9 = op7[1](self, op7), op8[1](self, op8), op9[1](self, op9) + return { rv1, rv2, rv3, + rv4, rv5, rv6, + rv7, rv8, rv9 } +end) + +registerFunction("matrix", "xm2", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], 0, + rv1[3], rv1[4], 0, + 0, 0, 0 } +end) + +registerFunction("identity", "", "m", function(self, args) + return { 1, 0, 0, + 0, 1, 0, + 0, 0, 1 } +end) + +/******************************************************************************/ + +registerOperator("ass", "m", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ +// Comparison + +registerOperator("is", "m", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] > delta || -rv1[1] > delta || + rv1[2] > delta || -rv1[2] > delta || + rv1[3] > delta || -rv1[3] > delta || + rv1[4] > delta || -rv1[4] > delta || + rv1[5] > delta || -rv1[5] > delta || + rv1[6] > delta || -rv1[6] > delta || + rv1[7] > delta || -rv1[7] > delta || + rv1[8] > delta || -rv1[8] > delta || + rv1[9] > delta || -rv1[9] > delta + then return 1 else return 0 end +end) + +registerOperator("eq", "mm", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta && + rv1[3] - rv2[3] <= delta && rv2[3] - rv1[3] <= delta && + rv1[4] - rv2[4] <= delta && rv2[4] - rv1[4] <= delta && + rv1[5] - rv2[5] <= delta && rv2[5] - rv1[5] <= delta && + rv1[6] - rv2[6] <= delta && rv2[6] - rv1[6] <= delta && + rv1[7] - rv2[7] <= delta && rv2[7] - rv1[7] <= delta && + rv1[8] - rv2[8] <= delta && rv2[8] - rv1[8] <= delta && + rv1[9] - rv2[9] <= delta && rv2[9] - rv1[9] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "mm", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta && rv2[1] - rv1[1] > delta && + rv1[2] - rv2[2] > delta && rv2[2] - rv1[2] > delta && + rv1[3] - rv2[3] > delta && rv2[3] - rv1[3] > delta && + rv1[4] - rv2[4] > delta && rv2[4] - rv1[4] > delta && + rv1[5] - rv2[5] > delta && rv2[5] - rv1[5] > delta && + rv1[6] - rv2[6] > delta && rv2[6] - rv1[6] > delta && + rv1[7] - rv2[7] > delta && rv2[7] - rv1[7] > delta && + rv1[8] - rv2[8] > delta && rv2[8] - rv1[8] > delta && + rv1[9] - rv2[9] > delta && rv2[9] - rv1[9] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ +// Basic operations + +registerOperator("dlt", "m", "m", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], + rv1[4] - rv2[4], rv1[5] - rv2[5], rv1[6] - rv2[6], + rv1[7] - rv2[7], rv1[8] - rv2[8], rv1[9] - rv2[9] } +end) + +registerOperator("neg", "m", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2], -rv1[3], + -rv1[4], -rv1[5], -rv1[6], + -rv1[7], -rv1[8], -rv1[9] } +end) + +registerOperator("add", "mm", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2], rv1[3] + rv2[3], + rv1[4] + rv2[4], rv1[5] + rv2[5], rv1[6] + rv2[6], + rv1[7] + rv2[7], rv1[8] + rv2[8], rv1[9] + rv2[9] } +end) + +registerOperator("sub", "mm", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], + rv1[4] - rv2[4], rv1[5] - rv2[5], rv1[6] - rv2[6], + rv1[7] - rv2[7], rv1[8] - rv2[8], rv1[9] - rv2[9] } +end) + +registerOperator("mul", "nm", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2], rv1 * rv2[3], + rv1 * rv2[4], rv1 * rv2[5], rv1 * rv2[6], + rv1 * rv2[7], rv1 * rv2[8], rv1 * rv2[9] } +end) + +registerOperator("mul", "mn", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2, rv1[3] * rv2, + rv1[4] * rv2, rv1[5] * rv2, rv1[6] * rv2, + rv1[7] * rv2, rv1[8] * rv2, rv1[9] * rv2 } +end) + +registerOperator("mul", "mv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1] + rv1[2] * rv2[2] + rv1[3] * rv2[3], + rv1[4] * rv2[1] + rv1[5] * rv2[2] + rv1[6] * rv2[3], + rv1[7] * rv2[1] + rv1[8] * rv2[2] + rv1[9] * rv2[3] } +end) + +registerOperator("mul", "mm", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1] + rv1[2] * rv2[4] + rv1[3] * rv2[7], + rv1[1] * rv2[2] + rv1[2] * rv2[5] + rv1[3] * rv2[8], + rv1[1] * rv2[3] + rv1[2] * rv2[6] + rv1[3] * rv2[9], + rv1[4] * rv2[1] + rv1[5] * rv2[4] + rv1[6] * rv2[7], + rv1[4] * rv2[2] + rv1[5] * rv2[5] + rv1[6] * rv2[8], + rv1[4] * rv2[3] + rv1[5] * rv2[6] + rv1[6] * rv2[9], + rv1[7] * rv2[1] + rv1[8] * rv2[4] + rv1[9] * rv2[7], + rv1[7] * rv2[2] + rv1[8] * rv2[5] + rv1[9] * rv2[8], + rv1[7] * rv2[3] + rv1[8] * rv2[6] + rv1[9] * rv2[9] } +end) + +registerOperator("div", "mn", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2, rv1[3] / rv2, + rv1[4] / rv2, rv1[5] / rv2, rv1[6] / rv2, + rv1[7] / rv2, rv1[8] / rv2, rv1[9] / rv2 } +end) + +registerOperator("exp", "mn", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + + if rv2 == -1 then return ( inverse3(rv1) ) + + elseif rv2 == 0 then return { 1, 0, 0, + 0, 1, 0, + 0, 0, 1 } + + elseif rv2 == 1 then return rv1 + + elseif rv2 == 2 then + return { rv1[1] * rv1[1] + rv1[2] * rv1[4] + rv1[3] * rv1[7], + rv1[1] * rv1[2] + rv1[2] * rv1[5] + rv1[3] * rv1[8], + rv1[1] * rv1[3] + rv1[2] * rv1[6] + rv1[3] * rv1[9], + rv1[4] * rv1[1] + rv1[5] * rv1[4] + rv1[6] * rv1[7], + rv1[4] * rv1[2] + rv1[5] * rv1[5] + rv1[6] * rv1[8], + rv1[4] * rv1[3] + rv1[5] * rv1[6] + rv1[6] * rv1[9], + rv1[7] * rv1[1] + rv1[8] * rv1[4] + rv1[9] * rv1[7], + rv1[7] * rv1[2] + rv1[8] * rv1[5] + rv1[9] * rv1[8], + rv1[7] * rv1[3] + rv1[8] * rv1[6] + rv1[9] * rv1[9] } + + else return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } + end +end) + +/******************************************************************************/ +// Row/column/element manipulation + +registerFunction("row", "m:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local k + + if rv2 < 1 then k = 3 + elseif rv2 > 3 then k = 9 + else k = (rv2 - rv2 % 1)*3 end + + local x = rv1[k - 2] + local y = rv1[k - 1] + local z = rv1[k] + return { x, y, z } +end) + +registerFunction("column", "m:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 3 then k = 3 + else k = rv2 - rv2 % 1 end + + local x = rv1[k] + local y = rv1[k + 3] + local z = rv1[k + 6] + return { x, y, z } +end) + +registerFunction("setRow", "m:nnnn", "m", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 3 then k = 3 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k * 3 - 2] = rv3 + a[k * 3 - 1] = rv4 + a[k * 3] = rv5 + return a +end) + +registerFunction("setRow", "m:nv", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 3 then k = 3 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k * 3 - 2] = rv3[1] + a[k * 3 - 1] = rv3[2] + a[k * 3] = rv3[3] + return a +end) + +registerFunction("setColumn", "m:nnnn", "m", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 3 then k = 3 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k] = rv3 + a[k + 3] = rv4 + a[k + 6] = rv5 + return a +end) + +registerFunction("setColumn", "m:nv", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 3 then k = 3 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k] = rv3[1] + a[k + 3] = rv3[2] + a[k + 6] = rv3[3] + return a +end) + +registerFunction("swapRows", "m:nn", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local r1, r2 + + if rv2 < 1 then r1 = 1 + elseif rv2 > 3 then r1 = 3 + else r1 = rv2 - rv2 % 1 end + if rv3 < 1 then r2 = 1 + elseif rv3 > 3 then r2 = 3 + else r2 = rv3 - rv3 % 1 end + + if r1 == r2 then return rv1 + elseif (r1 == 1 && r2 == 2) || (r1 == 2 && r2 == 1) then + rv1 = { rv1[4], rv1[5], rv1[6], + rv1[1], rv1[2], rv1[3], + rv1[7], rv1[8], rv1[9] } + elseif (r1 == 2 && r2 == 3) || (r1 == 3 && r2 == 2) then + rv1 = { rv1[1], rv1[2], rv1[3], + rv1[7], rv1[8], rv1[9], + rv1[4], rv1[5], rv1[6] } + elseif (r1 == 1 && r2 == 3) || (r1 == 3 && r2 == 1) then + rv1 = { rv1[7], rv1[8], rv1[9], + rv1[4], rv1[5], rv1[6], + rv1[1], rv1[2], rv1[3] } + end + return rv1 +end) + +registerFunction("swapColumns", "m:nn", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local r1, r2 + + if rv2 < 1 then r1 = 1 + elseif rv2 > 3 then r1 = 3 + else r1 = rv2 - rv2 % 1 end + if rv3 < 1 then r2 = 1 + elseif rv3 > 3 then r2 = 3 + else r2 = rv3 - rv3 % 1 end + + if r1 == r2 then return rv1 + elseif (r1 == 1 && r2 == 2) || (r1 == 2 && r2 == 1) then + rv1 = { rv1[2], rv1[1], rv1[3], + rv1[5], rv1[4], rv1[6], + rv1[8], rv1[7], rv1[9] } + elseif (r1 == 2 && r2 == 3) || (r1 == 3 && r2 == 2) then + rv1 = { rv1[1], rv1[3], rv1[2], + rv1[4], rv1[6], rv1[5], + rv1[7], rv1[9], rv1[8] } + elseif (r1 == 1 && r2 == 3) || (r1 == 3 && r2 == 1) then + rv1 = { rv1[3], rv1[2], rv1[1], + rv1[6], rv1[5], rv1[4], + rv1[9], rv1[8], rv1[7] } + end + return rv1 +end) + +registerFunction("element", "m:nn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local i, j + + if rv2 < 1 then i = 1 + elseif rv2 > 3 then i = 3 + else i = rv2 - rv2 % 1 end + if rv3 < 1 then j = 1 + elseif rv3 > 3 then j = 3 + else j = rv3 - rv3 % 1 end + + local k = i + (j - 1) * 3 + return rv1[k] +end) + +registerFunction("setElement", "m:nnn", "m", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local i, j + + if rv2 < 1 then i = 1 + elseif rv2 > 3 then i = 3 + else i = rv2 - rv2 % 1 end + if rv3 < 1 then j = 1 + elseif rv3 > 3 then j = 3 + else j = rv3 - rv3 % 1 end + + local a = clone(rv1) + a[i + (j - 1) * 3] = rv4 + return a +end) + +registerFunction("swapElements", "m:nnnn", "m", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + local i1, j1, i2, j2 + + if rv2 < 1 then i1 = 1 + elseif rv2 > 3 then i1 = 3 + else i1 = rv2 - rv2 % 1 end + + if rv3 < 1 then j1 = 1 + elseif rv3 > 3 then j1 = 3 + else j1 = rv3 - rv3 % 1 end + + if rv4 < 1 then i2 = 1 + elseif rv4 > 3 then i2 = 3 + else i2 = rv4 - rv4 % 1 end + + if rv5 < 1 then j2 = 1 + elseif rv5 > 3 then j2 = 3 + else j2 = rv5 - rv5 % 1 end + + local k1 = i1 + (j1 - 1) * 3 + local k2 = i2 + (j2 - 1) * 3 + local a = clone(rv1) + a[k1], a[k2] = rv1[k2], rv1[k1] + return a +end) + +registerFunction("setDiagonal", "m:v", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv2[1], rv1[4], rv1[7], + rv1[2], rv2[2], rv1[8], + rv1[3], rv1[6], rv2[3] } +end) + +registerFunction("setDiagonal", "m:nnn", "m", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + return { rv2, rv1[4], rv1[7], + rv1[2], rv3, rv1[8], + rv1[3], rv1[6], rv4 } +end) + +/******************************************************************************/ +// Useful matrix maths functions + +registerFunction("diagonal", "m", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[5], rv1[9] } +end) + +registerFunction("trace", "m", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return ( rv1[1] + rv1[5] + rv1[9] ) +end) + +registerFunction("det", "m", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return ( det3(rv1) ) +end) + +registerFunction("transpose", "m", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[4], rv1[7], + rv1[2], rv1[5], rv1[8], + rv1[3], rv1[6], rv1[9] } +end) + +registerFunction("adj", "m", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[5] * rv1[9] - rv1[8] * rv1[6], rv1[8] * rv1[3] - rv1[2] * rv1[9], rv1[2] * rv1[6] - rv1[5] * rv1[3], + rv1[7] * rv1[6] - rv1[4] * rv1[9], rv1[1] * rv1[9] - rv1[7] * rv1[3], rv1[4] * rv1[3] - rv1[1] * rv1[6], + rv1[4] * rv1[8] - rv1[7] * rv1[5], rv1[7] * rv1[2] - rv1[1] * rv1[8], rv1[1] * rv1[5] - rv1[4] * rv1[2] } +end) + +/******************************************************************************/ +// Extra functions + +registerFunction("matrix", "e", "m", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then + return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } + end + local factor = 10000 + local pos = rv1:GetPos() + local x = rv1:LocalToWorld(Vector(factor,0,0)) - pos + local y = rv1:LocalToWorld(Vector(0,factor,0)) - pos + local z = rv1:LocalToWorld(Vector(0,0,factor)) - pos + return { x.x/factor, y.x/factor, z.x/factor, + x.y/factor, y.y/factor, z.y/factor, + x.z/factor, y.z/factor, z.z/factor } +end) + +registerFunction("x", "m:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[4], rv1[7] } +end) + +registerFunction("y", "m:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[2], rv1[5], rv1[8] } +end) + +registerFunction("z", "m:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[3], rv1[6], rv1[9] } +end) + +// Create a rotation matrix in the format (v,n) where v is the axis direction vector and n is degrees (right-handed rotation) +registerFunction("mRotation", "vn", "m", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + + local vec + local len = (rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3]) ^ 0.5 + if len == 1 then vec = rv1 + elseif len > delta then vec = { rv1[1] / len, rv1[2] / len, rv1[3] / len } + else return { 0, 0, 0, + 0, 0, 0, + 0, 0, 0 } + end + + local vec2 = { vec[1] * vec[1], vec[2] * vec[2], vec[3] * vec[3] } + local a = rv2 * 3.14159265 / 180 + local cos = math.cos(a) + local sin = math.sin(a) + local cosmin = 1 - cos + return { vec2[1] + (1 - vec2[1]) * cos, + vec[1] * vec[2] * cosmin - vec[3] * sin, + vec[1] * vec[3] * cosmin + vec[2] * sin, + vec[1] * vec[2] * cosmin + vec[3] * sin, + vec2[2] + (1 - vec2[2]) * cos, + vec[2] * vec[3] * cosmin - vec[1] * sin, + vec[1] * vec[3] * cosmin - vec[2] * sin, + vec[2] * vec[3] * cosmin + vec[1] * sin, + vec2[3] + (1 - vec2[3]) * cos } +end) + +/******************************************************************************\ + 4x4 Matrices +\******************************************************************************/ + +registerType("matrix4", "xm4", { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 }, + function(self, input) + local ret = {} + for k,v in pairs(input) do ret[k] = v end + return ret + end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 16 then error("Return value does not have exactly 16 entries!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("matrix4", "", "xm4", function(self, args) + return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } +end) + +__e2setcost(5) -- temporary + +registerFunction("matrix4", "xv4xv4xv4xv4", "xm4", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + return { rv1[1], rv2[1], rv3[1], rv4[1], + rv1[2], rv2[2], rv3[2], rv4[2], + rv1[3], rv2[3], rv3[3], rv4[3], + rv1[4], rv2[4], rv3[4], rv4[4] } +end) + +registerFunction("matrix4", "nnnnnnnnnnnnnnnn", "xm4", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local op5, op6, op7, op8 = args[6], args[7], args[8], args[9] + local op9, op10, op11, op12 = args[10], args[11], args[12], args[13] + local op13, op14, op15, op16 = args[14], args[15], args[16], args[17] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local rv5, rv6, rv7, rv8 = op5[1](self, op5), op6[1](self, op6), op7[1](self, op7), op8[1](self, op8) + local rv9, rv10, rv11, rv12 = op9[1](self, op9), op10[1](self, op10), op11[1](self, op11), op12[1](self, op12) + local rv13, rv14, rv15, rv16 = op13[1](self, op13), op14[1](self, op14), op15[1](self, op15), op16[1](self, op16) + return { rv1, rv2, rv3, rv4, + rv5, rv6, rv7, rv8, + rv9, rv10, rv11, rv12, + rv13, rv14, rv15, rv16 } +end) + +registerFunction("matrix4", "xm2", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], 0, 0, + rv1[3], rv1[4], 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } +end) + +registerFunction("matrix4", "xm2xm2xm2xm2", "xm4", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + return { rv1[1], rv1[2], rv2[1], rv2[2], + rv1[3], rv1[4], rv2[3], rv2[4], + rv3[1], rv3[2], rv4[1], rv4[2], + rv3[3], rv3[4], rv4[3], rv4[4] } +end) + +registerFunction("matrix4", "m", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], rv1[3], 0, + rv1[4], rv1[5], rv1[6], 0, + rv1[7], rv1[8], rv1[9], 0, + 0, 0, 0, 0 } +end) + +registerFunction("identity4", "", "xm4", function(self, args) + return { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 } +end) + +/******************************************************************************/ + +registerOperator("ass", "xm4", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ +// Comparison + +registerOperator("is", "xm4", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] > delta || -rv1[1] > delta || + rv1[2] > delta || -rv1[2] > delta || + rv1[3] > delta || -rv1[3] > delta || + rv1[4] > delta || -rv1[4] > delta || + rv1[5] > delta || -rv1[5] > delta || + rv1[6] > delta || -rv1[6] > delta || + rv1[7] > delta || -rv1[7] > delta || + rv1[8] > delta || -rv1[8] > delta || + rv1[9] > delta || -rv1[9] > delta || + rv1[10] > delta || -rv1[10] > delta || + rv1[11] > delta || -rv1[11] > delta || + rv1[12] > delta || -rv1[12] > delta || + rv1[13] > delta || -rv1[13] > delta || + rv1[14] > delta || -rv1[14] > delta || + rv1[15] > delta || -rv1[15] > delta || + rv1[16] > delta || -rv1[16] > delta + then return 1 else return 0 end +end) + +registerOperator("eq", "xm4xm4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta && + rv1[3] - rv2[3] <= delta && rv2[3] - rv1[3] <= delta && + rv1[4] - rv2[4] <= delta && rv2[4] - rv1[4] <= delta && + rv1[5] - rv2[5] <= delta && rv2[5] - rv1[5] <= delta && + rv1[6] - rv2[6] <= delta && rv2[6] - rv1[6] <= delta && + rv1[7] - rv2[7] <= delta && rv2[7] - rv1[7] <= delta && + rv1[8] - rv2[8] <= delta && rv2[8] - rv1[8] <= delta && + rv1[9] - rv2[9] <= delta && rv2[9] - rv1[9] <= delta && + rv1[10] - rv2[10] <= delta && rv2[10] - rv1[10] <= delta && + rv1[11] - rv2[11] <= delta && rv2[11] - rv1[11] <= delta && + rv1[12] - rv2[12] <= delta && rv2[12] - rv1[12] <= delta && + rv1[13] - rv2[13] <= delta && rv2[13] - rv1[13] <= delta && + rv1[14] - rv2[14] <= delta && rv2[14] - rv1[14] <= delta && + rv1[15] - rv2[15] <= delta && rv2[15] - rv1[15] <= delta && + rv1[16] - rv2[16] <= delta && rv2[16] - rv1[16] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "xm4xm4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta && rv2[1] - rv1[1] > delta && + rv1[2] - rv2[2] > delta && rv2[2] - rv1[2] > delta && + rv1[3] - rv2[3] > delta && rv2[3] - rv1[3] > delta && + rv1[4] - rv2[4] > delta && rv2[4] - rv1[4] > delta && + rv1[5] - rv2[5] > delta && rv2[5] - rv1[5] > delta && + rv1[6] - rv2[6] > delta && rv2[6] - rv1[6] > delta && + rv1[7] - rv2[7] > delta && rv2[7] - rv1[7] > delta && + rv1[8] - rv2[8] > delta && rv2[8] - rv1[8] > delta && + rv1[9] - rv2[9] > delta && rv2[9] - rv1[9] > delta && + rv1[10] - rv2[10] > delta && rv2[10] - rv1[10] > delta && + rv1[11] - rv2[11] > delta && rv2[11] - rv1[11] > delta && + rv1[12] - rv2[12] > delta && rv2[12] - rv1[12] > delta && + rv1[13] - rv2[13] > delta && rv2[13] - rv1[13] > delta && + rv1[14] - rv2[14] > delta && rv2[14] - rv1[14] > delta && + rv1[15] - rv2[15] > delta && rv2[15] - rv1[15] > delta && + rv1[16] - rv2[16] > delta && rv2[16] - rv1[16] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ +// Basic operations + +registerOperator("dlt", "xm4", "xm4", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], rv1[4] - rv2[4], + rv1[5] - rv2[5], rv1[6] - rv2[6], rv1[7] - rv2[7], rv1[8] - rv2[8], + rv1[9] - rv2[9], rv1[10] - rv2[10], rv1[11] - rv2[11], rv1[12] - rv2[12], + rv1[13] - rv2[13], rv1[14] - rv2[14], rv1[15] - rv2[15], rv1[16] - rv2[16] } +end) + +registerOperator("neg", "xm4", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2], -rv1[3], -rv1[4], + -rv1[5], -rv1[6], -rv1[7], -rv1[8], + -rv1[9], -rv1[10], -rv1[11], -rv1[12], + -rv1[13], -rv1[14], -rv1[15], -rv1[16] } +end) + +registerOperator("add", "xm4xm4", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2], rv1[3] + rv2[3], rv1[4] + rv2[4], + rv1[5] + rv2[5], rv1[6] + rv2[6], rv1[7] + rv2[7], rv1[8] + rv2[8], + rv1[9] + rv2[9], rv1[10] + rv2[10], rv1[11] + rv2[11], rv1[12] + rv2[12], + rv1[13] + rv2[13], rv1[14] + rv2[14], rv1[15] + rv2[15], rv1[16] + rv2[16] } +end) + +registerOperator("sub", "xm4xm4", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], rv1[4] - rv2[4], + rv1[5] - rv2[5], rv1[6] - rv2[6], rv1[7] - rv2[7], rv1[8] - rv2[8], + rv1[9] - rv2[9], rv1[10] - rv2[10], rv1[11] - rv2[11], rv1[12] - rv2[12], + rv1[13] - rv2[13], rv1[14] - rv2[14], rv1[15] - rv2[15], rv1[16] - rv2[16] } +end) + +registerOperator("mul", "nxm4", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2], rv1 * rv2[3], rv1 * rv2[4], + rv1 * rv2[5], rv1 * rv2[6], rv1 * rv2[7], rv1 * rv2[8], + rv1 * rv2[9], rv1 * rv2[10], rv1 * rv2[11], rv1 * rv2[12], + rv1 * rv2[13], rv1 * rv2[14], rv1 * rv2[15], rv1 * rv2[16] } +end) + +registerOperator("mul", "xm4n", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2, rv1[3] * rv2, rv1[4] * rv2, + rv1[5] * rv2, rv1[6] * rv2, rv1[7] * rv2, rv1[8] * rv2, + rv1[9] * rv2, rv1[10] * rv2, rv1[11] * rv2, rv1[12] * rv2, + rv1[13] * rv2, rv1[14] * rv2, rv1[15] * rv2, rv1[16] * rv2 } +end) + +registerOperator("mul", "xm4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1] + rv1[2] * rv2[2] + rv1[3] * rv2[3] + rv1[4] * rv2[4], + rv1[5] * rv2[1] + rv1[6] * rv2[2] + rv1[7] * rv2[3] + rv1[8] * rv2[4], + rv1[9] * rv2[1] + rv1[10] * rv2[2] + rv1[11] * rv2[3] + rv1[12] * rv2[4], + rv1[13] * rv2[1] + rv1[14] * rv2[2] + rv1[15] * rv2[3] + rv1[16] * rv2[4] } +end) + +registerOperator("mul", "xm4xm4", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local lhs, rhs = op1[1](self, op1), op2[1](self, op2) + return { + lhs[ 1] * rhs[ 1] + lhs[ 2] * rhs[ 5] + lhs[ 3] * rhs[ 9] + lhs[ 4] * rhs[13], + lhs[ 1] * rhs[ 2] + lhs[ 2] * rhs[ 6] + lhs[ 3] * rhs[10] + lhs[ 4] * rhs[14], + lhs[ 1] * rhs[ 3] + lhs[ 2] * rhs[ 7] + lhs[ 3] * rhs[11] + lhs[ 4] * rhs[15], + lhs[ 1] * rhs[ 4] + lhs[ 2] * rhs[ 8] + lhs[ 3] * rhs[12] + lhs[ 4] * rhs[16], + lhs[ 5] * rhs[ 1] + lhs[ 6] * rhs[ 5] + lhs[ 7] * rhs[ 9] + lhs[ 8] * rhs[13], + lhs[ 5] * rhs[ 2] + lhs[ 6] * rhs[ 6] + lhs[ 7] * rhs[10] + lhs[ 8] * rhs[14], + lhs[ 5] * rhs[ 3] + lhs[ 6] * rhs[ 7] + lhs[ 7] * rhs[11] + lhs[ 8] * rhs[15], + lhs[ 5] * rhs[ 4] + lhs[ 6] * rhs[ 8] + lhs[ 7] * rhs[12] + lhs[ 8] * rhs[16], + lhs[ 9] * rhs[ 1] + lhs[10] * rhs[ 5] + lhs[11] * rhs[ 9] + lhs[12] * rhs[13], + lhs[ 9] * rhs[ 2] + lhs[10] * rhs[ 6] + lhs[11] * rhs[10] + lhs[12] * rhs[14], + lhs[ 9] * rhs[ 3] + lhs[10] * rhs[ 7] + lhs[11] * rhs[11] + lhs[12] * rhs[15], + lhs[ 9] * rhs[ 4] + lhs[10] * rhs[ 8] + lhs[11] * rhs[12] + lhs[12] * rhs[16], + lhs[13] * rhs[ 1] + lhs[14] * rhs[ 5] + lhs[15] * rhs[ 9] + lhs[16] * rhs[13], + lhs[13] * rhs[ 2] + lhs[14] * rhs[ 6] + lhs[15] * rhs[10] + lhs[16] * rhs[14], + lhs[13] * rhs[ 3] + lhs[14] * rhs[ 7] + lhs[15] * rhs[11] + lhs[16] * rhs[15], + lhs[13] * rhs[ 4] + lhs[14] * rhs[ 8] + lhs[15] * rhs[12] + lhs[16] * rhs[16] + } +end) + +registerOperator("div", "xm4n", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2, rv1[3] / rv2, rv1[4] / rv2, + rv1[5] / rv2, rv1[6] / rv2, rv1[7] / rv2, rv1[8] / rv2, + rv1[9] / rv2, rv1[10] / rv2, rv1[11] / rv2, rv1[12] / rv2, + rv1[13] / rv2, rv1[14] / rv2, rv1[15] / rv2, rv1[16] / rv2 } +end) + +registerOperator("exp", "xm4n", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local lhs, rhs = op1[1](self, op1), op2[1](self, op2) + + //if rhs == -1 then return ( inverse4(lhs) ) + + if rhs == 0 then return { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 } + + elseif rhs == 1 then return lhs + + elseif rhs == 2 then + return { + lhs[ 1] * lhs[ 1] + lhs[ 2] * lhs[ 5] + lhs[ 3] * lhs[ 9] + lhs[ 4] * lhs[13], + lhs[ 1] * lhs[ 2] + lhs[ 2] * lhs[ 6] + lhs[ 3] * lhs[10] + lhs[ 4] * lhs[14], + lhs[ 1] * lhs[ 3] + lhs[ 2] * lhs[ 7] + lhs[ 3] * lhs[11] + lhs[ 4] * lhs[15], + lhs[ 1] * lhs[ 4] + lhs[ 2] * lhs[ 8] + lhs[ 3] * lhs[12] + lhs[ 4] * lhs[16], + lhs[ 5] * lhs[ 1] + lhs[ 6] * lhs[ 5] + lhs[ 7] * lhs[ 9] + lhs[ 8] * lhs[13], + lhs[ 5] * lhs[ 2] + lhs[ 6] * lhs[ 6] + lhs[ 7] * lhs[10] + lhs[ 8] * lhs[14], + lhs[ 5] * lhs[ 3] + lhs[ 6] * lhs[ 7] + lhs[ 7] * lhs[11] + lhs[ 8] * lhs[15], + lhs[ 5] * lhs[ 4] + lhs[ 6] * lhs[ 8] + lhs[ 7] * lhs[12] + lhs[ 8] * lhs[16], + lhs[ 9] * lhs[ 1] + lhs[10] * lhs[ 5] + lhs[11] * lhs[ 9] + lhs[12] * lhs[13], + lhs[ 9] * lhs[ 2] + lhs[10] * lhs[ 6] + lhs[11] * lhs[10] + lhs[12] * lhs[14], + lhs[ 9] * lhs[ 3] + lhs[10] * lhs[ 7] + lhs[11] * lhs[11] + lhs[12] * lhs[15], + lhs[ 9] * lhs[ 4] + lhs[10] * lhs[ 8] + lhs[11] * lhs[12] + lhs[12] * lhs[16], + lhs[13] * lhs[ 1] + lhs[14] * lhs[ 5] + lhs[15] * lhs[ 9] + lhs[16] * lhs[13], + lhs[13] * lhs[ 2] + lhs[14] * lhs[ 6] + lhs[15] * lhs[10] + lhs[16] * lhs[14], + lhs[13] * lhs[ 3] + lhs[14] * lhs[ 7] + lhs[15] * lhs[11] + lhs[16] * lhs[15], + lhs[13] * lhs[ 4] + lhs[14] * lhs[ 8] + lhs[15] * lhs[12] + lhs[16] * lhs[16] + } + + else return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } + end +end) + +/******************************************************************************/ +// Row/column/element manipulation + +registerFunction("row", "xm4:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local k + + if rv2 < 1 then k = 4 + elseif rv2 > 4 then k = 16 + else k = (rv2 - rv2 % 1)*4 end + + local x = rv1[k - 3] + local y = rv1[k - 2] + local z = rv1[k - 1] + local w = rv1[k] + return { x, y, z, w } +end) + +registerFunction("column", "xm4:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 4 then k = 4 + else k = rv2 - rv2 % 1 end + + local x = rv1[k] + local y = rv1[k + 4] + local z = rv1[k + 8] + local w = rv1[k + 12] + return { x, y, z, w } +end) + +registerFunction("setRow", "xm4:nnnnn", "xm4", function(self, args) + local op1, op2, op3, op4, op5, op6 = args[2], args[3], args[4], args[5], args[6], args[7] + local rv1, rv2, rv3, rv4, rv5, rv6 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5), op6[1](self, op6) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 4 then k = 4 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k * 4 - 3] = rv3 + a[k * 4 - 2] = rv4 + a[k * 4 - 1] = rv5 + a[k * 4] = rv6 + return a +end) + +registerFunction("setRow", "xm4:nxv4", "xm4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 4 then k = 4 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k * 4 - 3] = rv3[1] + a[k * 4 - 2] = rv3[2] + a[k * 4 - 1] = rv3[3] + a[k * 4] = rv3[4] + return a +end) + +registerFunction("setColumn", "xm4:nnnnn", "xm4", function(self, args) + local op1, op2, op3, op4, op5, op6 = args[2], args[3], args[4], args[5], args[6], args[7] + local rv1, rv2, rv3, rv4, rv5, rv6 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5), op6[1](self, op6) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 4 then k = 4 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k] = rv3 + a[k + 4] = rv4 + a[k + 8] = rv5 + a[k + 12] = rv6 + return a +end) + +registerFunction("setColumn", "xm4:nxv4", "xm4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local k + + if rv2 < 1 then k = 1 + elseif rv2 > 4 then k = 4 + else k = rv2 - rv2 % 1 end + + local a = clone(rv1) + a[k] = rv3[1] + a[k + 4] = rv3[2] + a[k + 8] = rv3[3] + a[k + 12] = rv3[4] + return a +end) + +registerFunction("swapRows", "m:nn", "m", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local r1, r2 + + if rv2 < 1 then r1 = 1 + elseif rv2 > 4 then r1 = 4 + else r1 = rv2 - rv2 % 1 end + if rv3 < 1 then r2 = 1 + elseif rv3 > 4 then r2 = 4 + else r2 = rv3 - rv3 % 1 end + + if r1 == r2 then return rv1 + elseif (r1 == 1 && r2 == 2) || (r1 == 2 && r2 == 1) then + rv1 = { rv1[5], rv1[6], rv1[7], rv1[8], + rv1[1], rv1[2], rv1[3], rv1[4], + rv1[9], rv1[10], rv1[11], rv1[12], + rv1[13], rv1[14], rv1[15], rv1[16] } + elseif (r1 == 2 && r2 == 3) || (r1 == 3 && r2 == 2) then + rv1 = { rv1[1], rv1[2], rv1[3], rv1[4], + rv1[9], rv1[10], rv1[11], rv1[12], + rv1[5], rv1[6], rv1[7], rv1[8], + rv1[13], rv1[14], rv1[15], rv1[16] } + elseif (r1 == 3 && r2 == 4) || (r1 == 4 && r2 == 3) then + rv1 = { rv1[1], rv1[2], rv1[3], rv1[4], + rv1[5], rv1[6], rv1[7], rv1[8], + rv1[13], rv1[14], rv1[15], rv1[16], + rv1[9], rv1[10], rv1[11], rv1[12] } + elseif (r1 == 1 && r2 == 3) || (r1 == 3 && r2 == 1) then + rv1 = { rv1[9], rv1[10], rv1[11], rv1[12], + rv1[5], rv1[6], rv1[7], rv1[8], + rv1[1], rv1[2], rv1[3], rv1[4], + rv1[13], rv1[14], rv1[15], rv1[16] } + elseif (r1 == 2 && r2 == 4) || (r1 == 4 && r2 == 2) then + rv1 = { rv1[1], rv1[2], rv1[3], rv1[4], + rv1[13], rv1[14], rv1[15], rv1[16], + rv1[9], rv1[10], rv1[11], rv1[12], + rv1[5], rv1[6], rv1[7], rv1[8] } + elseif (r1 == 1 && r2 == 4) || (r1 == 4 && r2 == 1) then + rv1 = { rv1[13], rv1[14], rv1[15], rv1[16], + rv1[5], rv1[6], rv1[7], rv1[8], + rv1[9], rv1[10], rv1[11], rv1[12], + rv1[1], rv1[2], rv1[3], rv1[4] } + end + return rv1 +end) + +registerFunction("swapColumns", "xm4:nn", "xm4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local r1, r2 + + if rv2 < 1 then r1 = 1 + elseif rv2 > 4 then r1 = 4 + else r1 = rv2 - rv2 % 1 end + if rv3 < 1 then r2 = 1 + elseif rv3 > 4 then r2 = 4 + else r2 = rv3 - rv3 % 1 end + + if r1 == r2 then return rv1 + elseif (r1 == 1 && r2 == 2) || (r1 == 2 && r2 == 1) then + rv1 = { rv1[2], rv1[1], rv1[3], rv1[4], + rv1[6], rv1[5], rv1[7], rv1[8], + rv1[10], rv1[9], rv1[11], rv1[12], + rv1[14], rv1[13], rv1[15], rv1[16] } + elseif (r1 == 2 && r2 == 3) || (r1 == 3 && r2 == 2) then + rv1 = { rv1[1], rv1[3], rv1[2], rv1[4], + rv1[5], rv1[7], rv1[6], rv1[8], + rv1[9], rv1[11], rv1[10], rv1[12], + rv1[13], rv1[15], rv1[14], rv1[16] } + elseif (r1 == 3 && r2 == 4) || (r1 == 4 && r2 == 3) then + rv1 = { rv1[1], rv1[2], rv1[4], rv1[3], + rv1[5], rv1[6], rv1[8], rv1[7], + rv1[9], rv1[10], rv1[12], rv1[11], + rv1[13], rv1[14], rv1[16], rv1[15] } + elseif (r1 == 1 && r2 == 3) || (r1 == 3 && r2 == 1) then + rv1 = { rv1[3], rv1[2], rv1[1], rv1[4], + rv1[7], rv1[6], rv1[5], rv1[8], + rv1[11], rv1[10], rv1[9], rv1[12], + rv1[15], rv1[14], rv1[13], rv1[16] } + elseif (r1 == 2 && r2 == 4) || (r1 == 4 && r2 == 2) then + rv1 = { rv1[1], rv1[4], rv1[3], rv1[2], + rv1[5], rv1[8], rv1[7], rv1[6], + rv1[9], rv1[12], rv1[11], rv1[10], + rv1[13], rv1[16], rv1[15], rv1[14] } + elseif (r1 == 1 && r2 == 4) || (r1 == 4 && r2 == 1) then + rv1 = { rv1[4], rv1[2], rv1[3], rv1[1], + rv1[8], rv1[6], rv1[7], rv1[5], + rv1[12], rv1[10], rv1[11], rv1[9], + rv1[16], rv1[14], rv1[15], rv1[13] } + end + return rv1 +end) + +registerFunction("element", "xm4:nn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local i, j + + if rv2 < 1 then i = 1 + elseif rv2 > 4 then i = 4 + else i = rv2 - rv2 % 1 end + if rv3 < 1 then j = 1 + elseif rv3 > 4 then j = 4 + else j = rv3 - rv3 % 1 end + + local k = i + (j - 1) * 4 + return rv1[k] +end) + +registerFunction("setElement", "xm4:nnn", "xm4", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local i, j + + if rv2 < 1 then i = 1 + elseif rv2 > 4 then i = 4 + else i = rv2 - rv2 % 1 end + if rv3 < 1 then j = 1 + elseif rv3 > 4 then j = 4 + else j = rv3 - rv3 % 1 end + + local a = clone(rv1) + a[i + (j - 1) * 4] = rv4 + return a +end) + +registerFunction("swapElements", "xm4:nnnn", "xm4", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + local i1, j1, i2, j2 + + if rv2 < 1 then i1 = 1 + elseif rv2 > 4 then i1 = 4 + else i1 = rv2 - rv2 % 1 end + + if rv3 < 1 then j1 = 1 + elseif rv3 > 4 then j1 = 4 + else j1 = rv3 - rv3 % 1 end + + if rv4 < 1 then i2 = 1 + elseif rv4 > 4 then i2 = 4 + else i2 = rv4 - rv4 % 1 end + + if rv5 < 1 then j2 = 1 + elseif rv5 > 4 then j2 = 4 + else j2 = rv5 - rv5 % 1 end + + local k1 = i1 + (j1 - 1) * 4 + local k2 = i2 + (j2 - 1) * 4 + local a = clone(rv1) + a[k1], a[k2] = rv1[k2], rv1[k1] + return a +end) + +registerFunction("setDiagonal", "xm4:xv4", "xm4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv2[1], rv1[2], rv1[3], rv1[4], + rv1[5], rv2[2], rv1[7], rv1[8], + rv1[9], rv1[10], rv2[3], rv1[12], + rv1[13], rv1[14], rv1[15], rv2[4] } +end) + +registerFunction("setDiagonal", "xm4:nnnn", "xm4", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4), op5[1](self, op5) + return { rv2, rv1[2], rv1[3], rv1[4], + rv1[5], rv3, rv1[7], rv1[8], + rv1[9], rv1[10], rv4, rv1[12], + rv1[13], rv1[14], rv1[15], rv5 } +end) + +/******************************************************************************/ +// Useful matrix maths functions + +registerFunction("diagonal", "xm4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[6], rv1[11], rv1[16] } +end) + +registerFunction("trace", "xm4", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return ( rv1[1] + rv1[6] + rv1[11] + rv1[16] ) +end) + +registerFunction("transpose", "xm4", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[5], rv1[9], rv1[13], + rv1[2], rv1[6], rv1[10], rv1[14], + rv1[3], rv1[7], rv1[11], rv1[15], + rv1[4], rv1[8], rv1[12], rv1[16] } +end) + +// find the inverse for a standard affine transformation matix +registerFunction("inverseA", "xm4", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local t1 = rv1[1] * rv1[4] + rv1[5] * rv1[8] + rv1[9] * rv1[12] + local t2 = rv1[2] * rv1[4] + rv1[6] * rv1[8] + rv1[10] * rv1[12] + local t3 = rv1[3] * rv1[4] + rv1[7] * rv1[8] + rv1[11] * rv1[12] + return { rv1[1], rv1[5], rv1[9], -t1, + rv1[2], rv1[6], rv1[10], -t2, + rv1[3], rv1[7], rv1[11], -t3, + 0, 0, 0, 1 } +end) + +/******************************************************************************/ +// Extra functions + +registerFunction("matrix4", "e", "xm4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then + return { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 } + end + local factor = 10000 + local pos = rv1:GetPos() + local x = rv1:LocalToWorld(Vector(factor,0,0)) - pos + local y = rv1:LocalToWorld(Vector(0,factor,0)) - pos + local z = rv1:LocalToWorld(Vector(0,0,factor)) - pos + return { x.x/factor, y.x/factor, z.x/factor, pos.x, + x.y/factor, y.y/factor, z.y/factor, pos.y, + x.z/factor, y.z/factor, z.z/factor, pos.z, + 0, 0, 0, 1 } +end) + +registerFunction("x", "xm4:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[5], rv1[9] } +end) + +registerFunction("y", "xm4:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[2], rv1[6], rv1[10] } +end) + +registerFunction("z", "xm4:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[3], rv1[7], rv1[11] } +end) + +registerFunction("pos", "xm4:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[4], rv1[8], rv1[12] } +end) + +--- Returns a 3x3 reference frame matrix as described by the angle . Multiplying by this matrix will be the same as rotating by the given angle. +e2function matrix matrix(angle ang) + ang = Angle(ang[1], ang[2], ang[3]) + local x = ang:Forward() + local y = ang:Right() * -1 + local z = ang:Up() + return { + x.x, y.x, z.x, + x.y, y.y, z.y, + x.z, y.z, z.z + } +end + +--- Returns a 4x4 reference frame matrix as described by the angle . Multiplying by this matrix will be the same as rotating by the given angle. +e2function matrix4 matrix4(angle ang) + ang = Angle(ang[1], ang[2], ang[3]) + local x = ang:Forward() + local y = ang:Right() * -1 + local z = ang:Up() + return { + x.x, y.x, z.x, 0, + x.y, y.y, z.y, 0, + x.z, y.z, z.z, 0, + 0, 0, 0, 1 + } +end + +--- Returns a 4x4 reference frame matrix as described by the angle and the position . Multiplying by this matrix will be the same as rotating by the given angle and offsetting by the given vector. +e2function matrix4 matrix4(angle ang, vector pos) + ang = Angle(ang[1], ang[2], ang[3]) + local x = ang:Forward() + local y = ang:Right() * -1 + local z = ang:Up() + return { + x.x, y.x, z.x, pos[1], + x.y, y.y, z.y, pos[2], + x.z, y.z, z.z, pos[3], + 0, 0, 0, 1 + } +end + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/npc.lua b/lua/entities/gmod_wire_expression2/core/npc.lua new file mode 100644 index 0000000000..9dc86d8f1d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/npc.lua @@ -0,0 +1,158 @@ +/******************************************************************************\ + NPC control and such +\******************************************************************************/ + +__e2setcost(5) -- temporary + +function validNPC(entity) + return validEntity(entity) && entity:IsNPC() +end + +registerFunction("npcGoWalk", "e:v", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + rv1:SetLastPosition( Vector(rv2[1], rv2[2], rv2[3]) ) + rv1:SetSchedule( SCHED_FORCED_GO ) +end) + +registerFunction("npcGoRun", "e:v", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + rv1:SetLastPosition( Vector(rv2[1], rv2[2], rv2[3]) ) + rv1:SetSchedule( SCHED_FORCED_GO_RUN ) +end) + +registerFunction("npcAttack", "e:", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + rv1:SetSchedule( SCHED_MELEE_ATTACK1 ) +end) + +registerFunction("npcShoot", "e:", "", function(self,args) + local op1= args[2] + local rv1 = op1[1](self,op1) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + if !rv1:HasCondition( COND_NO_WEAPON ) then return end + rv1:SetSchedule( SCHED_RANGE_ATTACK1 ) +end) + +registerFunction("npcFace", "e:v", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + local Vec = Vector(rv2[1], rv2[2], rv2[3]) - self.entity:GetPos() + local ang = Vec:Angle() + rv1:SetAngles( Angle(0,ang.y,0) ) +end) + +registerFunction("npcGiveWeapon", "e:", "", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + rv1:Give( "ai_weapon_smg1" ) +end) + +registerFunction("npcGiveWeapon", "e:s", "", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + rv1:Give( "ai_weapon_" .. rv2 ) +end) + +registerFunction("npcStop", "e:", "", function(self,args) + local op1= args[2] + local rv1 = op1[1](self,op1) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + rv1:SetSchedule( SCHED_NONE ) +end) + + + + +//--Relationship functions--// + +// Disposition: 0 - Error, 1 - hate, 2 - fear, 3 - like, 4 - neutral + +local function NpcDisp(string) + if(string == "hate") then return 1 end + if(string == "fear") then return 2 end + if(string == "like") then return 3 end + if(string == "neutral") then return 4 end + return 0 +end + +local function DispToString(number) + if(number == 1) then return "hate" end + if(number == 2) then return "fear" end + if(number == 3) then return "like" end + if(number == 4) then return "neutral" end + return 0 +end + +local function NpcDispString(string) + if(string == "hate") then return "D_HT" end + if(string == "fear") then return "D_FR" end + if(string == "like") then return "D_LI" end + if(string == "neutral") then return "D_NU" end + return "D_ER" +end + +registerFunction("npcRelationship", "e:esn", "", function(self,args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + if !validNPC(rv1) || !validEntity(rv2) || !isOwner(self,rv1) then return end + local entity = rv1 + local target = rv2 + local disp = NpcDisp(rv3) + local prior = rv4 + if disp == 0 then return end + entity:AddEntityRelationship( target, disp, prior ) +end) + +registerFunction("npcRelationship", "e:ssn", "", function(self,args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + if !validNPC(rv1) || !isOwner(self,rv1) then return end + local entity = rv1 + local target = rv2 + local disp = NpcDispString(rv3) + local prior = math.floor( rv4 / 10 ) + local input = target.." "..disp.." "..tostring(prior) + if disp == "D_ER" then return end + entity:AddRelationship( input ) +end) + +registerFunction("npcRelationshipByOwner", "e:esn", "n", function(self,args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + if !validNPC(rv1) || !validEntity(rv2) || !isOwner(self,rv1) then return 0 end + local entity = rv1 + local owner = rv2 + local disp = NpcDisp(rv3) + local prior = rv4 + if disp == 0 then return 0 end + local Table = ents.FindByClass("npc_*") + if(table.Count(Table)==0) then return 0 end + + for i=1,table.Count(Table) do + if(isOwner(self, Table[i])) then entity:AddEntityRelationship( Table[i], disp, prior ) end + end + + return table.Count(Table) +end) + +registerFunction("npcDisp", "e:e", "s", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self,op1), op2[1](self,op2) + if !validNPC(rv1) || !validEntity(rv2) || !isOwner(self,rv1) then return "" end + local entity = rv1 + local target = rv2 + local disp = entity:Disposition( target ) + if disp == 0 then return "" end + return DispToString(disp) +end) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/number.lua b/lua/entities/gmod_wire_expression2/core/number.lua new file mode 100644 index 0000000000..0139b76dc2 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/number.lua @@ -0,0 +1,582 @@ +-- these upvalues (locals in an enclosing scope) are faster to access than globals. +local delta = wire_expression2_delta + +local math = math +local random = math.random +local pi = math.pi + +local exp = math.exp +local log = math.log +local log10 = math.log10 +local sqrt = math.sqrt + +local floor = math.floor +local ceil = math.ceil + +local sin = math.sin +local cos = math.cos +local tan = math.tan + +local acos = math.acos +local asin = math.asin +local atan = math.atan +local atan2 = math.atan2 + +local sinh = math.sinh +local cosh = math.cosh +local tanh = math.tanh + + +--[[************************************************************************]]-- +-- Numeric support +--[[************************************************************************]]-- + +registerType("normal", "n", 0, + nil, + nil, + function(retval) + if type(retval) ~= "number" then error("Return value is not a table, but a "..type(retval).."!",0) end + end +) + +E2Lib.registerConstant("PI", math.pi) +E2Lib.registerConstant("E", math.exp(1)) +E2Lib.registerConstant("PHI", (1+math.sqrt(5))/2) + +--[[************************************************************************]]-- + +__e2setcost(2) + +registerOperator("ass", "n", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +__e2setcost(1.5) + +registerOperator("inc", "n", "", function(self, args) + local op1 = args[2] + self.vars[op1] = self.vars[op1] + 1 + self.vclk[op1] = true +end) + +registerOperator("dec", "n", "", function(self, args) + local op1 = args[2] + self.vars[op1] = self.vars[op1] - 1 + self.vclk[op1] = true +end) + +--[[************************************************************************]]-- + +__e2setcost(1.5) + +registerOperator("eq", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rvd = op1[1](self, op1) - op2[1](self, op2) + if rvd <= delta && -rvd <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rvd = op1[1](self, op1) - op2[1](self, op2) + if rvd > delta || -rvd > delta + then return 1 else return 0 end +end) + +__e2setcost(1.25) + +registerOperator("geq", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rvd = op1[1](self, op1) - op2[1](self, op2) + if -rvd <= delta + then return 1 else return 0 end +end) + +registerOperator("leq", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rvd = op1[1](self, op1) - op2[1](self, op2) + if rvd <= delta + then return 1 else return 0 end +end) + +registerOperator("gth", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rvd = op1[1](self, op1) - op2[1](self, op2) + if rvd > delta + then return 1 else return 0 end +end) + +registerOperator("lth", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rvd = op1[1](self, op1) - op2[1](self, op2) + if -rvd > delta + then return 1 else return 0 end +end) + +--[[************************************************************************]]-- + +__e2setcost(5) + +registerOperator("dlt", "n", "n", function(self, args) + local op1 = args[2] + return self.vars[op1] - self.vars["$" .. op1] +end) + +__e2setcost(0.5) -- approximation + +registerOperator("neg", "n", "n", function(self, args) + local op1 = args[2] + return -op1[1](self, op1) +end) + +__e2setcost(1) + +registerOperator("add", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) + op2[1](self, op2) +end) + +registerOperator("sub", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) - op2[1](self, op2) +end) + +registerOperator("mul", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) * op2[1](self, op2) +end) + +registerOperator("div", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) / op2[1](self, op2) +end) + +registerOperator("exp", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) ^ op2[1](self, op2) +end) + +registerOperator("mod", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) % op2[1](self, op2) +end) + +--[[************************************************************************]]-- +-- TODO: select, average +-- TODO: is the shifting correct for rounding arbitrary decimals? + +__e2setcost(1) + +registerFunction("min", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 < rv2 then return rv1 else return rv2 end +end) + +registerFunction("min", "nnn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local val + if rv1 < rv2 then val = rv1 else val = rv2 end + if rv3 < val then return rv3 else return val end +end) + +registerFunction("min", "nnnn", "n", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local val + if rv1 < rv2 then val = rv1 else val = rv2 end + if rv3 < val then val = rv3 end + if rv4 < val then return rv4 else return val end +end) + +registerFunction("max", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 > rv2 then return rv1 else return rv2 end +end) + +registerFunction("max", "nnn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local val + if rv1 > rv2 then val = rv1 else val = rv2 end + if rv3 > val then return rv3 else return val end +end) + +registerFunction("max", "nnnn", "n", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local val + if rv1 > rv2 then val = rv1 else val = rv2 end + if rv3 > val then val = rv3 end + if rv4 > val then return rv4 else return val end +end) + +--[[************************************************************************]]-- + +__e2setcost(2) -- approximation + +registerFunction("abs", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 >= 0 then return rv1 else return -rv1 end +end) + +--- rounds towards +inf +e2function number ceil(rv1) + return rv1 - rv1 % -1 +end + +registerFunction("ceil", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + return rv1 - ((rv1 * shf) % -1) / shf +end) + +--- rounds towards -inf +e2function number floor(rv1) + return rv1 - rv1 % 1 +end + +registerFunction("floor", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + return rv1 - ((rv1 * shf) % 1) / shf +end) + +--- rounds to the nearest integer +e2function number round(rv1) + return rv1 - (rv1 + 0.5) % 1 + 0.5 +end + +registerFunction("round", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + return rv1 - ((rv1 * shf + 0.5) % 1 + 0.5) / shf +end) + +--- rounds towards zero +e2function number int(rv1) + if rv1 >= 0 then return rv1 - rv1 % 1 else return rv1 - rv1 % -1 end +end + +--- returns the fractional part. (frac(-1.5) == 0.5 & frac(3.2) == 0.2) +e2function number frac(rv1) + if rv1 >= 0 then return rv1 % 1 else return rv1 % -1 end +end + +-- TODO: what happens with negative modulo? +registerFunction("mod", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 >= 0 then return rv1 % rv2 else return rv1 % -rv2 end +end) + +-- TODO: change to a more suitable name? (cyclic modulo?) +-- add helpers for wrap90 wrap180, wrap90r wrap180r? or pointless? +-- wrap90(Pitch), wrap(Pitch, 90) +-- should be added... + +registerFunction("wrap", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return (rv1 + rv2) % (rv2 * 2) - rv2 +end) + +registerFunction("clamp", "nnn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if rv1 < rv2 then return rv2 elseif rv1 > rv3 then return rv3 else return rv1 end +end) + +--- Returns 1 if is in the interval [; ], 0 otherwise. +e2function number inrange(value, min, max) + if value < min then return 0 end + if value > max then return 0 end + return 1 +end + +registerFunction("sign", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 > delta then return 1 + elseif rv1 < -delta then return -1 + else return 0 end +end) + +--[[************************************************************************]]-- + +__e2setcost(2) -- approximation + +registerFunction("random", "", "n", function(self, args) + return random() +end) + +registerFunction("random", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return random() * rv1 +end) + +registerFunction("random", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1 + random() * (rv2 - rv1) +end) + +registerFunction("randint", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return random(rv1) +end) + +registerFunction("randint", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return random(rv1, rv2) +end) + +--[[************************************************************************]]-- + +__e2setcost(2) -- approximation + +registerFunction("sqrt", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1 ^ (1 / 2) +end) + +registerFunction("cbrt", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1 ^ (1 / 3) +end) + +registerFunction("root", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1 ^ (1 / rv2) +end) + +local const_e = exp(1) +registerFunction("e", "", "n", function(self, args) + return const_e +end) + +registerFunction("exp", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return exp(rv1) +end) + +registerFunction("ln", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return log(rv1) +end) + +local const_invlog2 = 1 / log(2) +registerFunction("log2", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return log(rv1) * const_invlog2 +end) + +registerFunction("log10", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return log10(rv1) +end) + +registerFunction("log", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return log(rv1) / log(rv2) +end) + +--[[************************************************************************]]-- + +__e2setcost(2) -- approximation + +local deg2rad = pi / 180 +local rad2deg = 180 / pi + +registerFunction("pi", "", "n", function(self, args) + return pi +end) + +registerFunction("toRad", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1 * deg2rad +end) + +registerFunction("toDeg", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1 * rad2deg +end) + +registerFunction("acos", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return acos(rv1) * rad2deg +end) + +registerFunction("asin", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return asin(rv1) * rad2deg +end) + +registerFunction("atan", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return atan(rv1) * rad2deg +end) + +registerFunction("atan", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return atan2(rv1, rv2) * rad2deg +end) + +registerFunction("cos", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return cos(rv1 * deg2rad) +end) + +registerFunction("sin", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return sin(rv1 * deg2rad) +end) + +registerFunction("tan", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return tan(rv1 * deg2rad) +end) + +registerFunction("cosh", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return cosh(rv1) +end) + +registerFunction("sinh", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return sinh(rv1) +end) + +registerFunction("tanh", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return tanh(rv1) +end) + +registerFunction("acosr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return acos(rv1) +end) + +registerFunction("asinr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return asin(rv1) +end) + +registerFunction("atanr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return atan(rv1) +end) + +registerFunction("atanr", "nn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return atan2(rv1, rv2) +end) + +registerFunction("cosr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return cos(rv1) +end) + +registerFunction("sinr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return sin(rv1) +end) + +registerFunction("tanr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return tan(rv1) +end) + +registerFunction("coshr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return cosh(rv1) +end) + +registerFunction("sinhr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return sinh(rv1) +end) + +registerFunction("tanhr", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return tanh(rv1) +end) + +--[[************************************************************************]]-- + +__e2setcost(15) -- approximation + +e2function string toString(number number) + return tostring(number) +end + +e2function string number:toString() + return tostring(this) +end + +__e2setcost(25) -- approximation + +local function tobase(number, base) + local ret = "" + if base < 2 or base > 36 or number == 0 then return "0" end + if base == 10 then return tostring(number) end + local chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + while number > 0 do + number, d = math.floor(number/base),(number%base)+1 + ret = string.sub(chars,d,d)..ret + end + return ret +end + +e2function string toString(number number, number base) + return tobase(number, base) +end + +e2function string number:toString(number base) + return tobase(this, base) +end + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/player.lua b/lua/entities/gmod_wire_expression2/core/player.lua new file mode 100644 index 0000000000..dc50c1ff9b --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/player.lua @@ -0,0 +1,235 @@ +/******************************************************************************\ + Player-Entity support +\******************************************************************************/ + +local validEntity = E2Lib.validEntity +local isOwner = E2Lib.isOwner +registerCallback("e2lib_replace_function", function(funcname, func, oldfunc) + if funcname == "isOwner" then + isOwner = func + elseif funcname == "validEntity" then + validEntity = func + end +end) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +registerFunction("isAdmin", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if not validEntity(rv1) then return 0 end + if not rv1:IsPlayer() then return 0 end + if rv1:IsAdmin() then return 1 else return 0 end +end) + +registerFunction("isSuperAdmin", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if not validEntity(rv1) then return 0 end + if not rv1:IsPlayer() then return 0 end + if rv1:IsSuperAdmin() then return 1 else return 0 end +end) + +/******************************************************************************/ + +registerFunction("shootPos", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return {0,0,0} end + if(rv1:IsPlayer() or rv1:IsNPC()) then + return rv1:GetShootPos() + else return {0,0,0} end +end) + +registerFunction("eye", "e:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if (!validEntity(rv1)) then return {0,0,0} end + if (rv1:IsPlayer()) then + return rv1:GetAimVector() + else + return rv1:GetForward() + end +end) + +--- Returns a local angle describing player 's view angles. +e2function angle entity:eyeAngles() + if not validEntity(this) then return { 0, 0, 0} end + local ang = this:EyeAngles() + return { ang.p, ang.y, ang.r } +end + +/******************************************************************************/ + +registerFunction("name", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return "" end + if(!rv1:IsPlayer()) then return "" end + return rv1:Name() +end) + +registerFunction("steamID", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return "" end + if(!rv1:IsPlayer()) then return "" end + return rv1:SteamID() +end) + +registerFunction("armor", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer() or rv1:IsNPC()) then return rv1:Armor() else return 0 end +end) + +/******************************************************************************/ + +registerFunction("height", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer() or rv1:IsNPC()) then + local pos = rv1:GetPos() + local up = rv1:GetUp() + return rv1:NearestPoint(Vector(pos.x+up.x*100,pos.y+up.y*100,pos.z+up.z*100)).z-rv1:NearestPoint(Vector(pos.x-up.x*100,pos.y-up.y*100,pos.z-up.z*100)).z + else return 0 end +end) + +registerFunction("width", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer() or rv1:IsNPC()) then + local pos = rv1:GetPos() + local right = rv1:GetRight() + return rv1:NearestPoint(Vector(pos.x+right.x*100,pos.y+right.y*100,pos.z+right.z*100)).z-rv1:NearestPoint(Vector(pos.x-right.x*100,pos.y-right.y*100,pos.z-right.z*100)).z + else return 0 end +end) + +/******************************************************************************/ + +registerFunction("isCrouch", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer() and rv1:Crouching()) then return 1 else return 0 end +end) + +registerFunction("isAlive", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer() and rv1:Alive()) then return 1 end + if(rv1:IsNPC() and rv1:Health() > 0) then return 1 end + return 0 +end) + +/******************************************************************************/ + +registerFunction("frags", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer()) then return rv1:Frags() else return 0 end +end) + +registerFunction("deaths", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!rv1 or !rv1:IsValid()) then return 0 end + if(rv1:IsPlayer()) then return rv1:Deaths() else return 0 end +end) + +/******************************************************************************/ + +registerFunction("team", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer()) then return rv1:Team() else return 0 end +end) + +registerFunction("teamName", "n", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local str = team.GetName(rv1) + if str == nil then return "" end + return str +end) + +registerFunction("teamScore", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.GetScore(rv1) +end) + +registerFunction("teamPlayers", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.NumPlayers(rv1) +end) + +registerFunction("teamDeaths", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.TotalDeaths(rv1) +end) + +registerFunction("teamFrags", "n", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return team.TotalFrags(rv1) +end) + +e2function vector teamColor(index) + local col = team.GetColor(index) + return { col.r, col.g, col.b } +end + +e2function array teams() + local team_indexes = {} + for index,_ in pairs(team.GetAllTeams()) do + team_indexes[#team_indexes+1] = index + end + table.sort(team_indexes) + return team_indexes +end + +registerFunction("ping", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if not validEntity(rv1) then return 0 end + if(rv1:IsPlayer()) then return rv1:Ping() else return 0 end +end) + +/******************************************************************************/ + +registerFunction("keyAttack1", "e:", "n", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if not validEntity(rv1) then return 0 end + if rv1:IsPlayer() and rv1:KeyDown(1) then return 1 end -- IN_ATTACK + return 0 +end) + +registerFunction("keyAttack2", "e:", "n", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if not validEntity(rv1) then return 0 end + if rv1:IsPlayer() and rv1:KeyDown(2048) then return 1 end -- IN_ATTACK2 + return 0 +end) + +registerFunction("keyUse", "e:", "n", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self,op1) + if not validEntity(rv1) then return 0 end + if rv1:IsPlayer() and rv1:KeyDown(32) then return 1 end -- IN_USE + return 0 +end) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/quaternion.lua b/lua/entities/gmod_wire_expression2/core/quaternion.lua new file mode 100644 index 0000000000..edc8170113 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/quaternion.lua @@ -0,0 +1,598 @@ +/******************************************************************************\ + Quaternion support +\******************************************************************************/ + +// TODO: implement more! + +-- faster access to some math library functions +local abs = math.abs +local Round = math.Round +local sqrt = math.sqrt +local exp = math.exp +local log = math.log +local sin = math.sin +local cos = math.cos +local sinh = math.sinh +local cosh = math.cosh +local acos = math.acos + +local deg2rad = math.pi/180 +local rad2deg = 180/math.pi + +registerType("quaternion", "q", { 0, 0, 0, 0 }, + function(self, input) return { input[1], input[2], input[3], input[4] } end, + function(self, output) return output end +) + +local function format(value) + local r,i,j,k,dbginfo + + r = "" + i = "" + j = "" + k = "" + + if abs(value[1]) > 0.0005 then + r = Round(value[1]*1000)/1000 + end + dbginfo = r + if abs(value[2]) > 0.0005 then + i = tostring(Round(value[2]*1000)/1000) + if string.sub(i,1,1)!="-" and dbginfo != "" then i = "+"..i end + i = i .. "i" + end + dbginfo = dbginfo .. i + if abs(value[3]) > 0.0005 then + j = tostring(Round(value[3]*1000)/1000) + if string.sub(j,1,1)!="-" and dbginfo != "" then j = "+"..j end + j = j .. "j" + end + dbginfo = dbginfo .. j + if abs(value[4]) > 0.0005 then + k = tostring(Round(value[4]*1000)/1000) + if string.sub(k,1,1)!="-" and dbginfo != "" then k = "+"..k end + k = k .. "k" + end + dbginfo = dbginfo .. k + if dbginfo == "" then dbginfo = "0" end + return dbginfo +end + +WireLib.registerDebuggerFormat("QUATERNION", format) + +/****************************** Helper functions ******************************/ + +local function qmul(lhs, rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + return { + lhs1 * rhs1 - lhs2 * rhs2 - lhs3 * rhs3 - lhs4 * rhs4, + lhs1 * rhs2 + lhs2 * rhs1 + lhs3 * rhs4 - lhs4 * rhs3, + lhs1 * rhs3 + lhs3 * rhs1 + lhs4 * rhs2 - lhs2 * rhs4, + lhs1 * rhs4 + lhs4 * rhs1 + lhs2 * rhs3 - lhs3 * rhs2 + } +end + +local function qexp(q) + local m = sqrt(q[2]*q[2] + q[3]*q[3] + q[4]*q[4]) + local u + if m ~= 0 then + u = { q[2]*sin(m)/m, q[3]*sin(m)/m, q[4]*sin(m)/m } + else + u = { 0, 0, 0 } + end + local r = exp(q[1]) + return { r*cos(m), r*u[1], r*u[2], r*u[3] } +end + +local function qlog(q) + local l = sqrt(q[1]*q[1] + q[2]*q[2] + q[3]*q[3] + q[4]*q[4]) + if l == 0 then return { 0, 0, 0, 0 } end + local u = { q[1]/l, q[2]/l, q[3]/l, q[4]/l } + local a = acos(u[1]) + local m = sqrt(u[2]*u[2] + u[3]*u[3] + u[4]*u[4]) + return { log(l), a*u[2]/m, a*u[3]/m, a*u[4]/m } +end + +/******************************************************************************/ + +--- Creates a zero quaternion +e2function quaternion quat() + return { 0, 0, 0, 0 } +end + +--- Creates a quaternion with real part equal to +e2function quaternion quat(real) + return { real, 0, 0, 0 } +end + +--- Creates a quaternion with real and "i" parts equal to +e2function quaternion quat(complex c) + return { c[1], c[2], 0, 0 } +end + +--- Converts a vector to a quaternion (returns .x*i + .y*j + .z*k) +e2function quaternion quat(vector imag) + return { 0, imag[1], imag[2], imag[3] } +end + +--- Returns +i+j+k +e2function quaternion quat(real, i, j, k) + return { real, i, j, k } +end + +--- Converts to a quaternion +e2function quaternion quat(angle ang) + local p, y, r = ang[1], ang[2], ang[3] + p = p*deg2rad*0.5 + y = y*deg2rad*0.5 + r = r*deg2rad*0.5 + local qr = {cos(r), sin(r), 0, 0} + local qp = {cos(p), 0, sin(p), 0} + local qy = {cos(y), 0, 0, sin(y)} + return qmul(qy,qmul(qp,qr)) +end + + + +--- Converts angle of to a quaternion +e2function quaternion quat(entity ent) + if(!validEntity(ent)) then + return { 0, 0, 0, 0 } + end + local ang = ent:GetAngles() + local p, y, r = ang.p, ang.y, ang.r + p = p*deg2rad*0.5 + y = y*deg2rad*0.5 + r = r*deg2rad*0.5 + local qr = {cos(r), sin(r), 0, 0} + local qp = {cos(p), 0, sin(p), 0} + local qy = {cos(y), 0, 0, sin(y)} + return qmul(qy,qmul(qp,qr)) +end + +--- Returns quaternion i +e2function quaternion qi() + return {0, 1, 0, 0} +end + +--- Returns quaternion *i +e2function quaternion qi(n) + return {0, n, 0, 0} +end + +--- Returns j +e2function quaternion qj() + return {0, 0, 1, 0} +end + +--- Returns *j +e2function quaternion qj(n) + return {0, 0, n, 0} +end + +--- Returns k +e2function quaternion qk() + return {0, 0, 0, 1} +end + +--- Returns *k +e2function quaternion qk(n) + return {0, 0, 0, n} +end + +/******************************************************************************/ + +e2function quaternion operator=(quaternion lhs, quaternion rhs) + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +/******************************************************************************/ +// TODO: define division as multiplication with (1/x), or is it not useful? + +e2function quaternion operator_neg(quaternion q) + return { -q[1], -q[2], -q[3], -q[4] } +end + +e2function quaternion operator+(quaternion lhs, quaternion rhs) + return { lhs[1] + rhs[1], lhs[2] + rhs[2], lhs[3] + rhs[3], lhs[4] + rhs[4] } +end + +e2function quaternion operator+(number lhs, quaternion rhs) + return { lhs + rhs[1], rhs[2], rhs[3], rhs[4] } +end + +e2function quaternion operator+(quaternion lhs, number rhs) + return { lhs[1] + rhs, lhs[2], lhs[3], lhs[4] } +end + +e2function quaternion operator+(complex lhs, quaternion rhs) + return { lhs[1] + rhs[1], lhs[2] + rhs[2], rhs[3], rhs[4] } +end + +e2function quaternion operator+(quaternion lhs, complex rhs) + return { lhs[1] + rhs[1], lhs[2] + rhs[2], lhs[3], lhs[4] } +end + +e2function quaternion operator-(quaternion lhs, quaternion rhs) + return { lhs[1] - rhs[1], lhs[2] - rhs[2], lhs[3] - rhs[3], lhs[4] - rhs[4] } +end + +e2function quaternion operator-(number lhs, quaternion rhs) + return { lhs - rhs[1], -rhs[2], -rhs[3], -rhs[4] } +end + +e2function quaternion operator-(quaternion lhs, number rhs) + return { lhs[1] - rhs, lhs[2], lhs[3], lhs[4] } +end + +e2function quaternion operator-(complex lhs, quaternion rhs) + return { lhs[1] - rhs[1], lhs[2] - rhs[2], -rhs[3], -rhs[4] } +end + +e2function quaternion operator-(quaternion lhs, complex rhs) + return { lhs[1] - rhs[1], lhs[2] - rhs[2], lhs[3], lhs[4] } +end + +e2function quaternion operator*(lhs, quaternion rhs) + return { lhs * rhs[1], lhs * rhs[2], lhs * rhs[3], lhs * rhs[4] } +end + +e2function quaternion operator*(quaternion lhs, rhs) + return { lhs[1] * rhs, lhs[2] * rhs, lhs[3] * rhs, lhs[4] * rhs } +end + +e2function quaternion operator*(complex lhs, quaternion rhs) + local lhs1, lhs2 = lhs[1], lhs[2] + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + return { + lhs1 * rhs1 - lhs2 * rhs2, + lhs1 * rhs2 + lhs2 * rhs1, + lhs1 * rhs3 - lhs2 * rhs4, + lhs1 * rhs4 + lhs2 * rhs3 + } +end + +e2function quaternion operator*(quaternion lhs, complex rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + local rhs1, rhs2 = rhs[1], rhs[2] + return { + lhs1 * rhs1 - lhs2 * rhs2, + lhs1 * rhs2 + lhs2 * rhs1, + lhs3 * rhs1 + lhs4 * rhs2, + lhs4 * rhs1 - lhs3 * rhs2 + } +end + +e2function quaternion operator*(quaternion lhs, quaternion rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + return { + lhs1 * rhs1 - lhs2 * rhs2 - lhs3 * rhs3 - lhs4 * rhs4, + lhs1 * rhs2 + lhs2 * rhs1 + lhs3 * rhs4 - lhs4 * rhs3, + lhs1 * rhs3 + lhs3 * rhs1 + lhs4 * rhs2 - lhs2 * rhs4, + lhs1 * rhs4 + lhs4 * rhs1 + lhs2 * rhs3 - lhs3 * rhs2 + } +end + +e2function quaternion operator*(quaternion lhs, vector rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + local rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3] + return { + -lhs2 * rhs2 - lhs3 * rhs3 - lhs4 * rhs4, + lhs1 * rhs2 + lhs3 * rhs4 - lhs4 * rhs3, + lhs1 * rhs3 + lhs4 * rhs2 - lhs2 * rhs4, + lhs1 * rhs4 + lhs2 * rhs3 - lhs3 * rhs2 + } +end + +e2function quaternion operator*(vector lhs, quaternion rhs) + local lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3] + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + return { + -lhs2 * rhs2 - lhs3 * rhs3 - lhs4 * rhs4, + lhs2 * rhs1 + lhs3 * rhs4 - lhs4 * rhs3, + lhs3 * rhs1 + lhs4 * rhs2 - lhs2 * rhs4, + lhs4 * rhs1 + lhs2 * rhs3 - lhs3 * rhs2 + } +end + +e2function quaternion operator/(quaternion lhs, number rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + return { + lhs1/rhs, + lhs2/rhs, + lhs3/rhs, + lhs4/rhs + } +end + +e2function quaternion operator/(number lhs, quaternion rhs) + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + local l = rhs1*rhs1 + rhs2*rhs2 + rhs3*rhs3 + rhs4*rhs4 + return { + ( lhs * rhs1)/l, + (-lhs * rhs2)/l, + (-lhs * rhs3)/l, + (-lhs * rhs4)/l + } +end + +e2function quaternion operator/(quaternion lhs, complex rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + local rhs1, rhs2 = rhs[1], rhs[2] + local l = rhs1*rhs1 + rhs2*rhs2 + return { + ( lhs1 * rhs1 + lhs2 * rhs2)/l, + (-lhs1 * rhs2 + lhs2 * rhs1)/l, + ( lhs3 * rhs1 - lhs4 * rhs2)/l, + ( lhs4 * rhs1 + lhs3 * rhs2)/l + } +end + +e2function quaternion operator/(complex lhs, quaternion rhs) + local lhs1, lhs2 = lhs[1], lhs[2] + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + local l = rhs1*rhs1 + rhs2*rhs2 + rhs3*rhs3 + rhs4*rhs4 + return { + ( lhs1 * rhs1 + lhs2 * rhs2)/l, + (-lhs1 * rhs2 + lhs2 * rhs1)/l, + (-lhs1 * rhs3 + lhs2 * rhs4)/l, + (-lhs1 * rhs4 - lhs2 * rhs3)/l + } +end + +e2function quaternion operator/(quaternion lhs, quaternion rhs) + local lhs1, lhs2, lhs3, lhs4 = lhs[1], lhs[2], lhs[3], lhs[4] + local rhs1, rhs2, rhs3, rhs4 = rhs[1], rhs[2], rhs[3], rhs[4] + local l = rhs1*rhs1 + rhs2*rhs2 + rhs3*rhs3 + rhs4*rhs4 + return { + ( lhs1 * rhs1 + lhs2 * rhs2 + lhs3 * rhs3 + lhs4 * rhs4)/l, + (-lhs1 * rhs2 + lhs2 * rhs1 - lhs3 * rhs4 + lhs4 * rhs3)/l, + (-lhs1 * rhs3 + lhs3 * rhs1 - lhs4 * rhs2 + lhs2 * rhs4)/l, + (-lhs1 * rhs4 + lhs4 * rhs1 - lhs2 * rhs3 + lhs3 * rhs2)/l + } +end + +e2function quaternion operator^(number lhs, quaternion rhs) + if lhs == 0 then return { 0, 0, 0, 0 } end + local l = log(lhs) + return qexp({ l*rhs[1], l*rhs[2], l*rhs[3], l*rhs[4] }) +end + +e2function quaternion operator^(quaternion lhs, number rhs) + local l = qlog(lhs) + return qexp({ l[1]*rhs, l[2]*rhs, l[3]*rhs, l[4]*rhs }) +end + +/******************************************************************************/ + +e2function number operator==(quaternion lhs, quaternion rhs) + local rvd1, rvd2, rvd3, rvd4 = lhs[1] - rhs[1], lhs[2] - rhs[2], lhs[3] - rhs[3], lhs[4] - rhs[4] + if rvd1 <= delta && rvd1 >= -delta && + rvd2 <= delta && rvd2 >= -delta && + rvd3 <= delta && rvd3 >= -delta && + rvd4 <= delta && rvd4 >= -delta + then return 1 else return 0 end +end + +e2function number operator!=(quaternion lhs, quaternion rhs) + local rvd1, rvd2, rvd3, rvd4 = lhs[1] - rhs[1], lhs[2] - rhs[2], lhs[3] - rhs[3], lhs[4] - rhs[4] + if rvd1 > delta || rvd1 < -delta || + rvd2 > delta || rvd2 < -delta || + rvd3 > delta || rvd3 < -delta || + rvd4 > delta || rvd4 < -delta + then return 1 else return 0 end +end + +/******************************************************************************/ + +--- Returns absolute value of +e2function number abs(quaternion q) + return sqrt(q[1]*q[1] + q[2]*q[2] + q[3]*q[3] + q[4]*q[4]) +end + +--- Returns the conjugate of +e2function quaternion conj(quaternion q) + return {q[1], -q[2], -q[3], -q[4]} +end + +--- Returns the inverse of +e2function quaternion inv(quaternion q) + local l = q[1]*q[1] + q[2]*q[2] + q[3]*q[3] + q[4]*q[4] + return { q[1]/l, -q[2]/l, -q[3]/l, -q[4]/l } +end + +--- Returns the real component of the quaternion +e2function number quaternion:real() + return this[1] +end + +--- Returns the i component of the quaternion +e2function number quaternion:i() + return this[2] +end + +--- Returns the j component of the quaternion +e2function number quaternion:j() + return this[3] +end + +--- Returns the k component of the quaternion +e2function number quaternion:k() + return this[4] +end + +/******************************************************************************/ + +--- Raises Euler's constant e to the power +e2function quaternion exp(quaternion q) + return qexp(q) +end + +--- Calculates natural logarithm of +e2function quaternion log(quaternion q) + return qlog(q) +end + +--- Changes quaternion so that the represented rotation is by an angle between 0 and 180 degrees (by coder0xff) +e2function quaternion qMod(quaternion q) + if q[1]<0 then return {-q[1], -q[2], -q[3], -q[4]} else return {q[1], q[2], q[3], q[4]} end +end + +--- Performs spherical linear interpolation between and . Returns for =0, for =1 +e2function quaternion slerp(quaternion q0, quaternion q1, number t) + local dot = q0[1]*q1[1] + q0[2]*q1[2] + q0[3]*q1[3] + q0[4]*q1[4] + local q11 + if dot<0 then + q11 = {-q1[1], -q1[2], -q1[3], -q1[4]} + else + q11 = { q1[1], q1[2], q1[3], q1[4] } -- dunno if just q11 = q1 works + end + + local l = q0[1]*q0[1] + q0[2]*q0[2] + q0[3]*q0[3] + q0[4]*q0[4] + if l==0 then return { 0, 0, 0, 0 } end + local invq0 = { q0[1]/l, -q0[2]/l, -q0[3]/l, -q0[4]/l } + local logq = qlog(qmul(invq0,q11)) + local q = qexp( { logq[1]*t, logq[2]*t, logq[3]*t, logq[4]*t } ) + return qmul(q0,q) +end + +/******************************************************************************/ + +--- Returns vector pointing forward for +e2function vector quaternion:forward() + local this1, this2, this3, this4 = this[1], this[2], this[3], this[4] + local t2, t3, t4 = this2 * 2, this3 * 2, this4 * 2 + return { + this1 * this1 + this2 * this2 - this3 * this3 - this4 * this4, + t3 * this2 + t4 * this1, + t4 * this2 - t3 * this1 + } +end + +--- Returns vector pointing right for +e2function vector quaternion:right() + local this1, this2, this3, this4 = this[1], this[2], this[3], this[4] + local t2, t3, t4 = this2 * 2, this3 * 2, this4 * 2 + return { + t4 * this1 - t2 * this3, + this2 * this2 - this1 * this1 + this4 * this4 - this3 * this3, + - t2 * this1 - t3 * this4 + } +end + +--- Returns vector pointing up for +e2function vector quaternion:up() + local this1, this2, this3, this4 = this[1], this[2], this[3], this[4] + local t2, t3, t4 = this2 * 2, this3 * 2, this4 * 2 + return { + t3 * this1 + t2 * this4, + t3 * this4 - t2 * this1, + this1 * this1 - this2 * this2 - this3 * this3 + this4 * this4 + } +end + +/******************************************************************************/ + +--- Returns quaternion for rotation about axis by angle +e2function quaternion qRotation(vector axis, ang) + local ax = Vector(axis[1], axis[2], axis[3]) + ax:Normalize() + local ang2 = ang*deg2rad*0.5 + return { cos(ang2), ax.x*sin(ang2), ax.y*sin(ang2), ax.z*sin(ang2) } +end + +--- Construct a quaternion from the rotation vector . Vector direction is axis of rotation, magnitude is angle in degress (by coder0xff) +e2function quaternion qRotation(vector rv1) + local angSquared = rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] + if angSquared == 0 then return { 1, 0, 0, 0 } end + local len = sqrt(angSquared) + local ang = (len + 180) % 360 - 180 + local ang2 = ang*deg2rad*0.5 + local sang2len = sin(ang2) / len + return { cos(ang2), rv1[1] * sang2len , rv1[2] * sang2len, rv1[3] * sang2len } +end + +--- Returns the angle of rotation in degrees (by coder0xff) +e2function number rotationAngle(quaternion q) + local l2 = q[1]*q[1] + q[2]*q[2] + q[3]*q[3] + q[4]*q[4] + if l2 == 0 then return 0 end + local l = sqrt(l2) + local ang = 2*acos(q[1]/l)*rad2deg //this returns angle from 0 to 360 + if ang > 180 then ang = ang - 360 end //make it -180 - 180 + return ang +end + +--- Returns the axis of rotation (by coder0xff) +e2function vector rotationAxis(quaternion q) + local m2 = q[2] * q[2] + q[3] * q[3] + q[4] * q[4] + if m2 == 0 then return { 0, 0, 1 } end + local m = sqrt(m2) + return { q[2] / m, q[3] / m, q[4] / m} +end + +--- Returns the rotation vector - rotation axis where magnitude is the angle of rotation in degress (by coder0xff) +e2function vector rotationVector(quaternion q) + local l2 = q[1]*q[1] + q[2]*q[2] + q[3]*q[3] + q[4]*q[4] + local m2 = q[2]*q[2] + q[3]*q[3] + q[4]*q[4] + if l2 == 0 or m2 == 0 then return { 0, 0, 0 } end + local s = 2 * acos(q[1]/sqrt(l2)) * rad2deg + if s > 180 then s = s - 360 end + s = s / sqrt(m2) + return { q[2] * s, q[3] * s, q[4] * s } +end + +/******************************************************************************/ + +--- Converts to a vector by dropping the real component +e2function vector vec(quaternion q) + return { q[2], q[3], q[4] } +end + +--- Converts to a transformation matrix +e2function matrix matrix(quaternion q) + local w,x,y,z = q[1],q[2],q[3],q[4] + return { + 1 - 2*y*y - 2*z*z , 2*x*y - 2*z*w , 2*x*z + 2*y*w, + 2*x*y + 2*z*w , 1 - 2*x*x - 2*z*z , 2*y*z - 2*x*w, + 2*x*z - 2*y*w , 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y + } +end + +--- Returns angle represented by +e2function angle quaternion:toAngle() + local l = sqrt(this[1]*this[1]+this[2]*this[2]+this[3]*this[3]+this[4]*this[4]) + local q1, q2, q3, q4 = this[1]/l, this[2]/l, this[3]/l, this[4]/l + + local x = Vector(q1*q1 + q2*q2 - q3*q3 - q4*q4, + 2*q3*q2 + 2*q4*q1, + 2*q4*q2 - 2*q3*q1) + + local y = Vector(2*q2*q3 - 2*q4*q1, + q1*q1 - q2*q2 + q3*q3 - q4*q4, + 2*q2*q1 + 2*q3*q4) + + local ang = x:Angle() + if ang.p > 180 then ang.p = ang.p - 360 end + if ang.y > 180 then ang.y = ang.y - 360 end + + local yyaw = Vector(0,1,0) + yyaw:Rotate(Angle(0,ang.y,0)) + + local roll = acos(y:DotProduct(yyaw))*rad2deg + + local dot = q2*q1 + q3*q4 + if dot < 0 then roll = -roll end + + return {ang.p, ang.y, roll} +end + +/******************************************************************************/ + +--- Formats as a string. +e2function string toString(quaternion q) + return format(q) +end diff --git a/lua/entities/gmod_wire_expression2/core/ranger.lua b/lua/entities/gmod_wire_expression2/core/ranger.lua new file mode 100644 index 0000000000..001c20280d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/ranger.lua @@ -0,0 +1,281 @@ +/******************************************************************************\ + Built-in Ranger support v1.71 +\******************************************************************************/ + +------------------- +-- Main function -- +------------------- + +local function ranger(self, rangertype, range, p1, p2) + local data = self.data + local chip = self.entity + local defaultzero = data.rangerdefaultzero + local ignoreworld = data.rangerignoreworld + local water = data.rangerwater + local entities = data.rangerentities + local filter = data.rangerfilter + data.rangerdefaultzero = false + data.rangerignoreworld = false + data.rangerwater = false + data.rangerentities = true + data.rangerfilter = {} + + -- begin building tracedata structure + table.insert(filter, chip) + local tracedata = { filter = filter } + if water then + if entities then + --(i)we + tracedata.mask = -1 + elseif ignoreworld then + --iw + tracedata.mask = MASK_WATER + ignoreworld = false + else + --w + tracedata.mask = MASK_WATER | CONTENTS_SOLID + end + elseif not entities then + if ignoreworld then + --i + tracedata.mask = 0 + ignoreworld = false + else + --no flags + tracedata.mask = MASK_NPCWORLDSTATIC + end + --else + --(i)e + end + + -- calculate startpos and endpos + if rangertype == 2 then + tracedata.start = Vector( p1[1], p1[2], p1[3] ) + tracedata.endpos = Vector( p2[1], p2[2], p2[3] ) + elseif rangertype == 3 then + tracedata.start = Vector( p1[1], p1[2], p1[3] ) + tracedata.endpos = tracedata.start + Vector( p2[1], p2[2], p2[3] ):Normalize()*range + else + tracedata.start = chip:GetPos() + + if rangertype == 1 && (p1!=0 || p2!=0) then + p1 = math.rad(p1) + p2 = math.rad(p2+270) + local zoff = -math.cos(p1)*range + local yoff = math.sin(p1)*range + local xoff = math.cos(p2)*zoff + zoff = math.sin(p2)*zoff + tracedata.endpos = chip:LocalToWorld(Vector(xoff,yoff,zoff)) + elseif rangertype == 0 && (p1!=0 || p2!=0) then + local skew = Vector(p2, -p1, 1) + tracedata.endpos = chip:LocalToWorld(skew:Normalize()*range) + else + tracedata.endpos = tracedata.start + chip:GetUp()*range + end + end + + -- do the trace + local trace = util.TraceLine(tracedata) + + -- handle some ranger settings + if ignoreworld and trace.HitWorld then + trace.HitPos = defaultzero and tracedata.start or tracedata.endpos + trace.Hit = false + elseif defaultzero and not trace.Hit then + trace.HitPos = tracedata.start + end + + return trace +end + +/******************************************************************************/ + +registerType("ranger", "xrd", nil, + nil, + nil, + function(retval) + if retval == nil then return end + if type(retval) ~= "table" then error("Return value is neither nil nor a table, but a "..type(retval).."!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(1) -- temporary + +--- RD = RD +e2function ranger operator=(ranger lhs, ranger rhs) + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +e2function number operator_is(ranger walker) + if walker then return 1 else return 0 end +end + +/******************************************************************************/ + +__e2setcost(1) -- temporary + +--- Resets all ranger flags and filters. +e2function void rangerReset() + self.data.rangerwater = false + self.data.rangerentities = true + self.data.rangerignoreworld = false + self.data.rangerdefaultzero = false + self.data.rangerfilter = {} +end + +local flaglookup = { + i = "rangerignoreworld", + w = "rangerwater", + e = "rangerentities", + z = "rangerdefaultzero", +} + +--- Returns the ranger flags as a string. +e2function string rangerFlags() + local ret = "" + for char,field in pairs(flaglookup) do + if self.data[field] then ret = ret .. char end + end + return ret +end + +--- Sets the ranger flags. can be any combination of I=ignore world, W=hit water, E=hit entities and Z=default to zero. +e2function void rangerFlags(string flags) + flags = flags:lower() + for char,field in pairs(flaglookup) do + self.data[field] = flags:find(char) and true or false + end +end + +--- Default is 0, if any other value is given it will hit water +e2function void rangerHitWater(hitwater) + self.data.rangerwater = hitwater ~= 0 +end + +--- Default is 1, if any other value is given it will hit entities +e2function void rangerHitEntities(hitentities) + self.data.rangerentities = hitentities ~= 0 +end + +--- Default is 0, if any other value is given it will ignore world +e2function void rangerIgnoreWorld(ignoreworld) + self.data.rangerignoreworld = ignoreworld ~= 0 +end + +--- If given any value other than 0 it will default the distance data to zero when nothing is hit +e2function void rangerDefaultZero(defaultzero) + self.data.rangerdefaultzero = defaultzero ~= 0 +end + +--- Feed entities you don't want the trace to hit +e2function void rangerFilter(entity ent) + if validEntity(ent) then + table.insert(self.data.rangerfilter,ent) + end +end + +__e2setcost(5) + +--- Feed an array of entities you don't want the trace to hit +e2function void rangerFilter(array filter) + local rangerfilter = self.data.rangerfilter + for _,ent in ipairs(filter) do + if validEntity(ent) then + table.insert(rangerfilter,ent) + end + end +end + +/******************************************************************************/ + +e2function ranger noranger() + return nil +end + +__e2setcost(20) -- temporary + +--- You input max range, it returns ranger data +e2function ranger ranger(distance) + return ranger(self, 0, distance, 0, 0) -- type 0, no skew +end + +--- Same as above with added inputs for X and Y skew +e2function ranger ranger(distance, xskew, yskew) + return ranger(self, 0, distance, xskew, yskew) -- type 0, with skew +end + +--- You input the distance, x-angle and y-angle (both in degrees) it returns ranger data +e2function ranger rangerAngle(distance, xangle, yangle) + return ranger(self, 1, distance, xangle, yangle) -- type 1, with angles +end + +--- You input two vector points, it returns ranger data +e2function ranger rangerOffset(vector from, vector to) + return ranger(self, 2, 0, from, to) -- type 2, from one point to another +end + +--- You input the range, a position vector, and a direction vector and it returns ranger data +e2function ranger rangerOffset(distance, vector from, vector direction) + return ranger(self, 3, distance, from, direction) -- type 3, from one position into a specific direction, in a specific range +end + +/******************************************************************************/ + +__e2setcost(2) -- temporary + +--- Returns the distance from the rangerdata input, else depends on rangerDefault +e2function number ranger:distance() + if not this then return 0 end + if this.StartSolid then return this.StartPos:Distance(this.HitPos)*(1/(1-this.FractionLeftSolid)-1) end + return this.StartPos:Distance(this.HitPos) +end + +--- Returns the position of the input ranger data trace IF it hit anything, else returns vec(0,0,0) +e2function vector ranger:position() + if not this then return { 0, 0, 0 } end + if this.StartSolid then return this.StartPos end + return this.HitPos +end + +--- Returns the entity of the input ranger data trace IF it hit an entity, else returns nil +e2function entity ranger:entity() + if not this then return nil end + return this.Entity +end + +--- Returns the bone of the input ranger data trace IF it hit an entity, else returns nil +e2function bone ranger:bone() + if not this then return nil end + + local ent = this.Entity + if not validEntity(ent) then return nil end + return getBone(ent, this.PhysicsBone) +end + +--- Returns 1 if the input ranger data hit anything and 0 if it didn't +e2function number ranger:hit() + if not this then return 0 end + if this.Hit then return 1 else return 0 end +end + +--- Outputs a normalized vector perpendicular to the surface the ranger is pointed at. +e2function vector ranger:hitNormal() + if not this then return { 0, 0, 0 } end + return this.HitNormal +end + +/******************************************************************************/ + +registerCallback("preexecute", function(self) + self.data.rangerwater = false + self.data.rangerentities = true + self.data.rangerignoreworld = false + self.data.rangerdefaultzero = false + self.data.rangerfilter = {} +end) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/selfaware.lua b/lua/entities/gmod_wire_expression2/core/selfaware.lua new file mode 100644 index 0000000000..3ed08fe267 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/selfaware.lua @@ -0,0 +1,116 @@ +/******************************************************************************\ + Selfaware support +\******************************************************************************/ + +__e2setcost(1) -- temporary + +registerFunction("entity", "", "e", function(self, args) + return self.entity +end) + +registerFunction("owner", "", "e", function(self, args) + return self.player +end) + +__e2setcost(nil) -- temporary + +registerFunction("setColor", "nnn", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + self.entity:SetColor(math.Clamp(rv1, 0, 255), math.Clamp(rv2, 0, 255), math.Clamp(rv3, 0, 255), 255) +end) + +registerFunction("selfDestruct", "", "", function(self, args) + self.entity:Remove() +end) + +registerFunction("selfDestructAll", "", "", function(self, args) + for k,v in pairs(constraint.GetAllConstrainedEntities(self.entity)) do + if(getOwner(self,v)==self.player) then + v:Remove() + end + end + //constraint.RemoveAll(self.entity) + self.entity:Remove() +end) + +/******************************************************************************/ + +registerCallback("construct", function(self) + self.data.changed = {} +end) + +-- This is the prototype for everything that can be compared using the == operator +e2function number changed(value) + local chg = self.data.changed + + if value == chg[args] then return 0 end + + chg[args] = value + return 1 +end + +-- vectors can be of gmod type Vector, so we need to treat them separately +e2function number changed(vector value) + local chg = self.data.changed + + local this_chg = chg[args] + if not this_chg then + chg[args] = value + return 1 + end + if this_chg + and value[1] == this_chg[1] + and value[2] == this_chg[2] + and value[3] == this_chg[3] + then return 0 end + + chg[args] = value + return 1 +end + +-- This is the prototype for all table types. +e2function number changed(angle value) + local chg = self.data.changed + + local this_chg = chg[args] + if not this_chg then + chg[args] = value + return 1 + end + for i,v in pairs(value) do + if v ~= this_chg[i] then + chg[args] = value + return 1 + end + end + return 0 +end + +local excluded_types = { + n = true, + v = true, + a = true, + + r = true, + t = true, +} +local comparable_types = { + s = true, + e = true, + xwl = true, + b = true, +} + +registerCallback("postinit", function() + -- generate this function for all types + for typeid,_ in pairs(wire_expression_types2) do + if not excluded_types[typeid] then + if comparable_types[typeid] then + registerFunction("changed", typeid, "n", e2_changed_n) + else + registerFunction("changed", typeid, "n", e2_changed_a) + end + end + end +end) diff --git a/lua/entities/gmod_wire_expression2/core/serverinfo.lua b/lua/entities/gmod_wire_expression2/core/serverinfo.lua new file mode 100644 index 0000000000..a1f96b9785 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/serverinfo.lua @@ -0,0 +1,70 @@ +/******************************************************************************\ + Server Information +\******************************************************************************/ + +local ostime_arguments = { + year = true, + month = true, + day = true, + hour = true, + min = true, + sec = true, + wday = true, + yday = true, + isdst = true +} + +registerFunction("map", "", "s", function(self, args) + return string.Replace(game.GetMap(),".bsp","") +end) + +registerFunction("hostname", "", "s", function(self, args) + if(SinglePlayer()) then return "" end + return GetConVarString("hostname") +end) + +registerFunction("isLan", "", "n", function(self, args) + if(GetConVar("sv_lan"):GetBool()) then return 1 else return 0 end +end) + +registerFunction("gamemode", "", "s", function(self, args) + return gmod.GetGamemode().Name +end) + +registerFunction("isSinglePlayer", "", "n", function(self, args) + if(SinglePlayer()) then return 1 else return 0 end +end) + +registerFunction("isDedicated", "", "n", function(self, args) + if(SinglePlayer()) then return 0 end + if(isDedicatedServer()) then return 1 else return 0 end +end) + +registerFunction("numPlayers", "", "n", function(self, args) + return table.Count(player.GetAll()) +end) + +registerFunction("maxPlayers", "", "n", function(self, args) + return MaxPlayers() +end) + +registerFunction("gravity", "", "n", function(self, args) + return GetConVarNumber("sv_gravity") +end) + +registerFunction("time", "s", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + + if not ostime_arguments[rv1] then return 0 end + local ostime = os.date("!*t") + local ret = ostime[rv1] + + if tonumber(ret) then + return ret + elseif ret == true then -- Occurs if input string is "isdst" + return 1 + else + return 0 + end +end) diff --git a/lua/entities/gmod_wire_expression2/core/signal.lua b/lua/entities/gmod_wire_expression2/core/signal.lua new file mode 100644 index 0000000000..e1df61f40d --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/signal.lua @@ -0,0 +1,307 @@ +--[[ +Original idea: Gwahir +Original implementation: Gwahir +Rewrite that made it work: TomyLobo +]] + +-- holds the currently registered signal handlers. Format: +-- scopes[player|1|2][group][name][context] = true|nil +local scopes = {{}, {}} + +-- holds the currently queued signals. Format: +-- signal_queue[group][name][receiver] = { group, name, scope, sender, senderid } +local signal_queue = {} + +-- holds the current signal data. Format: +-- currentSignal = nil|{ group, name, scope, sender, senderid } +local currentSignal = nil + +--[[************************************************************************]]-- + +-- executes a chip's code +local function triggerSignal(receiver, signaldata) + currentSignal = signaldata + receiver.entity:Execute() + currentSignal = nil +end + +local timerRunning=false + +local function checkSignals() + timerRunning = false + + -- make a copy of the signal queue so we can add new signals safely while iterating through the existing array + local queue = signal_queue + + -- clear the signal queue + signal_queue = {} + + -- loop through all queued signal groups + for group,signals in pairs(queue) do + -- loop through all queued signals in the group + for name,receivers in pairs(signals) do + -- ... and all receivers + for receiver,signaldata in pairs(receivers) do + -- and trigger all signals on the queue + triggerSignal(receiver, signaldata) + end + end + end + +end + +-- queues a chip's code for execution after at most +local function postSignal(receiver, group, name, scope, sender, senderid) + -- don't send the signal back to the sender + if sender == receiver then return end + + -- create signal's spot on the queue, if it doesnt exist + if not signal_queue[group] then signal_queue[group] = {} end + if not signal_queue[group][name] then signal_queue[group][name] = {} end + + -- add the given signal to the queue + signal_queue[group][name][receiver] = { group, name, scope, sender.entity, senderid } + + -- set a timer if it isnt already set + if not timerRunning then + timerRunning = true + timer.Simple(0.01, checkSignals) + end +end + +-- Sends the given signal group/name combination to everyone listening +local function broadcastSignal(group, name, scope, sender, filter_player) + + local sender_player = sender.player + + local groups + if scope == 0 then + -- scope 0 => read from scopes[sender.player] + groups = scopes[sender_player] + else + -- scope 1/2 => read from scopes[scope] + groups = scopes[scope] + end + + if not groups[group] then return end + local contexts = groups[group][name] + + -- there was no signal registered for the selected scope/group/name combination. + if not contexts then return end + + local senderid = sender.entity:EntIndex() + + for receiver,_ in pairs(contexts) do + if (not filter_player or receiver.player == filter_player) and (scope ~= 2 or receiver.player ~= sender_player) then + postSignal(receiver, group, name, scope, sender, senderid) + end + end +end + +--local function table_IsEmpty(t) return not pairs(t)(t) end +local function table_IsEmpty(t) return not next(t) end + +local function setGroup(self, group) + local oldgroup = self.data.signalgroup + + -- no change? don't waste precious cycles and get out here + if oldgroup == group then return end + + -- get the signal scope + local s = scopes[self.player] + -- remove the old group, if empty + if s[oldgroup] and table_IsEmpty(s[oldgroup]) then s[oldgroup] = nil end + -- set up the new group + if not s[group] then s[group] = {} end + + -- same for scope 1 + local s = scopes[1] + if s[oldgroup] and table_IsEmpty(s[oldgroup]) then s[oldgroup] = nil end + if not s[group] then s[group] = {} end + + -- same for scope 2 + local s = scopes[2] + if s[oldgroup] and table_IsEmpty(s[oldgroup]) then s[oldgroup] = nil end + if not s[group] then s[group] = {} end + + -- set the current group to the new group + self.data.signalgroup = group +end + +--[[************************************************************************]]-- + +--- Sets the E-2's current signal group to , this is applied during runOnSignal, signalSend, and signalSetOnRemove calls, so call it first. +e2function void signalSetGroup(string group) + setGroup(self, group) +end + +--- Gets the E-2's current signal group +e2function string signalGetGroup() + return self.data.signalgroup +end + +--- If == 0 the chip will no longer run on this signal, otherwise it makes this chip execute when signal is sent by someone in scope . +e2function void runOnSignal(string name, scope, activate) + -- sanitize inputs + if scope >= 3 or scope < 0 then return end + scope = math.floor(scope) + + -- process inputs + activate = activate ~= 0 or nil + if scope == 0 then scope = self.player end + + -- fetch the signal group + local signals = scopes[scope][self.data.signalgroup] + + -- if there is no entry for the signal in the group yet, create it + if not signals[name] then signals[name] = {} end + + -- (un-)register signal + signals[name][self] = activate +end + +--[[************************************************************************]]-- + +--- Returns 1 if the chip was executed because of any signal, regardless of name, group or scope. Returns 0 otherwise. +e2function number signalClk() + return currentSignal and 1 or 0 +end + +--- Returns 1 if the chip was executed because the signal was sent, regardless of group or scope. Returns 0 otherwise. +e2function number signalClk(string name) + return (currentSignal and currentSignal[2] == name) and 1 or 0 +end + +--- Returns 1 if the chip was executed because the signal was sent to the scope , regardless of group. Returns 0 otherwise. +e2function number signalClk(string name, scope) + return (currentSignal and currentSignal[2] == name and currentSignal[3] == scope) and 1 or 0 +end + +--- Returns 1 if the chip was executed because the signal was sent in the group , regardless of scope. Returns 0 otherwise. +e2function number signalClk(string group, string name) + return (currentSignal and currentSignal[1] == group and currentSignal[2] == name) and 1 or 0 +end + +--- Returns 1 if the chip was executed because the signal was sent in the group to the scope . Returns 0 otherwise. +e2function number signalClk(string group, string name, scope) + return (currentSignal and currentSignal[1] == group and currentSignal[2] == name and currentSignal[3] == scope) and 1 or 0 +end + +--- Returns the name of the received signal. +e2function string signalName() + if not currentSignal then return "" end + return currentSignal[2] +end + +--- Returns the group name of the received signal. +e2function string signalGroup() + if not currentSignal then return "" end + return currentSignal[1] +end + +--- Returns the entity of the chip that sent the signal. +e2function entity signalSender() + if not currentSignal then return nil end + return currentSignal[4] +end + +--- Returns the entity ID of the chip that sent the signal. Useful if the entity doesn't exist anymore. +e2function number signalSenderId() + if not currentSignal then return 0 end + return currentSignal[5] +end + +--[[************************************************************************]]-- + +--- Sets the signal that the chip sends when it is removed from the world. +e2function void signalSetOnRemove(string name, scope) + self.data.removeSignal = { self.data.signalgroup, name, scope, self } +end + +--- Clears the signal that the chip sends when it is removed from the world. +e2function void signalClearOnRemove() + self.data.removeSignal = nil +end + +--- Sends signal to scope . Additional calls to this function with the same signal will overwrite the old call until the signal is issued. +e2function void signalSend(string name, scope) + broadcastSignal(self.data.signalgroup, name, scope, self) +end + +--- Sends signal S to the given chip. Multiple calls for different chips do not overwrite each other. +e2function void signalSendDirect(string name, entity receiver) + if not validEntity(receiver) then return end + receiver = receiver.context + + -- filter out non-E2 entities + if not receiver then return end + + -- dont send back to ourselves + if receiver == self then return end + + local group = self.data.signalgroup + + -- check whether the target entity accepts signals from the "anyone" scope. + if not scopes[1][group][name] then return end + if not scopes[1][group][name][receiver] then return end + + -- send the signal + postSignal(receiver, group, name, 1, self, self.entity:EntIndex()) +end + +--- sends signal S to chips owned by the given player, multiple calls for different players do not overwrite each other +e2function void signalSendToPlayer(string name, entity player) + if not validEntity(player) then return end + broadcastSignal(self.data.signalgroup, name, 1, self, player) +end + +--[[************************************************************************]]-- + +registerCallback("construct",function(self) + -- if there is no personal scope for us yet, create it. + if not scopes[self.player] then scopes[self.player] = {} end + + -- place a bogus group into the personal scope to mark it as used. + scopes[self.player][self] = {{{}}} + -- ^^^-- context + -- |+-- signal + -- +-- group + + -- set a default group + setGroup(self, "default") +end) + +registerCallback("destruct",function(self) + -- loop through all scopes, ... + for scope,groups in pairs(scopes) do + -- ... all groups ... + for group, signals in pairs(groups) do + -- ... and all signals ... + for name, contexts in pairs(signals) do + -- to remove all signals the chip registered for. + contexts[self] = nil + + -- are we the last chip that received this signal? + if table_IsEmpty(contexts) then signals[name] = nil end + end + + -- was this the last signal in this group? + if table_IsEmpty(signals) then groups[group] = nil end + end + end + + -- remove the bogus group from the personal scope + if (scopes[self.player]) then + scopes[self.player][self] = nil + + -- and check whether this was the last group + if table_IsEmpty(scopes[self.player]) then scopes[self.player] = nil end + end + + -- broadcast the on-remove signal, if one was registered + if self.data.removeSignal then broadcastSignal(unpack(self.data.removeSignal)) end + + -- clean up (not actually necessary since the context is destroyed anyway) + self.data.signalgroup = nil + self.data.removeSignal = nil +end) diff --git a/lua/entities/gmod_wire_expression2/core/sound.lua b/lua/entities/gmod_wire_expression2/core/sound.lua new file mode 100644 index 0000000000..1dc1be52b9 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/sound.lua @@ -0,0 +1,201 @@ +/******************************************************************************\ + Built-in Sound support v1.18 +\******************************************************************************/ + +//-----------------------// +//--Server Convar--// +//-----------------------// + +local wire_expression2_maxsounds = CreateConVar( "wire_expression2_maxsounds", 8 ) + +//--------------------------// +//--Functions--// +//--------------------------// + +local function soundCreate(self, entity, index, time, path, fade) + if path:match('["?]') then return end + local data = self.data['currentsound'] + if table.Count(data)>wire_expression2_maxsounds:GetFloat() then return end + path = path:Trim() + local sound = CreateSound(entity, path) + if type(index)=="number" then index = index - index % 1 end + if data[index] then data[index]:Stop() end + data[index] = sound + sound:Play() + if time==0 && fade==0 then return end + if time<0 then time = time * -1 end + if fade<0 then fade = fade * -1 end + timer.Create( "sounddeletetime"..index, time, 1, function() + if !data[index] then return end + if fade==0 then + data[index]:Stop() + data[index] = nil + return + end + data[index]:FadeOut(fade) + timer.Create( "soundfadetime"..index, fade, 1, function() + if !data[index] then return end + data[index]:Stop() + data[index] = nil + end) + end) +end + +local function soundStop(self, index, fade) + local data = self.data['currentsound'] + if !data[index] then return end + local sound = data[index] + sound:FadeOut(fade) + data[index] = nil +end + +/*************************************************************/ + +registerFunction("soundPlay", "nns", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3) + soundCreate(self,self.entity,rv1,rv2,rv3,0) +end) + +registerFunction("soundPlay", "sns", "", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3) + soundCreate(self,self.entity,rv1, rv2,rv3,0) +end) + +registerFunction("soundPlay", "e:nns", "", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + local entity = checkEntity(rv1) + if(!entity) then return end + if !isOwner(self, entity) then return end + soundCreate(self,entity,rv2,rv3,rv4,0) +end) + +registerFunction("soundPlay", "e:sns", "", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + local entity = checkEntity(rv1) + if(!entity) then return end + if !isOwner(self, entity) then return end + soundCreate(self,entity,rv2,rv3,rv4,0) +end) + +registerFunction("soundPlay", "nnsn", "", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + soundCreate(self,self.entity,rv1,rv2,rv3,rv4) +end) + +registerFunction("soundPlay", "snsn", "", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4) + soundCreate(self,self.entity,rv1, rv2,rv3,rv4) +end) + +registerFunction("soundPlay", "e:nnsn", "", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4), op5[1](self,op5) + local entity = checkEntity(rv1) + if(!entity) then return end + if !isOwner(self, entity) then return end + soundCreate(self,entity,rv2,rv3,rv4,rv5) +end) + +registerFunction("soundPlay", "e:snsn", "", function(self, args) + local op1, op2, op3, op4, op5 = args[2], args[3], args[4], args[5], args[6] + local rv1, rv2, rv3, rv4, rv5 = op1[1](self,op1), op2[1](self,op2), op3[1](self,op3), op4[1](self,op4), op5[1](self,op5) + local entity = checkEntity(rv1) + if(!entity) then return end + if !isOwner(self, entity) then return end + soundCreate(self,entity,rv2,rv3,rv4,rv5) +end) + +registerFunction("soundStop", "n", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + rv1 = rv1 - rv1 % 1 + soundStop(self, rv1, 0) +end) + +registerFunction("soundStop", "nn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + rv1 = rv1 - rv1 % 1 + soundStop(self, rv1, rv2) +end) + +registerFunction("soundStop", "s", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + soundStop(self, rv1, 0) +end) + +registerFunction("soundStop", "sn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + soundStop(self, rv1, rv2) +end) + +registerFunction("soundVolume", "nn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), math.Clamp(op2[1](self, op2),0,1) + rv1 = rv1 - rv1 % 1 + local data = self.data['currentsound'] + if data[rv1] then + local sound = data[rv1] + sound:ChangeVolume(rv2) + end +end) + +registerFunction("soundVolume", "sn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), math.Clamp(op2[1](self, op2),0,1) + local data = self.data['currentsound'] + if data[rv1] then + local sound = data[rv1] + sound:ChangeVolume(rv2) + end +end) + +registerFunction("soundPitch", "nn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), math.Clamp(op2[1](self, op2),0,255) + rv1 = rv1 - rv1 % 1 + local data = self.data['currentsound'] + if data[rv1] then + local sound = data[rv1] + sound:ChangePitch(rv2) + end +end) + +registerFunction("soundPitch", "sn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), math.Clamp(op2[1](self, op2),0,255) + local data = self.data['currentsound'] + if data[rv1] then + local sound = data[rv1] + sound:ChangePitch(rv2) + end +end) + +registerFunction("soundPurge", "", "", function(self, args) + local data = self.data['currentsound'] + local count = table.Count(data) + for _,v in pairs(data) do + v:Stop() + end + self.data['currentsound'] = {} +end) + +/******************************************************************************/ + +registerCallback("construct", function(self) + self.data['currentsound'] = {} +end) + +registerCallback("destruct", function(self) + for _,v in pairs(self.data['currentsound']) do + v:Stop() + end +end) diff --git a/lua/entities/gmod_wire_expression2/core/string.lua b/lua/entities/gmod_wire_expression2/core/string.lua new file mode 100644 index 0000000000..56d495732c --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/string.lua @@ -0,0 +1,340 @@ +/******************************************************************************\ + String support +\******************************************************************************/ + +// TODO: is string.left() faster than s:left()? +// TODO: is string.sub faster than both left and right? +// TODO: these return bad results when used with negative numbers! +// TODO: benchmarks! + +local string = string -- optimization + +/******************************************************************************/ + +registerType("string", "s", "", + nil, + nil, + function(retval) + if type(retval) ~= "string" then error("Return value is not a string, but a "..type(retval).."!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(3) -- temporary + +registerOperator("ass", "s", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ + +registerOperator("is", "s", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 ~= "" then return 1 else return 0 end +end) + +registerOperator("eq", "ss", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 == rv2 then return 1 else return 0 end +end) + +registerOperator("neq", "ss", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1 != rv2 then return 1 else return 0 end +end) + +/******************************************************************************/ + +__e2setcost(10) -- temporary + +registerOperator("add", "ss", "s", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) .. op2[1](self, op2) +end) + +/******************************************************************************/ + +registerOperator("add", "sn", "s", function(self, args) + local op1, op2 = args[2], args[3] + return op1[1](self, op1) .. tostring(op2[1](self, op2)) +end) + +registerOperator("add", "ns", "s", function(self, args) + local op1, op2 = args[2], args[3] + return tostring(op1[1](self, op1)) .. op2[1](self, op2) +end) + +/******************************************************************************/ + +registerOperator("add", "sv", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + return op1[1](self, op1) .. "[" .. tostring(rv2[1]) .. "," .. tostring(rv2[2]) .. "," .. tostring(rv2[3]) .. "]" +end) + +registerOperator("add", "vs", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1 = op1[1](self, op1) + return "[" .. tostring(rv1[1]) .. "," .. tostring(rv1[2]) .. "," .. tostring(rv1[3]) .. "]" .. op2[1](self, op2) +end) + +/******************************************************************************/ + +registerOperator("add", "sa", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + return op1[1](self, op1) .. "[" .. tostring(rv2[1]) .. "," .. tostring(rv2[2]) .. "," .. tostring(rv2[3]) .. "]" +end) + +registerOperator("add", "as", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1 = op1[1](self, op1) + return "[" .. tostring(rv1[1]) .. "," .. tostring(rv1[2]) .. "," .. tostring(rv1[3]) .. "]" .. op2[1](self, op2) +end) + +/******************************************************************************/ + +__e2setcost(20) -- temporary + +e2function number string:toNumber() + local ret = tonumber(this) + if ret == nil then return 0 end + return ret +end + +e2function number string:toNumber(number base) + local ret = tonumber(this, base) + if ret == nil then return 0 end + return ret +end + + +registerFunction("toChar", "n", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 < 32 then return "" end + if rv1 > 255 then return "" end + return string.char(rv1) +end) + +registerFunction("toByte", "s", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1 == "" then return -1 end + return string.byte(rv1) +end) + +registerFunction("toByte", "sn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv2 < 1 || rv2 > string.len(rv1) then return -1 end + return string.byte(rv1, rv2) +end) + +/******************************************************************************/ + +registerFunction("index", "s:n", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1:sub(rv2, rv2) +end) + +registerFunction("left", "s:n", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1:Left(rv2) +end) + +registerFunction("right", "s:n", "s", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1:Right(rv2) +end) + +registerFunction("sub", "s:nn", "s", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + return rv1:sub(rv2, rv3) +end) + +e2function string string:sub(start) + return string.sub(this,start) +end + +e2function string string:operator[](index) + return this:sub(index,index) +end + +registerFunction("upper", "s:", "s", function(self, args) + local op1 = args[2], args[3] + local rv1 = op1[1](self, op1) + return rv1:upper() +end) + +registerFunction("lower", "s:", "s", function(self, args) + local op1 = args[2], args[3] + local rv1 = op1[1](self, op1) + return rv1:lower() +end) + +registerFunction("length", "s:", "n", function(self, args) + local op1 = args[2], args[3] + local rv1 = op1[1](self, op1) + return rv1:len() +end) + +/******************************************************************************/ + +registerFunction("repeat", "s:n", "s", function(self,args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1),op2[1](self, op2) + return string.rep(rv1,rv2) +end) + +registerFunction("trim", "s:", "s", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return string.Trim(rv1) +end) + +registerFunction("trimLeft", "s:", "s", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return string.match(rv1, "^ *(.-)$") +end) + +registerFunction("trimRight", "s:", "s", function(self,args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return string.TrimRight(rv1) +end) + +/******************************************************************************/ +--- Returns the 1st occurrence of the string , returns 0 if not found. Prints malformed string errors to the chat area. +e2function number string:findRE(string pattern) + local OK, Ret = pcall(string.find, this, pattern) + if not OK then + self.player:ChatPrint(Ret) + return 0 + else + return Ret or 0 + end +end + +--- Returns the 1st occurrence of the string starting at and going to the end of the string, returns 0 if not found. Prints malformed string errors to the chat area. +e2function number string:findRE(string pattern, start) + local OK, Ret = pcall(string.find, this, pattern, start) + if not OK then + self.player:ChatPrint(Ret) + return 0 + else + return Ret or 0 + end +end + +--- Returns the 1st occurrence of the string , returns 0 if not found. Does not use LUA patterns. +e2function number string:find(string needle) + return string.find(this, needle, 1, true) or 0 +end + +--- Returns the 1st occurrence of the string starting at and going to the end of the string, returns 0 if not found. Does not use LUA patterns. +e2function number string:find(string needle, start) + return string.find(this, needle, start, true) or 0 +end + +--- Finds and replaces every occurrence of with without regular expressions +e2function string string:replace(string needle, string new) + if needle == "" then return "" end -- prevent crashes. stupid garry... + return string.Replace(this, needle, new) +end + +--- Finds and replaces every occurrence of with using regular expressions. Prints malformed string errors to the chat area. +e2function string string:replaceRE(string pattern, string new) + local OK, NewStr = pcall(string.gsub, this, pattern, new) + if not OK then + self.player:ChatPrint(NewStr) + return "" + else + return NewStr or "" + end +end + +--- Splits the string into an array, along the boundaries formed by the string . See also [[string.Explode]] +e2function array string:explode(string pattern) + return string.Explode(pattern, this) +end + +--- Returns a reversed version of +e2function string string:reverse() + return string.reverse(this) +end + +/******************************************************************************/ + +--- Formats a values exactly like Lua's [http://www.lua.org/manual/5.1/manual.html#pdf-string.format string.format]. Any number and type of parameter can be passed through the "...". Prints errors to the chat area. +e2function string format(string fmt, ...) + -- TODO: call toString for table-based types + local ok, ret = pcall(string.format, fmt, ...) + if not ok then + self.player:ChatPrint(ret) + return "" + end + return ret +end + +/******************************************************************************/ +-- string.match wrappers by Jeremydeath, 2009-08-30 + +--- runs [[string.match]](, ) and returns the sub-captures as an array. Prints malformed pattern errors to the chat area. +e2function array string:match(string pattern) + local OK, Ret = pcall(string.match, this, pattern) + if not OK then + self.player:ChatPrint(Ret) + return {} + else + return { string.match(this, pattern) } + end +end + +--- runs [[string.match]](, , ) and returns the sub-captures as an array. Prints malformed pattern errors to the chat area. +e2function array string:match(string pattern, position) + local OK, Ret = pcall(string.match, this, pattern, position) + if not OK then + self.player:ChatPrint(Ret) + return {} + else + return { string.match(this, pattern) } + end +end + +--- runs [[string.match]](, ) and returns the first match or an empty string if the match failed. Prints malformed pattern errors to the chat area. +e2function string string:matchFirst(string pattern) + local OK, Ret = pcall(string.match, this, pattern) + if not OK then + self.player:ChatPrint(Ret) + return {} + else + return Ret or "" + end +end + +--- runs [[string.match]](, , ) and returns the first match or an empty string if the match failed. Prints malformed pattern errors to the chat area. +e2function string string:matchFirst(string pattern, position) + local OK, Ret = pcall(string.match, this, pattern, position) + if not OK then + self.player:ChatPrint(Ret) + return {} + else + return Ret or "" + end +end + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/table.lua b/lua/entities/gmod_wire_expression2/core/table.lua new file mode 100644 index 0000000000..bc2a034f2b --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/table.lua @@ -0,0 +1,282 @@ + +local function table_IsEmpty(t) return not next(t) end + +/******************************************************************************\ + Table support +\******************************************************************************/ + +registerType("table", "t", {}, + function(self, input) + local ret = {} + local c = 0 + for k,v in pairs(input) do c = c + 1 ret[k] = v end + self.prf = self.prf + c / 3 + return ret + end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + end +) + +-- these postexecute and construct hooks handle changes to both tables and arrays. +registerCallback("postexecute", function(self) + local vars, vclk, lookup = self.vars, self.vclk, self.data.lookup + + -- Go through all registered values of the types table and array. + for value,varnames in pairs(lookup) do + -- Was the value changed? + if vclk[value] then + -- For each changed value, go through the variables they're assigned to and trigger them. + for varname,_ in pairs(varnames) do + if value == vars[varname] then + -- The value is still assigned to the variable? => trigger it. + vclk[varname] = true + else + -- The value is no longer assigned to the variable? => remove the lookup table entry. + varnames[varname] = nil + end + end + -- If the value has no more variable names associated, remove the value's place in the lookup table. + if table_IsEmpty(varnames) then lookup[value] = nil end + end + end +end) + +registerCallback("construct", function(self) + --self.data.lookup[rhs][lhs] = true|nil + self.data.lookup = {} +end) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +e2function table operator=(table lhs, table rhs) + local lookup = self.data.lookup + + -- remove old lookup entry + if lookup[rhs] then lookup[rhs][lhs] = nil end + + -- add new lookup entry + local lookup_entry = lookup[rhs] + if not lookup_entry then + lookup_entry = {} + lookup[rhs] = lookup_entry + end + lookup_entry[lhs] = true + + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +/******************************************************************************/ + +e2function number operator_is(table tbl) + if table_IsEmpty(tbl) then return 0 else return 1 end +end + +/******************************************************************************/ + +e2function table table() + return {} +end + +e2function table table:clear() + self.prf = self.prf + table.Count(this) / 3 + table.Empty(this) +end + +e2function table table:clone() + local ret = {} + local c = 0 + for k,v in pairs(this) do c = c + 1 ret[k] = v end + self.prf = self.prf + c / 3 + return ret +end + +e2function number table:count() + local c = table.Count(this) + self.prf = self.prf + c / 3 + return c +end + +/******************************************************************************/ + +-- for invert(R) +local tostrings = { + number=tostring, + string=tostring, + Entity=tostring, + Weapon=tostring, + Player=tostring, + Vehicle=tostring, + NPC=tostring, + PhysObj=e2_tostring_bone, + ["nil"] = function() return "(null)" end +} + +function tostrings.table(t) + return "["..table.concat(t, ",").."]" +end + +function tostrings.Vector(v) + return "[" .. tostring(v[1]) .. "," .. tostring(v[2]) .. "," .. tostring(v[3]) .. "]" +end + +-- for invert(T) +local tostring_typeid = { + n=tostring, + s=tostring, + e=tostring, + xv2=tostrings.table, + v=tostrings.Vector, + xv4=tostrings.table, + a=tostrings.table, + b=e2_tostring_bone, +} + +--- Returns a lookup table for . Usage: Index = T:number(toString(Value)). +--- Don't overuse this function, as it can become expensive for arrays with > 10 entries! +e2function table invert(array arr) + local ret = {} + local c = 0 + for i,v in ipairs(arr) do + c = c + 1 + local tostring_this = tostrings[type(v)] + if tostring_this then + ret["n"..tostring_this(v)] = i + else + self.player:ChatPrint("E2: invert(R): Invalid type ("..type(v)..") in array. Ignored.") + end + end + self.prf = self.prf + c / 2 + return ret +end + +--- Returns a lookup table for . Usage: Key = T:string(toString(Value)). +--- Don't overuse this function, as it can become expensive for tables with > 10 entries! +e2function table invert(table tbl) + local ret = {} + local c = 0 + for i,v in pairs(tbl) do + c = c + 1 + local long_typeid = string.sub(i,1,1) == "x" + local typeid = string.sub(i,1,long_typeid and 3 or 1) + + local tostring_this = tostring_typeid[typeid] + if tostring_this then + ret["s"..tostring_this(v)] = i:sub(long_typeid and 4 or 2) + else + self.player:ChatPrint("E2: invert(T): Invalid type ("..typeid..") in table. Ignored.") + end + end + self.prf = self.prf + c / 2 + return ret +end + +/******************************************************************************/ + +registerCallback("postinit", function() + + -- retrieve information about all registered types + local types = table.Copy(wire_expression_types) + + -- change the name for numbers from "NORMAL" to "NUMBER" + types["NUMBER"] = types["NORMAL"] + types["NORMAL"] = nil + + -- we don't want tables and arrays as table elements, so get rid of them + types["TABLE"] = nil + types["ARRAY"] = nil + + local getf, setf + -- generate getters and setters for all types + for name,id,zero in pairs_map(types, unpack) do + + -- for T:number() etc + local getter = name:lower() + + -- for T:setNumber() etc + local setter = "set"..name:sub(1,1):upper()..name:sub(2):lower() + + if zero ~= nil then + -- getters for everything but entity, bone, wirelink and ranger + function getf(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local ret = rv1[id .. rv2] + if ret then return ret end + return zero + end + if type(zero) == "table" then + if table_IsEmpty(zero) then + -- setters for array and table (currently unused) + function setf(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if table_IsEmpty(rv3) then rv3 = nil end + rv1[id .. rv2] = rv3 + self.vclk[rv1] = true + return rv3 + end + elseif #zero ~= table.Count(zero) then + -- setters for tables with named entries. currently unused + function setf(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local val = nil + for i,v in pairs(zero) do if rv3[i] ~= v then val = rv3 break end end + rv1[id .. rv2] = val + self.vclk[rv1] = true + return rv3 + end + else + -- setters for vector*, matrix* and angle + function setf(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local val = nil + for i,v in ipairs(zero) do if rv3[i] ~= v then val = rv3 break end end + rv1[id .. rv2] = val + self.vclk[rv1] = true + return rv3 + end + end + else + -- setters for number and string + function setf(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + if rv3 == zero then rv3 = nil end + rv1[id .. rv2] = rv3 + self.vclk[rv1] = true + return rv3 + end + end + else + -- getters and setters for entity, bone, wirelink and ranger + function getf(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1[id .. rv2] + end + + function setf(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + rv1[id .. rv2] = rv3 + self.vclk[rv1] = true + return rv3 + end + end + registerFunction(getter, "t:s", id, getf) + registerOperator("idx", id.."=ts", id, getf) + registerFunction(setter, "t:s"..id, id, setf) + registerOperator("idx", id.."=ts"..id, id, setf) + end + -- all types not mentioned and custom types have their getters and setters generated like those of the built-in type that is most similar to them. +end) -- registerCallback("postinit") + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/timer.lua b/lua/entities/gmod_wire_expression2/core/timer.lua new file mode 100644 index 0000000000..b1645503da --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/timer.lua @@ -0,0 +1,102 @@ +/******************************************************************************\ + Timer support +\******************************************************************************/ + +local timerid = 0 +local runner + +local function Execute(self, name) + runner = name + + self.data['timer'].timers[name] = nil + + if(self.entity and self.entity.Execute) then + self.entity:Execute() + end + + if !self.data['timer'].timers[name] then + timer.Destroy("e2_" .. self.data['timer'].timerid .. "_" .. name) + end + + runner = nil +end + +local function AddTimer(self, name, delay) + if delay < 10 then delay = 10 end + + if runner == name then + timer.Adjust("e2_" .. self.data['timer'].timerid .. "_" .. name, delay/1000, 1, Execute, self, name) + timer.Start("e2_" .. self.data['timer'].timerid .. "_" .. name) + elseif !self.data['timer'].timers[name] then + timer.Create("e2_" .. self.data['timer'].timerid .. "_" .. name, delay/1000, 1, Execute, self, name) + end + + self.data['timer'].timers[name] = true +end + +local function RemoveTimer(self, name) + if self.data['timer'].timers[name] then + timer.Destroy("e2_" .. self.data['timer'].timerid .. "_" .. name) + self.data['timer'].timers[name] = nil + end +end + +/******************************************************************************/ + +registerCallback("construct", function(self) + self.data['timer'] = {} + self.data['timer'].timerid = timerid + self.data['timer'].timers = {} + + timerid = timerid + 1 +end) + +registerCallback("destruct", function(self) + for name,_ in pairs(self.data['timer'].timers) do + RemoveTimer(self, name) + end +end) + +/******************************************************************************/ + +__e2setcost(5) -- approximation + +registerFunction("interval", "n", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + AddTimer(self, "interval", rv1) +end) + +registerFunction("timer", "sn", "", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + AddTimer(self, rv1, rv2) +end) + +registerFunction("stoptimer", "s", "", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + RemoveTimer(self, rv1) +end) + +registerFunction("clk", "", "n", function(self, args) + if runner == "interval" + then return 1 else return 0 end +end) + +registerFunction("clk", "s", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if runner == rv1 + then return 1 else return 0 end +end) + +registerFunction("curtime", "", "n", function(self, args) + return CurTime() +end) + +registerFunction("realtime", "", "n", function(self, args) + return SysTime() +end) + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/unitconv.lua b/lua/entities/gmod_wire_expression2/core/unitconv.lua new file mode 100644 index 0000000000..03ad716af2 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/unitconv.lua @@ -0,0 +1,131 @@ +/******************************************************************************\ + Unit conversion +\******************************************************************************/ + +/* + mm - millimeter + cm - centimeter + dm - decimeter + m - meter + km - kilometer + in - inch + ft - foot + yd - yard + mi - mile + nmi - nautical mile + + g - gram + kg - kilogram + t - tonne + oz - ounce + lb - pound +*/ + +local speed = { + ["mm/s"] = 25.4, + ["cm/s"] = 2.54, + ["dm/s"] = 0.254, + ["m/s"] = 0.0254, + ["km/s"] = 0.0000254, + ["in/s"] = 1, + ["ft/s"] = 1 / 12, + ["yd/s"] = 1 / 36, + ["mi/s"] = 1 / 63360, + ["nmi/s"] = 127 / 9260000, + + ["mm/m"] = 60 * 25.4, + ["cm/m"] = 60 * 2.54, + ["dm/m"] = 60 * 0.254, + ["m/m"] = 60 * 0.0254, + ["km/m"] = 60 * 0.0000254, + ["in/m"] = 60, + ["ft/m"] = 60 / 12, + ["yd/m"] = 60 / 36, + ["mi/m"] = 60 / 63360, + ["nmi/m"] = 60 * 127 / 9260000, + + ["mm/h"] = 3600 * 25.4, + ["cm/h"] = 3600 * 2.54, + ["dm/h"] = 3600 * 0.254, + ["m/h"] = 3600 * 0.0254, + ["km/h"] = 3600 * 0.0000254, + ["in/h"] = 3600, + ["ft/h"] = 3600 / 12, + ["yd/h"] = 3600 / 36, + ["mi/h"] = 3600 / 63360, + ["nmi/h"] = 3600 * 127 / 9260000, + + ["mph"] = 3600 / 63360, + ["knots"] = 3600 * 127 / 9260000, + ["mach"] = 0.0254 / 295, +} + +local length = { + ["mm"] = 25.4, + ["cm"] = 2.54, + ["dm"] = 0.254, + ["m"] = 0.0254, + ["km"] = 0.0000254, + ["in"] = 1, + ["ft"] = 1 / 12, + ["yd"] = 1 / 36, + ["mi"] = 1 / 63360, + ["nmi"] = 127 / 9260000, +} + +local weight = { + ["g"] = 1000, + ["kg"] = 1, + ["t"] = 0.001, + ["oz"] = 1 / 0.028349523125, + ["lb"] = 1 / 0.45359237, +} + +__e2setcost(2) -- approximated + +registerFunction("toUnit", "sn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + + if speed[rv1] then + return rv2 * speed[rv1] + elseif length[rv1] then + return rv2 * length[rv1] + elseif weight[rv1] then + return rv2 * weight[rv1] + end + + return -1 +end) + +registerFunction("fromUnit", "sn", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + + if speed[rv1] then + return rv2 / speed[rv1] + elseif length[rv1] then + return rv2 / length[rv1] + elseif weight[rv1] then + return rv2 / weight[rv1] + end + + return -1 +end) + +registerFunction("convertUnit", "ssn", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + + if speed[rv1] and speed[rv2] then + return rv3 * (speed[rv2] / speed[rv1]) + elseif length[rv1] and length[rv2] then + return rv3 * (length[rv2] / length[rv1]) + elseif weight[rv1] and weight[rv2] then + return rv3 * (weight[rv2] / weight[rv1]) + end + + return -1 +end) + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/vector.lua b/lua/entities/gmod_wire_expression2/core/vector.lua new file mode 100644 index 0000000000..287c6c637e --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/vector.lua @@ -0,0 +1,613 @@ +/******************************************************************************\ + Vector support +\******************************************************************************/ + +local delta = wire_expression2_delta + +local random = math.random +local Vector = Vector +local sqrt = math.sqrt + +// TODO: add reflect? +// TODO: add absdotproduct? +// TODO: add helper for angle and dotproduct? (just strange?) + +/******************************************************************************/ + +registerType("vector", "v", { 0, 0, 0 }, + nil, + function(self, output) return Vector(output[1], output[2], output[3]) end, + function(retval) + if type(retval) == "Vector" then return end + if type(retval) ~= "table" then error("Return value is neither a Vector nor a table, but a "..type(retval).."!",0) end + if #retval ~= 3 then error("Return value does not have exactly 3 entries!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("vec", "", "v", function(self, args) + return { 0, 0, 0 } +end) + +__e2setcost(3) -- temporary + +registerFunction("vec", "nnn", "v", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + return { rv1, rv2, rv3 } +end) + +registerFunction("vec", "xv2", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], 0 } +end) + +registerFunction("vec", "xv2n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv2 } +end) + +registerFunction("vec", "xv4", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], rv1[3] } +end) + +// Convert Angle -> Vector +registerFunction("vec", "a", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], rv1[3] } +end) + +/******************************************************************************/ + +registerOperator("ass", "v", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ + +registerOperator("is", "v", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] > delta || -rv1[1] > delta || + rv1[2] > delta || -rv1[2] > delta || + rv1[3] > delta || -rv1[3] > delta + then return 1 else return 0 end +end) + +registerOperator("eq", "vv", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta && + rv1[3] - rv2[3] <= delta && rv2[3] - rv1[3] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "vv", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta || rv2[1] - rv1[1] > delta || + rv1[2] - rv2[2] > delta || rv2[2] - rv1[2] > delta || + rv1[3] - rv2[3] > delta || rv2[3] - rv1[3] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ + +registerOperator("dlt", "v", "v", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3] } +end) + +registerOperator("neg", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2], -rv1[3] } +end) + +registerOperator("add", "nv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 + rv2[1], rv1 + rv2[2], rv1 + rv2[3] } +end) + +registerOperator("add", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2, rv1[2] + rv2, rv1[3] + rv2 } +end) + +registerOperator("add", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2], rv1[3] + rv2[3] } +end) + +registerOperator("sub", "nv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 - rv2[1], rv1 - rv2[2], rv1 - rv2[3] } +end) + +registerOperator("sub", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2, rv1[2] - rv2, rv1[3] - rv2 } +end) + +registerOperator("sub", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3] } +end) + +registerOperator("mul", "nv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2], rv1 * rv2[3] } +end) + +registerOperator("mul", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2, rv1[3] * rv2 } +end) + +registerOperator("mul", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1], rv1[2] * rv2[2], rv1[3] * rv2[3] } +end) + +registerOperator("div", "nv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 / rv2[1], rv1 / rv2[2], rv1 / rv2[3] } +end) + +registerOperator("div", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2, rv1[3] / rv2 } +end) + +registerOperator("div", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2[1], rv1[2] / rv2[2], rv1[3] / rv2[3] } +end) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +--- Returns a uniformly distributed, random, normalized direction vector. +e2function vector randvec() + local s,a, x,y + + --[[ + This is a variant of the algorithm for computing a random point + on the unit sphere; the algorithm is suggested in Knuth, v2, + 3rd ed, p136; and attributed to Robert E Knop, CACM, 13 (1970), + 326. + ]] + -- translated to lua from http://mhda.asiaa.sinica.edu.tw/mhda/apps/gsl-1.6/randist/sphere.c + + -- Begin with the polar method for getting x,y inside a unit circle + repeat + x = random() * 2 - 1 + y = random() * 2 - 1 + s = x*x + y*y + until s <= 1.0 + + a = 2 * sqrt(1 - s) -- factor to adjust x,y so that x^2+y^2 is equal to 1-z^2 + return Vector(x*a, y*a, s * 2 - 1) -- z uniformly distributed from -1 to 1 + + --[[ + -- This variant saves 2 multiplications per loop, woo. But it's not readable and not verified, thus commented out. + -- I will also not add a cheaper non-uniform variant, as that can easily be derived from the other randvec functions and V:normalize(). + -- Begin with the polar method for getting x,y inside a (strangely skewed) unit circle + repeat + x = random() + y = random() + s = x*(x-1) + y*(y-1) + until s <= -0.25 + + a = sqrt(-16 - s*64) -- factor to adjust x,y so that x^2+y^2 is equal to 1-z^2 + return Vector((x-0.5)*a, (y-0.5)*a, s * 8 + 3) -- z uniformly distributed from -1 to 1 + ]] +end + +--- Returns a random vector with its components between and +e2function vector randvec(min, max) + local range = max-min + return Vector(min+random()*range, min+random()*range, min+random()*range) +end + +--- Returns a random vector between and +e2function vector randvec(vector min, vector max) + local minx, miny, minz = min[1], min[2], min[3] + return Vector(minx+random()*(max[1]-minx), miny+random()*(max[2]-miny), minz+random()*(max[3]-minz)) +end + +/******************************************************************************/ + +registerFunction("length", "v:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return (rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3]) ^ 0.5 +end) + +registerFunction("length2", "v:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] +end) + +registerFunction("distance", "v:v", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local rvd1, rvd2, rvd3 = rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3] + return (rvd1 * rvd1 + rvd2 * rvd2 + rvd3 * rvd3) ^ 0.5 +end) + +registerFunction("distance2", "v:v", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local rvd1, rvd2, rvd3 = rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3] + return rvd1 * rvd1 + rvd2 * rvd2 + rvd3 * rvd3 +end) + +registerFunction("normalized", "v:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local len = (rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3]) ^ 0.5 + if len > delta then + return { rv1[1] / len, rv1[2] / len, rv1[3] / len } + else + return { 0, 0, 0 } + end +end) + +// TODO: map these are EXP (dot) and MOD (cross) or something? +registerFunction("dot", "v:v", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1[1] * rv2[1] + rv1[2] * rv2[2] + rv1[3] * rv2[3] +end) + +registerFunction("cross", "v:v", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { + rv1[2] * rv2[3] - rv1[3] * rv2[2], + rv1[3] * rv2[1] - rv1[1] * rv2[3], + rv1[1] * rv2[2] - rv1[2] * rv2[1] + } +end) + +registerFunction("rotate", "v:a", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local v = Vector(rv1[1], rv1[2], rv1[3]) + v:Rotate(Angle(rv2[1], rv2[2], rv2[3])) + return { v.x, v.y, v.z } +end) + +registerFunction("rotate", "v:nnn", "v", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + local v = Vector(rv1[1], rv1[2], rv1[3]) + v:Rotate(Angle(rv2, rv3, rv4)) + return { v.x, v.y, v.z } +end) + +registerFunction("dehomogenized", "v:", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local w = rv1[3] + if w == 0 then return { rv1[1], rv1[2] } end + return { rv1[1]/w, rv1[2]/w } +end) + +registerFunction("positive", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x, y, z + if rv1[1] >= 0 then x = rv1[1] else x = -rv1[1] end + if rv1[2] >= 0 then y = rv1[2] else y = -rv1[2] end + if rv1[3] >= 0 then z = rv1[3] else z = -rv1[3] end + return { x, y, z } +end) + +/******************************************************************************/ + +--- Returns a vector in the same direction as , with a length clamped between (min) and (max) +e2function vector clamp(vector Input, Min, Max) + if Min < 0 then Min = 0 end + local x,y,z = Input[1], Input[2], Input[3] + local length = x*x+y*y+z*z + if length < Min*Min then + length = Min*(length ^ -0.5) -- Min*(length ^ -0.5) <=> Min/sqrt(length) + elseif length > Max*Max then + length = Max*(length ^ -0.5) -- Max*(length ^ -0.5) <=> Max/sqrt(length) + else + return Input + end + + return { x*length, y*length, z*length } +end + +/******************************************************************************/ + +registerFunction("x", "v:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] +end) + +registerFunction("y", "v:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[2] +end) + +registerFunction("z", "v:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[3] +end) + +// SET methods that returns vectors +registerFunction("setX", "v:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv2, rv1[2], rv1[3] } +end) + +registerFunction("setY", "v:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv2, rv1[3] } +end) + +registerFunction("setZ", "v:n", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv2 } +end) + +/******************************************************************************/ + +registerFunction("round", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - (rv1[1] + 0.5) % 1 + 0.5 + local y = rv1[2] - (rv1[2] + 0.5) % 1 + 0.5 + local z = rv1[3] - (rv1[3] + 0.5) % 1 + 0.5 + return {x, y, z} +end) + +registerFunction("round", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf + 0.5) % 1 + 0.5) / shf + local y = rv1[2] - ((rv1[2] * shf + 0.5) % 1 + 0.5) / shf + local z = rv1[3] - ((rv1[3] * shf + 0.5) % 1 + 0.5) / shf + return {x, y, z} +end) + +registerFunction("ceil", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - rv1[1] % -1 + local y = rv1[2] - rv1[2] % -1 + local z = rv1[3] - rv1[3] % -1 + return {x, y, z} +end) + +registerFunction("ceil", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf) % -1) / shf + local y = rv1[2] - ((rv1[2] * shf) % -1) / shf + local z = rv1[3] - ((rv1[3] * shf) % -1) / shf + return {x, y, z} +end) + +registerFunction("floor", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - rv1[1] % 1 + local y = rv1[2] - rv1[2] % 1 + local z = rv1[3] - rv1[3] % 1 + return {x, y, z} +end) + +registerFunction("floor", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf) % 1) / shf + local y = rv1[2] - ((rv1[2] * shf) % 1) / shf + local z = rv1[3] - ((rv1[3] * shf) % 1) / shf + return {x, y, z} +end) + +// min/max based on vector length - returns shortest/longest vector +registerFunction("min", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local length1 = ( rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] ) ^ 0.5 + local length2 = ( rv2[1] * rv2[1] + rv2[2] * rv2[2] + rv2[3] * rv2[3] ) ^ 0.5 + if length1 < length2 then return rv1 else return rv2 end +end) + +registerFunction("max", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local length1 = ( rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] ) ^ 0.5 + local length2 = ( rv2[1] * rv2[1] + rv2[2] * rv2[2] + rv2[3] * rv2[3] ) ^ 0.5 + if length1 > length2 then return rv1 else return rv2 end +end) + +// component-wise min/max +registerFunction("maxVec", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y, z + if rv1[1] > rv2[1] then x = rv1[1] else x = rv2[1] end + if rv1[2] > rv2[2] then y = rv1[2] else y = rv2[2] end + if rv1[3] > rv2[3] then z = rv1[3] else z = rv2[3] end + return {x, y, z} +end) + +registerFunction("minVec", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y, z + if rv1[1] < rv2[1] then x = rv1[1] else x = rv2[1] end + if rv1[2] < rv2[2] then y = rv1[2] else y = rv2[2] end + if rv1[3] < rv2[3] then z = rv1[3] else z = rv2[3] end + return {x, y, z} +end) + +// Performs modulo on x,y,z separately +registerFunction("mod", "vn", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x,y,z + if rv1[1] >= 0 then + x = rv1[1] % rv2 + else x = rv1[1] % -rv2 end + if rv1[2] >= 0 then + y = rv1[2] % rv2 + else y = rv1[2] % -rv2 end + if rv1[3] >= 0 then + z = rv1[3] % rv2 + else z = rv1[3] % -rv2 end + return {x, y, z} +end) + +// Modulo where divisors are defined as a vector +registerFunction("mod", "vv", "v", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x,y,z + if rv1[1] >= 0 then + x = rv1[1] % rv2[1] + else x = rv1[1] % -rv2[1] end + if rv1[2] >= 0 then + y = rv1[2] % rv2[2] + else y = rv1[2] % -rv2[2] end + if rv1[3] >= 0 then + z = rv1[3] % rv2[3] + else z = rv1[3] % -rv2[3] end + return {x, y, z} +end) + +// Clamp according to limits defined by two min/max vectors +registerFunction("clamp", "vvv", "v", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local x,y,z + + if rv1[1] < rv2[1] then x = rv2[1] + elseif rv1[1] > rv3[1] then x = rv3[1] + else x = rv1[1] end + + if rv1[2] < rv2[2] then y = rv2[2] + elseif rv1[2] > rv3[2] then y = rv3[2] + else y = rv1[2] end + + if rv1[3] < rv2[3] then z = rv2[3] + elseif rv1[3] > rv3[3] then z = rv3[3] + else z = rv1[3] end + + return {x, y, z} +end) + +// Mix two vectors by a given proportion (between 0 and 1) +registerFunction("mix", "vvn", "v", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local n + if rv3 < 0 then n = 0 + elseif rv3 > 1 then n = 1 + else n = rv3 end + local x = rv1[1] * n + rv2[1] * (1-n) + local y = rv1[2] * n + rv2[2] * (1-n) + local z = rv1[3] * n + rv2[3] * (1-n) + return {x, y, z} +end) + +// Circular shift function: shiftr( x,y,z ) = ( z,x,y ) +registerFunction("shiftR", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[3], rv1[1], rv1[2]} +end) + +registerFunction("shiftL", "v", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[2], rv1[3], rv1[1]} +end) + +// Returns 1 if the vector lies between (or is equal to) the min/max vectors +registerFunction("inrange", "vvv", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + + if rv1[1] < rv2[1] then return 0 end + if rv1[2] < rv2[2] then return 0 end + if rv1[3] < rv2[3] then return 0 end + + if rv1[1] > rv3[1] then return 0 end + if rv1[2] > rv3[2] then return 0 end + if rv1[3] > rv3[3] then return 0 end + + return 1 +end) + +/******************************************************************************/ + +registerFunction("toAngle", "v:", "a", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local angle = Vector(rv1[1], rv1[2], rv1[3]):Angle() + return { angle.p, angle.y, angle.r } +end) + +/******************************************************************************/ + +registerFunction("isInWorld", "v:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if util.IsInWorld(Vector(rv1[1], rv1[2], rv1[3])) then return 1 else return 0 end +end) + +--- Gets the vector nicely formatted as a string "[X,Y,Z]" +e2function string toString(vector v) + return "[" .. tostring(v[1]) .. "," .. tostring(v[2]) .. "," .. tostring(v[3]) .. "]" +end + +--- Gets the vector nicely formatted as a string "[X,Y,Z]" +e2function string vector:toString() = e2function string toString(vector v) + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/vector2.lua b/lua/entities/gmod_wire_expression2/core/vector2.lua new file mode 100644 index 0000000000..dcdfb99414 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/vector2.lua @@ -0,0 +1,988 @@ +/******************************************************************************\ + 2D Vector support +\******************************************************************************/ + +local delta = wire_expression2_delta + +/******************************************************************************/ + +registerType("vector2", "xv2", { 0, 0 }, + function(self, input) return { input[1], input[2] } end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 2 then error("Return value does not have exactly 2 entries!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("vec2", "", "xv2", function(self, args) + return { 0, 0 } +end) + +__e2setcost(3) -- temporary + +registerFunction("vec2", "nn", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1, rv2 } +end) + +registerFunction("vec2", "v", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2] } +end) + +registerFunction("vec2", "xv4", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2] } +end) + +/******************************************************************************/ + +registerOperator("ass", "xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ + +registerOperator("is", "xv2", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] > delta || -rv1[1] > delta || + rv1[2] > delta || -rv1[2] > delta + then return 1 else return 0 end +end) + +registerOperator("eq", "xv2xv2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "xv2xv2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta || rv2[1] - rv1[1] > delta || + rv1[2] - rv2[2] > delta || rv2[2] - rv1[2] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ + +registerOperator("dlt", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2] } +end) + +registerOperator("neg", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2] } +end) + +registerOperator("add", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2] } +end) + +registerOperator("sub", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2] } +end) + +registerOperator("mul", "nxv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2] } +end) + +registerOperator("mul", "xv2n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2 } +end) + +registerOperator("mul", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1], rv1[2] * rv2[2] } +end) + +registerOperator("div", "nxv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 / rv2[1], rv1 / rv2[2] } +end) + +registerOperator("div", "xv2n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2 } +end) + +registerOperator("div", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2[1], rv1[2] / rv2[2] } +end) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +registerFunction("length", "xv2:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return (rv1[1] * rv1[1] + rv1[2] * rv1[2] ) ^ 0.5 +end) + +registerFunction("length2", "xv2:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] * rv1[1] + rv1[2] * rv1[2] +end) + +registerFunction("distance", "xv2:xv2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local rvd1, rvd2 = rv1[1] - rv2[1], rv1[2] - rv2[2] + return (rvd1 * rvd1 + rvd2 * rvd2 ) ^ 0.5 +end) + +registerFunction("distance2", "xv2:xv2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local rvd1, rvd2 = rv1[1] - rv2[1], rv1[2] - rv2[2] + return rvd1 * rvd1 + rvd2 * rvd2 +end) + +registerFunction("normalized", "xv2:", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local len = (rv1[1] * rv1[1] + rv1[2] * rv1[2] ) ^ 0.5 + if len > delta then + return { rv1[1] / len, rv1[2] / len } + else + return { 0, 0 } + end +end) + +registerFunction("dot", "xv2:xv2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1[1] * rv2[1] + rv1[2] * rv2[2] +end) + +registerFunction("cross", "xv2:xv2", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1[1] * rv2[2] - rv1[2] * rv2[1] +end) + +registerFunction("rotate", "xv2:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local a = rv2 * 3.14159265 / 180 + local x = math.cos(a) * rv1[1] - math.sin(a) * rv1[2] + local y = math.sin(a) * rv1[1] - math.cos(a) * rv1[2] + return { x, y } +end) + +registerFunction("positive", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x, y + if rv1[1] >= 0 then x = rv1[1] else x = -rv1[1] end + if rv1[2] >= 0 then y = rv1[2] else y = -rv1[2] end + return { x, y } +end) + + +/******************************************************************************/ + +--- Returns a vector in the same direction as , with a length clamped between (min) and (max) +e2function vector2 clamp(vector2 Input, Min, Max) + if Min < 0 then Min = 0 end + local x,y = Input[1], Input[2] + local length = x*x+y*y + if length < Min*Min then + length = Min*(length ^ -0.5) -- Min*(length ^ -0.5) <=> Min/sqrt(length) + elseif length > Max*Max then + length = Max*(length ^ -0.5) -- Max*(length ^ -0.5) <=> Max/sqrt(length) + else + return Input + end + + return { x*length, y*length } +end + +/******************************************************************************/ + +registerFunction("x", "xv2:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] +end) + +registerFunction("y", "xv2:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[2] +end) + +// SET methods that returns vectors - you shouldn't need these for 2D vectors, but I've added them anyway for consistency +// NOTE: does not change the original vector! +registerFunction("setX", "xv2:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv2, rv1[2] } +end) + +registerFunction("setY", "xv2:n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv2 } +end) + +/******************************************************************************/ + +registerFunction("round", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - (rv1[1] + 0.5) % 1 + 0.5 + local y = rv1[2] - (rv1[2] + 0.5) % 1 + 0.5 + return { x, y } +end) + +registerFunction("round", "xv2n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf + 0.5) % 1 + 0.5) / shf + local y = rv1[2] - ((rv1[2] * shf + 0.5) % 1 + 0.5) / shf + return { x, y } +end) + +registerFunction("ceil", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - rv1[1] % -1 + local y = rv1[2] - rv1[2] % -1 + return { x, y } +end) + +registerFunction("ceil", "xv2n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf) % -1) / shf + local y = rv1[2] - ((rv1[2] * shf) % -1) / shf + return { x, y } +end) + +registerFunction("floor", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - rv1[1] % 1 + local y = rv1[2] - rv1[2] % 1 + return { x, y } +end) + +registerFunction("floor", "xv2n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf) % 1) / shf + local y = rv1[2] - ((rv1[2] * shf) % 1) / shf + return { x, y } +end) + +// min/max based on vector length - returns shortest/longest vector +registerFunction("min", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local length1 = ( rv1[1] * rv1[1] + rv1[2] * rv1[2] ) ^ 0.5 + local length2 = ( rv2[1] * rv2[1] + rv2[2] * rv2[2] ) ^ 0.5 + if length1 < length2 then return rv1 else return rv2 end +end) + +registerFunction("max", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local length1 = ( rv1[1] * rv1[1] + rv1[2] * rv1[2] ) ^ 0.5 + local length2 = ( rv2[1] * rv2[1] + rv2[2] * rv2[2] ) ^ 0.5 + if length1 > length2 then return rv1 else return rv2 end +end) + +// component-wise min/max +registerFunction("maxVec", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y + if rv1[1] > rv2[1] then x = rv1[1] else x = rv2[1] end + if rv1[2] > rv2[2] then y = rv1[2] else y = rv2[2] end + return {x, y} +end) + +registerFunction("minVec", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y + if rv1[1] < rv2[1] then x = rv1[1] else x = rv2[1] end + if rv1[2] < rv2[2] then y = rv1[2] else y = rv2[2] end + return {x, y} +end) + +// Performs modulo on x,y separately +registerFunction("mod", "xv2n", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y + + if rv1[1] >= 0 then + x = rv1[1] % rv2 + else x = rv1[1] % -rv2 end + + if rv1[2] >= 0 then + y = rv1[2] % rv2 + else y = rv1[2] % -rv2 end + + return { x, y } +end) + +// Modulo where divisors are defined as a vector +registerFunction("mod", "xv2xv2", "xv2", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y + + if rv1[1] >= 0 then + x = rv1[1] % rv2[1] + else x = rv1[1] % -rv2[1] end + + if rv1[2] >= 0 then + y = rv1[2] % rv2[2] + else y = rv1[2] % -rv2[2] end + + return { x, y } +end) + +// Clamp according to limits defined by two min/max vectors +registerFunction("clamp", "xv2xv2xv2", "xv2", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local x, y + + if rv1[1] < rv2[1] then x = rv2[1] + elseif rv1[1] > rv3[1] then x = rv3[1] + else x = rv1[1] end + + if rv1[2] < rv2[2] then y = rv2[2] + elseif rv1[2] > rv3[2] then y = rv3[2] + else y = rv1[2] end + + return { x, y } +end) + +// Mix two vectors by a given proportion (between 0 and 1) +registerFunction("mix", "xv2xv2n", "xv2", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local n + + if rv3 < 0 then n = 0 + elseif rv3 > 1 then n = 1 + else n = rv3 end + + local x = rv1[1] * n + rv2[1] * (1-n) + local y = rv1[2] * n + rv2[2] * (1-n) + return { x, y } +end) + +// swap x/y +registerFunction("shift", "xv2", "xv2", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[2], rv1[1] } +end) + +// Returns 1 if the vector lies between (or is equal to) the min/max vectors +registerFunction("inrange", "xv2xv2xv2", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + + if rv1[1] < rv2[1] then return 0 end + if rv1[2] < rv2[2] then return 0 end + if rv1[1] > rv3[1] then return 0 end + if rv1[2] > rv3[2] then return 0 end + + return 1 +end) + +/******************************************************************************/ + +registerFunction("toAngle", "xv2:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local angle = math.atan2( rv1[2], rv1[1] ) * 180 / 3.14159265 + --if (angle < 0) then angle = angle + 180 end + return angle +end) + +e2function string toString(vector2 v) + return "[" .. tostring(v[1]) .. "," .. tostring(v[2]) .. "]" +end + +e2function string vector2:toString() = e2function string toString(vector2 v) + +-- register a formatter for the debugger +WireLib.registerDebuggerFormat("VECTOR2", function(value) + return "(" .. math.Round(value[1]*10)/10 .. "," .. math.Round(value[2]*10)/10 .. ")" +end) + +/******************************************************************************\ + 4D Vector support +\******************************************************************************/ + +//NOTE: These are purely cartesian 4D vectors, so "w" denotes the 4th coordinate rather than a scaling factor as with an homogeneous coordinate system + +/******************************************************************************/ + +registerType("vector4", "xv4", { 0, 0, 0, 0 }, + function(self, input) return { input[1], input[2], input[3], input[4] } end, + nil, + function(retval) + if type(retval) ~= "table" then error("Return value is not a table, but a "..type(retval).."!",0) end + if #retval ~= 4 then error("Return value does not have exactly 4 entries!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(1) -- approximated + +registerFunction("vec4", "", "xv4", function(self, args) + return { 0, 0, 0, 0 } +end) + +__e2setcost(3) -- temporary + +registerFunction("vec4", "nnnn", "xv4", function(self, args) + local op1, op2, op3, op4 = args[2], args[3], args[4], args[5] + local rv1, rv2, rv3, rv4 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3), op4[1](self, op4) + return { rv1, rv2, rv3, rv4 } +end) + +registerFunction("vec4", "xv2", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], 0, 0 } +end) + +registerFunction("vec4", "xv2nn", "xv4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + return { rv1[1], rv1[2], rv2, rv3 } +end) + +registerFunction("vec4", "xv2xv2", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv2[1], rv2[2] } +end) + +registerFunction("vec4", "v", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { rv1[1], rv1[2], rv1[3], 0 } +end) + +registerFunction("vec4", "vn", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv1[3], rv2 } +end) + +/******************************************************************************/ + +registerOperator("ass", "xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv2 = op2[1](self, op2) + self.vars[op1] = rv2 + self.vclk[op1] = true + return rv2 +end) + +/******************************************************************************/ + +registerOperator("is", "xv4", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if rv1[1] > delta || -rv1[1] > delta || + rv1[2] > delta || -rv1[2] > delta || + rv1[3] > delta || -rv1[3] > delta || + rv1[4] > delta || -rv1[4] > delta + then return 1 else return 0 end +end) + +registerOperator("eq", "xv4xv4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] <= delta && rv2[1] - rv1[1] <= delta && + rv1[2] - rv2[2] <= delta && rv2[2] - rv1[2] <= delta && + rv1[3] - rv2[3] <= delta && rv2[3] - rv1[3] <= delta && + rv1[4] - rv2[4] <= delta && rv2[4] - rv1[4] <= delta + then return 1 else return 0 end +end) + +registerOperator("neq", "xv4xv4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if rv1[1] - rv2[1] > delta || rv2[1] - rv1[1] > delta || + rv1[2] - rv2[2] > delta || rv2[2] - rv1[2] > delta || + rv1[3] - rv2[3] > delta || rv2[3] - rv1[3] > delta || + rv1[4] - rv2[4] > delta || rv2[4] - rv1[4] > delta + then return 1 else return 0 end +end) + +/******************************************************************************/ + +registerOperator("dlt", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1, rv2 = self.vars[op1], self.vars["$" .. op1] + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], rv1[4] - rv2[4] } +end) + +registerOperator("neg", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return { -rv1[1], -rv1[2], -rv1[3], -rv[4] } +end) + +registerOperator("add", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] + rv2[1], rv1[2] + rv2[2], rv1[3] + rv2[3], rv1[4] + rv2[4] } +end) + +registerOperator("sub", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], rv1[4] - rv2[4] } +end) + +registerOperator("mul", "nxv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 * rv2[1], rv1 * rv2[2], rv1 * rv2[3], rv1 * rv2[4] } +end) + +registerOperator("mul", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2, rv1[2] * rv2, rv1[3] * rv2, rv1[4] * rv2 } +end) + +registerOperator("mul", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] * rv2[1], rv1[2] * rv2[2], rv1[3] * rv2[3], rv1[4] * rv2[4] } +end) + +registerOperator("div", "nxv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1 / rv2[1], rv1 / rv2[2], rv1 / rv2[3], rv1 / rv2[4] } +end) + +registerOperator("div", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2, rv1[2] / rv2, rv1[3] / rv2, rv1[4] / rv2 } +end) + +registerOperator("div", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1] / rv2[1], rv1[2] / rv2[2], rv1[3] / rv2[3], rv1[4] / rv2[4] } +end) + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +registerFunction("length", "xv4:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return (rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] + rv1[4] * rv1[4]) ^ 0.5 +end) + +registerFunction("length2", "xv4:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] + rv1[4] * rv1[4] +end) + +registerFunction("distance", "xv4:xv4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local rvd1, rvd2, rvd3, rvd4 = rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], rv1[4] - rv2[4] + return (rvd1 * rvd1 + rvd2 * rvd2 + rvd3 * rvd3 + rvd4 * rvd4) ^ 0.5 +end) + +registerFunction("distance2", "xv4:xv4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local rvd1, rvd2, rvd3 = rv1[1] - rv2[1], rv1[2] - rv2[2], rv1[3] - rv2[3], rv1[4] - rv2[4] + return rvd1 * rvd1 + rvd2 * rvd2 + rvd3 * rvd3 + rvd4 * rvd4 +end) + +registerFunction("dot", "xv4:xv4", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return rv1[1] * rv2[1] + rv1[2] * rv2[2] + rv1[3] * rv2[3] + rv1[4] * rv2[4] +end) + +registerFunction("normalized", "xv4:", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local len = (rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] + rv1[4] * rv1[4]) ^ 0.5 + if len > delta then + return { rv1[1] / len, rv1[2] / len, rv1[3] / len, rv1[4] / len } + else + return { 0, 0, 0, 0 } + end +end) + +registerFunction("dehomogenized", "xv4:", "v", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local w = rv1[4] + if w == 0 then return { rv1[1], rv1[2], rv1[3] } end + return { rv1[1]/w, rv1[2]/w, rv1[3]/w } +end) + +registerFunction("positive", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x, y, z, w + if rv1[1] >= 0 then x = rv1[1] else x = -rv1[1] end + if rv1[2] >= 0 then y = rv1[2] else y = -rv1[2] end + if rv1[3] >= 0 then z = rv1[3] else z = -rv1[3] end + if rv1[4] >= 0 then w = rv1[4] else w = -rv1[4] end + return { x, y, z, w } +end) + +/******************************************************************************/ + +registerFunction("x", "xv4:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[1] +end) + +registerFunction("y", "xv4:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[2] +end) + +registerFunction("z", "xv4:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[3] +end) + +registerFunction("w", "xv4:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return rv1[4] +end) + +// SET methods that returns vectors +// NOTE: does not change the original vector! +registerFunction("setX", "xv4:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv2, rv1[2], rv1[3], rv1[4] } +end) + +registerFunction("setY", "xv4:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv2, rv1[3], rv1[4] } +end) + +registerFunction("setZ", "xv4:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv2, rv1[4] } +end) + +registerFunction("setW", "xv4:n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + return { rv1[1], rv1[2], rv1[3], rv2 } +end) + +/******************************************************************************/ + +registerFunction("round", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - (rv1[1] + 0.5) % 1 + 0.5 + local y = rv1[2] - (rv1[2] + 0.5) % 1 + 0.5 + local z = rv1[3] - (rv1[3] + 0.5) % 1 + 0.5 + local w = rv1[4] - (rv1[4] + 0.5) % 1 + 0.5 + return {x, y, z, w} +end) + +registerFunction("round", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf + 0.5) % 1 + 0.5) / shf + local y = rv1[2] - ((rv1[2] * shf + 0.5) % 1 + 0.5) / shf + local z = rv1[3] - ((rv1[3] * shf + 0.5) % 1 + 0.5) / shf + local w = rv1[4] - ((rv1[4] * shf + 0.5) % 1 + 0.5) / shf + return {x, y, z, w} +end) + +registerFunction("ceil", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - rv1[1] % -1 + local y = rv1[2] - rv1[2] % -1 + local z = rv1[3] - rv1[3] % -1 + local w = rv1[4] - rv1[4] % -1 + return {x, y, z, w} +end) + +registerFunction("ceil", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf) % -1) / shf + local y = rv1[2] - ((rv1[2] * shf) % -1) / shf + local z = rv1[3] - ((rv1[3] * shf) % -1) / shf + local w = rv1[4] - ((rv1[4] * shf) % -1) / shf + return {x, y, z, w} +end) + +registerFunction("floor", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + local x = rv1[1] - rv1[1] % 1 + local y = rv1[2] - rv1[2] % 1 + local z = rv1[3] - rv1[3] % 1 + local w = rv1[4] - rv1[4] % 1 + return {x, y, z, w} +end) + +registerFunction("floor", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local shf = 10 ^ rv2 + local x = rv1[1] - ((rv1[1] * shf) % 1) / shf + local y = rv1[2] - ((rv1[2] * shf) % 1) / shf + local z = rv1[3] - ((rv1[3] * shf) % 1) / shf + local w = rv1[4] - ((rv1[4] * shf) % 1) / shf + return {x, y, z, w} +end) + +// min/max based on vector length - returns shortest/longest vector +registerFunction("min", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local length1 = ( rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] + rv1[4] * rv1[4] ) ^ 0.5 + local length2 = ( rv2[1] * rv2[1] + rv2[2] * rv2[2] + rv2[3] * rv2[3] + rv2[4] * rv2[4] ) ^ 0.5 + if length1 < length2 then return rv1 else return rv2 end +end) + +registerFunction("max", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local length1 = ( rv1[1] * rv1[1] + rv1[2] * rv1[2] + rv1[3] * rv1[3] + rv1[4] * rv1[4] ) ^ 0.5 + local length2 = ( rv2[1] * rv2[1] + rv2[2] * rv2[2] + rv2[3] * rv2[3] + rv2[4] * rv2[4] ) ^ 0.5 + if length1 > length2 then return rv1 else return rv2 end +end) + +// component-wise min/max +registerFunction("maxVec", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y, z, w + if rv1[1] > rv2[1] then x = rv1[1] else x = rv2[1] end + if rv1[2] > rv2[2] then y = rv1[2] else y = rv2[2] end + if rv1[3] > rv2[3] then z = rv1[3] else z = rv2[3] end + if rv1[4] > rv2[4] then w = rv1[4] else w = rv2[4] end + return {x, y, z, w} +end) + +registerFunction("minVec", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x, y, z, w + if rv1[1] < rv2[1] then x = rv1[1] else x = rv2[1] end + if rv1[2] < rv2[2] then y = rv1[2] else y = rv2[2] end + if rv1[3] < rv2[3] then z = rv1[3] else z = rv2[3] end + if rv1[4] < rv2[4] then w = rv1[4] else w = rv2[4] end + return {x, y, z, w} +end) + +// Performs modulo on x,y,z separately +registerFunction("mod", "xv4n", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x,y,z,w + if rv1[1] >= 0 then + x = rv1[1] % rv2 + else x = rv1[1] % -rv2 end + if rv1[2] >= 0 then + y = rv1[2] % rv2 + else y = rv1[2] % -rv2 end + if rv1[3] >= 0 then + z = rv1[3] % rv2 + else z = rv1[3] % -rv2 end + if rv1[4] >= 0 then + w = rv1[4] % rv2 + else w = rv1[4] % -rv2 end + return {x, y, z, w} +end) + +// Modulo where divisors are defined as a vector +registerFunction("mod", "xv4xv4", "xv4", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + local x,y,z,w + if rv1[1] >= 0 then + x = rv1[1] % rv2[1] + else x = rv1[1] % -rv2[1] end + if rv1[2] >= 0 then + y = rv1[2] % rv2[2] + else y = rv1[2] % -rv2[2] end + if rv1[3] >= 0 then + z = rv1[3] % rv2[3] + else z = rv1[3] % -rv2[3] end + if rv1[4] >= 0 then + w = rv1[4] % rv2[3] + else w = rv1[4] % -rv2[3] end + return {x, y, z, w} +end) + +// Clamp according to limits defined by two min/max vectors +registerFunction("clamp", "xv4xv4xv4", "xv4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local x,y,z,w + + if rv1[1] < rv2[1] then x = rv2[1] + elseif rv1[1] > rv3[1] then x = rv3[1] + else x = rv1[1] end + + if rv1[2] < rv2[2] then y = rv2[2] + elseif rv1[2] > rv3[2] then y = rv3[2] + else y = rv1[2] end + + if rv1[3] < rv2[3] then z = rv2[3] + elseif rv1[3] > rv3[3] then z = rv3[3] + else z = rv1[3] end + + if rv1[4] < rv2[4] then w = rv2[4] + elseif rv1[4] > rv3[4] then w = rv3[4] + else w = rv1[4] end + + return {x, y, z, w} +end) + +--- Returns a vector in the same direction as , with a length clamped between (min) and (max) +e2function vector4 clamp(vector4 Input, Min, Max) + if Min < 0 then Min = 0 end + local x,y,z,w = Input[1], Input[2], Input[3], Input[4] + local length = x*x+y*y+z*z+w*w + if length < Min*Min then + length = Min*(length ^ -0.5) -- Min*(length ^ -0.5) <=> Min/sqrt(length) + elseif length > Max*Max then + length = Max*(length ^ -0.5) -- Max*(length ^ -0.5) <=> Max/sqrt(length) + else + return Input + end + + return { x*length, y*length, z*length, w*length } +end + +// Mix two vectors by a given proportion (between 0 and 1) +registerFunction("mix", "xv4xv4n", "xv4", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + local n + if rv3 < 0 then n = 0 + elseif rv3 > 1 then n = 1 + else n = rv3 end + local x = rv1[1] * n + rv2[1] * (1-n) + local y = rv1[2] * n + rv2[2] * (1-n) + local z = rv1[3] * n + rv2[3] * (1-n) + local w = rv1[4] * n + rv2[4] * (1-n) + return {x, y, z, w} +end) + +// Circular shift function: shiftR( x,y,z,w ) = ( w,x,y,z ) +registerFunction("shiftR", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[4], rv1[1], rv1[2], rv1[3]} +end) + +registerFunction("shiftL", "xv4", "xv4", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + return {rv1[2], rv1[3], rv1[4], rv1[1]} +end) + +// Returns 1 if the vector lies between (or is equal to) the min/max vectors +registerFunction("inrange", "xv4xv4xv4", "n", function(self, args) + local op1, op2, op3 = args[2], args[3], args[4] + local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) + + if rv1[1] < rv2[1] then return 0 end + if rv1[2] < rv2[2] then return 0 end + if rv1[3] < rv2[3] then return 0 end + if rv1[4] < rv2[4] then return 0 end + + if rv1[1] > rv3[1] then return 0 end + if rv1[2] > rv3[2] then return 0 end + if rv1[3] > rv3[3] then return 0 end + if rv1[4] > rv3[4] then return 0 end + + return 1 +end) + +/******************************************************************************/ + +e2function string toString(vector4 v) + return "[" .. tostring(v[1]) .. "," .. tostring(v[2]) .. "," .. tostring(v[3]) .. "," .. tostring(v[4]) .. "]" +end + +e2function string vector4:toString() = e2function string toString(vector4 v) + +-- register a formatter for the debugger +WireLib.registerDebuggerFormat("VECTOR4", function(value) + return "(" .. math.Round(value[1]*10)/10 .. "," .. math.Round(value[2]*10)/10 .. "," .. math.Round(value[3]*10)/10 .. "," .. math.Round(value[4]*10)/10 .. ")" +end) + +__e2setcost(nil) diff --git a/lua/entities/gmod_wire_expression2/core/weapon.lua b/lua/entities/gmod_wire_expression2/core/weapon.lua new file mode 100644 index 0000000000..066cd245bd --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/weapon.lua @@ -0,0 +1,50 @@ +/******************************************************************************\ + Player-weapon support +\******************************************************************************/ + +__e2setcost(2) -- temporary + +registerFunction("weapon", "e:", "e", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return nil end + if(rv1:IsPlayer() or rv1:IsNPC()) then return rv1:GetActiveWeapon() else return nil end +end) + + +registerFunction("primaryAmmoType", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return "" end + if(rv1:IsWeapon()) then return rv1:GetPrimaryAmmoType() else return "" end +end) + +registerFunction("secondaryAmmoType", "e:", "s", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return "" end + if(rv1:IsWeapon()) then return rv1:GetSecondaryAmmoType() else return "" end +end) + +registerFunction("ammoCount", "e:s", "n", function(self, args) + local op1, op2 = args[2], args[3] + local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsPlayer()) then return rv1:GetAmmoCount(rv2) else return 0 end +end) + +registerFunction("clip1", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsWeapon()) then return rv1:Clip1() else return 0 end +end) + +registerFunction("clip2", "e:", "n", function(self, args) + local op1 = args[2] + local rv1 = op1[1](self, op1) + if(!validEntity(rv1)) then return 0 end + if(rv1:IsWeapon()) then return rv1:Clip2() else return 0 end +end) + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/core/wirelink.lua b/lua/entities/gmod_wire_expression2/core/wirelink.lua new file mode 100644 index 0000000000..0b95fb7bf0 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/core/wirelink.lua @@ -0,0 +1,492 @@ +/******************************************************************************\ + Wire link support +\******************************************************************************/ + +local triggercache = {} +registerCallback("postexecute", function(self) + for _,ent,portname,value in pairs_map(triggercache, unpack) do + WireLib.TriggerInput(ent, portname, value) + end + + triggercache = {} +end) + +local function TriggerInput(ent, portname, value, typename) + if not ent.Inputs[portname] then return value end + if ent.Inputs[portname].Type ~= typename then return value end + + triggercache[ent:EntIndex().."__"..portname] = { ent, portname, value } + + return value +end + +/******************************************************************************/ + +local function WriteStringZero(entity, address, string) + if not entity:WriteCell(address+#string, 0) then return 0 end + + for index = 1,#string do + local byte = string.byte(string,index) + if not entity:WriteCell(address+index-1, byte) then return 0 end + end + return address+#string+1 +end + +local function ReadStringZero(entity, address) + local byte + local tbl = {} + for index = address,address+16384 do + byte = entity:ReadCell(index, byte) + if not byte then return "" end + if byte < 1 then break end + if byte >= 256 then byte = 32 end + table.insert(tbl,string.char(math.floor(byte))) + end + return table.concat(tbl) +end + +local wa_lookup -- lookup table for tables that were already serialized. +local function WriteArray(entity, address, data) + -- check if enough space is available + if not entity:WriteCell(address+#data-1, 0) then return 0 end + + -- write the trailing 0 byte. + entity:WriteCell(address+#data, 0) + local free_address = address+#data+1 + + for index, value in ipairs(data) do + local tp = type(value) + if tp == "number" then + if not entity:WriteCell(address+index-1, value) then return 0 end + elseif tp == "string" then + if not entity:WriteCell(address+index-1, free_address) then return 0 end + free_address = WriteStringZero(entity, free_address, value) + if free_address == 0 then return 0 end + elseif tp == "table" then + if wa_lookup[value] then + if not entity:WriteCell(address+index-1, wa_lookup[value]) then return 0 end + else + wa_lookup[value] = free_address + if not entity:WriteCell(address+index-1, free_address) then return 0 end + free_address = WriteArray(entity, free_address, value) + end + elseif tp == "Vector" then + if not entity:WriteCell(address+index-1, free_address) then return 0 end + free_address = WriteArray(entity, free_address, { value[1], value[2], value[3] }) + end + end + return free_address +end + +/******************************************************************************/ + +registerType("wirelink", "xwl", nil, + nil, + nil, + function(retval) + if validEntity(retval) then return end + if retval ~= nil and retval.EntIndex then error("Return value is neither nil nor an Entity, but a "..type(retval).."!",0) end + end +) + +/******************************************************************************/ + +__e2setcost(2) -- temporary + +e2function wirelink operator=(wirelink lhs, wirelink rhs) + self.vars[lhs] = rhs + self.vclk[lhs] = true + return rhs +end + +registerCallback("postinit", function() + -- retrieve information about all registered types + local types = table.Copy(wire_expression_types) + + -- change the name for numbers from "NORMAL" to "NUMBER" + types["NUMBER"] = types["NORMAL"] + types["NORMAL"] = nil + + -- generate op[] for all types + for name,id in pairs_map(types, unpack) do + + -- XWL:number() etc + local getter = name:lower() + + -- XWL:setNumber() etc + local setter = "set"..name:sub(1,1):upper()..name:sub(2):lower() + + local getf = wire_expression2_funcs[getter.."(xwl:s)"] + local setf = wire_expression2_funcs[setter.."(xwl:s"..id..")"] + + if getf then + local f = getf.oldfunc or getf[3] -- use oldfunc if present, else func + if getf then + registerOperator("idx", id.."=xwls", id, f, getf[4], getf[5]) + end + end + if setf then + local f = setf.oldfunc or setf[3] -- use oldfunc if present, else func + if setf then + registerOperator("idx", id.."=xwls"..id, id, f, setf[4], setf[5]) + end + end + end +end) + +/******************************************************************************/ + +e2function number operator_is(wirelink value) + if not validEntity(value) then return 0 end + if value.extended then return 1 else return 0 end +end + +e2function number operator==(wirelink lhs, wirelink rhs) + if lhs == rhs then return 1 else return 0 end +end + +e2function number operator!=(wirelink lhs, wirelink rhs) + if lhs ~= rhs then return 1 else return 0 end +end + +/******************************************************************************/ + +e2function number wirelink:isHiSpeed() + if not validEntity(this) then return 0 end + if not this.extended then return 0 end + if this.WriteCell or this.ReadCell then return 1 else return 0 end +end + +e2function entity wirelink:entity() + return this +end + +/******************************************************************************/ + +e2function number wirelink:hasInput(string portname) + if not validEntity(this) then return 0 end + if not this.extended then return 0 end + + if not this.Inputs[portname] then return 0 end + return 1 +end + +e2function number wirelink:hasOutput(string portname) + if not validEntity(this) then return 0 end + if not this.extended then return 0 end + + if not this.Outputs[portname] then return 0 end + return 1 +end + +/******************************************************************************/ +// THESE NEED TO USE THE INPUT/OUTPUT SERIALIZERS! (not numbers) +// THE VALUES SHOULD BE SAVED AND PUSHED ON POST EXECUTION + +__e2setcost(5) -- temporary + +e2function number wirelink:setNumber(string portname, value) + if not validEntity(this) then return value end + if not this.extended then return value end + + TriggerInput(this, portname, value, "NORMAL") + return value +end + +e2function number wirelink:number(string portname) + if not validEntity(this) then return 0 end + if not this.extended then return 0 end + + if not this.Outputs[portname] then return 0 end + if this.Outputs[portname].Type ~= "NORMAL" then return 0 end + + return this.Outputs[portname].Value +end + + +e2function vector wirelink:setVector(string portname, vector value) + if not validEntity(this) then return value end + if not this.extended then return value end + + value = Vector(value[1], value[2], value[3]) + TriggerInput(this, portname, value, "VECTOR") + return value +end + +e2function vector wirelink:vector(string portname) + if not validEntity(this) then return { 0, 0, 0 } end + if not this.extended then return { 0, 0, 0 } end + + if not this.Outputs[portname] then return { 0, 0, 0 } end + if this.Outputs[portname].Type ~= "VECTOR" then return { 0, 0, 0 } end + + return this.Outputs[portname].Value +end + + +e2function angle wirelink:setAngle(string portname, angle value) + if not validEntity(this) then return value end + if not this.extended then return value end + + local avalue = Angle(value[1], value[2], value[3]) + TriggerInput(this, portname, avalue, "ANGLE") + return value +end + +e2function angle wirelink:angle(string portname) + if not validEntity(this) then return { 0, 0, 0 } end + if not this.extended then return { 0, 0, 0 } end + + if not this.Outputs[portname] then return { 0, 0, 0 } end + if this.Outputs[portname].Type ~= "ANGLE" then return { 0, 0, 0 } end + + return this.Outputs[portname].Value +end + + +e2function entity wirelink:setEntity(string portname, entity value) + if not validEntity(this) then return value end + if not this.extended then return value end + + TriggerInput(this, portname, value, "ENTITY") + return value +end + +e2function entity wirelink:entity(string portname) + if not validEntity(this) then return nil end + if not this.extended then return nil end + + if not this.Outputs[portname] then return nil end + if this.Outputs[portname].Type ~= "ENTITY" then return nil end + + return this.Outputs[portname].Value +end + + +e2function string wirelink:setString(string portname, string value) + if not validEntity(this) then return value end + if not this.extended then return value end + + TriggerInput(this, portname, value, "STRING") + return value +end + +e2function string wirelink:string(string portname) + if not validEntity(this) then return "" end + if not this.extended then return "" end + + if not this.Outputs[portname] then return "" end + if this.Outputs[portname].Type ~= "STRING" then return "" end + + return this.Outputs[portname].Value +end + +__e2setcost(15) -- temporary + +e2function void wirelink:setXyz(vector value) + if not validEntity(this) then return end + if not this.extended then return end + + TriggerInput(this, "X", value[1], "NORMAL") + TriggerInput(this, "Y", value[2], "NORMAL") + TriggerInput(this, "Z", value[3], "NORMAL") +end + +e2function vector wirelink:xyz() + if not validEntity(this) then return { 0, 0, 0 } end + if not this.extended then return { 0, 0, 0 } end + + local x, y, z = this.Outputs["X"], this.Outputs["Y"], this.Outputs["Z"] + + if not x or not y or not z then return { 0, 0, 0 } end + if x.Type ~= "NORMAL" or y.Type ~= "NORMAL" or z.Type ~= "NORMAL" then return { 0, 0, 0 } end + return { x.Value, y.Value, z.Value } +end + +/******************************************************************************/ +-- XWL:inputs/outputs/inputType/outputType by jeremydeath + +--- Returns an array of all the inputs that has without their types. Returns an empty array if it has none +e2function array wirelink:inputs() + if(!validEntity(this)) then return {} end + if(!this.extended) then return {} end + if(!this.Inputs) then return {} end + + local InputNames = {} + for k,v in pairs_sortvalues(this.Inputs, WireLib.PortComparator) do + table.insert(InputNames,k) + end + return InputNames +end + +--- Returns an array of all the outputs that has without their types. Returns an empty array if it has none +e2function array wirelink:outputs() + if(!validEntity(this)) then return {} end + if(!this.extended) then return {} end + if(!this.Outputs) then return {} end + + local OutputNames = {} + for k,v in pairs_sortvalues(this.Outputs, WireLib.PortComparator) do + table.insert(OutputNames,k) + end + return OutputNames +end + +--- Returns the type of input that is in lowercase. ( "NORMAL" is changed to "number" ) +e2function string wirelink:inputType(string Input) + if(!validEntity(this)) then return "" end + if(!this.extended) then return "" end + if(!this.Inputs or !this.Inputs[Input]) then return "" end + + local Type = this.Inputs[Input].Type or "" + if Type == "NORMAL" then Type = "number" end + return string.lower(Type) +end + +--- Returns the type of output that is in lowercase. ( "NORMAL" is changed to "number" ) +e2function string wirelink:outputType(string Output) + if(!validEntity(this)) then return "" end + if(!this.extended) then return "" end + if(!this.Outputs or !this.Outputs[Output]) then return "" end + + local Type = this.Outputs[Output].Type or "" + if Type == "NORMAL" then Type = "number" end + return string.lower(Type) +end + +/******************************************************************************/ + +__e2setcost(5) -- temporary + +e2function number wirelink:writeCell(address, value) + if not validEntity(this) then return 0 end + if not this.extended then return 0 end + + if not this.WriteCell then return 0 end + if this:WriteCell(address, value) then return 1 else return 0 end +end + +e2function number wirelink:readCell(address) + if not validEntity(this) then return 0 end + if not this.extended then return 0 end + + if not this.ReadCell then return 0 end + return this:ReadCell(address) or 0 +end + +e2function number wirelink:operator[](address, value) + if not validEntity(this) then return value end + if not this.extended then return value end + + if not this.WriteCell then return value end + this:WriteCell(address, value) + return value +end +e2function number wirelink:operator[](address) = e2function number wirelink:readCell(address) + +/******************************************************************************/ + +__e2setcost(20) -- temporary + +--- XWL[N,vector]=V +e2function vector wirelink:operator[T](address, vector value) + if not validEntity(this) then return value end + if not this.extended then return value end + + if not this.WriteCell then return value end + this:WriteCell(address, value[1]) + this:WriteCell(address+1, value[2]) + this:WriteCell(address+2, value[3]) + return value +end + +--- V=XWL[N,vector] +e2function vector wirelink:operator[T](address) + if not validEntity(this) then return { 0, 0, 0 } end + if not this.extended then return { 0, 0, 0 } end + + if not this.ReadCell then return 0 end + return { + this:ReadCell(address) or 0, + this:ReadCell(address+1) or 0, + this:ReadCell(address+2) or 0, + } +end + +--- XWL[N,string]=S +e2function string wirelink:operator[T](address, string value) + if not validEntity(this) or not this.extended or not this.WriteCell then return "" end + WriteStringZero(this, address, value) + return value +end + +--- S=XWL[N,string] +e2function string wirelink:operator[T](address) + if not validEntity(this) or not this.extended or not this.ReadCell then return "" end + return ReadStringZero(this, address) +end + +/******************************************************************************/ + +__e2setcost(20) -- temporary + +local function WriteString(entity, string, X, Y, Tcolour, Bgcolour, Flash) + if not validEntity(entity) then return end + if not entity.extended or not entity.WriteCell then return end + + Tcolour = math.Clamp(math.floor(Tcolour), 0, 999) + Bgcolour = math.Clamp(math.floor(Bgcolour), 0, 999) + Flash = (Flash ~= 0) and 1 or 0 + local Params = Flash*1000000 + Bgcolour*1000 + Tcolour + + for N = 1,#string do + local Address = 2*(X+N-1+30*Y) + if (Address>1080 or Address<0) then return end + local Byte = string.byte(string,N) + entity:WriteCell(Address, Byte) + entity:WriteCell(Address+1, Params) + end +end + +e2function void wirelink:writeString(string text, x, y, textcolor, bgcolor, flash) + WriteString(this,text,x,y,textcolor,bgcolor,flash) +end + +e2function void wirelink:writeString(string text, x, y, textcolor, bgcolor) + WriteString(this,text,x,y,textcolor,bgcolor,0) +end + +e2function void wirelink:writeString(string text, x, y, textcolor) + WriteString(this,text,x,y,textcolor,0,0) +end + +e2function void wirelink:writeString(string text, x, y) + WriteString(this,text,x,y,999,0,0) +end + +/******************************************************************************/ + +--- Writes a null-terminated string to the given address. Returns the next free address or 0 on failure. +e2function number wirelink:writeString(address, string data) + if not validEntity(this) or not this.extended or not this.WriteCell then return 0 end + return WriteStringZero(this, address, data) +end + +--- Reads a null-terminated string from the given address. Returns an empty string on failure. +e2function string wirelink:readString(address) + if not validEntity(this) or not this.extended or not this.ReadCell then return "" end + return ReadStringZero(this, address) +end + +/******************************************************************************/ + +--- Writes an array's elements into a piece of memory. Strings and sub-tables (angles, vectors, matrices) are written as pointers to the actual data. Strings are written null-terminated. +e2function number wirelink:writeArray(address, array data) + if not validEntity(this) or not this.extended or not this.WriteCell then return 0 end + wa_lookup = {} + local ret = WriteArray(this,address,data) + wa_lookup = nil + return ret +end + +__e2setcost(nil) -- temporary diff --git a/lua/entities/gmod_wire_expression2/init.lua b/lua/entities/gmod_wire_expression2/init.lua new file mode 100644 index 0000000000..3bee9dc2ff --- /dev/null +++ b/lua/entities/gmod_wire_expression2/init.lua @@ -0,0 +1,393 @@ +-- a variable inside a single if-branch is discarded, even though that type should be forced for any consecutive assignments + +resource.AddFile("materials/expression 2/cog.vmt") +resource.AddFile("materials/expression 2/cog.vtf") + +AddCSLuaFile('init.lua') +AddCSLuaFile('cl_init.lua') +AddCSLuaFile('shared.lua') +include('shared.lua') + +CreateConVar("wire_expression2_unlimited", "0") +CreateConVar("wire_expression2_quotasoft", "5000") +CreateConVar("wire_expression2_quotahard", "100000") +CreateConVar("wire_expression2_quotatick", "25000") + +timer.Create("e2quota", 1, 0, function() + local unlimited = GetConVar("wire_expression2_unlimited"):GetInt() + e2_softquota = GetConVar("wire_expression2_quotasoft"):GetInt() + e2_hardquota = GetConVar("wire_expression2_quotahard"):GetInt() + e2_tickquota = GetConVar("wire_expression2_quotatick"):GetInt() + + if unlimited == 0 then + if e2_softquota < 5000 then e2_softquota = 5000 end + if e2_hardquota < 100000 then e2_hardquota = 100000 end + if e2_tickquota < 25000 then e2_tickquota = 25000 end + else + e2_softquota = 1000000 + e2_hardquota = 1000000 + e2_tickquota = 100000 + end +end) + +ENT.OverlayDelay = 0 +ENT.WireDebugName = "Expression 2" + +function tablekeys(tbl) + l = {} + for k,v in pairs(tbl) do + l[#l + 1] = k + end + return l +end + +function tablevalues(tbl) + l = {} + for k,v in pairs(tbl) do + l[#l + 1] = v + end + return l +end + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Inputs = WireLib.CreateInputs(self.Entity, {}) + self.Outputs = WireLib.CreateOutputs(self.Entity, {}) + + self:SetOverlayText("Expression 2\n(none)") + self:SetColor(255, 0, 0, 255) +end + +function ENT:OnRestore() + self:Setup(self.original) +end + +function ENT:Execute() + if self.error then return end + + e2_install_hook_fix() + self:PCallHook('preexecute') + + local ok, msg = pcall(self.script[1], self.context, self.script) + if not ok then + if msg == "exit" then + elseif msg == "perf" then + self:Error("Expression 2 (" .. self.name .. "): tick quota exceeded", "tick quota exceeded") + + else + self:Error("Expression 2 (" .. self.name .. "): " .. e2_processerror(msg), "script error") + end + end + + self.first = false -- if hooks call execute + self.duped = false -- if hooks call execute + self.context.triggerinput = nil -- if hooks call execute + + self:PCallHook('postexecute') + e2_remove_hook_fix() + + self:TriggerOutputs() + + for k,v in pairs(self.inports[3]) do + if self.context.vclk[k] then + if wire_expression_types[self.Inputs[k].Type][3] then + self.context.vars[k] = wire_expression_types[self.Inputs[k].Type][3](self.context, self.Inputs[k].Value) + else + self.context.vars[k] = self.Inputs[k].Value + end + end + end + + self.context.vclk = {} + + if self.context.prfcount + self.context.prf - e2_softquota > e2_hardquota then + self:Error("Expression 2 (" .. self.name .. "): tick quota exceeded", "hard quota exceeded") + end + + if self.error then self:PCallHook('destruct') end +end + +function ENT:Think() + self.BaseClass.Think(self) + self.Entity:NextThink(CurTime()) + + if self.context and not self.error then + self.context.prfbench = self.context.prfbench * 0.95 + self.context.prf * 0.05 + self.context.prfcount = self.context.prfcount + self.context.prf - e2_softquota + if self.context.prfcount < 0 then self.context.prfcount = 0 end + + self.context.prf = 0 + + if self.context.prfcount / e2_hardquota > 0.33 then + self:SetOverlayText("Expression 2\n" .. self.name .. "\n" .. tostring(math.Round(self.context.prfbench)) .. " ops, " .. tostring(math.Round(self.context.prfbench / e2_softquota * 100)) .. "% (+" .. tostring(math.Round(self.context.prfcount / e2_hardquota * 100)) .. "%)") + else + self:SetOverlayText("Expression 2\n" .. self.name .. "\n" .. tostring(math.Round(self.context.prfbench)) .. " ops, " .. tostring(math.Round(self.context.prfbench / e2_softquota * 100)) .. "%") + end + end + + return true +end + +local CallHook = wire_expression2_CallHook +function ENT:CallHook(hookname, ...) + if not self.context then return end + return CallHook(hookname, self.context, ...) +end + +function ENT:OnRemove( ) + if not self.error then + self:PCallHook('destruct') + end +end + +function ENT:PCallHook(...) + local ok, ret = pcall(self.CallHook, self, ...) + if ok then + return ret + else + self:Error("Expression 2 (" .. self.name .. "): "..ret) + end +end + +function ENT:Error(message, overlaytext) + self:SetOverlayText("Expression 2\n" .. self.name .. "\n("..(overlaytext or "script error")..")") + self:SetColor(255, 0, 0, 255) + + self.error = true + ErrorNoHalt(message .. "\n") + WireLib.ClientError(message, self.player) +end + +local function copytype(var) + if type(var) == "table" then + return table.Copy(var) + else + return var + end +end + +function ENT:Setup(buffer, restore) + self.original = buffer + if self.script then + self:PCallHook('destruct') + if self.error then return end + end + + local status, directives, buffer = PreProcessor.Execute(buffer) + if not status then self:Error(directives) return end + self.buffer = buffer + self.error = false + + self.name = directives.name + if directives.name == "" then + self.name = "generic" + self.WireDebugName = "Expression 2" + else + self.WireDebugName = "E2 - " .. self.name + end + + self.inports = directives.inputs + self.outports = directives.outputs + self.persists = directives.persist + self.trigger = directives.trigger + + local status, tokens = Tokenizer.Execute(self.buffer) + if not status then self:Error(tokens) return end + + local status, tree, dvars = Parser.Execute(tokens) + if not status then self:Error(tree) return end + + local status, script, dvars = Compiler.Execute(tree, self.inports[3], self.outports[3], self.persists[3], dvars) + if not status then self:Error(script) return end + + self:SetOverlayText("Expression 2\n" .. self.name) + self:SetColor(255, 255, 255, 255) + + + + self.Inputs = WireLib.AdjustSpecialInputs(self.Entity, self.inports[1], self.inports[2]) + self.Outputs = WireLib.AdjustSpecialOutputs(self.Entity, self.outports[1], self.outports[2]) + + self.script = script + + self.context = {} + self.context.vars = {} + self.context.vclk = {} + self.context.data = {} + self.context.entity = self + self.context.player = self.player + self.context.prf = 0 + self.context.prfcount = 0 + self.context.prfbench = 0 + + self._original = string.Replace(string.Replace(self.original,"\"","£"),"\n","€") + self._buffer = self.original -- TODO: is that really intended? + + self._name = self.name + self._inputs = { {}, {} } + self._outputs = { {}, {} } + self._vars = self.context.vars + + for k,v in pairs(self.inports[3]) do + self._inputs[1][#self._inputs[1] + 1] = k + self._inputs[2][#self._inputs[2] + 1] = v + self.context.vars[k] = copytype(wire_expression_types[v][2]) + end + + for k,v in pairs(self.outports[3]) do + self._outputs[1][#self._outputs[1] + 1] = k + self._outputs[2][#self._outputs[2] + 1] = v + self.context.vars[k] = copytype(wire_expression_types[v][2]) + self.context.vclk[k] = true + end + + for k,v in pairs(self.persists[3]) do + self.context.vars[k] = copytype(wire_expression_types[v][2]) + end + + for k,v in pairs(self.Inputs) do + if wire_expression_types[v.Type][3] then + self.context.vars[k] = wire_expression_types[v.Type][3](self.context, v.Value) + else + self.context.vars[k] = v.Value + end + end + + for k,v in pairs(dvars) do + self.context.vars["$" .. k] = self.context.vars[k] + end + + local ok, msg = pcall(self.CallHook, self, 'construct') + if not ok then + Msg("Construct hook(s) failed, executing destruct hooks...\n") + local ok2, msg2 = pcall(self.CallHook, self, 'destruct') + if ok2 then + self:Error(msg.."\nDestruct hooks succeeded.") + else + self:Error(msg.."\n"..msg2) + end + return + end + + self.duped = false + + if !restore then + self.first = true + self:Execute() + end + + self.Entity:NextThink(CurTime()) + self:Think() +end + +function ENT:Reset() + self:Setup(self.original) +end + +function ENT:TriggerInput(key, value) + if self.error then return end + if key and self.inports[3][key] then + t = self.inports[3][key] + + self.context.vars["$" .. key] = self.context.vars[key] + if wire_expression_types[t][3] then + self.context.vars[key] = wire_expression_types[t][3](self.context, value) + else + self.context.vars[key] = value + end + + self.context.triggerinput = key + if self.trigger[1] || self.trigger[2][key] then self:Execute() end + self.context.triggerinput = nil + end +end + +function ENT:TriggerOutputs() + for key,t in pairs(self.outports[3]) do + if self.context.vclk[key] or self.first then + if wire_expression_types[t][4] then + WireLib.TriggerOutput(self.Entity, key, wire_expression_types[t][4](self.context, self.context.vars[key])) + else + WireLib.TriggerOutput(self.Entity, key, self.context.vars[key]) + end + end + end +end + +function ENT:SendCode(pl) + local chunksize = 200 + if(!self.original || !pl) then return end + local code = self.original + local chunks = math.ceil(code:len() / chunksize) + umsg.Start("wire_expression2_download", pl) + umsg.Short(chunks) + umsg.String(self.name) + umsg.End() + + for i=0,chunks do + umsg.Start("wire_expression2_download", pl) + umsg.Short(i) + umsg.String(code:sub(i * chunksize + 1, (i + 1) * chunksize)) + umsg.End() + end +end + +local buffer = {} + +function ENT:Prepare(player) + local ID = player:UserID() + buffer[ID] = {} + buffer[ID].ent = self +end + +concommand.Add("wire_expression_upload_begin", function(player, command, args) + local ID = player:UserID() + buffer[ID].text = "" + buffer[ID].len = tonumber(args[1]) + buffer[ID].chunk = 0 + buffer[ID].chunks = tonumber(args[2]) + buffer[ID].ent:SetOverlayText("Expression 2\n(transferring)") + buffer[ID].ent:SetColor(0, 255, 0, 255) +end) + +concommand.Add("wire_expression_upload_data", function(player, command, args) + local ID = player:UserID() + + if not buffer[ID].text or not buffer[ID].chunk then + --Msg("buffer does not exist! Player="..tostring(player).." chunk="..args[1].."\n") + return + end + + buffer[ID].text = buffer[ID].text .. args[1] + buffer[ID].chunk = buffer[ID].chunk + 1 + + local percent = math.Round((buffer[ID].chunk / buffer[ID].chunks) * 100) +end) + +concommand.Add("wire_expression_upload_end", function(player, command, args) + local ID = player:UserID() + + local buf = buffer[ID] + buffer[ID] = nil + + local ent = buf.ent + if not ValidEntity(ent) then return end + + if not buf.text then + -- caused by concurrent download from the same chip + ent:SetOverlayText("Expression 2\n(transfer error)") + ent:SetColor(255, 0, 0, 255) + end + + local decoded = E2Lib.decode(buf.text or "") + if(decoded:len() != buf.len) then + ent:SetOverlayText("Expression 2\n(transfer error)") + ent:SetColor(255, 0, 0, 255) + else + ent:Setup(decoded) + ent.player = player + end +end) diff --git a/lua/entities/gmod_wire_expression2/shared.lua b/lua/entities/gmod_wire_expression2/shared.lua new file mode 100644 index 0000000000..23c6bdeb22 --- /dev/null +++ b/lua/entities/gmod_wire_expression2/shared.lua @@ -0,0 +1,19 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Expression 2" +ENT.Author = "Syranide" +ENT.Contact = "me@syranide.com" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +include("core/e2lib.lua") +include("base/preprocessor.lua") +include("base/tokenizer.lua") +include("base/parser.lua") +include("base/compiler.lua") +include('core/init.lua') diff --git a/lua/entities/gmod_wire_eyepod/cl_init.lua b/lua/entities/gmod_wire_eyepod/cl_init.lua new file mode 100644 index 0000000000..92389dadcc --- /dev/null +++ b/lua/entities/gmod_wire_eyepod/cl_init.lua @@ -0,0 +1,34 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +local EyePod = {} +EyePod.enabled = 0 +EyePod.EyeAng = Angle(0,0,0) +EyePod.PreviousState = 0 +EyePod.Rotate90 = false + +local function UpdateEyePodState( UM ) + if(!UM) then return end + EyePod.enabled = UM:ReadShort() + EyePod.EyeAng = UM:ReadAngle() + EyePod.Rotate90 = UM:ReadBool() +end +usermessage.Hook("UpdateEyePodState", UpdateEyePodState) + +local function EyePodEyeControl(UCMD) + if(EyePod.enabled == 1) then + UCMD:SetViewAngles( EyePod.EyeAng ) + EyePod.PreviousState = 1 + elseif(EyePod.enabled == 0 and EyePod.PreviousState == 1) then + if(EyePod.Rotate90 == true) then + UCMD:SetViewAngles( Angle(0,90,0) ) + else + UCMD:SetViewAngles( Angle(0,0,0) ) + end + EyePod.PreviousState = 0 + end +end +hook.Add("CreateMove", "WireEyePodEyeControl", EyePodEyeControl) + diff --git a/lua/entities/gmod_wire_eyepod/init.lua b/lua/entities/gmod_wire_eyepod/init.lua new file mode 100644 index 0000000000..1abaee2341 --- /dev/null +++ b/lua/entities/gmod_wire_eyepod/init.lua @@ -0,0 +1,332 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Eye Pod" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + -- Make Physics work + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + -- set it so we don't colide + self:SetCollisionGroup( COLLISION_GROUP_WORLD ) + self.CollisionGroup = COLLISION_GROUP_WORLD + -- turn off shadow + self.Entity:DrawShadow(false) + + -- Set wire I/O + self.Entity.Inputs = WireLib.CreateSpecialInputs(self.Entity, { "Enable", "SetPitch", "SetYaw", "SetViewAngle" }, {"NORMAL", "NORMAL", "NORMAL", "ANGLE"}) + self.Entity.Outputs = WireLib.CreateSpecialOutputs(self.Entity, { "X", "Y", "XY" }, {"NORMAL", "NORMAL", "VECTOR2"}) + + -- Initialize values + self.driver = nil + self.X = 0 + self.Y = 0 + self.enabled = 0 + self.pod = nil + self.EyeAng = Angle(0,0,0) + self.Rotate90 = false + self.DefaultToZero = 1 + self.ShowRateOfChange = 0 + self.LastUpdateTime = CurTime() + + -- clamps + self.ClampXMin = 0 + self.ClampXMax = 0 + self.ClampYMin = 0 + self.ClampYMax = 0 + self.ClampX = 0 + self.ClampY = 0 + + local phys = self.Entity:GetPhysicsObject() + if phys:IsValid() then + phys:Wake() + end +end + +function ENT:Setup(DefaultToZero,RateOfChange,ClampXMin,ClampXMax,ClampYMin,ClampYMax, ClampX, ClampY) + self.DefaultToZero = DefaultToZero + self.ShowRateOfChange = RateOfChange + self.ClampXMin = ClampXMin + self.ClampXMax = ClampXMax + self.ClampYMin = ClampYMin + self.ClampYMax = ClampYMax + self.ClampX = ClampX + self.ClampY = ClampY +end + +function ENT:PodLink(vehicle) + if (!vehicle || !vehicle:IsValid() || !vehicle:IsVehicle()) then + if (self.pod != nil) then + self.pod.AttachedWireEyePod = nil + end + self.pod = nil + return false + end + self.pod = vehicle + + local Rotate90ModelList = { + "models/props_c17/furniturechair001a.mdl", + "models/airboat.mdl", + "models/props_c17/chair_office01a.mdl", + "models/nova/chair_office02.mdl", + "models/nova/chair_office01.mdl", + "models/props_combine/breenchair.mdl", + "models/nova/chair_wood01.mdl", + "models/nova/airboat_seat.mdl", + "models/nova/chair_plastic01.mdl", + "models/nova/jeep_seat.mdl", + "models/props_phx/carseat.mdl", + "models/props_phx/carseat2.mdl", + "models/props_phx/carseat3.mdl", + "models/buggy.mdl", + "models/vehicle.mdl" + } + self.Rotate90 = false + self.EyeAng = Angle(0,0,0) + if (self.pod and self.pod:IsValid() and self.pod:IsVehicle()) then + if ( table.HasValue( Rotate90ModelList,string.lower( self.pod:GetModel() ) ) ) then + self.Rotate90 = true + self.EyeAng = Angle(0,90,0) + end + end + + + local ttable = { + AttachedWireEyePod = self.Entity + } + table.Merge(vehicle:GetTable(), ttable ) + return true +end + +function ENT:OnRemove() + if (self.pod and self.pod:IsValid() and self.pod:IsVehicle()) then + self.pod:GetTable().AttachedWireEyePod = nil + end + if (self.driver != nil) then + umsg.Start("UpdateEyePodState", self.driver) + umsg.Short(0) + umsg.Angle(self.EyeAng) + umsg.Bool(self.Rotate90) + umsg.End() + self.driver = nil + end +end + +function ENT:Think() + -- Make sure the gate updates even if we don't receive any input + self:TriggerInput() + + if (self.pod and self.pod:IsValid()) then + -- if we are in a pod, set the player + if ( self.pod:IsVehicle() and self.pod:GetDriver():IsPlayer() ) then + self.driver = self.pod:GetDriver() + else -- else set X and Y to 0 + if (self.driver != nil) then + umsg.Start("UpdateEyePodState", self.driver) + umsg.Short(0) + umsg.Angle(self.EyeAng) + umsg.Bool(self.Rotate90) + umsg.End() + self.driver = nil + end + if (self.DefaultToZero == 1) then + self.X = 0 + self.Y = 0 + Wire_TriggerOutput(self.Entity, "X", self.X) + Wire_TriggerOutput(self.Entity, "Y", self.Y) + local XY_Vec = {self.X,self.Y} + Wire_TriggerOutput(self.Entity, "XY", XY_Vec) + end + end + else -- else set X and Y to 0 + if (self.driver != nil) then + umsg.Start("UpdateEyePodState", self.driver) + umsg.Short(0) + umsg.Angle(self.EyeAng) + umsg.Bool(self.Rotate90) + umsg.End() + self.driver = nil + end + if (self.DefaultToZero == 1) then + self.X = 0 + self.Y = 0 + Wire_TriggerOutput(self.Entity, "X", self.X) + Wire_TriggerOutput(self.Entity, "Y", self.Y) + local XY_Vec = {self.X,self.Y} + Wire_TriggerOutput(self.Entity, "XY", XY_Vec) + end + self.pod = nil + end + + -- update the overlay with the user's name + local Txt = "Eye Pod Control" + if self.Entity.enabled == 1 and self.driver and self.driver:IsPlayer() then + Txt = Txt.." - In use by "..self.driver:Name() + else + Txt = Txt.." - Not Active" + end + if self.pod and self.pod:IsValid() and self.pod:IsVehicle() then + Txt = Txt.."\nLinked to "..self.pod:GetModel() + else + Txt = Txt.."\nNot Linked" + end + + if Txt ~= self.LastOverlay then + self.Entity:SetNetworkedBeamString("GModOverlayText", Txt) + self.LastOverlay = Txt + end + + self.Entity:NextThink(CurTime() + 0.1) + + return true +end + +local function AngNorm(Ang) + return (Ang + 180) % 360 - 180 +end +local function AngNorm90(Ang) + return (Ang + 90) % 180 - 90 +end + +function ENT:TriggerInput(iname, value) + -- Change variables to reflect input + if (iname == "Enable") then + if (value != 0) then + self.enabled = 1 + else + self.enabled = 0 + end + elseif (iname == "SetPitch") then + self.EyeAng = Angle(AngNorm90(value),self.EyeAng.y,self.EyeAng.r) + elseif (iname == "SetYaw") then + if (self.Rotate90 == true) then + self.EyeAng = Angle(AngNorm90(self.EyeAng.p),AngNorm(value+90),self.EyeAng.r) + else + self.EyeAng = Angle(AngNorm90(self.EyeAng.p),AngNorm(value),self.EyeAng.r) + end + elseif (iname == "SetViewAngle") then + if (self.Rotate90 == true) then + self.EyeAng = Angle(AngNorm90(value.p),AngNorm(value.y+90),0) + else + self.EyeAng = Angle(AngNorm90(value.p),AngNorm(value.y),0) + end + end + + -- If we're not enabled, set the output to zero and exit + if (self.enabled == 0) then + if (self.DefaultToZero == 1) then + self.X = 0 + self.Y = 0 + Wire_TriggerOutput(self.Entity, "X", self.X) + Wire_TriggerOutput(self.Entity, "Y", self.Y) + local XY_Vec = {self.X,self.Y} + Wire_TriggerOutput(self.Entity, "XY", XY_Vec) + end + if (self.driver != nil and self.pod and self.pod:IsValid()) then + umsg.Start("UpdateEyePodState", self.driver) + umsg.Short(self.enabled) + umsg.Angle(self.EyeAng) + umsg.Bool(self.Rotate90) + umsg.End() + end + return + end + + --Turn on the EyePod Control file + self.enabled = 1 + if (self.driver != nil and self.pod and self.pod:IsValid()) then + umsg.Start("UpdateEyePodState", self.driver) + umsg.Short(self.enabled) + umsg.Angle(self.EyeAng) + umsg.Bool(self.Rotate90) + umsg.End() + end + +end + +local UpdateTimer = CurTime() + +local function EyePodMouseControl(ply, movedata) + local Vehicle = nil + local EyePod = nil + --is the player in a vehicle? + if (ply and ply:InVehicle() and ply:GetVehicle():IsValid()) then + Vehicle = ply:GetVehicle() + local Table = Vehicle:GetTable() + --is the vehicle linked to an EyePod? + if (Table and Table.AttachedWireEyePod and Table.AttachedWireEyePod:IsValid()) then + --get the EyePod + EyePod = Table.AttachedWireEyePod + else + return + end + else + return + end + + if (EyePod == nil or Vehicle == nil) then return end + + if (EyePod.enabled == 1) then + + local cmd = ply:GetCurrentCommand() + + --update the cumualative output + EyePod.X = cmd:GetMouseX()/10 + EyePod.X + EyePod.Y = -cmd:GetMouseY()/10 + EyePod.Y + + --clamp the output + if(EyePod.ClampX == 1)then + EyePod.X = math.Clamp(EyePod.X,EyePod.ClampXMin,EyePod.ClampXMax) + end + if(EyePod.ClampY == 1) then + EyePod.Y = math.Clamp(EyePod.Y,EyePod.ClampYMin,EyePod.ClampYMax) + end + + --update the outputs every 0.015 seconds + if (CurTime() > (EyePod.LastUpdateTime+0.015)) then + Wire_TriggerOutput(EyePod, "X", EyePod.X) + Wire_TriggerOutput(EyePod, "Y", EyePod.Y) + local XY_Vec = {EyePod.X,EyePod.Y} + Wire_TriggerOutput(EyePod, "XY", XY_Vec) + --reset the output so it is not cumualative if you want the rate of change + if (EyePod.ShowRateOfChange == 1)then + EyePod.X = 0 + EyePod.Y = 0 + end + EyePod.LastUpdateTime = CurTime() + end + + --reset the mouse + cmd:SetMouseX(0) + cmd:SetMouseY(0) + return + + end +end +hook.Add("SetupMove", "WireEyePodMouseControl", EyePodMouseControl) + +-- Advanced Duplicator Support +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if (self.pod) and (self.pod:IsValid()) then + info.pod = self.pod:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + if (info.pod) then + self.pod = GetEntByID(info.pod) + if (!self.pod) then + self.pod = ents.GetByIndex(info.pod) + end + if (self.pod) then + self.Entity:PodLink(self.pod) + end + end +end diff --git a/lua/entities/gmod_wire_eyepod/shared.lua b/lua/entities/gmod_wire_eyepod/shared.lua new file mode 100644 index 0000000000..2bd3c950bb --- /dev/null +++ b/lua/entities/gmod_wire_eyepod/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Eye Pod" +ENT.Author = "Jeremydeath" +ENT.Contact = "" +ENT.Purpose = "To control the player's view in a pod and output their mouse movements" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_forcer/cl_init.lua b/lua/entities/gmod_wire_forcer/cl_init.lua new file mode 100644 index 0000000000..6a2c3fc6aa --- /dev/null +++ b/lua/entities/gmod_wire_forcer/cl_init.lua @@ -0,0 +1,6 @@ +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1, self:GetForceBeam() ) +end diff --git a/lua/entities/gmod_wire_forcer/init.lua b/lua/entities/gmod_wire_forcer/init.lua new file mode 100644 index 0000000000..cc99ae544e --- /dev/null +++ b/lua/entities/gmod_wire_forcer/init.lua @@ -0,0 +1,146 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Forcer" +ENT.OverlayDelay = .05 + + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.F = 0 + self.FoO = 0 + self.V = 0 + + self.Inputs = Wire_CreateInputs(self.Entity, { "Force", "OffsetForce", "Velocity" }) + self:SetForceBeam(false) +end + +function ENT:Setup(force, length, showbeam, reaction) + self.Force = math.max(force, 1) + self.Tlength = math.max(length, 1) + self.F = 0 + self.FoO = 0 + self.V = 0 + if showbeam then + self:SetBeamLength(length) + else + self:SetBeamLength(0) + end + self.Reaction = reaction + self:TriggerInput("Force", 0) +end + +function ENT:TriggerInput(iname, value) + if iname == "Force" then + self.F = value + self:SetForceBeam(self.F != 0) + self:ShowOutput() + elseif iname == "OffsetForce" then + self.FoO = value + self:ShowOutput() + elseif iname == "Velocity" then + self.V = math.max(math.min(100000,value),-100000) + self:SetForceBeam(self.V != 0) + self:ShowOutput() + end +end + +local function clamp_length(vector) + local length = vector:length() + if length > 100000 then return vector / length * 100000 end + return vector +end + +function ENT:Think() + if self.F > 0.1 or self.FoO > 0.1 or self.V > 0.1 or self.F < -0.1 or self.FoO < -0.1 or self.V < -0.1 then + local vForward = self.Entity:GetUp() + local vStart = self.Entity:GetPos() + vForward*self.Entity:OBBMaxs().z + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self.Tlength) + trace.filter = { self.Entity } + + local trace = util.TraceLine( trace ) + + if trace.Entity and trace.Entity:IsValid() then + if trace.Entity:GetMoveType() == MOVETYPE_VPHYSICS then + local phys = trace.Entity:GetPhysicsObject() + if phys:IsValid() then + if self.F > 0.1 or self.F < -0.1 then phys:ApplyForceCenter( vForward * self.Force * self.F ) end + if self.FoO > 0.1 or self.FoO < -0.1 then phys:ApplyForceOffset( vForward * self.FoO, trace.HitPos ) end + --if self.V > 0.1 or self.V < -0.1 then phys:SetVelocity( vForward * self.V ) end + if self.V > 0.1 or self.V < -0.1 then phys:SetVelocityInstantaneous( vForward * self.V ) end + end + if self.Reaction then + phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + if self.F > 0.1 or self.F < -0.1 then phys:ApplyForceCenter( vForward * -self.Force * self.F ) end + if self.FoO > 0.1 or self.FoO < -0.1 then phys:ApplyForceCenter( vForward * -self.FoO ) end + end + end + else + if self.V > 0.1 or self.V < -0.1 then trace.Entity:SetVelocity( vForward * self.V ) end + end + end + end + + self.Entity:NextThink(CurTime() + 0.1) + return true +end + +function ENT:ShowOutput() + self:SetOverlayText( + "Forcer\nCenter Force= "..tostring(math.Round(self.F * self.Force)).. + "\nOffset Force= "..tostring(math.Round(self.FoO)).. + "\nVelocity= "..tostring(math.Round(self.V)) + ) +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + --Moves old "A" input to new "Force" input for older saves + if info.Wires and info.Wires.A then + info.Wires.Force = info.Wires.A + info.Wires.A = nil + end + + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) +end + + +function MakeWireForcer( pl, Pos, Ang, model, Force, Length, showbeam, reaction ) + if not pl:CheckLimit( "wire_forcers" ) then return false end + + local wire_forcer = ents.Create( "gmod_wire_forcer" ) + if not wire_forcer:IsValid() then return false end + + wire_forcer:SetAngles( Ang ) + wire_forcer:SetPos( Pos ) + wire_forcer:SetModel( model ) + wire_forcer:Spawn() + + wire_forcer:Setup(Force, Length, showbeam, reaction) + wire_forcer:SetPlayer( pl ) + + local ttable = { + pl = pl, + Force = Force, + Length = Length, + showbeam = showbeam, + reaction = reaction, + } + table.Merge(wire_forcer:GetTable(), ttable ) + + pl:AddCount( "wire_forcers", wire_forcer ) + + return wire_forcer +end + +duplicator.RegisterEntityClass("gmod_wire_forcer", MakeWireForcer, "Pos", "Ang", "Model", "Force", "Length", "showbeam", "reaction") + diff --git a/lua/entities/gmod_wire_forcer/shared.lua b/lua/entities/gmod_wire_forcer/shared.lua new file mode 100644 index 0000000000..8cc4bd38e8 --- /dev/null +++ b/lua/entities/gmod_wire_forcer/shared.lua @@ -0,0 +1,20 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Forcer" + +function ENT:SetForceBeam(on) + self.Entity:SetNetworkedBool("ForceBeam",on,true) +end + +function ENT:GetForceBeam() + return self.Entity:GetNetworkedBool("ForceBeam") +end + +function ENT:SetBeamLength(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_fx_emitter/cl_init.lua b/lua/entities/gmod_wire_fx_emitter/cl_init.lua new file mode 100644 index 0000000000..9ab2e8efec --- /dev/null +++ b/lua/entities/gmod_wire_fx_emitter/cl_init.lua @@ -0,0 +1,67 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_OPAQUE +ENT.Delay = 0.05 + +local matLight = Material( "sprites/light_ignorez" ) +local matBeam = Material( "effects/lamp_beam" ) + +/*--------------------------------------------------------- + Name: Draw +---------------------------------------------------------*/ + +function ENT:Draw() + + // Don't draw if we are in camera mode + local ply = LocalPlayer() + local wep = ply:GetActiveWeapon() + if ( wep:IsValid() ) then + local weapon_name = wep:GetClass() + if ( weapon_name == "gmod_camera" ) then return end + end + + self.BaseClass.Draw( self ) + +end + +/*--------------------------------------------------------- + Name: Think +---------------------------------------------------------*/ +function ENT:Think() + + if ( !(self:GetOn()~=0) ) then return end + + if ( self.Delay > CurTime() ) then return end + self.Delay = CurTime() + self:GetDelay() + + local Effect = self:GetEffect() + + // Missing effect... replace it if possible :/ + if ( !self.Effects[ Effect ] ) then if ( self.Effects[1] ) then Effect = 1 else return end end + + local Angle = self.Entity:GetAngles() + + local FXDir = self:GetFXDir() + if(FXDir && FXDir!=Vector(0,0,0))then Angle = FXDir:Angle() else self.Entity:GetUp():Angle() end + + local FXPos = self:GetFXPos() + if (!FXPos || FXPos==Vector(0,0,0)) then FXPos=self.Entity:GetPos() + Angle:Forward() * 12 end + + local b, e = pcall( self.Effects[Effect], FXPos, Angle ) + + // If there are errors.. + if (!b) then + + // Report the error + Print(self.Effects) + Print(FXPos) + Print(Angle) + Msg("Error in Emitter "..tostring(Effect).."\n -> "..tostring(e).."\n") + + // Remove the naughty function + self.Effects[ Effect ] = nil + + end + +end diff --git a/lua/entities/gmod_wire_fx_emitter/fx_default.lua b/lua/entities/gmod_wire_fx_emitter/fx_default.lua new file mode 100644 index 0000000000..9d31382127 --- /dev/null +++ b/lua/entities/gmod_wire_fx_emitter/fx_default.lua @@ -0,0 +1,133 @@ +AddCSLuaFile("fx_default.lua") + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetNormal( angle:Forward() * 2 ) + effectdata:SetMagnitude( 1 ) + effectdata:SetScale( 1 ) + effectdata:SetRadius( 2 ) + util.Effect( "Sparks", effectdata ) + +end +ENT:AddEffect( "small_sparks", FX, "Sparks (Small)" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetNormal( angle:Forward() * 2 ) + effectdata:SetMagnitude( 2 ) + effectdata:SetScale( 1 ) + effectdata:SetRadius( 6 ) + util.Effect( "Sparks", effectdata ) + +end +ENT:AddEffect( "sparks", FX, "Sparks" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos + angle:Forward() * 5 ) + effectdata:SetAngle( angle ) + effectdata:SetScale( 1 ) + util.Effect( "MuzzleEffect", effectdata ) + +end +ENT:AddEffect( "muzzle", FX, "Muzzleflash" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos + angle:Forward() * 5 ) + effectdata:SetAngle( angle ) + effectdata:SetScale( 2 ) + util.Effect( "MuzzleEffect", effectdata ) + +end +ENT:AddEffect( "muzzlebig", FX, "Muzzleflash (Big)" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + util.Effect( "BloodImpact", effectdata ) + +end +ENT:AddEffect( "bloodimpact", FX, "Blood Impact" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetAngle( angle ) + effectdata:SetNormal( angle:Forward() * 2 ) + effectdata:SetMagnitude( 1 ) + effectdata:SetScale( 1 ) + effectdata:SetRadius( 1 ) + util.Effect( "StriderBlood", effectdata ) + +end +ENT:AddEffect( "striderblood", FX, "Strider Blood" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetAngle( angle ) + util.Effect( "ShotgunShellEject", effectdata ) + +end +ENT:AddEffect( "shotgun shell", FX, "Shotgun Shell" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetAngle( angle ) + util.Effect( "RifleShellEject", effectdata ) + +end +ENT:AddEffect( "rifle shell", FX, "Rifle Shell" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetAngle( angle ) + util.Effect( "ShellEject", effectdata ) + +end +ENT:AddEffect( "pistol shell", FX, "Pistol Shell" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetAngle( angle ) + effectdata:SetNormal( angle:Forward() ) + util.Effect( "MetalSpark", effectdata ) + +end +ENT:AddEffect( "metalsparks", FX, "Metal Sparks" ) + + +local function FX( pos, angle ) + + local effectdata = EffectData() + effectdata:SetOrigin( pos ) + effectdata:SetAngle( angle ) + util.Effect( "GlassImpact", effectdata ) + +end +ENT:AddEffect( "glassimpact", FX, "Glass Impact" ) diff --git a/lua/entities/gmod_wire_fx_emitter/init.lua b/lua/entities/gmod_wire_fx_emitter/init.lua new file mode 100644 index 0000000000..1ce8083884 --- /dev/null +++ b/lua/entities/gmod_wire_fx_emitter/init.lua @@ -0,0 +1,101 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +// wire debug and overlay crap. +ENT.WireDebugName = "Wire FX Emitter" +ENT.OverlayDelay = 0 +ENT.LastClear = 0 + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + + self.Entity:SetModel( "models/props_lab/tpplug.mdl" ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Entity:DrawShadow( false ) + self.Entity:SetCollisionGroup( COLLISION_GROUP_WEAPON ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + self.Inputs = WireLib.CreateSpecialInputs( self.Entity, { "On", "Effect", "Delay", "Direction" }, { "NORMAL", "NORMAL", "NORMAL", "VECTOR" } ) + self:SetOverlayText( "Wire FX Emitter" ) + + self.datanstuff = { + pos = Vector(0,0,0), + dir = Vector(0,0,0), + delay = 0.05, + effect = 0, + on = 0 + } +end + +/*--------------------------------------------------------- + Name: OnTakeDamage +--------------------------------------------------------- +function ENT:OnTakeDamage( dmginfo ) + self.Entity:TakePhysicsDamage( dmginfo ) +end +*/ + +// trigger input +function ENT:TriggerInput( inputname, value, iter ) + // store values. + if(not value) then + if ( inputname == "On" ) then + self:SetOn(0) + end + return + end + if (inputname == "Direction") then + value = value:GetNormal() + self:SetFXDir(value) + elseif (inputname == "Effect") then + value = value - value % 1 + if (value < 1) then + value = 1 + elseif (value > self.fxcount) then + value=self.fxcount + end + self:SetEffect( value ) + elseif ( inputname == "On" ) then + if value ~= 0 then + self:SetOn(1) + else + self:SetOn(0) + end + elseif ( inputname == "Delay" ) then + if (value < 0.05) then + value=0.05 + elseif (value > 20) then + value=20 + end + self:SetDelay(value) + --elseif (inputname == "Position") then -- removed for excessive mingability + -- self:SetFXPos(value) + end +end + +/*--------------------------------------------------------- +--Duplicator support +---------------------------------------------------------*/ +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + info.Effect = self:GetEffect() + info.Delay = self:GetDelay() + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + self:SetEffect(info.Effect) + self:SetDelay(info.Delay) +end diff --git a/lua/entities/gmod_wire_fx_emitter/shared.lua b/lua/entities/gmod_wire_fx_emitter/shared.lua new file mode 100644 index 0000000000..58686cfb19 --- /dev/null +++ b/lua/entities/gmod_wire_fx_emitter/shared.lua @@ -0,0 +1,104 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire FX Emitter" +ENT.Author = "Team garry / ZeikJT" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +/*--------------------------------------------------------- + Effect +---------------------------------------------------------*/ +function ENT:SetEffect( value ) + if value ~= self.datanstuff.effect then + self.Entity:SetNWInt( "Effect", value ) + self.datanstuff.effect = value + end +end +function ENT:GetEffect() + return self.Entity:GetNWInt( "Effect" ) +end + +/*--------------------------------------------------------- + Delay +---------------------------------------------------------*/ +function ENT:SetDelay( f ) + if f ~= self.datanstuff.delay then + self.Entity:SetNWFloat( "Delay", f ) + self.datanstuff.delay=f + end +end +function ENT:GetDelay() + return self.Entity:GetNWFloat( "Delay" ) +end + +/*--------------------------------------------------------- + Position +---------------------------------------------------------*/ +--function ENT:SetFXPos( pos ) +-- if pos ~= self.datanstuff.pos then +-- self.Entity:SetNWVector( "FXPos", pos ) +-- self.datanstuff.pos = pos +-- end +--end +function ENT:GetFXPos() + --return self.Entity:GetNWVector( "FXPos" ) + return self.Entity:GetPos() +end + +/*--------------------------------------------------------- + Position +---------------------------------------------------------*/ +function ENT:SetFXDir( dir ) + if dir ~= self.datanstuff.dir then + self.Entity:SetNWVector( "FXDir", dir:Normalize() ) + self.datanstuff.dir = dir + end +end +function ENT:GetFXDir() + return self.Entity:GetNWVector( "FXDir" ) +end + +/*--------------------------------------------------------- + On +---------------------------------------------------------*/ +function ENT:SetOn( b ) + if b ~= self.datanstuff.on then + self.Entity:SetNWInt( "On", b ) + self.datanstuff.on = b + end +end +function ENT:GetOn() + return self.Entity:GetNWInt( "On" ) +end + + + +/*--------------------------------------------------------- + Effect registration +---------------------------------------------------------*/ + +ENT.Effects = {} +ENT.fxcount = 0 + +ComboBox_Wire_FX_Emitter_Options = {} + +function ENT:AddEffect( name, func, nicename ) + self.fxcount = self.fxcount+1 + // Maintain a global reference for these effects + ComboBox_Wire_FX_Emitter_Options[name] = self.fxcount + if CLIENT then + self.Effects[self.fxcount] = func + language.Add( "wire_fx_emitter_"..name, nicename ) + end +end + +/*--------------------------------------------------------- + Modular effect adding.. stuff +---------------------------------------------------------*/ + +include( "fx_default.lua" ) diff --git a/lua/entities/gmod_wire_gate/cl_init.lua b/lua/entities/gmod_wire_gate/cl_init.lua new file mode 100644 index 0000000000..9dca17e409 --- /dev/null +++ b/lua/entities/gmod_wire_gate/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_gate/init.lua b/lua/entities/gmod_wire_gate/init.lua new file mode 100644 index 0000000000..ec2a4bd89e --- /dev/null +++ b/lua/entities/gmod_wire_gate/init.lua @@ -0,0 +1,247 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Gate" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "A" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) +end + + +function ENT:Setup( action, noclip ) + if (action) then + self.WireDebugName = action.name + + WireLib.AdjustSpecialInputs(self.Entity, action.inputs, action.inputtypes ) + if (action.outputs) then + WireLib.AdjustSpecialOutputs(self.Entity, action.outputs, action.outputtypes) + else + //Wire_AdjustOutputs(self.Entity, { "Out" }) + WireLib.AdjustSpecialOutputs(self.Entity, { "Out" }, action.outputtypes) + end + + if (action.reset) then + action.reset(self) + end + end + + if (noclip) then + self.Entity:SetCollisionGroup( COLLISION_GROUP_WORLD ) + end + + self.Action = action + self.PrevValue = nil + + //self.Action.inputtypes = self.Action.inputtypes or {} + + self:CalcOutput() + self:ShowOutput() +end + + +function ENT:OnInputWireLink(iname, itype, src, oname, otype) + if (self.Action) and (self.Action.OnInputWireLink) then + self.Action.OnInputWireLink(self, iname, itype, src, oname, otype) + end +end + +function ENT:OnOutputWireLink(oname, otype, dst, iname, itype) + if (self.Action) and (self.Action.OnOutputWireLink) then + self.Action.OnOutputWireLink(self, oname, otype, dst, iname, itype) + end +end + + +function ENT:TriggerInput(iname, value, iter) + if (self.Action) and (not self.Action.timed) then + self:CalcOutput(iter) + self:ShowOutput() + end +end + + +function ENT:Think() + self.BaseClass.Think(self) + + if (self.Action) and (self.Action.timed) then + self:CalcOutput() + self:ShowOutput() + + self.Entity:NextThink(CurTime()+0.02) + return true + end +end + + +function ENT:CalcOutput(iter) + if (self.Action) and (self.Action.output) then + if (self.Action.outputs) then + local result = { self.Action.output(self, unpack(self:GetActionInputs())) } + + for k,v in ipairs(self.Action.outputs) do + Wire_TriggerOutput(self.Entity, v, result[k], iter) + end + else + local value = self.Action.output(self, unpack(self:GetActionInputs())) or 0 + + Wire_TriggerOutput(self.Entity, "Out", value, iter) + end + end +end + +function ENT:ReadCell(Address) + if (self.Action) && (self.Action.ReadCell) then + return self.Action:ReadCell(self,Address) + else + return nil + end +end + +function ENT:WriteCell(Address,value) + if (self.Action) && (self.Action.WriteCell) then + return self.Action:WriteCell(self,Address,value) + else + return false + end +end + +function ENT:ShowOutput() + local txt = "" + + if (self.Action) then + txt = (self.Action.name or "No Name") + if (self.Action.label) then + txt = txt.."\n"..self.Action.label(self:GetActionOutputs(), unpack(self:GetActionInputs(Wire_EnableGateInputValues))) + end + else + txt = "Invalid gate!" + end + + self:SetOverlayText(txt) +end + + +function ENT:OnRestore() + self.Action = GateActions[self.action] + + self.BaseClass.OnRestore(self) +end + + +function ENT:GetActionInputs(as_names) + local Args = {} + + if (self.Action.compact_inputs) then + for k,v in ipairs(self.Action.inputs) do + local input = self.Inputs[v] + if (not input) then + Msg("Missing input! ("..v..")") + return {} + end + + if (input.Src) and (input.Src:IsValid()) then + if (as_names) then + table.insert(Args, input.Src.WireName or input.Src.WireDebugName or v) + else + table.insert(Args, input.Value) + end + end + end + + while (#Args < self.Action.compact_inputs) do + if (as_names) then + table.insert(Args, self.Action.inputs[#Args+1] or "*Not enough inputs*") + else + //table.insert( Args, WireLib.DT[ (self.Action.inputtypes[#Args+1] or "NORMAL") ].Zero ) + table.insert( Args, WireLib.DT[ self.Inputs[ self.Action.inputs[#Args+1] ].Type ].Zero ) + end + end + else + for k,v in ipairs(self.Action.inputs) do + local input = self.Inputs[v] + if (not input) then + Msg("Missing input! ("..v..")") + return {} + end + + if (as_names) then + if (input.Src) and (input.Src:IsValid()) then + Args[k] = input.Src.WireName or input.Src.WireDebugName or v + else + Args[k] = v + end + else + if (input.Src) and (input.Src:IsValid()) then + //Args[k] = ( input.Value or WireLib.DT[ (self.Action.inputtypes[k] or "NORMAL") ].Zero ) + Args[k] = ( input.Value or WireLib.DT[ self.Inputs[v].Type ].Zero ) + else + //Args[k] = WireLib.DT[ (self.Action.inputtypes[k] or "NORMAL") ].Zero + Args[k] = WireLib.DT[ self.Inputs[v].Type ].Zero + end + end + end + end + + return Args +end + + +function ENT:GetActionOutputs() + if (self.Action.outputs) then + local result = {} + for _,v in ipairs(self.Action.outputs) do + result[v] = self.Outputs[v].Value or 0 + end + + return result + end + + return self.Outputs.Out.Value or 0 +end + + + + +function MakeWireGate(pl, Pos, Ang, model, action, noclip, frozen, nocollide) + if ( !pl:CheckLimit( "wire_gates" ) ) then return nil end + + local wire_gate = ents.Create( "gmod_wire_gate" ) + wire_gate:SetPos( Pos ) + wire_gate:SetAngles( Ang ) + wire_gate:SetModel( model ) + wire_gate:Spawn() + wire_gate:Activate() + + wire_gate:Setup( GateActions[action], noclip ) + wire_gate:SetPlayer( pl ) + + if wire_gate:GetPhysicsObject():IsValid() then + wire_gate:GetPhysicsObject():EnableMotion(!frozen) + end + if nocollide == true or noclip == true then + wire_gate:SetCollisionGroup(COLLISION_GROUP_WORLD) + end + + local ttable = { + pl = pl, + action = action, + noclip = noclip, + nocollide = nocollide + } + table.Merge( wire_gate:GetTable(), ttable ) + + pl:AddCount( "wire_gates", wire_gate ) + pl:AddCleanup( "gmod_wire_gate", wire_gate ) + + return wire_gate +end +duplicator.RegisterEntityClass("gmod_wire_gate", MakeWireGate, "Pos", "Ang", "Model", "action", "noclip", "frozen", "nocollide") diff --git a/lua/entities/gmod_wire_gate/shared.lua b/lua/entities/gmod_wire_gate/shared.lua new file mode 100644 index 0000000000..7c3a5d9fdc --- /dev/null +++ b/lua/entities/gmod_wire_gate/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Gate" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_gps/cl_init.lua b/lua/entities/gmod_wire_gps/cl_init.lua new file mode 100644 index 0000000000..62df87e74e --- /dev/null +++ b/lua/entities/gmod_wire_gps/cl_init.lua @@ -0,0 +1,21 @@ + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +include('shared.lua') + + +function ENT:Think() + self.BaseClass.Think(self) + + local pos = self.Entity:GetPos() + if (COLOSSAL_SANDBOX) then pos = pos * 6.25 end + local txt = "Position = " .. math.Round(pos.x*1000)/1000 .. "," .. math.Round(pos.y*1000)/1000 .. "," .. math.Round(pos.z*1000)/1000 + + self.Entity:SetNetworkedBeamString( "GModOverlayText", txt ) + //self.Entity:SetNetworkedString( "GModOverlayText", txt ) + --self.BaseClass.BaseClass.SetOverlayText( self, txt ) + + self.Entity:NextThink(CurTime()+0.04) + return true +end diff --git a/lua/entities/gmod_wire_gps/init.lua b/lua/entities/gmod_wire_gps/init.lua new file mode 100644 index 0000000000..2471d69716 --- /dev/null +++ b/lua/entities/gmod_wire_gps/init.lua @@ -0,0 +1,93 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "GPS" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.storedpositions = {}; + self.arrayindex = 0; + + self.Inputs = Wire_CreateInputs(self.Entity, { "Store/Save Pos", "Next", "Remove Save Position"}) + self.Outputs = WireLib.CreateSpecialOutputs( self.Entity, { "X", "Y", "Z", "Vector", "Recall X", "Recall Y", "Recall Z", "Recall Vector", "Current Memory"}, { "NORMAL", "NORMAL", "NORMAL", "VECTOR", "NORMAL", "NORMAL", "NORMAL", "VECTOR", "NORMAL"}) +end + +function ENT:Setup() + self.Value = 0 + self.PrevOutput = nil + + //self:ShowOutput(0, 0, 0) + Wire_TriggerOutput(self.Entity, "X", 0) + Wire_TriggerOutput(self.Entity, "Y", 0) + Wire_TriggerOutput(self.Entity, "Z", 0) + Wire_TriggerOutput(self.Entity, "Vector", Vector(0,0,0)) + Wire_TriggerOutput(self.Entity, "Recall X", 0) + Wire_TriggerOutput(self.Entity, "Recall Y", 0) + Wire_TriggerOutput(self.Entity, "Recall Z", 0) + Wire_TriggerOutput(self.Entity, "Recall Vector", Vector(0,0,0)) + Wire_TriggerOutput(self.Entity, "Current Memory", 0) +end + +function ENT:Think() + self.BaseClass.Think(self) + + local pos = self.Entity:GetPos() + if (COLOSSAL_SANDBOX) then pos = pos * 6.25 end + + Wire_TriggerOutput(self.Entity, "X", pos.x) + Wire_TriggerOutput(self.Entity, "Y", pos.y) + Wire_TriggerOutput(self.Entity, "Z", pos.z) + Wire_TriggerOutput(self.Entity, "Vector", pos) + Wire_TriggerOutput(self.Entity, "Current Memory", self.arrayindex) + if self.arrayindex > 0 then + Wire_TriggerOutput(self.Entity, "Recall X", self.storedpositions[self.arrayindex].x) + Wire_TriggerOutput(self.Entity, "Recall Y", self.storedpositions[self.arrayindex].y) + Wire_TriggerOutput(self.Entity, "Recall Z", self.storedpositions[self.arrayindex].z) + Wire_TriggerOutput(self.Entity, "Recall Vector", self.storedpositions[self.arrayindex]) + else + Wire_TriggerOutput(self.Entity, "Recall X", 0) + Wire_TriggerOutput(self.Entity, "Recall Y", 0) + Wire_TriggerOutput(self.Entity, "Recall Z", 0) + Wire_TriggerOutput(self.Entity, "Recall Vector", Vector(0,0,0)) + end + + self.Entity:NextThink(CurTime()+0.04) + return true +end + +function ENT:TriggerInput(iname, value) + if (iname == "Store/Save Pos") then + if (value ~= 0) then + local curpos = self.Entity:GetPos() + table.insert(self.storedpositions, curpos) + self.arrayindex = self.arrayindex+1 + end + elseif (iname == "Next") then + if (value ~= 0) then + if # self.storedpositions > 0 then + if not (self.arrayindex >= # self.storedpositions) then + self.arrayindex = self.arrayindex+1; + else + self.arrayindex = 1; --loop back + end + end + end + elseif (iname == "Remove Save Position") then + if (value ~= 0) then + if self.arrayindex ~= 0 then + table.remove(self.storedpositions, self.arrayindex) + end + if (self.arrayindex == 1) and (# self.storedpositions == 0) then + self.arrayindex = 0 + end + if (self.arrayindex == (# self.storedpositions+1)) then + self.arrayindex = self.arrayindex-1 + end + end + end +end diff --git a/lua/entities/gmod_wire_gps/shared.lua b/lua/entities/gmod_wire_gps/shared.lua new file mode 100644 index 0000000000..d72f7225da --- /dev/null +++ b/lua/entities/gmod_wire_gps/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Speedometer" +ENT.Author = "Erkle" +ENT.Contact = "ErkleMad@hotmail.com" diff --git a/lua/entities/gmod_wire_gpu/cl_init.lua b/lua/entities/gmod_wire_gpu/cl_init.lua new file mode 100644 index 0000000000..0376e9a43d --- /dev/null +++ b/lua/entities/gmod_wire_gpu/cl_init.lua @@ -0,0 +1,308 @@ +if (EmuFox) then + include('gmod_wire_gpu/shared.lua') + include('gmod_wire_gpu/gpu_vm.lua') + include('gmod_wire_gpu/gpu_opcodes.lua') + include('gmod_wire_gpu/gpu_clientbus.lua') +else + include('shared.lua') + include('gpu_vm.lua') + include('gpu_opcodes.lua') + include('gpu_clientbus.lua') +end + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + +WireGPU_HookedGPU = nil + +//local texFSB = render.GetSuperFPTex() +//local matFSB = Material("pp/motionblur") +//local matFB = Material("pp/fb") + +function ENT:Initialize() + self.IsGPU = true + self.PrevTime = CurTime() + + self.Debug = false + + self.Memory = {} + self.ROMMemory = {} + + self.PrecompileData = {} + self.PrecompileMemory = {} + + self:InitGraphicTablet() + self:InitializeGPUOpcodeTable() + self:InitializeGPULookupTables() + self:InitializeGPUVariableSet() + self:InitializeErrors() + self:GPUMathInit() + self:GPUHardReset() + + self.FramesSinceRedraw = 0 + self.FrameRateRatio = 4 + self.FrameInstructions = 0 + + WireGPU_NeedRenderTarget(self:EntIndex()) + + self.OF = CreateClientConVar("gpu_of",0,false,false) + self.OR = CreateClientConVar("gpu_or",0,false,false) + self.OU = CreateClientConVar("gpu_ou",0,false,false) + self.Scale = CreateClientConVar("gpu_scale",1,false,false) + self.Ratio = CreateClientConVar("gpu_ratio",1,false,false) + self.Rot90 = CreateClientConVar("gpu_rot90",0,false,false) + self.MinFrameRateRatio = CreateClientConVar("wire_gpu_frameratio",4,false,false) +end + +function ENT:OnRemove() + WireGPU_ReturnRenderTarget(self:EntIndex()) +end + +function DebugMessage(msg) + Msg("============================================\n") + Msg(msg.."\n") +end + +function WireGPU_MemoryMessage(umsg) + local ent = ents.GetByIndex(umsg:ReadLong()) + local cachebase = umsg:ReadLong() + local cachesize = umsg:ReadLong() + + if ((ent) && (ent.Memory)) then + if (cachebase >= 0) && (cachebase + cachesize < 65537) then + for i=0,cachesize-1 do + local value = umsg:ReadFloat() + + ent:WriteCell(cachebase+i,value) + ent.ROMMemory[cachebase+i] = value + end + end + end +end +usermessage.Hook("wiregpu_memorymessage", WireGPU_MemoryMessage) + +function ENT:SVN_Version() + local SVNString = "$Revision: 000$" + + return tonumber(string.sub(SVNString,12,14)) +end + +function ENT:DoCall(callid,calldepth) + if ((self.EntryPoint) && (self.EntryPoint[callid])) then + self:GPUFrameReset() + + self.IP = self.EntryPoint[callid] + local cmdcount = 0 + while ((cmdcount < calldepth) && (self.INTR == 0)) do + self:GPUExecute() + cmdcount = cmdcount + 1 + self.FrameInstructions = self.FrameInstructions + 1 + end + + if (EmuFox) then + SetInstructions(cmdcount) + end + end +end + +function ENT:OutputError(intnumber,intparam) + local ErrorText = "Unknown error" + if (self.ErrorText[intnumber]) then ErrorText = self.ErrorText[intnumber] end + + surface.SetDrawColor(200,0,0,255) + surface.DrawRect(64-4,128-4,512-128+8,128+8) + surface.SetDrawColor(30,30,30,255) + surface.DrawRect(64,128,512-128,128) + + draw.DrawText( + "GPU Error : \n".. + "Parameter : \n".. + "Instruction: \n".. + "Error: \n", + "WireGPU_ConsoleFont",64+4,128+4,Color(255,64,64,255),0) + draw.DrawText( + " "..intnumber.."\n".. + " "..intparam .."\n".. + " "..self.XEIP.."\n".. + " "..ErrorText.."\n", + "WireGPU_ConsoleFont",64+4,128+4,Color(255,255,255,255),0) +end + +function ENT:RenderGPU(clearbg) + if (EmuFox) then + self:WriteCell(65513,1.33) + end + + self.FrameBuffer = WireGPU_GetMyRenderTarget(self:EntIndex()) + //self.SpriteBuffer = WireGPU_GetMyRenderTarget(self:EntIndex().."_sprite") + + local FrameRate = self.MinFrameRateRatio:GetFloat() or 4 + self.FramesSinceRedraw = self.FramesSinceRedraw + 1 + self.FrameInstructions = 0 + if (self.FramesSinceRedraw >= FrameRate) then + self.FramesSinceRedraw = 0 + local oldw = ScrW() + local oldh = ScrH() + + local OldRT = render.GetRenderTarget() + local NewRT = self.FrameBuffer + + render.SetRenderTarget(NewRT) + render.SetViewPort(0,0,512,512) + cam.Start2D() + if (self:ReadCell(65531) == 0) then + if ((self:ReadCell(65533) == 1) && (clearbg == true)) then + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(0,0,512,512) + end + if (self:ReadCell(65535) == 1) then + if (self.EntryPoint[3]) && (self.HandleError == 1) then + self:DoCall(3,FrameRate*600) + else + self:DoCall(0,FrameRate*600) + end + end + end + cam.End2D() + + //matFSB:SetMaterialFloat("$alpha", 1) + //render.SetMaterial(matFSB) + //render.DrawScreenQuad() + + //local TempRT = self.SpriteBuffer + //render.SetRenderTarget(TempRT) + //render.Clear(255,0,0,127) + + //render.SetRenderTarget(NewRT) + //matFSB:SetMaterialTexture("$basetexture",self.SpriteBuffer) + //render.SetMaterial(matFSB) + //render.DrawQuad(Vector(0,0,0),Vector(256,0,0),Vector(256,256,0),Vector(0,256,0)) + //render.DrawScreenQuad() + + //render.CopyRenderTargetToTexture(NewRT) + + render.SetViewPort(0,0,oldw,oldh) + render.SetRenderTarget(OldRT) + end +end + +function ENT:Draw() + self.Entity.DoNormalDraw = function() end + self.Entity.DrawEntityOutline = function() end + self.Entity:DrawModel() + + local DeltaTime = CurTime()-(self.PrevTime or CurTime()) + self.PrevTime = CurTime() + self.DeltaTime = DeltaTime + + if (WireGPU_HookedGPU == self) then + Wire_Render(self.Entity) + return + end + + self:RenderGPU(true) + + if (WireGPU_Monitors[self.Entity:GetModel()]) && (WireGPU_Monitors[self.Entity:GetModel()].OF) then + OF = WireGPU_Monitors[self.Entity:GetModel()].OF + OU = WireGPU_Monitors[self.Entity:GetModel()].OU + OR = WireGPU_Monitors[self.Entity:GetModel()].OR + Res = WireGPU_Monitors[self.Entity:GetModel()].RS + RatioX = WireGPU_Monitors[self.Entity:GetModel()].RatioX + Rot90 = WireGPU_Monitors[self.Entity:GetModel()].rot90 + + self:WriteCell(65513,1/RatioX) + else + OF = self.OF:GetFloat() or 0 + OU = self.OU:GetFloat() or 0 + OR = self.OR:GetFloat() or 0 + Res = self.Scale:GetFloat() or 1 + RatioX = self.Ratio:GetFloat() or 1 + Rot90 = self.Rot90:GetBool() or 0 + end + + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + if (Rot90 == true) then + rot = Vector(0,90,0) + end + + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + + local pos = self.Entity:GetPos()+(self.Entity:GetForward()*OF)+(self.Entity:GetUp()*OU)+(self.Entity:GetRight()*OR) + + local OldTex = WireGPU_matScreen:GetMaterialTexture("$basetexture") + WireGPU_matScreen:SetMaterialTexture("$basetexture",self.FrameBuffer) + + cam.Start3D2D(pos,ang,Res) + local w = 512*math.Clamp(self:ReadCell(65525),0,1) + local h = 512*math.Clamp(self:ReadCell(65524),0,1) + local x = -w/2 + local y = -h/2 + + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(-256,-256,512/RatioX,512) + + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(WireGPU_texScreen) + WireGPU_DrawScreen(x,y,w/RatioX,h,self:ReadCell(65522),self:ReadCell(65523)-self:ReadCell(65518)/512) + + local trace = {} + trace.start = LocalPlayer():GetShootPos() + trace.endpos = (LocalPlayer():GetAimVector() * self.workingDistance) + trace.start + trace.filter = LocalPlayer() + local trace = util.TraceLine(trace) + + if (trace.Entity == self.Entity) then + local pos = self.Entity:WorldToLocal(trace.HitPos) + local cx = (self.x1 - pos.y) / (self.x1 - self.x2) + local cy = (self.y1 - pos.z) / (self.y1 - self.y2) + + self:WriteCell(65505,cx) + self:WriteCell(65504,cy) + + if (self:ReadCell(65503) == 1) and (cx >= 0 and cy >= 0 and cx <= 1 and cy <= 1) then + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(surface.GetTextureID("gui/arrow")) + surface.DrawTexturedRectRotated(-256+cx*512/RatioX,-256+cy*512,32,32,45) + end + end + cam.End3D2D() + + WireGPU_matScreen:SetMaterialTexture("$basetexture",OldTex) + Wire_Render(self.Entity) +end + +function drawGPUHUD() + if (WireGPU_HookedGPU) then + Msg("Render GPU\n") + + if (not WireGPU_HookedGPU.RenderGPU) then + WireGPU_HookedGPU = nil + return + end + + WireGPU_HookedGPU:RenderGPU(false) + + local OldTex = WireGPU_matScreen:GetMaterialTexture("$basetexture") + WireGPU_matScreen:SetMaterialTexture("$basetexture",WireGPU_HookedGPU.FrameBuffer) + + local w = ScrW()*math.Clamp(WireGPU_HookedGPU:ReadCell(65525),0,1) + local h = ScrH()*math.Clamp(WireGPU_HookedGPU:ReadCell(65524),0,1) + local x = 0 + local y = 0 + + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(WireGPU_texScreen) + WireGPU_DrawScreen(x,y,w,h,WireGPU_HookedGPU:ReadCell(65522),WireGPU_HookedGPU:ReadCell(65523)) + + WireGPU_matScreen:SetMaterialTexture("$basetexture",OldTex) + end +end +//hook.Add("HUDPaint","drawGPUHUD",drawGPUHUD) + +function ENT:IsTranslucent() + return false +end diff --git a/lua/entities/gmod_wire_gpu/emufox_init.lua b/lua/entities/gmod_wire_gpu/emufox_init.lua new file mode 100644 index 0000000000..37aed6ef91 --- /dev/null +++ b/lua/entities/gmod_wire_gpu/emufox_init.lua @@ -0,0 +1,24 @@ +include("gmod_wire_cpu/compiler_asm.lua") + +function ENT:ServerInitialize() + self:Initialize() + self:InitializeGPUOpcodeNames() + self:InitializeASMOpcodes() + self:InitializeRegisterNames() + self:InitializeOptimizer() + self.IsGPU = true + self.MinFrameRateRatio = {} + self.MinFrameRateRatio.GetFloat = function() return 4 end +end + +function ENT:Write(value) + if (tonumber(value) ~= nil) && (value) then + self:WriteCell(self.WIP,value) + self.ROMMemory[self.WIP] = value + if (self.WIP == 65534) then + self:GPUHardReset() + end + //if (self.Debug) && (value != 0) then Msg("-> ZyeliosASM: Wrote "..value.." at ["..self.WIP.."]\n") end + end + self.WIP = self.WIP + 1 +end diff --git a/lua/entities/gmod_wire_gpu/gpu_clientbus.lua b/lua/entities/gmod_wire_gpu/gpu_clientbus.lua new file mode 100644 index 0000000000..9864728cf2 --- /dev/null +++ b/lua/entities/gmod_wire_gpu/gpu_clientbus.lua @@ -0,0 +1,95 @@ +function ENT:ReadCell(Address) + if (not Address) then + if (self.Debug) then + print("Non-existant address fed into address bus (read)!") + end + return 0 + end + + + if (not self:Is48bitInteger(Address)) then + self:Interrupt(15,Address) + return 0 + end + + if (Address < 0) || (Address > 65536) then + return 0 + else + if (self.Memory[Address]) then + return self.Memory[Address] + else + return 0 + end + end +end + +function ENT:WriteCell(Address, value) + if (EmuFox and (Address ~= 65513)) then SendBytes(1) end + + if (not Address) then + if (self.Debug) then + print("Non-existant address fed into address bus (read)!") + end + return false + end + + if (not self:Is48bitInteger(Address)) then + self:Interrupt(15,Address) + return false + end + + if (self.PrecompileMemory[Address]) then //If this byte was precompiled + local xeip = self.PrecompileMemory[Address] + self.PrecompileMemory[Address] = nil //Void precompile information + self.PrecompileData[xeip] = nil + end + + + if (Address < 0) || (Address > 65536) then + return false + else + if (Address == 65534) then self:GPUHardReset() end + if (Address == 65530) then self:GPURAMReset() end + + self.Memory[Address] = value + return true + end +end + +function ENT:Push(value) + if (self.BusLock == 1) then + if (self.Debug) then DebugMessage("Warning: Stack write while locked") end + return false + end + + self:WriteCell(self.ESP+self.SS,value) + self.ESP = self.ESP - 1 + if (self.ESP < 0) then + self.ESP = 0 + self:Interrupt(6,self.ESP) + return false + end + return true +end + +function ENT:Pop() + if (self.BusLock == 1) then + if (self.Debug) then DebugMessage("Warning: Stack read while locked") end + return nil + end + + self.ESP = self.ESP + 1 + if (self.ESP > 32767) then + self.ESP = 32767 + self:Interrupt(6,self.ESP) + return 0 + end + + local popvalue = self:ReadCell(self.ESP+self.SS) + if (popvalue) then + return popvalue + else + self:Interrupt(6,self.ESP) + return 0 + end +end diff --git a/lua/entities/gmod_wire_gpu/gpu_interrupt.lua b/lua/entities/gmod_wire_gpu/gpu_interrupt.lua new file mode 100644 index 0000000000..225a1257d3 --- /dev/null +++ b/lua/entities/gmod_wire_gpu/gpu_interrupt.lua @@ -0,0 +1,50 @@ +//INTERRUPTS TABLE +//Value | Meaning +//--------------------------------------------------------------------- +//2 | End of program +//3 | Division by zero +//4 | Unknown opcode +//5 | Internal processor error +//6 | Stack error (overflow/underflow) +//7 | Memory read/write fault +//13 | General processor fault +//15 | Address space violation +//------|--------------------------------------------------------------- +//16 | Pants integrity violation +//17 | +//18 | +//19 | +//20 | +//21 | +//22 | +//23 | String read error +//24 | +//25 | +//---------------------------------------------------------------------- + +function ENT:InitializeErrors() + self.ErrorText = {} + // | | + self.ErrorText[2] = "unexpected program end" + self.ErrorText[3] = "division by zero" + self.ErrorText[4] = "unknown opcode" + // | | + self.ErrorText[5] = "internal gpu error" + // | | + self.ErrorText[6] = "stack error" + self.ErrorText[7] = "memory read/write fault" + self.ErrorText[13] = "general processor fault" + self.ErrorText[15] = "addr space violation" + self.ErrorText[16] = "pants violation!!!" + self.ErrorText[23] = "string read error" + // | | +end + +function ENT:Interrupt(intnumber,intparam) + if (self.INTR == 1) then return end + self.INTR = 1 + self.HandleError = 1 + if (not self.EntryPoint[3]) then + self:OutputError(intnumber,intparam) + end +end diff --git a/lua/entities/gmod_wire_gpu/gpu_opcodes.lua b/lua/entities/gmod_wire_gpu/gpu_opcodes.lua new file mode 100644 index 0000000000..e5dc2cf6b0 --- /dev/null +++ b/lua/entities/gmod_wire_gpu/gpu_opcodes.lua @@ -0,0 +1,695 @@ +function ENT:InitializeGPUOpcodeNames() + self:InitializeOpcodeNames() + + //--------------------------------------------------------------------------------------------------------------------- + // GPU Opcodes + //--------------------------------------------------------------------------------------------------------------------- + self.DecodeOpcode["drect_test"] = 200 //DRECT_TEST : Draw retarded stuff + self.DecodeOpcode["dexit"] = 201 //DEXIT : End current frame execution + self.DecodeOpcode["dclr"] = 202 //DCLR : Clear screen color to black + self.DecodeOpcode["dclrtex"] = 203 //DCLRTEX : Clear background with texture + self.DecodeOpcode["dvxflush"] = 204 //DVXFLUSH : Flush current vertex buffer to screen + self.DecodeOpcode["dvxclear"] = 205 //DVXCLEAR : Clear vertex buffer + self.DecodeOpcode["derrorexit"] = 206 //DERROREXIT : Exit error handler + self.DecodeOpcode["dsetbuf_spr"] = 207 //DSETBUF_SPR : Set frame buffer to sprite buffer + self.DecodeOpcode["dsetbuf_fbo"] = 208 //DSETBUF_FBO : Set frame buffer to view buffer + self.DecodeOpcode["dtexture"] = 209 //DTEXTURE : Bind a texture (requires vertex buffer) + //- Pipe controls and one-operand opcodes ----------------------------------------------------------------------------- + self.DecodeOpcode["dvxpipe"] = 210 //DVXPIPE X : Vertex pipe = X [INT] + self.DecodeOpcode["dcvxpipe"] = 211 //DCVXPIPE X : Coordinate vertex pipe = X [INT] + self.DecodeOpcode["denable"] = 212 //DENABLE X : Enable parameter X [INT] + self.DecodeOpcode["ddisable"] = 213 //DDISABLE X : Disable parameter X [INT] + self.DecodeOpcode["dclrscr"] = 214 //DCLRSCR X : Clear screen with color X [COLOR] + self.DecodeOpcode["dcolor"] = 215 //DCOLOR X : Set current color to X [COLOR] + self.DecodeOpcode["dsetloadsz"] = 216 //DSETLOADSZ X : Set target texture width in pixels for loading [INT] + self.DecodeOpcode["dsetfont"] = 217 //DSETFONT X : Set current font to X [FONTID] + self.DecodeOpcode["dsetsize"] = 218 //DSETSIZE X : Set font size to X [INT] + self.DecodeOpcode["dmove"] = 219 //DMOVE X : Set offset position to X [2F] + //- Rendering opcodes ------------------------------------------------------------------------------------------------- + self.DecodeOpcode["dvxdata_2f"] = 220 //DVXDATA_2F X,Y : Draw solid 2d polygon (OFFSET,NUMVALUES) [2F,INT] + self.DecodeOpcode["dvxpoly"] = 220 // + self.DecodeOpcode["dvxdata_2f_tex"] = 221 //DVXDATA_2F_TEX X,Y : Draw textured 2d polygon (OFFSET,NUMVALUES) [2F+UV,INT] + self.DecodeOpcode["dvxtexpoly"] = 221 // + self.DecodeOpcode["dvxdata_3f"] = 222 //DVXDATA_3F X,Y : Draw solid 3d polygon (OFFSET,NUMVALUES) [3F,INT] + self.DecodeOpcode["dvxdata_3f_tex"] = 223 //DVXDATA_3F_TEX X,Y : Draw textured 3d polygon (OFFSET,NUMVALUES) [3F+UV,INT] + self.DecodeOpcode["dvxdata_wf"] = 224 //DVXDATA_WF X,Y : Draw wireframe 3d polygon (OFFSET,NUMVALUES) [3F,INT] + self.DecodeOpcode["drect"] = 225 //DRECT X,Y : Draw rectangle (XY1,XY2) [2F,2F] + self.DecodeOpcode["dcircle"] = 226 //DCIRCLE X,Y : Draw circle (XY,R) [2F,F] + self.DecodeOpcode["dline"] = 227 //DLINE X,Y : Draw line (XY1,XY2) [2F,2F] + self.DecodeOpcode["drectwh"] = 228 //DRECTWH X,Y : Draw rectangle (XY,WH) [2F,2F] + self.DecodeOpcode["dorect"] = 229 //DORECT X,Y : Draw outlined rectangle (XY1,XY2) [2F,2F] + //- More rendering ---------------------------------------------------------------------------------------------------- + self.DecodeOpcode["dtransform2f"] = 230 //DTRANSFORM2F X,Y : Transform Y, save to X [2F,2F] + self.DecodeOpcode["dtransform3f"] = 231 //DTRANSFORM3F X,Y : Transform Y, save to X [3F,3F] + self.DecodeOpcode["dscrsize"] = 232 //DSCRSIZE X,Y : Set screen size [F,F] + self.DecodeOpcode["drotatescale"] = 233 //DROTATESCALE X,Y : Rotate and scale [F,F] + self.DecodeOpcode["dorectwh"] = 234 //DORECTWH X,Y : Draw outlined rectangle (XY1,XY2) [2F,2F] + self.DecodeOpcode["dcullmode"] = 235 //DCULLMODE X,Y : Cull front (X) face, back (Y) faces [INT,INT] +// self.DecodeOpcode[""] = 236 //DPIXELS X,Y : Draw pixel data [] +// self.DecodeOpcode[""] = 237 //DTERMINAL X,Y : Draw console screen/terminal [] +// self.DecodeOpcode[""] = 238 //DPIXEL X,Y : Draw pixel [2F,COLOR] +// self.DecodeOpcode[""] = 239 //DEXECUTE X,Y : Execute fast render commands [] + //- Writing and lighting ---------------------------------------------------------------------------------------------- + self.DecodeOpcode["dwrite"] = 240 //DWRITE X,Y : Write Y to coordinates X [2F,STRING] + self.DecodeOpcode["dwritei"] = 241 //DWRITEI X,Y : Write INT Y to coordinates X [2F,I] + self.DecodeOpcode["dwritef"] = 242 //DWRITEF X,Y : Write 1F Y to coordinates X [2F,F] + self.DecodeOpcode["dentrypoint"] = 243 //DENTRYPOINT X,Y : Set entry point X to address Y [INT,INT] + self.DecodeOpcode["dsetlight"] = 244 //DSETLIGHT X,Y : Set light X to Y (Y points to [pos,color]) [INT,3F+COLOR] + self.DecodeOpcode["dgetlight"] = 245 //DGETLIGHT X,Y : Get light Y to X (X points to [pos,color]) [INT,3F+COLOR] + self.DecodeOpcode["dwritefmt"] = 246 //DWRITEFMT X,Y : Write formatted string Y to coordinates X [2F,STRING+PARAMS] + self.DecodeOpcode["dwritefix"] = 247 //DWRITEFIX X,Y : Write fixed value Y to coordinates X [2F,F] +// self.DecodeOpcode[""] = 248 //DTEXTWIDTH X,Y : Return text width of Y [INT,STRING] +// self.DecodeOpcode[""] = 249 //DTEXTHEIGHT X,Y : Return text height of Y [INT,STRING] + //--------------------------------------------------------------------------------------------------------------------- +// self.DecodeOpcode[""] = 258 // + self.DecodeOpcode["dloopxy"] = 259 //DLOOPXY X,Y : IF DX>0 {IP=X;IF CX>0{CX--}ELSE{DX--;CX=Y}} [INT,INT] + //- Misc -------------------------------------------------------------------------------------------------------------- + self.DecodeOpcode["mloadproj"] = 271 //MLOADPROJ X : Load matrix X into view matrix [MATRIX] + self.DecodeOpcode["mread"] = 272 //MREAD X : Write view matrix into matrix X [MATRIX] + self.DecodeOpcode["dt"] = 274 //DT X : X -> Frame DeltaTime [F] + self.DecodeOpcode["dstrprecache"] = 275 //DSTRPRECACHE X : Read and cache string [STRING] + self.DecodeOpcode["dshade"] = 276 //DSHADE X : COLOR = COLOR * X [F] + self.DecodeOpcode["dsetwidth"] = 277 //DSETWIDTH X : LINEWIDTH = X [F] + self.DecodeOpcode["mload"] = 278 //MLOAD X : Load matrix X into model matrix [MATRIX] + self.DecodeOpcode["dshadecol"] = 279 //DSHADECOL X : COLOR = Clamp(COLOR * X) [F] + //- Extra drawing ----------------------------------------------------------------------------------------------------- + self.DecodeOpcode["ddframe"] = 280 //DDFRAME X : Draw bordered frame [BORDER_STRUCT] + self.DecodeOpcode["ddbar"] = 281 //DDBAR X : Draw progress bar [BAR_STRUCT] + self.DecodeOpcode["ddgauge"] = 282 //DDGAUGE X : Draw gauge needle [GAUGE_STRUCT] + self.DecodeOpcode["draster"] = 284 //DRASTER X : Rasterize level [INT] + self.DecodeOpcode["ddterrain"] = 285 //DDTERRAIN X : Draw terrain [TERRAIN_STRUCT] + //- Sprites ----------------------------------------------------------------------------------------------------------- + self.DecodeOpcode["dloadbytes"] = 290 //DLOADBYTES X,Y : Load Y textures starting from ptr X [INT,INT] +// self.DecodeOpcode[""] = 291 //DCOPYTO +// self.DecodeOpcode[""] = 292 //DCOPYFROM + self.DecodeOpcode["dmuldt"] = 294 //DMULDT X,Y : X = Y * dT [2F,2F] + self.DecodeOpcode["dsmooth"] = 295 //DSMOOTH X,Y : X = X * U + Y * (1 - U) [2F,2F] + //- 3D ---------------------------------------------------------------------------------------------------------------- + self.DecodeOpcode["drotate"] = 300 //DROTATE X : Rotate(X) [4F] + self.DecodeOpcode["dtranslate"] = 301 //DTRANSLATE X : Translate(X) [4F] + self.DecodeOpcode["dscale"] = 302 //DSCALE X : Scale(X) [4F] + + + + //Bordered frames info: + //X points to array of 2f's: + //[PosX;PosY][Width;Height][HighlightPointer;ShadowPointer][FacePointer;BorderSize] + //--------------------------------------------------------------------------------------------------------------------- +end + +function ENT:InitializeGPUOpcodeTable() + self:InitializeASMOpcodes() + self:InitializeOpcodeTable() + //ZCPU-only opcodes: + self.OpcodeTable[16] = nil //RD + self.OpcodeTable[17] = nil //WD + self.OpcodeTable[28] = nil //SPG + self.OpcodeTable[29] = nil //CPG + self.OpcodeTable[37] = nil //HALT + self.OpcodeTable[41] = nil //IRET + self.OpcodeTable[42] = nil //STI + self.OpcodeTable[43] = nil //CLI + self.OpcodeTable[44] = nil //STP + self.OpcodeTable[45] = nil //CLP + self.OpcodeTable[46] = nil //STD + self.OpcodeTable[48] = nil //STE + self.OpcodeTable[49] = nil //CLE + self.OpcodeTable[70] = nil //NMIINT + self.OpcodeTable[95] = nil //ERPG + self.OpcodeTable[96] = nil //WRPG + self.OpcodeTable[97] = nil //RDPG + self.OpcodeTable[99] = nil //LIDTR + self.OpcodeTable[100] = nil //STATESTORE + self.OpcodeTable[109] = nil //STATERESTORE + self.OpcodeTable[110] = nil //NMIRET + self.OpcodeTable[111] = nil //IDLE + self.OpcodeTable[113] = nil //RLADD + self.OpcodeTable[122] = nil //SPP + self.OpcodeTable[123] = nil //CPP + self.OpcodeTable[124] = nil //SRL + self.OpcodeTable[125] = nil //GRL + self.OpcodeTable[131] = nil //SMAP + self.OpcodeTable[132] = nil //GMAP + + //------------------------------------------------------------ + self.OpcodeTable[84] = function (Param1,Param2) //IN + return self:ReadCell(63488+Param2) + end + self.OpcodeTable[85] = function (Param1,Param2) //OUT + self:WriteCell(63488+Param1,Param2) + end + //------------------------------------------------------------ + self.OpcodeTable[200] = function (Param1, Param2) //DRECT_TEST + surface.SetDrawColor(math.abs(255*math.sin(self.TIMER)),math.abs(255*math.sin(self.TIMER*2)),math.abs(255*math.sin(self.TIMER*3)),255) + surface.DrawRect(math.abs(460*math.sin(self.TIMER/7)),math.abs(460*math.cos(self.TIMER/5)),32,32) + end + self.OpcodeTable[201] = function (Param1, Param2) //DEXIT + self.INTR = 1 //trigger suicide + end + self.OpcodeTable[202] = function (Param1, Param2) //DCLR + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(0,0,512,512) + end + self.OpcodeTable[203] = function (Param1, Param2) //DCLRTEX + surface.SetDrawColor(255,255,255,255) + surface.DrawTexturedRect(0,0,512,512) + end + self.OpcodeTable[204] = function (Param1, Param2) //DVXFLUSH + self:VertexBuffer_Flush() + end + self.OpcodeTable[205] = function (Param1, Param2) //DVXCLEAR + self.VertexBuffer = {} + self.VertexBufferCount = 0 + end + self.OpcodeTable[206] = function (Param1, Param2) //DERROREXIT + self.INTR = 1 + self.HandleError = 0 + end + //------------------------------------------------------------ + self.OpcodeTable[210] = function (Param1, Param2) //DVXPIPE + self.VertexPipe = math.Clamp(Param1,0,5) + end + self.OpcodeTable[211] = function (Param1, Param2) //DCVXPIPE + self.CVertexPipe = math.Clamp(Param1,0,3) + end + self.OpcodeTable[212] = function (Param1, Param2) //DENABLE + if (Param1 == 0) then + self.VertexBufEnabled = true + elseif (Param1 == 1) then + self.VertexBufZSort = true + elseif (Param1 == 2) then + self.VertexLighting = true + elseif (Param1 == 3) then + self.VertexCulling = true + end + end + self.OpcodeTable[213] = function (Param1, Param2) //DDISABLE + if (Param1 == 0) then + self.VertexBufEnabled = false + elseif (Param1 == 1) then + self.VertexBufZSort = false + elseif (Param1 == 2) then + self.VertexLighting = false + elseif (Param1 == 3) then + self.VertexCulling = false + end + + end + self.OpcodeTable[214] = function (Param1, Param2) //DCLRSCR + self:VertexBuffer_SetColor(self:Read4f(Param1)) + surface.DrawRect(0,0,512,512) + end + self.OpcodeTable[215] = function (Param1, Param2) //DCOLOR + self:VertexBuffer_SetColor(self:Read4f(Param1)) + end + self.OpcodeTable[216] = function (Param1, Param2) //DBINDTEXTURE + end + self.OpcodeTable[217] = function (Param1, Param2) //DSETFONT + self.CurFont = math.floor(math.Clamp(Param1,0,#self.FontNames-1)) + end + self.OpcodeTable[218] = function (Param1, Param2) //DSETSIZE + self.CurFontSize = math.floor(math.Clamp(Param1,4,200)) + end + self.OpcodeTable[219] = function (Param1, Param2) //DMOVE + if (Param1 == 0) then + self:WriteCell(65484,0) + self:WriteCell(65483,0) + else + self:WriteCell(65484,self:ReadCell(Param1+0)) + self:WriteCell(65483,self:ReadCell(Param1+1)) + end + end + //------------------------------------------------------------ + self.OpcodeTable[220] = function (Param1, Param2) //DVXDATA_2F + local vertexbuf = {} + for i=1,Param2 do vertexbuf[i] = {} end + + for i=1,Param2 do + vertexbuf[i].x = self:ReadCell(Param1+(i-1)*2+0) + vertexbuf[i].y = self:ReadCell(Param1+(i-1)*2+1) + vertexbuf[i].u = 0 + vertexbuf[i].v = 0 + end + + self:VertexBuffer_Add(vertexbuf) + end + self.OpcodeTable[221] = function (Param1, Param2) //DVXDATA_2F_TEX + local vertexbuf = {} + for i=1,Param2 do vertexbuf[i] = {} end + + for i=1,Param2 do + vertexbuf[i].x = self:ReadCell(Param1+(i-1)*4+0) + vertexbuf[i].y = self:ReadCell(Param1+(i-1)*4+1) + vertexbuf[i].u = self:ReadCell(Param1+(i-1)*4+2) + vertexbuf[i].v = self:ReadCell(Param1+(i-1)*4+3) + end + self:VertexBuffer_Add(vertexbuf) + end + self.OpcodeTable[222] = function (Param1, Param2) //DVXDATA_3F + local vertexbuf = {} + for i=1,3 do vertexbuf[i] = {} end + + for i=1,Param2 do + vertexbuf[1].x = self:ReadCell(Param1+(i-1)*9+0) + vertexbuf[1].y = self:ReadCell(Param1+(i-1)*9+1) + vertexbuf[1].z = self:ReadCell(Param1+(i-1)*9+2) + + vertexbuf[2].x = self:ReadCell(Param1+(i-1)*9+3) + vertexbuf[2].y = self:ReadCell(Param1+(i-1)*9+4) + vertexbuf[2].z = self:ReadCell(Param1+(i-1)*9+5) + + vertexbuf[3].x = self:ReadCell(Param1+(i-1)*9+6) + vertexbuf[3].y = self:ReadCell(Param1+(i-1)*9+7) + vertexbuf[3].z = self:ReadCell(Param1+(i-1)*9+8) + + self:VertexBuffer_Add(vertexbuf) + end + end + self.OpcodeTable[223] = function (Param1, Param2) //DVXDATA_3F_TEX + local vertexbuf = {} + for i=1,3 do vertexbuf[i] = {} end + + for i=1,Param2 do + vertexbuf[1].x = self:ReadCell(Param1+(i-1)*15+0) + vertexbuf[1].y = self:ReadCell(Param1+(i-1)*15+1) + vertexbuf[1].z = self:ReadCell(Param1+(i-1)*15+2) + + vertexbuf[1].u = self:ReadCell(Param1+(i-1)*15+3) + vertexbuf[1].v = self:ReadCell(Param1+(i-1)*15+4) + + vertexbuf[2].x = self:ReadCell(Param1+(i-1)*15+5) + vertexbuf[2].y = self:ReadCell(Param1+(i-1)*15+6) + vertexbuf[2].z = self:ReadCell(Param1+(i-1)*15+7) + + vertexbuf[2].u = self:ReadCell(Param1+(i-1)*15+8) + vertexbuf[2].v = self:ReadCell(Param1+(i-1)*15+9) + + vertexbuf[3].x = self:ReadCell(Param1+(i-1)*15+10) + vertexbuf[3].y = self:ReadCell(Param1+(i-1)*15+11) + vertexbuf[3].z = self:ReadCell(Param1+(i-1)*15+12) + + vertexbuf[3].u = self:ReadCell(Param1+(i-1)*15+13) + vertexbuf[3].v = self:ReadCell(Param1+(i-1)*15+14) + + self:VertexBuffer_Add(vertexbuf) + end + end + self.OpcodeTable[225] = function (Param1, Param2) //DRECT + local vertexbuf = {} + for i=1,4 do vertexbuf[i] = {} end + + vertexbuf[1].x = self:ReadCell(Param1+0) + vertexbuf[1].y = self:ReadCell(Param1+1) + vertexbuf[1].u = 0 + vertexbuf[1].v = 0 + + vertexbuf[2].x = self:ReadCell(Param2+0) + vertexbuf[2].y = self:ReadCell(Param1+1) + vertexbuf[2].u = 1 + vertexbuf[2].v = 0 + + vertexbuf[3].x = self:ReadCell(Param2+0) + vertexbuf[3].y = self:ReadCell(Param2+1) + vertexbuf[3].u = 1 + vertexbuf[3].v = 1 + + vertexbuf[4].x = self:ReadCell(Param1+0) + vertexbuf[4].y = self:ReadCell(Param2+1) + vertexbuf[4].u = 0 + vertexbuf[4].v = 1 + + self:VertexBuffer_Add(vertexbuf) + end + self.OpcodeTable[226] = function (Param1, Param2) //DCIRCLE + local vertexbuf = {} + local numsides = math.Clamp(self:ReadCell(65485),3,64) + local astart = self:ReadCell(65478) + local aend = self:ReadCell(65477) + local astep = (aend-astart) / numsides + local c = self:Read2f(Param1) + + local r = Param2 + + for i=1,3 do vertexbuf[i] = {} end + + for i=1,numsides do + vertexbuf[1].x = c.x + r*math.sin(astart+astep*(i+0)) + vertexbuf[1].y = c.y + r*math.cos(astart+astep*(i+0)) + vertexbuf[1].u = 0 + vertexbuf[1].v = 0 + + vertexbuf[2].x = c.x + vertexbuf[2].y = c.y + vertexbuf[2].u = 0 + vertexbuf[2].v = 0 + + vertexbuf[3].x = c.x + r*math.sin(astart+astep*(i+1)) + vertexbuf[3].y = c.y + r*math.cos(astart+astep*(i+1)) + vertexbuf[3].u = 0 + vertexbuf[3].v = 0 + + self:VertexBuffer_Add(vertexbuf) + end + end + self.OpcodeTable[227] = function (Param1, Param2) //DLINE + self:DrawLine(self:Read2f(Param1),self:Read2f(Param2)) + end + self.OpcodeTable[228] = function (Param1, Param2) //DRECTWH + local vertexbuf = {} + for i=1,4 do vertexbuf[i] = {} end + + vertexbuf[1].x = self:ReadCell(Param1+0) + vertexbuf[1].y = self:ReadCell(Param1+1) + vertexbuf[1].u = 0 + vertexbuf[1].v = 0 + + vertexbuf[2].x = self:ReadCell(Param1+0)+self:ReadCell(Param2+0) + vertexbuf[2].y = self:ReadCell(Param1+1) + vertexbuf[2].u = 1 + vertexbuf[2].v = 0 + + vertexbuf[3].x = self:ReadCell(Param1+0)+self:ReadCell(Param2+0) + vertexbuf[3].y = self:ReadCell(Param1+1)+self:ReadCell(Param2+1) + vertexbuf[3].u = 1 + vertexbuf[3].v = 1 + + vertexbuf[4].x = self:ReadCell(Param1+0) + vertexbuf[4].y = self:ReadCell(Param1+1)+self:ReadCell(Param2+1) + vertexbuf[4].u = 0 + vertexbuf[4].v = 1 + + self:VertexBuffer_Add(vertexbuf) + end + self.OpcodeTable[229] = function (Param1, Param2) //DTRECTWH + local vertexbuf = {} + for i=1,4 do vertexbuf[i] = {} end + + vertexbuf[1].x = self:ReadCell(Param1+0) + vertexbuf[1].y = self:ReadCell(Param1+1) + vertexbuf[1].u = 0 + vertexbuf[1].v = 0 + + vertexbuf[2].x = self:ReadCell(Param2+0) + vertexbuf[2].y = self:ReadCell(Param1+1) + vertexbuf[2].u = 1 + vertexbuf[2].v = 0 + + vertexbuf[3].x = self:ReadCell(Param2+0) + vertexbuf[3].y = self:ReadCell(Param2+1) + vertexbuf[3].u = 1 + vertexbuf[3].v = 1 + + vertexbuf[4].x = self:ReadCell(Param1+0) + vertexbuf[4].y = self:ReadCell(Param2+1) + vertexbuf[4].u = 0 + vertexbuf[4].v = 1 + + self:VertexBuffer_Add(vertexbuf) + end + //------------------------------------------------------------ + self.OpcodeTable[230] = function (Param1, Param2) //DTRANSFORM2F + local tcoord = self:Read2f(Param2) + tcoord = self:VertexTransform(tcoord) + self:Write2f(Param1,tcoord) + end + self.OpcodeTable[231] = function (Param1, Param2) //DTRANSFORM3F + local tcoord = self:Read3f(Param1) + tcoord = self:VertexTransform(tcoord) + self:Write3f(Param1,tcoord) + end + self.OpcodeTable[232] = function (Param1, Param2) //DSCRSIZE + self:WriteCell(65515,Param1) + self:WriteCell(65514,Param2) + end + self.OpcodeTable[233] = function (Param1, Param2) //DROTATESCALE + self:WriteCell(65482,Param1) + self:WriteCell(65481,Param2) + end + self.OpcodeTable[234] = function (Param1, Param2) //DORECTWH + local vertexbuf = {} + for i=1,4 do vertexbuf[i] = {} end + + vertexbuf[1].x = self:ReadCell(Param1+0) + vertexbuf[1].y = self:ReadCell(Param1+1) + vertexbuf[2].x = self:ReadCell(Param1+0)+self:ReadCell(Param2+0) + vertexbuf[2].y = self:ReadCell(Param1+1) + vertexbuf[3].x = self:ReadCell(Param1+0)+self:ReadCell(Param2+0) + vertexbuf[3].y = self:ReadCell(Param1+1)+self:ReadCell(Param2+1) + vertexbuf[4].x = self:ReadCell(Param1+0) + vertexbuf[4].y = self:ReadCell(Param1+1)+self:ReadCell(Param2+1) + + self:DrawLine(vertexbuf[1],vertexbuf[2]) + self:DrawLine(vertexbuf[2],vertexbuf[3]) + self:DrawLine(vertexbuf[3],vertexbuf[4]) + self:DrawLine(vertexbuf[4],vertexbuf[1]) + end + //------------------------------------------------------------ + self.OpcodeTable[240] = function (Param1, Param2) //DWRITE + if (not self.StringCache[Param2]) then + self.StringCache[Param2] = self:ReadStr(Param2) + end + local text = self.StringCache[Param2] + + self:FontWrite(Param1,text) + end + self.OpcodeTable[241] = function (Param1, Param2) //DWRITEI + self:FontWrite(Param1,math.floor(Param2)) + end + self.OpcodeTable[242] = function (Param1, Param2) //DWRITEF + self:FontWrite(Param1,Param2) + end + self.OpcodeTable[243] = function (Param1, Param2) //DENTRYPOINT + self.EntryPoint[Param1] = Param2 + end + self.OpcodeTable[244] = function (Param1, Param2) //DSETLIGHT + if (Param1 < 0) || (Param1 > 7) then + self:Interrupt(19,0) + else + self.Lights[Param1] = {} + self.Lights[Param1].pos = self:Read4f(Param2+0) //Pos + self.Lights[Param1].col = self:Read4f(Param2+4) //Color + end + end + self.OpcodeTable[245] = function (Param1, Param2) //DGETLIGHT + if (Param1 < 0) || (Param1 > 7) then + self:Interrupt(19,0) + else + if (self.Lights[Param1]) then + self:Write3f(self.Lights[Param1]["pos"]) + self:Write3f(self.Lights[Param1]["col"]) + else + self:Write3f(0) + self:Write3f(0) + end + end + end + self.OpcodeTable[246] = function (Param1, Param2) //DWRITEFMT string.format( + if (not self.StringCache[Param2]) then + self.StringCache[Param2] = self:ReadStr(Param2) + end + local text = self.StringCache[Param2] + local ptr = Param2 + string.len(text) + 1 + local ptr2 = self:ReadCell(65512) + if (ptr2 ~= 0) then ptr = ptr2 end + local finaltext = "" + + local inparam = false + local lengthmod = nil + + while (text ~= "") do + local chr = string.sub(text,1,1) + text = string.sub(text,2,65536) + + if (inparam == false) then + if (chr == "%") then + inparam = true + else + finaltext = finaltext .. chr + end + else + if (chr == ".") then + chr = string.sub(text,1,1) + text = string.sub(text,2,65536) + + if (tonumber(chr)) then + lengthmod = tonumber(chr) + end + elseif (chr == "i") then + if (lengthmod) then + local digits = 0 + local num = math.floor(self:ReadCell(ptr)) + local temp = num + while (temp > 0) do + digits = digits + 1 + temp = math.floor(temp / 10) + end + if (num == 0) then + digits = 1 + end + + local fnum = tostring(num) + while (digits < lengthmod) do + digits = digits + 1 + fnum = "0"..fnum + end + + finaltext = finaltext ..fnum + else + finaltext = finaltext .. math.floor(self:ReadCell(ptr)) + end + ptr = ptr + 1 + inparam = false + lengthmod = nil + elseif (chr == "f") then + finaltext = finaltext .. self:ReadCell(ptr) + ptr = ptr + 1 + inparam = false + lengthmod = nil + elseif (chr == "s") then + local addr = self:ReadCell(ptr) + if (not self.StringCache[addr]) then + self.StringCache[addr] = self:ReadStr(addr) + end + local str = self.StringCache[addr] + finaltext = finaltext .. str + ptr = ptr + 1 + inparam = false + lengthmod = nil + elseif (chr == "t") then + while (string.len(finaltext) % (lengthmod or 6) != 0) do + finaltext = finaltext.." " + end + inparam = false + lengthmod = nil + elseif (chr == "%") then + finaltext = finaltext .. "%" + inparam = false + lengthmod = nil + end + end + end + + self:FontWrite(Param1,finaltext) + end + self.OpcodeTable[247] = function (Param1, Param2) //DWRITEFIX + local text = Param2 + if (Param2 == math.floor(Param2)) then + text = text..".0" + end + + self:FontWrite(Param1,text) + end + //------------------------------------------------------------ + self.OpcodeTable[271] = function (Param1, Param2) //MLOADPROJ + self.ProjectionMatrix = self:ReadMatrix(Param1) + end + self.OpcodeTable[272] = function (Param1, Param2) //MREAD + self:WriteMatrix(Param1,self.ModelMatrix) + end + self.OpcodeTable[274] = function (Param1, Param2) //DT + return self.DeltaTime + end + self.OpcodeTable[275] = function (Param1, Param2) //DSTRPRECACHE + self.StringCache[Param1] = self:ReadStr(Param1) + end + self.OpcodeTable[276] = function (Param1, Param2) //DSHADE + self.CurColor.x = self.CurColor.x*Param1 + self.CurColor.y = self.CurColor.y*Param1 + self.CurColor.z = self.CurColor.z*Param1 + self:VertexBuffer_SetColor(self.CurColor) + end + self.OpcodeTable[277] = function (Param1, Param2) //DSETWIDTH + self:WriteCell(65476,Param1) + end + self.OpcodeTable[278] = function (Param1, Param2) //MLOAD + self.ModelMatrix = self:ReadMatrix(Param1) + end + + //------------------------------------------------------------ + self.OpcodeTable[280] = function (Param1, Param2) //DDFRAME + local v1 = self:Read2f(Param1+0) //x,y + local v2 = self:Read2f(Param1+2) //w,h + local v3 = self:Read2f(Param1+4) //c1,c2 + local v4 = self:Read2f(Param1+6) //c3,bordersize + + local cshadow = self:Read3f(v3.x) + local chighlight = self:Read3f(v3.y) + local cface = self:Read3f(v4.x) + + surface.SetDrawColor(cshadow.x,cshadow.y,cshadow.z,255) + self:DrawRectangle(v4.y+v1.x ,v4.y+v1.y, + v4.y+v2.x+v1.x,v4.y+v2.y+v1.y) //Shadow + + surface.SetDrawColor(chighlight.x,chighlight.y,chighlight.z,255) + self:DrawRectangle(-v4.y+v1.x ,-v4.y+v1.y, + -v4.y+v2.x+v1.x,-v4.y+v2.y+v1.y) //Highlight + + surface.SetDrawColor(cface.x,cface.y,cface.z,255) + self:DrawRectangle(v1.x ,v1.y, + v2.x+v1.x,v2.y+v1.y) //Face + end + self.OpcodeTable[285] = function (Param1, Param2) //DDTERRAIN + local w = math.floor(self:ReadCell(Param1+0) or 0) + local h = math.floor(self:ReadCell(Param1+1) or 0) + local r = math.Clamp(math.floor(self:ReadCell(Param1+2) or 0),0,16) + local u = math.floor(self:ReadCell(Param1+3) or 0) + local v = math.floor(self:ReadCell(Param1+4) or 0) + + local vertexbuf = {} + for i=1,3 do vertexbuf[i] = {} end + + local minx = math.Clamp(math.floor(w/2+u - r),1,w-1) + local miny = math.Clamp(math.floor(h/2+v - r),1,h-1) + local maxx = math.Clamp(math.floor(w/2+u + r),1,w-1) + local maxy = math.Clamp(math.floor(h/2+v + r),1,h-1) + + for i=minx,maxx do + for j=miny,maxy do + local x = i + local y = j + local xpos = x-w/2-u-0.5 + local ypos = y-h/2-v-0.5 + + if ((x>0) and (x0) and (y 65536) then + return nil + else + if (self.Memory[Address]) then + return self.Memory[Address] + else + return 0 + end + end +end + +function ENT:WriteCell(Address, value) + if (Address < 0) || (Address > 65536) then + return false + else + //0. Write it to internal memory + self.Memory[Address] = value + + if (self.CachingEnabled == true) && (Address ~= 65535) && (Address ~= 65534) then + //1. Check if address recieved is in current memory cache + if ((Address >= self.MemoryCacheBase) && + (Address < self.MemoryCacheBase+self.MemoryCacheSize)) then + //1a. Address is inside memory cache. Just alter cache + self.MemoryCache[Address - self.MemoryCacheBase] = value + return true + end + + //2. Check if address is on boundary of current memory cache, and we can still add to cache + if ((Address == self.MemoryCacheBase+self.MemoryCacheSize) && + (self.MemoryCacheSize <= 32)) then + self.MemoryCacheSize = self.MemoryCacheSize + 1 + self.MemoryCache[Address - self.MemoryCacheBase] = value + return true + end + + //3. It did not write to cache. Purge old cache, and create new one + self:FlushCache() + self.MemoryCacheSize = 1 + self.MemoryCacheBase = Address + self.MemoryCache[0] = value + else + local rp = RecipientFilter() + if (self.ForcePlayer) then + rp:AddPlayer(self.ForcePlayer) + else + rp:AddAllPlayers() + end + + umsg.Start("wiregpu_memorymessage", rp) + umsg.Long(self:EntIndex()) + umsg.Long(Address) + umsg.Long(1) + umsg.Float(value) + umsg.End() + end + return true + end +end diff --git a/lua/entities/gmod_wire_gpu/gpu_vm.lua b/lua/entities/gmod_wire_gpu/gpu_vm.lua new file mode 100644 index 0000000000..b3582477fb --- /dev/null +++ b/lua/entities/gmod_wire_gpu/gpu_vm.lua @@ -0,0 +1,803 @@ +if (EmuFox) then + include('gmod_wire_cpu/cpu_vm.lua') //Include ZCPU VM + include('gmod_wire_cpu/cpu_opcodes.lua') //Include ZCPU opcodes + include('gmod_wire_cpu/cpu_bitwise.lua') //Include bitwise operations + + include('gmod_wire_gpu/gpu_opcodes.lua') //Override ZCPU opcodes + include('gmod_wire_gpu/gpu_clientbus.lua') //Own GPU bus + include('gmod_wire_gpu/gpu_interrupt.lua') //Own GPU interrupts +else + include('entities/gmod_wire_cpu/cpu_vm.lua') //Include ZCPU VM + include('entities/gmod_wire_cpu/cpu_opcodes.lua') //Include ZCPU opcodes + include('entities/gmod_wire_cpu/cpu_bitwise.lua') //Include bitwise operations + + include('gpu_opcodes.lua') //Override ZCPU opcodes + include('gpu_clientbus.lua') //Own GPU bus + include('gpu_interrupt.lua') //Own GPU interrupts +end + +function ENT:GPUHardReset() + self.HandleError = 0 + self:GPUFrameReset() + + self.EntryPoint = {} + self.EntryPoint[0] = 0 + + self.Memory[65533] = 1 + self.Memory[65531] = 0 +end + +function ENT:GPURAMReset() + self.Memory = {} + self.ROMMemory = {} + + self.PrecompileData = {} + self.PrecompileMemory = {} + + self:GPUMathInit() +end + +function ENT:GPUFrameReset() + self:Reset() + + //Remove junk: + self.Page = nil + self.IDTR = nil + self.EF = nil + self.PF = nil + self.IF = nil + self.LADD = nil + self.BusLock = nil + self.Idle = nil + self.CPAGE = nil + + self.ESP = 32767 + + self.TIMER = self.PrevTime + + //Initialize GPU + self:GPUResetRegisters() + self:GPUMathReset() +end + +function ENT:GPUResetRegisters() + //self:WriteCell(65533,0) //Restore first page + + //Hardware control registers: + //[65535] - CLK + //[65534] - RESET + //[65533] - HARDWARE CLEAR + //[65532] - Vertex mode (render vertex instead of RT) + //[65531] - HALT + //[65530] - RAM_RESET + + self.Memory[65535] = 1 + self.Memory[65534] = 0 + //self.Memory[65533] = 1 + self.Memory[65532] = 0 + //self.Memory[65531] = 0 + + //Image control: + //[65525] - Horizontal image scale + //[65524] - Vertical image scale + //[65523] - Hardware scale + //[65522] - Rotation (0 - 0*, 1 - 90*, 2 - 180*, 3 - 270*) + //[65521] - Sprite/texture size [32] + //[65520] - Pointer to texture data + //[65519] - Size of texture data + //[65518] - Raster quality + + self.Memory[65525] = 1 + self.Memory[65524] = 1 + self.Memory[65523] = 0 + self.Memory[65522] = 0 + self.Memory[65521] = 32 + self.Memory[65520] = 0 + self.Memory[65519] = 0 + self.Memory[65518] = 0 + + //Vertex pipe controls: + //[65515] - Image width (800) + //[65514] - Image height (600) + //[65513] - Real screen ratio + //[65512] - Parameter list address (for dwritefmt) + + self.Memory[65515] = 800 + self.Memory[65514] = 600 + //self.Memory[65513] = 0 + self.Memory[65512] = 0 + + //Cursor control: + //[65505] - Cursor X (0..1) + //[65504] - Cursor Y (0..1) + //[65503] - Cursor visible + + //self.Memory[65505] = 0 + //self.Memory[65504] = 0 + self.Memory[65503] = 0 + + //Brightness control: + //[65495] - Brightness W + //[65494] - Brightness R + //[65493] - Brightness G + //[65492] - Brightness B + //[65491] - Contrast W + //[65490] - Contrast R + //[65489] - Contrast G + //[65488] - Contrast B + + self.Memory[65495] = 1 + self.Memory[65494] = 1 + self.Memory[65493] = 1 + self.Memory[65492] = 1 + self.Memory[65491] = 0 + self.Memory[65490] = 0 + self.Memory[65489] = 0 + self.Memory[65488] = 0 + + //Rendering settings + //[65485] - Circle quality (3..128) + //[65484] - Offset Point X + //[65483] - Offset Point Y + //[65482] - Rotation (rad) + //[65481] - Scale + //[65480] - Center point X + //[65479] - Center point Y + //[65478] - Circle start (rad) + //[65477] - Circle end (rad) + //[65476] - Line width (1) + //[65475] - Scale X + //[65474] - Scale Y + //[65473] - Font align + //[65472] - ZOffset + + self.Memory[65485] = 32 + self.Memory[65484] = 0 + self.Memory[65483] = 0 + self.Memory[65482] = 0 + self.Memory[65481] = 1 + self.Memory[65480] = 0 + self.Memory[65479] = 0 + self.Memory[65478] = 0 + self.Memory[65477] = 3.141592*2 + self.Memory[65476] = 1 + self.Memory[65475] = 1 + self.Memory[65474] = 1 + self.Memory[65473] = 0 + self.Memory[65472] = 0 + + + //================================= + //[64512] - last register + //Ports: + //[63488]..[64511] - External ports +end + +function ENT:InitializeGPUVariableSet() + self:InitializeCPUVariableSet() + + self.CPUVariable[24] = nil //IDTR + self.CPUVariable[27] = nil //LADD + + self.CPUVariable[32] = nil //IF + self.CPUVariable[33] = nil //PF + self.CPUVariable[34] = nil //EF + + self.CPUVariable[45] = nil //BusLock + self.CPUVariable[46] = nil //IDLE + self.CPUVariable[47] = nil //INTR + + self.CPUVariable[52] = nil //NIDT +end + +function ENT:InitializeGPULookupTables() + self:InitializeLookupTables() + + for i=1000,2024 do + self.ParamFunctions_1[i] = function() return self:ReadCell(63488+self.PrecompileData[self.XEIP].dRM1-1000) end + self.ParamFunctions_2[i] = function() return self:ReadCell(63488+self.PrecompileData[self.XEIP].dRM2-1000) end + self.WriteBackFunctions[i] = function(Result) self:WriteCell(63488+self.PrecompileData[self.XEIP].dRM1-1000,Result) end + self.WriteBackFunctions2[i] = function(Result) self:WriteCell(63488+self.PrecompileData[self.XEIP].dRM2-1000,Result) end + end + +end + +function ENT:GPUExecute() +// self.TIMER = self.TIMER + self.DeltaTime + self.TMR = self.TMR + 1 + + if (not self.IP) then + self:Interrupt(5,0) + return + end + + self.XEIP = self.IP+self.CS + self.CPAGE = math.floor(self.XEIP / 128) + + //Dynamic precompiler: check if opcode was precompiled + if (self.PrecompileData[self.XEIP]) then + //Simulate read + self.IP = self.IP + self.PrecompileData[self.XEIP].Size + self.CODEBYTES = self.CODEBYTES + self.PrecompileData[self.XEIP].Size + + //Verify opcode + if (self.PrecompileData[self.XEIP].Valid) then + //Execute + local Result = self.PrecompileData[self.XEIP].Execute() + if (Result) then + if (Result == "Read error") then + self:Interrupt(5,1) //Read error during execute + else + self.PrecompileData[self.XEIP].WriteBack(Result) + end + end + else + if (self.PrecompileData[self.XEIP].UnknownOpcode) then + self:Interrupt(4,self.PrecompileData[self.XEIP].Opcode) //Unknown Opcode + else + self:Interrupt(5,2) //Internal/opcode read error + end + end + else + self:Precompile(self.XEIP) + end +end + +function ENT:GPUMathInit() + self.FontNames = {} + self.FontNames[0] = "Lucida Console" + self.FontNames[1] = "Courier New" + self.FontNames[2] = "Trebuchet" + self.FontNames[3] = "Arial" + self.FontNames[4] = "Times New Roman" + + self:GPUMathReset() +end + +function ENT:GPUMathReset() + self.StringCache = {} + + //CVertex pipes: + //0 - direct (0..512 or 0..1024 range) + //1 - mapped to screen (set by special registers in GPU) + //2 - mapped to 0..1 range + //3 - mapped to -1..1 range + + //Vertex pipes: + //0 - XY mapping + //1 - YZ mapping + //2 - XZ mapping + //3 - XYZ projective mapping + //4 - XY mapping + matrix + //5 - XYZ projective mapping + matrix + + //Entry points: + //[0] DRAW Called when screen is being drawn + //[1] INIT Called when screen is initialized (reuploaded) + //[2] USE Called when screen is used + //[3] ERROR Called when GPU error occurs (instead of DRAW) + //[4] ASYNC Called when GPU is reset, for running asynchrounous thread + + //DDisable/DEnable parameters: + //[0] VERTEX_ZSORT Enable or disable ZSorting in vertex buffer (sorted on flush) + //[1] VERTEX_LIGHTING Enable or disable vertex lighting + //[2] VERTEX_BUFFER Enable or disable vertex buffer + //[3] VERTEX_CULLING Enable or disable culling on faces + //[4] + + self.CVertexPipe = 0 + self.VertexPipe = 0 + + self.VertexBufEnabled = false + self.VertexBufZSort = false + self.VertexLighting = false + self.VertexCulling = false + + self.VertexBuffer = {} + self.VertexBufferCount = 0 + + self.Lights = {} + + self.CurFont = 0 + self.CurFontSize = 12 + self.CurColor = {x = 0, y = 0, z = 0, w = 255} + + //Model transform matrix + self.ModelMatrix = {} + + self.ModelMatrix[0] = 1 + self.ModelMatrix[1] = 0 + self.ModelMatrix[2] = 0 + self.ModelMatrix[3] = 0 + + self.ModelMatrix[4] = 0 + self.ModelMatrix[5] = 1 + self.ModelMatrix[6] = 0 + self.ModelMatrix[7] = 0 + + self.ModelMatrix[8] = 0 + self.ModelMatrix[9] = 0 + self.ModelMatrix[10] = 1 + self.ModelMatrix[11] = 0 + + self.ModelMatrix[12] = 0 + self.ModelMatrix[13] = 0 + self.ModelMatrix[14] = 0 + self.ModelMatrix[15] = 1 + + //View transform matrix + self.ProjectionMatrix = {} + + self.ProjectionMatrix[0] = 1 + self.ProjectionMatrix[1] = 0 + self.ProjectionMatrix[2] = 0 + self.ProjectionMatrix[3] = 0 + + self.ProjectionMatrix[4] = 0 + self.ProjectionMatrix[5] = 1 + self.ProjectionMatrix[6] = 0 + self.ProjectionMatrix[7] = 0 + + self.ProjectionMatrix[8] = 0 + self.ProjectionMatrix[9] = 0 + self.ProjectionMatrix[10] = 1 + self.ProjectionMatrix[11] = 0 + + self.ProjectionMatrix[12] = 0 + self.ProjectionMatrix[13] = 0 + self.ProjectionMatrix[14] = 0 + self.ProjectionMatrix[15] = 1 +end + +function ENT:Transform(x,y) + local transx = x + local transy = y + + if ((self:ReadCell(65482) != 0) || (self:ReadCell(65481) != 1)) then + local centerx = self:ReadCell(65480) + local centery = self:ReadCell(65479) + + local vd = math.sqrt((x-centerx)*(x-centerx)+(y-centery)*(y-centery)) + 0.0001 + local vx = x / vd + local vy = y / vd + + local atan = math.atan2(vx,vy) + + atan = atan + self:ReadCell(65482) + + transx = math.cos(atan) * vd * self:ReadCell(65481) * self:ReadCell(65475) + centerx + transy = math.sin(atan) * vd * self:ReadCell(65481) * self:ReadCell(65474) + centery + end + + transx = transx+self:ReadCell(65484) + transy = transy+self:ReadCell(65483) + + if (self.CVertexPipe == 0) then + transx = transx + elseif (self.CVertexPipe == 1) then + transx = (transx/self:ReadCell(65515))*512 + elseif (self.CVertexPipe == 2) then + transx = transx*512 + elseif (self.CVertexPipe == 3) then + transx = 256+transx*256 + elseif (self.CVertexPipe == 4) then + transx = 256+transx + end + + if (self.CVertexPipe == 0) then + transy = transy + elseif (self.CVertexPipe == 1) then + transy = (transy/self:ReadCell(65514))*512 + elseif (self.CVertexPipe == 2) then + transy = transy*512 + elseif (self.CVertexPipe == 3) then + transy = 256+transy*256 + elseif (self.CVertexPipe == 4) then + transy = 256+transy + end + + local trans = {} + local rasterq = self:ReadCell(65518) + + if (rasterq > 0) then + trans.x = (transx-256)*(1-(rasterq/256))+256 + trans.y = (transy-256)*(1-(rasterq/256))+256 + else + trans.x = transx + trans.y = transy + end + return trans +end + +function ENT:VertexTransform(coord) + //Make sure coordinate is in full form + if (not coord) then coord = {} end + if (not coord.x) then coord.x = 0 end + if (not coord.y) then coord.y = 0 end + if (not coord.z) then coord.z = 0 end + if (not coord.w) then coord.w = 1 end + if (not coord.u) then coord.u = 0 end + if (not coord.v) then coord.v = 0 end + + local resultcoord = {} + resultcoord.x = coord.x + resultcoord.y = coord.y + resultcoord.z = coord.z + resultcoord.w = coord.w + resultcoord.u = coord.u + resultcoord.v = coord.v + + //Prepare result + resultcoord.transformed = {} //Transformed 3d point + resultcoord.transformed.x = 0 + resultcoord.transformed.y = 0 + resultcoord.transformed.z = 0 + resultcoord.transformed.w = 0 + resultcoord.transformed.u = coord.u + resultcoord.transformed.v = coord.v + + //Add Z offset + coord.z = coord.z + self:ReadCell(65472) + + if (self.VertexPipe == 0) then + resultcoord = self:Transform(coord.x,coord.y) + elseif (self.VertexPipe == 1) then + resultcoord = self:Transform(coord.y,coord.z) + elseif (self.VertexPipe == 2) then + resultcoord = self:Transform(coord.x,coord.z) + elseif (self.VertexPipe == 3) then + local transx = (coord.x+self:ReadCell(65512))/(coord.z+self:ReadCell(65512)) + local transy = (coord.y+self:ReadCell(65512))/(coord.z+self:ReadCell(65512)) + + resultcoord = self:Transform(transx,transy) + elseif (self.VertexPipe == 4) then + local transx = self.ModelMatrix[0*4+0] * coord.x + + self.ModelMatrix[0*4+1] * coord.y + + self.ModelMatrix[0*4+2] * 0 + + self.ModelMatrix[0*4+3] * 1 + + local transy = self.ModelMatrix[1*4+0] * coord.x + + self.ModelMatrix[1*4+1] * coord.y + + self.ModelMatrix[1*4+2] * 0 + + self.ModelMatrix[1*4+3] * 1 + + resultcoord = self:Transform(transx,transy) + elseif (self.VertexPipe == 5) then //3d matrix transformation + //1. Transform into world coordinates + local world_coord = {} + + for i=0,3 do + world_coord[i] = self.ModelMatrix[i*4+0] * coord.x + + self.ModelMatrix[i*4+1] * coord.y + + self.ModelMatrix[i*4+2] * coord.z + + self.ModelMatrix[i*4+3] * coord.w + end + + resultcoord.transformed.x = world_coord[0] + resultcoord.transformed.y = world_coord[1] + resultcoord.transformed.z = world_coord[2] + resultcoord.transformed.w = world_coord[3] + + //2. Transform into screen coordinates + local screen_coord = {} + + for i=0,3 do + screen_coord[i] = self.ProjectionMatrix[i*4+0] * world_coord[0] + + self.ProjectionMatrix[i*4+1] * world_coord[1] + + self.ProjectionMatrix[i*4+2] * world_coord[2] + + self.ProjectionMatrix[i*4+3] * world_coord[3] + end + + if (screen_coord[3] == 0) then screen_coord[3] = 1 end + for i=0,3 do + screen_coord[i] = screen_coord[i] / screen_coord[3] + end + + local transcoord = self:Transform(screen_coord[0],screen_coord[1]) + resultcoord.x = transcoord.x + resultcoord.y = transcoord.y + resultcoord.z = screen_coord[2] + resultcoord.w = screen_coord[3] + end + + return resultcoord +end + +function ENT:TransformColor(color) + local tcolor = color + tcolor.x = color.x * self:ReadCell(65495) * self:ReadCell(65494) + tcolor.y = color.y * self:ReadCell(65495) * self:ReadCell(65493) + tcolor.z = color.z * self:ReadCell(65495) * self:ReadCell(65492) + tcolor.w = color.w + return tcolor +end + +function ENT:ReadStr(addr) + local str = "" + local cnt = 0 + local chr = 255 + while (chr != 0) do + chr = self:ReadCell(addr+cnt) + if ((chr > 0) && (chr < 256)) then + str = str..string.char(chr) + else + if (chr != 0) then + self:Interrupt(23,chr) + return "" + end + end + cnt = cnt + 1 + if (cnt > 8192) then + self:Interrupt(23,0) + return "" + end + end + return str +end + +function ENT:FontWrite(posaddr,text) + local vertexbuf = {} + vertexbuf.x = self:ReadCell(posaddr+0) + vertexbuf.y = self:ReadCell(posaddr+1) + vertexbuf = self:VertexTransform(vertexbuf) + + surface.CreateFont(self.FontNames[self.CurFont], self.CurFontSize, 800, true, false, + "WireGPU_"..self.FontNames[self.CurFont]..self.CurFontSize) + draw.DrawText(text,"WireGPU_"..self.FontNames[self.CurFont]..self.CurFontSize, + vertexbuf.x,vertexbuf.y,Color(self.CurColor.x,self.CurColor.y,self.CurColor.z,255), + self:ReadCell(65473)) +end + +function ENT:DrawLine(point1,point2) + local vertexbuf = {} + for i=1,4 do vertexbuf[i] = {} end + + local center = {} + center.x = (point1.x + point2.x) / 2 + center.y = (point1.y + point2.y) / 2 + + local width = self:ReadCell(65476) + + local len = math.sqrt((point1.x-point2.x)*(point1.x-point2.x)+ + (point1.y-point2.y)*(point1.y-point2.y)) + 0.0001 + local dx = (point2.x-point1.x) / len + local dy = (point2.y-point1.y) / len + local angle = math.atan2(dy,dx)//+3.1415926/2 + local dangle = math.atan2(width,len/2) + + vertexbuf[1].x = center.x - 0.5 * len * math.cos(angle-dangle) + vertexbuf[1].y = center.y - 0.5 * len * math.sin(angle-dangle) + vertexbuf[1].u = 0 + vertexbuf[1].v = 0 + + vertexbuf[2].x = center.x + 0.5 * len * math.cos(angle+dangle) + vertexbuf[2].y = center.y + 0.5 * len * math.sin(angle+dangle) + vertexbuf[2].u = 1 + vertexbuf[2].v = 1 + + vertexbuf[3].x = center.x + 0.5 * len * math.cos(angle-dangle) + vertexbuf[3].y = center.y + 0.5 * len * math.sin(angle-dangle) + vertexbuf[3].u = 0 + vertexbuf[3].v = 1 + + vertexbuf[4].x = center.x - 0.5 * len * math.cos(angle+dangle) + vertexbuf[4].y = center.y - 0.5 * len * math.sin(angle+dangle) + vertexbuf[4].u = 1 + vertexbuf[4].v = 0 + + self:VertexBuffer_Add(vertexbuf) +end + +ENT.VertexSortFunc = function(a,b) + local z1 = (a.vertexbuft[1].z + a.vertexbuft[2].z + a.vertexbuft[3].z) / 3 + local z2 = (b.vertexbuft[1].z + b.vertexbuft[2].z + b.vertexbuft[3].z) / 3 + + return z1 > z2 +end + + +function ENT:VertexBuffer_Flush() + if (self.VertexBufEnabled == true) then + //1. Transform vertexes (FIXME: THEY ARE TRANSFORMED WHEN PASSED) + --[[for i=1,self.VertexBufferCount do + //print(i) + //print(" input xyz: ("..self.VertexBuffer[1].vertexbuf[1].x..",")// + //print(" "..self.VertexBuffer[1].vertexbuf[1].y..",")// + //print(" "..self.VertexBuffer[1].vertexbuf[1].z..")") + + self.VertexBuffer[i].vertexbuft = {} + for j=1,#self.VertexBuffer[i].vertexbuf do + //print(i.." "..j) + //print(" input xyz: ("..self.VertexBuffer[i].vertexbuf[j].x..",") + //print(" "..self.VertexBuffer[i].vertexbuf[j].y..",")// + //print(" "..self.VertexBuffer[i].vertexbuf[j].z..")") + + self.VertexBuffer[i].vertexbuft[j] = self:VertexTransform(self.VertexBuffer[i].vertexbuf[j]) + end + if (#self.VertexBuffer[i].vertexbuf < 3) then + return + end + end]]-- + + //2. Perform ZSort + if self.VertexBufZSort == true then + table.sort(self.VertexBuffer,self.VertexSortFunc) + end + + //3. Render each vertex + for i=1,self.VertexBufferCount do + if (self.VertexBuffer[i].color) then + self.CurColor = self.VertexBuffer[i].color + end + + local Cull = false //We dont want this vertex? + if (self.VertexCulling == true) || (self.VertexLighting == true) then + local v1 = Vector(self.VertexBuffer[i].vertexbuft[1].transformed.x, + self.VertexBuffer[i].vertexbuft[1].transformed.y, + self.VertexBuffer[i].vertexbuft[1].transformed.z) + local v2 = Vector(self.VertexBuffer[i].vertexbuft[2].transformed.x, + self.VertexBuffer[i].vertexbuft[2].transformed.y, + self.VertexBuffer[i].vertexbuft[2].transformed.z) + local v3 = Vector(self.VertexBuffer[i].vertexbuft[3].transformed.x, + self.VertexBuffer[i].vertexbuft[3].transformed.y, + self.VertexBuffer[i].vertexbuft[3].transformed.z) + + local t1 = Vector(self.VertexBuffer[i].vertexbuft[1].x, + self.VertexBuffer[i].vertexbuft[1].y, + self.VertexBuffer[i].vertexbuft[1].z) + local t2 = Vector(self.VertexBuffer[i].vertexbuft[2].x, + self.VertexBuffer[i].vertexbuft[2].y, + self.VertexBuffer[i].vertexbuft[2].z) + + //local vpos = (v1+v2+v3) * 1/3 + local vpos = { + x = (v1.x+v2.x) * 1/3, + y = (v1.y+v2.y) * 1/3, + z = (v1.z+v2.z) * 1/3 + } + + //local tpos = (t1+t2+t3) * 1/3 + local tpos = { + x = (t1.x+t2.x) * 1/3, + y = (t1.y+t2.y) * 1/3, + z = (t1.z+t2.z) * 1/3 + } + + //local normal = (v1 - v2):Cross(v2 - v3) + local normal = { + x = (v1.y - v2.y)*(v2.z - v3.z) - (v1.z - v2.z)*(v2.y - v3.y), + y = (v1.z - v2.z)*(v2.x - v3.x) - (v1.x - v2.x)*(v2.z - v3.z), + z = (v1.x - v2.x)*(v2.y - v3.y) - (v1.y - v2.y)*(v2.x - v3.x) + } + + //normal:Normalize() + local d = (normal.x^2 + normal.y^2 + normal.z^2)^(0.5)+1e-7 + normal.x = normal.x / d + normal.y = normal.y / d + normal.z = normal.z / d + + + if (self.VertexCulling == true) then + local cullval = (self.VertexBuffer[i].vertexbuft[1].x - self.VertexBuffer[i].vertexbuft[2].x)* + (self.VertexBuffer[i].vertexbuft[2].y - self.VertexBuffer[i].vertexbuft[3].y) - + (self.VertexBuffer[i].vertexbuft[1].y - self.VertexBuffer[i].vertexbuft[2].y)* + (self.VertexBuffer[i].vertexbuft[2].x - self.VertexBuffer[i].vertexbuft[3].x) + if (cullval > 0) then Cull = true end + end + + + //if ((self.VertexBuffer[i].vertexbuft[1].z < 0.5) and + // (self.VertexBuffer[i].vertexbuft[2].z < 0.5) and + // (self.VertexBuffer[i].vertexbuft[3].z < 0.5)) then Cull = true end + + if ((self.VertexBuffer[i].vertexbuft[1].transformed.z > -1) or + (self.VertexBuffer[i].vertexbuft[2].transformed.z > -1) or + (self.VertexBuffer[i].vertexbuft[3].transformed.z > -1)) then Cull = true end + + //Perform face shading on this vertex + if ((self.VertexLighting == true) && (Cull == false)) then + local diffuse_color = {} + diffuse_color.x = 0 + diffuse_color.y = 0 + diffuse_color.z = 0 + diffuse_color.w = 255 + + for i=0,7 do //Apply lights + if (self.Lights[i]) then + local plight = self.Lights[i].pos//self:VertexTransform(self.Lights[i].pos) + local vlight = Vector(plight.x,plight.y,plight.z)//.transformed + + //local vvec = (tpos - vlight):Normalize() + local vvec = { + x = tpos.x - vlight.x, + y = tpos.y - vlight.y, + z = tpos.z - vlight.z + } + local dv = (vvec.x^2 + vvec.y^2 + vvec.z^2)^(0.5)+0.0001 + vvec.x = vvec.x / dv + vvec.y = vvec.y / dv + vvec.z = vvec.z / dv + + local brightness = self.Lights[i].col.w + + //local diffuse = normal:Dot(vvec) + local diffuse = -(vvec.x*normal.x+vvec.y*normal.y+vvec.z*normal.z) + + //diffuse < 0 = invisible face (to light) + //could be shaded... + //if (diffuse < 0) then diffuse = 0 end + diffuse = math.abs(diffuse * brightness) + + diffuse_color.x = math.Clamp(diffuse_color.x + self.Lights[i].col.x * diffuse,0,255) + diffuse_color.y = math.Clamp(diffuse_color.y + self.Lights[i].col.y * diffuse,0,255) + diffuse_color.z = math.Clamp(diffuse_color.z + self.Lights[i].col.z * diffuse,0,255) + end + end + + //FIXME: support for object color + //self.CurColor.x = diffuse_color.x * (self.CurColor.x / 255) + //self.CurColor.y = diffuse_color.y * (self.CurColor.y / 255) + //self.CurColor.z = diffuse_color.z * (self.CurColor.z / 255) + self.CurColor = diffuse_color + end + end + if (Cull == false) then + //self.CurColor.x = math.Clamp( self.VertexBuffer[i].vertexbuft[1].transformed.z * 0.1,0,255) + //self.CurColor.y = 0 + //self.CurColor.z = math.Clamp(-self.VertexBuffer[i].vertexbuft[1].transformed.z * 0.1,0,255) + + surface.SetDrawColor(self.CurColor.x,self.CurColor.y,self.CurColor.z,self.CurColor.w) + surface.DrawPoly(self.VertexBuffer[i].vertexbuft) + end + end + + self.VertexBuffer = {} + self.VertexBufferCount = 0 + end +end + +function ENT:VertexBuffer_SetColor(color) + if (self.VertexBufEnabled == true) then + if (self.VertexBufferCount > 0) then + if (not self.VertexBuffer[self.VertexBufferCount]) then self.VertexBuffer[self.VertexBufferCount] = {} end + self.VertexBuffer[self.VertexBufferCount].color = self:TransformColor(color) + else + if (not self.VertexBuffer[self.VertexBufferCount+1]) then self.VertexBuffer[self.VertexBufferCount+1] = {} end + self.VertexBuffer[self.VertexBufferCount+1].color = self:TransformColor(color) + end + else + local tcolor = self:TransformColor(color) + self.CurColor = tcolor + surface.SetDrawColor(tcolor.x,tcolor.y,tcolor.z,tcolor.w) + surface.SetTexture(0) + end +end + +function ENT:VertexBuffer_Add(vertexbuf) + if (self.VertexBufEnabled == true) then + self.VertexBufferCount = self.VertexBufferCount + 1 + if (not self.VertexBuffer[self.VertexBufferCount]) then self.VertexBuffer[self.VertexBufferCount] = {} end + + self.VertexBuffer[self.VertexBufferCount].vertexbuf = {} + self.VertexBuffer[self.VertexBufferCount].vertexbuft = {} + for i=1,#vertexbuf do + self.VertexBuffer[self.VertexBufferCount].vertexbuf[i] = {} + self.VertexBuffer[self.VertexBufferCount].vertexbuf[i].x = vertexbuf[i].x + self.VertexBuffer[self.VertexBufferCount].vertexbuf[i].y = vertexbuf[i].y + self.VertexBuffer[self.VertexBufferCount].vertexbuf[i].z = vertexbuf[i].z + self.VertexBuffer[self.VertexBufferCount].vertexbuf[i].u = vertexbuf[i].u + self.VertexBuffer[self.VertexBufferCount].vertexbuf[i].v = vertexbuf[i].v + + self.VertexBuffer[self.VertexBufferCount].vertexbuft[i] = self:VertexTransform(vertexbuf[i]) + end + + //FIXME: why directly setting does not work?? + //self.VertexBuffer[self.VertexBufferCount].vertexbuf = vertexbuf + else + for i=1,#vertexbuf do + vertexbuf[i] = self:VertexTransform(vertexbuf[i]) + end + + surface.DrawPoly(vertexbuf) + end +end diff --git a/lua/entities/gmod_wire_gpu/init.lua b/lua/entities/gmod_wire_gpu/init.lua new file mode 100644 index 0000000000..2eb05a2db9 --- /dev/null +++ b/lua/entities/gmod_wire_gpu/init.lua @@ -0,0 +1,191 @@ +AddCSLuaFile("gpu_vm.lua") +AddCSLuaFile("gpu_opcodes.lua") +AddCSLuaFile("gpu_serverbus.lua") +AddCSLuaFile("gpu_interrupt.lua") +AddCSLuaFile("gpu_clientbus.lua") + +AddCSLuaFile("entities/gmod_wire_cpu/cpu_opcodes.lua") +AddCSLuaFile("entities/gmod_wire_cpu/cpu_vm.lua") +AddCSLuaFile("entities/gmod_wire_cpu/cpu_bitwise.lua") +AddCSLuaFile("entities/gmod_wire_cpu/cpu_advmath.lua") + +AddCSLuaFile("entities/gmod_wire_cpu/compiler_asm.lua") + +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + +include('shared.lua') +include('entities/gmod_wire_cpu/compiler_asm.lua') //Include ZASM +include('entities/gmod_wire_cpu/cpu_opcodes.lua') //Include ZCPU opcodes +include('entities/gmod_wire_cpu/cpu_advmath.lua') //Include vector and matrix math +include('gpu_serverbus.lua') //Include ZGPU serverside bus +include('gpu_opcodes.lua') //Include ZGPU opcodes for ZASM + +ENT.WireDebugName = "GPU" + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + + self.Inputs = Wire_CreateInputs(self.Entity, { "Clk", "Reset", "MemBus", "IOBus" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Memory" }) + + self.Clk = 1 + self.IOBus = nil + self.MemBus = nil + + self.Debug = false //will cause massive fps drop! + + self.DebugLines = {} + self.DebugData = {} + + self.Memory = {} + self.PrecompileData = {} + self.PrecompileMemory = {} + + self.IsGPU = true + self.UseROM = false + + self.SerialNo = 30000000 + math.floor(math.random()*1000000) + + self:SetOverlayText("Graphical Processing Unit") + + self:InitializeGPUOpcodeNames() + self:InitializeASMOpcodes() + self:InitializeRegisterNames() + self:InitializeBus() +end + +//function ENT:Use(pl) +// //if (!self.Using) then +// // self.Using = true +// // self.Entity:NextThink(CurTime()+0.4) +// +// local rp = RecipientFilter() +// rp:AddPlayer(pl) +// +// Msg("Binding GPU (server)\n") +// +// umsg.Start("wiregpu_onuse", rp) +// umsg.Long(self:EntIndex()) +// umsg.End() +// //end +//end +// +//function ENT:Think() +// self.BaseClass.Think(self) +// +// //self.Using = nil +// //return false +//end +// + +function GPU_PlayerRespawn(pl) + for k,v in pairs(ents.FindByClass("gmod_wire_gpu")) do + v:GPU_ResendData(pl) + end +end +hook.Add("PlayerSpawn", "GPUPlayerRespawn", GPU_PlayerRespawn) + +function ENT:Reset() + self:WriteCell(65534,1) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Clk") then + self.Clk = value + self:WriteCell(65535,self.Clk) + elseif (iname == "Reset") then + if (value >= 1.0) then + self:WriteCell(65534,1) + end + end +end + +function ENT:Think() + if (self.Inputs.IOBus.Src) then + local DataUpdated = false + + for i = 0, 1023 do + if (self.Inputs.IOBus.Src.ReadCell) then + local var = self.Inputs.IOBus.Src:ReadCell(i) + if (var) then + if (self:ReadCell(i+63488) ~= var) then + self:WriteCell(i+63488,var) + DataUpdated = true + end + end + end + end + + if (DataUpdated == true) then + self:FlushCache() + end + end + self.Entity:NextThink(CurTime()+0.05) + return true +end + + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + info.SerialNo = self.SerialNo + info.Memory = {} + for i=0,65535 do + if (self.Memory[i]) then + info.Memory[i] = self.Memory[i] + end + end + + return info +end + +function Resend_GPU_Data(gpuent) + gpuent:InitializeBus() + gpuent:FlushCache() + for i=0,65535 do + if (gpuent.Memory[i]) then + gpuent:WriteCell(i,gpuent.Memory[i]) + end + end + gpuent:FlushCache() + + gpuent:WriteCell(65534,1) //reset + gpuent:WriteCell(65535,gpuent.Clk) +end + +function Reflush_GPU_Data(gpuent,pl) + gpuent.ForcePlayer = pl + gpuent:FlushCache() + for i=0,65535 do + if (gpuent.Memory[i]) then + gpuent:WriteCell(i,gpuent.Memory[i]) + end + end + gpuent:FlushCache() + + gpuent:WriteCell(65534,1) //reset + gpuent:WriteCell(65535,gpuent.Clk) + gpuent.ForcePlayer = nil +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + self.SerialNo = info.SerialNo + self.Memory = {} + + for i=0,65535 do + if (info.Memory[i]) then + self.Memory[i] = info.Memory[i] + end + end + + timer.Create("GPU_Paste_Timer"..math.floor(math.random()*1000000),0.1+math.random()*0.7,1,Resend_GPU_Data,self) +end + +function ENT:GPU_ResendData(pl) + timer.Create("GPU_Resend_Timer"..math.floor(math.random()*1000000),0.1+math.random()*3.0,1,Reflush_GPU_Data,self,pl) +end diff --git a/lua/entities/gmod_wire_gpu/shared.lua b/lua/entities/gmod_wire_gpu/shared.lua new file mode 100644 index 0000000000..2245f1cded --- /dev/null +++ b/lua/entities/gmod_wire_gpu/shared.lua @@ -0,0 +1,43 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire GPU" +ENT.Author = "Black Phoenix" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:InitGraphicTablet() + local model = tostring(self.Entity:GetModel()) + + if WireGPU_Monitors[model] then + self.x1 = WireGPU_Monitors[model].x1 + self.x2 = WireGPU_Monitors[model].x2 + self.y1 = WireGPU_Monitors[model].y1 + self.y2 = WireGPU_Monitors[model].y2 + self.z = WireGPU_Monitors[model].z + else + self.x1 = 0 + self.x2 = 1 + self.y1 = 0 + self.y2 = 1 + self.z = 0 + end + + self.res = 0.05 + self.workingDistance = 64 + + self.x = self.x1 / self.res + self.y = -self.y1 / self.res + self.x0 = self.x + (self.x2 / self.res) - self.x * 2 + self.y0 = self.y + (-self.y2 / self.res) - self.y * 2 + self.w = (self.x2 / self.res) - self.x + self.h = math.abs((self.y2 / self.res) + self.y) + + self.ox = 5 + self.oy = 5 +end diff --git a/lua/entities/gmod_wire_grabber/cl_init.lua b/lua/entities/gmod_wire_grabber/cl_init.lua new file mode 100644 index 0000000000..32f8433103 --- /dev/null +++ b/lua/entities/gmod_wire_grabber/cl_init.lua @@ -0,0 +1,6 @@ +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_grabber/init.lua b/lua/entities/gmod_wire_grabber/init.lua new file mode 100644 index 0000000000..8e29fba2d6 --- /dev/null +++ b/lua/entities/gmod_wire_grabber/init.lua @@ -0,0 +1,279 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Grabber" + + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, { "Grab","Strength" }) + self.Outputs = Wire_CreateOutputs(self.Entity, {"Holding"}) + self.WeldStrength = 0 + self.Weld = nil + self.WeldEntity = nil + self.ExtraProp = nil + self.ExtraPropWeld = nil + self.Gravity = true + self.Entity:GetPhysicsObject():SetMass(10) + + self:SetBeamLength(100) + + if GetConVarNumber('sbox_wire_grabbers_onlyOwnersProps') > 0 then + self.OnlyGrabOwners = true + else + self.OnlyGrabOwners = false + end + + self:ShowOutput() +end + +function ENT:OnRemove() + if self.Weld ~= nil then + self:ResetGrab() + end + Wire_Remove(self.Entity) +end + +function ENT:Setup(Range,Gravity) + self:SetBeamLength(Range) + self.Gravity = Gravity + --Msg("Setup:\n\tRange:"..tostring(Range).."\n\tGravity:"..tostring(Gravity).."\n") +end + +function ENT:ResetGrab() + if self.Weld and self.Weld:IsValid() then + self.Weld:Remove() + --Msg("-Weld1\n") + if self.WeldEntity then + if self.WeldEntity:IsValid() then + if self.Gravity then + self.WeldEntity:GetPhysicsObject():EnableGravity(true) + end + end + end + end + if self.ExtraPropWeld and self.ExtraPropWeld:IsValid() then + self.ExtraPropWeld:Remove() + --Msg("-Weld2\n") + end + + self.Weld = nil + self.WeldEntity = nil + self.ExtraPropWeld = nil + + self.Entity:SetColor(255,255,255,255) + Wire_TriggerOutput(self.Entity,"Holding",0) +end + +function ENT:TriggerInput(iname, value) + if iname == "Grab" then + if value ~= 0 and self.Weld == nil then + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamLength()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + -- Bail if we hit world or a player + if (not trace.Entity:IsValid() and trace.Entity ~= GetWorldEntity()) or trace.Entity:IsPlayer() then return end + -- If there's no physics object then we can't constraint it! + if not util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) then return end + + if self.OnlyGrabOwners and (trace.Entity.Owner ~= self.Owner or not self:CheckOwner(trace.Entity)) then return end + + -- Weld them! + local const = constraint.Weld(self.Entity, trace.Entity, 0, 0, self.WeldStrength) + if const then + const.Type = "" --prevents the duplicator from making this weld + end + + local const2 + --Msg("+Weld1\n") + if self.ExtraProp then + if self.ExtraProp:IsValid() then + const2 = constraint.Weld(self.ExtraProp, trace.Entity, 0, 0, self.WeldStrength) + if const2 then + const2.Type = "" --prevents the duplicator from making this weld + end + --Msg("+Weld2\n") + end + end + if self.Gravity then + trace.Entity:GetPhysicsObject():EnableGravity(false) + end + + self.WeldEntity = trace.Entity + self.Weld = const + self.ExtraPropWeld = const2 + + self.Entity:SetColor(255, 0, 0, 255) + Wire_TriggerOutput(self.Entity, "Holding", 1) + elseif value == 0 then + if self.Weld ~= nil or self.ExtraPropWeld ~= nil then + self:ResetGrab() + end + end + elseif iname == "Strength" then + self.WeldStrength = math.max(value,0) + end +end + +function ENT:ShowOutput() + self:SetOverlayText( "Grabber" ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +--duplicator support (TAD2020) +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if self.WeldEntity and self.WeldEntity:IsValid() then + info.WeldEntity = self.WeldEntity:EntIndex() + end + + if self.ExtraProp and self.ExtraProp:IsValid() then + info.ExtraProp = self.ExtraProp:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if info.WeldEntity then + self.WeldEntity = GetEntByID(info.WeldEntity) + if not self.WeldEntity then + self.WeldEntity = ents.GetByIndex(info.WeldEntity) + end + end + + if info.ExtraProp then + self.ExtraProp = GetEntByID(info.ExtraProp) + if not self.ExtraProp then + self.ExtraProp = ents.GetByIndex(info.ExtraProp) + end + end + + if self.WeldEntity and self.Inputs.Grab.Value ~= 0 then + + if not self.Weld then + self.Weld = constraint.Weld(self.Entity, trace.Entity, 0, 0, self.WeldStrength) + self.Weld.Type = "" --prevents the duplicator from making this weld + end + + if self.ExtraProp then + self.ExtraPropWeld = constraint.Weld(self.ExtraProp, self.WeldEntity, 0, 0, self.WeldStrength) + self.ExtraPropWeld.Type = "" --prevents the duplicator from making this weld + end + + if self.Gravity then + self.WeldEntity:GetPhysicsObject():EnableGravity(false) + end + + self.Entity:SetColor(255, 0, 0, 255) + Wire_TriggerOutput(self.Entity, "Holding", 1) + + end +end + +-- Free Fall's Owner Check Code +function ENT:CheckOwner(ent) + ply = self.pl + + hasCPPI = (type( CPPI ) == "table") + hasEPS = type( eps ) == "table" + hasPropSecure = type( PropSecure ) == "table" + hasProtector = type( Protector ) == "table" + + if not hasCPPI and not hasPropProtection and not hasSPropProtection and not hasEPS and not hasPropSecure and not hasProtector then return true end + + local t = hook.GetTable() + + local fn = t.CanTool.PropProtection + hasPropProtection = type( fn ) == "function" + if hasPropProtection then + -- We're going to get the function we need now. It's local so this is a bit dirty + local gi = debug.getinfo( fn ) + for i=1, gi.nups do + local k, v = debug.getupvalue( fn, i ) + if k == "Appartient" then + propProtectionFn = v + end + end + end + + local fn = t.CanTool[ "SPropProtection.EntityRemoved" ] + hasSPropProtection = type( fn ) == "function" + if hasSPropProtection then + local gi = debug.getinfo( fn ) + for i=1, gi.nups do + local k, v = debug.getupvalue( fn, i ) + if k == "SPropProtection" then + SPropProtectionFn = v.PlayerCanTouch + end + end + end + + local owns + if hasCPPI then + owns = ent:CPPICanPhysgun( ply ) + elseif hasPropProtection then -- Chaussette's Prop Protection (preferred over PropSecure) + owns = propProtectionFn( ply, ent ) + elseif hasSPropProtection then -- Simple Prop Protection by Spacetech + if ent:GetNetworkedString( "Owner" ) ~= "" then -- So it doesn't give an unowned prop + owns = SPropProtectionFn( ply, ent ) + else + owns = false + end + elseif hasEPS then -- EPS + owns = eps.CanPlayerTouch( ply, ent ) + elseif hasPropSecure then -- PropSecure + owns = PropSecure.IsPlayers( ply, ent ) + elseif hasProtector then -- Protector + owns = Protector.Owner( ent ) == ply:UniqueID() + end + + return owns +end + + +function MakeWireGrabber( pl, Pos, Ang, model, Range, Gravity ) + if not pl:CheckLimit( "wire_grabbers" ) then return false end + + local wire_grabber = ents.Create( "gmod_wire_grabber" ) + if not wire_grabber:IsValid() then return false end + + wire_grabber:SetAngles( Ang ) + wire_grabber:SetPos( Pos ) + wire_grabber:SetModel( model ) + wire_grabber:Spawn() + wire_grabber:Setup(Range, Gravity) + + wire_grabber:SetPlayer( pl ) + + local ttable = { + Range = Range, + Gravity = Gravity, + pl = pl + } + table.Merge(wire_grabber:GetTable(), ttable ) + + pl:AddCount( "wire_grabbers", wire_grabber ) + + return wire_grabber +end + +duplicator.RegisterEntityClass("gmod_wire_grabber", MakeWireGrabber, "Pos", "Ang", "Model", "Range", "Gravity") + diff --git a/lua/entities/gmod_wire_grabber/shared.lua b/lua/entities/gmod_wire_grabber/shared.lua new file mode 100644 index 0000000000..3285dd0c77 --- /dev/null +++ b/lua/entities/gmod_wire_grabber/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Grabber" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamLength(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_graphics_tablet/cl_init.lua b/lua/entities/gmod_wire_graphics_tablet/cl_init.lua new file mode 100644 index 0000000000..84de0c7c2b --- /dev/null +++ b/lua/entities/gmod_wire_graphics_tablet/cl_init.lua @@ -0,0 +1,59 @@ +--Wire graphics tablet by greenarrow + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH +ENT.paramSetup = false + +function ENT:Initialize() + self:SetupParams() + self.allowDraw = true +end + +function ENT:Draw() + if !self.allowDraw then return true end + + if !self.paramsSetup then + self:SetupParams() + end + + self.Entity:DrawModel() + --begin adapted nighteagle code (amazing work with vectors!!!): --Nighteagles has put a lot of work into refining the use of cam3d2d and traces to create cursor screen systems. + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + local pos = self.Entity:GetPos() + (self.Entity:GetForward() * self.z) + + cam.Start3D2D(pos, ang, self.res) + local trace = {} + trace.start = LocalPlayer():GetShootPos() + trace.endpos = (LocalPlayer():GetAimVector() * self.workingDistance) + trace.start + trace.filter = LocalPlayer() + local trace = util.TraceLine(trace) + + if (trace.Entity == self.Entity) then + local pos = self.Entity:WorldToLocal(trace.HitPos) + local cx = (self.x1 - pos.y) / (self.x1 - self.x2) + local cy = (self.y1 - pos.z) / (self.y1 - self.y2) + + --surface.SetDrawColor(0,0,255,255) + --surface.DrawRect(self.x ,self.y, self.w, self.h) + + if (cx >= 0 and cy >= 0 and cx <= 1 and cy <= 1) then + surface.SetDrawColor (255, 255, 255, 255) + --surface.SetTexture (surface.GetTextureID ("gui/arrow")) + --surface.DrawTexturedRectRotated (self.x + (self.w * cx) + self.ox, self.y + (self.h * cy) + self.oy, 16, 16, 45) + local curSize = 16 + local curWidth = 2 + local midX = self.x + (self.w * cx) + local midY = self.y + (self.h * cy) + surface.DrawRect (midX - curSize, midY - curWidth, curSize * 2, curWidth * 2) + surface.DrawRect (midX - curWidth, midY - curSize, curWidth * 2, curSize * 2) + end + end + cam.End3D2D() + --end adapted nighteagle code + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_graphics_tablet/init.lua b/lua/entities/gmod_wire_graphics_tablet/init.lua new file mode 100644 index 0000000000..88f1abf6b0 --- /dev/null +++ b/lua/entities/gmod_wire_graphics_tablet/init.lua @@ -0,0 +1,96 @@ +--Wire graphics tablet by greenarrow +--http://forums.facepunchstudios.com/greenarrow + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Graphics Tablet" +ENT.outputMode = false + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Outputs = Wire_CreateOutputs(self.Entity, { "X", "Y", "Use", "OnScreen" }) + + Wire_TriggerOutput(self.Entity, "X", 0) + Wire_TriggerOutput(self.Entity, "Y", 0) + Wire_TriggerOutput(self.Entity, "Use", 0) + Wire_TriggerOutput(self.Entity, "OnScreen", 0) + + self.lastOnscreen = 0 + self.lastX = 0 + self.lastY = 0 + self.lastClick = 0 + self:SetupParams() +end + +function ENT:Setup(gmode) + self.outputMode = gmode +end + +function ENT:Use() +end + +function ENT:Think() + self.BaseClass.Think(self) + local onScreen = 0 + local clickActive = 0 + + for i, player in pairs(player.GetAll()) do + local trace = {} + trace.start = player:GetShootPos() + trace.endpos = (player:GetAimVector() * self.workingDistance) + trace.start + trace.filter = player + local trace = util.TraceLine(trace) + + if (trace.Entity == self.Entity) then + if (player:KeyDown (IN_ATTACK) or player:KeyDown (IN_USE)) then + clickActive = 1 + end + local pos = self.Entity:WorldToLocal(trace.HitPos) + local xval = (self.x1 - pos.y) / (self.x1 - self.x2) + local yval = (self.y1 - pos.z) / (self.y1 - self.y2) + + if (xval >= 0 and yval >= 0 and xval <= 1 and yval <= 1) then + onScreen = 1 + if (xval ~= self.lastX or yval ~= self.lastY) then + if (self.outputMode) then + xval = (xval * 2) - 1 + yval = (-yval * 2) + 1 + end + Wire_TriggerOutput(self.Entity, "X", xval) + Wire_TriggerOutput(self.Entity, "Y", yval) + self:ShowOutput(xval, yval, self.lastClick, self.lastOnScreen) + self.lastX = xval + self.lastY = yval + end + end + end + end + + if (onScreen ~= self.lastOnScreen) then + Wire_TriggerOutput(self.Entity, "OnScreen", onScreen) + self:ShowOutput(self.lastX, self.lastY, self.lastClick, onScreen) + self.lastOnScreen = onScreen + end + + if (clickActive ~= self.lastClick) then + Wire_TriggerOutput(self.Entity, "Use", clickActive) + self:ShowOutput(self.lastX, self.lastY, clickActive, self.lastOnScreen) + self.lastClick = clickActive + end + + self.Entity:NextThink(CurTime()+0.08) + return true +end + +function ENT:ShowOutput(xval, yval, activeval, osval) + self:SetOverlayText(string.format("X = %f, Y = %f, Use = %d, On Screen = %d\n", xval, yval, activeval, osval)) +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) + Wire_AdjustOutputs(self.Entity, { "X", "Y", "Use", "OnScreen" }) +end diff --git a/lua/entities/gmod_wire_graphics_tablet/shared.lua b/lua/entities/gmod_wire_graphics_tablet/shared.lua new file mode 100644 index 0000000000..609c3361ac --- /dev/null +++ b/lua/entities/gmod_wire_graphics_tablet/shared.lua @@ -0,0 +1,100 @@ +--Wire graphics tablet by greenarrow +--http://gmodreviews.googlepages.com/ +--http://forums.facepunchstudios.com/greenarrow + +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Graphics Tablet" +ENT.Author = "greenarrow" +ENT.Contact = "http://forums.facepunchstudios.com/greenarrow" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:OnRemove() +end + +ENT.paramsSetup = false +ENT.drawParameters = { + ["models/props/cs_office/TV_plasma.mdl"] = { + x1 = -28.5, + x2 = 28.5, + y1 = 36, + y2 = 2, + z = 6.1 + }, + ["models/props/cs_office/computer_monitor.mdl"] = { + x1 = -10.5, + x2 = 10.5, + y1 = 24.7, + y2 = 8.6, + z = 3.33 + }, + ["models/props_lab/monitor01b.mdl"] = { + x1 = -5.535, + x2 = 3.5, + y1 = 5.091, + y2 = -4.1, + z = 6.4 + }, + ["models/kobilica/wiremonitorsmall.mdl"] = { + x1 = -4.4, + x2 = 4.5, + y1 = 9.5, + y2 = 0.6, + z = 0.2 + }, + ["models/kobilica/wiremonitorbig.mdl"] = { + x1 = -11.5, + x2 = 11.6, + y1 = 24.5, + y2 = 1.6, + z = 0.2 + }, + ["default"] = { + x1 = -10.5, + x2 = 10.5, + y1 = 24.7, + y2 = 8.6, + z = 6 + }, +} + +function ENT:SetupParams() + local model = tostring(self.Entity:GetModel()) + + if self.drawParameters[model] then + self.x1 = self.drawParameters[model].x1 + self.x2 = self.drawParameters[model].x2 + self.y1 = self.drawParameters[model].y1 + self.y2 = self.drawParameters[model].y2 + self.z = self.drawParameters[model].z + self.paramsSetup = true + else + --Msg ("graphics tablet error - model not found\n") + self.x1 = self.drawParameters["default"].x1 + self.x2 = self.drawParameters["default"].x2 + self.y1 = self.drawParameters["default"].y1 + self.y2 = self.drawParameters["default"].y2 + self.z = self.drawParameters["default"].z + end + + --begin adapted nighteagle code + self.res = 0.05 + self.workingDistance = 64 + + self.x = self.x1 / self.res + self.y = -self.y1 / self.res + self.x0 = self.x + (self.x2 / self.res) - self.x * 2 + self.y0 = self.y + (-self.y2 / self.res) - self.y * 2 + self.w = (self.x2 / self.res) - self.x + self.h = math.abs((self.y2 / self.res) + self.y) + + self.ox = 5 + self.oy = 5 + --end adapted nighteagle code +end diff --git a/lua/entities/gmod_wire_gyroscope/cl_init.lua b/lua/entities/gmod_wire_gyroscope/cl_init.lua new file mode 100644 index 0000000000..2410a49f0b --- /dev/null +++ b/lua/entities/gmod_wire_gyroscope/cl_init.lua @@ -0,0 +1,28 @@ + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +include('shared.lua') + +--handle overlay text client side instead (TAD2020) +function ENT:Think() + self.BaseClass.Think(self) + + local ang = self.Entity:GetAngles() + if (ang.p < 0 && !self:GetOut180()) then ang.p = ang.p + 360 end + if (ang.y < 0 && !self:GetOut180()) then ang.y = ang.y + 360 end + if (ang.r < 0 && !self:GetOut180()) then ang.r = ang.r + 360 + elseif (ang.r > 180 && self:GetOut180()) then ang.r = ang.r - 360 end + self:ShowOutput(ang.p, ang.y, ang.r) + + self.Entity:NextThink(CurTime()+0.04) + return true +end + +function ENT:ShowOutput(p, y, r) + --self.Entity:SetNetworkedString( "GModOverlayText", "Angles = " .. math.Round(p*1000)/1000 .. "," .. math.Round(y*1000)/1000 .. "," .. math.Round(r*1000)/1000 ) + self.Entity:SetNetworkedBeamString( "GModOverlayText", "Angles = " .. math.Round(p*1000)/1000 .. "," .. math.Round(y*1000)/1000 .. "," .. math.Round(r*1000)/1000 ) + //self.BaseClass.BaseClass.SetOverlayText(self, "Angles = " .. math.Round(p*1000)/1000 .. "," .. math.Round(y*1000)/1000 .. "," .. math.Round(r*1000)/1000 ) + --self:SetOverlayText(self, "Angles = " .. math.Round(p*1000)/1000 .. "," .. math.Round(y*1000)/1000 .. "," .. math.Round(r*1000)/1000 ) + --self.Txt = "Angles = " .. math.Round(p*1000)/1000 .. "," .. math.Round(y*1000)/1000 .. "," .. math.Round(r*1000)/1000 +end diff --git a/lua/entities/gmod_wire_gyroscope/init.lua b/lua/entities/gmod_wire_gyroscope/init.lua new file mode 100644 index 0000000000..0fe0dca6da --- /dev/null +++ b/lua/entities/gmod_wire_gyroscope/init.lua @@ -0,0 +1,49 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Gyro" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Pitch", "Yaw", "Roll" }) +end + +function ENT:Setup( out180 ) + + self.Out180 = out180 --wft is this used for + self:SetOut180(out180) + self.Value = 0 + self.PrevOutput = nil + + --self:ShowOutput(0, 0, 0) + Wire_TriggerOutput(self.Entity, "Pitch", 0) + Wire_TriggerOutput(self.Entity, "Yaw", 0) + Wire_TriggerOutput(self.Entity, "Roll", 0) +end + +function ENT:Think() + self.BaseClass.Think(self) + + local ang = self.Entity:GetAngles() + if (ang.p < 0 && !self.Out180) then ang.p = ang.p + 360 end + if (ang.y < 0 && !self.Out180) then ang.y = ang.y + 360 end + if (ang.r < 0 && !self.Out180) then ang.r = ang.r + 360 + elseif (ang.r > 180 && self.Out180) then ang.r = ang.r - 360 end + Wire_TriggerOutput(self.Entity, "Pitch", ang.p) + Wire_TriggerOutput(self.Entity, "Yaw", ang.y) + Wire_TriggerOutput(self.Entity, "Roll", ang.r) + --now handled client side (TAD2020) + --self:ShowOutput(ang.p, ang.y, ang.r) + + self.Entity:NextThink(CurTime()+0.04) + return true +end + +function ENT:ShowOutput(p, y, r) + self:SetOverlayText( "Angles = " .. math.Round(p*1000)/1000 .. "," .. math.Round(y*1000)/1000 .. "," .. math.Round(r*1000)/1000 ) +end diff --git a/lua/entities/gmod_wire_gyroscope/shared.lua b/lua/entities/gmod_wire_gyroscope/shared.lua new file mode 100644 index 0000000000..de58eb21be --- /dev/null +++ b/lua/entities/gmod_wire_gyroscope/shared.lua @@ -0,0 +1,15 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Speedometer (Wire)" +ENT.Author = "Erkle" +ENT.Contact = "ErkleMad@hotmail.com" + + +function ENT:GetOut180() + return self.Entity:GetNetworkedBool( 0 ) +end + +function ENT:SetOut180( Out180 ) + return self.Entity:SetNetworkedBool( 0, Out180 ) +end diff --git a/lua/entities/gmod_wire_hdd/cl_init.lua b/lua/entities/gmod_wire_hdd/cl_init.lua new file mode 100644 index 0000000000..9dca17e409 --- /dev/null +++ b/lua/entities/gmod_wire_hdd/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_hdd/init.lua b/lua/entities/gmod_wire_hdd/init.lua new file mode 100644 index 0000000000..847820538b --- /dev/null +++ b/lua/entities/gmod_wire_hdd/init.lua @@ -0,0 +1,306 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + +include('shared.lua') + +ENT.WireDebugName = "WireHDD" +ENT.OverlayDelay = 0 + +function ENT:OnRemove() + self:SaveCachedBlock() +end + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + self.Entity:SetUseType(SIMPLE_USE) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Data" }) + self.Inputs = Wire_CreateInputs(self.Entity, { "Clk", "AddrRead", "AddrWrite", "Data" }) + + self.Clk = 0 + self.AWrite = 0 + self.ARead = 0 + self.Data = 0 + self.Out = 0 + + self.BlockSize = 16 + + //Hard drive id/folder id: + self.DriveID = 0 + self.PrevDriveID = -1 + //Hard drive capicacity (loaded from hdd) + self.DriveCap = 0 + + //Cache for read/written values + self.ReadCache = {} + //Shows whether block is cached for reading + self.BlockCached = {} + + //Current sector cache + self.Cache = {} + for i = 0,self.BlockSize-1 do + self.Cache[i] = 0 + end + //Block that is cached + self.CachedBlock = -1 + self.CacheWrites = 0 + //Owners STEAMID + self.owner_steamid = "SINGLEPLAYER" + + self:SetOverlayText("Flash memory") +end + +function ENT:GetStructName(name) + return "WireFlash\\"..(self.owner_steamid or "UNKNOWN").."\\HDD"..self.DriveID.."\\"..name..".txt" +end + +function ENT:GetCap() + //If hard drive exists + if (file.Exists(self:GetStructName("drive"))) then + //Read size cap + local sizecap = file.Read(self:GetStructName("drive")) + //If it is a number + if (tonumber(sizecap)) then + self.DriveCap = tonumber(sizecap) + else + file.Write(self:GetStructName("drive"), self.DriveCap) + end + else + file.Write(self:GetStructName("drive"), self.DriveCap) + end + + //Can't have cap bigger than 256 in MP + if ((!SinglePlayer()) && (self.DriveCap > 256)) then + self.DriveCap = 256 + end +end + +function ENT:UpdateCap() + //Can't have cap bigger than 256 in MP + if ((!SinglePlayer()) && (self.DriveCap > 256)) then + self.DriveCap = 256 + end + file.Write(self:GetStructName("drive"), self.DriveCap) + + self:GetCap() +end + +function ENT:GetFloatTable(Text) + local text = Text + local tbl = {} + local ptr = 0 + while (string.len(text) > 0) do + local value = string.sub(text,1,24) + text = string.sub(text,24,string.len(text)) + tbl[ptr] = tonumber(value) + ptr = ptr + 1 + end + return tbl +end + +function ENT:MakeFloatTable(Table) + local text = "" + for i=0,table.Count(Table)-1 do + //Clamp size to 24 chars + local floatstr = string.sub(tostring(Table[i]),1,24) + //Make a string, and append missing spaces + floatstr = floatstr .. string.rep(" ",24-string.len(floatstr)) + + text = text..floatstr + end + + return text +end + +function ENT:ReadCell(Address) + //DriveID should be > 0, and less than 4 in MP + if ((self.DriveID < 0) || (!SinglePlayer() && (self.DriveID >= 4))) then + return nil + end + + local player = self.pl + if (player:IsValid()) then + local steamid = player:SteamID() + steamid = string.gsub(steamid, ":", "_") + if (steamid ~= "UNKNOWN") then + self.owner_steamid = steamid + else + self.owner_steamid = "SINGLEPLAYER" + end + + //If drive has changed, change cap + if (self.DriveID ~= self.PrevDriveID) then + self:GetCap() + self.PrevDriveID = self.DriveID + end + + //Check if address is valid + if ((Address < self.DriveCap * 1024) && (Address >= 0)) then + //1. Check if this address is cached for read + if (self.ReadCache[Address]) then + return self.ReadCache[Address] + end + + //2. Read sector + local block = math.floor(Address / self.BlockSize) + local blockaddress = math.floor(Address) % self.BlockSize + + //If sector isn't created yet, return 0 + if (!file.Exists(self:GetStructName(block))) then + for i=0,self.BlockSize-1 do + self.ReadCache[block*self.BlockSize+i] = 0 + end + self.BlockCached[block] = true + return 0 + end + + //Read the block + local blockdata = self:GetFloatTable(file.Read(self:GetStructName(block))) + for i=0,self.BlockSize-1 do + if (blockdata[i]) then + self.ReadCache[block*self.BlockSize+i] = blockdata[i] + else + self.ReadCache[block*self.BlockSize+i] = 0 + end + end + self.BlockCached[block] = true + + return self.ReadCache[block*self.BlockSize+blockaddress] + else + return nil + end + else + return nil + end +end + +function ENT:SaveCachedBlock() + if (self.CachedBlock != -1) then + file.Write(self:GetStructName(self.CachedBlock),self:MakeFloatTable(self.Cache)) + end + self.CacheWrites = 0 +end + +function ENT:WriteCell(Address, value) + //DriveID should be > 0, and less than 4 in MP + if ((self.DriveID < 0) || (!SinglePlayer() && (self.DriveID >= 4))) then + return false + end + + local player = self.pl + if (player:IsValid()) then + local steamid = player:SteamID() + steamid = string.gsub(steamid, ":", "_") + if (steamid ~= "UNKNOWN") then + self.owner_steamid = steamid + else + self.owner_steamid = "SINGLEPLAYER" + end + + //If drive has changed, change cap + if (self.DriveID ~= self.PrevDriveID) then + self:GetCap() + self.PrevDriveID = self.DriveID + end + + //Check if address is valid + if ((Address < self.DriveCap * 1024) && (Address >= 0)) then + local block = math.floor(Address / self.BlockSize) + local blockaddress = math.floor(Address) % self.BlockSize + + //1. Check if this sector isn't the one which is cached + // If no, make it current block + if (self.CachedBlock != block) then + //Save previous one + self:SaveCachedBlock() + + //Check if this block is cached for read + if (!self.BlockCached[block]) then + //If sector isn't created yet, return 0 + if (!file.Exists(self:GetStructName(block))) then + for i=0,self.BlockSize-1 do + self.ReadCache[block*self.BlockSize+i] = 0 + end + else + //Read the block + local blockdata = self:GetFloatTable(file.Read(self:GetStructName(block))) + for i=0,self.BlockSize-1 do + if (blockdata[i]) then + self.ReadCache[block*self.BlockSize+i] = blockdata[i] + else + self.ReadCache[block*self.BlockSize+i] = 0 + end + end + end + self.BlockCached[block] = true + end + + //Load it from readcache + for i=0,self.BlockSize-1 do + self.Cache[i] = self.ReadCache[block*self.BlockSize+i] + end + self.CachedBlock = block + end + + //Write to the block + self.Cache[blockaddress] = value + self.ReadCache[Address] = value + self.CacheWrites = self.CacheWrites + 1 + + //If under 256 writes to same sector, dont dump sector to disk + if (self.CacheWrites > 256) then + self:SaveCachedBlock() + end + return true + else + return false + end + else + return false + end +end + +function ENT:TriggerInput(iname, value) + if (iname == "Clk") then + self.Clk = value + if (self.Clk >= 1) then + self:WriteCell(self.AWrite, self.Data) + if (self.ARead == self.AWrite) then + local val = self:ReadCell(self.ARead) + if (val) then + Wire_TriggerOutput(self.Entity, "Data", val) + self.Out = val + end + end + end + elseif (iname == "AddrRead") then + self.ARead = value + local val = self:ReadCell(value) + if (val) then + Wire_TriggerOutput(self.Entity, "Data", val) + self.Out = val + end + elseif (iname == "AddrWrite") then + self.AWrite = value + if (self.Clk >= 1) then + self:WriteCell(self.AWrite, self.Data) + end + elseif (iname == "Data") then + self.Data = value + if (self.Clk >= 1) then + self:WriteCell(self.AWrite, self.Data) + if (self.ARead == self.AWrite) then + local val = self:ReadCell(self.ARead) + if (val) then + Wire_TriggerOutput(self.Entity, "Data", val) + self.Out = val + end + end + end + end + + self:SetOverlayText("Flash memory - "..self.DriveCap.."kb".."\nWriteAddr:"..self.AWrite.." Data:"..self.Data.." Clock:"..self.Clk.. + "\nReadAddr:"..self.ARead.." = ".. self.Out) +end diff --git a/lua/entities/gmod_wire_hdd/shared.lua b/lua/entities/gmod_wire_hdd/shared.lua new file mode 100644 index 0000000000..0aac5af077 --- /dev/null +++ b/lua/entities/gmod_wire_hdd/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Flash EEPROM" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_holoemitter/cl_init.lua b/lua/entities/gmod_wire_holoemitter/cl_init.lua new file mode 100644 index 0000000000..8062c3b563 --- /dev/null +++ b/lua/entities/gmod_wire_holoemitter/cl_init.lua @@ -0,0 +1,223 @@ +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +CreateClientConVar("cl_wire_holoemitter_minfaderate",10,true,false) + +-- mats +local matbeam = Material( "tripmine_laser" ) +local matpoint = Material( "sprites/gmdm_pickups/light" ) + +local render_SetMaterial = render.SetMaterial +local render_DrawBeam = render.DrawBeam +local render_DrawSprite = render.DrawSprite + + +function ENT:Initialize() + -- self.PointList[i] = { pos, alpha, faderate } + self.PointList = {} + self.LastClear = self.Entity:GetNetworkedInt("Clear") + + -- active point + self.ActivePoint = Vector( 0, 0, 0 ) + + -- make the hologram visible even when not looking at it + self.Entity:SetRenderBounds( Vector(-8192,-8192,-8192), Vector(8192,8192,8192) ) + + self.nextremove = 0 -- the next time the point list is cleaned up (ENT:Think) + self.lastfade = 0 -- the last time the alphas were calculated (ENT:Think, ENT:Draw) +end + + +function ENT:Think() + local emitter = self.Entity + + -- read point. + local point = Vector( + emitter:GetNetworkedFloat( "X" ), + emitter:GetNetworkedFloat( "Y" ), + emitter:GetNetworkedFloat( "Z" )+64 -- who came up with this offset? + ) + + lastclear = emitter:GetNetworkedInt("Clear") + if lastclear ~= self.LastClear then + self.PointList = {} + self.LastClear = lastclear + end + + -- did the point differ from active point? + if point ~= self.ActivePoint && emitter:GetNetworkedBool( "Active" ) then + -- fetch color. + local _, _, _, a = emitter:GetColor() + + -- determine fade rate + local minfaderate = 0.1 + if not SinglePlayer() then + -- Due to a request, in Multiplayer, the people can control this with a client-side cvar (aVoN) + minfaderate = GetConVarNumber("cl_wire_holoemitter_minfaderate") or 10 + end + + local tempfaderate = math.Clamp( emitter:GetNetworkedFloat( "FadeRate" ),minfaderate, 255 ) + + -- store this point inside the point list + table.insert( self.PointList, { self.ActivePoint, a, tempfaderate } ) + + -- store new active point + self.ActivePoint = point + + end + + -- This is repeated here, so the client doesn't lag and die when not looking at a holo. + if self.nextremove <= CurTime() then + local t = CurTime() + local frametime = t-self.lastfade + self.lastfade = t + self.nextremove = t+0.5 + for i = #self.PointList,1,-1 do + -- easy access + local point = self.PointList[i] + -- alpha -= faderate*frametime + point[2] = point[2] - point[3] * frametime + + -- if the point is no longer visible, remove it + if( point[2] <= 0 ) then -- [2] = alpha + table.remove( self.PointList, i ) + end + end + end +end + +function ENT:Draw() + local emitter = self.Entity + + -- render model + emitter:DrawModel() + + -- are we rendering? + if not emitter:GetNetworkedBool( "Active" ) then return end + + -- read HoloGrid. + local hologrid = emitter:GetNetworkedEntity( "grid" ) + if not hologrid or not hologrid:IsValid() then return end + + local reference_entity = hologrid:GetNetworkedEntity( "reference" ) + local LocalToWorld + if ValidEntity(reference_entity) then + LocalToWorld = reference_entity.LocalToWorld + else + -- LocalToWorld(reference_entity, pos) <=> Vector.__add(Vector(0,0,-64), pos) <=> pos - Vector(0,0,64) + reference_entity = Vector(0,0,-64) + LocalToWorld = reference_entity.__add + end + + -- draw beam? + local drawbeam = emitter:GetNetworkedBool( "ShowBeam" ) + local groundbeam = emitter:GetNetworkedBool( "GroundBeam" ) + + -- read point size + local size = emitter:GetNetworkedFloat( "PointSize" ) + local beamsize = 2 + if size > 8 then beamsize = size * 0.25 end + local pointbeamsize = beamsize * 2 + + -- read color + local r,g,b,a = emitter:GetColor() + local color = Color(r,g,b,a) + + -- calculate pixel point. + local emitterpos = emitter:GetPos() + local pixelpos + + -------------------------------------------------------------------------------- + -- draw ActivePoint + pixelpos = LocalToWorld(reference_entity, self.ActivePoint) + + -- draw emitter-ActivePoint beam + if groundbeam then + render_SetMaterial( matbeam ) + render_DrawBeam( + emitterpos, + pixelpos, + beamsize, + 0, 1, + color + ) + end + + local drawpoints = size > 0 + if drawpoints then + -- draw Active Point sprite + render_SetMaterial( matpoint ) + render_DrawSprite( + pixelpos, + size, size, + color + ) + end + + -------------------------------------------------------------------------------- + -- draw fading points. + local lastpos = pixelpos + + local t = CurTime() + local frametime = t-self.lastfade + self.lastfade = t + + local PointList = self.PointList -- easy access + for i = #PointList, 1, -1 do + -- easy access + local point = PointList[i] + + + -- fade away + -- alpha -= faderate*frametime + local a = point[2] - point[3] * frametime + point[2] = a + + -- if the point is no longer visible, remove it + if a <= 0 then + table.remove( PointList, i ) + else + -- calculate pixel point. + pixelpos = LocalToWorld(reference_entity, point[1]) + + -- calculate color. + color = Color( r, g, b, alpha ) -- [2] = alpha + + -- draw emitter-point beam + if groundbeam then + render_SetMaterial( matbeam ) + render_DrawBeam( + emitterpos, + pixelpos, + beamsize, + 0, 1, + color + ) + end + + -- draw point-point beam + if drawbeam then + render_SetMaterial( matbeam ) + render_DrawBeam( + lastpos, + pixelpos, + pointbeamsize, + 0, 1, + color + ) + lastpos = pixelpos + end + + if drawpoints then + -- draw active point - sprite + render_SetMaterial( matpoint ) + render_DrawSprite( + pixelpos, + size, size, + color + ) + end -- if drawpoints + end -- if alpha > 0 + end -- for PointList +end -- ENT:Draw diff --git a/lua/entities/gmod_wire_holoemitter/init.lua b/lua/entities/gmod_wire_holoemitter/init.lua new file mode 100644 index 0000000000..e37e164d6e --- /dev/null +++ b/lua/entities/gmod_wire_holoemitter/init.lua @@ -0,0 +1,143 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include( "shared.lua" ) + +-- wire debug and overlay crap. +ENT.WireDebugName = "Holographic Emitter" +ENT.OverlayDelay = 0 +ENT.LastClear = 0 + +-- init. +function ENT:Initialize( ) + -- set model + util.PrecacheModel( "models/jaanus/wiretool/wiretool_range.mdl" ) + self.Entity:SetModel( "models/jaanus/wiretool/wiretool_range.mdl" ) + self:DrawShadow( false ) + + -- setup physics + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + -- vars + self.Entity:SetNetworkedFloat( "X", 0 ) + self.Entity:SetNetworkedFloat( "Y", 0 ) + self.Entity:SetNetworkedFloat( "Z", 0 ) + self.Entity:SetNetworkedFloat( "FadeRate", 50 ) + self.Entity:SetNetworkedFloat( "PointSize", 0.2 ) + self.Entity:SetNetworkedBool( "ShowBeam", true ) + self.Entity:SetNetworkedBool( "GroundBeam", true ) + self.Entity:SetNetworkedBool( "Active", false ) + self.Entity:SetNetworkedEntity( "reference", self.Entity ) + self.Entity:SetNetworkedInt( "LastClear", 0 ) + self:LinkToGrid(nil) + + -- create inputs. + self.Inputs = WireLib.CreateSpecialInputs( self.Entity, { "X", "Y", "Z", "Vector", "Active", "FadeRate", "Clear" }, { "NORMAL", "NORMAL", "NORMAL", "VECTOR", "NORMAL", "NORMAL", "NORMAL" } ) +end + +-- link to grid +function ENT:LinkToGrid( ent ) + if ent == nil then ent = self.Entity end + self.Entity:SetNetworkedEntity( "grid", ent ) +end + +-- trigger input +function ENT:TriggerInput( inputname, value, iter ) + -- store values. + if(not value) then return end + if (inputname == "Clear" and value ~= 0) then + self.LastClear = self.LastClear + 1 + self.Entity:SetNetworkedInt( "Clear", self.LastClear ) + + elseif ( inputname == "Active" ) then + self.Entity:SetNetworkedBool( "Active", value ~= 0 ) + + -- store float values. + elseif ( inputname == "Vector" ) and ( type(value) == "Vector" ) then + self.Entity:SetNetworkedFloat( "X", value.x ) + self.Entity:SetNetworkedFloat( "Y", value.y ) + self.Entity:SetNetworkedFloat( "Z", value.z ) + + elseif ( inputname ~= nil ) then + self.Entity:SetNetworkedFloat( inputname, value ) + end +end + +function ENT:Setup( r, g, b, a, showbeams, groundbeams, size ) + self:SetColor( r, g, b, a ); + + -- update size and show states + self:SetNetworkedBool( "ShowBeam", showbeams ); + self:SetNetworkedBool( "GroundBeam", groundbeams ); + self:SetNetworkedFloat( "PointSize", size ); + + self.r = r + self.g = g + self.b = b + self.a = a + self.showbeams = showbeams + self.groundbeams = groundbeams + self.size = size +end + + + +function MakeWireHoloemitter( pl, Pos, Ang, model, r, g, b, a, showbeams, groundbeams, size, frozen ) + -- check the players limit + if( !pl:CheckLimit( "wire_holoemitters" ) ) then return end + + -- create the emitter + local emitter = ents.Create( "gmod_wire_holoemitter" ) + emitter:SetPos( Pos ) + emitter:SetAngles( Ang ) + emitter:SetModel( model ) + emitter:Spawn() + emitter:Activate() + + if emitter:GetPhysicsObject():IsValid() then + local Phys = emitter:GetPhysicsObject() + Phys:EnableMotion(not frozen) + end + + -- setup the emitter. + emitter:Setup( r, g, b, a, showbeams, groundbeams, size, frozen ) + emitter.pl = pl + emitter:SetPlayer( pl ) + + -- add to the players count + pl:AddCount( "wire_holoemitters", emitter ) + + return emitter +end + +duplicator.RegisterEntityClass("gmod_wire_holoemitter", MakeWireHoloemitter, "Pos", "Ang", "Model", "r", "g", "b", "a", "showbeams", "groundbeams", "size", "frozen") + + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + grid = self.Entity:GetNetworkedEntity( "grid" ) + if (grid) and (grid:IsValid()) then + info.holoemitter_grid = grid:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + local grid = nil + if (info.holoemitter_grid) then + grid = GetEntByID(info.holoemitter_grid) + if (!grid) then + grid = ents.GetByIndex(info.holoemitter_grid) + end + end + if (grid && grid:IsValid()) then + self:LinkToGrid(grid) + end +end + + diff --git a/lua/entities/gmod_wire_holoemitter/shared.lua b/lua/entities/gmod_wire_holoemitter/shared.lua new file mode 100644 index 0000000000..dc2dde5ca6 --- /dev/null +++ b/lua/entities/gmod_wire_holoemitter/shared.lua @@ -0,0 +1,9 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Holographic Emitter" +ENT.Author = "Chad 'Jinto'" +ENT.Contact = "cdbarrett@gmail.com" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_hologram/cl_init.lua b/lua/entities/gmod_wire_hologram/cl_init.lua new file mode 100644 index 0000000000..136e9fdd6b --- /dev/null +++ b/lua/entities/gmod_wire_hologram/cl_init.lua @@ -0,0 +1,112 @@ +include( "shared.lua" ) + +ENT.RenderGroup = RENDERGROUP_BOTH + +local scales = {} +hook.Add("EntityRemoved", "gmod_wire_hologram", function(ent) + scales[ent:EntIndex()] = nil +end) + +local blocked = {} +concommand.Add("wire_holograms_block_client", function(ply, command, args) + local toblock + for _,ply in ipairs(player.GetAll()) do + if ply:Name() == args[1] then + toblock = ply + break + end + end + if not toblock then error("Player not found") end + + local id = toblock:UserID() + blocked[id] = true + for _,ent in ipairs(ents.FindByClass("gmod_wire_hologram")) do + if ent:GetNetworkedInt("ownerid") == id then + ent.blocked = true + end + end +end, +function() + local names = {} + for _,ply in ipairs(player.GetAll()) do + table.insert(names, "wire_holograms_block_client \""..ply:Name().."\"") + end + table.sort(names) + return names +end) + +concommand.Add("wire_holograms_unblock_client", function(ply, command, args) + local toblock + for _,ply in ipairs(player.GetAll()) do + if ply:Name() == args[1] then + toblock = ply + break + end + end + if not toblock then error("Player not found") end + + local id = toblock:UserID() + blocked[id] = nil + for _,ent in ipairs(ents.FindByClass("gmod_wire_hologram")) do + if ent:GetNetworkedInt("ownerid") == id then + ent.blocked = false + end + end +end, +function() + local names = {} + for _,ply in ipairs(player.GetAll()) do + if blocked[ply:UserID()] then + table.insert(names, "wire_holograms_unblock_client \""..ply:Name().."\"") + end + end + table.sort(names) + return names +end) + +function ENT:Initialize( ) + self:DoScale() + local ownerid = self:GetNetworkedInt("ownerid") + self.blocked = blocked[ownerid] or false +end + +function ENT:Draw() + if self.blocked then return end + self.BaseClass.Draw(self) +end + +function ENT:DoScale() + local scale = scales[self:EntIndex()] or Vector(1,1,1) + + self:SetModelScale( scale ) + + local propmax = self:OBBMaxs() + local propmin = self:OBBMins() + + propmax.x = scale.x * propmax.x + propmax.y = scale.y * propmax.y + propmax.z = scale.z * propmax.z + propmin.x = scale.x * propmin.x + propmin.y = scale.y * propmin.y + propmin.z = scale.z * propmin.z + + self:SetRenderBounds( propmax, propmin ) +end + +local function SetScale(entindex, scale) + scales[entindex] = scale + local prop = ents.GetByIndex(entindex) + if prop and prop.DoScale then + prop:DoScale() + end +end + +usermessage.Hook("wire_holograms_set_scale", function( um ) + local index = um:ReadShort() + while index ~= 0 do + local scale = um:ReadVector() + + SetScale(index, scale) + index = um:ReadShort() + end +end) diff --git a/lua/entities/gmod_wire_hologram/init.lua b/lua/entities/gmod_wire_hologram/init.lua new file mode 100644 index 0000000000..5475abf1ac --- /dev/null +++ b/lua/entities/gmod_wire_hologram/init.lua @@ -0,0 +1,12 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include( "shared.lua" ) + +function ENT:Initialize() + + self.Entity:SetSolid( SOLID_NONE ) + self.Entity:SetMoveType( MOVETYPE_NONE ) + self.Entity:DrawShadow( false ) + +end diff --git a/lua/entities/gmod_wire_hologram/shared.lua b/lua/entities/gmod_wire_hologram/shared.lua new file mode 100644 index 0000000000..98dc28cf62 --- /dev/null +++ b/lua/entities/gmod_wire_hologram/shared.lua @@ -0,0 +1,18 @@ +ENT.Type = "anim" +ENT.Base = "base_anim" + +ENT.PrintName = "Wire Hologram" +ENT.Author = "I am McLovin" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +-- copy some functions from base_gmodentity +local ENT = ENT +hook.Add("InitPostEntity", "gmod_wire_hologram_shared", function() + base_gmodentity = scripted_ents.GetList().base_gmodentity.t + + ENT.SetPlayer = base_gmodentity.SetPlayer + ENT.GetPlayer = base_gmodentity.GetPlayer +end) diff --git a/lua/entities/gmod_wire_hologrid/cl_init.lua b/lua/entities/gmod_wire_hologrid/cl_init.lua new file mode 100644 index 0000000000..2a36deb50e --- /dev/null +++ b/lua/entities/gmod_wire_hologrid/cl_init.lua @@ -0,0 +1,3 @@ +include( "shared.lua" ); + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_hologrid/init.lua b/lua/entities/gmod_wire_hologrid/init.lua new file mode 100644 index 0000000000..6d2f0cabcf --- /dev/null +++ b/lua/entities/gmod_wire_hologrid/init.lua @@ -0,0 +1,125 @@ +AddCSLuaFile( "cl_init.lua" ); +AddCSLuaFile( "shared.lua" ); +include( "shared.lua" ); + +-- wire debug and overlay crap. +ENT.WireDebugName = "Holographic Grid"; +ENT.OverlayDelay = 0; + +-- init. +function ENT:Initialize( ) + -- set model + util.PrecacheModel( "models/jaanus/wiretool/wiretool_siren.mdl" ); + self.Entity:SetModel( "models/jaanus/wiretool/wiretool_siren.mdl" ); + + -- setup physics + self.Entity:PhysicsInit( SOLID_VPHYSICS ); + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ); + self.Entity:SetSolid( SOLID_VPHYSICS ); + self.Entity:SetUseType(SIMPLE_USE) + + -- vars + self:UpdateGPS(false) + + -- create inputs. + self.Inputs = WireLib.CreateSpecialInputs(self.Entity, { "UseGPS", "Reference" }, { "NORMAL", "ENTITY" }) + self.reference = self.Entity +end + +function ENT:UpdateGPS(UseGPS) + if UseGPS then + self.usesgps = true + self.Entity:SetNetworkedEntity( "reference", ents.GetByIndex(-1) ) + self:SetOverlayText( "Holo Grid\n(GPS)" ) + else + self.usesgps = false + self.Entity:SetNetworkedEntity( "reference", self.reference ) + self:SetOverlayText( "Holo Grid\n(Local)" ) + end +end + +-- trigger input +function ENT:TriggerInput( inputname, value ) + -- store values. + if inputname == "UseGPS" then + self:UpdateGPS(value ~= 0) + elseif inputname == "Reference" then + if ValidEntity(value) then + self.reference = value + else + self.reference = self.Entity + end + self:UpdateGPS(self.usesgps) + end +end + +function ENT:Use( activator, caller ) + if caller:IsPlayer() then self:UpdateGPS(not self.usesgps) end +end + + +function MakeWireHologrid( pl, Pos, Ang, model, usegps, frozen ) + -- check the players limit + if( !pl:CheckLimit( "wire_hologrids" ) ) then return end + + -- create the grid + local grid = ents.Create( "gmod_wire_hologrid" ) + + grid:SetPos( Pos ) + grid:SetAngles( Ang ) + grid:SetModel( model ) + + grid:Spawn() + grid:Activate() + + if grid:GetPhysicsObject():IsValid() then + local Phys = grid:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + -- setup the grid. + grid:UpdateGPS(usegps) + grid.pl = pl + grid:SetPlayer(pl) + + -- add to the players count + pl:AddCount( "wire_hologrids", grid ) + + return grid; +end + +duplicator.RegisterEntityClass("gmod_wire_hologrid", MakeWireHologrid, "Pos", "Ang", "Model", "usegps", "frozen") + + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + info.hologrid_usegps = self.usesgps and 1 or 0 + + if ValidEntity(self.reference) then + info.reference = self.reference:EntIndex() + else + info.reference = nil + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + local reference + if info.reference then + reference = GetEntByID(info.reference) + if not reference then + reference = ents.GetByIndex(info.reference) + end + end + if reference then + self.reference = reference + else + self.reference = self.Entity + end + + self:UpdateGPS(info.hologrid_usegps ~= 0) +end diff --git a/lua/entities/gmod_wire_hologrid/shared.lua b/lua/entities/gmod_wire_hologrid/shared.lua new file mode 100644 index 0000000000..abc0b33398 --- /dev/null +++ b/lua/entities/gmod_wire_hologrid/shared.lua @@ -0,0 +1,9 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Holographic Grid" +ENT.Author = "Chad 'Jinto'" +ENT.Contact = "cdbarrett@gmail.com" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_hoverball/cl_init.lua b/lua/entities/gmod_wire_hoverball/cl_init.lua new file mode 100644 index 0000000000..10f1848c9f --- /dev/null +++ b/lua/entities/gmod_wire_hoverball/cl_init.lua @@ -0,0 +1,88 @@ + +ENT.RenderGroup = RENDERGROUP_BOTH + +CreateConVar( "cl_drawhoverballs", "1" ) + +include('shared.lua') + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + + self.Refraction = Material( "sprites/heatwave" ) + self.Glow = Material( "sprites/light_glow02_add" ) + self.ShouldDraw = 1 + + self.NextSmokeEffect = 0 + +end + + +/*--------------------------------------------------------- + Name: Draw +---------------------------------------------------------*/ +function ENT:Draw() + + if ( self.ShouldDraw == 0 ) then return end + self.BaseClass.Draw( self ) + + Wire_Render(self.Entity) + +end + +/*--------------------------------------------------------- + Name: DrawTranslucent + Desc: Draw translucent +---------------------------------------------------------*/ +function ENT:DrawTranslucent() + + if ( self.ShouldDraw == 0 ) then return end + + if (self:GetHoverMode()) then + local vOffset = self.Entity:GetPos() + local vPlayerEyes = LocalPlayer():EyePos() + local vDiff = (vOffset - vPlayerEyes):GetNormalized() + + render.SetMaterial( self.Glow ) + local color = Color( 40, 50, 200, 255 ) //70,180,255,255 + render.DrawSprite( vOffset - vDiff * 2, 22, 22, color ) + + local Distance = math.abs( (self:GetTargetZ() - self.Entity:GetPos().z) * math.sin( CurTime() * 20 ) ) * 0.05 + color.r = color.r * math.Clamp( Distance, 0, 1 ) + color.b = color.b * math.Clamp( Distance, 0, 1 ) + color.g = color.g * math.Clamp( Distance, 0, 1 ) + + render.DrawSprite( vOffset + vDiff * 4, 48, 48, color ) + render.DrawSprite( vOffset + vDiff * 4, 52, 52, color ) + else + local vOffset = self.Entity:GetPos() + local vPlayerEyes = LocalPlayer():EyePos() + local vDiff = (vOffset - vPlayerEyes):GetNormalized() + + render.SetMaterial( self.Glow ) + local color = Color( 255, 50, 60, 255 ) //70,180,255,255 + render.DrawSprite( vOffset - vDiff * 2, 22, 22, color ) + + local Pulse = math.sin( CurTime() * 20 ) * 0.05 + color.r = color.r * math.Clamp( Pulse, 0, 1 ) + color.b = color.b * math.Clamp( Pulse, 0, 1 ) + color.g = color.g * math.Clamp( Pulse, 0, 1 ) + + render.DrawSprite( vOffset + vDiff * 4, 48, 48, color ) + render.DrawSprite( vOffset + vDiff * 4, 52, 52, color ) + end + +end + + +/*--------------------------------------------------------- + Name: Think + Desc: Client Think - called every frame +---------------------------------------------------------*/ +function ENT:Think() + self.BaseClass.Think(self) + + self.ShouldDraw = GetConVarNumber( "cl_drawhoverballs" ) +end + diff --git a/lua/entities/gmod_wire_hoverball/init.lua b/lua/entities/gmod_wire_hoverball/init.lua new file mode 100644 index 0000000000..224a1fcf37 --- /dev/null +++ b/lua/entities/gmod_wire_hoverball/init.lua @@ -0,0 +1,272 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Hoverball" +ENT.OnState = 0 + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + + self.Entity:SetModel( "models/dav0r/hoverball.mdl" ) + + // Don't use the model's physics object, create a perfect sphere + + self.Entity:PhysicsInitSphere( 8, "metal_bouncy" ) + + // Wake up our physics object so we don't start asleep + + local phys = self.Entity:GetPhysicsObject() + + if ( phys:IsValid() ) then + phys:SetMass( 100 ) + phys:EnableGravity( false ) + phys:Wake() + end + + // Start the motion controller (so PhysicsSimulate gets called) + self.Entity:StartMotionController() + + self.Fraction = 0 + + self.ZVelocity = 0 + self:SetTargetZ( self.Entity:GetPos().z ) + self:SetSpeed( 1 ) + self:EnableHover() + + self.Inputs = Wire_CreateInputs(self.Entity, { "A: ZVelocity", "B: HoverMode", "C: SetZTarget" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "A: Zpos", "B: Xpos", "C: Ypos" }) + +end + + +function ENT:TriggerInput(iname, value) + if (iname == "A: ZVelocity") then + self:SetZVelocity( value ) + elseif (iname == "B: HoverMode") then + if (value >= 1 && self.OnState==0) then + self:EnableHover() + else + self:DisableHover() + end + elseif (iname == "C: SetZTarget") then + self:SetTargetZ(value) + end +end + + +function ENT:EnableHover() + self.OnState = 1 + self:SetHoverMode( true ) + self:SetStrength( self.strength or 1 ) //reset weight so it will work + self:SetTargetZ ( self.Entity:GetPos().z ) //set height to current + local phys = self.Entity:GetPhysicsObject() + if ( phys:IsValid() ) then + phys:EnableGravity( false ) + phys:Wake() + end +end + +function ENT:DisableHover() + self.OnState = 0 + self:SetHoverMode( false ) + self:SetStrength(0.1) //for less dead weight while off + local phys = self.Entity:GetPhysicsObject() + if ( phys:IsValid() ) then + phys:EnableGravity( true ) //falls slowly otherwise + end +end + + +function ENT:OnRestore() + self.ZVelocity = 0 + + self.BaseClass.OnRestore(self) +end + +/*--------------------------------------------------------- + Name: OnTakeDamage +---------------------------------------------------------*/ +function ENT:OnTakeDamage( dmginfo ) + //self.Entity:TakePhysicsDamage( dmginfo ) +end + +/*--------------------------------------------------------- + Name: Think +---------------------------------------------------------*/ +function ENT:Think() + + self.Entity:NextThink( CurTime() + 0.25 ) + + self.Entity:SetNetworkedInt( "TargetZ", self:GetTargetZ() ) + + return true + +end + +/*--------------------------------------------------------- + Name: Simulate +---------------------------------------------------------*/ +function ENT:PhysicsSimulate( phys, deltatime ) + + local Pos = phys:GetPos() + local txt = string.format( "Speed: %i\nResistance: %.2f", self:GetSpeed(), self:GetAirResistance() ) + txt = txt.."\nZ pos: "..math.floor(Pos.z) //.."Target: "..math.floor(self:GetTargetZ()) + + Wire_TriggerOutput(self.Entity, "A: Zpos", Pos.z) + Wire_TriggerOutput(self.Entity, "B: Xpos", Pos.x) + Wire_TriggerOutput(self.Entity, "C: Ypos", Pos.y) + + + if (self:GetHoverMode()) then + + txt = txt.." (on)" + self:SetOverlayText( txt ) + + if ( self.ZVelocity != 0 ) then + + self:SetTargetZ( self:GetTargetZ() + (self.ZVelocity * deltatime * self:GetSpeed()) ) + self.Entity:GetPhysicsObject():Wake() + + end + + phys:Wake() + + local Vel = phys:GetVelocity() + local Distance = self:GetTargetZ() - Pos.z + local AirResistance = self:GetAirResistance() + + + if ( Distance == 0 ) then return end + + local Exponent = Distance^2 + + if ( Distance < 0 ) then + Exponent = Exponent * -1 + end + + Exponent = Exponent * deltatime * 300 + + local physVel = phys:GetVelocity() + local zVel = physVel.z + + Exponent = Exponent - (zVel * deltatime * 600 * ( AirResistance + 1 ) ) + // The higher you make this 300 the less it will flop about + // I'm thinking it should actually be relative to any objects we're connected to + // Since it seems to flop more and more the heavier the object + + Exponent = math.Clamp( Exponent, -5000, 5000 ) + + local Linear = Vector(0,0,0) + local Angular = Vector(0,0,0) + + Linear.z = Exponent + + if ( AirResistance > 0 ) then + + Linear.y = physVel.y * -1 * AirResistance + Linear.x = physVel.x * -1 * AirResistance + + end + + return Angular, Linear, SIM_GLOBAL_ACCELERATION + else + txt = txt.." (off)" + self:SetOverlayText( txt ) + return SIM_GLOBAL_FORCE + end + +end + +function ENT:SetZVelocity( z ) + + if ( z != 0 ) then + self.Entity:GetPhysicsObject():Wake() + end + + self.ZVelocity = z * FrameTime() * 5000 +end + +/*--------------------------------------------------------- + GetAirFriction +---------------------------------------------------------*/ +function ENT:GetAirResistance( ) + return self.Entity:GetVar( "AirResistance", 0 ) +end + + +/*--------------------------------------------------------- + SetAirFriction +---------------------------------------------------------*/ +function ENT:SetAirResistance( num ) + self.Entity:SetVar( "AirResistance", num ) +end + +/*--------------------------------------------------------- + SetStrength +---------------------------------------------------------*/ +function ENT:SetStrength( strength ) + + local phys = self.Entity:GetPhysicsObject() + if ( phys:IsValid() ) then + phys:SetMass( 150 * strength ) + end +end + +/*--------------------------------------------------------- +--Duplicator support +---------------------------------------------------------*/ +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + info.OnState = self.OnState + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + if(ply && ent && info && GetEntByID)then + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + end + if (info && info.OnState) then + if(info.OnState==0)then + self:DisableHover() + else + self:EnableHover() + end + end +end + + +function MakeWireHoverBall( pl, Pos, Ang, model, speed, resistance, strength, nocollide ) + if not pl:CheckLimit( "wire_hoverballs" ) then return nil end + + local wire_ball = ents.Create( "gmod_wire_hoverball" ) + if not wire_ball:IsValid() then return false end + + wire_ball:SetPos( Pos ) + wire_ball:SetAngles( Ang ) + wire_ball:SetModel( model ) + wire_ball:Spawn() + wire_ball:SetSpeed( speed ) + wire_ball:SetPlayer( pl ) + wire_ball:SetAirResistance( resistance ) + wire_ball:SetStrength( strength ) + + local ttable = { + pl = pl, + nocollide = nocollide, + speed = speed, + strength = strength, + resistance = resistance + } + table.Merge( wire_ball:GetTable(), ttable ) + + pl:AddCount( "wire_hoverballs", wire_ball ) + + return wire_ball +end + +duplicator.RegisterEntityClass("gmod_wire_hoverball", MakeWireHoverBall, "Pos", "Ang", "Model", "speed", "resistance", "strength", "nocollide") + diff --git a/lua/entities/gmod_wire_hoverball/shared.lua b/lua/entities/gmod_wire_hoverball/shared.lua new file mode 100644 index 0000000000..2b02b3a81d --- /dev/null +++ b/lua/entities/gmod_wire_hoverball/shared.lua @@ -0,0 +1,46 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Hover Ball" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:GetTargetZ() + return self.Entity:GetNetworkedInt( 0 ) +end + +function ENT:SetTargetZ( z ) + return self.Entity:SetNetworkedInt( 0, z ) +end + + +function ENT:GetSpeed() + + // Sensible limits + if (!SinglePlayer()) then + return math.Clamp( self.Entity:GetNetworkedFloat( 1 ), 0.0, 10.0 ) + end + + return self.Entity:GetNetworkedFloat( 1 ) +end + +function ENT:SetSpeed( s ) + + self.Entity:SetNetworkedFloat( 1, s ) + +end + + +function ENT:GetHoverMode() + return self.Entity:GetNetworkedBool( 2 ) +end + +function ENT:SetHoverMode( h ) + return self.Entity:SetNetworkedBool( 2, h ) +end diff --git a/lua/entities/gmod_wire_hoverdrivecontroler/cl_init.lua b/lua/entities/gmod_wire_hoverdrivecontroler/cl_init.lua new file mode 100644 index 0000000000..d772a73078 --- /dev/null +++ b/lua/entities/gmod_wire_hoverdrivecontroler/cl_init.lua @@ -0,0 +1,90 @@ + +ENT.RenderGroup = RENDERGROUP_BOTH + +language.Add( "Cleanup_hoverdrivecontrolers", "Hoverdrive Controllers" ) +language.Add( "Cleaned_hoverdrivecontrolers", "Cleaned up Hoverdrive Controllers" ) +language.Add( "SBoxLimit_wire_hoverdrives", "You have hit the Hoverdrive Controllers limit!" ) + +include('shared.lua') + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + + self.Refraction = Material( "sprites/heatwave" ) + self.Glow = Material( "sprites/light_glow02_add" ) + self.ShouldDraw = 1 + + self.NextSmokeEffect = 0 + +end + + +/*--------------------------------------------------------- + Name: Draw +---------------------------------------------------------*/ +function ENT:Draw() + + if ( self.ShouldDraw == 0 ) then return end + self.BaseClass.Draw( self ) + + Wire_Render(self.Entity) + +end + +/*--------------------------------------------------------- + Name: DrawTranslucent + Desc: Draw translucent +---------------------------------------------------------*/ +function ENT:DrawTranslucent() + + if ( self.ShouldDraw == 0 ) then return end + + if (self:GetHoverMode() > 0) then + local vOffset = self.Entity:GetPos() + local vPlayerEyes = LocalPlayer():EyePos() + local vDiff = (vOffset - vPlayerEyes):GetNormalized() + + render.SetMaterial( self.Glow ) + local color = Color( 40, 50, 200, 255 ) //70,180,255,255 + render.DrawSprite( vOffset - vDiff * 2, 22, 22, color ) + + local Distance = math.abs( (self:GetTargetZ() - self.Entity:GetPos().z) * math.sin( CurTime() * 20 ) ) * 0.05 + color.r = color.r * math.Clamp( Distance, 0, 1 ) + color.b = color.b * math.Clamp( Distance, 0, 1 ) + color.g = color.g * math.Clamp( Distance, 0, 1 ) + + render.DrawSprite( vOffset + vDiff * 4, 48, 48, color ) + render.DrawSprite( vOffset + vDiff * 4, 52, 52, color ) + else + local vOffset = self.Entity:GetPos() + local vPlayerEyes = LocalPlayer():EyePos() + local vDiff = (vOffset - vPlayerEyes):GetNormalized() + + render.SetMaterial( self.Glow ) + local color = Color( 255, 50, 60, 255 ) //70,180,255,255 + render.DrawSprite( vOffset - vDiff * 2, 22, 22, color ) + + local Pulse = math.sin( CurTime() * 20 ) * 0.05 + color.r = color.r * math.Clamp( Pulse, 0, 1 ) + color.b = color.b * math.Clamp( Pulse, 0, 1 ) + color.g = color.g * math.Clamp( Pulse, 0, 1 ) + + render.DrawSprite( vOffset + vDiff * 4, 48, 48, color ) + render.DrawSprite( vOffset + vDiff * 4, 52, 52, color ) + end + +end + + +/*--------------------------------------------------------- + Name: Think + Desc: Client Think - called every frame +---------------------------------------------------------*/ +function ENT:Think() + self.BaseClass.Think(self) + + self.ShouldDraw = GetConVarNumber( "cl_drawhoverballs" ) +end + diff --git a/lua/entities/gmod_wire_hoverdrivecontroler/init.lua b/lua/entities/gmod_wire_hoverdrivecontroler/init.lua new file mode 100644 index 0000000000..11f03b551a --- /dev/null +++ b/lua/entities/gmod_wire_hoverdrivecontroler/init.lua @@ -0,0 +1,965 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "HoverDrive" + +local useenergy = CreateConVar( "sv_HoverDriveUseEnergy", 0, {FCVAR_ARCHIVE} ) + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + + self.Entity:SetModel( "models//props_c17/utilityconducter001.mdl" ) + + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Entity:DrawShadow(false) + + //self.Entity:SetModel( "models/dav0r/hoverball.mdl" ) + //self.Entity:PhysicsInitSphere( 8, "metal_bouncy" ) + + local phys = self.Entity:GetPhysicsObject() + + if ( phys:IsValid() ) then + phys:SetMass( 100 ) + phys:EnableGravity( false ) + phys:Wake() + end + + if ( RES_DISTRIB == 2 ) then + RD_AddResource(self, "energy", 0) + if(LS_RegisterEnt) then LS_RegisterEnt(self, "Generator") end; -- Not everyone uses LifeSupport if he has Resource Distribution installed + end + + //self.Entity:StartMotionController() + + self.Fraction = 0 + + self.Velocity = Vector(0,0,0) + self:SetTargetZ( self.Entity:GetPos().z ) + self.Target = self.Entity:GetPos() + self:SetSpeed( 1 ) + self:SetHoverMode( 1 ) + + self.TargetYaw = 0 + self.YawVelocity = 0 + + self.TargetAngle = Angle(0, 0, 0) + self.AngleVelocity = Angle(0, 0, 0) + + self.JumpTarget = Vector(0,0,0) + + self.Sound = true + + self.Inputs = WireLib.CreateSpecialInputs( self.Entity, { "X_JumpTarget", "Y_JumpTarget", "Z_JumpTarget", "SetJumpTarget", "Jump", "JumpTarget", "Sound" }, { "NORMAL", "NORMAL", "NORMAL", "NORMAL", "NORMAL", "VECTOR", "NORMAL" } ) //"X_Velocity", "Y_Velocity", "Z_Velocity", "Pitch_Velocity", "Yaw_Velocity", "Roll_Velocity", "HoverMode", + //self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, {"Data"}, {"HOVERDATAPORT"}) + self:ShowOutput() +end + +CreateConVar('sbox_maxwire_hoverdrives', 2) +local function MakeWireHoverDriveCtrl(pl, Data) + if !pl:CheckLimit("wire_hoverdrives") then return nil end + + local ent = ents.Create("gmod_wire_hoverdrivecontroler") + if !ent:IsValid() then return end + duplicator.DoGeneric(ent, Data) + ent:SetPlayer(pl) + ent:Spawn() + ent:Activate() + + duplicator.DoGenericPhysics(ent, pl, Data) + + ent:SetSpeed(1) + ent:SetAirResistance(0) + ent:SetStrength(10) + + pl:AddCount("wire_hoverdrives", ent) + pl:AddCleanup("hoverdrivecontrolers", ent) + return ent +end +duplicator.RegisterEntityClass("gmod_wire_hoverdrivecontroler", MakeWireHoverDriveCtrl, "Data") + +function ENT:SpawnFunction( pl, tr ) + + if ( !tr.Hit ) then return end + + local SpawnPos = tr.HitPos + tr.HitNormal * 16 + + local ent = MakeWireHoverDriveCtrl( pl, {Pos = SpawnPos} ) + + return ent +end + +function ENT:TriggerInput(iname, value) + if (iname == "Jump") then + if (value ~= 0) and (!self.Jumping) then + if not self.Inputs.SetJumpTarget.Src then + -- if there is nothing connected to SetJumpTarget, set the jump target + self.JumpTargetSet = true + end + self:Jump() + end + elseif (iname == "JumpTarget") then + self.JumpTarget = value or Vector(0,0,20) + elseif (iname == "X_JumpTarget") then + self.JumpTarget = self.JumpTarget or Vector(0,0,20) + self.JumpTarget.x = value + elseif (iname == "Y_JumpTarget") then + self.JumpTarget = self.JumpTarget or Vector(0,0,20) + self.JumpTarget.y = value + elseif (iname == "Z_JumpTarget") then + self.JumpTarget = self.JumpTarget or Vector(0,0,20) + self.JumpTarget.z = value + elseif (iname == "SetJumpTarget") then + //Msg("value = "..value.."\n") + if (value ~= 0) then + self.JumpTargetSet = true + /*else + self.JumpTargetSet = false*/ + end + elseif (iname == "Sound") then + self.Sound = value ~= 0 + elseif (iname == "Z_Velocity") then + self:SetZVelocity( value ) + elseif (iname == "X_Velocity") then + self:SetXVelocity( value ) + elseif (iname == "Y_Velocity") then + self:SetYVelocity( value ) + elseif (iname == "HoverMode") then + if (value ~= 0) then + self.Target = self.Entity:GetPos() + self:SetHoverMode( 1 ) + else + self:SetHoverMode( 0 ) + end + elseif (iname == "Pitch_Velocity") then + self:SetPitchVelocity( value ) + elseif (iname == "Yaw_Velocity") then + self:SetYawVelocity( value ) + elseif (iname == "Roll_Velocity") then + self:SetRollVelocity( value ) + end + self:ShowOutput() +end + + +function ENT:ShowOutput() + local txt = "-HoverDrive-\nJump Target = "..tostring(self.JumpTarget) + if (self.JumpTargetSet) then + txt = txt.."\n( Jump Target Set )" + end + self:SetOverlayText( txt ) +end + + +function ENT:OnRestore() + self.Velocity = Vector(0,0,0) + self.Target = self.Entity:GetPos() + + self.BaseClass.OnRestore(self) +end + +/*--------------------------------------------------------- + Name: OnTakeDamage +---------------------------------------------------------*/ +/*function ENT:OnTakeDamage( dmginfo ) + //self.Entity:TakePhysicsDamage( dmginfo ) +end*/ + + +local function GetTargetAndExponentVector(deltatime, Target, Velocity, AxisPos, AxisVel, AirResistance, Speed) + + local Diff = Target - AxisPos + Diff.x = math.Clamp( Diff.x, -100, 100 ) + Diff.y = math.Clamp( Diff.y, -100, 100 ) + Diff.z = math.Clamp( Diff.z, -100, 100 ) + + if ( Diff == Vector(0,0,0) ) then + return Target, Vector(0,0,0) + end + + local Exponent = Vector() + Exponent.x = Diff.x^2 + Exponent.y = Diff.y^2 + Exponent.z = Diff.z^2 + + if ( Diff.x < 0 ) then Exponent.x = Exponent.x * -1 end + if ( Diff.y < 0 ) then Exponent.y = Exponent.y * -1 end + if ( Diff.z < 0 ) then Exponent.z = Exponent.z * -1 end + + Exponent = ( Exponent * deltatime * 300 ) - ( AxisVel * deltatime * 600 * ( AirResistance + 1 ) ) + + Exponent.x = math.Clamp( Exponent.x, -5000, 5000 ) + Exponent.y = math.Clamp( Exponent.y, -5000, 5000 ) + Exponent.z = math.Clamp( Exponent.z, -5000, 5000 ) + + return Target, Exponent +end + +/*--------------------------------------------------------- + Think wasn't good enough +---------------------------------------------------------*/ +/*function ENT:PhysicsSimulate( phys, deltatime ) + + /*if ( self.YawVelocity != 0 ) then + self.TargetYaw = math.fmod( ( self.TargetYaw + ( self.YawVelocity * deltatime ) ), 360 ) + //Msg("self.TargetYaw = "..self.TargetYaw.."\n") + end* + if ( self.AngleVelocity.p != 0 ) then + self.TargetAngle.p = math.fmod( ( self.TargetAngle.p + ( self.AngleVelocity.p * deltatime ) ), 360 ) + end + if ( self.AngleVelocity.y != 0 ) then + self.TargetAngle.y = math.fmod( ( self.TargetAngle.y + ( self.AngleVelocity.y * deltatime ) ), 360 ) + end + if ( self.AngleVelocity.r != 0 ) then + self.TargetAngle.r = math.fmod( ( self.TargetAngle.r + ( self.AngleVelocity.r * deltatime ) ), 360 ) + end + + local Vel = self.Entity:GetPhysicsObject():LocalToWorldVector( self.Velocity ) + + self.Target = self.Target + ( Vel * deltatime * self:GetSpeed() ) + self:SetTargetZ(self.Target.Z) + + local data = {} + data.Hover = self:GetHoverMode() + data.Target = self.Target + //data.TargetYaw = self.TargetYaw + data.TargetAngle = self.TargetAngle + //data.ControlerPos = self.Entity:GetPos() + + Wire_TriggerOutput(self.Entity, "Data", data) + + local txt = "-HoverDrive-\nJump Target = "..tostring(self.JumpTarget) + if (self:GetHoverMode()) then + txt = txt.."\n(on)" + self:SetOverlayText( txt ) + else + txt = txt.."\n(off)" + self:SetOverlayText( txt ) + end + + return SIM_GLOBAL_FORCE* + + /*local Pos = phys:GetPos() + //local txt = string.format( "Speed: %i\nResistance: %.2f", self:GetSpeed(), self:GetAirResistance() ) + //txt = txt.."\nZ pos: "..math.floor(Pos.z) //.."Target: "..math.floor(self:GetTargetZ()) + + local txt = "TargetX = "..self.Target.x.."\nTargetY = "..self.Target.y.."\nTargetZ = "..self.Target.z + + Wire_TriggerOutput(self.Entity, "A: Zpos", Pos.z) + Wire_TriggerOutput(self.Entity, "B: Xpos", Pos.x) + Wire_TriggerOutput(self.Entity, "C: Ypos", Pos.y) + + + if (self:GetHoverMode()) then + + txt = txt.."\n(on)" + self:SetOverlayText( txt ) + + local physVel = phys:GetVelocity() + local physAngVel = phys:GetAngleVelocity() + local AirResistance = self:GetAirResistance() + local Speed = self:GetSpeed() + + phys:Wake() + + self.Velovity = Vector( self.XVelocity, self.YVelocity, self.ZVelocity ) + local Vel = phys:LocalToWorldVector( self.Velovity ) + + /*local TargetX, ExponentX = GetTargetAndExponent(deltatime, self:GetTargetX(), Vel.x, Pos.x, physVel.x, AirResistance, Speed) + self:SetTargetX(TargetX) + + local TargetY, ExponentY = GetTargetAndExponent(deltatime, self:GetTargetY(), Vel.y, Pos.y, physVel.y, AirResistance, Speed) + self:SetTargetY(TargetY) + + local TargetZ, ExponentZ = GetTargetAndExponent(deltatime, self:GetTargetZ(), Vel.z, Pos.z, physVel.z, AirResistance, Speed) + self:SetTargetZ(TargetZ)* + + local Target, Exponent = GetTargetAndExponentVector(deltatime, self.Target, Vel, Pos, physVel, AirResistance, Speed) + self.Target = Target + self:SetTargetZ(Target.Z) + + local Ang = phys:GetAngles() + + if ( Exponent == Vector(0,0,0) ) then return end + + //local Linear = Vector(0,0,0) + local Angular = Vector(0,0,0) + + /*Linear.z = ExponentZ + Linear.x = ExponentX + Linear.y = ExponentY* + // Linear + return Angular, Exponent, SIM_GLOBAL_ACCELERATION //SIM_LOCAL_ACCELERATION + else + txt = txt.."\n(off)" + self:SetOverlayText( txt ) + return SIM_GLOBAL_FORCE + end*/ + +//end + + +/*function ENT:DoOutput() + + local data = {} + data.Hover = self:GetHoverMode() + data.Target = self.Target + data.TargetNorm = self.Entity:GetForward() + //data.ControlerPos = self.Entity:GetPos() + + Wire_TriggerOutput(self.Entity, "Data", data) + + local txt = "Target = "..tostring(self.Target) + if (self:GetHoverMode()) then + txt = txt.."\n(on)" + self:SetOverlayText( txt ) + else + txt = txt.."\n(off)" + self:SetOverlayText( txt ) + end +end*/ + + +function ENT:WakePhys() + local phys = self.Entity:GetPhysicsObject() + if ( phys:IsValid() ) then + phys:Wake() + end +end + +function ENT:SetXVelocity( x ) + self.Velocity.x = x * FrameTime() * 5000 + self:WakePhys() +end + +function ENT:SetYVelocity( y ) + self.Velocity.y = y * FrameTime() * 5000 + self:WakePhys() +end + +function ENT:SetZVelocity( z ) + self.Velocity.z = z * FrameTime() * 5000 + self:WakePhys() +end + +function ENT:SetVelocity( vel ) + self.Velocity = vel * FrameTime() * 5000 + self:WakePhys() +end + +function ENT:SetPitchVelocity( vel ) + self.AngleVelocity.p = vel * FrameTime() * 2000 + self:WakePhys() +end + +function ENT:SetYawVelocity( vel ) + self.AngleVelocity.y = vel * FrameTime() * 2000 + self:WakePhys() +end + +function ENT:SetRollVelocity( vel ) + self.AngleVelocity.r = vel * FrameTime() * 2000 + self:WakePhys() +end + + +/*--------------------------------------------------------- + GetAirFriction +---------------------------------------------------------*/ +function ENT:GetAirResistance( ) + return self.Entity:GetVar( "AirResistance", 0 ) +end + + +/*--------------------------------------------------------- + SetAirFriction +---------------------------------------------------------*/ +function ENT:SetAirResistance( num ) + self.Entity:SetVar( "AirResistance", num ) +end + +/*--------------------------------------------------------- + SetStrength +---------------------------------------------------------*/ +function ENT:SetStrength( strength ) + + local phys = self.Entity:GetPhysicsObject() + if ( phys:IsValid() ) then + phys:SetMass( 150 * strength ) + end +end + + + + +ENT.JumpStage = 0 + + +//util.PrecacheSound("stargate/teleport.mp3") +util.PrecacheSound("npc/turret_floor/die.wav") +//util.PrecacheSound("npc/scanner/combat_scan_loop2.wav") +util.PrecacheSound("ambient/levels/citadel/weapon_disintegrate2.wav") +//util.PrecacheSound("ambient/levels/labs/electric_explosion2.wav") +util.PrecacheSound("buttons/button2.wav") +util.PrecacheSound("buttons/button8.wav") +function ENT:FailJump() + self.Entity:EmitSound("npc/turret_floor/die.wav", 450, 70) +end + +function ENT:Jump() + if (self.Jumping) then return end + + if (!self.JumpTargetSet) then + self.Entity:EmitSound("buttons/button8.wav", 130) + return + end + + if ( RES_DISTRIB == 2 and useenergy:GetBool() ) then + local dist = self.Entity:GetPos():Distance(self.JumpTarget) + local needed = math.floor(dist ^ 2 / 5000 + 200) + --Msg("hover drive requires ",needed," energy to jump ",dist,"\n") + local energy = RD_GetResourceAmount(self, "energy") + if (energy >= needed) then + RD_ConsumeResource(self, "energy", needed) + else + self.Entity:EmitSound("buttons/button2.wav", 500) + self:FailJump() + return + end + end + + if ( not util.IsInWorld( self.JumpTarget ) ) then + self.Entity:EmitSound("buttons/button8.wav", 500) + self:FailJump() + return + end + + self.Jumping = true + + self.other_gate = self.Entity + self.LastPos = self.Entity:GetPos()// + Vector(0,0,5) + + self.JumpStage = 1 + self.JumpTargetSet = false + --Msg("Jumping!\n") +end + +function ENT:Think() + if (self.JumpStage == 1) then + --Msg("Start Jump 1\n") + + local attached = self:GetEntitiesForTeleport(self.Entity); + if(attached) then + + --TODO: LS2 energy required based on attached mass + /*if ( RES_DISTRIB == 2 and useenergy:GetBool() ) then + local mass = something + local dist = self.Entity:GetPos():Distance(self.JumpTarget) + local needed = math.floor(dist ^ 2 / 5000 + 200) + mass? + --Msg("hover drive requires ",needed," energy to jump ",dist,"\n") + local energy = RD_GetResourceAmount(self, "energy") + if (energy >= needed) then + RD_ConsumeResource(self, "energy", needed) + else + self.Entity:EmitSound("buttons/button2.wav", 500) + self:FailJump() + self.JumpStage = 0 + return + end + end*/ + + self.ents = self:PrepareTeleport(attached); + + DoPropSpawnedEffect( self.Entity ); + + //self.LastPos = self.Entity:GetPos() + + local Ofs = self.JumpTarget - self.Entity:GetPos() + //local ang = Ofs:Angle() + //local effectend = self.Entity:GetPos() + (Ofs:Normalize() * 180) + + local ed = EffectData() + ed:SetEntity( self.Entity ) + ed:SetOrigin( self.Entity:GetPos() + (Ofs:Normalize() * math.Clamp( self.Entity:BoundingRadius() * 5, 180, 4092 ) ) ) + util.Effect( "jump_out", ed, true, true ); + + for _,v in pairs(self.ents.Attached) do + if (v and v.Entity and v.Entity:IsValid()) then + + v.Entity:DrawShadow(false) + + DoPropSpawnedEffect( v.Entity ); + + local ed = EffectData() + ed:SetEntity( v.Entity ) + ed:SetOrigin( self.Entity:GetPos() + (Ofs:Normalize() * math.Clamp( v.Entity:BoundingRadius() * 5, 180, 4092 ) ) ) + util.Effect( "jump_out", ed, true, true ); + + end + end + + //self.Entity:EmitSound("stargate/teleport.mp3") + //self.Entity:EmitSound("npc/scanner/combat_scan_loop2.wav", 500) + if self.Sound then + self.Entity:EmitSound("ambient/levels/citadel/weapon_disintegrate2.wav", 500) + end + self.JumpStage = 2 + else + self:FailJump() + self.JumpStage = 0 + end + + --Msg("End Jump 1\n") + elseif (self.JumpStage == 2) then + --Msg("Start Jump 2\n") + + self:Teleport(self.ents.Entity, self.Entity); + + DoPropSpawnedEffect( self.Entity ); + + local Ofs = self.LastPos - self.Entity:GetPos() + + local ed = EffectData() + ed:SetEntity( self.Entity ) + ed:SetOrigin( self.Entity:GetPos() + (Ofs:Normalize() * math.Clamp( self.Entity:BoundingRadius() * 5, 180, 4092 ) ) ) + util.Effect( "jump_in", ed, true, true ); + + for _,v in pairs(self.ents.Attached) do + self:Teleport(v,self.Entity); + + if (v and v.Entity and v.Entity:IsValid()) then + + v.Entity:DrawShadow(true) + + DoPropSpawnedEffect( v.Entity ); + + local ed = EffectData() + ed:SetEntity( v.Entity ) + ed:SetOrigin( self.Entity:GetPos() + (Ofs:Normalize() * math.Clamp( v.Entity:BoundingRadius() * 5, 180, 4092 ) ) ) + util.Effect( "jump_in", ed, true, true ); + + end + end + + if self.Sound then + //self.Entity:EmitSound("stargate/teleport.mp3") + self.Entity:EmitSound("npc/turret_floor/die.wav", 450, 70) + //self.Entity:EmitSound("ambient/levels/labs/electric_explosion2.wav", 500, 90) + end + + //self.JumpTarget = self.NextJumpTarget + self.Target = self.Entity:GetPos() + self.JumpStage = 0 + self.Jumping = false + + --Msg("End Jump 1\n") + end +end + + + +/*--------------------------------------------------------- + Teleport Functions + Based on Teleport functions from Stargates + Credits to Avon +---------------------------------------------------------*/ + + +--################# Allowed for teleport? +function ENT:Allowed(e,auto_close_check) + local c = e:GetClass(); + local t = type(e):lower(); + /*local p = e:GetPhysicsObject(); + local moveable = true; + if(p:IsValid() and t == "entity") then + if(not p:IsMoveable()) then + moveable = false; + end + end*/ + if(((t == "player" and not e:InVehicle()) or + (t == "entity" and (c:find("prop_[prv]") or (c:find("phys_") and not auto_close_check))) or -- Allow props and constraints + t == "vehicle" or -- Vehicles + (e.Type ~= nil) or //and not c:find("stargate") and not (c:find("dhd") and auto_close_check)) or -- SENT's but not stargates - May cause into infinity selfteleportation - For more, watch this funny screeny ;) http://forums.facepunchstudios.com/showpost.php?p=4747617&postcount=256 + t == "npc" or -- NPC's + t == "weapon" or c == "npc_grenade_frag" or c == "rpg_missile" or c == "grenade_ar2" or c == "crossbow_bolt" or c == "npc_satchel" or c == "prop_combine_ball") and -- Weapons and grenades from weapons etc + e:GetParent():EntIndex() == 0 -- Only allow unparented props to get teleported + //and (moveable or not auto_close_check) -- For the autoclose only - Is the object awake? + ) then + return true + end + return false +end + + +--################# Bones for vehicle teleportation +function ENT:GetBones(e) + -- And as well, get the bones of an object + local bones = {}; + if(type(e):lower() == "vehicle" or e:GetClass() == "prop_ragdoll") then + for k=0,e:GetPhysicsObjectCount()-1 do + local bone = e:GetPhysicsObjectNum(k); + if(bone:IsValid()) then + table.insert(bones,{ + Entity=bone, + Position=e:WorldToLocal(bone:GetPos()), + Velocity=e:WorldToLocal(e:GetPos()+bone:GetVelocity()), + }); + end + end + end + return bones; +end + +--################# Prepares the teleport for the entity e and the attached entities a +function ENT:PrepareTeleport(tbl) + -- Entities + local e = tbl.Entity; + -- Gate specific + local g = {self.Entity,self.other_gate} -- Gates + local a = { -- Angles + This=g[1]:GetAngles(), + Other=g[2]:GetAngles(), + } + a.Delta=a.Other-a.This; + + /*local maxz = e:GetPos().z + e:BoundingRadius(); + local maxeent = e; + local minz = e:GetPos().z - e:BoundingRadius(); + local minzent = e;*/ + + + -- Return table + local ret = {Attached={}}; + -- ######### Calculate new positions,angles and velocity for attached + for _,v in pairs(tbl.Attached) do + local vel = v:GetVelocity(); + local data = { + Entity=v, + Position={ + New=e:WorldToLocal(v:GetPos()), + Old=v:GetPos(), + }, + Velocity={ + New=e:WorldToLocal(vel+e:GetPos()), + Old=vel, + }, + Angles={ + Old=v:GetAngles(), + New=v:GetAngles()+a.Delta, + Delta=a.Delta, + }, + Bones=self:GetBones(v), + } + table.insert(ret.Attached,data); + /*if minz < v:GetPos().z - v:BoundingRadius() then + minz = v:GetPos().z - v:BoundingRadius(); + maxeent = v; + end + if maxz < v:GetPos().z + v:BoundingRadius() then + maxz = v:GetPos().z + v:BoundingRadius(); + minzent = v; + end*/ + end + -- ######### Calculate new positions,angles and velocity for constraints + -- No we don't do. Why? I found out, constraints are at the same placer - always. so, don't change them + -- ######### Now change the base-entity itself + local vel = e:GetVelocity(); + ret.Entity={ + Entity=e; + Position={ + //New=g[2]:LocalToWorld(g[1]:WorldToLocal(e:GetPos())), + New=self.JumpTarget,// + e:WorldToLocal(e:GetPos()), + Old=e:GetPos(), + }, + Velocity={ + //New=g[2]:LocalToWorld(g[1]:WorldToLocal(-1*vel+g[1]:GetPos())) - g[2]:GetPos(), + New=vel, //self.JumpTarget + e:WorldToLocal(vel+e:GetPos()), + Old=vel, + }, + Angles={ + Old=e:GetAngles(), + New=e:GetAngles()+a.Delta, + Delta=a.Delta, + }, + //Bones=self:GetBones(e), + } + -- ######### Calculate the heigh of the object, so it won't get stuck on the other side + /*local localmaxz = ( e:GetPos() - maxeent:GetPos() ).z + maxeent:BoundingRadius() + local localminz = ( e:GetPos() - minzent:GetPos() ).z - minzent:BoundingRadius() + local trace = util.TraceLine({ + start = ret.Entity.Position.New + Vector(0,0,localmaxz), + endpos = ret.Entity.Position.New - Vector(0,0,localminz), + }) + + if (trace.HitWorld) and (trace.Fraction == 0) then + //local add_height = localminz - trace.HitPos.z + local add_height = 5 + trace.Fraction * ( localmaxz - localminz ); + ret.Entity.Position.New = ret.Entity.Position.New + Vector(0,0,add_height); + end*/ + + /*local height = 60; + local trace={ + util.TraceLine({ + start=e:GetPos(), + endpos=e:GetPos()-Vector(0,0,height), + filter=self.Entity, + }), + util.TraceLine({ + start=ret.Entity.Position.New+Vector(0,0,height), + endpos=ret.Entity.Position.New-Vector(0,0,height), + filter=self.Entity, + }), + } + if(trace[1].Hit and trace[2].Hit) then + local add_height = 5 + (1 - 2*trace[2].Fraction + trace[1].Fraction)*height; + ret.Entity.Position.New = ret.Entity.Position.New + Vector(0,0,add_height); + end*/ + return ret; +end + +--################# Retrieves the valid entites for a teleport from an ent +function ENT:GetEntitiesForTeleport(e) + if(self:Allowed(e)) then + local entities = {}; + local constraints = {}; -- We dont need constraints + --################# Attached Props and constraints + local attached = {}; + //DebugDuplicator.GetAllConstrainedEntities(e,attached[1],attached[2]); + AdvDupe.GetAllEnts( e, attached, {}, {} ) + --################# Check, if the prop is attached to the gate (like hoverballs) and disallow it's teleportataion then + local allow = true; + /*for _,v in pairs(attached[1]) do + if(v == self.Entity) then + allow = false; + break; + end + end*/ + --################# Filter specific entities + if(allow) then + --################# Attached props filter + /*local allow = true; + for _,v in pairs(attached[1]) do + if(v:GetClass() == "gmod_spawner") then + allow = false; + break; + end + end*/ + //if(allow) then + for _,v in pairs(attached) do + //if(v:GetClass() ~= "gmod_spawner" and + if (v ~= e and self:Allowed(v)) then + table.insert(entities,v); + end + end + //end + --[[ -- Disabled - Not necsessary + --################# Constraint filter + for _,v in pairs(attached[2]) do + if(self:Allowed(v)) then + table.insert(constraints,v); + end + end + --]] + end + return {Entity=e,Attached=entities}; + else + return false; + end +end + +-- The awesome StarGate sounds, ftw! +/*ENT.snd = { + "stargate/gate_roll.mp3", + "stargate/chevron.mp3", + "stargate/chevron_inbound.mp3", + "stargate/gate_open.mp3", + "stargate/gate_travel.mp3", + "stargate/gate_close.mp3", + "stargate/teleport.mp3", + "stargate/chevron_inbound_lock.mp3", + "stargate/dial_fail.mp3", + "stargate/iris_open.mp3", + "stargate/iris_close.mp3", + "stargate/iris_hit.mp3", + "stargate/wormhole_loop.wav", -- Thx to appollo114 for sending me this sounds + "stargate/chevron2.mp3", -- Second engage sound + "stargate/chevron_lock.mp3", -- Chevron lock sound +}*/ + +--################# Teleportation function +function ENT:Teleport(tbl,base) + local g = {self.Entity,self.other_gate} -- Gates + local p = tbl.Position; + local b = tbl.Bones; + local e = tbl.Entity; + local a = tbl.Angles; + local v = tbl.Velocity; + local t = type(e):lower(); + if(e ~= base) then + p.New = base:LocalToWorld(p.New); + v.New = base:LocalToWorld(v.New)-base:GetPos(); + end + -- Now, rotate the velocity vector by 180 degrees around the Forward axis of the stargate + --v.New = math.RotationMatrix(g[2]:GetForward(),0,v.New); + e:SetNetworkedInt("last_stargate_teleport",CurTime()); + -- ######### Disable stucking (make the gates possible to pass threw) for some seconds + //g[1]:SetSolid(0); + //g[2]:SetSolid(0); + //timer.Create("StarGate_"..g[1]:EntIndex().."solid",0.8,1,g[1].SetSolid,g[1],6); + //timer.Create("StarGate_"..g[2]:EntIndex().."solid",0.8,1,g[2].SetSolid,g[2],6); + -- ######### Player teleport + if(t == "player") then + //if(not g[2]:IsBlocked()) then + -- Calculate correct viewangle + local ai = e:GetAimVector(); + local pitch = math.deg(math.acos(ai.z))-90; + ai.z=0; ai:Normalize(); + local parity = 1; -- This will handle, whether the y componet is below the x-axis in the unit-circle or not, so the angle has the right orientation + if(ai:Normalize().y <0) then + parity = -1; + end + local yaw = math.deg(math.acos(parity*ai.x))+(1-parity)*90; + e:SetNetworkedString("stargate_movetype",tonumber(e:GetNetworkedString("stargate_movetype")) or e:GetMoveType()); + e:SetMoveType(MOVETYPE_NOCLIP); -- Needed, or person dont get teleported correctly + timer.Create("RestoreMovetype"..e:EntIndex(),0.1,1, + function (p) + e:SetMoveType(tonumber(e:GetNetworkedString("stargate_movetype"))); + e:SetNetworkedString("stargate_movetype",""); + end + ,e); + e:SetPos(p.New); + e:SetEyeAngles(Angle(pitch,yaw+a.Delta.y,0)); + e:SetVelocity(v.New-v.Old); + /*else + e:StripWeapons(); + e:KillSilent(); + end*/ + end + -- ######### Entity teleport + if(t == "entity" or t == "npc" or t == "weapon") then + //if(not g[2]:IsBlocked()) then + -- Hoverball fix + if(e:GetClass() == "gmod_hoverball")then + local hp = (p.New-p.Old); + e.TargetZ = e.TargetZ + hp.z; -- Set changed hoverball heigh to the hoverball + end + + if (e:GetClass() == "gmod_toggleablehoverball") + or (e:GetClass() == "gmod_wire_hoverball") then + local hp = (p.New-p.Old); + e:SetTargetZ( e:GetTargetZ() + hp.z ); -- Set changed hoverball heigh to the hoverball + end + + local ph = e:GetPhysicsObject(); + -- ######### Teleport + e:SetPos(p.New); + if(t == "npc") then a.Delta.p = 0 a.Delta.r = 0 end -- Remove roll and pitch from NPCs + e:SetAngles(a.Old + a.Delta + Angle(0,0,0)); + e:SetVelocity(-1*v.Old) -- Substract old velocity first! + if(ph:IsValid()) then + local ma = ph:GetMass(); + timer.Create("prop_velocity_"..e:EntIndex(),0.05,1,ph.ApplyForceCenter,ph,v.New*ma); -- Apply power so it has velocity again + //ph:ApplyForceCenter(v.New*ma); -- Apply power so it has velocity again + else + -- Try another method (for grenades etc) + e:SetVelocity(v.New); + end + -- ######### Move the bones of the entity + if(b) then + for _,v in pairs(b) do + v.Entity:SetPos(e:LocalToWorld(v.Position)); + v.Entity:SetVelocity(e:LocalToWorld(v.Velocity)-e:GetPos()); + end + end + /*else + e:Remove(); + end*/ + end + -- ######### Vehicle teleport + if(t == "vehicle") then + //if(not self.other_gate:IsBlocked()) then + e:SetAngles(a.New + Angle(0,0,0)); + e:SetPos(p.New); + -- ######### Move the bones of the entity + for _,v in pairs(b) do + v.Entity:SetPos(e:LocalToWorld(v.Position)); + v.Entity:SetVelocity(e:LocalToWorld(v.Velocity)-e:GetPos()); + end + /*else + for _,p in pairs(player.GetAll()) do + if(p:GetParent() == e) then + p:StripWeapons(); + p:KillSilent(); + break; + end + end + e:Remove(); + end*/ + end + -- ######### Teleportation sounds (only for the base entity, not for the attached or you will have cummulated sounds) + if(e == base) then + //local mysound = ;self.snd[7] + //local yoursound = self.snd[7]; + /*if(g[1].irisclosed) then + mysound = self.snd[12]; + end + if(g[2].irisclosed) then + yoursound = self.other_gate.snd[12]; + self.other_gate:IrisHitEffect(); + end*/ + //timer.Create("StarGate_"..g[1]:EntIndex(),0,1,g[2].EmitSound,g[2],yoursound); + //timer.Create("StarGate_"..g[1]:EntIndex().."_other_gate",0,1,g[1].EmitSound,g[1],self.snd[7]); + -- ######### Debug + if(self.debug) then + player.GetByID(1):SendLua("DrawVector(Vector("..p.other.x+p.differ.x..","..p.other.y+p.differ.y..","..p.other.z+p.differ.z.."),Vector("..v.new2.x..","..v.new2.y..","..v.new2.z.."))"); + end + self.last_teleport = CurTime(); + end + -- ######### Use energy equivalent to the mass of the object + //if(e and e:IsValid()) then + //DoPropSpawnedEffect( e ) + /*local ph = e:GetPhysicsObject(); + if(ph and ph:IsValid()) then + self:UseEnergy(ph:GetMass(),true); + end*/ + //end +end + +if !math.RotationMatrix then //if we didn't get this function from some where else, define it now. + --################# Needed again to rotate the velocity correctly + function math.RotationMatrix(axis,angle,vector) + local a = axis; + local v = vector; + local p = math.rad(angle); + -- Regulary rotation matrix + local M = { + { + (math.cos(p) + (1-math.cos(p))*math.pow(a.x,2)), + ((1-math.cos(p))*a.x*a.y - math.sin(p)*a.z), + ((1-math.cos(p))*a.x*a.z+math.sin(p)*a.y), + }, + { + ((1-math.cos(p))*a.y*a.x+math.sin(p)*a.z), + (math.cos(p) + (1-math.cos(p))*math.pow(a.y,2)), + ((1-math.cos(p))*a.y*a.z - math.sin(p)*a.x), + }, + { + ((1-math.cos(p))*a.x*a.z - math.sin(p)*a.y), + ((1-math.cos(p))*a.z*a.y+math.sin(p)*a.x), + (math.cos(p) + (1-math.cos(p))*math.pow(a.z,2)), + } + } + -- Matrix/vector multiplication + local r = Vector(); + for i=1,3 do + r[i] = v.x*M[i][1] + v.y*M[i][2] + v.z*M[i][3]; + end + return r; + end +end diff --git a/lua/entities/gmod_wire_hoverdrivecontroler/shared.lua b/lua/entities/gmod_wire_hoverdrivecontroler/shared.lua new file mode 100644 index 0000000000..247ba70b95 --- /dev/null +++ b/lua/entities/gmod_wire_hoverdrivecontroler/shared.lua @@ -0,0 +1,46 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Hover Drive Controller" +ENT.Author = "TAD2020" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = true +ENT.AdminSpawnable = false + +cleanup.Register("hoverdrivecontrolers") + +function ENT:GetTargetZ() + return self.Entity:GetNetworkedInt( 0 ) +end +function ENT:SetTargetZ( z ) + return self.Entity:SetNetworkedInt( 0, z ) +end + + +function ENT:GetSpeed() + + // Sensible limits + if (!SinglePlayer()) then + return math.Clamp( self.Entity:GetNetworkedFloat( 1 ), 0.0, 10.0 ) + end + + return self.Entity:GetNetworkedFloat( 1 ) +end + +function ENT:SetSpeed( s ) + + self.Entity:SetNetworkedFloat( 1, s ) + +end + + +function ENT:GetHoverMode() + return self.Entity:GetNetworkedInt( 2 ) +end + +function ENT:SetHoverMode( h ) + return self.Entity:SetNetworkedInt( 2, h ) +end diff --git a/lua/entities/gmod_wire_hudindicator/cl_init.lua b/lua/entities/gmod_wire_hudindicator/cl_init.lua new file mode 100644 index 0000000000..0cf3e2c163 --- /dev/null +++ b/lua/entities/gmod_wire_hudindicator/cl_init.lua @@ -0,0 +1,293 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +local hudindicators = {} +// Default HUD x/y +local hudx = 22 +local hudy = 200 +local nextupdate = 0 +// Text Height Constant +local dtextheight = draw.GetFontHeight("Default") +// So we don't need to calculate this every frame w/ Percent Bar style +local pbarheight = dtextheight + 16 +// Y Offset constants +local offsety = {32, 32, 32, 92 + dtextheight, 60 + dtextheight} +// Texture IDs for Full/Semi-Circle styles +local fullcircletexid = surface.GetTextureID("hudindicator/hi_fullcircle") +local semicircletexid = surface.GetTextureID("hudindicator/hi_semicircle") + +// Function to check if a registered HUD Indicator: +// A) belongs to someone other than the calling LocalPlayer() +// B) is not registered as pod-only +function ENT:ClientCheckRegister() + local ply = LocalPlayer() + local plyuid = ply:UniqueID() + return (ply != self:GetPlayer() && !self.Entity:GetNetworkedBool(plyuid)) +end + +// Used by STool for unregister control panel +// Only allowed to unregister HUD Indicators that aren't yours +// and for those that aren't pod-only registers +function HUDIndicator_GetCurrentRegistered() + local registered = {} + for eindex,_ in pairs(hudindicators) do + local ent = ents.GetByIndex(eindex) + if (ent && ent:IsValid()) then + if (ent:CheckClientRegister()) then + local entry = {} + entry.EIndex = eindex + entry.Description = hudindicators[eindex].Description + table.insert(registered, entry) + end + end + end + + return registered +end + +local function DrawHUDIndicators() + if (!LocalPlayer():Alive()) then return end + + local currenty = hudy + + // Now draw HUD Indicators + for _, index in ipairs(table.MakeSortedKeys(hudindicators)) do + if (hudindicators[index]) then // Is this necessary? + local ent = ents.GetByIndex(index) + + if (ent && ent:IsValid()) then + local indinfo = hudindicators[index] + if (!indinfo.HideHUD && indinfo.Ready) then + local txt = indinfo.FullText or "" + + if (indinfo.Style == 0) then // Basic + draw.WordBox(8, hudx, currenty, txt, "Default", Color(50, 50, 75, 192), Color(255, 255, 255, 255)) + elseif (indinfo.Style == 1) then // Gradient + /*local r, g, b, a = ent:GetColor() + local textcolor = Color(255, 255, 255, 255) + if (r >= 192 && g >= 192 && b >= 192) then + // Draw dark text for very bright Indicator colors + textcolor = Color(32, 32, 32, 255) + end*/ + + draw.WordBox(8, hudx, currenty, txt, "Default", indinfo.DisplayColor, indinfo.TextColor) + elseif (indinfo.Style == 2) then // Percent Bar + //surface.SetFont("Default") + //local pbarwidth, h = surface.GetTextSize(txt) + //pbarwidth = math.max(pbarwidth + 16, 100) // The extra 16 pixels is a "buffer" to make it look better + local startx = hudx + //local w1 = math.floor(indinfo.Factor * pbarwidth) + //local w2 = math.ceil(pbarwidth - w1) + local pbarwidth = indinfo.BoxWidth + local w1 = indinfo.W1 + local w2 = indinfo.W2 + if (indinfo.Factor > 0) then // Draw only if we have a factor + local BColor = indinfo.BColor + surface.SetDrawColor(BColor.r, BColor.g, BColor.b, 160) + surface.DrawRect(startx, currenty, w1, pbarheight) + startx = w1 + hudx + end + + if (indinfo.Factor < 1) then + local AColor = indinfo.AColor + surface.SetDrawColor(AColor.r, AColor.g, AColor.b, 160) + surface.DrawRect(startx, currenty, w2, pbarheight) + end + + // Center the description (+ value if applicable) on the percent bar + draw.SimpleText(txt, "Default", hudx + (pbarwidth / 2), currenty + (pbarheight / 2), Color(255, 255, 255, 255), 1, 1) + elseif (indinfo.Style == 3) then // Full Circle Gauge + draw.RoundedBox(8, hudx, currenty, indinfo.BoxWidth, 88 + dtextheight, Color(50, 50, 75, 192)) + + surface.SetTexture(fullcircletexid) + surface.DrawTexturedRect(hudx + 8, currenty + 8, 64, 64) + + local startx = hudx + 40 + local starty = currenty + 40 + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawLine(startx, starty, startx + indinfo.LineX, starty + indinfo.LineY) + + // Now the text + draw.SimpleText(txt, "Default", hudx + (indinfo.BoxWidth / 2), currenty + 72 + (pbarheight / 2), Color(255, 255, 255, 255), 1, 1) + elseif (indinfo.Style == 4) then // Semi-Circle Gauge + draw.RoundedBox(8, hudx, currenty, indinfo.BoxWidth, 56 + dtextheight, Color(50, 50, 75, 192)) + + surface.SetTexture(semicircletexid) + surface.DrawTexturedRect(hudx + 8, currenty + 8, 64, 32) + + local startx = hudx + 40 + local starty = currenty + 39 + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawLine(startx, starty, startx + indinfo.LineX, starty + indinfo.LineY) + + // Now the text + draw.SimpleText(txt, "Default", hudx + (indinfo.BoxWidth / 2), currenty + 40 + (pbarheight / 2), Color(255, 255, 255, 255), 1, 1) + end + + // Go to next "line" + currenty = currenty + offsety[indinfo.Style + 1] + end + else + // Clear this from the table so we don't check again + hudindicators[index] = nil + end + end + end +end +hook.Add("HUDPaint", "DrawHUDIndicators", DrawHUDIndicators) + +local function HUDFormatDescription( eindex ) + // This is placed here so we don't have to update + // the description more often than is necessary + local indinfo = hudindicators[eindex] + if (indinfo.ShowValue == 0) then // No Value + hudindicators[eindex].FullText = indinfo.Description + elseif (indinfo.ShowValue == 1) then // Percent + hudindicators[eindex].FullText = indinfo.Description.." ("..string.format("%.1f", ((indinfo.Factor or 0) * 100)).."%)" + elseif (indinfo.ShowValue == 2) then // Value + // Round to up to 2 places + hudindicators[eindex].FullText = indinfo.Description.." ("..string.format("%g", math.Round((indinfo.Value or 0) * 100) / 100)..")" + end + + // Do any extra processing for certain HUD styles + // so we aren't calculating this every frame + surface.SetFont("Default") + local textwidth, _ = surface.GetTextSize(hudindicators[eindex].FullText) + + if (indinfo.Style == 1) then // Gradient + local ent = ents.GetByIndex(eindex) + if (ent && ent:IsValid()) then + local r, g, b, _ = ent:GetColor() + hudindicators[eindex].DisplayColor = Color(r, g, b, 160) + + local textcolor = Color(255, 255, 255, 255) + if (r >= 192 && g >= 192 && b >= 192) then + // Draw dark text for very bright Indicator colors + textcolor = Color(32, 32, 32, 255) + end + + hudindicators[eindex].TextColor = textcolor + end + elseif (indinfo.Style == 2) then // Percent Bar + local pbarwidth = math.max(textwidth + 16, 100) // The extra 16 pixels is a "buffer" to make it look better + hudindicators[eindex].BoxWidth = pbarwidth + hudindicators[eindex].W1 = math.floor((indinfo.Factor or 0) * pbarwidth) + hudindicators[eindex].W2 = math.ceil(pbarwidth - hudindicators[eindex].W1) + elseif (indinfo.Style == 3) then // Full Circle Gauge + local ang = math.rad(math.fmod((indinfo.Factor or 0) * 360 + (indinfo.FullCircleAngle or 0), 360)) + hudindicators[eindex].LineX = math.cos(ang) * 32 + hudindicators[eindex].LineY = math.sin(ang) * 32 + hudindicators[eindex].BoxWidth = math.max(textwidth + 16, 80) + elseif (indinfo.Style == 4) then // Semi-Circle Gauge + local ang = math.rad((indinfo.Factor or 0) * 180 + 180) + hudindicators[eindex].LineX = math.cos(ang) * 32 + hudindicators[eindex].LineY = math.sin(ang) * 32 + hudindicators[eindex].BoxWidth = math.max(textwidth + 16, 80) + end +end + +// Function to ensure that the respective table index is created before any elements are added or modified +// The HUDIndicatorRegister umsg is *supposed* to arrive (and be processed) before all the others, +// but for some reason (probably net lag or whatever) it isn't (TheApathetic) +local function CheckHITableElement(eindex) + if (!hudindicators[eindex]) then + hudindicators[eindex] = {} + end +end + +// UserMessage stuff +local function HUDIndicatorRegister( um ) + local eindex = um:ReadShort() + CheckHITableElement(eindex) + + hudindicators[eindex].Description = um:ReadString() + hudindicators[eindex].ShowValue = um:ReadShort() + local tempstyle = um:ReadShort() + if (!hudindicators[eindex].Style || hudindicators[eindex].Style != tempstyle) then + hudindicators[eindex].Ready = false // Make sure that everything's ready first before drawing + end + hudindicators[eindex].Style = tempstyle + + if (!hudindicators[eindex].Factor) then // First-time register + hudindicators[eindex].Factor = 0 + hudindicators[eindex].Value = 0 + hudindicators[eindex].HideHUD = false + hudindicators[eindex].BoxWidth = 100 + end + HUDFormatDescription( eindex ) +end +usermessage.Hook("HUDIndicatorRegister", HUDIndicatorRegister) + +local function HUDIndicatorUnRegister( um ) + local eindex = um:ReadShort() + hudindicators[eindex] = nil +end +usermessage.Hook("HUDIndicatorUnRegister", HUDIndicatorUnRegister) + +local function HUDIndicatorFactor( um ) + local eindex = um:ReadShort() + CheckHITableElement(eindex) + + hudindicators[eindex].Factor = um:ReadFloat() + hudindicators[eindex].Value = um:ReadFloat() + HUDFormatDescription( eindex ) +end +usermessage.Hook("HUDIndicatorFactor", HUDIndicatorFactor) + +local function HUDIndicatorHideHUD( um ) + local eindex = um:ReadShort() + CheckHITableElement(eindex) + + hudindicators[eindex].HideHUD = um:ReadBool() +end +usermessage.Hook("HUDIndicatorHideHUD", HUDIndicatorHideHUD) + +local function HUDIndicatorStylePercent( um ) + local eindex = um:ReadShort() + local ainfo = string.Explode("|", um:ReadString()) + local binfo = string.Explode("|", um:ReadString()) + CheckHITableElement(eindex) + + hudindicators[eindex].AColor = { r = ainfo[1], g = ainfo[2], b = ainfo[3]} + hudindicators[eindex].BColor = { r = binfo[1], g = binfo[2], b = binfo[3]} +end +usermessage.Hook("HUDIndicatorStylePercent", HUDIndicatorStylePercent) + +local function HUDIndicatorStyleFullCircle( um ) + local eindex = um:ReadShort() + CheckHITableElement(eindex) + + hudindicators[eindex].FullCircleAngle = um:ReadFloat() + HUDFormatDescription( eindex ) // So the gauge updates with FullCircleAngle factored in +end +usermessage.Hook("HUDIndicatorStyleFullCircle", HUDIndicatorStyleFullCircle) + +// Check for updates every 1/5 seconds +local function HUDIndicatorCheck() + if (CurTime() < nextupdate) then return end + + nextupdate = CurTime() + 0.20 + // Keep x/y within range (the 50 and 100 are arbitrary and may change) + hudx = math.Clamp(GetConVarNumber("wire_hudindicator_hudx") or 22, 0, ScrW() - 50) + hudy = math.Clamp(GetConVarNumber("wire_hudindicator_hudy") or 200, 0, ScrH() - 100) + + // Now check readiness + for eindex,indinfo in pairs(hudindicators) do + if (!indinfo.Ready) then + if (indinfo.Style == 0) then // Basic + hudindicators[eindex].Ready = true // Don't need to do any additional checks + elseif (indinfo.Style == 1) then // Gradient + hudindicators[eindex].Ready = (indinfo.DisplayColor && indinfo.TextColor) + elseif (indinfo.Style == 2) then // Percent Bar + hudindicators[eindex].Ready = (indinfo.BoxWidth && indinfo.W1 && indinfo.W2 && indinfo.AColor && indinfo.BColor) + elseif (indinfo.Style == 3) then // Full Circle Gauge + hudindicators[eindex].Ready = (indinfo.BoxWidth && indinfo.LineX && indinfo.LineY && indinfo.FullCircleAngle) + elseif (indinfo.Style == 4) then // Semi-Circle Gauge + hudindicators[eindex].Ready = (indinfo.BoxWidth && indinfo.LineX && indinfo.LineY) + end + end + end +end +hook.Add("Think", "WireHUDIndicatorCVarCheck", HUDIndicatorCheck) diff --git a/lua/entities/gmod_wire_hudindicator/init.lua b/lua/entities/gmod_wire_hudindicator/init.lua new file mode 100644 index 0000000000..95ec75a337 --- /dev/null +++ b/lua/entities/gmod_wire_hudindicator/init.lua @@ -0,0 +1,366 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "HUD Indicator" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.A = 0 + self.AR = 0 + self.AG = 0 + self.AB = 0 + self.AA = 0 + self.B = 0 + self.BR = 0 + self.BG = 0 + self.BB = 0 + self.BA = 0 + + // List of players who have hooked this indicator + self.RegisteredPlayers = {} + self.PrefixText = "(Hud) Color = " + + self.Inputs = Wire_CreateInputs(self.Entity, { "A", "HideHUD" }) +end + +function ENT:Setup(a, ar, ag, ab, aa, b, br, bg, bb, ba) + self.A = a or 0 + self.AR = ar or 255 + self.AG = ag or 0 + self.AB = ab or 0 + self.AA = aa or 255 + self.B = b or 1 + self.BR = br or 0 + self.BG = bg or 255 + self.BB = bb or 0 + self.BA = ba or 255 + + // Why is this here? (TheApathetic) + //local factor = math.max(0, math.min(self.Inputs.A.Value-self.A/(self.B-self.A), 1)) + // Moved to HUDSetup() to ensure that the entity is + // registered in the HUD before the first value is passed + //self:TriggerInput("A", 0) +end + +// For HUD Indicators +function ENT:HUDSetup(showinhud, huddesc, hudaddname, hudshowvalue, hudstyle, allowhook, fullcircleangle) + local ply = self:GetPlayer() + local eindex = self.Entity:EntIndex() + // If user updates with the STool to take indicator off of HUD + if (!showinhud && self.ShowInHUD) then + self:UnRegisterPlayer(ply) + + // Adjust inputs back to normal + //Wire_AdjustInputs(self.Entity, { "A" }) + elseif (showinhud) then + // Basic style is useless without a value + // to show so set a default if necessary + if (hudstyle == 0 && hudshowvalue == 0) then + hudshowvalue = 1 + end + + if (!self:CheckRegister(ply)) then + // First-time register + // Updating this player is handled further down + self:RegisterPlayer(ply, true) + end + + // Add name if desired + if (hudaddname) then + self.Entity:SetNetworkedString("WireName", huddesc) + elseif (self.Entity:GetNetworkedString("WireName") == huddesc) then + // Only remove it if the HUD Description was there + // because there might be another name on it + self.Entity:SetNetworkedString("WireName", "") + end + + // Adjust inputs accordingly + /* if (!self.Inputs.HideHUD) then + Wire_AdjustInputs(self.Entity, { "A", "HideHUD" }) + self:TriggerInput("HideHUD", 0) + self.PrevHideHUD = false + end */ + end + + self.ShowInHUD = showinhud + self.HUDDesc = huddesc + self.HUDAddName = hudaddname + self.HUDShowValue = hudshowvalue + self.HUDStyle = hudstyle + self.AllowHook = allowhook + self.FullCircleAngle = fullcircleangle + + // To tell if you can hook a HUD Indicator at a glance + if (allowhook) then + self.PrefixText = "(Hud) Color = " + else + self.PrefixText = "(Hud - Locked) Color = " + end + + // Update all registered players with this info + for k,v in pairs(self.RegisteredPlayers) do + self:RegisterPlayer(v.ply, v.hookhidehud) + end + + // Only trigger this input on the + // first time that Setup() is called + if (!self.HasBeenSetup) then + self:TriggerInput("A", self.A) + self:TriggerInput("HideHUD", 0) + self.PrevHideHUD = false + self.HasBeenSetup = true + end +end + +// This is called from RegisterPlayer to send any style-specific info +function ENT:SetupHUDStyle(hudstyle, rplayer) + // 0 (Basic) and 1 (Gradient) don't require any extra info + local pl = rplayer or self:GetPlayer() + // Allow for hooked players + //if (rplayer) then pl = rplayer end + + if (hudstyle == 2) then // Percent Bar + // Send as string (there should be a way to send colors) + local ainfo = self.AR.."|"..self.AG.."|"..self.AB + local binfo = self.BR.."|"..self.BG.."|"..self.BB + umsg.Start("HUDIndicatorStylePercent", pl) + umsg.Short(self.Entity:EntIndex()) + umsg.String(ainfo) + umsg.String(binfo) + umsg.End() + elseif (hudstyle == 3) then // Full Circle Gauge + umsg.Start("HUDIndicatorStyleFullCircle", pl) + umsg.Short(self.Entity:EntIndex()) + umsg.Float(self.FullCircleAngle) + umsg.End() + end +end + +// Hook this player to the HUD Indicator +function ENT:RegisterPlayer(ply, hookhidehud, podonly) + local plyuid = ply:UniqueID() + local eindex = self.Entity:EntIndex() + + // If player is already registered, this will send an update + // The podonly is used for players who are registered only because they are in a linked pod + if (!self.RegisteredPlayers[plyuid]) then + self.RegisteredPlayers[plyuid] = { ply = ply, hookhidehud = hookhidehud, podonly = podonly } + // This is used to check for pod-only status in ClientCheckRegister() + self.Entity:SetNetworkedBool( plyuid, util.tobool(podonly) ) + end + + umsg.Start("HUDIndicatorRegister", ply) + umsg.Short(eindex) + umsg.String(self.HUDDesc or "") + umsg.Short(self.HUDShowValue) + umsg.Short(self.HUDStyle) + umsg.End() + self:SetupHUDStyle(self.HUDStyle, ply) + + // Trigger inputs to fully add this player to the list + // Force factor to update + self.PrevOutput = nil + self:TriggerInput("A", self.Inputs.A.Value) + if (hookhidehud) then + self:TriggerInput("HideHUD", self.Inputs.HideHUD.Value) + end +end + +function ENT:UnRegisterPlayer(ply) + umsg.Start("HUDIndicatorUnRegister", ply) + umsg.Short(self.Entity:EntIndex()) + umsg.End() + self.RegisteredPlayers[ply:UniqueID()] = nil +end + +// Is this player registered? +function ENT:CheckRegister(ply) + return (self.RegisteredPlayers[ply:UniqueID()] != nil) +end + +// Is this player registered only because he is in a linked pod? +function ENT:CheckPodOnly(ply) + local plyuid = ply:UniqueID() + return (self.RegisteredPlayers[plyuid] != nil && self.RegisteredPlayers[plyuid].podonly) +end + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + local factor = math.Clamp((value-self.A)/(self.B-self.A), 0, 1) + self:ShowOutput(factor, value) + + local r = math.Clamp((self.BR-self.AR)*factor+self.AR, 0, 255) + local g = math.Clamp((self.BG-self.AG)*factor+self.AG, 0, 255) + local b = math.Clamp((self.BB-self.AB)*factor+self.AB, 0, 255) + local a = math.Clamp((self.BA-self.AA)*factor+self.AA, 0, 255) + self.Entity:SetColor(r, g, b, a) + elseif (iname == "HideHUD") then + if (self.PrevHideHUD == (value > 0)) then return end + + self.PrevHideHUD = (value > 0) + // Value has updated, so send information + self:SendHUDInfo(self.PrevHideHUD) + end +end + +function ENT:ShowOutput(factor, value) + if (factor ~= self.PrevOutput) then + self:SetOverlayText( self.PrefixText .. string.format("%.1f", (factor * 100)) .. "%" ) + self.PrevOutput = factor + + local rf = RecipientFilter() + local pl = self:GetPlayer() + + // RecipientFilter will contain all registered players + for index,rplayer in pairs(self.RegisteredPlayers) do + if (rplayer.ply) then + if (rplayer.ply != pl || (self.ShowInHUD || self.PodPly == pl)) then + rf:AddPlayer(rplayer.ply) + end + else + self.RegisteredPlayers[index] = nil + end + end + + umsg.Start("HUDIndicatorFactor", rf) + umsg.Short(self.Entity:EntIndex()) + // Send both to ensure that all styles work properly + umsg.Float(factor) + umsg.Float(value) + umsg.End() + end +end + +function ENT:SendHUDInfo(hidehud) + // Sends information to player + local pl = self:GetPlayer() + + for index,rplayer in pairs(self.RegisteredPlayers) do + if (rplayer.ply) then + if (rplayer.ply != pl || (self.ShowInHUD || self.PodPly == pl)) then + umsg.Start("HUDIndicatorHideHUD", rplayer.ply) + umsg.Short(self.Entity:EntIndex()) + // Check player's preference + if (rplayer.hookhidehud) then + umsg.Bool(hidehud) + else + umsg.Bool(false) + end + umsg.End() + end + else + self.RegisteredPlayers[index] = nil + end + end +end + +// Despite everything being named "pod", any vehicle will work +function ENT:LinkVehicle(pod) + if (!pod || !pod:IsValid() || !string.find(pod:GetClass(), "prop_vehicle_")) then return false end + + local ply = nil + // Check if a player is in pod first + for k,v in pairs(player.GetAll()) do + if (v:GetVehicle() == pod) then + ply = v + break + end + end + + if (ply && !self:CheckRegister(ply)) then + // Register as "only in pod" if not registered before + self:RegisterPlayer(ply, false, true) + + // Force factor to update + self.PrevOutput = nil + self:TriggerInput("A", self.Inputs.A.Value) + end + self.Pod = pod + self.PodPly = ply + + return true +end + +function ENT:UnLinkVehicle() + local ply = self.PodPly + + if (ply && self:CheckPodOnly(ply)) then + // Only unregister if player is registered only because he is in a linked pod + self:UnRegisterPlayer(ply) + end + self.Pod = nil + self.PodPly = nil +end + +function ENT:Think() + self.BaseClass.Think(self) + + if (self.Pod && self.Pod:IsValid()) then + local ply = nil + + if (!self.PodPly || self.PodPly:GetVehicle() != self.Pod) then + for k,v in pairs(player.GetAll()) do + if (v:GetVehicle() == self.Pod) then + ply = v + break + end + end + else + ply = self.PodPly + end + + // Has the player changed? + if (ply != self.PodPly) then + if (self.PodPly && self:CheckPodOnly(self.PodPly)) then // Don't send umsg if player disconnected or is registered otherwise + self:UnRegisterPlayer(self.PodPly) + end + + self.PodPly = ply + + if (self.PodPly && !self:CheckRegister(self.PodPly)) then + self:RegisterPlayer(self.PodPly, false, true) + + // Force factor to update + self.PrevOutput = nil + self:TriggerInput("A", self.Inputs.A.Value) + end + end + else + // If we deleted this pod and there was a player in it + if (self.PodPly && self:CheckPodOnly(self.PodPly)) then + self:UnRegisterPlayer(self.PodPly) + end + self.PodPly = nil + end + + self.Entity:NextThink(CurTime() + 0.1) + return true +end + +// Advanced Duplicator Support +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.Pod) and (self.Pod:IsValid()) then + info.pod = self.Pod:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.pod) then + self.Pod = GetEntByID(info.pod) + if (!self.Pod) then + self.Pod = ents.GetByIndex(info.pod) + end + end +end diff --git a/lua/entities/gmod_wire_hudindicator/shared.lua b/lua/entities/gmod_wire_hudindicator/shared.lua new file mode 100644 index 0000000000..1b06578d02 --- /dev/null +++ b/lua/entities/gmod_wire_hudindicator/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire HUD Indicator" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_hydraulic/cl_init.lua b/lua/entities/gmod_wire_hydraulic/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_hydraulic/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_hydraulic/init.lua b/lua/entities/gmod_wire_hydraulic/init.lua new file mode 100644 index 0000000000..5a5f905fb4 --- /dev/null +++ b/lua/entities/gmod_wire_hydraulic/init.lua @@ -0,0 +1,117 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +ENT.WireDebugName = "Hydraulic" + +include('shared.lua') + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs( self.Entity, { "Length" } ) + self.Outputs = Wire_CreateOutputs( self.Entity, { "Length" } ) + + self.Trigger = 0 +end + +function ENT:Think() + local c = self.constraint + if(not (c and c:IsValid())) then return end; + local p1 = self:GetWPos(c:GetTable().Ent1, c:GetTable().Phys1, c:GetTable().LPos1) + local p2 = self:GetWPos(c:GetTable().Ent2, c:GetTable().Phys2, c:GetTable().LPos2) + + Wire_TriggerOutput(self.Entity, "Length", (p1 - p2):Length()) + self.Entity:NextThink(CurTime()+0.04) +end + +function ENT:Setup() + self.current_length = 0 + self.IsOn = true +end + + +function ENT:GetWPos( ent, phys, lpos ) + if (ent:EntIndex() == 0) then + return lpos + end + + if (phys) and (phys:IsValid()) then + return phys:LocalToWorld( lpos ) + else + return ent:LocalToWorld( lpos ) + end +end + + +function ENT:SetConstraint( c ) + self.constraint = c + + local p1 = self:GetWPos(c:GetTable().Ent1, c:GetTable().Phys1, c:GetTable().LPos1) + local p2 = self:GetWPos(c:GetTable().Ent2, c:GetTable().Phys2, c:GetTable().LPos2) + local dist = (p1 - p2) + + self:ShowOutput( dist:Length() ) + self.constraint:Fire("SetSpringLength", self.current_length, 0) + if self.rope then self.rope:Fire("SetLength", self.current_length, 0) end +end + + +function ENT:SetRope( r ) + self.rope = r +end + + +function ENT:TriggerInput(iname, value) + if (iname == "Length") then + self:ShowOutput( math.max(1, value) ) + end +end + + +function ENT:ShowOutput( Length ) + if ( Length ~= self.current_length and self.constraint ) then + self:SetOverlayText( "Hydraulic length : " .. Length ) + self.current_length = Length + self.constraint:Fire("SetSpringLength", self.current_length, 0) + if self.rope then self.rope:Fire("SetLength", self.current_length, 0) end + end +end + + +/*function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.constraint) and (self.constraint:IsValid()) then + info.constraint = self.constraint:EntIndex() + end + if (self.rope) and (self.rope:IsValid()) then + info.rope = self.rope:EntIndex() + end + + return info +end*/ + + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID, GetConstByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID, GetConstByID) + + if (GetConstByID) then + if (info.constraint) and (info.constraint > 0) then + local const = GetConstByID(info.constraint) + if (const) then + self:SetConstraint(const) + end + end + + if (info.rope) and (info.rope > 0) then + local rope = GetConstByTable(info.rope) + if (rope) then + self:SetConstraint(rope) + end + end + end +end + diff --git a/lua/entities/gmod_wire_hydraulic/shared.lua b/lua/entities/gmod_wire_hydraulic/shared.lua new file mode 100644 index 0000000000..f89f29f8bc --- /dev/null +++ b/lua/entities/gmod_wire_hydraulic/shared.lua @@ -0,0 +1,37 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Hydraulic Controller" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_igniter/cl_init.lua b/lua/entities/gmod_wire_igniter/cl_init.lua new file mode 100644 index 0000000000..32f8433103 --- /dev/null +++ b/lua/entities/gmod_wire_igniter/cl_init.lua @@ -0,0 +1,6 @@ +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_igniter/init.lua b/lua/entities/gmod_wire_igniter/init.lua new file mode 100644 index 0000000000..4959405edc --- /dev/null +++ b/lua/entities/gmod_wire_igniter/init.lua @@ -0,0 +1,98 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Igniter" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, { "A", "Length" }) + self.IgniteLength = 10 + self.TargetPlayers = false + self:SetBeamLength(2048) + self:ShowOutput() +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Setup(trgply,Range) + self.TargetPlayers = trgply + self:SetBeamLength(Range) +end + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + if (value ~= 0) then + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamLength()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + local svarTargetPlayers = false + if(GetConVarNumber('sbox_wire_igniters_allowtrgply') > 0)then + svarTargetPlayers = true + else + svarTargetPlayers = false + end + + if (!trace.Entity) then return false end + if (!trace.Entity:IsValid() ) then return false end + if (trace.Entity:IsPlayer() && (!self.TargetPlayers || !svarTargetPlayers)) then return false end + if (trace.Entity:IsWorld()) then return false end + if ( CLIENT ) then return true end + trace.Entity:Extinguish() + trace.Entity:Ignite( self.IgniteLength, 0 ) + end + else + if(iname == "Length") then + self.IgniteLength = math.min(value,GetConVarNumber("sbox_wire_igniters_maxlen")) + end + end +end + +function ENT:ShowOutput() + self:SetOverlayText( "Ingiter" ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + + +function MakeWireIgniter( pl, Pos, Ang, model, TargetPlayers, Range ) + if not pl:CheckLimit( "wire_igniters" ) then return false end + + local wire_igniter = ents.Create( "gmod_wire_igniter" ) + if not wire_igniter:IsValid() then return false end + + wire_igniter:SetAngles( Ang ) + wire_igniter:SetPos( Pos ) + wire_igniter:SetModel( model ) + wire_igniter:Spawn() + wire_igniter:Setup(TargetPlayers,Range) + + wire_igniter:GetTable():SetPlayer( pl ) + + local ttable = { + TargetPlayers = TargetPlayers, + Range = Range, + pl = pl + } + table.Merge(wire_igniter:GetTable(), ttable ) + + pl:AddCount( "wire_igniters", wire_igniter ) + + return wire_igniter +end + +duplicator.RegisterEntityClass("gmod_wire_igniter", MakeWireIgniter, "Pos", "Ang", "Model", "TargetPlayers", "Range") diff --git a/lua/entities/gmod_wire_igniter/shared.lua b/lua/entities/gmod_wire_igniter/shared.lua new file mode 100644 index 0000000000..847b679b5f --- /dev/null +++ b/lua/entities/gmod_wire_igniter/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Igniter" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamLength(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_indicator/cl_init.lua b/lua/entities/gmod_wire_indicator/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_indicator/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_indicator/init.lua b/lua/entities/gmod_wire_indicator/init.lua new file mode 100644 index 0000000000..45d1489740 --- /dev/null +++ b/lua/entities/gmod_wire_indicator/init.lua @@ -0,0 +1,172 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Indicator" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.a = 0 + self.ar = 0 + self.ag = 0 + self.ab = 0 + self.aa = 0 + self.b = 0 + self.br = 0 + self.bg = 0 + self.bb = 0 + self.ba = 0 + + self.Inputs = Wire_CreateInputs(self.Entity, { "A" }) +end + +function ENT:Setup(a, ar, ag, ab, aa, b, br, bg, bb, ba, material) + self.a = a or 0 + self.ar = ar or 255 + self.ag = ag or 0 + self.ab = ab or 0 + self.aa = aa or 255 + self.b = b or 1 + self.br = br or 0 + self.bg = bg or 255 + self.bb = bb or 0 + self.ba = ba or 255 + self:SetMaterial( material ) + + local factor = math.max(0, math.min(self.Inputs.A.Value-self.a/(self.b-self.a), 1)) + self:TriggerInput("A", 0) +end + +function ENT:TriggerInput(iname, value) + if iname == "A" then + local factor = math.Clamp((value-self.a)/(self.b-self.a), 0, 1) + self:ShowOutput(factor) + + local r = math.Clamp((self.br-self.ar)*factor+self.ar, 0, 255) + local g = math.Clamp((self.bg-self.ag)*factor+self.ag, 0, 255) + local b = math.Clamp((self.bb-self.ab)*factor+self.ab, 0, 255) + local a = math.Clamp((self.ba-self.aa)*factor+self.aa, 0, 255) + self.Entity:SetColor(r, g, b, a) + end +end + +function ENT:ShowOutput(value) + if value ~= self.PrevOutput then + self:SetOverlayText( "Color = " .. string.format("%.1f", (value * 100)) .. "%" ) + self.PrevOutput = value + end +end + + +function MakeWireIndicator( pl, Pos, Ang, model, a, ar, ag, ab, aa, b, br, bg, bb, ba, material, nocollide, frozen ) + if not pl:CheckLimit( "wire_indicators" ) then return false end + + local wire_indicator = ents.Create( "gmod_wire_indicator" ) + if not wire_indicator:IsValid() then return false end + + wire_indicator:SetModel( model ) + wire_indicator:SetAngles( Ang ) + wire_indicator:SetPos( Pos ) + wire_indicator:Spawn() + + wire_indicator:Setup(a, ar, ag, ab, aa, b, br, bg, bb, ba, material) + wire_indicator:SetPlayer(pl) + + if wire_indicator:GetPhysicsObject():IsValid() then + wire_indicator:GetPhysicsObject():EnableMotion(!frozen) + end + if nocollide == true then + wire_indicator:SetCollisionGroup(COLLISION_GROUP_WORLD) + end + + local ttable = { + material = material, + pl = pl, + nocollide = nocollide + } + table.Merge(wire_indicator:GetTable(), ttable ) + + pl:AddCount( "wire_indicators", wire_indicator ) + + return wire_indicator +end + +duplicator.RegisterEntityClass("gmod_wire_indicator", MakeWireIndicator, "Pos", "Ang", "Model", "a", "ar", "ag", "ab", "aa", "b", "br", "bg", "bb", "ba", "material", "nocollide", "frozen") + +function MakeWire7Seg( pl, Pos, Ang, Model, a, ar, ag, ab, aa, b, br, bg, bb, ba, nocollide, Vel, aVel, frozen ) + + if not pl:CheckLimit( "wire_indicators" ) then return false end + + local wire_indicators = {} + + Ang = Ang - Angle(90, 0, 0) + + --make the center one first so we can get use its OBBMins/OBBMaxs + wire_indicators[1] = ents.Create( "gmod_wire_indicator" ) + if not wire_indicators[1]:IsValid() then return false end + wire_indicators[1]:SetModel( Model ) + wire_indicators[1]:SetAngles( Ang + Angle(90, 0, 0) ) + wire_indicators[1]:SetPos( Pos ) + wire_indicators[1]:Spawn() + wire_indicators[1]:Setup(a, ar, ag, ab, aa, b, br, bg, bb, ba) + wire_indicators[1]:SetPlayer(pl) + wire_indicators[1]:SetNetworkedString("WireName", "G") + pl:AddCount( "wire_indicators", wire_indicators[1] ) + local min = wire_indicators[1]:OBBMins(wire_indicators[1]) + Pos = Pos - Ang:Up() * min.x --correct Pos for thichness of segment + wire_indicators[1]:SetPos( Pos + Ang:Up() ) + + if wire_indicators[1]:GetPhysicsObject():IsValid() then + wire_indicators[1]:GetPhysicsObject():EnableMotion(!frozen) + end + if nocollide == true then + wire_indicators[1]:SetCollisionGroup(COLLISION_GROUP_WORLD) + end + + local ttable = { + pl = pl, + nocollide = nocollide + } + table.Merge(wire_indicators[1]:GetTable(), ttable ) + + local max = wire_indicators[1]:OBBMaxs(wire_indicators[1]) + + local angles = {Angle( 90, 0, 90 ), Angle( 90, 0, 90 ), Angle( 90, 0, 90 ), Angle( 90, 0, 90 ), Angle( 90, 0, 0 ), Angle( 90, 0, 0 )} + local vectors = {Vector( 1, (-1 * max.y), max.y ), Vector( 1, (-1 * max.y), (-1 * max.y) ), Vector( 1, max.y, max.y ), Vector( 1, max.y, (-1 * max.y) ), Vector( 1, 0, (2 * max.y) ), Vector( 1, 0, (-2 * max.y) ) } + local segname = {"B", "C", "F", "E", "A", "D"} + + for x=2, 7 do + wire_indicators[x] = ents.Create( "gmod_wire_indicator" ) + if (!wire_indicators[x]:IsValid()) then return false end + wire_indicators[x]:SetModel( Model ) + wire_indicators[x]:SetPos( Pos + Ang:Up() * vectors[x-1].X + Ang:Forward() * -1 * vectors[x-1].Z + Ang:Right() * vectors[x-1].Y ) + wire_indicators[x]:SetAngles( Ang + angles[x-1] ) + wire_indicators[x]:Spawn() + wire_indicators[x]:Setup(cmin, ar, ag, ab, aa, cmax, br, bg, bb, ba) + wire_indicators[x]:SetPlayer(pl) + wire_indicators[x]:SetNetworkedString("WireName", segname[x-1]) + if wire_indicators[x]:GetPhysicsObject():IsValid() then + wire_indicators[x]:GetPhysicsObject():EnableMotion(!frozen) + end + if nocollide == true then + wire_indicators[x]:SetCollisionGroup(COLLISION_GROUP_WORLD) + end + table.Merge(wire_indicators[x]:GetTable(), ttable ) + pl:AddCount( "wire_indicators", wire_indicators[x] ) + + --weld this segment to eveyone before it + for y=1,x do + const = constraint.Weld( wire_indicators[x], wire_indicators[y], 0, 0, 0, true, true ) + end + wire_indicators[x-1]:DeleteOnRemove( wire_indicators[x] ) --when one is removed, all are. a linked chain + end + wire_indicators[7]:DeleteOnRemove( wire_indicators[1] ) --loops chain back to first + + return wire_indicators +end + diff --git a/lua/entities/gmod_wire_indicator/shared.lua b/lua/entities/gmod_wire_indicator/shared.lua new file mode 100644 index 0000000000..ecbfa1a3b8 --- /dev/null +++ b/lua/entities/gmod_wire_indicator/shared.lua @@ -0,0 +1,38 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Indicator" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_input/cl_init.lua b/lua/entities/gmod_wire_input/cl_init.lua new file mode 100644 index 0000000000..e719186ab9 --- /dev/null +++ b/lua/entities/gmod_wire_input/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_input/init.lua b/lua/entities/gmod_wire_input/init.lua new file mode 100644 index 0000000000..4c57987d50 --- /dev/null +++ b/lua/entities/gmod_wire_input/init.lua @@ -0,0 +1,119 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Input" +ENT.OverlayDelay = 0 + +local keylist = {"0","1","2","3","4","5","6","7","8","9",".","Enter","+","-","*","/"} + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + // Used to keep track of numpad.OnUp/Down returns + // Fixes bug where player cannot change numpad key (TheApathetic) + self.OnUpImpulse = nil + self.OnDownImpulse = nil + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) +end + +function ENT:Setup(keygroup, toggle, value_off, value_on) + self.keygroup = keygroup + self.toggle = (toggle == 1 || toggle == true) + self.value_off = value_off + self.value_on = value_on + self.Value = value_off + + if (self.OnUpImpulse) then + numpad.Remove(self.OnUpImpulse) + numpad.Remove(self.OnDownImpulse) + end + + local pl = self:GetPlayer() + self.OnDownImpulse = numpad.OnDown( pl, keygroup, "WireInput_On", self.Entity, 1 ) + self.OnUpImpulse = numpad.OnUp( pl, keygroup, "WireInput_Off", self.Entity, 1 ) + + + self:ShowOutput(self.value_off) + Wire_TriggerOutput(self.Entity, "Out", self.value_off) +end + +function ENT:InputActivate( mul ) + if ( self.toggle ) then + return self:Switch( !self.On, mul ) + end + + return self:Switch( true, mul ) +end + +function ENT:InputDeactivate( mul ) + if ( self.toggle ) then return true end + + return self:Switch( false, mul ) +end + +function ENT:Switch( on, mul ) + if (!self.Entity:IsValid()) then return false end + + self.On = on + + if (on) then + self:ShowOutput(self.value_on) + self.Value = self.value_on + else + self:ShowOutput(self.value_off) + self.Value = self.value_off + end + + Wire_TriggerOutput(self.Entity, "Out", self.Value) + + return true +end + +function ENT:ShowOutput(value) + self:SetOverlayText( "Numpad Input ("..keylist[self.keygroup + 1]..")\n(" .. self.value_off .. " - " .. self.value_on .. ") = " .. value ) +end + +local function On( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputActivate( mul ) +end + +local function Off( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputDeactivate( mul ) +end + +numpad.Register( "WireInput_On", On ) +numpad.Register( "WireInput_Off", Off ) + +function MakeWireInput( pl, Pos, Ang, model, keygroup, toggle, value_off, value_on, frozen ) + if ( !pl:CheckLimit( "wire_inputs" ) ) then return false end + + local wire_input = ents.Create( "gmod_wire_input" ) + if (!wire_input:IsValid()) then return false end + + wire_input:SetAngles( Ang ) + wire_input:SetPos( Pos ) + wire_input:SetModel( Model(model or "models/jaanus/wiretool/wiretool_input.mdl") ) + wire_input:Spawn() + + if wire_input:GetPhysicsObject():IsValid() then + wire_input:GetPhysicsObject():EnableMotion(!frozen) + end + + wire_input:SetPlayer( pl ) + wire_input:Setup( keygroup, toggle, value_off, value_on ) + wire_input.pl = pl + + pl:AddCount( "wire_inputs", wire_input ) + pl:AddCleanup( "gmod_wire_input", wire_input ) + + return wire_input +end +duplicator.RegisterEntityClass("gmod_wire_input", MakeWireInput, "Pos", "Ang", "Model", "keygroup", "toggle", "value_off", "value_on", "frozen") diff --git a/lua/entities/gmod_wire_input/shared.lua b/lua/entities/gmod_wire_input/shared.lua new file mode 100644 index 0000000000..777343cf12 --- /dev/null +++ b/lua/entities/gmod_wire_input/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Numpad Input" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_keyboard/cl_init.lua b/lua/entities/gmod_wire_keyboard/cl_init.lua new file mode 100644 index 0000000000..a16dd385f3 --- /dev/null +++ b/lua/entities/gmod_wire_keyboard/cl_init.lua @@ -0,0 +1,51 @@ +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + +local BlockFrame + +local function Wire_BlockInput() + if (BlockFrame) then + BlockFrame:SetVisible(false) + end + + if (GetConVarString("wire_keyboard_sync") == "1") then + BlockFrame = vgui.Create("Panel") + BlockFrame:SetSize(10,10) + BlockFrame:SetPos(-100,-100) + BlockFrame:SetVisible(true) + BlockFrame:MakePopup() + BlockFrame:SetMouseInputEnabled(false) + end +end +usermessage.Hook("wire_keyboard_blockinput", Wire_BlockInput) +concommand.Add("wire_keyboard_blockinput", Wire_BlockInput) + +local function Wire_ReleaseInput() + if (BlockFrame) then + BlockFrame:SetVisible(false) + BlockFrame = nil + end +end +usermessage.Hook("wire_keyboard_releaseinput", Wire_ReleaseInput) +concommand.Add("wire_keyboard_releaseinput", Wire_ReleaseInput) + + +local KeyEvents = {} +hook.Add("CalcView", "wire_keyboard", function() + if (WIRE_SERVER_INSTALLED) then + for i=1,130 do + if(input.IsKeyDown(i) && !KeyEvents[i]) then + // The key has been pressed + KeyEvents[i] = true + LocalPlayer():ConCommand("wire_keyboard_press p "..i) + elseif(!input.IsKeyDown(i) && KeyEvents[i]) then + // The key has been released + KeyEvents[i] = false + LocalPlayer():ConCommand("wire_keyboard_press r "..i) + end + end + end +end) diff --git a/lua/entities/gmod_wire_keyboard/init.lua b/lua/entities/gmod_wire_keyboard/init.lua new file mode 100644 index 0000000000..b5d65dbcd9 --- /dev/null +++ b/lua/entities/gmod_wire_keyboard/init.lua @@ -0,0 +1,314 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + +include('shared.lua') +include('remap.lua') + +ENT.WireDebugName = "Wired Keyboard" +ENT.OverlayDelay = 0 + +--Duplicator support to save pod link (modified from TAD2020's work on advpod) +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if (self.LinkedPod) and (self.LinkedPod:IsValid()) then + info.pod = self.LinkedPod:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + if (info.pod) then + local LinkedPod = GetEntByID(info.pod) + if (!LinkedPod) then + LinkedPod = ents.GetByIndex(info.pod) + end + self:LinkPod(LinkedPod, true) + end +end + +function ENT:Initialize() + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType(MOVETYPE_VPHYSICS) + self.Entity:SetSolid(SOLID_VPHYSICS) + self.Entity:SetUseType(SIMPLE_USE) + + self.On = {} + self.Inputs = Wire_CreateInputs(self.Entity, { "Kick the bastard out of keyboard" }) + self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, { "Memory", "User", "InUse" }, { "NORMAL", "ENTITY", "NORMAL" }) + + for i = 0,223 do + self.On[i] = false + end + + self.Buffer = {} + for i = 0,31 do + self.Buffer[i] = 0 + end + + self.InUse = false + self.IgnoredFirstChar = false + self:SetOverlayText("Keyboard - not in use") + Wire_TriggerOutput(self.Entity, "InUse", 0) +end + + +function ENT:ReadCell(Address) + if (Address >= 0) && (Address < 32) then + return self.Buffer[Address] + elseif (Address >= 32) && (Address < 256) then + if (self.On[Address-32]) then + return 1 + else + return 0 + end + else + return nil + end +end + +function ENT:WriteCell(Address, value) + if (Address == 0) then + self.Buffer[0] = 0 + return true + elseif (Address > 0) && (Address < 256) then + self:Switch(false,value) + return true + else + return false + end +end + +function ENT:Use(pl) + if self.LinkedPod then pl:PrintMessage(HUD_PRINTTALK, "This keyboard is linked to a pod, please use the pod.") return end + self:PlayerAttach(pl) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Kick the bastard out of keyboard") then + self.lock = value + if value ~= 0 and self.InUse and self.InUseBy:IsValid() then + self:PlayerDetach(self.InUseBy) + end + end +end + +//============================================================================= +// Switch key state to ON/OFF +//============================================================================= + +function ENT:Switch(on, key) + if (!self.Entity:IsValid()) then return false end + + if (key == -1) then + self.Buffer[0] = 0 + return true + end + + self.On[ key ] = on + + if ((key != 21) && (key != 16)) then + if (on == true) then + if (self.InUse) then + self.Buffer[0] = self.Buffer[0] + 1 + self.Buffer[self.Buffer[0]] = key + Wire_TriggerOutput(self.Entity, "Memory", key) + end + else + Wire_TriggerOutput(self.Entity, "Memory", 0) + for i = 1,self.Buffer[0] do + if (self.Buffer[i] == key) then + self.Buffer[0] = self.Buffer[0] - 1 + for j = i,self.Buffer[0] do + self.Buffer[j] = self.Buffer[j+1] + end + return true + end + end + end + end + + return true +end + +//============================================================================= +// Keyboard turning ON/OFF +//============================================================================= + +local KeyBoardPlayerKeys = {} + +hook.Add("EntityRemoved", "wire_keyboard", function(ply) + KeyBoardPlayerKeys[ply:EntIndex()] = nil +end) + +local function Wire_KeyOff(pl) + local prev_ent = KeyBoardPlayerKeys[pl:EntIndex()] + if (prev_ent) && (prev_ent:IsValid()) && (prev_ent.InUse) then + Wire_TriggerOutput(prev_ent, "User", NULL) + Wire_TriggerOutput(prev_ent, "InUse", 0) + prev_ent.InUse = false + prev_ent:SetOverlayText("Keyboard - not in use") + end + KeyBoardPlayerKeys[pl:EntIndex()] = nil + + umsg.Start("wire_keyboard_releaseinput", pl) umsg.End() + + pl:PrintMessage(HUD_PRINTTALK,"Wired keyboard turned off\n") +end + +local function Wire_KeyOn(pl, ent) + local prev_ent = KeyBoardPlayerKeys[pl:EntIndex()] + if prev_ent and prev_ent.InUse then return end -- If the player is already using the keyboard, don't use another one + + KeyBoardPlayerKeys[pl:EntIndex()] = ent + + umsg.Start("wire_keyboard_blockinput", pl) umsg.End() + if ent.LinkedPod then + pl:PrintMessage(HUD_PRINTTALK, "This pod is linked to a keyboard - press ALT to leave\n") + else + pl:PrintMessage(HUD_PRINTTALK, "Wired keyboard turned on - press ALT to exit the mode\n") + end +end + +function ENT:PlayerAttach(pl) + if self.InUse then return end -- If the keyboard is already in use, don't attach the player + if self.lock == 1 then return end -- If the keyboard is locked, don't attach the player + + self.InUse = true + self.IgnoredFirstChar = false + self.InUseBy = pl + Wire_TriggerOutput(self.Entity, "User", pl.Entity) + Wire_TriggerOutput(self.Entity, "InUse", 1) + + self:SetOverlayText("Keyboard - In use by " .. pl:GetName()) + Wire_KeyOn(pl, self.Entity) +end + +function ENT:PlayerDetach(pl) + if not self.InUse then return end + if self.HasPodDriver and self.LinkedPod then + if self.LinkedPod.GetDriver and self.LinkedPod:GetDriver().ExitVehicle then + self.LinkedPod:GetDriver():ExitVehicle() + end + else + Wire_KeyOff(pl) + end + +end + +//============================================================================= +// Key press/release hook handlers +//============================================================================= + + +concommand.Add("wire_keyboard_press", function(pl, cmd, args) + local key = tonumber(args[2]) + + if (!KeyBoardPlayerKeys[pl:EntIndex()]) then return end + local ent = KeyBoardPlayerKeys[pl:EntIndex()] + if (!ent) || (!ent:IsValid()) then + Wire_KeyOff(pl) + return + end + if (!ent.InUse) then + ent:PlayerDetach(pl) + return + end + + if (key == KEY_RALT) || (key == KEY_LALT) then + ent:PlayerDetach(pl) + return + end + + //Get normalized/ASCII key + local nkey + if (Keyboard_ReMap[key]) then nkey = Keyboard_ReMap[key] + else nkey = 0 end + + if (ent.On[21] == true) then + if (Keyboard_CaseReMap[string.char(nkey)]) then + nkey = string.byte(Keyboard_CaseReMap[string.char(nkey)]) + end + end + + if (ent.IgnoredFirstChar == false) then + ent.IgnoredFirstChar = true + return + end + + //Msg("Received key press ("..string.char(nkey)..") for player "..pl:EntIndex()..", entity "..ent:EntIndex().."\n") + + if (args[1] == "p") then + if (key == KEY_LCONTROL) || (key == KEY_RCONTROL) then ent:Switch(true,16) end + if (key == KEY_LSHIFT) || (key == KEY_RSHIFT) then ent:Switch(true,21) end + + ent:Switch(true,nkey) + else + if (key == KEY_LCONTROL) || (key == KEY_RCONTROL) then ent:Switch(false,16) end + if (key == KEY_LSHIFT) || (key == KEY_RSHIFT) then ent:Switch(false,21) end + + ent:Switch(false,nkey) + end +end) + + +-- a table containing the pods and their linked keyboards +local linked_pods = {} + +-- place some hooks that allow the keyboard to track the state of its linked pod +hook.Add("PlayerEnteredVehicle", "wire_keyboard_PlayerEnteredVehicle", function(player, pod, role) + if not linked_pods[pod] then return end + + for keyboard,b in pairs(linked_pods[pod]) do + if b then + keyboard:PlayerEnteredVehicle(player, role) + end + end +end) + +hook.Add("PlayerLeaveVehicle", "wire_keyboard_PlayerLeaveVehicle", function(player, pod, role) + if not linked_pods[pod] then return end + + for keyboard,b in pairs(linked_pods[pod]) do + if b then + keyboard:PlayerLeaveVehicle(player, role) + end + end +end) + +function ENT:LinkPod(pod, silent) + if (pod and pod:IsValid()) then + -- unlink previous pod + if self.LinkedPod then self:LinkPod(nil, true) end + self.LinkedPod = pod + + if not silent then self:GetPlayer():PrintMessage( HUD_PRINTTALK,"Keyboard linked to Pod" ) end + --self:GetPlayer():PrintMessage( HUD_PRINTTALK,"Keyboard "..tostring(self).." linked to Pod "..tostring(pod) ) + if not linked_pods[pod] then + linked_pods[pod] = {} + end + linked_pods[pod][self] = true + elseif self.LinkedPod then + + if not silent then self:GetPlayer():PrintMessage( HUD_PRINTTALK,"Keyboard unlinked" ) end + --self:GetPlayer():PrintMessage( HUD_PRINTTALK,"Keyboard "..tostring(self).." unlinked" ) + if not linked_pods[self.LinkedPod] then return end + linked_pods[self.LinkedPod][self] = nil --TODO: optimization: remove sub-table if empty + self.LinkedPod = nil + end +end +function ENT:PlayerEnteredVehicle(player, role) + --print("Player "..tostring(player).." entered the vehicle") + self.HasPodDriver=true + self:PlayerAttach(player) +end + +function ENT:PlayerLeaveVehicle(player, role) + --print("Player "..tostring(player).." left the vehicle") + self.HasPodDriver=nil + self:PlayerDetach(player) +end + +function ENT:OnRemove() + self:LinkPod(nil, true) +end diff --git a/lua/entities/gmod_wire_keyboard/remap.lua b/lua/entities/gmod_wire_keyboard/remap.lua new file mode 100644 index 0000000000..ff61002f03 --- /dev/null +++ b/lua/entities/gmod_wire_keyboard/remap.lua @@ -0,0 +1,181 @@ +Keyboard_ReMap = {} + +Keyboard_ReMap[KEY_NONE] = 0 +Keyboard_ReMap[KEY_0] = string.byte('0') +Keyboard_ReMap[KEY_1] = string.byte('1') +Keyboard_ReMap[KEY_2] = string.byte('2') +Keyboard_ReMap[KEY_3] = string.byte('3') +Keyboard_ReMap[KEY_4] = string.byte('4') +Keyboard_ReMap[KEY_5] = string.byte('5') +Keyboard_ReMap[KEY_6] = string.byte('6') +Keyboard_ReMap[KEY_7] = string.byte('7') +Keyboard_ReMap[KEY_8] = string.byte('8') +Keyboard_ReMap[KEY_9] = string.byte('9') +Keyboard_ReMap[KEY_A] = string.byte('a') +Keyboard_ReMap[KEY_B] = string.byte('b') +Keyboard_ReMap[KEY_C] = string.byte('c') +Keyboard_ReMap[KEY_D] = string.byte('d') +Keyboard_ReMap[KEY_E] = string.byte('e') +Keyboard_ReMap[KEY_F] = string.byte('f') +Keyboard_ReMap[KEY_G] = string.byte('g') +Keyboard_ReMap[KEY_H] = string.byte('h') +Keyboard_ReMap[KEY_I] = string.byte('i') +Keyboard_ReMap[KEY_J] = string.byte('j') +Keyboard_ReMap[KEY_K] = string.byte('k') +Keyboard_ReMap[KEY_L] = string.byte('l') +Keyboard_ReMap[KEY_M] = string.byte('m') +Keyboard_ReMap[KEY_N] = string.byte('n') +Keyboard_ReMap[KEY_O] = string.byte('o') +Keyboard_ReMap[KEY_P] = string.byte('p') +Keyboard_ReMap[KEY_Q] = string.byte('q') +Keyboard_ReMap[KEY_R] = string.byte('r') +Keyboard_ReMap[KEY_S] = string.byte('s') +Keyboard_ReMap[KEY_T] = string.byte('t') +Keyboard_ReMap[KEY_U] = string.byte('u') +Keyboard_ReMap[KEY_V] = string.byte('v') +Keyboard_ReMap[KEY_W] = string.byte('w') +Keyboard_ReMap[KEY_X] = string.byte('x') +Keyboard_ReMap[KEY_Y] = string.byte('y') +Keyboard_ReMap[KEY_Z] = string.byte('z') +Keyboard_ReMap[KEY_PAD_0] = 128 +Keyboard_ReMap[KEY_PAD_1] = 129 +Keyboard_ReMap[KEY_PAD_2] = 130 +Keyboard_ReMap[KEY_PAD_3] = 131 +Keyboard_ReMap[KEY_PAD_4] = 132 +Keyboard_ReMap[KEY_PAD_5] = 133 +Keyboard_ReMap[KEY_PAD_6] = 134 +Keyboard_ReMap[KEY_PAD_7] = 135 +Keyboard_ReMap[KEY_PAD_8] = 136 +Keyboard_ReMap[KEY_PAD_9] = 137 +Keyboard_ReMap[KEY_PAD_DIVIDE] = 138 +Keyboard_ReMap[KEY_PAD_MULTIPLY]= 139 +Keyboard_ReMap[KEY_PAD_MINUS] = 140 +Keyboard_ReMap[KEY_PAD_PLUS] = 141 +Keyboard_ReMap[KEY_PAD_ENTER] = 142 +Keyboard_ReMap[KEY_PAD_DECIMAL] = 143 +Keyboard_ReMap[KEY_LBRACKET] = string.byte('[') +Keyboard_ReMap[KEY_RBRACKET] = string.byte(']') +Keyboard_ReMap[KEY_SEMICOLON] = string.byte(';') +Keyboard_ReMap[KEY_APOSTROPHE] = string.byte('\'') +Keyboard_ReMap[KEY_BACKQUOTE] = string.byte('`') +Keyboard_ReMap[KEY_COMMA] = string.byte(',') +Keyboard_ReMap[KEY_PERIOD] = string.byte('.') +Keyboard_ReMap[KEY_SLASH] = string.byte('/') +Keyboard_ReMap[KEY_BACKSLASH] = string.byte('\\') +Keyboard_ReMap[KEY_MINUS] = string.byte('-') +Keyboard_ReMap[KEY_EQUAL] = string.byte('=') +Keyboard_ReMap[KEY_ENTER] = 13 +Keyboard_ReMap[KEY_SPACE] = string.byte(' ') +Keyboard_ReMap[KEY_BACKSPACE] = 127 +Keyboard_ReMap[KEY_TAB] = 9 +Keyboard_ReMap[KEY_CAPSLOCK] = 144 +Keyboard_ReMap[KEY_NUMLOCK] = 145 +Keyboard_ReMap[KEY_ESCAPE] = 18 +Keyboard_ReMap[KEY_SCROLLLOCK] = 146 +Keyboard_ReMap[KEY_INSERT] = 147 +Keyboard_ReMap[KEY_DELETE] = 148 +Keyboard_ReMap[KEY_HOME] = 149 +Keyboard_ReMap[KEY_END] = 150 +Keyboard_ReMap[KEY_PAGEUP] = 151 +Keyboard_ReMap[KEY_PAGEDOWN] = 152 +Keyboard_ReMap[KEY_BREAK] = 153 +Keyboard_ReMap[KEY_LSHIFT] = 154 +Keyboard_ReMap[KEY_RSHIFT] = 155 +Keyboard_ReMap[KEY_LALT] = 156 +Keyboard_ReMap[KEY_RALT] = 157 +Keyboard_ReMap[KEY_LCONTROL] = 158 +Keyboard_ReMap[KEY_RCONTROL] = 159 +Keyboard_ReMap[KEY_LWIN] = 160 +Keyboard_ReMap[KEY_RWIN] = 161 +Keyboard_ReMap[KEY_APP] = 162 +Keyboard_ReMap[KEY_UP] = 17 +Keyboard_ReMap[KEY_LEFT] = 19 +Keyboard_ReMap[KEY_DOWN] = 18 +Keyboard_ReMap[KEY_RIGHT] = 20 +Keyboard_ReMap[KEY_F1] = 163 +Keyboard_ReMap[KEY_F2] = 164 +Keyboard_ReMap[KEY_F3] = 165 +Keyboard_ReMap[KEY_F4] = 166 +Keyboard_ReMap[KEY_F5] = 167 +Keyboard_ReMap[KEY_F6] = 168 +Keyboard_ReMap[KEY_F7] = 169 +Keyboard_ReMap[KEY_F8] = 170 +Keyboard_ReMap[KEY_F9] = 171 +Keyboard_ReMap[KEY_F10] = 172 +Keyboard_ReMap[KEY_F11] = 173 +Keyboard_ReMap[KEY_F12] = 174 +Keyboard_ReMap[KEY_CAPSLOCKTOGGLE] = 175 +Keyboard_ReMap[KEY_NUMLOCKTOGGLE] = 176 +Keyboard_ReMap[KEY_SCROLLLOCKTOGGLE] = 177 +Keyboard_ReMap[KEY_XBUTTON_UP] = 200 +Keyboard_ReMap[KEY_XBUTTON_DOWN] = 201 +Keyboard_ReMap[KEY_XBUTTON_LEFT] = 202 +Keyboard_ReMap[KEY_XBUTTON_RIGHT] = 203 +Keyboard_ReMap[KEY_XBUTTON_START] = 204 +Keyboard_ReMap[KEY_XBUTTON_BACK] = 205 +Keyboard_ReMap[KEY_XBUTTON_STICK1] = 206 +Keyboard_ReMap[KEY_XBUTTON_STICK2] = 207 +Keyboard_ReMap[KEY_XBUTTON_A] = 208 +Keyboard_ReMap[KEY_XBUTTON_B] = 209 +Keyboard_ReMap[KEY_XBUTTON_X] = 210 +Keyboard_ReMap[KEY_XBUTTON_Y] = 211 +--Keyboard_ReMap[KEY_XBUTTON_BLACK] = 212 +--Keyboard_ReMap[KEY_XBUTTON_WHITE] = 213 +Keyboard_ReMap[KEY_XBUTTON_LTRIGGER] = 214 +Keyboard_ReMap[KEY_XBUTTON_RTRIGGER] = 215 +Keyboard_ReMap[KEY_XSTICK1_UP] = 216 +Keyboard_ReMap[KEY_XSTICK1_DOWN] = 217 +Keyboard_ReMap[KEY_XSTICK1_LEFT] = 218 +Keyboard_ReMap[KEY_XSTICK1_RIGHT] = 219 +Keyboard_ReMap[KEY_XSTICK2_UP] = 220 +Keyboard_ReMap[KEY_XSTICK2_DOWN] = 221 +Keyboard_ReMap[KEY_XSTICK2_LEFT] = 222 +Keyboard_ReMap[KEY_XSTICK2_RIGHT] = 223 + +Keyboard_CaseReMap = {} +Keyboard_CaseReMap["a"] = "A" +Keyboard_CaseReMap["b"] = "B" +Keyboard_CaseReMap["c"] = "C" +Keyboard_CaseReMap["d"] = "D" +Keyboard_CaseReMap["e"] = "E" +Keyboard_CaseReMap["f"] = "F" +Keyboard_CaseReMap["g"] = "G" +Keyboard_CaseReMap["h"] = "H" +Keyboard_CaseReMap["i"] = "I" +Keyboard_CaseReMap["j"] = "J" +Keyboard_CaseReMap["k"] = "K" +Keyboard_CaseReMap["l"] = "L" +Keyboard_CaseReMap["m"] = "M" +Keyboard_CaseReMap["n"] = "N" +Keyboard_CaseReMap["o"] = "O" +Keyboard_CaseReMap["p"] = "P" +Keyboard_CaseReMap["q"] = "Q" +Keyboard_CaseReMap["r"] = "R" +Keyboard_CaseReMap["s"] = "S" +Keyboard_CaseReMap["t"] = "T" +Keyboard_CaseReMap["u"] = "U" +Keyboard_CaseReMap["v"] = "V" +Keyboard_CaseReMap["w"] = "W" +Keyboard_CaseReMap["x"] = "X" +Keyboard_CaseReMap["y"] = "Y" +Keyboard_CaseReMap["z"] = "Z" +Keyboard_CaseReMap["1"] = "!" +Keyboard_CaseReMap["2"] = "@" +Keyboard_CaseReMap["3"] = "#" +Keyboard_CaseReMap["4"] = "$" +Keyboard_CaseReMap["5"] = "%" +Keyboard_CaseReMap["6"] = "^" +Keyboard_CaseReMap["7"] = "&" +Keyboard_CaseReMap["8"] = "*" +Keyboard_CaseReMap["9"] = "(" +Keyboard_CaseReMap["0"] = ")" +Keyboard_CaseReMap["-"] = "_" +Keyboard_CaseReMap["="] = "+" +Keyboard_CaseReMap["\\"] = "|" +Keyboard_CaseReMap["["] = "{" +Keyboard_CaseReMap["]"] = "}" +Keyboard_CaseReMap[";"] = ":" +Keyboard_CaseReMap["'"] = "\"" +Keyboard_CaseReMap[","] = "<" +Keyboard_CaseReMap["."] = ">" +Keyboard_CaseReMap["/"] = "?" diff --git a/lua/entities/gmod_wire_keyboard/shared.lua b/lua/entities/gmod_wire_keyboard/shared.lua new file mode 100644 index 0000000000..7dc2f15671 --- /dev/null +++ b/lua/entities/gmod_wire_keyboard/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wie Keyboard" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_lamp/cl_init.lua b/lua/entities/gmod_wire_lamp/cl_init.lua new file mode 100644 index 0000000000..d205772100 --- /dev/null +++ b/lua/entities/gmod_wire_lamp/cl_init.lua @@ -0,0 +1,76 @@ + +include('shared.lua') + +local matLight = Material( "sprites/light_ignorez" ) +local matBeam = Material( "effects/lamp_beam" ) + +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Initialize() + + self.PixVis = util.GetPixelVisibleHandle() + +end + +/*--------------------------------------------------------- + Name: Draw +---------------------------------------------------------*/ +function ENT:Draw() + + self.BaseClass.Draw( self ) + + Wire_Render(self.Entity) + +end + +/*--------------------------------------------------------- + Name: DrawTranslucent + Desc: Draw translucent +---------------------------------------------------------*/ +function ENT:DrawTranslucent() + + self.BaseClass.DrawTranslucent( self ) + + // No glow if we're not switched on! + if ( !self:GetOn() ) then return end + + local LightNrm = self.Entity:GetAngles():Up() + local ViewNormal = self.Entity:GetPos() - EyePos() + local Distance = ViewNormal:Length() + ViewNormal:Normalize() + local ViewDot = ViewNormal:Dot( LightNrm ) + local r, g, b, a = self:GetColor() + local LightPos = self.Entity:GetPos() + LightNrm * -6 + + // glow sprite + /* + render.SetMaterial( matBeam ) + + local BeamDot = BeamDot = 0.25 + + render.StartBeam( 3 ) + render.AddBeam( LightPos + LightNrm * 1, 128, 0.0, Color( r, g, b, 255 * BeamDot) ) + render.AddBeam( LightPos - LightNrm * 100, 128, 0.5, Color( r, g, b, 64 * BeamDot) ) + render.AddBeam( LightPos - LightNrm * 200, 128, 1, Color( r, g, b, 0) ) + render.EndBeam() + */ + + if ( ViewDot >= 0 ) then + + render.SetMaterial( matLight ) + local Visibile = util.PixelVisible( LightPos, 16, self.PixVis ) + + if (!Visibile) then return end + + local Size = math.Clamp( Distance * Visibile * ViewDot * 2, 64, 512 ) + + Distance = math.Clamp( Distance, 32, 800 ) + local Alpha = math.Clamp( (1000 - Distance) * Visibile * ViewDot, 0, 100 ) + local Col = Color( r, g, b, Alpha ) + + render.DrawSprite( LightPos, Size, Size, Col, Visibile * ViewDot ) + render.DrawSprite( LightPos, Size*0.4, Size*0.4, Color(255, 255, 255, Alpha), Visibile * ViewDot ) + + end + +end diff --git a/lua/entities/gmod_wire_lamp/init.lua b/lua/entities/gmod_wire_lamp/init.lua new file mode 100644 index 0000000000..9a72930d13 --- /dev/null +++ b/lua/entities/gmod_wire_lamp/init.lua @@ -0,0 +1,188 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Lamp" + +local MODEL = Model( "models/props_wasteland/prison_lamp001c.mdl" ) + +AccessorFunc( ENT, "Texture", "FlashlightTexture" ) + +ENT:SetFlashlightTexture( "effects/flashlight001" ) + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + + self:SetModel( MODEL ) + self:PhysicsInit( SOLID_VPHYSICS ) + self:SetMoveType( MOVETYPE_VPHYSICS ) + self:SetSolid( SOLID_VPHYSICS ) + + local phys = self:GetPhysicsObject() + + if (phys:IsValid()) then + phys:Wake() + end + + self.lightr = 255 + self.lightg = 255 + self.lightb = 255 + + self.Inputs = WireLib.CreateSpecialInputs(self.Entity, {"Red", "Green", "Blue", "RGB", "On"}, {"NORMAL", "NORMAL", "NORMAL", "VECTOR", "NORMAL"}) + self:TurnOn() +end + +/*--------------------------------------------------------- + Name: Sets the color of the light +---------------------------------------------------------*/ +function ENT:SetLightColor( r, g, b ) + -- for dupe + self.r = r + self.g = g + self.b = b + + self.lightr = r + self.lightg = g + self.lightb = b + + self:SetVar( "lightr", r ) + self:SetVar( "lightg", g ) + self:SetVar( "lightb", b ) + + self:SetColor( r, g, b, 255 ) + + self.Entity:SetColor( r, g, b, 255 ) + + self.m_strLightColor = Format( "%i %i %i", r, g, b ) + + if ( self.flashlight ) then + self.flashlight:SetKeyValue( "lightcolor", self.m_strLightColor ) + end + + self:SetOverlayText( "Red:" .. r .. " Green:" .. g .. " Blue" .. b ) +end + +function ENT:Setup( r, g, b ) + self:SetLightColor( r, g, b ) +end + +/*--------------------------------------------------------- + Name: Sets the texture +---------------------------------------------------------*/ +function ENT:SetFlashlightTexture( tex ) + -- for dupe + self.Texture = tex + + if ( self.flashlight ) then + self.flashlight:Input( "SpotlightTexture", NULL, NULL, self:GetFlashlightTexture() ) + end + +end + +/*--------------------------------------------------------- + Name: OnTakeDamage +---------------------------------------------------------*/ +function ENT:OnTakeDamage( dmginfo ) + self.Entity:TakePhysicsDamage( dmginfo ) +end + +/*--------------------------------------------------------- + Name: TriggerInput + Desc: the inputs +---------------------------------------------------------*/ +function ENT:TriggerInput(iname, value) + if (iname == "Red") then + self:SetLightColor( value, self.lightg, self.lightb ) + elseif (iname == "Green") then + self:SetLightColor( self.lightr, value, self.lightb ) + elseif (iname == "Blue") then + self:SetLightColor( self.lightr, self.lightg, value ) + elseif (iname == "RGB") then + self:SetLightColor( value[1], value[2], value[3] ) + elseif (iname == "On") then + if value > 0 then + if !self.flashlight then self:TurnOn() end + elseif self.flashlight then + self:TurnOff() + end + end + + +end + +function ENT:TurnOn() + self:SetOn(true) + local angForward = self.Entity:GetAngles() + Angle( 90, 0, 0 ) + + self.flashlight = ents.Create( "env_projectedtexture" ) + + self.flashlight:SetParent( self.Entity ) + + // The local positions are the offsets from parent.. + self.flashlight:SetLocalPos( Vector( 0, 0, 0 ) ) + self.flashlight:SetLocalAngles( Angle(90,90,90) ) + + // Looks like only one flashlight can have shadows enabled! + self.flashlight:SetKeyValue( "enableshadows", 1 ) + self.flashlight:SetKeyValue( "farz", 2048 ) + self.flashlight:SetKeyValue( "nearz", 8 ) + + //Todo: Make this tweakable? + self.flashlight:SetKeyValue( "lightfov", 50 ) + + // Color.. Bright pink if none defined to alert us to error + self.flashlight:SetKeyValue( "lightcolor", self.m_strLightColor or "255 0 255" ) + + + self.flashlight:Spawn() + + self.flashlight:Input( "SpotlightTexture", NULL, NULL, self:GetFlashlightTexture() ) +end + +function ENT:TurnOff() + self:SetOn(false) + SafeRemoveEntity( self.flashlight ) + self.flashlight = nil +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function ENT:Setup( r, g, b, Texture ) + self:SetLightColor( r, g, b ) + self:SetFlashlightTexture( Texture or "effects/flashlight001" ) +end + +include('shared.lua') + +function MakeWireLamp( pl, r, g, b, Texture, Data ) + + if ( !pl:CheckLimit( "wire_lamps" ) ) then return false end + + local wire_lamp = ents.Create( "gmod_wire_lamp" ) + if (!wire_lamp:IsValid()) then return end + duplicator.DoGeneric( wire_lamp, Data ) + wire_lamp:Setup( r, g, b, Texture ) + wire_lamp:Spawn() + + duplicator.DoGenericPhysics( wire_lamp, pl, Data ) + + wire_lamp:SetPlayer( pl ) + wire_lamp.pl = pl + + pl:AddCount( "wire_lamps", wire_lamp ) + pl:AddCleanup( "wire_lamp", wire_lamp ) + + return wire_lamp +end + +duplicator.RegisterEntityClass( "gmod_wire_lamp", MakeWireLamp, "lightr", "lightg", "lightb", "Texture", "Data" ) diff --git a/lua/entities/gmod_wire_lamp/shared.lua b/lua/entities/gmod_wire_lamp/shared.lua new file mode 100644 index 0000000000..c86128639f --- /dev/null +++ b/lua/entities/gmod_wire_lamp/shared.lua @@ -0,0 +1,20 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Lamp" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetOn( on ) + self:SetNetworkedBool( "Enabled", on ) +end + +function ENT:GetOn() + return self:GetNetworkedVar( "Enabled", true ) +end diff --git a/lua/entities/gmod_wire_las_reciever/cl_init.lua b/lua/entities/gmod_wire_las_reciever/cl_init.lua new file mode 100644 index 0000000000..48d2275a21 --- /dev/null +++ b/lua/entities/gmod_wire_las_reciever/cl_init.lua @@ -0,0 +1,10 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_las_reciever/init.lua b/lua/entities/gmod_wire_las_reciever/init.lua new file mode 100644 index 0000000000..1dbccade42 --- /dev/null +++ b/lua/entities/gmod_wire_las_reciever/init.lua @@ -0,0 +1,51 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Laser Receiver" + +local MODEL = Model("models/jaanus/wiretool/wiretool_range.mdl") + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, {"X", "Y", "Z", "Active", "Pos", "RangerData"}, {"NORMAL", "NORMAL", "NORMAL", "NORMAL", "VECTOR", "RANGER"}) + self.VPos = Vector(0,0,0) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Setup() +end + +function ENT:GetBeaconPos(sensor) + return self.VPos +end + +function ENT:ShowOutput(value) + if (value ~= self.PrevOutput) then + self:SetOverlayText( "Laser Receiver" ) + self.PrevOutput = value + end +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function playerDeath( victim, weapon, killer) + if(victim:HasWeapon("laserPointer"))then + local pointer = victim:GetWeapon("laserPointer") + if(pointer && pointer:IsValid())then + victim.LasReceiver = pointer.Receiver + end + end +end + +hook.Add( "PlayerDeath", "laserMemory", playerDeath) diff --git a/lua/entities/gmod_wire_las_reciever/shared.lua b/lua/entities/gmod_wire_las_reciever/shared.lua new file mode 100644 index 0000000000..d6c8348116 --- /dev/null +++ b/lua/entities/gmod_wire_las_reciever/shared.lua @@ -0,0 +1,47 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Laser Pointer Receiver" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + + +function ENT:SetPointing(value) + self.Entity:SetNetworkedFloat( "Pointing", value ) +end + +function ENT:GetPointing() + return self.Entity:GetNetworkedSFloat( "Pointing" ) +end diff --git a/lua/entities/gmod_wire_latch/cl_init.lua b/lua/entities/gmod_wire_latch/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_latch/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_latch/init.lua b/lua/entities/gmod_wire_latch/init.lua new file mode 100644 index 0000000000..f9e69c3e07 --- /dev/null +++ b/lua/entities/gmod_wire_latch/init.lua @@ -0,0 +1,155 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +ENT.WireDebugName = "Latch" + +include('shared.lua') + +local MODEL = Model("models/jaanus/wiretool/wiretool_siren.mdl") + + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs( self.Entity, { "Activate", "NoCollide" } ) + + -- masks containing all current states + self.nocollide_masks = { + -- Ent1, Ent2 , nocollide between the two + { false, false, true }, -- 1 nocollide between the two + { true , false, false }, -- 2 nocollide Ent1 with all + { false, true , false }, -- 3 nocollide Ent2 with all + { true , true , false } -- 4 nocollide both with all + -- all other values: { false, false, false } + } + + self.nocollide_description = { + "NoCollided", + "Ent1 has collisions disabled", + "Ent2 has collisions disabled", + "all collisions disabled" + } + + -- the constraint + self.nocollide = nil + + self:TriggerInput("NoCollide", 0) +end + +-- This function is called by the STOOL +function ENT:SendVars( Ent1, Ent2, Bone1, Bone2, const ) + self.Ent1 = Ent1 + self.Ent2 = Ent2 + self.Bone1 = Bone1 + self.Bone2 = Bone2 + self.Constraint = const +end + +function ENT:TriggerInput(iname, value) + if iname == "Activate" then + + if value == 0 and self.Constraint then + + self.Constraint:Remove() + self.Constraint = nil + + self:SetOverlayText( "Weld Latch - Deactivated" ) + + end + + if value ~= 0 and not self.Constraint then + self.Constraint = constraint.Weld( self.Ent1, self.Ent2, self.Bone1, self.Bone2, 0 ) + + if self.Constraint then + self.Constraint.Type = "" -- prevents the duplicator from making this weld + end + end + + elseif iname == "NoCollide" then + + self.nocollide_status = value + local mask = self.nocollide_masks[value] or {false, false, false} + + if self.Ent1 and not self.Ent1:IsWorld() then + local phys = self.Ent1:GetPhysicsObject() + if phys:IsValid() then phys:EnableCollisions(not mask[1]) end + end + + if self.Ent2 and not self.Ent2:IsWorld() then + local phys = self.Ent2:GetPhysicsObject() + if phys:IsValid() then phys:EnableCollisions(not mask[2]) end + end + + if mask[3] then + if not self.nocollide then + if self.Ent1 and self.Ent2 then + -- enable NoCollide between the two entities + self.nocollide = constraint.NoCollide( self.Ent1, self.Ent2, self.Bone1, self.Bone2 ) + end + end + else + if self.nocollide then + -- disable NoCollide between the two entities + self.nocollide:Remove() + self.nocollide=nil + end + end + + end + + self:UpdateOverlay() +end + +function ENT:UpdateOverlay() + local desc = self.nocollide_description[self.nocollide_status] + if not desc then + if self.Constraint then + self:SetOverlayText( "Weld Latch - Welded" ) + else + self:SetOverlayText( "Weld Latch - Deactivated" ) + end + return + end + local text = self.Constraint and "Weld Latch - Welded and " or "Weld Latch - Not welded but " + text = text .. desc + self:SetOverlayText( text ) +end + +-- duplicator support (TAD2020) +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if (self.Ent1) and (self.Ent1:IsValid()) then + info.Ent1 = self.Ent1:EntIndex() + info.Bone1 = self.Bone1 + end + if (self.Ent2) and (self.Ent2:IsValid()) then + info.Ent2 = self.Ent2:EntIndex() + info.Bone2 = self.Bone2 + end + info.Activate = self.Constraint and 1 or 0 + info.NoCollide = self.nocollide_status + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + if (info.Ent1) then + self.Ent1 = GetEntByID(info.Ent1) + self.Bone1 = info.Bone1 + if (!self.Ent1) then + self.Ent1 = ents.GetByIndex(info.Ent1) + end + end + if (info.Ent2) then + self.Ent2 = GetEntByID(info.Ent2) + self.Bone2 = info.Bone2 + if (!self.Ent2) then + self.Ent2 = ents.GetByIndex(info.Ent2) + end + end + self:TriggerInput("Activate", info.Activate) + self:TriggerInput("NoCollide", info.NoCollide) +end diff --git a/lua/entities/gmod_wire_latch/shared.lua b/lua/entities/gmod_wire_latch/shared.lua new file mode 100644 index 0000000000..d4821755ce --- /dev/null +++ b/lua/entities/gmod_wire_latch/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Constraint Latch" +ENT.Author = "Exitlights" +ENT.Contact = "" +ENT.Purpose = "Controllable weld and nocollide between two selected entities" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_light/cl_init.lua b/lua/entities/gmod_wire_light/cl_init.lua new file mode 100644 index 0000000000..d0f4f797bb --- /dev/null +++ b/lua/entities/gmod_wire_light/cl_init.lua @@ -0,0 +1,58 @@ + +include('shared.lua') + +local matLight = Material( "sprites/light_ignorez" ) +local matBeam = Material( "effects/lamp_beam" ) + +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Initialize() + + self.PixVis = util.GetPixelVisibleHandle() + +end + +/*--------------------------------------------------------- + Name: Draw +---------------------------------------------------------*/ +function ENT:Draw() + + self.BaseClass.Draw( self ) + +end + +/*--------------------------------------------------------- + Name: DrawTranslucent + Desc: Draw translucent +---------------------------------------------------------*/ +function ENT:DrawTranslucent() + + local LightNrm = self.Entity:GetAngles():Up()*(-1) + local ViewDot = EyeVector():Dot( LightNrm ) + local r, g, b, a = self.Entity:GetColor() + local LightPos = self.Entity:GetPos() + LightNrm * -10 + + // glow sprite + /* + render.SetMaterial( matBeam ) + + local BeamDot = BeamDot = 0.25 + + render.StartBeam( 3 ) + render.AddBeam( LightPos + LightNrm * 1, 128, 0.0, Color( r, g, b, 255 * BeamDot) ) + render.AddBeam( LightPos - LightNrm * 100, 128, 0.5, Color( r, g, b, 64 * BeamDot) ) + render.AddBeam( LightPos - LightNrm * 200, 128, 1, Color( r, g, b, 0) ) + render.EndBeam() + */ + + if ( ViewDot < 0 ) then return end + + render.SetMaterial( matLight ) + local Visible = util.PixelVisible( LightPos, 16, self.PixVis ) + local Size = math.Clamp( 512 * (1 - Visible*ViewDot),128, 512 ) + + local Col = Color( r, g, b, 200*Visible*ViewDot ) + + render.DrawSprite( LightPos, Size, Size, Col, Visible * ViewDot ) + +end diff --git a/lua/entities/gmod_wire_light/init.lua b/lua/entities/gmod_wire_light/init.lua new file mode 100644 index 0000000000..2e9fa1188e --- /dev/null +++ b/lua/entities/gmod_wire_light/init.lua @@ -0,0 +1,193 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Light" + +local MODEL = Model("models/jaanus/wiretool/wiretool_siren.mdl") + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.R, self.G, self.B = 0, 0, 0 + self.Entity:SetColor( 0, 0, 0, 255 ) + + self.Inputs = WireLib.CreateSpecialInputs(self.Entity, {"Red", "Green", "Blue", "RGB"}, {"NORMAL", "NORMAL", "NORMAL", "VECTOR"}) +end + +function ENT:OnRemove() + if (!self.RadiantComponent) then return end + if not self.RadiantComponent:IsValid() then return end + self.RadiantComponent:SetParent() //Bugfix by aVoN + self.RadiantComponent:Fire("TurnOff","",0) + self.RadiantComponent:Fire("kill","",1) +end + +function ENT:DirectionalOn() + if (self.DirectionalComponent) then + self:DirectionalOff() + end + + local flashlight = ents.Create( "env_projectedtexture" ) + flashlight:SetParent( self.Entity ) + + // The local positions are the offsets from parent.. + flashlight:SetLocalPos( Vector( 0, 0, 0 ) ) + flashlight:SetAngles( self.Entity:GetAngles() + Angle( -90, 0, 0 ) ) + + // Looks like only one flashlight can have shadows enabled! + flashlight:SetKeyValue( "enableshadows", 1 ) + flashlight:SetKeyValue( "farz", 2048 ) + flashlight:SetKeyValue( "nearz", 8 ) + + //Todo: Make this tweakable? + flashlight:SetKeyValue( "lightfov", 50 ) + + // Color.. Bright pink if none defined to alert us to error + flashlight:SetKeyValue( "lightcolor", "255 0 255" ) + flashlight:Spawn() + flashlight:Input( "SpotlightTexture", NULL, NULL, "effects/flashlight001" ) + + self.DirectionalComponent = flashlight +end + +function ENT:DirectionalOff() + if (!self.DirectionalComponent) then return end + + self.DirectionalComponent:Remove() + self.DirectionalComponent = nil +end + +function ENT:RadiantOn() + if (self.RadiantComponent) then + self.RadiantComponent:Fire("TurnOn","","0") + else + local dynlight = ents.Create( "light_dynamic" ) + dynlight:SetPos( self.Entity:GetPos() ) + local dynlightpos = dynlight:GetPos()+Vector( 0, 0, 10 ) + dynlight:SetPos( dynlightpos ) + dynlight:SetKeyValue( "_light", self.R .. " " .. self.G .. " " .. self.B .. " " .. 255 ) + dynlight:SetKeyValue( "style", 0 ) + dynlight:SetKeyValue( "distance", 255 ) + dynlight:SetKeyValue( "brightness", 5 ) + dynlight:SetParent( self.Entity ) + dynlight:Spawn() + self.RadiantComponent = dynlight + end + + self.RadiantState = 1 +end + +function ENT:RadiantOff() + if (!self.RadiantComponent) then return end + if not self.RadiantComponent:IsValid() then return end + self.RadiantComponent:Fire("TurnOff","","0") + + self.RadiantState = 0 + --self.RadiantComponent:Remove() + --self.RadiantComponent = nil +end + +function ENT:TriggerInput(iname, value) + local R,G,B = self.R, self.G, self.B + if (iname == "Red") then + R = value + elseif (iname == "Green") then + G = value + elseif (iname == "Blue") then + B = value + elseif (iname == "RGB") then + R,G,B = value[1], value[2], value[3] + end + self:ShowOutput( R, G, B ) +end + +function ENT:Setup(directional, radiant) + self.directional = directional + self.radiant = radiant + self.RadiantState = 0 + if (self.directional) then + if (!self.DirectionalComponent) then + self:DirectionalOn() + end + else + if (self.DirectionalComponent) then + self:DirectionalOff() + end + end + if (self.radiant) then + if (self.RadiantState == 0) then + self:RadiantOn() + end + else + if (self.RadiantState) then + self:RadiantOff() + end + end + +end + +function ENT:ShowOutput( R, G, B ) + if ( R ~= self.R or G ~= self.G or B ~= self.B ) then + if (((R + G) + B) != 0) then + if (self.directional) then + if (!self.DirectionalComponent) then + self:DirectionalOn() + end + self.DirectionalComponent:SetKeyValue( "lightcolor", Format( "%i %i %i", R, G, B ) ) + end + if (self.radiant) then + if (self.RadiantState == 0) then + self:RadiantOn() + end + self.RadiantComponent:SetColor( R, G, B, 255 ) + end + else + self:DirectionalOff() + self:RadiantOff() + end + self:SetOverlayText( "Light: Red=" .. R .. " Green:" .. G .. " Blue:" .. B ) + self.R, self.G, self.B = R, G, B + local _,_,_,A = self.Entity:GetColor() + self.Entity:SetColor( R, G, B, A ) + end +end + +function MakeWireLight( pl, Pos, Ang, model, directional, radiant, nocollide, frozen, nocollide ) + if ( !pl:CheckLimit( "wire_lights" ) ) then return false end + + local wire_light = ents.Create( "gmod_wire_light" ) + if (!wire_light:IsValid()) then return false end + + wire_light:SetAngles( Ang ) + wire_light:SetPos( Pos ) + wire_light:SetModel( model ) + wire_light:Spawn() + + wire_light:GetTable():Setup(directional, radiant) + wire_light:GetTable():SetPlayer(pl) + + if wire_light:GetPhysicsObject():IsValid() then + local Phys = wire_light:GetPhysicsObject() + if nocollide == true then + Phys:EnableCollisions(false) + end + Phys:EnableMotion(!frozen) + end + + local ttable = { + pl = pl, + nocollide = nocollide + } + table.Merge(wire_light:GetTable(), ttable ) + + pl:AddCount( "wire_lights", wire_light ) + + return wire_light +end + +duplicator.RegisterEntityClass("gmod_wire_light", MakeWireLight, "Pos", "Ang", "Model", "directional", "radiant", "nocollide", "frozen", "nocollide") diff --git a/lua/entities/gmod_wire_light/shared.lua b/lua/entities/gmod_wire_light/shared.lua new file mode 100644 index 0000000000..9578b61169 --- /dev/null +++ b/lua/entities/gmod_wire_light/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Light" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_locator/cl_init.lua b/lua/entities/gmod_wire_locator/cl_init.lua new file mode 100644 index 0000000000..e719186ab9 --- /dev/null +++ b/lua/entities/gmod_wire_locator/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_locator/init.lua b/lua/entities/gmod_wire_locator/init.lua new file mode 100644 index 0000000000..f75cf814a1 --- /dev/null +++ b/lua/entities/gmod_wire_locator/init.lua @@ -0,0 +1,25 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Locator" +ENT.OverlayDelay = 0 + +local MODEL = Model( "models/props_lab/powerbox02d.mdl" ) + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) +end + +function ENT:GetBeaconPos(sensor) + return self.Entity:GetPos() +end + +function ENT:Setup() + +end diff --git a/lua/entities/gmod_wire_locator/shared.lua b/lua/entities/gmod_wire_locator/shared.lua new file mode 100644 index 0000000000..bcbec9599f --- /dev/null +++ b/lua/entities/gmod_wire_locator/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Locator Beacon" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_nailer/cl_init.lua b/lua/entities/gmod_wire_nailer/cl_init.lua new file mode 100644 index 0000000000..717fe63556 --- /dev/null +++ b/lua/entities/gmod_wire_nailer/cl_init.lua @@ -0,0 +1,6 @@ +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1, false, 2048 ) +end diff --git a/lua/entities/gmod_wire_nailer/init.lua b/lua/entities/gmod_wire_nailer/init.lua new file mode 100644 index 0000000000..458f78606b --- /dev/null +++ b/lua/entities/gmod_wire_nailer/init.lua @@ -0,0 +1,74 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Nailer" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, { "A" }) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Setup(flim) + self:TriggerInput("A", 0) + self.Flim = math.Clamp(flim, 0, 10000) + self:ShowOutput() +end + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + if (value ~= 0) then + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * 2048) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + // Bail if we hit world or a player + if ( !trace.Entity:IsValid() || trace.Entity:IsPlayer() ) then return end + // If there's no physics object then we can't constraint it! + if ( !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return end + + local tr = {} + tr.start = trace.HitPos + tr.endpos = trace.HitPos + (self.Entity:GetUp() * 50.0) + tr.filter = { trace.Entity, self.Entity } + local trTwo = util.TraceLine( tr ) + + if ( trTwo.Hit && !trTwo.Entity:IsPlayer() ) then + // Weld them! + local constraint = constraint.Weld( trace.Entity, trTwo.Entity, trace.PhysicsBone, trTwo.PhysicsBone, self.Flim ) + + //effect on weld (tomb332) + local effectdata = EffectData() + effectdata:SetOrigin( trTwo.HitPos ) + effectdata:SetNormal( trTwo.HitNormal ) + effectdata:SetMagnitude( 5 ) + effectdata:SetScale( 1 ) + effectdata:SetRadius( 10 ) + util.Effect( "Sparks", effectdata ) + end + end + end +end + +function ENT:ShowOutput() + self:SetOverlayText( "Nailer\n" .. + "Force Limit: " .. self.Flim ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + diff --git a/lua/entities/gmod_wire_nailer/shared.lua b/lua/entities/gmod_wire_nailer/shared.lua new file mode 100644 index 0000000000..038deedd0a --- /dev/null +++ b/lua/entities/gmod_wire_nailer/shared.lua @@ -0,0 +1,38 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Nailer" +ENT.Author = "TomB" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_numpad/cl_init.lua b/lua/entities/gmod_wire_numpad/cl_init.lua new file mode 100644 index 0000000000..e719186ab9 --- /dev/null +++ b/lua/entities/gmod_wire_numpad/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_numpad/init.lua b/lua/entities/gmod_wire_numpad/init.lua new file mode 100644 index 0000000000..b5362052a8 --- /dev/null +++ b/lua/entities/gmod_wire_numpad/init.lua @@ -0,0 +1,130 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Numpad" +ENT.OverlayDelay = 0 + +local keynames = {"0","1","2","3","4","5","6","7","8","9",".","enter","+","-","*","/"} +local lookupkeynames = {} +for k,v in ipairs(keynames) do + lookupkeynames[v] = k-1 + lookupkeynames[k-1] = v +end +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.On = {} + + self.Inputs = Wire_CreateInputs(self.Entity, keynames) + self.Outputs = Wire_CreateOutputs(self.Entity, keynames) + + self.Buffer = {} + for i = 0,#keynames-1 do + self.Buffer[i] = 0 + end +end + + +function ENT:ReadCell( Address ) + if (Address >= 0) && (Address < #keynames) then + return self.Buffer[Address] + else + return nil + end +end + +function ENT:WriteCell( Address, value ) + if (Address >= 0) && (Address < #keynames) then + self:TriggerInput(lookupkeynames[Address], value) + return true + else + return false + end +end + +function ENT:TriggerInput(key, value) + if value ~= 0 then + numpad.Activate( self:GetPlayer(), nil, {key}, self:GetPlayerIndex() ) + else + numpad.Deactivate( self:GetPlayer(), nil, {key}, self:GetPlayerIndex() ) + end +end + +function ENT:Setup( toggle, value_off, value_on) + self.Toggle = (toggle == 1) + self.ValueOff = value_off + self.ValueOn = value_on + + self:ShowOutput() +end + +function ENT:NumpadActivate( key ) + if ( self.Toggle ) then + return self:Switch( !self.On[ key ], key ) + end + + return self:Switch( true, key ) +end + +function ENT:NumpadDeactivate( key ) + if ( self.Toggle ) then return true end + + return self:Switch( false, key ) +end + +function ENT:Switch( on, key ) + if (!self.Entity:IsValid()) then return false end + + self.On[ key ] = on + + if (on) then + self:ShowOutput() + self.Value = self.ValueOn + else + self:ShowOutput() + self.Value = self.ValueOff + end + + Wire_TriggerOutput(self.Entity, lookupkeynames[key], self.Value) + + if ( on ) then + self.Buffer[key] = 1 + else + self.Buffer[key] = 0 + end + + return true +end + +function ENT:ShowOutput() + txt = "Numpad" + for k = 0, #keynames-1 do + if (self.On[k]) then + txt = txt..", "..lookupkeynames[k] + end + end + + self:SetOverlayText( txt ) +end + +function ENT:OnRemove() + for _,impulse in ipairs(self.impulses) do + numpad.Remove(impulse) + end +end + +local function On( pl, ent, key ) + return ent:NumpadActivate( key ) +end + +local function Off( pl, ent, key ) + return ent:NumpadDeactivate( key ) +end + +numpad.Register( "WireNumpad_On", On ) +numpad.Register( "WireNumpad_Off", Off ) diff --git a/lua/entities/gmod_wire_numpad/shared.lua b/lua/entities/gmod_wire_numpad/shared.lua new file mode 100644 index 0000000000..a4e66f804e --- /dev/null +++ b/lua/entities/gmod_wire_numpad/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Numpad" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_oscilloscope/cl_init.lua b/lua/entities/gmod_wire_oscilloscope/cl_init.lua new file mode 100644 index 0000000000..2dce126cd4 --- /dev/null +++ b/lua/entities/gmod_wire_oscilloscope/cl_init.lua @@ -0,0 +1,118 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Initialize() + self.RTTexture = WireGPU_NeedRenderTarget(self:EntIndex()) +end + +function ENT:OnRemove() + WireGPU_ReturnRenderTarget(self:EntIndex()) +end + +function ENT:Draw() + self.Entity:DrawModel() + + self.RTTexture = WireGPU_GetMyRenderTarget(self:EntIndex()) + + local NewRT = self.RTTexture + local OldRT = render.GetRenderTarget() + + local OldTex = WireGPU_matScreen:GetMaterialTexture("$basetexture") + WireGPU_matScreen:SetMaterialTexture("$basetexture",self.RTTexture) + + if (true) then + local oldw = ScrW() + local oldh = ScrH() + + render.SetRenderTarget(NewRT) + render.SetViewPort(0,0,512,512) + cam.Start2D() + surface.SetDrawColor(10,20,5,255) + surface.DrawRect(0,0,512,512) + + local nodes = self:GetNodeList() + for i=1,39 do + local i_next = i+1 + + local nx1 = nodes[i].X*256+256 + local ny1 = -nodes[i].Y*256+256 + local nx2 = nodes[i_next].X*256+256 + local ny2 = -nodes[i_next].Y*256+256 + + if ((nx1-nx2)*(nx1-nx2) + (ny1-ny2)*(ny1-ny2) < 256*256) then + local b = math.max(0, math.min(i*i*0.16, 255)) + + + for i=-3,3 do + surface.SetDrawColor(b/8, b/2, b/8, 255) + surface.DrawLine(nx1, ny1+i, nx2, ny2+i) + surface.SetDrawColor(b/8, b/2, b/8, 255) + surface.DrawLine(nx1+i, ny1, nx2+i, ny2) + end + + surface.SetDrawColor(b/4, b, b/4, 255) + surface.DrawLine(nx1, ny1, nx2, ny2) + end + end + + surface.SetDrawColor(30, 120, 10, 255) + surface.DrawLine(0, 128, 512, 128) + surface.DrawLine(0, 384, 512, 384) + surface.DrawLine(128, 0, 128, 512) + surface.DrawLine(384, 0, 384, 512) + + surface.SetDrawColor(180, 200, 10, 255) + surface.DrawLine(0, 256, 512, 256) + surface.DrawLine(256, 0, 256, 512) + cam.End2D() + render.SetViewPort(0,0,oldw,oldh) + render.SetRenderTarget(OldRT) + end + + + if (WireGPU_Monitors[self.Entity:GetModel()]) && (WireGPU_Monitors[self.Entity:GetModel()].OF) then + OF = WireGPU_Monitors[self.Entity:GetModel()].OF + OU = WireGPU_Monitors[self.Entity:GetModel()].OU + OR = WireGPU_Monitors[self.Entity:GetModel()].OR + Res = WireGPU_Monitors[self.Entity:GetModel()].RS + RatioX = WireGPU_Monitors[self.Entity:GetModel()].RatioX + else + OF = 0 + OU = 0 + OR = 0 + Res = 1 + RatioX = 1 + end + + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + + local pos = self.Entity:GetPos() + (self.Entity:GetForward() * OF) + (self.Entity:GetUp() * OU) + (self.Entity:GetRight() * OR) + + cam.Start3D2D(pos,ang,Res) + local w = 512 + local h = 512 + local x = -w/2 + local y = -h/2 + + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(-256,-256,512/RatioX,512) + + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(WireGPU_texScreen) + WireGPU_DrawScreen(x,y,w/RatioX,h,0,0) + cam.End3D2D() + + Wire_Render(self.Entity) +end + +function ENT:IsTranslucent() + return true +end diff --git a/lua/entities/gmod_wire_oscilloscope/init.lua b/lua/entities/gmod_wire_oscilloscope/init.lua new file mode 100644 index 0000000000..8d2cedb7f2 --- /dev/null +++ b/lua/entities/gmod_wire_oscilloscope/init.lua @@ -0,0 +1,48 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Oscilloscope" + +function ENT:Initialize() + + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "X", "Y" }) +end + +function ENT:Think() + self.BaseClass.Think(self) + + local x = math.max(-1, math.min(self.Inputs.X.Value or 0, 1)) + local y = math.max(-1, math.min(self.Inputs.Y.Value or 0, 1)) + self:SetNextNode(x, y) + + self.Entity:NextThink(CurTime()+0.08) + return true +end + + +function MakeWireOscilloscope( pl, Pos, Ang, model ) + + if ( !pl:CheckLimit( "wire_oscilloscopes" ) ) then return false end + + local wire_oscilloscope = ents.Create( "gmod_wire_oscilloscope" ) + if (!wire_oscilloscope:IsValid()) then return false end + wire_oscilloscope:SetModel( model ) + + wire_oscilloscope:SetAngles( Ang ) + wire_oscilloscope:SetPos( Pos ) + wire_oscilloscope:Spawn() + + wire_oscilloscope:SetPlayer(pl) + wire_oscilloscope.pl = pl + + pl:AddCount( "wire_oscilloscopes", wire_oscilloscope ) + + return wire_oscilloscope +end + +duplicator.RegisterEntityClass("gmod_wire_oscilloscope", MakeWireOscilloscope, "Pos", "Ang", "Model") diff --git a/lua/entities/gmod_wire_oscilloscope/shared.lua b/lua/entities/gmod_wire_oscilloscope/shared.lua new file mode 100644 index 0000000000..933af214c0 --- /dev/null +++ b/lua/entities/gmod_wire_oscilloscope/shared.lua @@ -0,0 +1,34 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Oscilloscope" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetNextNode(x, y) + local node_idx = self.Entity:GetNetworkedInt("OscN") or 0 + if (node_idx > 41) then node_idx = node_idx-41 end + + self.Entity:SetNetworkedFloat("OscX"..node_idx, x) + self.Entity:SetNetworkedFloat("OscY"..node_idx, y) + self.Entity:SetNetworkedInt("OscN", node_idx+1) +end + +function ENT:GetNodeList() + local nodes = {} + local node_idx = self.Entity:GetNetworkedInt("OscN") + for i=1,40 do + table.insert(nodes, { X = (self.Entity:GetNetworkedFloat("OscX"..node_idx) or 0), Y = (self.Entity:GetNetworkedFloat("OscY"..node_idx) or 0) }) + + node_idx = node_idx+1 + if (node_idx > 41) then node_idx = node_idx-41 end + end + + return nodes +end diff --git a/lua/entities/gmod_wire_output/cl_init.lua b/lua/entities/gmod_wire_output/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_output/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_output/init.lua b/lua/entities/gmod_wire_output/init.lua new file mode 100644 index 0000000000..fccef832bb --- /dev/null +++ b/lua/entities/gmod_wire_output/init.lua @@ -0,0 +1,68 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Output" + +local keylist = {"0","1","2","3","4","5","6","7","8","9",".","Enter","+","-","*","/"} + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self:SetOn( false ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "A" }) +end + + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + if ((value > 0) ~= self:IsOn()) then + self:Switch(not self:IsOn(), self:GetPlayer()) + end + end +end + + +function ENT:Switch( on, ply ) + local plyindex = self:GetPlayerIndex() + local key = self:GetKey() + + if (not key) then return end + + if (on) then + numpad.Activate( ply, _, {key}, plyindex ) + else + numpad.Deactivate( ply, _, {key}, plyindex ) + end + + self:SetOn(on) +end + +function ENT:ShowOutput() + if (self.key) then + if (keylist[self.key + 1]) then + self:SetOverlayText("Numpad Output ("..keylist[self.key + 1]..")") + end + end +end + +function ENT:SetKey( key ) + self.Key = key + self:ShowOutput() +end + +function ENT:GetKey() + return self.Key +end + +function ENT:SetOn( on ) + self.On = on +end + +function ENT:IsOn() + return self.On +end diff --git a/lua/entities/gmod_wire_output/shared.lua b/lua/entities/gmod_wire_output/shared.lua new file mode 100644 index 0000000000..06750a1eec --- /dev/null +++ b/lua/entities/gmod_wire_output/shared.lua @@ -0,0 +1,8 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Numpad Output" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" diff --git a/lua/entities/gmod_wire_panel/cl_init.lua b/lua/entities/gmod_wire_panel/cl_init.lua new file mode 100644 index 0000000000..6be0fedc68 --- /dev/null +++ b/lua/entities/gmod_wire_panel/cl_init.lua @@ -0,0 +1,236 @@ + +include('shared.lua') +CreateClientConVar( "wire_panel_chan", 1, true, true ) --client variable to server goodness + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Initialize() + self.menu = nil + self.click = 0 + self.chan = 1 + self.disp1 = 0 + + //Edit the menu here. Maximum of 10 lines. + self.menus = {"","", //Do not use these. The menus start at 3. + "Ch. 1|Channel 1", + "Ch. 2|Channel 2", + "Ch. 3|Channel 3", + "Ch. 4|Channel 4", + "Ch. 5|Channel 5", + "Ch. 6|Channel 6", + "Ch. 7|Channel 7", + "Ch. 8|Channel 8" + } + + surface.CreateFont( "coolvetica", 80, 400, false, false, "panel_font" ) + +end + +function ENT:Draw() + self.Entity:DrawModel() + + local OF = 0 + local OU = 0 + local OR = 0 + local Res = 0.1 + local RatioX = 1 + + if self.Entity:GetModel() == "models/props_lab/monitor01b.mdl" then + OF = 6.53 + OU = 0 + OR = 0 + Res = 0.05 + elseif self.Entity:GetModel() == "models/kobilica/wiremonitorsmall.mdl" then + OF = 0.2 + OU = 4.5 + OR = -0.85 + Res = 0.045 + elseif self.Entity:GetModel() == "models/kobilica/wiremonitorbig.mdl" then + OF = 0.3 + OU = 11.8 + OR = -2.35 + Res = 0.12 + elseif self.Entity:GetModel() == "models/props/cs_office/computer_monitor.mdl" then + OF = 3.25 + OU = 15.85 + OR = -2.2 + Res = 0.085 + RatioX = 0.75 + elseif self.Entity:GetModel() == "models/props/cs_office/TV_plasma.mdl" then + OF = 6.1 + OU = 17.05 + OR = -5.99 + Res = 0.175 + RatioX = 0.57 + end + + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + + local pos = self.Entity:GetPos() + (self.Entity:GetForward() * OF) + (self.Entity:GetUp() * OU) + (self.Entity:GetRight() * OR) + + cam.Start3D2D(pos,ang,Res) + local clicker = LocalPlayer():GetNetworkedInt(self.Entity:EntIndex().."click") + local click = false + if clicker >= 8 and self.click ~= 0 then + self.click = 0 + click = true + elseif clicker < 8 then + if clicker > self.click then + self.click = clicker + click = true + end + end + + local x = -112 + local y = -104 + local w = 296 + local h = 292 + + local x1 = -5.535 + local x2 = 3.5 + local y1 = 5.091 + local y2 = -4.1 + + local ox = 5 + local oy = 5 + + local pos + local cx + local cy + local onscreen = false + + local trace = {} + trace.start = LocalPlayer():GetShootPos() + trace.endpos = LocalPlayer():GetAimVector() * 64 + trace.start + trace.filter = LocalPlayer() + local trace = util.TraceLine(trace) + + local control = LocalPlayer():GetNetworkedBool(self.Entity:EntIndex().."control") + if control then + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(x/RatioX,y,(x+w)/RatioX,y+h) + + pos = self.Entity:WorldToLocal(trace.HitPos) +-- pos = self.Entity:GetPos() + (self.Entity:GetForward() * OF) + (self.Entity:GetUp() * OU) + (self.Entity:GetRight() * OR) + local posfix_x + local posfix_y + if (OR == 0) then + posfix_x = 1 + else + posfix_x = math.abs(OR) + end + if (OU == 0) then + posfix_y = 1 + else + posfix_y = math.abs(OU) + end +-- cx = (pos.y - x1) / (math.abs(x1) + math.abs(x2)) + cx = (((pos.y + OR)/math.abs(posfix_x)) - x1) / (math.abs(x1) + math.abs(x2)) +-- cy = 1 - ((pos.z + y1) / (math.abs(y1) + math.abs(y2))) +-- cy = 1 - (((pos.z + (OU / 2)) + (y1 - OU)) / (math.abs(y1 - OU) + math.abs(y2 - OU))) + cy = 1 - (((pos.z - OU) + y1)) / (math.abs(y1) + math.abs(y2)) + if trace.Entity == self.Entity and cx >= 0 and cy >= 0 and cx <= 1 and cy <= 1 then + onscreen = true + end + else + self.menu = nil + + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(x/RatioX,y,(x+w)/RatioX,y+h) + + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(surface.GetTextureID("gui/info")) + surface.DrawTexturedRect((x+(w*.4*.621))/RatioX,y + h*.4*.621,w*.2*.621,w*.2*.621) + end + + + if control then + surface.SetDrawColor(100,100,100,255) + surface.DrawRect((x+(w*0*.621))/RatioX,y+(h*0*.621),w*.3*.621,h*1*.621) + if self.menu then + if ((self.menu-2) == self.chan) then + surface.SetDrawColor(0,100,0,255) + else + surface.SetDrawColor(100,0,0,255) + end + surface.DrawRect((x+(w*.3*.621))/RatioX,y+(h*.5*.621),w*.7*.621,h*.5*.621) + draw.DrawText("SET","panel_font",(x+(w*.32*.621))/RatioX,(y+(h*.55*.621)),Color(255,255,255,255),0) + end + end + if control and onscreen then + if (math.abs(pos.x - OF) < 1.0) then + if cx <= .3 then + for i = 3,10 do + if cy >= .1*i-.1 and cy < .1*i then + surface.SetDrawColor(30,144,255,100) + surface.DrawRect((x+(w*0*.621))/RatioX,y+(h*(.1*i-.1)*.621),w*.3*.621,h*.1*.621) + if click then + self.menu = i + end + end + end + else + if cy >= 0.5 then + if self.menu then + if ((self.menu-2) == self.chan) then + surface.SetDrawColor(0,150,0,255) + else + surface.SetDrawColor(150,0,0,255) + end + surface.DrawRect((x+(w*.3*.621))/RatioX,y+(h*.5*.621),w*.7*.621,h*.5*.621) + draw.DrawText("SET","panel_font",(x+(w*.32*.621))/RatioX,(y+(h*.55*.621)),Color(255,255,255,255),0) + if click then + self.chan = self.menu-2 + LocalPlayer():ConCommand("wire_panel_chan "..self.chan.."\n") + end + end + end + end + end + end + if control then + local out = "Channel\nIndex\n" + for i = 3, 10 do + local disp = self.menus[i] + local loc = string.find(disp,"|", 1, true) + if loc then + out = out..string.sub(disp,1,loc-1).."\n" + else + out = out.."\n" + end + end + draw.DrawText(out,"Trebuchet18",(x+2)/RatioX,y,Color(255,255,255,255)) + if self.menu then + local ChannelValue = self:GetChannelValue( self.menu-2 ) + local disp = self.menus[self.menu].."\n\n"..string.format("%.2f", ChannelValue) + local loc = string.find(disp,"|", 1, true) + if loc then + local disp = string.sub(disp,loc+1) + draw.DrawText(disp,"Trebuchet18",(x+2+(w*.3*.621))/RatioX,y,Color(255,255,255,255)) + end + end + end + if control and onscreen then + if (math.abs(pos.x - OF) < 1.0) then + surface.SetDrawColor(255,255,255,255) + surface.SetTexture(surface.GetTextureID("gui/arrow")) + surface.DrawTexturedRectRotated((x+(w*cx*.621)+ox)/RatioX,y+(h*cy*.621)+oy,16,16,45) + end + else + end + + + cam.End3D2D() + + Wire_Render(self.Entity) +end + +function ENT:IsTranslucent() + return true +end diff --git a/lua/entities/gmod_wire_panel/init.lua b/lua/entities/gmod_wire_panel/init.lua new file mode 100644 index 0000000000..b5952c77ed --- /dev/null +++ b/lua/entities/gmod_wire_panel/init.lua @@ -0,0 +1,147 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Panel" + +function ENT:OnRemove() +-- SetGlobalInt( "chan", nil ) + for i,pl in pairs(player.GetAll()) do + pl:SetNetworkedInt(self.Entity:EntIndex().."click",nil) + end +end + + +/*--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------*/ +function ENT:Initialize() + self.click = 0 + self.chan = 1 + + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "Ch1", "Ch2", "Ch3", "Ch4", "Ch5", "Ch6", "Ch7", "Ch8" }) + +-- SetGlobalInt( "chan", self.chan ) + + self.Entity:SetNetworkedInt('chan',self.chan) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) + + for i,pl in pairs(player.GetAll()) do + pl:SetNetworkedInt(self.Entity:EntIndex().."click",self.click) + end +end + + +function ENT:Setup() + for i = 0, 7 do + self:SetChannelValue( i, string.format("%.2f", 0.0) ) + end +end + + +function ENT:Use() +end + +function ENT:Think() + self.BaseClass.Think(self) + + for i,pl in pairs(player.GetAll()) do + local trace = {} + trace.start = pl:GetShootPos() + trace.endpos = pl:GetAimVector() * 64 + trace.start + trace.filter = pl + local trace = util.TraceLine(trace) + + if trace.Entity == self.Entity then + pl:SetNetworkedBool(self.Entity:EntIndex().."control",true) + local s_set = self.chan + local c_set = pl:GetInfoNum("wire_panel_chan", 1) + if s_set != c_set then + if (c_set > 0) then + --Msg("Think: Set changed, updating var.\n") + self.chan = c_set + local value = self:GetChannelValue( self.chan ) + pl:ConCommand("wire_panel_chan 0\n") + Wire_TriggerOutput(self.Entity, "Out", value) + end + end + else + pl:SetNetworkedBool(self.Entity:EntIndex().."control",false) + end + end + + self.Entity:NextThink(CurTime()+0.08) + return true +end + +function ENT:AcceptInput(name,activator,caller) + if name == "Use" and caller:IsPlayer() and caller:KeyDownLast(IN_USE) == false then + self.click = self.click + 1 + if self.click > 8 then + self.click = 1 + end + caller:SetNetworkedInt(self.Entity:EntIndex().."click",self.click) + end +end + + +function ENT:TriggerInput(iname, value, iter) + if (iname == "Ch1") then + if (self.chan == 1) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 1, string.format("%.2f", value) ) + elseif (iname == "Ch2") then + if (self.chan == 2) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 2, string.format("%.2f", value) ) + elseif (iname == "Ch3") then + if (self.chan == 3) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 3, string.format("%.2f", value) ) + elseif (iname == "Ch4") then + if (self.chan == 4) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 4, string.format("%.2f", value) ) + elseif (iname == "Ch5") then + if (self.chan == 5) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 5, string.format("%.2f", value) ) + elseif (iname == "Ch6") then + if (self.chan == 6) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 6, string.format("%.2f", value) ) + elseif (iname == "Ch7") then + if (self.chan == 7) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 7, string.format("%.2f", value) ) + elseif (iname == "Ch8") then + if (self.chan == 8) then Wire_TriggerOutput(self.Entity, "Out", value, iter) end + self:SetChannelValue( 8, string.format("%.2f", value) ) + end + +end + + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) +end + + +function MakeWirePanel( pl, Pos, Ang, model ) + + if ( !pl:CheckLimit( "wire_panels" ) ) then return false end + + local wire_panel = ents.Create( "gmod_wire_panel" ) + if (!wire_panel:IsValid()) then return false end + wire_panel:SetModel(model) + + wire_panel:SetAngles( Ang ) + wire_panel:SetPos( Pos ) + wire_panel:Spawn() + + wire_panel:SetPlayer(pl) + + pl:AddCount( "wire_panels", wire_panel ) + + return wire_panel + +end + +duplicator.RegisterEntityClass("gmod_wire_panel", MakeWirePanel, "Pos", "Ang", "Model") diff --git a/lua/entities/gmod_wire_panel/shared.lua b/lua/entities/gmod_wire_panel/shared.lua new file mode 100644 index 0000000000..3a31a68b07 --- /dev/null +++ b/lua/entities/gmod_wire_panel/shared.lua @@ -0,0 +1,34 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Control Panel" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetChannelValue( int, float ) + if (int == 1) then self.Entity:SetNetworkedFloat( "cVal1", float ) end + if (int == 2) then self.Entity:SetNetworkedFloat( "cVal2", float ) end + if (int == 3) then self.Entity:SetNetworkedFloat( "cVal3", float ) end + if (int == 4) then self.Entity:SetNetworkedFloat( "cVal4", float ) end + if (int == 5) then self.Entity:SetNetworkedFloat( "cVal5", float ) end + if (int == 6) then self.Entity:SetNetworkedFloat( "cVal6", float ) end + if (int == 7) then self.Entity:SetNetworkedFloat( "cVal7", float ) end + if (int == 8) then self.Entity:SetNetworkedFloat( "cVal8", float ) end +end + +function ENT:GetChannelValue( int ) + if (int == 1) then return self.Entity:GetNetworkedFloat( "cVal1" ) end + if (int == 2) then return self.Entity:GetNetworkedFloat( "cVal2" ) end + if (int == 3) then return self.Entity:GetNetworkedFloat( "cVal3" ) end + if (int == 4) then return self.Entity:GetNetworkedFloat( "cVal4" ) end + if (int == 5) then return self.Entity:GetNetworkedFloat( "cVal5" ) end + if (int == 6) then return self.Entity:GetNetworkedFloat( "cVal6" ) end + if (int == 7) then return self.Entity:GetNetworkedFloat( "cVal7" ) end + if (int == 8) then return self.Entity:GetNetworkedFloat( "cVal8" ) end +end diff --git a/lua/entities/gmod_wire_pixel/cl_init.lua b/lua/entities/gmod_wire_pixel/cl_init.lua new file mode 100644 index 0000000000..667d5503e4 --- /dev/null +++ b/lua/entities/gmod_wire_pixel/cl_init.lua @@ -0,0 +1,15 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Draw( ) + self:DrawModel( ) +end + +function ENT:DrawEntityOutline( ff ) +end + +function ENT:DoNormalDraw( ) + +end diff --git a/lua/entities/gmod_wire_pixel/init.lua b/lua/entities/gmod_wire_pixel/init.lua new file mode 100644 index 0000000000..46cdcc02e6 --- /dev/null +++ b/lua/entities/gmod_wire_pixel/init.lua @@ -0,0 +1,86 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Pixel" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.R, self.G, self.B = 0, 0, 0 + self.Inputs = Wire_CreateInputs( self.Entity, { "Red", "Green", "Blue", "PackedRGB", "RGB" } ) +end + +function ENT:Think( ) +end + +function ENT:TriggerInput(iname, value) + local R,G,B = self.R, self.G, self.B + if (iname == "Red") then + R = value + elseif (iname == "Green") then + G = value + elseif (iname == "Blue") then + B = value + elseif (iname == "PackedRGB") then + B = value % 256 + G = ( value / 256 ) % 256 + R = ( value / ( 256 * 256 ) ) % 256 + elseif (iname == "RGB") then + local crgb = math.floor( value / 1000 ) + local cgray = value - math.floor( value / 1000 ) * 1000 + local cb = 24 * math.fmod( crgb, 10 ) + local cg = 24 * math.fmod( math.floor( crgb / 10 ), 10 ) + local cr = 24 * math.fmod( math.floor( crgb / 100 ), 10 ) + B = cgray + cb + G = cgray + cg + R = cgray + cr + end + self:ShowOutput( math.floor( R ), math.floor( G ), math.floor( B ) ) +end + +function ENT:Setup() + self:ShowOutput( 0, 0, 0 ) +end + +function ENT:ShowOutput( R, G, B ) + if ( R ~= self.R or G ~= self.G or B ~= self.B ) then + --self:SetOverlayText( "Pixel: Red=" .. R .. " Green:" .. G .. " Blue:" .. B ) + self.R, self.G, self.B = R, G, B + self.Entity:SetColor( R, G, B, 255 ) + end +end + + +function MakeWirePixel( pl, Pos, Ang, model, nocollide) + if ( !pl:CheckLimit( "wire_pixels" ) ) then return false end + + local wire_pixel = ents.Create( "gmod_wire_pixel" ) + if (!wire_pixel:IsValid()) then return false end + + wire_pixel:SetModel( model ) + wire_pixel:SetAngles( Ang ) + wire_pixel:SetPos( Pos ) + wire_pixel:Spawn() + + wire_pixel:Setup() + wire_pixel:SetPlayer(pl) + + if ( nocollide == true ) then wire_pixel:SetCollisionGroup(COLLISION_GROUP_WORLD) end + + local ttable = { + pl = pl, + nocollide = nocollide + } + table.Merge(wire_pixel:GetTable(), ttable ) + + pl:AddCount( "wire_pixels", wire_pixel ) + + return wire_pixel +end + +duplicator.RegisterEntityClass("gmod_wire_pixel", MakeWirePixel, "Pos", "Ang", "Model", "nocollide") diff --git a/lua/entities/gmod_wire_pixel/shared.lua b/lua/entities/gmod_wire_pixel/shared.lua new file mode 100644 index 0000000000..61ae9b6daa --- /dev/null +++ b/lua/entities/gmod_wire_pixel/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Pixel" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_plug/cl_init.lua b/lua/entities/gmod_wire_plug/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_plug/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_plug/init.lua b/lua/entities/gmod_wire_plug/init.lua new file mode 100644 index 0000000000..5f94d222c9 --- /dev/null +++ b/lua/entities/gmod_wire_plug/init.lua @@ -0,0 +1,130 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +local MODEL = Model( "models/props_lab/tpplug.mdl" ) + +ENT.WireDebugName = "Plug" + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.MySocket = nil + + self.Inputs = Wire_CreateInputs(self.Entity, { "A","B","C","D","E","F","G","H" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "A","B","C","D","E","F","G","H" }) +end + +function ENT:SetValue(index,value) + if (self.MySocket.Const) and (self.MySocket.Const:IsValid()) then + Wire_TriggerOutput(self.Entity, index, value) + else + Wire_TriggerOutput(self.Entity, index, 0) + end + + self:ShowOutput() +end + +function ENT:OnRemove() + self.BaseClass.Think(self) + + if (self.MySocket) and (self.MySocket:IsValid()) then + self.MySocket.MyPlug = nil + end +end + +function ENT:Setup() + self:ShowOutput() +end + +function ENT:TriggerInput(iname, value) + if (self.MySocket) and (self.MySocket:IsValid()) then + self.MySocket:SetValue(iname, value) + end + self:ShowOutput() +end + +function ENT:SetSocket(socket) + if (socket == nil) then + for i,v in pairs(self.Outputs)do + Wire_TriggerOutput(self.Entity, v.Name, 0) + end + self:ShowOutput() + end + self.MySocket = socket +end + +function ENT:AttachedToSocket(socket) + for i,v in pairs(self.Inputs)do + socket:SetValue(v.Name,v.Value) + end + self:ShowOutput() +end + +function ENT:ShowOutput(value) + self.OutText = "Plug:" + if (self.Inputs) then + self.OutText = self.OutText .. "\nInputs: " + if (self.Inputs.A.Value) then + self.OutText = self.OutText .. " A:" .. self.Inputs.A.Value + end + if (self.Inputs.B.Value) then + self.OutText = self.OutText .. " B:" .. self.Inputs.B.Value + end + if (self.Inputs.C.Value) then + self.OutText = self.OutText .. " C:" .. self.Inputs.C.Value + end + if (self.Inputs.D.Value) then + self.OutText = self.OutText .. " D:" .. self.Inputs.D.Value + end + if (self.Inputs.E.Value) then + self.OutText = self.OutText .. " E:" .. self.Inputs.E.Value + end + if (self.Inputs.F.Value) then + self.OutText = self.OutText .. " F:" .. self.Inputs.F.Value + end + if (self.Inputs.G.Value) then + self.OutText = self.OutText .. " G:" .. self.Inputs.G.Value + end + if (self.Inputs.H.Value) then + self.OutText = self.OutText .. " H:" .. self.Inputs.H.Value + end + end + if (self.Outputs) then + self.OutText = self.OutText .. "\nOutputs: " + if (self.Outputs.A.Value) then + self.OutText = self.OutText .. " A:" .. self.Outputs.A.Value + end + if (self.Outputs.B.Value) then + self.OutText = self.OutText .. " B:" .. self.Outputs.B.Value + end + if (self.Outputs.C.Value) then + self.OutText = self.OutText .. " C:" .. self.Outputs.C.Value + end + if (self.Outputs.D.Value) then + self.OutText = self.OutText .. " D:" .. self.Outputs.D.Value + end + if (self.Outputs.E.Value) then + self.OutText = self.OutText .. " E:" .. self.Outputs.E.Value + end + if (self.Outputs.F.Value) then + self.OutText = self.OutText .. " F:" .. self.Outputs.F.Value + end + if (self.Outputs.G.Value) then + self.OutText = self.OutText .. " G:" .. self.Outputs.G.Value + end + if (self.Outputs.H.Value) then + self.OutText = self.OutText .. " H:" .. self.Outputs.H.Value + end + end + self:SetOverlayText(self.OutText) +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) +end diff --git a/lua/entities/gmod_wire_plug/shared.lua b/lua/entities/gmod_wire_plug/shared.lua new file mode 100644 index 0000000000..2f6c19af9e --- /dev/null +++ b/lua/entities/gmod_wire_plug/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Plug" +ENT.Author = "UnDeR_SEEG" +ENT.Contact = "facepunch forums" +ENT.Purpose = "plug fits into socket" +ENT.Instructions = "move close to a socket to plug it in, and the signal will be carried through!" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_pod/cl_init.lua b/lua/entities/gmod_wire_pod/cl_init.lua new file mode 100644 index 0000000000..9510a8f0b3 --- /dev/null +++ b/lua/entities/gmod_wire_pod/cl_init.lua @@ -0,0 +1,11 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_pod/init.lua b/lua/entities/gmod_wire_pod/init.lua new file mode 100644 index 0000000000..e413d3412b --- /dev/null +++ b/lua/entities/gmod_wire_pod/init.lua @@ -0,0 +1,153 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Pod Controller" + +local MODEL = Model("models/jaanus/wiretool/wiretool_siren.mdl") + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + -- Output keys. Format: self.Keys["name"] = IN_* + self.Keys = { } + self.Keys["Attack"] = IN_ATTACK + self.Keys["Attack2"] = IN_ATTACK2 + self.Keys["Forward"] = IN_FORWARD + self.Keys["Left"] = IN_MOVELEFT + self.Keys["Back"] = IN_BACK + self.Keys["Right"] = IN_MOVERIGHT + self.Keys["Reload"] = IN_RELOAD + self.Keys["Jump"] = IN_JUMP + self.Keys["Duck"] = IN_DUCK + self.Keys["Sprint"] = IN_SPEED + self.Keys["Zoom"] = IN_ZOOM + + -- Invert the table to use it with Wire_CreateOutputs + local outputs = { } + local n = 1 + + for k, v in pairs( self.Keys ) do + outputs[n] = k + n = n + 1 + end + + outputs[n] = "Active" + + self.VPos = Vector(0, 0, 0) + + -- Create outputs + self.Outputs = Wire_CreateOutputs( self.Entity, outputs ) + self.Inputs = Wire_CreateInputs( self.Entity, { "Lock", "Eject", "Crosshair", "Open" } ) + self:SetOverlayText( "Pod Controller" ) +end + +function ENT:SetKeys(keys) + self.Keys = keys + local out = {} + for k,v in pairs(keys) do + out[#out+1] = k + end + out[#out+1] = "Active" + WireLib.AdjustOutputs(self.Entity, out) +end + +-- Link to pod +function ENT:Setup(pod) + self.Pod = pod +end + +function ENT:ShowOutput(value) + if value ~= self.PrevOutput then + self:SetOverlayText( "Pod Controller" ) + self.PrevOutput = value + end +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) +end + +-- Called every 0.01 seconds, check for key down +function ENT:Think() + -- Check that we have a pod + if self.Pod and self.Pod:IsValid() then + self.Ply = self.Pod:GetPassenger() + + if self.Ply and self.Ply:IsValid() and self.Keys then + -- Loop through all the self.Keys, and check if they was pressed last frame + for k, v in pairs(self.Keys) do + if self.Ply:KeyDownLast(v) then + Wire_TriggerOutput(self.Entity, k, 1) + else + Wire_TriggerOutput(self.Entity, k, 0) + end + end + local trace = util.GetPlayerTrace(self.Ply) + trace.filter = self.Pod + self.VPos = util.TraceLine(trace).HitPos + Wire_TriggerOutput(self.Entity, "Active", 1) + else + Wire_TriggerOutput(self.Entity, "Active", 0) + end + end + self.Entity:NextThink(CurTime() + 0.01) + return true +end + +function ENT:TriggerInput(iname, value) + if not (self.Pod and self.Pod:IsValid()) then return end + if iname == "Lock" then + if value ~= 0 then + self.Pod:Fire("Lock", "1", 0) + else + self.Pod:Fire("Unlock", "1", 0) + end + elseif iname == "Eject" then + if value ~= 0 then + self.Pod:Fire("ExitVehicle", "1", 0) + end + elseif iname == "Crosshair" and self.Ply and self.Ply:IsValid() then + if value ~= 0 then + self.Ply:CrosshairEnable() + else + self.Ply:CrosshairDisable() + end + elseif iname == "Open" then + if value ~= 0 then + self.Pod:Fire("Open", "1", 0) + else + self.Pod:Fire("Close", "1", 0) + end + end +end + +function ENT:GetBeaconPos(sensor) + return self.VPos +end + +-- Duplicator support to save pod link (TAD2020) +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if self.Pod and self.Pod:IsValid() then + info.pod = self.Pod:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if info.pod then + self.Pod = GetEntByID(info.pod) + if not (self.Pod and self.Pod:IsValid()) then + self.Pod = ents.GetByIndex(info.pod) + end + end +end diff --git a/lua/entities/gmod_wire_pod/shared.lua b/lua/entities/gmod_wire_pod/shared.lua new file mode 100644 index 0000000000..b5ba56642e --- /dev/null +++ b/lua/entities/gmod_wire_pod/shared.lua @@ -0,0 +1,37 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Pod Controller" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_radio/cl_init.lua b/lua/entities/gmod_wire_radio/cl_init.lua new file mode 100644 index 0000000000..9dca17e409 --- /dev/null +++ b/lua/entities/gmod_wire_radio/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_radio/init.lua b/lua/entities/gmod_wire_radio/init.lua new file mode 100644 index 0000000000..2c51599424 --- /dev/null +++ b/lua/entities/gmod_wire_radio/init.lua @@ -0,0 +1,136 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Radio" + +local MODEL = Model( "models/props_lab/binderblue.mdl" ) + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self, { "Channel"}) + self.Outputs = Wire_CreateOutputs(self, { "ERRORS!!!" }) + + self.Channel = 0 + self.Values = 4 + self.RecievedData = {} + for i=0,31 do + self.RecievedData[i] = {} + self.RecievedData[i].Owner = nil + self.RecievedData[i].Data = 0 + end + self.SentData = {} + for i=0,31 do + self.SentData[i] = 0 + end + + Radio_Register(self) + Radio_RecieveData(self) +end + +function ENT:Setup(channel,values,secure) + channel = math.floor(tonumber(channel) or 0) + self.Secure = secure + self.Old = false + if (tonumber(values) == nil) then + values = 4 + self.Old = true + else + values = math.Clamp(math.floor(values),1,32) + end + + self.Values = values + local onames = {} + if (self.Old == false) then + for i = 1,self.Values do + onames[i] = tostring(i) //without tostring() you kill the debugger. + end + else + onames = {"A","B","C","D"} + end + + Wire_AdjustOutputs(self,onames) + table.insert(onames,"Channel") + Wire_AdjustInputs(self,onames) + + self.Channel = channel + Radio_ChangeChannel(self) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Channel") then + self.Channel = math.floor(value) + Radio_ChangeChannel(self) + + elseif (iname != nil && value != nil) then + if (self.Old == true) then + if (iname == "A") then + Radio_SendData(self,self.Channel,0,value) + elseif (iname == "B") then + Radio_SendData(self,self.Channel,1,value) + elseif (iname == "C") then + Radio_SendData(self,self.Channel,2,value) + elseif (iname == "D") then + Radio_SendData(self,self.Channel,3,value) + end + else + Radio_SendData(self,tonumber(iname)-1,value) + end + end + self:ShowOutput() +end + +function ENT:NotifyDataRecieved(subch) + Wire_TriggerOutput(self,tostring(subch+1),self.RecievedData[subch].Data) +end + +function ENT:ReadCell(Address) + if (Address >= 0) && (Address < self.Values) then + return self.RecievedData[Address].Data + else + return nil + end +end + +function ENT:WriteCell(Address, value) + if (Address >= 0) && (Address < self.Values) then + Radio_SendData(self,Address,value) + return true + else + return false + end +end + +function ENT:ShowOutput() + if (self.Old == true) then + self:SetOverlayText( "(Channel " .. self.Channel .. ") Transmit A: " .. (self.Inputs.A.Value or 0) .. " B: " .. (self.Inputs.B.Value or 0) .. " C: " .. (self.Inputs.C.Value or 0) .. " D: " .. (self.Inputs.D.Value or 0) .. "\nReceive A: " .. (self.Outputs.A.Value or 0) .. " B: " .. (self.Outputs.B.Value or 0) .. " C: " .. (self.Outputs.C.Value or 0) .. " D: " .. (self.Outputs.D.Value or 0) ) + else + local overlay = "(Channel " .. self.Channel .. ") Transmit" + for i=1,self.Values do + overlay = overlay .. " " .. i .. ":" .. + math.Round((self.SentData[i-1])*1000)/1000 + end + overlay = overlay .. "\nReceive" + for i=1,self.Values do + overlay = overlay .. " " .. i .. ":" .. + math.Round((self.RecievedData[i-1].Data)*1000)/1000 + end + if (self.Secure == true) then overlay = overlay .. "\nSecured" end + self:SetOverlayText(overlay) + end +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) + Radio_Register(self) +end + +function ENT:OnRemove() + if (!self.Channel) then return end + Radio_Unregister(self) +end diff --git a/lua/entities/gmod_wire_radio/shared.lua b/lua/entities/gmod_wire_radio/shared.lua new file mode 100644 index 0000000000..1c5fe23c51 --- /dev/null +++ b/lua/entities/gmod_wire_radio/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Radio" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_ranger/cl_init.lua b/lua/entities/gmod_wire_ranger/cl_init.lua new file mode 100644 index 0000000000..6dca30d004 --- /dev/null +++ b/lua/entities/gmod_wire_ranger/cl_init.lua @@ -0,0 +1,9 @@ +ENT.Spawnable = false +ENT.AdminSpawnable = false + +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_ranger/init.lua b/lua/entities/gmod_wire_ranger/init.lua new file mode 100644 index 0000000000..0789aae6fe --- /dev/null +++ b/lua/entities/gmod_wire_ranger/init.lua @@ -0,0 +1,336 @@ +AddCSLuaFile( "cl_init.lua" ) + +AddCSLuaFile( "shared.lua" ) + + +include('shared.lua') + + +ENT.WireDebugName = "Ranger" + + +local MODEL = Model("models/jaanus/wiretool/wiretool_range.mdl") + + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:StartMotionController() + + self.Inputs = Wire_CreateInputs(self.Entity, { "X", "Y", "SelectValue"}) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Dist" }) + self.hires = false +end + +function ENT:Setup( range, default_zero, show_beam, ignore_world, trace_water, out_dist, out_pos, out_vel, out_ang, out_col, out_val, out_sid, out_uid, out_eid, out_hnrm, hiRes ) + --for duplication + self.range = range + self.default_zero = default_zero + self.show_beam = show_beam + self.ignore_world = ignore_world + self.trace_water = trace_water + self.out_dist = out_dist + self.out_pos = out_pos + self.out_vel = out_vel + self.out_ang = out_ang + self.out_col = out_col + self.out_val = out_val + self.out_sid = out_sid + self.out_uid = out_uid + self.out_eid = out_eid + self.out_hnrm = out_hnrm + self.hires = hires + + self.PrevOutput = nil + + if (show_beam) then + self:SetBeamLength(math.min(self.range, 2000)) + else + self:SetBeamLength(0) + end + + self.Entity:SetNetworkedBool("TraceWater", trace_water) + + local onames, otypes = {}, {} + if (out_dist) then + table.insert(onames, "Dist") table.insert(otypes, "NORMAL") + end + if (out_pos) then + table.insert(onames, "Pos") table.insert(otypes, "VECTOR") + table.insert(onames, "Pos X") table.insert(otypes, "NORMAL") + table.insert(onames, "Pos Y") table.insert(otypes, "NORMAL") + table.insert(onames, "Pos Z") table.insert(otypes, "NORMAL") + end + if (out_vel) then + table.insert(onames, "Vel") table.insert(otypes, "VECTOR") + table.insert(onames, "Vel X") table.insert(otypes, "NORMAL") + table.insert(onames, "Vel Y") table.insert(otypes, "NORMAL") + table.insert(onames, "Vel Z") table.insert(otypes, "NORMAL") + end + if (out_ang) then + table.insert(onames, "Ang") table.insert(otypes, "ANGLE") + table.insert(onames, "Ang Pitch") table.insert(otypes, "NORMAL") + table.insert(onames, "Ang Yaw") table.insert(otypes, "NORMAL") + table.insert(onames, "Ang Roll") table.insert(otypes, "NORMAL") + end + if (out_col) then + table.insert(onames, "Col RGB") table.insert(otypes, "VECTOR") + table.insert(onames, "Col R") table.insert(otypes, "NORMAL") + table.insert(onames, "Col G") table.insert(otypes, "NORMAL") + table.insert(onames, "Col B") table.insert(otypes, "NORMAL") + table.insert(onames, "Col A") table.insert(otypes, "NORMAL") + end + if (out_val) then + table.insert(onames, "Val") table.insert(otypes, "NORMAL") + table.insert(onames, "ValSize") table.insert(otypes, "NORMAL") + end + if (out_sid) then + table.insert(onames, "SteamID") table.insert(otypes, "STRING") + end + if (out_uid) then + table.insert(onames, "UniqueID") table.insert(otypes, "NORMAL") + end + if (out_eid) then + table.insert(onames, "EntID") table.insert(otypes, "NORMAL") + table.insert(onames, "Entity") table.insert(otypes, "ENTITY") + end + if (out_hnrm) then + table.insert(onames, "HitNormal") table.insert(otypes, "VECTOR") + table.insert(onames, "HitNormal X") table.insert(otypes, "NORMAL") + table.insert(onames, "HitNormal Y") table.insert(otypes, "NORMAL") + table.insert(onames, "HitNormal Z") table.insert(otypes, "NORMAL") + end + table.insert(onames, "RangerData") table.insert(otypes, "RANGER") + WireLib.AdjustSpecialOutputs(self.Entity, onames, otypes) + + self:TriggerOutput(0, Vector(0, 0, 0), Vector(0, 0, 0), Angle(0, 0, 0), Color(255, 255, 255, 255),nil,0,0,NULL, Vector(0, 0, 0),nil) + self:ShowOutput() +end + +function ENT:TriggerInput(iname, value) + if (iname == "X") then + self:SetSkewX(value) + elseif (iname == "Y") then + self:SetSkewY(value) + end +end + +function ENT:Think() + self.BaseClass.Think(self) + + local trace = {} + trace.start = self.Entity:GetPos() + if (self.Inputs.X.Value == 0 and self.Inputs.Y.Value == 0) then + trace.endpos = trace.start + self.Entity:GetUp()*self.range + else + local skew = Vector(self.Inputs.X.Value, self.Inputs.Y.Value, 1) + skew = skew*(self.range/skew:Length()) + local beam_x = self.Entity:GetRight()*skew.x + local beam_y = self.Entity:GetForward()*skew.y + local beam_z = self.Entity:GetUp()*skew.z + trace.endpos = trace.start + beam_x + beam_y + beam_z + end + trace.filter = { self.Entity } + if (self.trace_water) then trace.mask = -1 end + trace = util.TraceLine(trace) + + local dist = 0 + local pos = Vector(0, 0, 0) + local vel = Vector(0, 0, 0) + local ang = Angle(0, 0, 0) + local col = Color(255, 255, 255, 255) + local ent = NULL + local sid = "" + local uid = 0 + local val = {} + local hnrm = Vector(0,0,0) + + if (trace.Hit) then + dist = trace.Fraction*self.range + pos = trace.HitPos + hnrm = trace.HitNormal + ent = trace.Entity + + if (ent:IsValid()) then + + vel = ent:GetVelocity() + ang = ent:GetAngles() + col = Color(ent:GetColor()) + + if (self.out_sid or self.out_uid) and (ent:IsPlayer()) then + sid = ent:SteamID() or "" + uid = tonumber(ent:UniqueID()) or -1 + end + + if (self.out_val and ent.Outputs) then + local i = 0 + for k,v in pairs(ent.Outputs) do + if (v.Value != nil) then + val[i] = v.Value + i = i + 1 + end + end + end + + elseif(self.ignore_world) then + if (self.default_zero) then + dist = 0 + else + dist = self.range + end + end + + else + if (not self.default_zero) then + dist = self.range + end + end + + if (COLOSSAL_SANDBOX) then + vel = vel * 6.25 + pos = pos * 6.25 + dist = dist * 6.25 + end + + self:TriggerOutput(dist, pos, vel, ang, col, val, sid, uid, ent, hnrm, trace) + self:ShowOutput() + + if (self.hires) then + self.Entity:NextThink(CurTime()+0.01) + else + self.Entity:NextThink(CurTime()+0.04) + end + + return true +end + +function ENT:ShowOutput() --this function is evil (very), should be done clientside + + local txt = "Max Range: " .. self.range + + if (self.out_dist) then + txt = txt .. "\nRange = " .. math.Round(self.Outputs["Dist"].Value*1000)/1000 + end + + if (self.out_pos) then + txt = txt .. "\nPosition = " + .. math.Round(self.Outputs["Pos X"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Pos Y"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Pos Z"].Value*1000)/1000 + end + + if (self.out_vel) then + txt = txt .. "\nVelocity = " + .. math.Round(self.Outputs["Vel X"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Vel Y"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Vel Z"].Value*1000)/1000 + end + + if (self.out_ang) then + txt = txt .. "\nAngles = " + .. math.Round(self.Outputs["Ang Pitch"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Ang Yaw"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Ang Roll"].Value*1000)/1000 + end + + if (self.out_col) then + txt = txt .. "\nColor = " + .. math.Round(self.Outputs["Col R"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Col G"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Col B"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["Col A"].Value*1000)/1000 + end + + if (self.out_val) then + txt = txt .. "\nValue = " .. math.Round((self.Outputs["Val"].Value)*1000)/1000 .. " ValSize = " .. self.Outputs["ValSize"].Value + end + + if (self.out_sid) then + txt = txt .. "\nSteamID = " .. (self.Outputs["SteamID"].Value or "") + end + + if (self.out_uid) then + txt = txt .. "\nUniqueID = " .. (self.Outputs["UniqueID"].Value or 0) + end + + if (self.out_eid) then + txt = txt .. "\nEntID = " .. (self.Outputs["EntID"].Value or 0) + end + + if (self.out_hnrm) then + txt = txt .. "\nHitNormal = " + .. math.Round(self.Outputs["HitNormal X"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["HitNormal Y"].Value*1000)/1000 .. ", " + .. math.Round(self.Outputs["HitNormal Z"].Value*1000)/1000 + end + + self:SetOverlayText(txt) +end + +function ENT:TriggerOutput(dist, pos, vel, ang, col, val, sid, uid, ent, hnrm, trace) + + if (self.out_dist) then + Wire_TriggerOutput(self.Entity, "Dist", dist) + end + + if (self.out_pos) then + Wire_TriggerOutput(self.Entity, "Pos", pos) + Wire_TriggerOutput(self.Entity, "Pos X", pos.x) + Wire_TriggerOutput(self.Entity, "Pos Y", pos.y) + Wire_TriggerOutput(self.Entity, "Pos Z", pos.z) + end + + if (self.out_vel) then + Wire_TriggerOutput(self.Entity, "Vel", vel) + Wire_TriggerOutput(self.Entity, "Vel X", vel.x) + Wire_TriggerOutput(self.Entity, "Vel Y", vel.y) + Wire_TriggerOutput(self.Entity, "Vel Z", vel.z) + end + + if (self.out_ang) then + Wire_TriggerOutput(self.Entity, "Ang", ang) + Wire_TriggerOutput(self.Entity, "Ang Pitch", ang.p) + Wire_TriggerOutput(self.Entity, "Ang Yaw", ang.y) + Wire_TriggerOutput(self.Entity, "Ang Roll", ang.r) + end + + if (self.out_col) then + Wire_TriggerOutput(self.Entity, "Col RGB", Vector(col.r, col.g, col.b)) + Wire_TriggerOutput(self.Entity, "Col R", col.r) + Wire_TriggerOutput(self.Entity, "Col G", col.g) + Wire_TriggerOutput(self.Entity, "Col B", col.b) + Wire_TriggerOutput(self.Entity, "Col A", col.a) + end + + if (self.out_sid) then + Wire_TriggerOutput(self.Entity, "SteamID", sid) + end + + if (self.out_uid) then + Wire_TriggerOutput(self.Entity, "UniqueID", uid) + end + + if (self.out_eid) then + Wire_TriggerOutput(self.Entity, "EntID", ent:EntIndex()) + Wire_TriggerOutput(self.Entity, "Entity", ent) + end + + if (self.out_hnrm and hnrm) then + Wire_TriggerOutput(self.Entity, "HitNormal", hnrm) + Wire_TriggerOutput(self.Entity, "HitNormal X", hnrm.x) + Wire_TriggerOutput(self.Entity, "HitNormal Y", hnrm.y) + Wire_TriggerOutput(self.Entity, "HitNormal Z", hnrm.z) + end + + if (val != nil && table.getn(val) > 0 && self.Inputs.SelectValue.Value < table.Count(val)) then + Wire_TriggerOutput(self.Entity, "Val", val[self.Inputs.SelectValue.Value]) + Wire_TriggerOutput(self.Entity, "ValSize", table.Count(val)) + else + Wire_TriggerOutput(self.Entity, "Val", 0) + Wire_TriggerOutput(self.Entity, "ValSize", 0) + end + Wire_TriggerOutput(self.Entity, "RangerData", trace) + +end diff --git a/lua/entities/gmod_wire_ranger/shared.lua b/lua/entities/gmod_wire_ranger/shared.lua new file mode 100644 index 0000000000..57ab015b30 --- /dev/null +++ b/lua/entities/gmod_wire_ranger/shared.lua @@ -0,0 +1,33 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Ranger" +ENT.Author = "Erkle" +ENT.Contact = "ErkleMad@hotmail.com" + + +function ENT:SetSkewX(value) + self.Entity:SetNetworkedFloat("SkewX", math.max(-1, math.min(value, 1))) +end + +function ENT:SetSkewY(value) + self.Entity:SetNetworkedFloat("SkewY", math.max(-1, math.min(value, 1))) +end + + +function ENT:GetSkewX() + return self.Entity:GetNetworkedFloat("SkewX") or 0 +end + +function ENT:GetSkewY() + return self.Entity:GetNetworkedFloat("SkewY") or 0 +end + + +function ENT:SetBeamLength(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_relay/cl_init.lua b/lua/entities/gmod_wire_relay/cl_init.lua new file mode 100644 index 0000000000..e719186ab9 --- /dev/null +++ b/lua/entities/gmod_wire_relay/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + diff --git a/lua/entities/gmod_wire_relay/init.lua b/lua/entities/gmod_wire_relay/init.lua new file mode 100644 index 0000000000..1bc94afacf --- /dev/null +++ b/lua/entities/gmod_wire_relay/init.lua @@ -0,0 +1,126 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Relay" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + + self.Value = {} //stores current output value + self.Last = {} //stores last input value for each input + + self.Inputs = Wire_CreateInputs(self.Entity, { "1A", "2A", "Switch" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "A" }) +end + +function ENT:Setup(keygroup1, keygroup2, keygroup3, keygroup4, keygroup5, keygroupoff, toggle, normclose, poles, throws) + self.KeyGroup1 = keygroup1 + self.KeyGroup2 = keygroup2 + self.KeyGroup3 = keygroup3 + self.KeyGroup4 = keygroup4 + self.KeyGroup5 = keygroup5 + self.KeyGroupOff = keygroupoff + self.Toggle = toggle + self.NormClose = normclose or 0 + self.SelInput = normclose or 0 + self.Poles = poles + self.Throws = throws + + local outpoles = {"A", "B", "C", "D", "E", "F", "G", "H"} //output names + local inputs = {} //wont need this outside setup + self.outputs = {} //need to rebuild output names + + //build inputs and putputs, init all nil values + for p=1, self.Poles do + self.outputs[p] = outpoles[p] + self.Value[p] = self.Value[p] or 0 + for t=1, self.Throws do + //inputs[ p * self.Poles + t ] = t .. outpoles[p] + table.insert(inputs, ( t .. outpoles[p] )) + self.Last[ t .. outpoles[p] ] = self.Last[ t .. outpoles[p] ] or 0 + end + end + //add switch input to end of input list + table.insert(inputs, "Switch") + + Wire_AdjustInputs(self.Entity, inputs) + Wire_AdjustOutputs(self.Entity, self.outputs) + + //set the switch to its new normal state + self:Switch( normclose ) +end + + +function ENT:TriggerInput(iname, value) + if (iname == "Switch") then + if (math.abs(value) >= 0 && math.abs(value) <= self.Throws) then + self:Switch(math.abs(value)) + end + elseif (iname) then + self.Last[iname] = value or 0 + self:Switch(self.SelInput) + end +end + + +function ENT:Switch( mul ) + if (!self.Entity:IsValid()) then return false end + self.SelInput = mul + for p,v in ipairs(self.outputs) do + self.Value[p] = self.Last[ mul .. v ] or 0 + Wire_TriggerOutput(self.Entity, v, self.Value[p]) + end + self:ShowOutput() + return true +end + + +function ENT:ShowOutput() + txt = self.Poles .. "P" .. self.Throws .. "T " + if (self.SelInput == 0) then + txt = txt .. "Sel: off" + else + txt = txt .. "Sel: " .. self.SelInput + end + + for p,v in ipairs(self.outputs) do + txt = txt .. "\n" .. v .. ": " .. self.Value[p] + end + + self:SetOverlayText( txt ) +end + + +function ENT:InputActivate( mul ) + if ( self.Toggle && self.SelInput == mul) then //only toggle for the same key + return self:Switch( self.NormClose ) + else + return self:Switch( mul ) + end +end + +function ENT:InputDeactivate( mul ) + if ( self.Toggle ) then return true end + return self:Switch( self.NormClose ) +end + + +local function On( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputActivate( mul ) +end + +local function Off( pl, ent, mul ) + if (!ent:IsValid()) then return false end + return ent:InputDeactivate( mul ) +end + +numpad.Register( "WireRelay_On", On ) +numpad.Register( "WireRelay_Off", Off ) diff --git a/lua/entities/gmod_wire_relay/shared.lua b/lua/entities/gmod_wire_relay/shared.lua new file mode 100644 index 0000000000..3f705841ab --- /dev/null +++ b/lua/entities/gmod_wire_relay/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Relay" +ENT.Author = "tad2020" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_screen/cl_init.lua b/lua/entities/gmod_wire_screen/cl_init.lua new file mode 100644 index 0000000000..33be00e898 --- /dev/null +++ b/lua/entities/gmod_wire_screen/cl_init.lua @@ -0,0 +1,158 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Initialize() + + surface.CreateFont( "coolvetica", 64, 400, false, false, "screen_font" ) + + // Create new fonts here for Single Value screens + // According to the wiki, the font size is capped at 128 (TheApathetic) + surface.CreateFont("coolvetica", 128, 400, false, false, "screen_font_single") + surface.CreateFont("Trebuchet", 36, 400, false, false, "Trebuchet36") + +end + +function ENT:Draw() + self.Entity:DrawModel() + + local OF = 0 + local OU = 0 + local OR = 0 + local Res = 0.1 + local RatioX = 1 + + if self.Entity:GetModel() == "models/props_lab/monitor01b.mdl" then + OF = 6.53 + OU = 0 + OR = 0 + Res = 0.05 + elseif self.Entity:GetModel() == "models/kobilica/wiremonitorsmall.mdl" then + OF = 0.2 + OU = 4.5 + OR = -0.85 + Res = 0.045 + elseif self.Entity:GetModel() == "models/kobilica/wiremonitorbig.mdl" then + OF = 0.3 + OU = 11.8 + OR = -2.35 + Res = 0.12 + elseif self.Entity:GetModel() == "models/props/cs_office/computer_monitor.mdl" then + OF = 3.25 + OU = 15.85 + OR = -2.2 + Res = 0.085 + RatioX = 0.75 + elseif self.Entity:GetModel() == "models/props/cs_office/tv_plasma.mdl" then + OF = 6.1 + OU = 17.05 + OR = -5.99 + Res = 0.175 + RatioX = 0.57 + end + + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + + local pos = self.Entity:GetPos() + (self.Entity:GetForward() * OF) + (self.Entity:GetUp() * OU) + (self.Entity:GetRight() * OR) + + cam.Start3D2D(pos,ang,Res) + + local x = -112 + local y = -104 + local w = 296 + local h = 292 + + local x1 = -5.535 + local x2 = 3.5 + local y1 = 5.091 + local y2 = -4.1 + + local ox = 5 + local oy = 5 + + local pos + local cx + local cy + + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(x/RatioX,y,(x+w)/RatioX,y+h) + + // Check for Single Value (TheApathetic) + if (self:GetSingleValue()) then + local rectheight = 20 + local fontsize = "18" + local sf_suffix = "" + + // Check for Single Bigger Font setting + if (self:GetSingleBigFont()) then + rectheight = 40 + fontsize = "36" + sf_suffix = "_single" + end + + // Sizes here have been doubled when possible + surface.SetDrawColor(100,100,150,255) + surface.DrawRect(x/RatioX,y,(x+w)/RatioX,rectheight) + + draw.DrawText(self:GetTextA(),"Trebuchet"..fontsize,(x + 92)/RatioX,y + 2,Color(255,255,255,255),1) + + local DisplayA + + if (self:GetFloor()) then + DisplayA = math.floor(self:GetDisplayA( )) + else + DisplayA = math.floor(self:GetDisplayA( ) * 1000)/ 1000 + end + + if (self:GetLeftAlign()) then + draw.DrawText(DisplayA,"screen_font"..sf_suffix,x/RatioX,y + rectheight,Color(255,255,255,255),0) + else + draw.DrawText(DisplayA,"screen_font"..sf_suffix,(x + 92)/RatioX,y + rectheight,Color(255,255,255,255),1) + end + else + // Normal two-value Wire Screen + surface.SetDrawColor(100,100,150,255) + surface.DrawRect(x/RatioX,y,(x+w)/RatioX,20) + + surface.SetDrawColor(100,100,150,255) + surface.DrawRect(x/RatioX,y+94,(x+w)/RatioX,20) + + // Replaced "Value A" and "Value B" here (TheApathetic) + draw.DrawText(self:GetTextA(),"Trebuchet18",(x + 92)/RatioX,y + 2,Color(255,255,255,255),1) + draw.DrawText(self:GetTextB(),"Trebuchet18",(x + 92)/RatioX,y + 96,Color(255,255,255,255),1) + + local DisplayA + local DisplayB + + if (self:GetFloor()) then + DisplayA = math.floor(self:GetDisplayA( )) + DisplayB = math.floor(self:GetDisplayB( )) + else + DisplayA = math.floor(self:GetDisplayA( ) * 1000)/ 1000 + DisplayB = math.floor(self:GetDisplayB( ) * 1000)/ 1000 + end + + if (self:GetLeftAlign()) then + draw.DrawText(DisplayA,"screen_font",x/RatioX,y + 20,Color(255,255,255,255),0) + draw.DrawText(DisplayB,"screen_font",x/RatioX,y + 114,Color(255,255,255,255),0) + else + draw.DrawText(DisplayA,"screen_font",(x + 90)/RatioX,y + 20,Color(255,255,255,255),1) + draw.DrawText(DisplayB,"screen_font",(x + 92)/RatioX,y + 114,Color(255,255,255,255),1) + end + end + + cam.End3D2D() + + Wire_Render(self.Entity) +end + +function ENT:IsTranslucent() + return true +end diff --git a/lua/entities/gmod_wire_screen/init.lua b/lua/entities/gmod_wire_screen/init.lua new file mode 100644 index 0000000000..fe1d177945 --- /dev/null +++ b/lua/entities/gmod_wire_screen/init.lua @@ -0,0 +1,94 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Screen" + +ENT.ValueA = 0 +ENT.ValueB = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "A", "B" }) +end + +function ENT:Think() + if self.ValueA then + self:SetDisplayA( self.ValueA ) + self.ValueA = nil + end + + if self.ValueB then + self:SetDisplayB( self.ValueB ) + self.ValueB = nil + end + + self:NextThink(CurTime() + 0.05) + return true +end + +function ENT:Use() +end + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + self.ValueA = value + elseif (iname == "B") then + self.ValueB = value + end +end + +function ENT:Setup(SingleValue, SingleBigFont, TextA, TextB, LeftAlign, Floor) + --for duplication + self.SingleValue = SingleValue + self.SingleBigFont = SingleBigFont + self.TextA = TextA + self.TextB = TextB + self.LeftAlign = LeftAlign + self.Floor = Floor + + -- Extra stuff for Wire Screen (TheApathetic) + self:SetTextA(TextA) + self:SetTextB(TextB) + self:SetSingleBigFont(SingleBigFont) + + --LeftAlign (TAD2020) + self:SetLeftAlign(LeftAlign) + --Floor (TAD2020) + self:SetFloor(Floor) + + --Put it here to update inputs if necessary (TheApathetic) + self:SetSingleValue(SingleValue) +end + +function MakeWireScreen( pl, Pos, Ang, model, SingleValue, SingleBigFont, TextA, TextB, LeftAlign, Floor, frozen ) + + if ( !pl:CheckLimit( "wire_screens" ) ) then return false end + + local wire_screen = ents.Create( "gmod_wire_screen" ) + if (!wire_screen:IsValid()) then return false end + wire_screen:SetModel(model) + wire_screen:SetAngles( Ang ) + wire_screen:SetPos( Pos ) + wire_screen:Spawn() + + if wire_screen:GetPhysicsObject():IsValid() then + local Phys = wire_screen:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + wire_screen:Setup(SingleValue, SingleBigFont, TextA, TextB, LeftAlign, Floor) + + wire_screen:SetPlayer(pl) + wire_screen.pl = pl + + pl:AddCount( "wire_screens", wire_screen ) + + return wire_screen + +end +duplicator.RegisterEntityClass("gmod_wire_screen", MakeWireScreen, "Pos", "Ang", "Model", "SingleValue", "SingleBigFont", "TextA", "TextB", "LeftAlign", "Floor", "frozen") + diff --git a/lua/entities/gmod_wire_screen/shared.lua b/lua/entities/gmod_wire_screen/shared.lua new file mode 100644 index 0000000000..728a3b0afd --- /dev/null +++ b/lua/entities/gmod_wire_screen/shared.lua @@ -0,0 +1,91 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Screen" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetDisplayA( float ) + self.Entity:SetNetworkedBeamFloat( 1, float, true ) +end + +function ENT:SetDisplayB( float ) + self.Entity:SetNetworkedBeamFloat( 2, float, true ) +end + +function ENT:GetDisplayA( ) + return self.Entity:GetNetworkedBeamFloat( 1 ) +end + +function ENT:GetDisplayB( ) + return self.Entity:GetNetworkedBeamFloat( 2 ) +end + + +// Extra stuff for Wire Screen (TheApathetic) +function ENT:SetSingleValue(singlevalue) + self.Entity:SetNetworkedBool("SingleValue",singlevalue) + + // Change inputs if necessary + if (singlevalue) then + Wire_AdjustInputs(self.Entity, {"A"}) + else + Wire_AdjustInputs(self.Entity, {"A","B"}) + end +end + +function ENT:GetSingleValue() + return self.Entity:GetNetworkedBool("SingleValue") +end + + +function ENT:SetSingleBigFont(singlebigfont) + self.Entity:SetNetworkedBool("SingleBigFont",singlebigfont) +end + +function ENT:GetSingleBigFont() + return self.Entity:GetNetworkedBool("SingleBigFont") +end + + +function ENT:SetTextA(text) + self.Entity:SetNetworkedString("TextA",text) +end + +function ENT:GetTextA() + return self.Entity:GetNetworkedString("TextA") +end + +function ENT:SetTextB(text) + self.Entity:SetNetworkedString("TextB",text) +end + +function ENT:GetTextB() + return self.Entity:GetNetworkedString("TextB") +end + + +//LeftAlign (TAD2020) +function ENT:SetLeftAlign(leftalign) + self.Entity:SetNetworkedBool("LeftAlign",leftalign) +end + +function ENT:GetLeftAlign() + return self.Entity:GetNetworkedBool("LeftAlign") +end + + +//Floor (TAD2020) +function ENT:SetFloor(Floor) + self.Entity:SetNetworkedBool("Floor",Floor) +end + +function ENT:GetFloor() + return self.Entity:GetNetworkedBool("Floor") +end diff --git a/lua/entities/gmod_wire_sensor/cl_init.lua b/lua/entities/gmod_wire_sensor/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_sensor/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_sensor/init.lua b/lua/entities/gmod_wire_sensor/init.lua new file mode 100644 index 0000000000..5a763e0e30 --- /dev/null +++ b/lua/entities/gmod_wire_sensor/init.lua @@ -0,0 +1,225 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Distance" + +local MODEL = Model( "models/props_lab/huladoll.mdl" ) + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "Target" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) +end + +function ENT:Setup( xyz_mode, outdist, outbrng, gpscord, swapyz,direction_vector,direction_normalized,target_velocity,velocity_normalized) + self.XYZMode = xyz_mode + self.PrevOutput = nil + self.Value = 0 + self.OutDist = outdist + self.OutBrng = outbrng + self.GPSCord = gpscord + self.SwapYZ = swapyz + self.direction_vector = direction_vector; + self.direction_normalized = direction_normalized; + self.target_velocity = target_velocity; + self.velocity_normalized = velocity_normalized; + + if !xyz_mode and !outdist and !outbrng and !gpscord and !direction_vector and !target_velocity then self.OutDist = true outdist = true end + + local onames = {} + if (outdist) then + table.insert(onames, "Distance") + end + + if (xyz_mode) then + table.insert(onames, "X") + table.insert(onames, "Y") + table.insert(onames, "Z") + end + if (outbrng) then + table.insert(onames, "Bearing") + table.insert(onames, "Elevation") + end + if (gpscord) then + table.insert(onames, "World_X") + table.insert(onames, "World_Y") + table.insert(onames, "World_Z") + end + if (direction_vector) then + table.insert(onames, "Direction_X") + table.insert(onames, "Direction_Y") + table.insert(onames, "Direction_Z") + end + if (target_velocity) then + table.insert(onames, "Velocity_X") + table.insert(onames, "Velocity_Y") + table.insert(onames, "Velocity_Z") + end + + Wire_AdjustOutputs(self.Entity, onames) + self:TriggerOutputs(0, Angle(0, 0, 0),Vector(0, 0, 0),Vector(0, 0, 0),Vector(0, 0, 0),Vector(0,0,0)) + self:ShowOutput() +end + + +function ENT:Think() + self.BaseClass.Think(self) + + //if (!self.Inputs.Target.Src or !self.Inputs.Target.Src:IsValid() ) then return end + if ( !self.ToSense or !self.ToSense:IsValid() or !self.ToSense.GetBeaconPos ) then return end + if (self.Active) then + local dist = 0 + local distc = Vector(0,0,0); + local brng = Angle(0,0,0); + local velo = Vector(0,0,0); + local gpscords = Vector(0,0,0); + local dirvec = Vector(0,0,0); + local MyPos = self.Entity:GetPos() + //local BeaconPos = self.Inputs["Target"].Src:GetBeaconPos(self.Entity) + local BeaconPos = self.ToSense:GetBeaconPos(self.Entity) + if (self.OutDist) then + dist = (BeaconPos-MyPos):Length() + end + if (self.XYZMode) then + local DeltaPos = self.Entity:WorldToLocal(BeaconPos) + if (self.SwapYZ) then + distc = Vector(DeltaPos.z,DeltaPos.x,-DeltaPos.y) + else + distc = Vector(-DeltaPos.y,DeltaPos.x,DeltaPos.z) + end + end + if (self.OutBrng) then + local DeltaPos = self.Entity:WorldToLocal(BeaconPos) + brng = DeltaPos:Angle() + end + if (self.GPSCord) then gpscords = BeaconPos end + if (self.direction_vector) then + dirvec = BeaconPos - MyPos; + if(self.direction_normalized) then dirvec:Normalize() end; + end; + if (self.target_velocity) then + velo = self.ToSense:GetBeaconVelocity(self.Entity); + if(self.velocity_normalized) then velo:Normalize() end; + end + self:TriggerOutputs(dist, brng, distc, gpscords,dirvec,velo) + self:ShowOutput() + + self.Entity:NextThink(CurTime()+0.04) + return true + end +end + + +function ENT:ShowOutput() + local txt = "Beacon Sensor" + if (self.OutDist) then + txt = txt .. "\nDistance = " .. math.Round(self.Outputs.Distance.Value*1000)/1000 + end + if (self.XYZMode) then + txt = txt .. "\nOffset = " .. math.Round(self.Outputs.X.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Y.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Z.Value*1000)/1000 + end + if (self.OutBrng) then + txt = txt .. "\nBearing = " .. math.Round(self.Outputs.Bearing.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Elevation.Value*1000)/1000 + end + if (self.GPSCord) then + txt = txt .. "\nWorldPos = " .. math.Round(self.Outputs.World_X.Value*1000)/1000 .. "," .. math.Round(self.Outputs.World_Y.Value*1000)/1000 .. "," .. math.Round(self.Outputs.World_Z.Value*1000)/1000 + end + if (self.direction_vector) then + txt = txt .. "\nDirectionVector = " .. math.Round(self.Outputs.Direction_X.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Direction_Y.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Direction_Z.Value*1000)/1000 + end + if (self.target_velocity) then + txt = txt .. "\nTargetVelocity = " .. math.Round(self.Outputs.Velocity_X.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Velocity_Y.Value*1000)/1000 .. "," .. math.Round(self.Outputs.Velocity_Z.Value*1000)/1000 + end + + self:SetOverlayText(txt) +end + + +function ENT:TriggerOutputs(dist, brng, distc, gpscords,dirvec,velo) + if (self.OutDist) then + Wire_TriggerOutput(self.Entity, "Distance", dist) + end + if (self.XYZMode) then + Wire_TriggerOutput(self.Entity, "X", distc.x) + Wire_TriggerOutput(self.Entity, "Y", distc.y) + Wire_TriggerOutput(self.Entity, "Z", distc.z) + end + if (self.GPSCord) then + Wire_TriggerOutput(self.Entity, "World_X", gpscords.x) + Wire_TriggerOutput(self.Entity, "World_Y", gpscords.y) + Wire_TriggerOutput(self.Entity, "World_Z", gpscords.z) + end + if (self.OutBrng) then + local pitch = brng.p + local yaw = brng.y + + if (pitch > 180) then pitch = pitch - 360 end + if (yaw > 180) then yaw = yaw - 360 end + + Wire_TriggerOutput(self.Entity, "Bearing", -yaw) + Wire_TriggerOutput(self.Entity, "Elevation", -pitch) + end + if(self.direction_vector) then + Wire_TriggerOutput(self.Entity, "Direction_X", dirvec.x) + Wire_TriggerOutput(self.Entity, "Direction_Y", dirvec.y) + Wire_TriggerOutput(self.Entity, "Direction_Z", dirvec.z) + end + if(self.target_velocity) then + Wire_TriggerOutput(self.Entity, "Velocity_X", velo.x) + Wire_TriggerOutput(self.Entity, "Velocity_Y", velo.y) + Wire_TriggerOutput(self.Entity, "Velocity_Z", velo.z) + end +end + +function ENT:TriggerInput(iname, value) + if (iname == "Target") and ( self.ToSense != self.Inputs.Target.Src ) then + self:SetBeacon(self.Inputs.Target.Src) + end +end + +function ENT:SetBeacon(beacon) + if (beacon) and (beacon:IsValid()) then + self.ToSense = beacon + self.Active = true + else + self.ToSense = nil + self.Active = false + end +end + +function ENT:OnRestore() + //this is to prevent old save breakage + self:Setup(self.XYZMode, self.OutDist, self.OutBrng, self.GPSCord, self.SwapYZ,self.direction_vector,self.direction_normalized,self.target_velocity,self.velocity_normalized) + + self.BaseClass.OnRestore(self) +end + + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.ToSense) and (self.ToSense:IsValid()) then + info.to_sense = self.ToSense:EntIndex() + end + + return info +end + + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.to_sense) then + self:SetBeacon(GetEntByID(info.to_sense)) + if (!self.ToSense) then + self:SetBeacon(ents.GetByIndex(info.to_sense)) + end + end +end diff --git a/lua/entities/gmod_wire_sensor/shared.lua b/lua/entities/gmod_wire_sensor/shared.lua new file mode 100644 index 0000000000..84a322ef6c --- /dev/null +++ b/lua/entities/gmod_wire_sensor/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Beacon Sensor" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_simple_explosive/cl_init.lua b/lua/entities/gmod_wire_simple_explosive/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_simple_explosive/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_simple_explosive/init.lua b/lua/entities/gmod_wire_simple_explosive/init.lua new file mode 100644 index 0000000000..acfc0e0c97 --- /dev/null +++ b/lua/entities/gmod_wire_simple_explosive/init.lua @@ -0,0 +1,163 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Simple Explosive" +ENT.OverlayDelay = 0.5 + +--[[--------------------------------------------------------- + Name: Initialize + Desc: First function called. Use to set up your entity +---------------------------------------------------------]] +function ENT:Initialize() + + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + self.NormInfo = "" + + self.Inputs = Wire_CreateInputs(self.Entity, { "Detonate" }) + +end + +--[[--------------------------------------------------------- + Name: Setup + Desc: does a whole lot of setting up +---------------------------------------------------------]] +function ENT:Setup( damage, delaytime, removeafter, doblastdamage, radius, nocollide ) + + self.Damage = damage + self.Radius = math.max(radius, 1) + self.NoCollide = nocollide + self.DoBlastDamage = doblastdamage + self.Exploded = false + self.Removeafter = removeafter + + if (self.NoCollide) then + self.Entity:SetCollisionGroup(COLLISION_GROUP_DEBRIS_TRIGGER) + else + self.Entity:SetCollisionGroup(COLLISION_GROUP_NONE) + end + + self.NormInfo = "" + if (self.DoBlastDamage) then + self.NormInfo = "Damage: " .. math.floor(self.Damage) .. "\nRadius: " .. math.floor(self.Radius) + else + self.NormInfo = "Radius: " .. math.floor(self.Radius) + end + + self:ShowOutput() + +end + + +--[[--------------------------------------------------------- + Name: OnTakeDamage + Desc: Entity takes damage +---------------------------------------------------------]] +function ENT:OnTakeDamage( dmginfo ) + self.Entity:TakePhysicsDamage( dmginfo ) +end + + + +--[[--------------------------------------------------------- + Name: TriggerInput + Desc: the inputs +---------------------------------------------------------]] +function ENT:TriggerInput(iname, value) + if (iname == "Detonate") then + if (!self.Exploded) and ( math.abs(value) == self.key ) then + self:Explode() + elseif (value == 0) then + self.Exploded = false + end + end +end + +--[[--------------------------------------------------------- + Name: Explode + Desc: is one needed? +---------------------------------------------------------]] +function ENT:Explode( ) + + if ( !self.Entity:IsValid() ) then return end + if (self.Exploded) then return end + + ply = self:GetPlayer() or self.Entity + + if ( self.DoBlastDamage ) then + util.BlastDamage( self.Entity, ply, self.Entity:GetPos(), self.Radius, self.Damage ) + end + + local effectdata = EffectData() + effectdata:SetOrigin( self.Entity:GetPos() ) + util.Effect( "Explosion", effectdata, true, true ) + + self.Exploded = true + self:ShowOutput() + + if ( self.Removeafter ) then + self.Entity:Remove() + return + end + +end + +--[[--------------------------------------------------------- + Name: ShowOutput + Desc: don't foreget to call this when changes happen +---------------------------------------------------------]] +function ENT:ShowOutput( ) + local txt = "" + if (self.Exploded) then + txt = "Exploded\n"..self.NormInfo + else + txt = "Explosive\n"..self.NormInfo + end + self:SetOverlayText(txt) +end + + +function MakeWireSimpleExplosive(pl, Pos, Ang, model, key, damage, removeafter, doblastdamage, radius, nocollide ) + if ( !pl:CheckLimit( "wire_simple_explosive" ) ) then return nil end + + damage = math.Min(damage, 1500) + radius = math.Min(radius, 10000) + + local explosive = ents.Create( "gmod_wire_simple_explosive" ) + + explosive:SetModel( model ) + explosive:SetPos( Pos ) + explosive:SetAngles( Ang ) + explosive:Spawn() + explosive:Activate() + + explosive:Setup( damage, delaytime, removeafter, doblastdamage, radius, nocollide ) + explosive:SetPlayer( pl ) + + local ttable = { + pl = pl, + nocollide = nocollide, + key = key, + damage = damage, + removeafter = removeafter, + doblastdamage = doblastdamage, + radius = radius + } + table.Merge( explosive:GetTable(), ttable ) + + pl:AddCount( "wire_simple_explosive", explosive ) + pl:AddCleanup( "gmod_wire_simple_explosive", explosive ) + + return explosive +end +duplicator.RegisterEntityClass( "gmod_wire_simple_explosive", MakeWireSimpleExplosive, "Pos", "Ang", "Model", "key", "damage", "removeafter", "doblastdamage", "radius", "nocollide" ) diff --git a/lua/entities/gmod_wire_simple_explosive/shared.lua b/lua/entities/gmod_wire_simple_explosive/shared.lua new file mode 100644 index 0000000000..7ea01109c7 --- /dev/null +++ b/lua/entities/gmod_wire_simple_explosive/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Simple Explosive" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_socket/cl_init.lua b/lua/entities/gmod_wire_socket/cl_init.lua new file mode 100644 index 0000000000..4bdf6213ce --- /dev/null +++ b/lua/entities/gmod_wire_socket/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE diff --git a/lua/entities/gmod_wire_socket/init.lua b/lua/entities/gmod_wire_socket/init.lua new file mode 100644 index 0000000000..ce6028252d --- /dev/null +++ b/lua/entities/gmod_wire_socket/init.lua @@ -0,0 +1,198 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Socket" + +local MODEL = Model( "models/props_lab/tpplugholder_single.mdl" ) + +//Time after loosing one plug to search for another +local NEW_PLUG_WAIT_TIME = 2 +local PLUG_IN_SOCKET_CONSTRAINT_POWER = 5000 +local PLUG_IN_ATTACH_RANGE = 3 + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.MyPlug = nil + self.Const = nil + + self.Inputs = Wire_CreateInputs(self.Entity, { "A","B","C","D","E","F","G","H" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "A","B","C","D","E","F","G","H" }) +end + +function ENT:TriggerInput(iname, value) + if (self.MyPlug) and (self.MyPlug:IsValid()) then + self.MyPlug:SetValue(iname, value) + end + self:ShowOutput() +end + +function ENT:SetValue(index,value) + if (self.Const) and (self.Const:IsValid()) then + Wire_TriggerOutput(self.Entity, index, value) + else + Wire_TriggerOutput(self.Entity, index, 0) + end + + self:ShowOutput() +end + +function ENT:Setup() + self:ShowOutput() +end + +function ENT:Think() + self.BaseClass.Think(self) + + // If we were unplugged, reset the plug and socket to accept new ones. + if (self.Const) and (not self.Const:IsValid()) then + self.Const = nil + self.NoCollideConst = nil + if (self.MyPlug) and (self.MyPlug:IsValid()) then + self.MyPlug:SetSocket(nil) + self.MyPlug = nil + end + + self.ReceivedValue = 0 //We're now getting no signal + for i,v in pairs(self.Outputs)do + Wire_TriggerOutput(self.Entity, v.Name, 0) + end + self:ShowOutput() + + self.Entity:NextThink( CurTime() + NEW_PLUG_WAIT_TIME ) //Give time before next grabbing a plug. + return true + end + + // If we have no plug in us + if (not self.MyPlug) or (not self.MyPlug:IsValid()) then + + // Find entities near us + local sockCenter = self:GetOffset( Vector(8, -13, -10) ) + local local_ents = ents.FindInSphere( sockCenter, PLUG_IN_ATTACH_RANGE ) + for key, plug in pairs(local_ents) do + + // If we find a plug, try to attach it to us + if ( plug:IsValid() && plug:GetClass() == "gmod_wire_plug" ) then + + // If no other sockets are using it + if plug.MySocket == nil then + local plugpos = plug:GetPos() + local dist = (sockCenter-plugpos):Length() + + self:AttachPlug(plug) + end + end + end + end +end + +function ENT:AttachPlug( plug ) + // Set references between them + plug:SetSocket(self.Entity) + self.MyPlug = plug + + // Position plug + local newpos = self:GetOffset( Vector(8, -13, -5) ) + local socketAng = self.Entity:GetAngles() + plug:SetPos( newpos ) + plug:SetAngles( socketAng ) + + self.NoCollideConst = constraint.NoCollide(self.Entity, plug, 0, 0) + if (not self.NoCollideConst) then + self.MyPlug = nil + plug:SetSocket(nil) + return + end + + // Constrain together + self.Const = constraint.Weld( self.Entity, plug, 0, 0, PLUG_IN_SOCKET_CONSTRAINT_POWER, true ) + if (not self.Const) then + self.NoCollideConst:Remove() + self.NoCollideConst = nil + self.MyPlug = nil + plug:SetSocket(nil) + return + end + + // Prepare clearup incase one is removed + plug:DeleteOnRemove( self.Const ) + self.Entity:DeleteOnRemove( self.Const ) + self.Const:DeleteOnRemove( self.NoCollideConst ) + + for i,v in pairs(self.Inputs)do + plug:SetValue(v.Name,v.Value) + end + + plug:AttachedToSocket(self.Entity) + + self:ShowOutput() +end + +function ENT:ShowOutput() + self.OutText = "Socket" + if (self.Inputs) then + self.OutText = self.OutText .. "\nInputs: " + if (self.Inputs.A.Value) then + self.OutText = self.OutText .. " A:" .. self.Inputs.A.Value + end + if (self.Inputs.B.Value) then + self.OutText = self.OutText .. " B:" .. self.Inputs.B.Value + end + if (self.Inputs.C.Value) then + self.OutText = self.OutText .. " C:" .. self.Inputs.C.Value + end + if (self.Inputs.D.Value) then + self.OutText = self.OutText .. " D:" .. self.Inputs.D.Value + end + if (self.Inputs.E.Value) then + self.OutText = self.OutText .. " E:" .. self.Inputs.E.Value + end + if (self.Inputs.F.Value) then + self.OutText = self.OutText .. " F:" .. self.Inputs.F.Value + end + if (self.Inputs.G.Value) then + self.OutText = self.OutText .. " G:" .. self.Inputs.G.Value + end + if (self.Inputs.H.Value) then + self.OutText = self.OutText .. " H:" .. self.Inputs.H.Value + end + end + if (self.Outputs) then + self.OutText = self.OutText .. "\nOutputs: " + if (self.Outputs.A.Value) then + self.OutText = self.OutText .. " A:" .. self.Outputs.A.Value + end + if (self.Outputs.B.Value) then + self.OutText = self.OutText .. " B:" .. self.Outputs.B.Value + end + if (self.Outputs.C.Value) then + self.OutText = self.OutText .. " C:" .. self.Outputs.C.Value + end + if (self.Outputs.D.Value) then + self.OutText = self.OutText .. " D:" .. self.Outputs.D.Value + end + if (self.Outputs.E.Value) then + self.OutText = self.OutText .. " E:" .. self.Outputs.E.Value + end + if (self.Outputs.F.Value) then + self.OutText = self.OutText .. " F:" .. self.Outputs.F.Value + end + if (self.Outputs.G.Value) then + self.OutText = self.OutText .. " G:" .. self.Outputs.G.Value + end + if (self.Outputs.H.Value) then + self.OutText = self.OutText .. " H:" .. self.Outputs.H.Value + end + end + self:SetOverlayText(self.OutText) +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) +end diff --git a/lua/entities/gmod_wire_socket/shared.lua b/lua/entities/gmod_wire_socket/shared.lua new file mode 100644 index 0000000000..470fcbd91a --- /dev/null +++ b/lua/entities/gmod_wire_socket/shared.lua @@ -0,0 +1,22 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Socket" +ENT.Author = "UnDeR_SEEG" +ENT.Contact = "facepunch forums" +ENT.Purpose = "socket receives plug" +ENT.Instructions = "move a plug close to a socket to plug it in, and the signal will be carried through!" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:GetOffset( vec ) + local offset = vec + + local ang = self.Entity:GetAngles() + local stackdir = ang:Up() + offset = ang:Up() * offset.X + ang:Forward() * -1 * offset.Z + ang:Right() * offset.Y + + return self.Entity:GetPos() + stackdir * 2 + offset +end diff --git a/lua/entities/gmod_wire_soundemitter/cl_init.lua b/lua/entities/gmod_wire_soundemitter/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_soundemitter/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_soundemitter/init.lua b/lua/entities/gmod_wire_soundemitter/init.lua new file mode 100644 index 0000000000..2d20c5f8d5 --- /dev/null +++ b/lua/entities/gmod_wire_soundemitter/init.lua @@ -0,0 +1,236 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Sound" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "A", "Toggle", "Volume", "Play", "Stop", + "PitchRelative", "LFOType", "LFORate", "LFOModPitch", "LFOModVolume", "Sample" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Memory" }) + + self.Active = 0 + self.Volume = 5 + self.Pitch = 100 + + self.SampleTable = {} + self.SampleTable[0] = "synth/square.wav" + self.SampleTable[1] = "synth/square.wav" + self.SampleTable[2] = "synth/saw.wav" + self.SampleTable[3] = "synth/tri.wav" + self.SampleTable[4] = "synth/sine.wav" + + //LFO: + // 0 - none + // 1 - square + // 2 - tri + // 3 - saw + // 4 - sine + // 5 - random noise + + self.LFOType = 0 + self.LFORate = 0 + self.LFOModPitch = 0 + self.LFOModVolume = 0 + self.Sample = 0 + + self.LFOValue = 0 + self.LFONoiseTime = 0 + +// note = 69+12 * log2(f/440) +// f = (2^((note - 69) / 12))*440 +end + +function ENT:OnRemove() + self.BaseClass.OnRemove(self) + self:StopSounds() +end + +function ENT:ReadCell(Address) + if (Address < 0) || (Address > 8) then + return nil + else + return 0 + end +end + +function ENT:WriteCell(Address, value) + if (Address == 0) then + self:TriggerInput("A",value) + elseif (Address == 1) then + self:TriggerInput("Volume",value) + elseif (Address == 2) then + self:TriggerInput("PitchRelative",value) + elseif (Address == 3) then + self:TriggerInput("Sample",value) + elseif (Address == 4) then + self:TriggerInput("LFOType",value) + elseif (Address == 5) then + self:TriggerInput("LFORate",value) + elseif (Address == 6) then + self:TriggerInput("LFOModPitch",value) + elseif (Address == 7) then + self:TriggerInput("LFOModVolume",value) + else + return false + end + return true +end + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + local active = value >= 1 + if (self.Active == active) then return end + self.Active = active + if (active) then + self:StartSounds() + self.SND:ChangeVolume(self.Volume) + self.SND:ChangePitch(self.Pitch) + else + self:StopSounds() + end + elseif (iname == "Toggle") then + local active = value >= 1 + if (active) then + self.Active = !self.Active + end + if (self.Active) then + self:StartSounds() + self.SND:ChangeVolume(self.Volume) + self.SND:ChangePitch(self.Pitch) + else + self:StopSounds() + end + elseif (iname == "Volume") then + local volume = math.Clamp(math.floor(value*100),0,100) + self.Volume = volume + + self.SND:ChangeVolume(volume) + elseif (iname == "Play") then + local active = value >= 1 + if (active) then + self.Active = true + self:StartSounds() + self.SND:ChangeVolume(self.Volume) + self.SND:ChangePitch(self.Pitch) + end + elseif (iname == "Stop") then + local active = value >= 1 + if (active) then + self.Active = false + self:StopSounds() + end + elseif (iname == "PitchRelative") then + local relpitch = math.Clamp(math.floor(value*100),0,255) + if (self.Active) then + self.SND:ChangePitch(relpitch) + end + self.Pitch = relpitch + elseif (iname == "LFOType") then + local val = math.Clamp(math.floor(value),0,5) + self.LFOType = val + elseif (iname == "LFORate") then + self.LFORate = value + elseif (iname == "LFOModPitch") then + self.LFOModPitch = value + elseif (iname == "LFOModVolume") then + self.LFOModVolume = value + elseif (iname == "Sample") then + self:SetSample(value) + end + +// "Toggle", "Volume", "Play", "Stop", +// "PitchFreq", "PitchNote", "PitchRelative", "PitchStart", +// "SpinUpTime", "SpinDownTime", "FadeInStartVolume", "FadeInTime", "FadeOutTime", +// "LFOType", "LFORate", "LFOModPitch", "LFOModVolume", +end + +function ENT:SetSound(sound) + self:StopSounds() + + if sound:match('["?]') then return end + parsedsound = sound:Trim() + util.PrecacheSound(parsedsound) + + self.SampleTable[0] = parsedsound + self.SND = CreateSound(self.Entity, Sound(self.SampleTable[0])) + self:SetOverlayText( "Sound: " .. parsedsound:gsub("[/\\]+","/") ) +end + +function ENT:SetSample(sample) + if (self.SampleTable[sample]) then + self:StopSounds() + self:SetSound(self.SampleTable[sample]) + end +end + +function ENT:StartSounds() + if (self.SND) then + self.SND:Play() + end +end + +function ENT:StopSounds() + if (self.SND) then + self.SND:Stop() + end +end + +function ENT:Think() + if (self.LFOType == 5) then //Random noise + if ((self.LFORate ~= 0) && (CurTime() - self.LFONoiseTime > 1 / self.LFORate)) then + self.LFONoiseTime = CurTime() + + self.LFOValue = math.random()*2-1 + + if (self.Active) then + self.SND:ChangePitch(self.Pitch + 100*self.LFOValue*self.LFOModPitch) + self.SND:ChangeVolume(self.Volume + 5*self.LFOValue*self.LFOModVolume) + end + end + end + + self.Entity:NextThink(CurTime()+0.01) + return true +end + +function ENT:Setup( sound ) + self:SetSound( Sound(sound) ) + self.sound = sound +end + +function MakeWireEmitter( pl, Pos, Ang, model, sound, nocollide, frozen ) + + if ( !pl:CheckLimit( "wire_emitters" ) ) then return false end + + local wire_emitter = ents.Create( "gmod_wire_soundemitter" ) + if (!wire_emitter:IsValid()) then return false end + wire_emitter:SetModel( model ) + + wire_emitter:SetAngles( Ang ) + wire_emitter:SetPos( Pos ) + wire_emitter:Spawn() + + if wire_emitter:GetPhysicsObject():IsValid() then + local Phys = wire_emitter:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + wire_emitter:Setup( sound ) + wire_emitter:SetPlayer( pl ) + wire_emitter.pl = pl + wire_emitter.nocollide = nocollide + + pl:AddCount( "wire_emitters", wire_emitter ) + + return wire_emitter + +end +duplicator.RegisterEntityClass("gmod_wire_soundemitter", MakeWireEmitter, "Pos", "Ang", "Model", "sound", "nocollide", "frozen") diff --git a/lua/entities/gmod_wire_soundemitter/shared.lua b/lua/entities/gmod_wire_soundemitter/shared.lua new file mode 100644 index 0000000000..c85c97500e --- /dev/null +++ b/lua/entities/gmod_wire_soundemitter/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Sound Emitter" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_spawner/cl_init.lua b/lua/entities/gmod_wire_spawner/cl_init.lua new file mode 100644 index 0000000000..6158875e24 --- /dev/null +++ b/lua/entities/gmod_wire_spawner/cl_init.lua @@ -0,0 +1,10 @@ + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + self.Entity:DrawModel() +end diff --git a/lua/entities/gmod_wire_spawner/init.lua b/lua/entities/gmod_wire_spawner/init.lua new file mode 100644 index 0000000000..1bf2249a76 --- /dev/null +++ b/lua/entities/gmod_wire_spawner/init.lua @@ -0,0 +1,191 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +local GlobalUndoList = {} + +hook.Add("EntityRemoved", "wire_spawner_EntityRemoved", function(ent) + if not GlobalUndoList[ent] then return end + GlobalUndoList[ent]:CheckEnts(ent) + GlobalUndoList[ent] = nil +end) + +function ENT:Initialize() + + self.Entity:SetMoveType( MOVETYPE_NONE ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetCollisionGroup( COLLISION_GROUP_WEAPON ) + self.Entity:DrawShadow( false ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then phys:Wake() end + + self.UndoList = {} + + -- Spawner is "edge-triggered" + self.SpawnLastValue = 0 + self.UndoLastValue = 0 + + -- Made more efficient by updating the overlay text and + -- Wire output only when number of active props changes (TheApathetic) + self.CurrentPropCount = 0 + + -- Add inputs/outputs (TheApathetic) + self.Inputs = WireLib.CreateSpecialInputs(self.Entity, { "Spawn", "Undo", "UndoEnt" }, { "NORMAL", "NORMAL", "ENTITY" }) + self.Outputs = WireLib.CreateSpecialOutputs(self.Entity, { "Out", "LastSpawned", "Props" }, { "NORMAL", "ENTITY", "ARRAY" }) + + Wire_TriggerOutput(self.Entity, "Props", self.UndoList) +end + +function ENT:Setup( delay, undo_delay ) + self.delay = delay + self.undo_delay = undo_delay + self:ShowOutput() +end + +function ENT:DoSpawn( pl, down ) + + local ent = self.Entity + if (not ent:IsValid()) then return end + + local phys = ent:GetPhysicsObject() + if (not phys:IsValid()) then return end + + local Pos = ent:GetPos() + local Ang = ent:GetAngles() + local model = ent:GetModel() + + local prop = MakeProp( pl, Pos, Ang, model, {}, {} ) + if (not prop or not prop:IsValid()) then return end + + -- apply material and color (TAD2020) + prop:SetMaterial( ent:GetMaterial() ) + prop:SetColor( self.r, self.g, self.b, self.a ) + + -- set the skin {Jeremydeath} + prop:SetSkin( ent:GetSkin() or 0 ) + + -- apply the physic's objects properties + local phys2 = prop:GetPhysicsObject() + phys2:SetMass( phys:GetMass() ) -- known issue: while being held with the physgun, the spawner spawns 45k mass props. Could be worked around with a Think hook, but nah... + + if not ent:IsPlayerHolding() then -- minge protection :) + phys2:SetVelocity( phys:GetVelocity() ) + phys2:AddAngleVelocity( phys:GetAngleVelocity() - phys2:GetAngleVelocity() ) -- No SetAngleVelocity, so we must subtract the current angular velocity + end + + local nocollide = constraint.NoCollide( prop, ent, 0, 0 ) + if (nocollide:IsValid()) then prop:DeleteOnRemove( nocollide ) end + + undo.Create("Prop") + undo.AddEntity( prop ) + undo.AddEntity( nocollide ) + undo.SetPlayer( pl ) + undo.Finish() + + pl:AddCleanup( "props", prop ) + pl:AddCleanup( "props", nocollide ) + + table.insert( self.UndoList, 1, prop ) + GlobalUndoList[prop] = self.Entity + + Wire_TriggerOutput(self.Entity, "LastSpawned", prop) + self.CurrentPropCount = #self.UndoList + Wire_TriggerOutput(self.Entity, "Out", self.CurrentPropCount) + Wire_TriggerOutput(self.Entity, "Props", self.UndoList) + self:ShowOutput() + + if (self.undo_delay == 0) then return end + + timer.Simple( self.undo_delay, function( ent ) if ent:IsValid() then ent:Remove() end end, prop ) + +end + +function ENT:DoUndo( pl ) + + if #self.UndoList == 0 then return end + + local ent = self.UndoList[ #self.UndoList ] + self.UndoList[ #self.UndoList ] = nil + + if (not ent or not ent:IsValid()) then + return self:DoUndo(pl) + end + + ent:Remove() + WireLib.AddNotify(pl, "Undone Prop", NOTIFY_UNDO, 2 ) + +end + +function ENT:DoUndoEnt( pl, ent ) + if (not ent or not ent:IsValid()) then return end + + if GlobalUndoList[ent] ~= self.Entity then return end + + ent:Remove() + WireLib.AddNotify(pl, "Undone Prop", NOTIFY_UNDO, 2 ) + +end + +function ENT:CheckEnts(removed_entity) + -- Purge list of no longer existing props + for i = #self.UndoList,1,-1 do + local ent = self.UndoList[i] + if not ValidEntity(ent) or ent == removed_entity then + table.remove(self.UndoList, i) + end + end + + -- Check to see if active prop count has changed + if (#self.UndoList ~= self.CurrentPropCount) then + self.CurrentPropCount = #self.UndoList + Wire_TriggerOutput(self.Entity, "Out", self.CurrentPropCount) + Wire_TriggerOutput(self.Entity, "Props", self.UndoList) + self:ShowOutput() + end +end + +function ENT:TriggerInput(iname, value) + local pl = self:GetPlayer() + + if (iname == "Spawn") then + -- Spawner is "edge-triggered" (TheApathetic) + local SpawnThisValue = value > 0 + if (SpawnThisValue == self.SpawnLastValue) then return end + self.SpawnLastValue = SpawnThisValue + + if (SpawnThisValue) then + -- Simple copy/paste of old numpad Spawn with a few modifications + if (self.delay == 0) then self:DoSpawn( pl ) return end + + local TimedSpawn = function ( ent, pl ) + if not ValidEntity(ent) then return end + ent:GetTable():DoSpawn( pl ) + end + + timer.Simple( self.delay, TimedSpawn, self.Entity, pl ) + end + elseif (iname == "Undo") then + -- Same here + local UndoThisValue = value > 0 + if (UndoThisValue == self.UndoLastValue) then return end + self.UndoLastValue = UndoThisValue + + if (UndoThisValue) then self:DoUndo(pl) end + elseif (iname == "UndoEnt") then + self:DoUndoEnt(pl, value) + end +end + +function ENT:ShowOutput() + self:SetOverlayText("Spawn Delay: "..self.delay.."\nUndo Delay: "..self.undo_delay.."\nActive Props: "..self.CurrentPropCount) +end + +function ENT:OnRemove() + -- unregister spawned props from GlobalUndoList + for _,ent in ipairs(self.UndoList) do + GlobalUndoList[ent] = nil + end +end diff --git a/lua/entities/gmod_wire_spawner/shared.lua b/lua/entities/gmod_wire_spawner/shared.lua new file mode 100644 index 0000000000..5d6e74bc68 --- /dev/null +++ b/lua/entities/gmod_wire_spawner/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Prop Spawner" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_speedometer/cl_init.lua b/lua/entities/gmod_wire_speedometer/cl_init.lua new file mode 100644 index 0000000000..f8d04b9aac --- /dev/null +++ b/lua/entities/gmod_wire_speedometer/cl_init.lua @@ -0,0 +1,31 @@ + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +include('shared.lua') + +--handle overlay text client side instead (TAD2020) +function ENT:Think() + self.BaseClass.Think(self) + + local txt + + if (self:GetXYZMode()) then + local vel = self.Entity:WorldToLocal(self.Entity:GetVelocity()+self.Entity:GetPos()) + txt = "Velocity = " .. math.Round((-vel.y or 0)*1000)/1000 .. "," .. math.Round((vel.x or 0)*1000)/1000 .. "," .. math.Round((vel.z or 0)*1000)/1000 + else + local vel = self.Entity:GetVelocity():Length() + txt = "Speed = " .. math.Round((x or 0)*1000)/1000 + end + + --sadly self.Entity:GetPhysicsObject():GetAngleVelocity() does work client side, so read out is unlikely + /*if (self:GetAngVel()) then + local ang = self.Entity:GetPhysicsObject():GetAngleVelocity() + txt = txt .. "\nAngVel = P " .. math.Round((ang.y or 0)*1000)/1000 .. ", Y " .. math.Round((ang.z or 0)*1000) /1000 .. ", R " .. math.Round((ang.x or 0)*1000)/1000 + end*/ + + self.Entity:SetNetworkedBeamString( "GModOverlayText", txt ) + + self.Entity:NextThink(CurTime()+0.04) + return true +end diff --git a/lua/entities/gmod_wire_speedometer/init.lua b/lua/entities/gmod_wire_speedometer/init.lua new file mode 100644 index 0000000000..faa3ca88ba --- /dev/null +++ b/lua/entities/gmod_wire_speedometer/init.lua @@ -0,0 +1,90 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Speedo" + +local MODEL = Model("models/jaanus/wiretool/wiretool_speed.mdl") + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out", "MPH", "KPH" }) +end + +function ENT:Setup( xyz_mode, AngVel ) + self.z_only = xyz_mode --was renamed but kept for dupesaves + self.XYZMode = xyz_mode + self.AngVel = AngVel + self:SetModes( xyz_mode,AngVel ) + + local outs = {} + if (xyz_mode) then + outs = { "X", "Y", "Z" } + else + outs = { "Out", "MPH", "KPH", } + end + if (AngVel) then + table.Add(outs, {"AngVel_P", "AngVel_Y", "AngVel_R" } ) + end + Wire_AdjustOutputs(self.Entity, outs) +end + +function ENT:Think() + self.BaseClass.Think(self) + + if (self.XYZMode) then + local vel = self.Entity:WorldToLocal(self.Entity:GetVelocity()+self.Entity:GetPos()) + if (COLOSSAL_SANDBOX) then vel = vel * 6.25 end + Wire_TriggerOutput(self.Entity, "X", -vel.y) + Wire_TriggerOutput(self.Entity, "Y", vel.x) + Wire_TriggerOutput(self.Entity, "Z", vel.z) + else + local vel = self.Entity:GetVelocity():Length() + if (COLOSSAL_SANDBOX) then vel = vel * 6.25 end + Wire_TriggerOutput(self.Entity, "Out", vel) + Wire_TriggerOutput(self.Entity, "MPH", vel / 17.6) + Wire_TriggerOutput(self.Entity, "KPH", vel * 0.09144) + end + + if (self.AngVel) then + local ang = self.Entity:GetPhysicsObject():GetAngleVelocity() + Wire_TriggerOutput(self.Entity, "AngVel_P", ang.y) + Wire_TriggerOutput(self.Entity, "AngVel_Y", ang.z) + Wire_TriggerOutput(self.Entity, "AngVel_R", ang.x) + end + + self.Entity:NextThink(CurTime()+0.04) + return true +end + + +function MakeWireSpeedometer( pl, Pos, Ang, model, xyz_mode, AngVel, nocollide, frozen ) + if !pl:CheckLimit( "wire_speedometers" ) then return false end + + local wire_speedometer = ents.Create("gmod_wire_speedometer") + if !wire_speedometer:IsValid() then return false end + wire_speedometer:SetAngles(Ang) + wire_speedometer:SetPos(Pos) + wire_speedometer:SetModel(model or MODEL) + wire_speedometer:Spawn() + + wire_speedometer:Setup(xyz_mode, AngVel) + wire_speedometer:SetPlayer(pl) + wire_speedometer.pl = pl + + if wire_speedometer:GetPhysicsObject():IsValid() then + wire_speedometer:GetPhysicsObject():EnableMotion(!frozen) + if nocollide == true then wire_speedometer:GetPhysicsObject():EnableCollisions(false) end + end + + pl:AddCount( "wire_speedometers", wire_speedometer ) + pl:AddCleanup( "gmod_wire_speedometer", wire_speedometer ) + + return wire_speedometer +end +duplicator.RegisterEntityClass("gmod_wire_speedometer", MakeWireSpeedometer, "Pos", "Ang", "Model", "z_only", "AngVel", "nocollide", "frozen") diff --git a/lua/entities/gmod_wire_speedometer/shared.lua b/lua/entities/gmod_wire_speedometer/shared.lua new file mode 100644 index 0000000000..f0cda7878c --- /dev/null +++ b/lua/entities/gmod_wire_speedometer/shared.lua @@ -0,0 +1,20 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Speedometer" +ENT.Author = "Erkle" +ENT.Contact = "ErkleMad@hotmail.com" + + +function ENT:GetXYZMode() + return self.Entity:GetNetworkedBool( 0 ) +end + +function ENT:GetAngVel() + return self.Entity:GetNetworkedBool( 1 ) +end + +function ENT:SetModes( XYZMode, AngVel ) + self.Entity:SetNetworkedBool( 0, XYZMode ) + self.Entity:SetNetworkedBool( 1, AngVel ) +end diff --git a/lua/entities/gmod_wire_target_finder/cl_init.lua b/lua/entities/gmod_wire_target_finder/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_target_finder/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_target_finder/init.lua b/lua/entities/gmod_wire_target_finder/init.lua new file mode 100644 index 0000000000..80c4b8c617 --- /dev/null +++ b/lua/entities/gmod_wire_target_finder/init.lua @@ -0,0 +1,471 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Targetter" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "Hold" }) + self.Outputs = WireLib.CreateSpecialOutputs( self.Entity, { "Out" }, { "ENTITY" } ) +end + +function ENT:Setup(maxrange, players, npcs, npcname, beacons, hoverballs, thrusters, props, propmodel, vehicles, playername, casesen, rpgs, painttarget, minrange, maxtargets, maxbogeys, notargetowner, entity, notownersstuff, steamname, colorcheck, colortarget, pcolR, pcolG, pcolB, pcolA, checkbuddylist, onbuddylist ) + self.MaxRange = maxrange + self.MinRange = minrange or 1 + self.TargetPlayer = players + self.NoTargetOwner = notargetowner + self.NoTargetOwnersStuff = notownersstuff + self.TargetNPC = npcs + self.NPCName = npcname + self.TargetBeacon = beacons + self.TargetHoverballs = hoverballs + self.TargetThrusters = thrusters + self.TargetProps = props + self.PropModel = propmodel + self.TargetVehicles = vehicles + self.PlayerName = playername + self.SteamName = steamname + self.ColorCheck = colorcheck + self.ColorTarget = colortarget + self.PcolR = pcolR + self.PcolG = pcolG + self.PcolB = pcolB + self.PcolA = pcolA + self.CaseSen = casesen + self.TargetRPGs = rpgs + self.EntFil = entity + self.CheckBuddyList = checkbuddylist + self.OnBuddyList = onbuddylist + self.PaintTarget = painttarget + self.MaxTargets = math.floor(math.Clamp((maxtargets or 1), 1, server_settings.Int("wire_target_finders_maxtargets", 10))) + self.MaxBogeys = math.floor(math.Clamp((maxbogeys or 1), self.MaxTargets , server_settings.Int("wire_target_finders_maxbogeys", 30))) + + if (self.SelectedTargets) then --unpaint before clearing + for _,ent in pairs(self.SelectedTargets) do + self:TargetPainter(ent, false) + end + end + self.SelectedTargets = {} + self.SelectedTargetsSel = {} + + local AdjOutputs = {} + local AdjOutputsT = {} + for i = 1, self.MaxTargets do + table.insert(AdjOutputs, tostring(i)) + table.insert(AdjOutputsT, "NORMAL") + table.insert(AdjOutputs, tostring(i).."_Ent") + table.insert(AdjOutputsT, "ENTITY") + end + WireLib.AdjustSpecialOutputs(self.Entity, AdjOutputs, AdjOutputsT) + + + self.Selector = {} + self.Selector.Next = {} + self.Selector.Prev = {} + self.Selector.Hold = {} + local AdjInputs = {} + for i = 1, self.MaxTargets do + local inputnext = tostring(i).."-NextTarget" + //local inputprev = tostring(i).."-PrevTarget" + local inputhold = tostring(i).."-HoldTarget" + self.Selector.Next[inputnext] = i + //self.Selector.Prev[inputprev] = i + //self.Selector.Hold[inputhold] = i + table.insert(AdjInputs, inputnext) + //table.insert(AdjInputs, inputprev) + table.insert(AdjInputs, inputhold) + end + table.insert(AdjInputs, "Hold") + Wire_AdjustInputs(self.Entity, AdjInputs) + + + self:ShowOutput(false) +end + +function ENT:TriggerInput(iname, value) + if (value > 0) then + if self.Selector.Next[iname] then + self:SelectorNext(self.Selector.Next[iname]) + /*elseif self.Selector.Prev[iname] then + self:SelectorPrev(self.Selector.Prev[iname])*/ + /*elseif self.Selector.Hold[iname] then + self:SelectorHold(self.Selector.Hold[iname])*/ + end + end +end + + +function ENT:GetBeaconPos(sensor) + local ch = 1 + if (sensor.Inputs) and (sensor.Inputs.Target.SrcId) then + ch = tonumber(sensor.Inputs.Target.SrcId) + end + if self.SelectedTargets[ch] then + if (not self.SelectedTargets[ch]:IsValid()) then + self.SelectedTargets[ch] = nil + Wire_TriggerOutput(self.Entity, tostring(ch), 0) + return sensor:GetPos() + end + + return self.SelectedTargets[ch]:GetPos() + end + + return sensor:GetPos() +end + +function ENT:GetBeaconVelocity(sensor) + local ch = 1 + if (sensor.Inputs) and (sensor.Inputs.Target.SrcId) then + ch = tonumber(sensor.Inputs.Target.SrcId) + end + if self.SelectedTargets[ch] then + if (not self.SelectedTargets[ch]:IsValid()) then + self.SelectedTargets[ch] = nil + Wire_TriggerOutput(self.Entity, tostring(ch), 0) + return sensor:GetVelocity() + end + return self.SelectedTargets[ch]:GetVelocity() + end + return sensor:GetVelocity() +end + + +function ENT:SelectorNext(ch) + if (self.Bogeys) and (#self.Bogeys > 0) then + if (!self.SelectedTargetsSel[ch]) then self.SelectedTargetsSel[ch] = 1 end + + local sel = self.SelectedTargetsSel[ch] + if (sel > #self.Bogeys) then sel = 1 end + + if (self.SelectedTargets[ch]) and (self.SelectedTargets[ch]:IsValid()) then + + if (self.PaintTarget) then self:TargetPainter(self.SelectedTargets[ch], false) end + table.insert(self.Bogeys, self.SelectedTargets[ch]) //put old target back + self.SelectedTargets[ch] = table.remove(self.Bogeys, sel) //pull next target + if (self.PaintTarget) then self:TargetPainter(self.SelectedTargets[ch], true) end + + else + + self.SelectedTargets[ch] = table.remove(self.Bogeys, sel) //pull next target + if (self.PaintTarget) then self:TargetPainter(self.SelectedTargets[ch], true) end + + end + + self.SelectedTargetsSel[ch] = sel + 1 + self.Inputs[ch.."-HoldTarget"].Value = 1 //put the channel on hold so it wont change in the next scan + Wire_TriggerOutput(self.Entity, tostring(ch), 1) + Wire_TriggerOutput(self.Entity, tostring(ch).."_Ent", self.SelectedTargets[ch]) + end +end + +//function ENT:SelectorPrev(ch) end --TODO if needed + +function ENT:FindInValue(haystack,needle,case_sensitive) + if(type(haystack) ~= "string" or type(needle) ~= "string") then return false end; + if(needle == "") then return true end; + if(case_sensitive) then + if(haystack:find(needle)) then return true end; + else + if(haystack:lower():find(needle:lower())) then return true end; + end + return false +end + +function ENT:FindColor(contact) + if (not self.ColorCheck) then return true end + local col = Color(contact:GetColor()) + if (col.r == self.PcolR) and (col.g == self.PcolG) and (col.b == self.PcolB) and (col.a == self.PcolA) then + return self.ColorTarget + else + return !self.ColorTarget + end +end + +//Prop Protection Buddy List Support (EXPERIMENTAL!!!) +function ENT:CheckTheBuddyList(Ami) + if (!self.CheckBuddyList) then return true end + + local info = Ami:GetInfo( "propprotection_ami"..self:GetPlayer():EntIndex() ) --am I on their list + if ( info == "0" ) then + return !self.OnBuddyList + end + return self.OnBuddyList +end + +/*function ENT:BelongsToBuddy(contact) + if (!self.CheckBuddyList) then return true end + + local info = self:GetOwner():GetInfo( "propprotection_ami"..contact:GetPlayer():EntIndex() ) + if ( info == "0" ) then + return false + else + return true + end +end*/ + + +function ENT:Think() + self.BaseClass.Think(self) + + if (self.Inputs.Hold) and (self.Inputs.Hold.Value > 0) then + //do nothing for now + else + if (self.NextTargetTime) and (CurTime() < self.NextTargetTime) then return end + self.NextTargetTime = CurTime()+1 + + // Find targets that meet requirements + local mypos = self.Entity:GetPos() + local bogeys,dists = {},{} + for _,contact in pairs(ents.FindInSphere(self.Entity:GetPos(), self.MaxRange or 10)) do + local class = contact:GetClass() + if (not self.NoTargetOwnersStuff or (class == "player") or (contact:GetOwner() ~= self:GetPlayer() and not self:checkOwnership(contact))) and ( + -- NPCs + ((self.TargetNPC) and (string.match(class, "^npc_.*")) and (class ~= "npc_heli_avoidsphere") and (self:FindInValue(class,self.NPCName))) or + --Players + ((self.TargetPlayer) and (class == "player") and (!self.NoTargetOwner or self:GetPlayer() != contact) and self:FindInValue(contact:GetName(),self.PlayerName,self.CaseSen) and self:FindInValue(contact:SteamID(),self.SteamName) and self:FindColor(contact) and self:CheckTheBuddyList(contact)) or + --Locators + ((self.TargetBeacon) and (class == "gmod_wire_locator")) or + --RPGs + ((self.TargetRPGs) and (class == "rpg_missile")) or + -- Hoverballs + ((self.TargetHoverballs) and (class == "gmod_hoverball" or class == "gmod_wire_hoverball")) or + -- Thruster + ((self.TargetThrusters) and (class == "gmod_thruster" or class == "gmod_wire_thruster" or class == "gmod_wire_vectorthruster")) or + -- Props + ((self.TargetProps) and (class == "prop_physics") and (self:FindInValue(contact:GetModel(),self.PropModel))) or + -- Vehicles + ((self.TargetVehicles) and (string.match(class, "prop_vehicle"))) or + -- Entity classnames + (self.EntFil ~= "" and self:FindInValue(class,self.EntFil))) + then + local dist = (contact:GetPos() - mypos):Length() + if (dist >= self.MinRange) then + //put targets in a table index by the distance from the finder + bogeys[dist] = contact + table.insert(dists,dist) + end + end + end + + //sort the list of bogeys by key (distance) + self.Bogeys = {} + self.InRange = {} + table.sort(dists) + local k = 1 + for i,d in pairs(dists) do + if !self:IsTargeted(bogeys[d], i) then + self.Bogeys[k] = bogeys[d] + k = k + 1 + if (k > self.MaxBogeys) then break end + end + end + + + //check that the selected targets are valid + for i = 1, self.MaxTargets do + if (self:IsOnHold(i)) then + self.InRange[i] = true + end + + if (!self.InRange[i]) or (!self.SelectedTargets[i]) or (self.SelectedTargets[i] == nil) or (!self.SelectedTargets[i]:IsValid()) then + if (self.PaintTarget) then self:TargetPainter(self.SelectedTargets[i], false) end + if (#self.Bogeys > 0) then + self.SelectedTargets[i] = table.remove(self.Bogeys, 1) + if (self.PaintTarget) then self:TargetPainter(self.SelectedTargets[i], true) end + Wire_TriggerOutput(self.Entity, tostring(i), 1) + Wire_TriggerOutput(self.Entity, tostring(i).."_Ent", self.SelectedTargets[i]) + else + self.SelectedTargets[i] = nil + Wire_TriggerOutput(self.Entity, tostring(i), 0) + Wire_TriggerOutput(self.Entity, tostring(i).."_Ent", NULL) + end + end + end + + end + + //temp hack + if self.SelectedTargets[1] then + self:ShowOutput(true) + else + self:ShowOutput(false) + end +end + +function ENT:IsTargeted(bogey, bogeynum) + for i = 1, self.MaxTargets do + if (self.SelectedTargets[i]) and (self.SelectedTargets[i] == bogey) then + //hold this target + if (self.Inputs[i.."-HoldTarget"]) and (self.Inputs[i.."-HoldTarget"].Value > 0) then + self.InRange[i] = true + return true + end + + //this bogey is not as close as others, untarget it and let it be add back to the list + if (bogeynum > self.MaxTargets) then + self.SelectedTargets[i] = nil + if (self.PaintTarget) then self:TargetPainter(bogey, false) end + return false + end + + self.InRange[i] = true + return true + end + end + return false +end + +function ENT:IsOnHold(ch) + if (self.Inputs[ch.."-HoldTarget"]) and (self.Inputs[ch.."-HoldTarget"].Value > 0) then + return true + end + return false +end + + +function ENT:OnRemove() + self.BaseClass.OnRemove(self) + + --unpaint all our targets + if (self.PaintTarget) then + for _,ent in pairs(self.SelectedTargets) do + self:TargetPainter(ent, false) + end + end +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) + + self.MaxTargets = self.MaxTargets or 1 +end + +function ENT:TargetPainter( tt, targeted ) + if tt and -- There is a target + tt.Entity and -- Target has is an entity + tt.Entity:IsValid() and -- And it's valid + tt.Entity:EntIndex() != 0 -- And isn't worldspawn + then + if (targeted) then + self.OldColor = { tt.Entity:GetColor() } + tt.Entity:SetColor(255, 0, 0, 255) + else + if not self.OldColor then self.OldColor = { 255, 255, 255, 255 } end + + local r,g,b,a = tt.Entity:GetColor() + + -- do not change color back if the target color changed in the meantime + if r != 255 or g != 0 or b != 0 then self.OldColor = { r, g, b, self.OldColor[4] } end + + -- do not change alpha back if the target color changed in the meantime + if a != 255 then self.OldColor[4] = a end + + tt.Entity:SetColor(unpack(self.OldColor)) + end + end +end + + +function ENT:ShowOutput(value) + local txt = "Target Finder - " + if (value) then + txt = txt .. "Target Acquired" + else + txt = txt .. "No Target" + end + + if (self.Inputs.Hold) and (self.Inputs.Hold.Value > 0) then txt = txt .. " - Locked" end + + self:SetOverlayText(txt) +end + + + +// +// PropProtection support +// +// Uses code from uclip for checking ownership +// +-- Written by Team Ulysses, http://ulyssesmod.net/ +local hasPropProtection = false -- Chaussette's Prop Protection (preferred over PropSecure) +local propProtectionFn -- Function to call to see if a prop belongs to a player. We have to fetch it from a local so we'll store it here. + +local hasPropSecure = false -- Prop Secure by Conna +local hasProtector = false -- Protector by Conna + +local noProtection = false -- If there's no protection whatsoever, this is flagged. +-- We need this flag because with a protector, we default to _not_ being able to go through things. +-- This flag saves us major memory/bandwidth when there's no protection + +-- We'll check status of protectors in this init +local function init() + local t = hook.GetTable() + local fn + if(t.CanTool) then + if t.CanTool[0] then -- ULib + fn = t.CanTool[0].PropProtection + else + fn = t.CanTool.PropProtection + end + end + + hasPropProtection = type( fn ) == "function" + + if hasPropProtection then + -- We're going to get the function we need now. It's local so this is a bit dirty + local gi = debug.getinfo( fn ) + for i=1,gi.nups do + if debug.getupvalue( fn, i ) == "Appartient" then + local junk + junk, propProtectionFn = debug.getupvalue( fn, i ) + break + end + end + end + + hasPropSecure = type( PropSecure ) == "table" + hasProtector = type( Protector ) == "table" + + if not hasPropProtection and not hasPropSecure and not hasProtector then + noProtection = true + end +end +hook.Add( "Initialize", "WireTargetFinderInitialize", init ) + +-- This function checks the protector to see if ownership has changed from what we think it is. Notifies player too. +local function updateOwnership( ply, ent ) + if noProtection then return end -- No point on going on + if not ent.WireTargetFinder then ent.WireTargetFinder = {} end -- Initialize table + + local owns + if hasPropProtection then -- Chaussette's Prop Protection (preferred over PropSecure) + owns = propProtectionFn( ply, ent ) + elseif hasPropSecure then -- PropSecure + owns = PropSecure.IsPlayers( ply, ent ) + elseif hasProtector then -- Protector + owns = Protector.Owner( ent ) == ply:UniqueID() + end + + if owns == false then -- More convienent to store as nil, takes less memory! + owns = nil + end + + if ent.WireTargetFinder[ ply ] ~= owns then + ent.WireTargetFinder[ ply ] = owns + end +end + +function ENT:checkOwnership( ent ) + if noProtection then return true end -- No protection, they own everything. + if (!self.CheckBuddyList) then return true end + + updateOwnership( self:GetPlayer(), ent ) -- Make sure server and the client are current + return ent.WireTargetFinder[ self:GetPlayer() ] +end diff --git a/lua/entities/gmod_wire_target_finder/shared.lua b/lua/entities/gmod_wire_target_finder/shared.lua new file mode 100644 index 0000000000..7fe9dba3f7 --- /dev/null +++ b/lua/entities/gmod_wire_target_finder/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Target Finder" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_textreceiver/cl_init.lua b/lua/entities/gmod_wire_textreceiver/cl_init.lua new file mode 100644 index 0000000000..57d2fd70e3 --- /dev/null +++ b/lua/entities/gmod_wire_textreceiver/cl_init.lua @@ -0,0 +1,5 @@ + +ENT.Spawnable = false +ENT.AdminSpawnable = false + +include('shared.lua') diff --git a/lua/entities/gmod_wire_textreceiver/cpu doc.txt b/lua/entities/gmod_wire_textreceiver/cpu doc.txt new file mode 100644 index 0000000000..b74f168588 --- /dev/null +++ b/lua/entities/gmod_wire_textreceiver/cpu doc.txt @@ -0,0 +1,18 @@ +0 = default +1 = trigger +2 = trigger hold length +3-4 = parser text +5 = global +6 = toggle +7 = display output text +8 = include self +9 = secure args +10 = player outputs //Reload to remove inputs +11 = sensitivity +12 = reload (change to 1 to reload lines/args) +13 = lines +14 = Struct line + +Struct line +0 = length +1-length = line \ No newline at end of file diff --git a/lua/entities/gmod_wire_textreceiver/init.lua b/lua/entities/gmod_wire_textreceiver/init.lua new file mode 100644 index 0000000000..e22dea135d --- /dev/null +++ b/lua/entities/gmod_wire_textreceiver/init.lua @@ -0,0 +1,463 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "TextReceiver" + +local MODEL = Model("models/jaanus/wiretool/wiretool_range.mdl") + +local TextReceivers = {} +local securetext = nil +local returntext = false + +local function Add_TextReceiver( r ) + table.insert( TextReceivers, r ) +end + +hook.Add("PlayerSay","TextReceiverSay", function(pl, text, toall) + securetext = nil + for i,ent in ipairs( TextReceivers ) do + if not IsEntity(ent.Entity) then + table.remove(TextReceivers, i) + else + local temptext = ent:TextReceived(pl,text) + if (securetext == nil && temptext != nil) then + securetext = temptext + end + end + end + if (securetext != nil) then return securetext end +end) + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + Add_TextReceiver(self.Entity) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Error" }) +end + +function ENT:Setup( liness, globall,OutputTextt,Holdd,Triggerr,SELFF,sensitivityy,togglee,utriggerr,parsetextt,secure,playerout) + self.global = globall + self.Hold = Holdd or 0.1 + self.Trig = Triggerr or 1 + self.Toggle = togglee + self.UTrig = utriggerr or 0 + self.Bytes = {} + self.values = {} + self.lines = {} + self.CLines = {} + self.CLines = table.Copy(liness) //Makes getting the lines easier + self.Iself = SELFF + self.char1 = string.Left(parsetextt,1) or '"' + self.char2 = string.Right(parsetextt,1) or '"' + self.Sensitivity = sensitivityy or 4 + self.OutputText = OutputTextt or "" + self.secure = secure + self.playerout = playerout + + local onames = {} + local i = 1 + for k,o in pairs(liness) do + self.lines[i] = {} + self.lines[i].args = {} + self.lines[i].line, self.lines[i].args,self.lines[i].toggle,self.lines[i].global,self.lines[i].on,self.lines[i].off,self.lines[i].Allow,self.lines[i].Deny,self.lines[i].TAllow,self.lines[i].TDeny, self.lines[i].Radius = self:SParseTextAndArgs(o,"<",">",0) + self.lines[i].argsC = nil + table.insert(onames, self.lines[i].line) + for k2,o2 in pairs(self.lines[i].args) do + o2.V = 0 + table.insert(onames, o2.A) + self.lines[i].argsC = true + end + self.lines[i].value = self.lines[i].off or self.UTrig + self.lines[i].reset = 0 + i = i + 1 + end + if (playerout == true) then + table.insert(onames,"X") + table.insert(onames,"Y") + table.insert(onames,"Z") + table.insert(onames,"EntId") + end + Wire_AdjustOutputs(self.Entity, onames) + + self.changed = false + + self.Bytes[1] = self.UTrig + self.Bytes[2] = self.Trig + self.Bytes[3] = self.Hold + self.Bytes[4] = string.byte(self.char1) or 0 + self.Bytes[5] = string.byte(self.char2) or 0 + self.Bytes[6] = self.global + self.Bytes[7] = self.Toggle + self.Bytes[8] = self.OutputText + self.Bytes[9] = self.Iself + self.Bytes[10] = self.secure + self.Bytes[11] = self.playerout + self.Bytes[12] = self.Sensitivity + self.Bytes[13] = 0 + self.Bytes[14] = table.Count(self.CLines) + local q = 15 + for k,v in pairs(self.CLines) do + self.Bytes[tonumber(q)] = #v + for i = 1,#v do //String to table :P + self.Bytes[tonumber(q + i)] = tonumber(string.byte(string.sub(v,i,i)) or 0) or 0 + end + q = q + #v + end + + self:TriggerOutput() +end + +function ENT:ParsePar(text) + local y = string.find(text,"(",0,true) + if (y == nil) then return nil end + local z = string.find(text,")",y + 1,true) + if (z == nil) then return nil end + return tonumber(string.sub(text,y + 1, z - 1)) +end + +function ENT:ParseFPar(text) + local allow,deny = {},{} + local p = string.find(text,"(",0,true) + if (p == nil) then return {} end + local n = string.find(text,")",p + 1,true) + if (n == nil) then return {} end + local args = string.Explode(",",string.sub(text,p + 1, n - 1)) + for k,o in pairs(args) do + if (o != nil) then + if (string.sub(o,1,1)=="~") then + table.insert(deny,string.sub(o,2)) + else + table.insert(allow,o) + end + end + end + return allow,deny +end + +function ENT:SParseTextAndArgs(line,char1,char2,remov) + if !(string.find(line,char1, 1, true) && string.find(line,char2, 1, true)) then return string.Trim(line),{nil} end + local ret = {} + local ret2 = string.Trim(string.sub(line,1,string.find(line,char1, 1, true) - 1)) + local p = string.find(line,char1, 1, true) + local n = 1 + local i = 1 + local toggle = false + local global = false + local on = nil + local off = nil + local allow = {} + local deny = {} + local tallow = {} + local tdeny = {} + local curarg = nil + local radius = nil + while(p) do + n = string.find(line,char2,p + 1, true) + curarg = string.sub(line,p + 1,n - 1) + if (curarg == "t()") then toggle = true + elseif (curarg == "g()") then global = true + elseif (curarg == "") then + elseif (string.sub(curarg,1,3) == "on(") then + on = self:ParsePar(curarg) + elseif (string.sub(curarg,1,4) == "off(") then + off = self:ParsePar(curarg) + elseif (string.sub(curarg,1,2) == "f(") then + allow,deny = self:ParseFPar(curarg) + elseif (string.sub(curarg,1,3) == "tf(") then + tallow,tdeny = self:ParseFPar(curarg) + elseif (string.sub(curarg,1,2) == "r(") then + radius = self:ParsePar(curarg) + elseif (string.sub(curarg,1,1) == "~") then + ret[i] = {} + ret[i].A = char1 .. (string.sub(line,p + 2 + remov,n - remov) or "") + ret[i].V = 0 + ret[i].R = true + i = i + 1 + else + ret[i] = {} + ret[i].A = string.sub(line,p + remov,n - remov) + ret[i].V = 0 + ret[i].R = false + i = i + 1 + end + p = string.find(line,char1,n + 1, true) + end + return ret2,ret,toggle,global,on,off,allow,deny,tallow,tdeny,radius +end + + +//yay for while loops and Trim :D +function ENT:ParseTextAndArgs(line,char1,char2,remov) + if (line == nil) then return "",{} end + if !(string.find(line,char1, 1, true) && string.find(line,char2, 1, true)) then return string.Trim(line),{nil} end + local ret = {} + local ret2 = string.Trim(string.sub(line,1,string.find(line,char1, 1, true) - 1)) + local ret3 = string.sub(line,1,string.find(line,char1, 1, true) - 1) + local p = string.find(line,char1, 1, true) + local n = 1 + while(p) do + n = string.find(line,char2,p + 1, true) + if (string.sub(string.sub(line,p + 1,n - 1),1,1) == "#") then + table.insert(ret,tonumber(string.byte(string.sub(line,p + 1 + remov,n - remov)) or 0)) + ret3 = ret3 .. '"*"' + else + table.insert(ret,tonumber(string.sub(line,p + remov,n - remov)) or tonumber(string.byte(string.sub(line,p + remov,n - remov)) or 0)) + ret3 = ret3 .. '"' .. (string.rep("*",string.len(string.sub(line,p + remov,n - remov))) or "") .. '"' + end + p = string.find(line,char1, n + 1, true) + if (p!=nil) then + ret3 = ret3 .. (string.sub(line,n+1,p-1) or "") + end + end + return ret2,ret,ret3 +end + +function ENT:Think() + self.BaseClass.Think(self) + + if (self.Toggle == false) then + for k,o in pairs(self.lines) do + if !(o.toggle) then + if (o.value != (o.off or self.UTrig) && CurTime() >= o.reset) then + o.value = o.off or self.UTrig + if (o.argsC) then + for k2,o2 in pairs(o.args) do + if (o2.R == true) then + o2.V = 0 + end + end + end + self:TriggerOutput() + end + end + end + end + + self.Entity:NextThink(CurTime()+0.04) + return true +end + +function ENT:SensitivityCheck(text,line) + if !(text || line) then return false end + if (self.Sensitivity == 1) then + if (text == line) then return true end + elseif (self.Sensitivity == 2) then + if (string.lower(text) == string.lower(line)) then return true end + elseif (self.Sensitivity == 3) then + if (string.find(text,line, 1, true)) then return true end + elseif (self.Sensitivity == 4) then + if (string.find(string.lower(text), string.lower(line), 1, true)) then return true end + end + return false +end + +function ENT:CheckforChar(text,char1,char2) + if (string.find(text,char1, 1, true) && string.find(text,char2,string.find(text,char1, 1, true)+1, true)) then return true end + return false +end + +function ENT:InRadius(plpos,selfpos,radius) + if (radius == nil) then return true end + if (selfpos:Distance(plpos) < radius) then + return true + else + return false + end +end + +//All text is sent here now :D +function ENT:TextReceived(pl,text) + local i = 1 + TextReceiversecuretext = nil + for k,o in pairs(self.lines) do + if (text != nil && o.line != nil) then + TextReceivertext = text + TextReceiverarg = nil + if (self:InRadius(pl:GetPos(),self.Entity:GetPos(),o.Radius) == true) then + if (o.argsC == true) then + if (self:CheckforChar(TextReceivertext or "",self.char1,self.char2) == true) then + TextReceiverarg = {} + local temptext = nil + TextReceivertext,TextReceiverarg,temptext = self:ParseTextAndArgs(text,self.char1,self.char2,1) + if (TextReceiversecuretext==nil && self.secure == true) then + TextReceiversecuretext = temptext + end + end + end + if (self:SensitivityCheck((TextReceivertext or ""),(o.line or ""))) then + local allow = self:ParseNameandTeam(pl:Nick(),team.GetName(pl:Team()),o,self.global || o.global) + if (allow == true || (self.Iself == true && self.pl == pl)) then + if (self.playerout == true) then + self.Caller = pl + end + if (TextReceiverarg != nil) then + if (table.Count(TextReceiverarg) > 0) then + for k = 1,table.Count(o.args) do + o.args[k].V = (TextReceiverarg[k] or 0) + end + TextReceiverarg = {} + end + end + if (self.Toggle || o.toggle) then + if (o.value == o.on) then + o.value = o.off or self.UTrig + else + o.value = o.on or self.Trig + end + o.reset = -1 + else + o.value = o.on or self.Trig + o.reset = CurTime() + self.Hold + end + self:TriggerOutput() + end + end + end + end + i = i + 1 + end + if (TextReceiversecuretext==nil) then + return nil + else + return TextReceiversecuretext + end +end + +function ENT:ParseNameandTeam(name,TEAM,line,global) + if (line.Allow != nil) then + for k,o in pairs(line.Allow) do + if (o==name) then return true end + end + end + if (line.Deny != nil) then + for k,o in pairs(line.Deny) do + if(o==name) then return false end + end + end + if (line.TAllow != nil) then + for k,o in pairs(line.TAllow) do + if (o==TEAM) then return true end + end + end + if (line.TDeny != nil) then + for k,o in pairs(line.TDeny) do + if(o==TEAM) then return false end + end + end + return global +end + +function ENT:FOutText(text) + return math.floor(text*10)/10 +end + +function ENT:ReadCell(Addr) + return self.Bytes[Addr+1] or 0 //Makes for faster reading :D +end + +function ENT:ReloadLines() + if (self.changed == false) then return end + self.changed = false + local onames = {} + local lines = {} + local i = 15 + local line = "" + while (self.Bytes[i]!=nil) do + line = "" + for q = 1,self.Bytes[i] do + i = i + 1 + line = line .. (string.char(self.Bytes[tonumber(i)] or 0) or "") + end + i = i + 1 + if (line != nil && line != "") then table.insert(lines,line) end + end + i = 1 + for k,o in pairs(lines) do + self.lines[i] = {} + self.lines[i].args = {} + self.lines[i].line, self.lines[i].args,self.lines[i].toggle,self.lines[i].global,self.lines[i].on,self.lines[i].off,self.lines[i].Allow,self.lines[i].Deny,self.lines[i].TAllow,self.lines[i].TDeny, self.lines[i].Radius = self:SParseTextAndArgs(o,"<",">",0) + self.lines[i].argsC = nil + table.insert(onames, self.lines[i].line) + for k2,o2 in pairs(self.lines[i].args) do + o2.V = 0 + table.insert(onames, o2.A) + self.lines[i].argsC = true + end + self.lines[i].value = self.lines[i].off or self.UTrig + self.lines[i].reset = 0 + i = i + 1 + end + if (self.playerout == true) then + table.insert(onames,"X") + table.insert(onames,"Y") + table.insert(onames,"Z") + table.insert(onames,"EntId") + end + Wire_AdjustOutputs(self.Entity, onames) +end + +function ENT:WriteCell(Addr,value) + Addr = tonumber(Addr) + if (Addr == 0) then self.Bytes[Addr+1] = value or 0 self.UTrig = value or 0 end + if (Addr == 1) then self.Bytes[Addr+1] = value or 0 self.Trig = value or 0 end + if (Addr == 2) then self.Bytes[Addr+1] = value or 0 self.Hold = value or 0 end + if (Addr == 3) then self.Bytes[Addr+1] = string.char(value or 0) self.char1 = string.char(value or 0) end + if (Addr == 4) then self.Bytes[Addr+1] = string.char(value or 0) self.char2 = string.char(value or 0) end + if (Addr == 5) then self.Bytes[Addr+1] = value or 0 self.global = value or 0 end + if (Addr == 6) then self.Bytes[Addr+1] = value or 0 self.Toggle = value or 0 end + if (Addr == 7) then self.Bytes[Addr+1] = value or 0 self.OutputText = value or 0 end + if (Addr == 8) then self.Bytes[Addr+1] = value or 0 self.Iself = value or 0 end + if (Addr == 9) then self.Bytes[Addr+1] = value or 0 self.secure = value or 0 end + if (Addr == 10) then self.Bytes[Addr+1] = value or 0 self.playerout = value or 0 end + if (Addr == 11) then self.Bytes[Addr+1] = value or 0 self.Sensitivity = value or 0 end + if (Addr == 12 && Value != 0) then self.Bytes[Addr+1] = value or 0 self:ReloadLines() end + if (Addr >= 13) then + self.Bytes[Addr+1] = value + self.changed = true + end + self:TriggerOutput() + return true +end + +function ENT:TriggerOutput() + self.OutText = "TextReceiver:" + local pos = nil + if (self.playerout == true && self.Caller != nil) then + pos = self.Caller:GetPos() + Wire_TriggerOutput(self.Entity,"X",pos.X) + Wire_TriggerOutput(self.Entity,"Y",pos.Y) + Wire_TriggerOutput(self.Entity,"Z",pos.Z) + Wire_TriggerOutput(self.Entity,"EntId",self.Caller:EntIndex()) + end + local i = 1 + for k,o in pairs(self.lines) do + if (self.OutputText == true) then self.OutText = self.OutText .. "\n" .. (o.line or "") .. ":" .. (o.value or 0) end + for q = 1, table.Count(o.args) do + Wire_TriggerOutput(self.Entity,o.args[q].A,(o.args[q].V or 0)) + if (self.OutputText == true) then self.OutText =self.OutText .. " " .. (o.args[q].A or "") .. ":" .. (o.args[q].V or 0) end + end + Wire_TriggerOutput(self.Entity,o.line,o.value) + i = i + 1 + end + if (self.playerout == true) then //We want it to trigger first BUT display last. + if (pos != nil) then + self.OutText = self.OutText .. "\nPos: " .. self:FOutText(pos.X or 0) .. ", " .. self:FOutText(pos.Y or 0) .. ", " .. self:FOutText(pos.Z or 0) .. "\nEntId: " + else + self.OutText = self.OutText .. "\nPos: 0, 0, 0\nEntId: " + end + if (self.Caller != nil) then + self.OutText = self.OutText .. self.Caller:EntIndex() + else + self.OutText = self.OutText .. "0" + end + end + /*if (self.NoFilter == false) then + self.OutText = "\nBuddyList: " .. string.Implode(" ",self.Allow) .. "\nIgnoreList: " .. string.Implode(" ",self.Deny) + end*/ + self:SetOverlayText(self.OutText) +end diff --git a/lua/entities/gmod_wire_textreceiver/shared.lua b/lua/entities/gmod_wire_textreceiver/shared.lua new file mode 100644 index 0000000000..94d8b8a96f --- /dev/null +++ b/lua/entities/gmod_wire_textreceiver/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire TextReceiver" +ENT.Author = "High6" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_textscreen/cl_init.lua b/lua/entities/gmod_wire_textscreen/cl_init.lua new file mode 100644 index 0000000000..c6dd677672 --- /dev/null +++ b/lua/entities/gmod_wire_textscreen/cl_init.lua @@ -0,0 +1,78 @@ +--Wire text screen by greenarrow and wire team +--http://gmodreviews.googlepages.com/ +--http://forums.facepunchstudios.com/greenarrow + +include('shared.lua') +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH +ENT.currentText = "" +ENT.allowDraw = false + +function ENT:Initialize() + self:GetConfig() + self.allowDraw = true + self.currentText = self:GetText() +end + +function ENT:Draw() + self.Entity:DrawModel() + if (!self.allowDraw) then return true end + --nighteagle screen vector rotation and positioning legacy code + local OF = 0.3 + local OU = 11.8 + local OR = -2.35 + local Res = 0.12 + local RatioX = 1 + + local ang = self.Entity:GetAngles() + local rot = Vector(-90,90,0) + ang:RotateAroundAxis(ang:Right(), rot.x) + ang:RotateAroundAxis(ang:Up(), rot.y) + ang:RotateAroundAxis(ang:Forward(), rot.z) + local pos = self.Entity:GetPos() + (self.Entity:GetForward() * OF) + (self.Entity:GetUp() * OU) + (self.Entity:GetRight() * OR) + + cam.Start3D2D(pos,ang,Res) + local x = -112 + local y = -104 + local w = 296 + local h = 292 + + --add changable backround colour some time. + surface.SetDrawColor(0,0,0,255) + surface.DrawRect(x/RatioX,y,(x+w)/RatioX,y+h) + + local justOffset = (w / 3) + (self.textJust * (w / 3.5)) + self:GetConfig() + self.currentText = self:GetText() + if (self.chrPerLine ~= 0) then + draw.DrawText(self.currentText, "textScreenfont"..tostring(self.chrPerLine), (x + justOffset - 92) / RatioX, y + 2, Color(self.tRed, self.tGreen, self.tBlue, 255), self.textJust) + end + cam.End3D2D() + Wire_Render(self.Entity) +end + +function ENT:IsTranslucent() + return true +end + + +if !textScreenFontsCreated then + textScreenFontsCreated = true + local fontSize = 380 + surface.CreateFont( "coolvetica", fontSize, 400, false, false, "textScreenfont1" ) + surface.CreateFont( "coolvetica", fontSize / 2, 400, false, false, "textScreenfont2" ) + surface.CreateFont( "coolvetica", fontSize / 3, 400, false, false, "textScreenfont3" ) + surface.CreateFont( "coolvetica", fontSize / 4, 400, false, false, "textScreenfont4" ) + surface.CreateFont( "coolvetica", fontSize / 5, 400, false, false, "textScreenfont5" ) + surface.CreateFont( "coolvetica", fontSize / 6, 400, false, false, "textScreenfont6" ) + surface.CreateFont( "coolvetica", fontSize / 7, 400, false, false, "textScreenfont7" ) + surface.CreateFont( "coolvetica", fontSize / 8, 400, false, false, "textScreenfont8" ) + surface.CreateFont( "coolvetica", fontSize / 9, 400, false, false, "textScreenfont9" ) + surface.CreateFont( "coolvetica", fontSize / 10, 400, false, false, "textScreenfont10" ) + surface.CreateFont( "coolvetica", fontSize / 11, 400, false, false, "textScreenfont11" ) + surface.CreateFont( "coolvetica", fontSize / 12, 400, false, false, "textScreenfont12" ) + surface.CreateFont( "coolvetica", fontSize / 13, 400, false, false, "textScreenfont13" ) + surface.CreateFont( "coolvetica", fontSize / 14, 400, false, false, "textScreenfont14" ) + surface.CreateFont( "coolvetica", fontSize / 15, 400, false, false, "textScreenfont15" ) +end diff --git a/lua/entities/gmod_wire_textscreen/init.lua b/lua/entities/gmod_wire_textscreen/init.lua new file mode 100644 index 0000000000..37eee6540c --- /dev/null +++ b/lua/entities/gmod_wire_textscreen/init.lua @@ -0,0 +1,194 @@ +--Wire text screen by greenarrow + wire team +--http://gmodreviews.googlepages.com/ +--http://forums.facepunchstudios.com/greenarrow + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) +include('shared.lua') + +ENT.WireDebugName = "Text Screen" +ENT.initOn = true +ENT.firstConfig = true +ENT.clock = false +ENT.currentLine = 0 +ENT.currentText = "" +ENT.currentTextnum = 0 + +wire_text_screen_lastCreated = nil + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) +end + +function ENT:Setup(TextList, chrPl, textJust, tRed, tGreen, tBlue, numInputs, defaultOn) + self.TextList = TextList --table of text lines + self.TextList[0] = "" + + self.maxLineLen = math.abs(chrPl) + self.maxLines = math.abs(chrPl) / 2 + self.chrPerLine = math.abs(chrPl) + self.numInputs = math.abs(numInputs) + self.textJust = textJust + self.tRed = tRed + self.tGreen = tGreen + self.tBlue = tBlue + self.currentLine = 0 + self.StringNum = 1 + self.String = self.TextList[1] + self.StringClk = 0 + + --setup input with required number of value inputs + valInputs = {} + self.Val = {} + local SpecialInputs = {} + SpecialInputTypes = {"NORMAL", "NORMAL", "NORMAL", "STRING", "NORMAL"} + for n=1, numInputs do + table.insert(self.Val, 0) + table.insert(valInputs, "Value "..n) + table.insert(SpecialInputTypes, "NORMAL") + end + inputTable = {"Clk", "Text", "StringNum", "String", "StringClk"} + table.Add(inputTable, valInputs) + self.Inputs = Wire_CreateInputs(self.Entity, inputTable) + WireLib.AdjustSpecialInputs(self.Entity, inputTable, SpecialInputTypes) + + self.defaultOn = defaultOn + + --send config to client + self:SetConfig() + --format text and send to client + self:UpdateScreen() + + --if option is selected, show text without the need for wire inputs + if (defaultOn == 1) then + self:TriggerInput("Clk", 1) --make text on by default + self:TriggerInput("Text", 1) + else + self:TriggerInput("Clk", 0) + self:TriggerInput("Text", 0) + end +end + +local function UpdateValuesCheck(self) + if (self.StringClk==1) then + if (self.TextList[self.StringNum]!=self.String) then + self.TextList[self.StringNum] = self.String + if (self.StringNum==self.currentTextnum) then + self:UpdateScreen() + end + end + end +end + +--wire input routine +function ENT:TriggerInput(iname, value) + if (!iname) then + //do nothing, this prevents the next line from erroring + elseif (iname == "Text") then + self.currentTextnum = math.abs(value) + self:UpdateScreen() + elseif (iname == "Clk") then + if (math.abs(value) > 0) then + self.clock = true + self:UpdateScreen() + else + self.clock = false + end + elseif (iname == "StringNum") then + if (value<=12 && value>=1) then + value = value - value % 1 + end + self.StringNum = value + UpdateValuesCheck(self) + elseif (iname == "String") then + self.String = value + UpdateValuesCheck(self) + elseif (iname == "StringClk") then + if (value==1) then + self.StringClk=1 + UpdateValuesCheck(self) + else + self.StringClk=0 + end + elseif (string.sub(iname, 1, 6) == "Value ") then + self.Val[tonumber(string.sub(iname, 7, -1))] = math.abs(value) + self:UpdateScreen() + end +end + +--format text and send it to the client(s) +function ENT:UpdateScreen() + if (self.clock && (self.currentLine <= self.maxLines)) then + local compstring = "" + local outString = "" + local intoText = false + local basestring = self.TextList[self.currentTextnum] + if (!basestring) then return false end + + for k,inp in ipairs(self.Val) do + local nString = string.format("%G", inp) + basestring = string.gsub(basestring, "<"..k..">", nString) + end + + basestring = string.gsub(basestring, "
", "\n") + compstring = basestring + local outString = "" + if (string.len(compstring) > self.maxLineLen) then + local lastSpace = 0 + local lastBreak = 1 + local numLines = 1 + for chrNum = 1, string.len(compstring) do + if (string.byte(string.sub(compstring, chrNum, chrNum)) == 10) && (numLines <= self.maxLines) then + outString = outString..string.Left(string.sub(compstring, lastBreak, chrNum), self.chrPerLine) + lastBreak = chrNum + 1 + lastSpace = 0 + numLines = numLines + 1 + end + if (string.sub(compstring, chrNum, chrNum) == " ") then + lastSpace = chrNum + end + if (chrNum >= lastBreak + self.maxLineLen) && (numLines <= self.maxLines) then --if we've gone past a line length since the last break and line is still on screen + if (lastSpace > 0) then + outString = outString..string.Left(string.sub(compstring, lastBreak, lastSpace), self.chrPerLine).."\n" + lastBreak = lastSpace + 1 + lastSpace = 0 + numLines = numLines + 1 + end + end + end + if (numLines <= self.maxLines) then + local foff = 0 + outString = outString..string.Left(string.sub(compstring, lastBreak + foff, string.len(compstring)), self.chrPerLine).."\n" + end + else + outString = compstring + end + self:SetText (outString) + end +end + + +function MakeWireTextScreen( pl, Pos, Ang, model, TextList, chrPerLine, textJust, tRed, tGreen, tBlue, numInputs, defaultOn, frozen) + if ( !pl:CheckLimit( "wire_textscreens" ) ) then return false end + local wire_textscreen = ents.Create( "gmod_wire_textscreen" ) + if (!wire_textscreen:IsValid()) then return false end + wire_textscreen:SetModel(model) + wire_textscreen:Setup(TextList, chrPerLine, textJust, tRed, tGreen, tBlue, numInputs, defaultOn) + wire_textscreen:SetAngles( Ang ) + wire_textscreen:SetPos( Pos ) + wire_textscreen:Spawn() + + if wire_textscreen:GetPhysicsObject():IsValid() then + local Phys = wire_textscreen:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + wire_textscreen:SetPlayer(pl) + wire_textscreen.pl = pl + + pl:AddCount( "wire_textscreens", wire_textscreen ) + return wire_textscreen +end +duplicator.RegisterEntityClass("gmod_wire_textscreen", MakeWireTextScreen, "Pos", "Ang", "Model", "TextList", "chrPerLine", "textJust", "tRed", "tGreen", "tBlue", "numInputs", "defaultOn", "frozen") diff --git a/lua/entities/gmod_wire_textscreen/shared.lua b/lua/entities/gmod_wire_textscreen/shared.lua new file mode 100644 index 0000000000..26eae45487 --- /dev/null +++ b/lua/entities/gmod_wire_textscreen/shared.lua @@ -0,0 +1,44 @@ +--Wire text screen by greenarrow +--http://gmodreviews.googlepages.com/ +--http://forums.facepunchstudios.com/greenarrow +--There are a few bits of code from wire digital screen here and there, mainly just +--the values to correctly format cam3d2d for the screen, and a few standard things in the stool. + +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Text Screen" +ENT.Author = "greenarrow" +ENT.Contact = "http://forums.facepunchstudios.com/greenarrow" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetText(text) + self.Entity:SetNetworkedString("TLine", text) +end + +function ENT:GetText() + return self.Entity:GetNetworkedString("TLine") +end + + +function ENT:GetConfig() + self.chrPerLine = self.Entity:GetNetworkedInt("chrpl") + self.textJust = self.Entity:GetNetworkedInt("textjust") + self.tRed = self.Entity:GetNetworkedInt("colourr") + self.tGreen = self.Entity:GetNetworkedInt("colourg") + self.tBlue = self.Entity:GetNetworkedInt("colourb") + return +end + +function ENT:SetConfig() + self.Entity:SetNetworkedInt("chrpl", self.chrPerLine) + self.Entity:SetNetworkedInt("textjust", self.textJust) + self.Entity:SetNetworkedInt("colourr", self.tRed) + self.Entity:SetNetworkedInt("colourg", self.tGreen) + self.Entity:SetNetworkedInt("colourb", self.tBlue) +end diff --git a/lua/entities/gmod_wire_thruster/cl_init.lua b/lua/entities/gmod_wire_thruster/cl_init.lua new file mode 100644 index 0000000000..25e4dc62fa --- /dev/null +++ b/lua/entities/gmod_wire_thruster/cl_init.lua @@ -0,0 +1,1597 @@ + +include('shared.lua') + +CreateConVar( "cl_drawthrusterseffects", "1" ) + +local matHeatWave = Material( "sprites/heatwave" ) +local matFire = Material( "effects/fire_cloud1" ) +local matPlasma = Material( "effects/strider_muzzle" ) +local matColor = Material( "effects/bloodstream" ) + + +// Thrusters only really need to be twopass when they're active.. something to think about.. +ENT.RenderGroup = RENDERGROUP_BOTH + +local emitter = nil + +function ENT:Initialize() + self.ShouldDraw = 1 + self.NextSmokeEffect = 0 + + -- fixed by WeltEnSTurm: one emitter is enough! + if !emitter then emitter = ParticleEmitter(Vector(0,0,0)) end + mx, mn = self.Entity:GetRenderBounds() + self.Entity:SetRenderBounds( mn + Vector(0,0,128), mx, 0 ) +end + + +function ENT:Draw() + self.BaseClass.Draw( self ) + + self:DrawTranslucent() +end + +function ENT:DrawTranslucent() + if ( self.ShouldDraw == 0 ) then return end + + if ( !self:IsOn() ) then return end + if ( self:GetEffect() == "none" ) then return end + + local EffectThink = self[ "EffectDraw_"..self:GetEffect() ] + if ( EffectThink ) then EffectThink( self ) end +end + + +function ENT:Think() + self.BaseClass.Think(self) + + self.ShouldDraw = GetConVarNumber( "cl_drawthrusterseffects" ) + + if ( self.ShouldDraw == 0 ) then return end + + if ( !self:IsOn() ) then return end + if ( self:GetEffect() == "none" ) then return end + + local EffectThink = self[ "EffectThink_"..self:GetEffect() ] + if ( EffectThink ) then EffectThink( self ) end +end + + +function ENT:EffectThink_fire() +end + + + +function ENT:EffectDraw_fire() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matFire ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matFire ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_heatwave() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matHeatWave ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + + +function ENT:EffectDraw_color() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_color_random() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( math.random(0,255), math.random(0,255), math.random(0,255), 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( math.random(0,255), math.random(0,255), math.random(0,255), 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( math.random(0,255), math.random(0,255), math.random(0,255), 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( math.random(0,255), math.random(0,255), math.random(0,255), 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( math.random(0,255), math.random(0,255), math.random(0,255), 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_color_diy() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + local r,g,b,a = self.Entity:GetColor(); + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( r, g, g, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( r, g, b, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( r, g, g, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( r, g, g, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_plasma() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -20 + + render.SetMaterial( matPlasma ) + + scroll = scroll * 0.9 + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 16, scroll, Color( 0, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 8, 16, scroll + 0.01, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 64, 16, scroll + 0.02, Color( 0, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.9 + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 16, scroll, Color( 0, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 8, 16, scroll + 0.01, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 64, 16, scroll + 0.02, Color( 0, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.9 + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 16, scroll, Color( 0, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 8, 16, scroll + 0.01, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 64, 16, scroll + 0.02, Color( 0, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_fire_smoke() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matFire ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matFire ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 8, 16 ) ) + particle:SetEndSize( math.Rand( 32, 64 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( 200, 200, 210 ) + + emitter:Finish() + +end + +function ENT:EffectDraw_fire_smoke_big() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 10 ) + effectdata:SetScale( 6 ) + util.Effect( "HelicopterMegaBomb", effectdata ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 20 ) ) + particle:SetDieTime( 5.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 64, 128 ) ) + particle:SetEndSize( math.Rand( 256, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( 200, 200, 210 ) + + emitter:Finish() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "ThumperDust ", effectdata ) + +end + + +function ENT:EffectThink_smoke() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( 200, 200, 210 ) + + emitter:Finish() + +end + +function ENT:EffectThink_smoke_firecolors() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor(math.random(220,255),math.random(110,220),0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_smoke_random() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( math.random(100,255),math.random(100,255),math.random(100,255) ) + + emitter:Finish() + +end + +function ENT:EffectThink_smoke_diy() +local r,g,b,a = self.Entity:GetColor(); + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( r,g,b) + + emitter:Finish() + +end + +function ENT:EffectDraw_color_magic() + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_money() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 20 + + local particle = emitter:Add( "thrusteraddon/money"..math.floor(math.random(1,3)).."", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 70 ) ) + particle:SetDieTime( math.Rand(3,5 ) ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_debug_10() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "decals/cross", vOffset ) + particle:SetVelocity( vNormal * 0 ) + particle:SetDieTime( 10 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(0,255,0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll(0) + + emitter:Finish() + +end + +function ENT:EffectThink_debug_30() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "decals/cross", vOffset ) + particle:SetVelocity( vNormal * 0 ) + particle:SetDieTime( 30 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(0,255,0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll(0) + + emitter:Finish() + +end + +function ENT:EffectThink_debug_60() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local particle = emitter:Add( "decals/cross", vOffset ) + particle:SetVelocity( vNormal * 0 ) + particle:SetDieTime( 60 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(0,255,0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll(0) + + emitter:Finish() + +end + +function ENT:EffectThink_souls() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 20 + + local particle = emitter:Add( "sprites/soul", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 50 ) ) + particle:SetDieTime( math.Rand(3,5 ) ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 0 ) + particle:SetColor(255,255,255 ) + particle:SetStartSize( 0 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_sperm() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "thrusteraddon/sperm", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 70 ) ) + particle:SetDieTime( math.Rand(3,5 ) ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 200 ) + particle:SetStartSize( 10 ) + particle:SetEndSize( 1 ) + particle:SetRoll( math.random(-180, 180) ) + + local particle2 = emitter:Add( "thrusteraddon/goo", vOffset ) + particle2:SetVelocity( vNormal * 0.5 ) + particle2:SetDieTime( math.Rand(3,5 ) ) + particle2:SetStartAlpha( 100 ) + particle2:SetEndAlpha( 5 ) + particle2:SetColor(255,255,255 ) + particle2:SetStartSize( 5 ) + particle2:SetEndSize( 1 ) + particle2:SetRoll( math.random(-180, 180) ) + + local particle3 = emitter:Add( "thrusteraddon/goo2", vOffset ) + particle3:SetVelocity( vNormal * 0.5 ) + particle3:SetDieTime( math.Rand(3,5 ) ) + particle3:SetStartAlpha(100 ) + particle3:SetEndAlpha( 5 ) + particle3:SetColor(255,255,255 ) + particle3:SetStartSize( 5 ) + particle3:SetEndSize( 1 ) + particle3:SetRoll( math.random(-180, 180) ) + + emitter:Finish() + +end + + +function ENT:EffectThink_feather() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 30 + + local particle = emitter:Add( "thrusteraddon/feather"..math.floor(math.random(2,4)).."", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 50 ) ) + particle:SetDieTime( math.Rand(5,7 ) ) + particle:SetStartAlpha( 120 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_goldstar() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 10 + + local particle = emitter:Add( "thrusteraddon/Goldstar", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 150, 200 ) ) + particle:SetDieTime( math.Rand(0,1 ) ) + particle:SetStartAlpha( 120 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_candy_cane() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "thrusteraddon/candy", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 20 ) ) + particle:SetDieTime( math.Rand(5,7 ) ) + particle:SetStartAlpha( 120 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_jetflame_advanced() + self.Smoking = self.Smoking or false + if self.Entity:GetVelocity():Length() == 0 then + self.Smoking = false + end + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local r,g,b + if self.Entity:GetVelocity():Length() < 1700 then + r = math.Rand(220,255) + g = math.Rand(180,220) + b = 55 + else + r = 55 + g = 55 + b = math.Rand(200,255) + end + + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( r,g,b) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + if self.Entity:GetVelocity():Length() < 1000 then + if not self.Smoking then + local particle4 = emitter:Add( "particles/smokey", vOffset ) + particle4:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle4:SetDieTime( 20.0 ) + particle4:SetStartAlpha( math.Rand( 50, 150 ) ) + particle4:SetEndAlpha( math.Rand( 0, 10 ) ) + particle4:SetStartSize( math.Rand( 512, 1024 ) ) + particle4:SetEndSize( math.Rand( 16,32 ) ) + particle4:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle4:SetColor( 200, 200, 210 ) + end + elseif self.Entity:GetVelocity():Length() > 1000 then + self.Smoking = true + end + + emitter:Finish() + +end +function ENT:EffectThink_jetflame() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( math.Rand(220,255),math.Rand(180,220),55 ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + + + emitter:Finish() + +end + +function ENT:EffectThink_jetflame_purple() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( math.Rand(220,255),55, math.Rand(220,255) ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + + + emitter:Finish() + +end + +function ENT:EffectThink_jetflame_red() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( math.Rand(220,255),55,55 ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + emitter:Finish() + +end + + +function ENT:EffectThink_jetflame_blue() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( 55,55, math.Rand(220,255) ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + + emitter:Finish() + +end + + +function ENT:EffectThink_balls_firecolors() + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.025 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 2 + + local particle = emitter:Add( "sprites/sent_ball", vOffset ) + particle:SetVelocity( vNormal * 80 ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(math.random(220,255),math.random(100,200),0) + particle:SetStartSize( 4 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_balls_random() + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.025 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 2 + + local particle = emitter:Add( "sprites/sent_ball", vOffset ) + particle:SetVelocity( vNormal * 80 ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(math.random(0,255),math.random(0,255),math.random(0,255)) + particle:SetStartSize( 4 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_balls() +local r,g,b,a = self.Entity:GetColor(); + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.025 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 2 + + local particle = emitter:Add( "sprites/sent_ball", vOffset ) + particle:SetVelocity( vNormal * 80 ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(r,g,b) + particle:SetStartSize( 4 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_plasma_rings() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "sprites/magic", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetStartSize( math.Rand( 3,5 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic_firecolors() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(math.random(220,255),math.random(100,200),0) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic_diy() +local r,g,b,a = self.Entity:GetColor(); + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(r,g,b) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic_color() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor( math.random(0,255),math.random(0,255),math.random(0,255)) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + + +function ENT:EffectDraw_rings() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring", effectdata ) + +end + +function ENT:EffectDraw_tesla() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "TeslaZap ", effectdata ) + +end + +function ENT:EffectDraw_blood() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "BloodImpact", effectdata ) + +end + +function ENT:EffectDraw_some_sparks() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "StunstickImpact", effectdata ) + +end + +function ENT:EffectDraw_spark_fountain() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "ManhackSparks", effectdata ) + +end + +function ENT:EffectDraw_more_sparks() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "cball_explode", effectdata ) + +end + +function ENT:EffectDraw_water_small() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "watersplash", effectdata ) + +end + +function ENT:EffectDraw_water_medium() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 5 ) + effectdata:SetScale( 3 ) + + util.Effect( "watersplash", effectdata ) + +end + +function ENT:EffectDraw_water_big() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 10 ) + effectdata:SetScale( 6 ) + util.Effect( "watersplash", effectdata ) + +end + +function ENT:EffectDraw_water_huge() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 50 ) + effectdata:SetScale( 50 ) + util.Effect( "watersplash", effectdata ) + +end + + +function ENT:EffectDraw_striderblood_small() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_striderblood_medium() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 2 ) + effectdata:SetScale( 2 ) + + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_striderblood_big() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 3 ) + effectdata:SetScale( 3 ) + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_striderblood_huge() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 4) + effectdata:SetScale( 4 ) + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_rings_grow() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring_grow", effectdata ) + +end + +function ENT:EffectDraw_rings_grow_rings() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring", effectdata ) + util.Effect( "thruster_ring_grow", effectdata ) + util.Effect( "thruster_ring_grow1", effectdata ) + util.Effect( "thruster_ring_grow2", effectdata ) + util.Effect( "thruster_ring_grow3", effectdata ) + +end + + +function ENT:EffectDraw_rings_shrink() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring_shrink", effectdata ) + +end + + +function ENT:EffectThink_bubble() + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.005 + + local vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 5 + + local particle = emitter:Add( "effects/bubble", vOffset ) + vNormal.x = vNormal.x * 0.7 + vNormal.y = vNormal.y * 0.7 + vNormal.z = (vNormal.z+1) * 20 + particle:SetVelocity( vNormal) + particle:SetDieTime( 2 ) + particle:SetStartAlpha( 125 ) + particle:SetEndAlpha( 125 ) + particle:SetColor(255,255,255) + particle:SetStartSize( 7 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() +end diff --git a/lua/entities/gmod_wire_thruster/init.lua b/lua/entities/gmod_wire_thruster/init.lua new file mode 100644 index 0000000000..82f0df2e93 --- /dev/null +++ b/lua/entities/gmod_wire_thruster/init.lua @@ -0,0 +1,263 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Thruster" + +local Thruster_Sound = Sound( "PhysicsCannister.ThrusterLoop" ) + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Entity:DrawShadow( false ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + local max = self.Entity:OBBMaxs() + local min = self.Entity:OBBMins() + + self.ThrustOffset = Vector( 0, 0, max.z ) + self.ThrustOffsetR = Vector( 0, 0, min.z ) + self.ForceAngle = self.ThrustOffset:GetNormalized() * -1 + + self:SetForce( 2000 ) + + self.OWEffect = "fire" + self.UWEffect = "same" + + self:SetOffset( self.ThrustOffset ) + self.Entity:StartMotionController() + + self:Switch( false ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "A" }) +end + +function ENT:OnRemove() + self.BaseClass.OnRemove(self) + + if (self.EnableSound) then + self.Entity:StopSound(Thruster_Sound) + end +end + +function ENT:SetForce( force, mul ) + if (force) then + self.force = force + self:NetSetForce( force ) + end + mul = mul or 1 + + local phys = self.Entity:GetPhysicsObject() + if (!phys:IsValid()) then + Msg("Warning: [gmod_thruster] Physics object isn't valid!\n") + return + end + + // Get the data in worldspace + local ThrusterWorldPos = phys:LocalToWorld( self.ThrustOffset ) + local ThrusterWorldForce = phys:LocalToWorldVector( self.ThrustOffset * -1 ) + + // Calculate the velocity + ThrusterWorldForce = ThrusterWorldForce * self.force * mul * 50 + self.ForceLinear, self.ForceAngle = phys:CalculateVelocityOffset( ThrusterWorldForce, ThrusterWorldPos ); + self.ForceLinear = phys:WorldToLocalVector( self.ForceLinear ) + + if ( mul > 0 ) then + self:SetOffset( self.ThrustOffset ) + else + self:SetOffset( self.ThrustOffsetR ) + end + +-- self.Entity:SetNetworkedVector( 1, self.ForceAngle ) +-- self.Entity:SetNetworkedVector( 2, self.ForceLinear ) +end + +function ENT:Setup(force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound) + self:SetForce(force) + + self.OWEffect = oweffect + self.UWEffect = uweffect + self.ForceMin = force_min + self.ForceMax = force_max + self.BiDir = bidir + self.EnableSound = sound + self.OWater = owater + self.UWater = uwater + + if (not sound) then + self.Entity:StopSound(Thruster_Sound) + end + + --self:SetOverlayText( "Thrust = " .. 0 .. "\nMul: " .. math.Round(force*1000)/1000 ) +end + +function ENT:TriggerInput(iname, value) + if (iname == "A") then + if ( (self.BiDir) and (math.abs(value) > 0.01) and (math.abs(value) > self.ForceMin) ) or ( (value > 0.01) and (value > self.ForceMin) ) then + self:Switch(true, math.min(value, self.ForceMax)) + else + self:Switch(false, 0) + end + end +end + +function ENT:PhysicsSimulate( phys, deltatime ) + if (!self:IsOn()) then return SIM_NOTHING end + + if (self.Entity:WaterLevel() > 0) then + if (not self.UWater) then + self:SetEffect("none") + return SIM_NOTHING + end + + if (self.UWEffect == "same") then + self:SetEffect(self.OWEffect) + else + self:SetEffect(self.UWEffect) + end + else + if (not self.OWater) then + self:SetEffect("none") + return SIM_NOTHING + end + + self:SetEffect(self.OWEffect) + end + + local ForceAngle, ForceLinear = self.ForceAngle, self.ForceLinear + + return ForceAngle, ForceLinear, SIM_LOCAL_ACCELERATION +end + +function ENT:Switch( on, mul ) + if (!self.Entity:IsValid()) then return false end + + local changed = (self:IsOn() ~= on) + self:SetOn( on ) + + + if (on) then + if (changed) and (self.EnableSound) then + self.Entity:StopSound( Thruster_Sound ) + self.Entity:EmitSound( Thruster_Sound ) + end + + self:NetSetMul( mul ) + + /*if (mul ~= self.PrevOutput) then + self:SetOverlayText( "Thrust = " .. math.Round(self.force*mul*1000)/1000 .. "\nMul: " .. math.Round(self.force*1000)/1000 ) + self.PrevOutput = mul + end*/ + + self:SetForce( nil, mul ) + else + if (self.EnableSound) then + self.Entity:StopSound( Thruster_Sound ) + end + + /*if (self.PrevOutput) then + self:SetOverlayText( "Thrust = Off".."\nMul: "..math.Round(self.force*1000)/1000 ) + self.PrevOutput = nil + end*/ + end + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + return true +end + +function ENT:OnRestore() + local phys = self.Entity:GetPhysicsObject() + + if (phys:IsValid()) then + phys:Wake() + end + + local max = self.Entity:OBBMaxs() + local min = self.Entity:OBBMins() + + self.ThrustOffset = Vector( 0, 0, max.z ) + self.ThrustOffsetR = Vector( 0, 0, min.z ) + self.ForceAngle = self.ThrustOffset:GetNormalized() * -1 + + self:SetOffset( self.ThrustOffset ) + self.Entity:StartMotionController() + + if (self.PrevOutput) then + self:Switch(true, self.PrevOutput) + else + self:Switch(false) + end + + self.BaseClass.OnRestore(self) +end + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.PrevOutput) and (self:IsOn()) then + info.PrevOutput = self.PrevOutput + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.PrevOutput) then + self:Switch(true, info.PrevOutput) + end + +end + + +function MakeWireThruster( pl, Pos, Ang, model, force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, nocollide ) + if not pl:CheckLimit( "wire_thrusters" ) then return false end + + local wire_thruster = ents.Create( "gmod_wire_thruster" ) + if not wire_thruster:IsValid() then return false end + wire_thruster:SetModel( model ) + + wire_thruster:SetAngles( Ang ) + wire_thruster:SetPos( Pos ) + wire_thruster:Spawn() + + wire_thruster:Setup(force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound) + wire_thruster:SetPlayer( pl ) + + if nocollide == true then wire_thruster:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + force = force, + force_min = force_min, + force_max = force_max, + bidir = bidir, + sound = sound, + pl = pl, + oweffect = oweffect, + uweffect = uweffect, + owater = owater, + uwater = uwater, + nocollide = nocollide + } + table.Merge(wire_thruster:GetTable(), ttable ) + + pl:AddCount( "wire_thrusters", wire_thruster ) + + return wire_thruster +end + +duplicator.RegisterEntityClass("gmod_wire_thruster", MakeWireThruster, "Pos", "Ang", "Model", "force", "force_min", "force_max", "oweffect", "uweffect", "owater", "uwater", "bidir", "sound", "nocollide") + diff --git a/lua/entities/gmod_wire_thruster/shared.lua b/lua/entities/gmod_wire_thruster/shared.lua new file mode 100644 index 0000000000..13cb830403 --- /dev/null +++ b/lua/entities/gmod_wire_thruster/shared.lua @@ -0,0 +1,88 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Thruster" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + + +function ENT:NetSetForce( force ) + self.Entity:SetNetworkedInt(4, math.floor(force*100)) +end +function ENT:NetGetForce() + return self.Entity:GetNetworkedInt(4)/100 +end + + +local Limit = .1 +local LastTime = 0 +local LastTimeA = 0 +function ENT:NetSetMul( mul ) + --self.Entity:SetNetworkedBeamInt(5, math.floor(mul*100)) + if (CurTime() < LastTimeA + .05) then + LastTimeA = CurTime() + return + end + LastTimeA = CurTime() + + if (CurTime() > LastTime + Limit) then + self.Entity:SetNetworkedInt(5, math.floor(mul*100)) + LastTime = CurTime() + end +end +function ENT:NetGetMul() + --return self.Entity:GetNetworkedBeamInt(5)/100 + return self.Entity:GetNetworkedInt(5)/100 +end + + +function ENT:GetOverlayText() + local force = self:NetGetForce() + local txt = "Thrust = " + if (self:IsOn()) then + txt = txt .. ( force * self:NetGetMul() ) + else + txt = txt .. "off" + end + txt = txt .. "\nMul: " ..force + local PlayerName = self:GetPlayerName() + if (not SinglePlayer()) then + txt = txt .. "\n(" .. PlayerName .. ")" + end + if(PlayerName and PlayerName ~= "") then + if (txt == "") then + return "- "..PlayerName.." -" + end + return "- "..PlayerName.." -\n"..txt + end + return txt +end diff --git a/lua/entities/gmod_wire_trail/cl_init.lua b/lua/entities/gmod_wire_trail/cl_init.lua new file mode 100644 index 0000000000..93e87c68d3 --- /dev/null +++ b/lua/entities/gmod_wire_trail/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Think() +end diff --git a/lua/entities/gmod_wire_trail/init.lua b/lua/entities/gmod_wire_trail/init.lua new file mode 100644 index 0000000000..c04b1c5ea4 --- /dev/null +++ b/lua/entities/gmod_wire_trail/init.lua @@ -0,0 +1,121 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Trail" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, {"Set", "Length","StartSize","EndSize","R","G","B","A"}) + self.Outputs = Wire_CreateOutputs(self.Entity, {}) + self.R = 0 + self.G = 0 + self.B = 0 + self.A = 0 + self.Length = 0 + self.StartSize = 0 + self.EndSize = 0 + self.Material = "" +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Setup(Material) + self.Material = Material +end + +function ENT:SetTrails( Player, Entity, Data ) + + if ( Entity.SToolTrail ) then + + Entity.SToolTrail:Remove() + Entity.SToolTrail = nil + + end + + if ( Data.StartSize == 0 ) then + + Data.StartSize = 0.0001; + + end + + local trail_entity = util.SpriteTrail( Entity, //Entity + 0, //iAttachmentID + Data.Color, //Color + false, // bAdditive + Data.StartSize, //fStartWidth + Data.EndSize, //fEndWidth + Data.Length, //fLifetime + 1 / ((Data.StartSize+Data.EndSize) * 0.5), //fTextureRes + Data.Material .. ".vmt" ) //strTexture + + Entity.SToolTrail = trail_entity +end + +function ENT:TriggerInput(iname, value) + if (iname == "Set") then + if (value ~= 0) then + self:SetTrails( self:GetOwner(), self.Entity, { + Color = Color( self.R, self.G, self.B, self.A ), + Length = self.Length, + StartSize = self.StartSize, + EndSize = self.EndSize, + Material = self.Material + }) + end + elseif(iname == "Length")then + self.Length = value + elseif(iname == "StartSize")then + self.StartSize = value + elseif(iname == "EndSize")then + self.EndSize = value + elseif(iname == "R")then + self.R = value + elseif(iname == "G")then + self.G = value + elseif(iname == "B")then + self.B = value + elseif(iname == "A")then + self.A = value + end +end + +function ENT:ShowOutput() + local text = "Trail" + self:SetOverlayText( text ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + + +function MakeWireTrail( pl, Pos, Ang, model, mat) + if not pl:CheckLimit( "wire_trails" ) then return false end + + local wire_trail = ents.Create( "gmod_wire_trail" ) + if not wire_trail:IsValid() then return false end + + wire_trail:SetAngles( Ang ) + wire_trail:SetPos( Pos ) + wire_trail:SetModel( Model(model or "models/jaanus/wiretool/wiretool_range.mdl") ) + wire_trail:Spawn() + wire_trail:Setup(mat) + + wire_trail:SetPlayer( pl ) + wire_trail.pl = pl + wire_trail.mat = mat + + pl:AddCount( "wire_trails", wire_trail ) + + return wire_trail +end + +duplicator.RegisterEntityClass("gmod_wire_trail", MakeWireTrail, "Pos", "Ang", "Model", "mat") + diff --git a/lua/entities/gmod_wire_trail/shared.lua b/lua/entities/gmod_wire_trail/shared.lua new file mode 100644 index 0000000000..486e81f73d --- /dev/null +++ b/lua/entities/gmod_wire_trail/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Trail" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamRange(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamRange() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_turret/cl_init.lua b/lua/entities/gmod_wire_turret/cl_init.lua new file mode 100644 index 0000000000..cb76243171 --- /dev/null +++ b/lua/entities/gmod_wire_turret/cl_init.lua @@ -0,0 +1,11 @@ +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_OPAQUE + +--[[--------------------------------------------------------- + Overridden because I want to show the name of the + player that spawned it. +---------------------------------------------------------]] +function ENT:GetOverlayText() + return self:GetPlayerName() +end diff --git a/lua/entities/gmod_wire_turret/init.lua b/lua/entities/gmod_wire_turret/init.lua new file mode 100644 index 0000000000..46f91cd7f2 --- /dev/null +++ b/lua/entities/gmod_wire_turret/init.lua @@ -0,0 +1,246 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +--[[--------------------------------------------------------- + Name: Initialize +---------------------------------------------------------]] +function ENT:Initialize() + + //self.Entity:SetModel( "models/weapons/w_smg1.mdl" ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Entity:DrawShadow( false ) + self.Entity:SetCollisionGroup( COLLISION_GROUP_WEAPON ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + self.Firing = false + self.NextShot = 0 + + self.Inputs = Wire_CreateInputs(self.Entity, { "Fire" }) + +end + +-- Damage +function ENT:SetDamage( f ) + self.Damage = f +end +function ENT:GetDamage() + return self.Damage +end + +-- Delay +function ENT:SetDelay( f ) + self.Delay = f +end +function ENT:GetDelay() + return self.Delay +end + +-- Force +function ENT:SetForce( f ) + self.Force = f +end +function ENT:GetForce() + return self.Force +end + +-- Number of Bullets +function ENT:SetNumBullets( f ) + self.NumBullets = f +end +function ENT:GetNumBullets( f ) + return self.NumBullets +end + +-- Spread +function ENT:SetSpread( f ) + self.Spread = Vector( f, f, 0 ) +end +function ENT:GetSpread() + return self.Spread +end + +-- Sound +function ENT:SetSound( str ) + self.Sound = str +end +function ENT:GetSound() + return self.Sound +end + +-- Tracer +function ENT:SetTracer( trcer ) + self.Tracer = trcer +end +function ENT:GetTracer() + return self.Tracer +end + +-- TracerNum +function ENT:SetTracerNum( trcernum ) + self.TracerNum = trcernum or 1 +end +function ENT:GetTracerNum() + return self.TracerNum +end + + +function ENT:FireShot() + + if ( self.NextShot > CurTime() ) then return end + + self.NextShot = CurTime() + self.Delay + + -- Make a sound if you want to. + if ( self:GetSound() ) then + self.Entity:EmitSound( self:GetSound() ) + end + + -- Get the muzzle attachment (this is pretty much always 1) + local Attachment = self.Entity:GetAttachment( 1 ) + + -- Get the shot angles and stuff. + local shootOrigin = Attachment.Pos + local shootAngles = self.Entity:GetAngles() + local shootDir = shootAngles:Forward() + + -- Shoot a bullet + local bullet = {} + bullet.Num = self:GetNumBullets() + bullet.Src = shootOrigin + bullet.Dir = shootDir + bullet.Spread = self:GetSpread() + bullet.Tracer = self:GetTracerNum() + bullet.TracerName = self:GetTracer() + bullet.Force = self:GetForce() + bullet.Damage = self:GetDamage() + bullet.Attacker = self:GetPlayer() + self.Entity:FireBullets( bullet ) + + -- Make a muzzle flash + local effectdata = EffectData() + effectdata:SetOrigin( shootOrigin ) + effectdata:SetAngle( shootAngles ) + effectdata:SetScale( 1 ) + util.Effect( "MuzzleEffect", effectdata ) + +end + +--[[--------------------------------------------------------- + Name: OnTakeDamage +---------------------------------------------------------]] +function ENT:OnTakeDamage( dmginfo ) + self.Entity:TakePhysicsDamage( dmginfo ) +end + +function ENT:Think() + self.BaseClass.Think(self) + + if( self.Firing ) then + self:FireShot() + end + + self.Entity:NextThink(CurTime()) + return true +end + +--[[--------------------------------------------------------- + Name: TriggerInput + Desc: the inputs +---------------------------------------------------------]] +function ENT:TriggerInput(iname, value) + if (iname == "Fire") then + self.Firing = value > 0 + end +end + + +function MakeWireTurret( ply, Pos, Ang, model, delay, damage, force, sound, numbullets, spread, tracer, tracernum, nocollide ) + + if not ply:CheckLimit( "wire_turrets" ) then return nil end + + local turret = ents.Create( "gmod_wire_turret" ) + if not turret:IsValid() then return false end + + turret:SetPos( Pos ) + if Ang then turret:SetAngles( Ang ) end + + function CorrectModel() + local TurretModels = { + "models/weapons/w_smg1.mdl", + "models/weapons/w_smg_mp5.mdl", + "models/weapons/w_smg_mac10.mdl", + "models/weapons/w_rif_m4a1.mdl", + "models/weapons/w_357.mdl", + "models/weapons/w_shot_m3super90.mdl" + } + local found = false + for k, v in pairs(TurretModels) do + if model == TurretModels[k] then found = true end + end + return found + end + + if CorrectModel() then + turret:SetModel( model ) + else + turret:SetModel( "models/weapons/w_smg1.mdl" ) + end + turret:Spawn() + + -- Clamp stuff in multiplayer.. because people are idiots + if not SinglePlayer() then + delay = math.Clamp( delay, 0.05, 3600 ) + numbullets = 1 + force = math.Clamp( force, 0.01, 100 ) + spread = math.Clamp( spread, 0, 1 ) + damage = math.Clamp( damage, 0, 500 ) + tracernum = 1 + end + + turret:SetDamage( damage ) + turret:SetPlayer( ply ) + + turret:SetSpread( spread ) + turret:SetForce( force ) + turret:SetSound( sound ) + turret:SetTracer( tracer ) + turret:SetTracerNum( tracernum or 1 ) + + turret:SetNumBullets( numbullets ) + + turret:SetDelay( delay ) + + if nocollide == true then turret:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + delay = delay, + damage = damage, + pl = ply, + nocollide = nocollide, + force = force, + sound = sound, + spread = spread, + numbullets = numbullets, + tracer = tracer + } + table.Merge( turret:GetTable(), ttable ) + + ply:AddCount( "wire_turrets", turret ) + ply:AddCleanup( "wire_turrets", turret ) + + return turret +end + +duplicator.RegisterEntityClass( "gmod_wire_turret", MakeWireTurret, "Pos", "Ang", "Model", "delay", "damage", "force", "sound", "numbullets", "spread", "tracer", "tracernum", "nocollide" ) + + diff --git a/lua/entities/gmod_wire_turret/shared.lua b/lua/entities/gmod_wire_turret/shared.lua new file mode 100644 index 0000000000..fb5342e8e3 --- /dev/null +++ b/lua/entities/gmod_wire_turret/shared.lua @@ -0,0 +1,9 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Turret" +ENT.Author = "" +ENT.Contact = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_twoway_radio/cl_init.lua b/lua/entities/gmod_wire_twoway_radio/cl_init.lua new file mode 100644 index 0000000000..9dca17e409 --- /dev/null +++ b/lua/entities/gmod_wire_twoway_radio/cl_init.lua @@ -0,0 +1,6 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_twoway_radio/init.lua b/lua/entities/gmod_wire_twoway_radio/init.lua new file mode 100644 index 0000000000..fbde99fa41 --- /dev/null +++ b/lua/entities/gmod_wire_twoway_radio/init.lua @@ -0,0 +1,151 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "2W Radio" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "A", "B", "C", "D" }) + self.Outputs = Wire_CreateOutputs(self.Entity, { "A", "B", "C", "D" }) + + self.PairID = nil + self.Other = nil +end + +function ENT:Setup( channel ) + self.Channel = channel + self.PrevOutputA = nil + self.PrevOutputB = nil + self.PrevOutputC = nil + self.PrevOutputD = nil + + self:ShowOutput("update", 1) + Wire_TriggerOutput(self.Entity, "A", self.Outputs.A.Value or 0) + Wire_TriggerOutput(self.Entity, "B", self.Outputs.B.Value or 0) + Wire_TriggerOutput(self.Entity, "C", self.Outputs.C.Value or 0) + Wire_TriggerOutput(self.Entity, "D", self.Outputs.D.Value or 0) +end + +function ENT:TriggerInput(iname, value) + if (self.Other) and (self.Other:IsValid()) then + self.Other:ReceiveRadio(iname, value) + self:ShowOutput("update", 1) + end +end + +function ENT:Think() + self.BaseClass.Think(self) + + if (not self.Other) or (not self.Other:IsValid()) then + self.Other = nil + self.PairID = nil + end +end + +function ENT:ReceiveRadio(iname, value) + if (iname == "A") and (self.Other) and (self.Other:IsValid()) then + Wire_TriggerOutput(self.Entity, "A", value) + elseif (iname == "B") and (self.Other) and (self.Other:IsValid()) then + Wire_TriggerOutput(self.Entity, "B", value) + elseif (iname == "C") and (self.Other) and (self.Other:IsValid()) then + Wire_TriggerOutput(self.Entity, "C", value) + elseif (iname == "D") and (self.Other) and (self.Other:IsValid()) then + Wire_TriggerOutput(self.Entity, "D", value) + end + self:ShowOutput(iname, value) +end + +function ENT:RadioLink(other, id) + self.Other = other + self.PairID = id + self.PeerID = id + + self:TriggerInput("A", self.Inputs.A.Value or 0) + self:TriggerInput("B", self.Inputs.B.Value or 0) + self:TriggerInput("C", self.Inputs.C.Value or 0) + self:TriggerInput("D", self.Inputs.D.Value or 0) + self:ShowOutput("update", 1) +end + + +function ENT:ShowOutput(iname, value) + local changed + if (iname == "A") then + if (A ~= self.PrevOutputA) then + self.PrevOutputA = (value or 0) + changed = 1 + end + elseif (iname == "B") then + if (B ~= self.PrevOutputB) then + self.PrevOutputB = (value or 0) + changed = 1 + end + elseif (iname == "C") then + if (C ~= self.PrevOutputC) then + self.PrevOutputC = (value or 0) + changed = 1 + end + elseif (iname == "D") then + if (D ~= self.PrevOutputD) then + self.PrevOutputD = (value or 0) + changed = 1 + end + elseif (iname == "update") then + changed = 1 + end + if (changed) then + if self.PairID == nil then + self:SetOverlayText( "(Not Paired) Transmit: 0, 0, 0, 0" ) + else + self:SetOverlayText( "(Pair ID: " .. self.PairID .. ")\nTransmit A: " .. (self.Inputs.A.Value or 0) .. " B: " .. (self.Inputs.B.Value or 0) .. " C: " .. (self.Inputs.C.Value or 0) .. " D: " .. (self.Inputs.D.Value or 0) .. "\nReceive A: " .. (self.Outputs.A.Value or 0) .. " B: " .. (self.Outputs.B.Value or 0) .. " C: " .. (self.Outputs.C.Value or 0) .. " D: " .. (self.Outputs.D.Value or 0) ) + end + + end +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) + + Wire_AdjustInputs(self.Entity, { "A", "B", "C", "D" }) + Wire_AdjustOutputs(self.Entity, { "A", "B", "C", "D" }) +end + +// Dupe info functions added by TheApathetic +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.Other) && (self.Other:IsValid()) then + info.Other = self.Other:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.Other) then + local other = GetEntByID(info.Other) + if (!other) then + other = ents.GetByIndex(info.Other) + end + + local id = 0 + // A new two-way ID is created upon paste to avoid + // interference with current two-way radios + // This works because ApplyDupeInfo is called after + // all entities are already pasted (TheApathetic) + if (other && other:IsValid() && other.PairID) then + id = other.PairID + else + id = Radio_GetTwoWayID() + end + self:RadioLink(other, id) + end +end diff --git a/lua/entities/gmod_wire_twoway_radio/shared.lua b/lua/entities/gmod_wire_twoway_radio/shared.lua new file mode 100644 index 0000000000..6d9c50361b --- /dev/null +++ b/lua/entities/gmod_wire_twoway_radio/shared.lua @@ -0,0 +1,6 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Two-way Radio" +ENT.Author = "" +ENT.Contact = "" diff --git a/lua/entities/gmod_wire_user/cl_init.lua b/lua/entities/gmod_wire_user/cl_init.lua new file mode 100644 index 0000000000..32f8433103 --- /dev/null +++ b/lua/entities/gmod_wire_user/cl_init.lua @@ -0,0 +1,6 @@ +include('shared.lua') + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_DrawTracerBeam( self, 1 ) +end diff --git a/lua/entities/gmod_wire_user/init.lua b/lua/entities/gmod_wire_user/init.lua new file mode 100644 index 0000000000..6ef65c2607 --- /dev/null +++ b/lua/entities/gmod_wire_user/init.lua @@ -0,0 +1,59 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "User" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity, { "Fire"}) + self.Outputs = Wire_CreateOutputs(self.Entity, {}) + self:SetBeamLength(2048) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:Setup(Range) + self:SetBeamLength(Range) + self:ShowOutput() +end + +function ENT:TriggerInput(iname, value) + if (iname == "Fire") then + if (value ~= 0) then + local vStart = self.Entity:GetPos() + local vForward = self.Entity:GetUp() + + local trace = {} + trace.start = vStart + trace.endpos = vStart + (vForward * self:GetBeamLength()) + trace.filter = { self.Entity } + local trace = util.TraceLine( trace ) + + if (!trace.Entity) then return false end + if (!trace.Entity:IsValid() ) then return false end + if (trace.Entity:IsWorld()) then return false end + + if trace.Entity.Use and trace.Entity.GetPlayer then + trace.Entity:Use(trace.Entity:GetPlayer()) + else + trace.Entity:Fire("use","1",0) + end + end + end +end + +function ENT:ShowOutput() + local text = "User" + self:SetOverlayText( text ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end diff --git a/lua/entities/gmod_wire_user/shared.lua b/lua/entities/gmod_wire_user/shared.lua new file mode 100644 index 0000000000..90731a4134 --- /dev/null +++ b/lua/entities/gmod_wire_user/shared.lua @@ -0,0 +1,45 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire User" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end + +function ENT:SetBeamLength(length) + self.Entity:SetNetworkedFloat("BeamLength", length) +end + +function ENT:GetBeamLength() + return self.Entity:GetNetworkedFloat("BeamLength") or 0 +end diff --git a/lua/entities/gmod_wire_value/cl_init.lua b/lua/entities/gmod_wire_value/cl_init.lua new file mode 100644 index 0000000000..61f78df955 --- /dev/null +++ b/lua/entities/gmod_wire_value/cl_init.lua @@ -0,0 +1,7 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_value/init.lua b/lua/entities/gmod_wire_value/init.lua new file mode 100644 index 0000000000..10a7d142c8 --- /dev/null +++ b/lua/entities/gmod_wire_value/init.lua @@ -0,0 +1,130 @@ + +ParseType = {} + +function ParseType.NORMAL(v) + return tonumber(v) or 0 +end + +function ParseType.STRING(v) + return v +end + +local pat = {} + +do + local patstart = "^ *[([]? *" + local patelement = "([^()[%],; /]+)" + local patsep = " *[,; /] *" + local patend = " *[)%]]? *$" + + local cur = patelement + pat[1] = patstart..cur..patend + for i=2,16 do + cur = cur..patsep..patelement + pat[i] = patstart..cur..patend + end +end + +function ParseType.VECTOR2(v) + local x,y = string.match(v, pat[2]) + if not x then return { 0, 0 } end + return { tonumber(x) or 0, tonumber(y) or 0} +end + +function ParseType.VECTOR(v) + local x,y,z = string.match(v, pat[3]) + if not x then return Vector(0, 0, 0) end + return Vector(tonumber(x) or 0, tonumber(y) or 0, tonumber(z) or 0) +end + +function ParseType.VECTOR4(v) + local x,y,z,w = string.match(v, pat[4]) + if not x then return { 0, 0, 0, 0 } end + return { tonumber(x) or 0, tonumber(y) or 0, tonumber(z) or 0, tonumber(w) or 0 } +end + +function ParseType.ANGLE(v) + local p,y,r = string.match(v, pat[3]) + if not p then return Angle(0, 0, 0) end + return Angle(tonumber(p) or 0, tonumber(y) or 0, tonumber(r) or 0) +end + + + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Value" +ENT.OverlayDelay = 0 + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Outputs = Wire_CreateOutputs(self.Entity, { "Out" }) +end + +function ENT:Setup(values) + + local adjoutputs, adjtypes = {}, {} + for k,v in pairs(values) do + local tp,value = string.match(v, "^ *([^: ]+) *:(.*)$") + + if tp then + tp = tp:upper() + v = value + if not ParseType[tp] then + tp = "STRING" + v = "Invalid type \""..tp.."\"." + end + else + tp = "NORMAL" + end + print(k,v,tp) + + values[k] = v + adjoutputs[k] = "Value"..tostring(k) + adjtypes[k] = tp + end + + self.value = values + //this is where storing the values as strings comes in + WireLib.AdjustSpecialOutputs(self.Entity, adjoutputs, adjtypes, values) + + local txt = "" + + for k,v in pairs(values) do + //line break after 4 values + //if (k == 5) or (k == 9) then txt = txt.."\n" end + txt = txt .. k .. ": " .. v + if (k < #values) then txt = txt .. "\n" end + + local tp = adjtypes[k] + v = ParseType[tp](v) + + Wire_TriggerOutput(self.Entity, adjoutputs[k], v) + end + + self:SetOverlayText(txt) + +end + + +function ENT:ReadCell( Address ) + if (Address >= 0) && (Address < table.Count(self.value)) then + return self.value[Address+1] + else + return nil + end +end + +function ENT:WriteCell( Address, value ) + if (Address >= 0) && (Address < table.Count(self.value)) then + return true + else + return false + end +end diff --git a/lua/entities/gmod_wire_value/shared.lua b/lua/entities/gmod_wire_value/shared.lua new file mode 100644 index 0000000000..ac3d379f58 --- /dev/null +++ b/lua/entities/gmod_wire_value/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Constant Value" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_vectorthruster/cl_init.lua b/lua/entities/gmod_wire_vectorthruster/cl_init.lua new file mode 100644 index 0000000000..1ebc5d3e41 --- /dev/null +++ b/lua/entities/gmod_wire_vectorthruster/cl_init.lua @@ -0,0 +1,1673 @@ + +include('shared.lua') + +CreateConVar( "cl_drawthrusterseffects", "1" ) + +local matHeatWave = Material( "sprites/heatwave" ) +local matFire = Material( "effects/fire_cloud1" ) +local matPlasma = Material( "effects/strider_muzzle" ) +local matColor = Material( "effects/bloodstream" ) + + +// Thrusters only really need to be twopass when they're active.. something to think about.. +ENT.RenderGroup = RENDERGROUP_BOTH + +function ENT:Initialize() + self.ShouldDraw = 1 + self.NextSmokeEffect = 0 + + mx, mn = self.Entity:GetRenderBounds() + self.Entity:SetRenderBounds( mn + Vector(0,0,128), mx, 0 ) +end + + +function ENT:Draw() + self.BaseClass.Draw( self ) + + self:DrawTranslucent() +end + +function ENT:DrawTranslucent() + if ( self.ShouldDraw == 0 ) then return end + + if ( !self:IsOn() ) then return end + if ( self:GetEffect() == "none" ) then return end + + local EffectThink = self[ "EffectDraw_"..self:GetEffect() ] + if ( EffectThink ) then EffectThink( self ) end +end + + +function ENT:Think() + self.BaseClass.Think(self) + + self.ShouldDraw = GetConVarNumber( "cl_drawthrusterseffects" ) + + local bDraw = true + + if ( self.ShouldDraw == 0 ) then bDraw = false end + + if ( !self:IsOn() ) then bDraw = false end + if ( self:GetEffect() == "none" ) then bDraw = false end + + if ( !bDraw ) then return end + + local EffectThink = self[ "EffectThink_"..self:GetEffect() ] + if ( EffectThink ) then EffectThink( self ) end +end + + +function ENT:EffectThink_fire() +end + + +function ENT:vOffset() + local mode = self:GetMode() + if (mode == 1) then + return self.Entity:GetPos() + self:GetOffset() + elseif (mode == 2) then + local v =self:GetOffset() + local z = v.z + v.z = 0 + v = self.Entity:LocalToWorld( v ) + v.z = v.z + z + return v + else + return self.Entity:LocalToWorld( self:GetOffset() ) + end + +end + + + +function ENT:EffectDraw_fire() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matFire ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matFire ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_heatwave() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matHeatWave ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + + +function ENT:EffectDraw_color() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_color_random() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( math.random(0,255), math.random(0,255), math.random(0,255), 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( math.random(0,255), math.random(0,255), math.random(0,255), 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( math.random(0,255), math.random(0,255), math.random(0,255), 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( math.random(0,255), math.random(0,255), math.random(0,255), 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( math.random(0,255), math.random(0,255), math.random(0,255), 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_color_diy() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + local r,g,b,a = self.Entity:GetColor(); + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( r, g, g, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( r, g, b, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( r, g, g, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( r, g, g, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_plasma() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -20 + + render.SetMaterial( matPlasma ) + + scroll = scroll * 0.9 + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 16, scroll, Color( 0, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 8, 16, scroll + 0.01, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 64, 16, scroll + 0.02, Color( 0, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.9 + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 16, scroll, Color( 0, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 8, 16, scroll + 0.01, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 64, 16, scroll + 0.02, Color( 0, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.9 + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 16, scroll, Color( 0, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 8, 16, scroll + 0.01, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 64, 16, scroll + 0.02, Color( 0, 255, 255, 0) ) + render.EndBeam() + +end + +function ENT:EffectDraw_fire_smoke() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matFire ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matHeatWave ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matFire ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + vOffset = self:vOffset() + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 8, 16 ) ) + particle:SetEndSize( math.Rand( 32, 64 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( 200, 200, 210 ) + + emitter:Finish() + +end + +function ENT:EffectDraw_fire_smoke_big() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 10 ) + effectdata:SetScale( 6 ) + util.Effect( "HelicopterMegaBomb", effectdata ) + + vOffset = self:vOffset() + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 20 ) ) + particle:SetDieTime( 5.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 64, 128 ) ) + particle:SetEndSize( math.Rand( 256, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( 200, 200, 210 ) + + emitter:Finish() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "ThumperDust ", effectdata ) + +end + + +function ENT:EffectThink_smoke() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self:vOffset() + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( 200, 200, 210 ) + + emitter:Finish() + +end + +function ENT:EffectThink_smoke_firecolors() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self:vOffset() + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor(math.random(220,255),math.random(110,220),0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_smoke_random() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self:vOffset() + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( math.random(100,255),math.random(100,255),math.random(100,255) ) + + emitter:Finish() + +end + +function ENT:EffectThink_smoke_diy() +local r,g,b,a = self.Entity:GetColor(); + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.015 + + local vOffset = self:vOffset() + Vector( math.Rand( -3, 3 ), math.Rand( -3, 3 ), math.Rand( -3, 3 ) ) + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "particles/smokey", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle:SetDieTime( 2.0 ) + particle:SetStartAlpha( math.Rand( 50, 150 ) ) + particle:SetStartSize( math.Rand( 16, 32 ) ) + particle:SetEndSize( math.Rand( 64, 128 ) ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle:SetColor( r,g,b) + + emitter:Finish() + +end + +function ENT:EffectDraw_color_magic() + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local scroll = CurTime() * -10 + + render.SetMaterial( matColor ) + + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 255, 0, 0, 128) ) + render.AddBeam( vOffset + vNormal * 60, 32, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 32, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + scroll = scroll * 0.5 + + render.UpdateRefractTexture() + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 255, 0, 128) ) + render.AddBeam( vOffset + vNormal * 32, 32, scroll + 2, Color( 255, 255, 255, 255) ) + render.AddBeam( vOffset + vNormal * 128, 48, scroll + 5, Color( 0, 0, 0, 0) ) + render.EndBeam() + + + scroll = scroll * 1.3 + render.SetMaterial( matColor ) + render.StartBeam( 3 ) + render.AddBeam( vOffset, 8, scroll, Color( 0, 0, 255, 128) ) + render.AddBeam( vOffset + vNormal * 60, 16, scroll + 1, Color( 255, 255, 255, 128) ) + render.AddBeam( vOffset + vNormal * 148, 16, scroll + 3, Color( 255, 255, 255, 0) ) + render.EndBeam() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_money() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 20 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "thrusteraddon/money"..math.floor(math.random(1,3)).."", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 70 ) ) + particle:SetDieTime( math.Rand(3,5 ) ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_debug_10() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "decals/cross", vOffset ) + particle:SetVelocity( vNormal * 0 ) + particle:SetDieTime( 10 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(0,255,0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll(0) + + emitter:Finish() + +end + +function ENT:EffectThink_debug_30() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "decals/cross", vOffset ) + particle:SetVelocity( vNormal * 0 ) + particle:SetDieTime( 30 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(0,255,0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll(0) + + emitter:Finish() + +end + +function ENT:EffectThink_debug_60() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "decals/cross", vOffset ) + particle:SetVelocity( vNormal * 0 ) + particle:SetDieTime( 60 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(0,255,0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll(0) + + emitter:Finish() + +end + +function ENT:EffectThink_souls() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.05 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 20 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/soul", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 50 ) ) + particle:SetDieTime( math.Rand(3,5 ) ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 0 ) + particle:SetColor(255,255,255 ) + particle:SetStartSize( 0 ) + particle:SetEndSize( math.Rand(7,10) ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_sperm() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "thrusteraddon/sperm", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 70 ) ) + particle:SetDieTime( math.Rand(3,5 ) ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 200 ) + particle:SetStartSize( 10 ) + particle:SetEndSize( 1 ) + particle:SetRoll( math.random(-180, 180) ) + + local particle2 = emitter:Add( "thrusteraddon/goo", vOffset ) + particle2:SetVelocity( vNormal * 0.5 ) + particle2:SetDieTime( math.Rand(3,5 ) ) + particle2:SetStartAlpha( 100 ) + particle2:SetEndAlpha( 5 ) + particle2:SetColor(255,255,255 ) + particle2:SetStartSize( 5 ) + particle2:SetEndSize( 1 ) + particle2:SetRoll( math.random(-180, 180) ) + + local particle3 = emitter:Add( "thrusteraddon/goo2", vOffset ) + particle3:SetVelocity( vNormal * 0.5 ) + particle3:SetDieTime( math.Rand(3,5 ) ) + particle3:SetStartAlpha(100 ) + particle3:SetEndAlpha( 5 ) + particle3:SetColor(255,255,255 ) + particle3:SetStartSize( 5 ) + particle3:SetEndSize( 1 ) + particle3:SetRoll( math.random(-180, 180) ) + + emitter:Finish() + +end + + +function ENT:EffectThink_feather() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 30 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "thrusteraddon/feather"..math.floor(math.random(2,4)).."", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 50 ) ) + particle:SetDieTime( math.Rand(5,7 ) ) + particle:SetStartAlpha( 120 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_goldstar() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 10 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "thrusteraddon/Goldstar", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 150, 200 ) ) + particle:SetDieTime( math.Rand(0,1 ) ) + particle:SetStartAlpha( 120 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_candy_cane() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + math.random(0.005,0.00005) + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "thrusteraddon/candy", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 0, 20 ) ) + particle:SetDieTime( math.Rand(5,7 ) ) + particle:SetStartAlpha( 120 ) + particle:SetEndAlpha( 0 ) + particle:SetStartSize( 5 ) + particle:SetEndSize( 5 ) + particle:SetRoll( math.Rand( -90, 90 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_jetflame_advanced() + self.Smoking = self.Smoking or false + if self.Entity:GetVelocity():Length() == 0 then + self.Smoking = false + end + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local r,g,b + if self.Entity:GetVelocity():Length() < 1700 then + r = math.Rand(220,255) + g = math.Rand(180,220) + b = 55 + else + r = 55 + g = 55 + b = math.Rand(200,255) + end + local emitter = ParticleEmitter( vOffset ) + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( r,g,b) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + if self.Entity:GetVelocity():Length() < 1000 then + if not self.Smoking then + local particle4 = emitter:Add( "particles/smokey", vOffset ) + particle4:SetVelocity( vNormal * math.Rand( 10, 30 ) ) + particle4:SetDieTime( 20.0 ) + particle4:SetStartAlpha( math.Rand( 50, 150 ) ) + particle4:SetEndAlpha( math.Rand( 0, 10 ) ) + particle4:SetStartSize( math.Rand( 512, 1024 ) ) + particle4:SetEndSize( math.Rand( 16,32 ) ) + particle4:SetRoll( math.Rand( -0.2, 0.2 ) ) + particle4:SetColor( 200, 200, 210 ) + end + elseif self.Entity:GetVelocity():Length() > 1000 then + self.Smoking = true + end + + emitter:Finish() + +end +function ENT:EffectThink_jetflame() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( math.Rand(220,255),math.Rand(180,220),55 ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + + + emitter:Finish() + +end + +function ENT:EffectThink_jetflame_purple() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( math.Rand(220,255),55, math.Rand(220,255) ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + + + emitter:Finish() + +end + +function ENT:EffectThink_jetflame_red() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( math.Rand(220,255),55,55 ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + emitter:Finish() + +end + + +function ENT:EffectThink_jetflame_blue() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.0000005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + //vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + local speed = math.Rand(90,252) + local roll = math.Rand(-90,90) + + local particle = emitter:Add( "particle/fire", vOffset ) + particle:SetVelocity( vNormal * speed ) + particle:SetDieTime( 0.3 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 150 ) + particle:SetStartSize( 15.8 ) + particle:SetEndSize( 9 ) + particle:SetColor( 55,55, math.Rand(220,255) ) + particle:SetRoll( roll ) + + local particle3 = emitter:Add( "sprites/heatwave", vOffset ) + particle3:SetVelocity( vNormal * speed ) + particle3:SetDieTime( 0.7 ) + particle3:SetStartAlpha( 255 ) + particle3:SetEndAlpha( 255 ) + particle3:SetStartSize( 16 ) + particle3:SetEndSize( 18 ) + particle3:SetColor( 255,255,255 ) + particle3:SetRoll( roll ) + + vOffset = self.Entity:LocalToWorld( self:GetOffset() ) + + local particle2 = emitter:Add( "particle/fire", vOffset ) + particle2:SetVelocity( vNormal * speed ) + particle2:SetDieTime( 0.2 ) + particle2:SetStartAlpha( 200 ) + particle2:SetEndAlpha( 50 ) + particle2:SetStartSize( 8.8 ) + particle2:SetEndSize( 5 ) + particle2:SetColor( 200,200,200 ) + particle2:SetRoll( roll ) + + + + emitter:Finish() + +end + + +function ENT:EffectThink_balls_firecolors() + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.025 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 2 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/sent_ball", vOffset ) + particle:SetVelocity( vNormal * 80 ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(math.random(220,255),math.random(100,200),0) + particle:SetStartSize( 4 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_balls_random() + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.025 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 2 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/sent_ball", vOffset ) + particle:SetVelocity( vNormal * 80 ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(math.random(0,255),math.random(0,255),math.random(0,255)) + particle:SetStartSize( 4 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_balls() +local r,g,b,a = self.Entity:GetColor(); + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.025 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 2 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/sent_ball", vOffset ) + particle:SetVelocity( vNormal * 80 ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(r,g,b) + particle:SetStartSize( 4 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() + +end + +function ENT:EffectThink_plasma_rings() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/magic", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetStartSize( math.Rand( 3,5 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic_firecolors() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(math.random(220,255),math.random(100,200),0) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic_diy() +local r,g,b,a = self.Entity:GetColor(); + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80 ) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor(r,g,b) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + +function ENT:EffectThink_magic_color() + + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + vOffset = vOffset + VectorRand() * 5 + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "sprites/gmdm_pickups/light", vOffset ) + particle:SetVelocity( vNormal * math.Rand( 50, 80) ) + particle:SetDieTime( 1 ) + particle:SetStartAlpha( 255 ) + particle:SetEndAlpha( 255 ) + particle:SetColor( math.random(0,255),math.random(0,255),math.random(0,255)) + particle:SetStartSize( math.Rand( 1, 3 ) ) + particle:SetEndSize( 0 ) + particle:SetRoll( math.Rand( -0.2, 0.2 ) ) + + emitter:Finish() + +end + + +function ENT:EffectDraw_rings() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring", effectdata ) + +end + +function ENT:EffectDraw_tesla() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "TeslaZap ", effectdata ) + +end + +function ENT:EffectDraw_blood() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "BloodImpact", effectdata ) + +end + +function ENT:EffectDraw_some_sparks() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "StunstickImpact", effectdata ) + +end + +function ENT:EffectDraw_spark_fountain() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "ManhackSparks", effectdata ) + +end + +function ENT:EffectDraw_more_sparks() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "cball_explode", effectdata ) + +end + +function ENT:EffectDraw_water_small() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "watersplash", effectdata ) + +end + +function ENT:EffectDraw_water_medium() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 5 ) + effectdata:SetScale( 3 ) + + util.Effect( "watersplash", effectdata ) + +end + +function ENT:EffectDraw_water_big() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 10 ) + effectdata:SetScale( 6 ) + util.Effect( "watersplash", effectdata ) + +end + +function ENT:EffectDraw_water_huge() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 50 ) + effectdata:SetScale( 50 ) + util.Effect( "watersplash", effectdata ) + +end + + +function ENT:EffectDraw_striderblood_small() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 1 ) + effectdata:SetScale( 1 ) + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_striderblood_medium() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 2 ) + effectdata:SetScale( 2 ) + + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_striderblood_big() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 3 ) + effectdata:SetScale( 3 ) + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_striderblood_huge() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + effectdata:SetRadius( 4) + effectdata:SetScale( 4 ) + util.Effect( "StriderBlood", effectdata ) + +end + +function ENT:EffectDraw_rings_grow() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring_grow", effectdata ) + +end + +function ENT:EffectDraw_rings_grow_rings() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring", effectdata ) + util.Effect( "thruster_ring_grow", effectdata ) + util.Effect( "thruster_ring_grow1", effectdata ) + util.Effect( "thruster_ring_grow2", effectdata ) + util.Effect( "thruster_ring_grow3", effectdata ) + +end + + +function ENT:EffectDraw_rings_shrink() + + self.RingTimer = self.RingTimer or 0 + if ( self.RingTimer > CurTime() ) then return end + self.RingTimer = CurTime() + 0.00005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + + local effectdata = EffectData() + effectdata:SetOrigin( vOffset ) + effectdata:SetNormal( vNormal ) + util.Effect( "thruster_ring_shrink", effectdata ) + +end + + +function ENT:EffectThink_bubble() + self.SmokeTimer = self.SmokeTimer or 0 + if ( self.SmokeTimer > CurTime() ) then return end + + self.SmokeTimer = CurTime() + 0.005 + + local vOffset = self:vOffset() + local vNormal = (vOffset - self.Entity:GetPos()):GetNormalized() + vOffset = vOffset + VectorRand() * 5 + + + local emitter = ParticleEmitter( vOffset ) + + local particle = emitter:Add( "effects/bubble", vOffset ) + vNormal.x = vNormal.x * 0.7 + vNormal.y = vNormal.y * 0.7 + vNormal.z = (vNormal.z+1) * 20 + particle:SetVelocity( vNormal) + particle:SetDieTime( 2 ) + particle:SetStartAlpha( 125 ) + particle:SetEndAlpha( 125 ) + particle:SetColor(255,255,255) + particle:SetStartSize( 7 ) + particle:SetEndSize( 0 ) + particle:SetRoll( 0 ) + + emitter:Finish() +end diff --git a/lua/entities/gmod_wire_vectorthruster/init.lua b/lua/entities/gmod_wire_vectorthruster/init.lua new file mode 100644 index 0000000000..4fd4691baf --- /dev/null +++ b/lua/entities/gmod_wire_vectorthruster/init.lua @@ -0,0 +1,320 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Thruster" + +local Thruster_Sound = Sound( "PhysicsCannister.ThrusterLoop" ) +--local MODEL = Model("models/jaanus/wiretool/wiretool_speed.mdl") + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + --self.Entity:SetModel( MODEL ) + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + local max = self.Entity:OBBMaxs() + local min = self.Entity:OBBMins() + + self.X = 0 + self.Y = 0 + self.Z = 0 + self.Mode = 0 + self.yaw = 0 + self.pitch = 0 + self.mul = 0 + + self.ThrustOffset = Vector( 0, 0, 0 ) + self.ForceAngle = self.ThrustOffset:GetNormalized() * -1 + + self:SetForce( 2000 ) + + self.OWEffect = "fire" + self.UWEffect = "same" + + self:SetOffset( self.ThrustOffset ) + self.Entity:StartMotionController() + + self:Switch( false ) + + self.Inputs = Wire_CreateInputs(self.Entity, { "Mul" }) +end + +function ENT:OnRemove() + self.BaseClass.OnRemove(self) + + if (self.EnableSound) then + self.Entity:StopSound(Thruster_Sound) + end +end + +function ENT:SetForce( force, mul ) + if (force) then + self.force = force + self:NetSetForce( force ) + end + mul = mul or 1 + + local phys = self.Entity:GetPhysicsObject() + if (!phys:IsValid()) then + Msg("Warning: [gmod_thruster] Physics object isn't valid!\n") + return + end + + local ThrusterWorldPos + local ThrusterWorldForce + if (self.Mode == 1) then + ThrusterWorldPos = self.Entity:GetPos() + self.ThrustOffset + ThrusterWorldForce = self.ThrustOffset * -1 + else + ThrusterWorldPos = phys:LocalToWorld( self.ThrustOffset ) + ThrusterWorldForce = phys:LocalToWorldVector( self.ThrustOffset * -1 ) + end + if (self.Mode == 2) then + ThrusterWorldPos.z = ThrusterWorldPos.z + self.Z + ThrusterWorldForce.z = ThrusterWorldForce.z - self.Z + end + + // Calculate the velocity + ThrusterWorldForce = ThrusterWorldForce * self.force * mul * 50 + self.ForceLinear, self.ForceAngle = phys:CalculateVelocityOffset( ThrusterWorldForce, ThrusterWorldPos ); + + self.ForceLinear = phys:WorldToLocalVector( self.ForceLinear ) +end + +/*function ENT:Think() + self.BaseClass.Think(self) + + if (self.Mode > 0 and self:IsOn()) then + self:Switch( self:IsOn(), self.mul ) + + local phys = self.Entity:GetPhysicsObject() + if (!phys:IsValid()) then return end + + local ThrusterWorldPos + local ThrusterWorldForce + if (self.Mode == 1) then + ThrusterWorldPos = self.Entity:GetPos() + self.ThrustOffset + ThrusterWorldForce = (self.ThrustOffset * -1) + elseif (self.Mode == 2) then + ThrusterWorldPos = phys:LocalToWorld( self.ThrustOffset ) + ThrusterWorldForce = phys:LocalToWorldVector( self.ThrustOffset * -1 ) + ThrusterWorldPos.z = ThrusterWorldPos.z + self.Z + ThrusterWorldForce.z = ThrusterWorldForce.z - self.Z + end + + ThrusterWorldForce = ThrusterWorldForce * self.force * mul * 10 + self.ForceLinear, self.ForceAngle = phys:CalculateVelocityOffset( ThrusterWorldForce, ThrusterWorldPos ); + self.ForceLinear = phys:WorldToLocalVector( self.ForceLinear ) + + self.Entity:NextThink(CurTime()+0.04) + return true + end +end*/ + +function ENT:Setup(force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, mode, angleinputs) + self:SetForce(force) + + self.OWEffect = oweffect + self.UWEffect = uweffect + self.ForceMin = force_min + self.ForceMax = force_max + self.BiDir = bidir + self.EnableSound = sound + self.OWater = owater + self.UWater = uwater + + if (not sound) then + self.Entity:StopSound(Thruster_Sound) + end + + Msg("mode = "..mode.."\n") + self.Mode = mode + self:SetMode( self.Mode ) + + if (angleinputs) then + WireLib.AdjustInputs(self.Entity, {"Mul", "Pitch", "Yaw"}) + else + WireLib.AdjustSpecialInputs(self.Entity, {"Mul", "X", "Y", "Z", "Vector"}, { "NORMAL", "NORMAL", "NORMAL", "NORMAL", "VECTOR"}) + end + + --self:ShowOutput( self.mul ) +end + +function ENT:TriggerInput(iname, value) + if (iname == "Mul") then + if (value == 0) or (self.ThrustOffset == Vector(0,0,0)) then + self:Switch(false, math.min(value, self.ForceMax)) + elseif ( (self.BiDir) and (math.abs(value) > 0.01) and (math.abs(value) > self.ForceMin) ) or ( (value > 0.01) and (value > self.ForceMin) ) then + self:Switch(true, math.Clamp(value, -self.ForceMax, self.ForceMax)) + else + self:Switch(false, 0) + end + return + elseif (iname == "X") then + self.X = value + elseif (iname == "Y") then + self.Y = value + elseif (iname == "Z") then + self.Z = value + elseif (iname == "Vector") then + self.X = value.x + self.Y = value.y + self.Z = value.z + elseif (iname == "Yaw") or (iname == "Pitch") then + value = math.rad( value ) + if (iname == "Yaw") then self.yaw = value else self.pitch = value end + self.X = math.cos(self.pitch) * math.cos(self.yaw) + self.Y = math.sin(self.pitch) + self.Z = math.cos(self.pitch) * math.sin(self.yaw) + end + + self.ThrustOffset = Vector( self.X, self.Y, self.Z ):GetNormalized() + self:SetOffset( self.ThrustOffset ) + if (self.Mode == 2) then + self.ThrustOffset = Vector( self.X, self.Y, 0 ):GetNormalized() + end + + if (self.ThrustOffset == Vector(0,0,0)) then self:SetOn( false ) elseif (self.mul != 0) then self:SetOn( true ) end + self:Switch( self:IsOn(), self.mul ) +end + +function ENT:PhysicsSimulate( phys, deltatime ) + if (!self:IsOn()) then return SIM_NOTHING end + if (self.Entity:IsPlayerHolding()) then return SIM_NOTHING end + + if (self.Entity:WaterLevel() > 0) then + if (not self.UWater) then + self:SetEffect("none") + return SIM_NOTHING + end + + if (self.UWEffect == "same") then + self:SetEffect(self.OWEffect) + else + self:SetEffect(self.UWEffect) + end + else + if (not self.OWater) then + self:SetEffect("none") + return SIM_NOTHING + end + + self:SetEffect(self.OWEffect) + end + + if (self.Mode > 0 and self:IsOn()) then + self:Switch( self:IsOn(), self.mul ) + end + + local ForceAngle, ForceLinear = self.ForceAngle, self.ForceLinear + + return ForceAngle, ForceLinear, SIM_LOCAL_ACCELERATION +end + +function ENT:Switch( on, mul ) + if (!self.Entity:IsValid()) then return false end + self.mul = mul + + local changed = (self:IsOn() ~= on) + self:SetOn( on ) + + if (on) then + if (changed) and (self.EnableSound) then + self.Entity:StopSound( Thruster_Sound ) + self.Entity:EmitSound( Thruster_Sound ) + end + + self:NetSetMul( mul ) + + if (mul ~= self.PrevOutput) then + --self:SetOverlayText( "Thrust = " .. math.Round(self.force*mul*1000)/1000 .. "\nMul: " .. math.Round(self.force*1000)/1000 ) + --self:ShowOutput( true ) + self.PrevOutput = mul + end + + self:SetForce( nil, mul ) + else + if (self.EnableSound) then + self.Entity:StopSound( Thruster_Sound ) + end + + if (self.PrevOutput) then + --self:SetOverlayText( "Thrust = Off".."\nMul: "..math.Round(self.force*1000)/1000 ) + --self:ShowOutput( false ) + self.PrevOutput = nil + end + end + + local phys = self.Entity:GetPhysicsObject() + if (phys:IsValid()) then + phys:Wake() + end + + return true +end + +function ENT:ShowOutput( on ) + local mode = "XYZ Local" + if (self.Mode == 1) then + mode = "XYZ World" + elseif (self.Mode == 2) then + mode = "XY Local, Z World" + end + if ( on ) then + self:SetOverlayText( "Thrust = " .. math.Round(self.force*self.mul*1000)/1000 .. "\nMul: " .. math.Round(self.force*1000)/1000 .. "\nMode: "..mode ) + else + self:SetOverlayText( "Thrust = Off".."\nMul: "..math.Round(self.force*1000)/1000 .. "\nMode: "..mode ) + end +end + +function ENT:OnRestore() + local phys = self.Entity:GetPhysicsObject() + + if (phys:IsValid()) then + phys:Wake() + end + + local max = self.Entity:OBBMaxs() + local min = self.Entity:OBBMins() + + self.ThrustOffset = Vector( 0, 0, 1) + self.ForceAngle = self.ThrustOffset:GetNormalized() * -1 + + self:SetOffset( self.ThrustOffset ) + self.Entity:StartMotionController() + + if (self.PrevOutput) then + self:Switch(true, self.PrevOutput) + else + self:Switch(false) + end + + self.BaseClass.OnRestore(self) +end + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.PrevOutput) and (self:IsOn()) then + info.PrevOutput = self.PrevOutput + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.PrevOutput) then + self:Switch(true, info.PrevOutput) + end + +end diff --git a/lua/entities/gmod_wire_vectorthruster/shared.lua b/lua/entities/gmod_wire_vectorthruster/shared.lua new file mode 100644 index 0000000000..6728ecbf6c --- /dev/null +++ b/lua/entities/gmod_wire_vectorthruster/shared.lua @@ -0,0 +1,111 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Vector Thruster" +ENT.Author = "TAD2020" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end +function ENT:GetEffect() + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "vecon", boolon, true ) +end +function ENT:IsOn() + return self.Entity:GetNetworkedBool( "vecon" ) +end + + +/*function ENT:SetToWorld( b ) + self.Entity:SetNetworkedBool( "vecworld", b, true ) +end +function ENT:IsToWorld() + return self.Entity:GetNetworkedBool( "vecworld" ) +end*/ + + +function ENT:SetMode( v ) + self.Entity:SetNetworkedInt( "vecmode", v, true ) +end +function ENT:GetMode() + return self.Entity:GetNetworkedInt( "vecmode" ) +end + + +function ENT:SetOffset( v ) + --self.Entity:SetNetworkedBeamVector( "Offset", v, true ) + self.Entity:SetNetworkedInt( "vecx", v.x * 100, true ) + self.Entity:SetNetworkedInt( "vecy", v.y * 100, true ) + self.Entity:SetNetworkedInt( "vecz", v.z * 100, true ) +end +function ENT:GetOffset() + --return self.Entity:GetNetworkedBeamVector( "Offset" ) + return Vector( + self.Entity:GetNetworkedInt( "vecx" ) / 100, + self.Entity:GetNetworkedInt( "vecy" ) / 100, + self.Entity:GetNetworkedInt( "vecz" ) / 100 + ) +end + + +function ENT:NetSetForce( force ) + self.Entity:SetNetworkedInt("vecforce", math.floor(force*100)) +end +function ENT:NetGetForce() + return self.Entity:GetNetworkedInt("vecforce")/100 +end + + +local Limit = .1 +local LastTime = 0 +function ENT:NetSetMul( mul ) + --self.Entity:SetNetworkedBeamInt("vecmul", math.floor(mul*100)) + if (CurTime() > LastTime + Limit) then + self.Entity:SetNetworkedInt("vecmul", math.floor((mul or 0)*100)) + LastTime = CurTime() + end +end +function ENT:NetGetMul() + --return self.Entity:GetNetworkedBeamInt(5)/100 + return self.Entity:GetNetworkedInt("vecmul")/100 +end + + +function ENT:GetOverlayText() + + local txt = "Thrust = " + local force = self:NetGetForce() + if (self:IsOn()) then + txt = txt .. ( force * self:NetGetMul() ) + else + txt = txt .. "off" + end + txt = txt .. "\nMul: " .. force .. "\nMode: " + + local mode = self:GetMode() + if (self.Mode == 0) then + txt = txt .. "XYZ Local" + elseif (self.Mode == 1) then + txt = txt .. "XYZ World" + elseif (self.Mode == 2) then + txt = txt .. "XY Local, Z World" + end + + if (not SinglePlayer()) then + local PlayerName = self:GetPlayerName() + txt = txt .. "\n(" .. PlayerName .. ")" + end + + return txt +end diff --git a/lua/entities/gmod_wire_vehicle/cl_init.lua b/lua/entities/gmod_wire_vehicle/cl_init.lua new file mode 100644 index 0000000000..9510a8f0b3 --- /dev/null +++ b/lua/entities/gmod_wire_vehicle/cl_init.lua @@ -0,0 +1,11 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_vehicle/init.lua b/lua/entities/gmod_wire_vehicle/init.lua new file mode 100644 index 0000000000..f2bd8fe1e9 --- /dev/null +++ b/lua/entities/gmod_wire_vehicle/init.lua @@ -0,0 +1,85 @@ +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Vehicle Controller" + +local MODEL = Model("models/jaanus/wiretool/wiretool_siren.mdl") + +-- Number of Vehicles (Used for creating an uniqe name) +-- wire_Vehicle_count = 0 + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + -- Create outputs + self.Inputs = Wire_CreateOutputs( self.Entity, { "Throttle", "Steering", "Handbrake", "Engine", "Lock" } ) + self:SetOverlayText( "Vehicle Controller" ) +end + +-- Link to Vehicle +function ENT:Setup( Vehicle ) + self.Vehicle = Vehicle +end + +-- Inputs +function ENT:TriggerInput(iname, value) + -- Check that we have a vehicle and that the vehicle is valid + if not (self.Vehicle and self.Vehicle:IsValid()) then return end + + if (iname == "Throttle") then + self.Vehicle:Fire("throttle", tostring(value), 0) + elseif (iname == "Steering") then + self.Vehicle:Fire("steer", tostring(value), 0) + elseif (iname == "Handbrake") then + if value > 0 then self.Vehicle:Fire("handbrakeon", 1, 0) + else self.Vehicle:Fire("handbrakeoff", 1, 0) end + elseif (iname == "Engine") then + if value > 0 then self.Vehicle:Fire("turnon", 1, 0) + else self.Vehicle:Fire("turnoff", 1, 0) end + elseif (iname == "Lock") then + if value > 0 then self.Vehicle:Fire("lock", 1, 0) + else self.Vehicle:Fire("unlock", 1, 0) end + end +end + +function ENT:ShowOutput(value) + if (value ~= self.PrevOutput) then + self:SetOverlayText( "Vehicle Controller" ) + self.PrevOutput = value + end +end + +function ENT:OnRestore() + self.BaseClass.OnRestore(self) +end + +function ENT:Think() + +end + + +//Duplicator support to save Vehicle link (TAD2020) +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.Vehicle) and (self.Vehicle:IsValid()) then + info.Vehicle = self.Vehicle:EntIndex() + end + + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + + if (info.Vehicle) then + self.Vehicle = GetEntByID(info.Vehicle) + if (!self.Vehicle) then + self.Vehicle = ents.GetByIndex(info.Vehicle) + end + end +end diff --git a/lua/entities/gmod_wire_vehicle/shared.lua b/lua/entities/gmod_wire_vehicle/shared.lua new file mode 100644 index 0000000000..dbec6c5097 --- /dev/null +++ b/lua/entities/gmod_wire_vehicle/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Vehicle Controller" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_watersensor/cl_init.lua b/lua/entities/gmod_wire_watersensor/cl_init.lua new file mode 100644 index 0000000000..48d2275a21 --- /dev/null +++ b/lua/entities/gmod_wire_watersensor/cl_init.lua @@ -0,0 +1,10 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + + +function ENT:Draw() + self.BaseClass.Draw(self) + Wire_Render(self.Entity) +end diff --git a/lua/entities/gmod_wire_watersensor/init.lua b/lua/entities/gmod_wire_watersensor/init.lua new file mode 100644 index 0000000000..13f16ebfab --- /dev/null +++ b/lua/entities/gmod_wire_watersensor/init.lua @@ -0,0 +1,48 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Water_Sensor" + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Outputs = Wire_CreateOutputs(self.Entity, {"Out"}) +end + +function ENT:OnRemove() + Wire_Remove(self.Entity) +end + +function ENT:TriggerInput(iname, value) +end + +function ENT:ShowOutput() + local text = "Water Sensor" + if(self.Outputs["Out"])then + if(self.Outputs["Out"].Value>0)then + text = text .. "\nSubmerged!" + else + text = text.. "\nAbove Water" + end + end + self:SetOverlayText( text ) +end + +function ENT:OnRestore() + Wire_Restored(self.Entity) +end + +function ENT:Think() + self.BaseClass.Think(self) + if(self.Entity:WaterLevel()>0)then + Wire_TriggerOutput(self.Entity,"Out",1) + else + Wire_TriggerOutput(self.Entity,"Out",0) + end + self:ShowOutput() + self.Entity:NextThink(CurTime()+0.125) +end diff --git a/lua/entities/gmod_wire_watersensor/shared.lua b/lua/entities/gmod_wire_watersensor/shared.lua new file mode 100644 index 0000000000..c63bcdc2a8 --- /dev/null +++ b/lua/entities/gmod_wire_watersensor/shared.lua @@ -0,0 +1,37 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Water Sensor" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/gmod_wire_waypoint/cl_init.lua b/lua/entities/gmod_wire_waypoint/cl_init.lua new file mode 100644 index 0000000000..b224cc2c4a --- /dev/null +++ b/lua/entities/gmod_wire_waypoint/cl_init.lua @@ -0,0 +1,20 @@ + +include('shared.lua') + +ENT.Spawnable = false +ENT.AdminSpawnable = false +ENT.RenderGroup = RENDERGROUP_OPAQUE + +function ENT:Draw() + self.BaseClass.Draw(self) + + local nextWP = self:GetNextWaypoint() + if (nextWP) and (nextWP:IsValid()) and (LocalPlayer():GetEyeTrace().Entity == self.Entity) and (EyePos():Distance(self.Entity:GetPos()) < 4096) then + local start = self.Entity:GetPos() + local endpos = nextWP:GetPos() + local scroll = -3*CurTime() + + render.SetMaterial(Material("cable/physbeam")) + render.DrawBeam(start, endpos, 8, scroll, (endpos-start):Length()/10+scroll, Color(255, 255, 255, 192)) + end +end diff --git a/lua/entities/gmod_wire_waypoint/init.lua b/lua/entities/gmod_wire_waypoint/init.lua new file mode 100644 index 0000000000..a99cb231cb --- /dev/null +++ b/lua/entities/gmod_wire_waypoint/init.lua @@ -0,0 +1,33 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Locator" +ENT.OverlayDelay = 0 + +local MODEL = Model( "models/props_lab/powerbox02d.mdl" ) + +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) +end + + +function ENT:Setup(range) + self.Range = range + + self:SetOverlayText("Waypoint Beacon") +end + + +function ENT:GetBeaconPos(sensor) + if ((sensor:GetPos()-self.Entity:GetPos()):Length() < self.Range) then + sensor:SetBeacon(self:GetNextWaypoint()) + end + + return self.Entity:GetPos() +end diff --git a/lua/entities/gmod_wire_waypoint/shared.lua b/lua/entities/gmod_wire_waypoint/shared.lua new file mode 100644 index 0000000000..81f2cf4f6a --- /dev/null +++ b/lua/entities/gmod_wire_waypoint/shared.lua @@ -0,0 +1,15 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Waypoint Beacon" +ENT.Author = "" +ENT.Contact = "" + + +function ENT:SetNextWaypoint(wp) + self.Entity:SetNetworkedEntity("NextWaypoint", wp) +end + +function ENT:GetNextWaypoint() + return self.Entity:GetNetworkedEntity("NextWaypoint") +end diff --git a/lua/entities/gmod_wire_weight/cl_init.lua b/lua/entities/gmod_wire_weight/cl_init.lua new file mode 100644 index 0000000000..53c2cbeebc --- /dev/null +++ b/lua/entities/gmod_wire_weight/cl_init.lua @@ -0,0 +1,5 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH + diff --git a/lua/entities/gmod_wire_weight/init.lua b/lua/entities/gmod_wire_weight/init.lua new file mode 100644 index 0000000000..8623cf3b05 --- /dev/null +++ b/lua/entities/gmod_wire_weight/init.lua @@ -0,0 +1,70 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Weight" +ENT.OverlayDelay = 0.1 + +local MODEL = Model("models/props_interiors/pot01a.mdl") + +function ENT:Initialize() + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Inputs = Wire_CreateInputs(self.Entity,{"Weight"}) + self.Outputs = Wire_CreateOutputs(self.Entity,{"Weight"}) + self:ShowOutput(self.Entity:GetPhysicsObject():GetMass()) +end + +function ENT:TriggerInput(iname,value) + if(value>0)then + value = math.Clamp(value, 0.001, 50000) + local phys = self.Entity:GetPhysicsObject() + if ( phys:IsValid() ) then + phys:SetMass(value) + phys:Wake() + self:ShowOutput(value) + Wire_TriggerOutput(self.Entity,"Weight",value) + end + end + return true +end + +function ENT:Think() + self.BaseClass.Think(self) +end + +function ENT:Setup() +end + +function ENT:ShowOutput(value) + self:SetOverlayText( "Weight: "..tostring(value) ) +end + + +function MakeWireWeight( pl, Pos, Ang, model, frozen ) + if ( !pl:CheckLimit( "wire_weights" ) ) then return false end + + local wire_weight = ents.Create( "gmod_wire_weight" ) + if (!wire_weight:IsValid()) then return false end + + wire_weight:SetAngles( Ang ) + wire_weight:SetPos( Pos ) + wire_weight:SetModel( Model(model or MODEL) ) + wire_weight:Spawn() + + if wire_weight:GetPhysicsObject():IsValid() then + wire_weight:GetPhysicsObject():EnableMotion(!frozen) + end + + wire_weight:SetPlayer( pl ) + wire_weight.pl = pl + + pl:AddCount( "wire_weights", wire_weight ) + pl:AddCleanup( "gmod_wire_weight", wire_weight ) + + return wire_weight +end +duplicator.RegisterEntityClass("gmod_wire_weight", MakeWireWeight, "Pos", "Ang", "Model", "frozen") diff --git a/lua/entities/gmod_wire_weight/shared.lua b/lua/entities/gmod_wire_weight/shared.lua new file mode 100644 index 0000000000..6e75f74a8b --- /dev/null +++ b/lua/entities/gmod_wire_weight/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Weight" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_wheel/cl_init.lua b/lua/entities/gmod_wire_wheel/cl_init.lua new file mode 100644 index 0000000000..f8992835c7 --- /dev/null +++ b/lua/entities/gmod_wire_wheel/cl_init.lua @@ -0,0 +1,6 @@ +ENT.RenderGroup = RENDERGROUP_OPAQUE + +include('shared.lua') + +function ENT:Initialize() +end diff --git a/lua/entities/gmod_wire_wheel/init.lua b/lua/entities/gmod_wire_wheel/init.lua new file mode 100644 index 0000000000..9cf79b3158 --- /dev/null +++ b/lua/entities/gmod_wire_wheel/init.lua @@ -0,0 +1,362 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +include('shared.lua') + +ENT.WireDebugName = "Wheel" +ENT.OverlayDelay = 0 + +--[[--------------------------------------------------------- + Name: Initialize + Desc: First function called. Use to set up your entity +---------------------------------------------------------]] +function ENT:Initialize() + + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + self.Entity:SetUseType( SIMPLE_USE ) + + self:SetToggle( false ) + + self.ToggleState = false + self.BaseTorque = 1 + self.TorqueScale = 1 + self.Breaking = 0 + self.SpeedMod = 0 + self.Go = 0 + + self.Inputs = Wire_CreateInputs(self.Entity, { "A: Go", "B: Break", "C: SpeedMod" }) + +end + +--[[--------------------------------------------------------- + Sets the base torque +---------------------------------------------------------]] +function ENT:SetBaseTorque( base ) + + self.BaseTorque = base + if ( self.BaseTorque == 0 ) then self.BaseTorque = 1 end + self:UpdateOverlayText() + +end + +--[[--------------------------------------------------------- + Sets the base torque +---------------------------------------------------------]] +function ENT:UpdateOverlayText() + + txt = "Torque: " .. math.floor( self.TorqueScale * self.BaseTorque ) .. "\nSpeed: 0\nBreak: " .. self.Breaking .. "\nSpeedMod: " .. math.floor( self.SpeedMod * 100 ) .. "%" + self:SetOverlayText( txt ) + +end + +--[[--------------------------------------------------------- + Sets the axis (world space) +---------------------------------------------------------]] +function ENT:SetAxis( vec ) + + self.Axis = self.Entity:GetPos() + vec * 512 + self.Axis = self.Entity:NearestPoint( self.Axis ) + self.Axis = self.Entity:WorldToLocal( self.Axis ) + +end + + +--[[--------------------------------------------------------- + Name: PhysicsCollide + Desc: Called when physics collides. The table contains + data on the collision +---------------------------------------------------------]] +function ENT:PhysicsCollide( data, physobj ) +end + + +--[[--------------------------------------------------------- + Name: PhysicsUpdate + Desc: Called to update the physics .. or something. +---------------------------------------------------------]] +function ENT:PhysicsUpdate( physobj ) +end + + +--[[--------------------------------------------------------- + Name: KeyValue + Desc: Called when a keyvalue is added to us (usually from the map) +---------------------------------------------------------]] +function ENT:KeyValue( key, value ) +end + + +--[[--------------------------------------------------------- + Name: Think + Desc: Entity's think function. +---------------------------------------------------------]] +function ENT:Think() +end + + +--[[--------------------------------------------------------- + Name: OnTakeDamage + Desc: Entity takes damage +---------------------------------------------------------]] +function ENT:OnTakeDamage( dmginfo ) + + self.Entity:TakePhysicsDamage( dmginfo ) + +end + + +function ENT:SetMotor( Motor ) + self.Motor = Motor +end + +function ENT:GetMotor() + + if (!self.Motor) then + self.Motor = constraint.FindConstraintEntity( self.Entity, "Motor" ) + if (!self.Motor or !self.Motor:IsValid()) then + self.Motor = nil + end + end + + return self.Motor +end + + +function ENT:SetDirection( dir ) + self.Entity:SetNetworkedInt( 1, dir ) + self.Direction = dir +end + +function ENT:SetToggle( bool ) + self.Toggle = bool +end + +function ENT:GetToggle() + return self.Toggle +end + + +function ENT:SetFwd( fwd ) + self.Fwd = fwd +end + +function ENT:SetBck( bck ) + self.Bck = bck +end + +function ENT:SetStop( stop ) + self.Stop = stop +end + + +--[[--------------------------------------------------------- + Forward +---------------------------------------------------------]] +function ENT:Forward( mul ) + + -- Is this key invalid now? If so return false to remove it + if ( !self.Entity:IsValid() ) then return false end + local Motor = self:GetMotor() + if ( Motor and !Motor:IsValid() ) then + Msg("Wheel doesn't have a motor!\n"); + return false + elseif ( !Motor ) then return false + end + + mul = mul or 1 + local mdir = Motor.direction + local Speed = mdir * mul * self.TorqueScale * (1 + self.SpeedMod) + + txt = "Torque: " .. math.floor( self.TorqueScale * self.BaseTorque ) .. "\nSpeed: " .. (mdir * mul * (1 + self.SpeedMod)) .. "\nBreak: " .. self.Breaking .. "\nSpeedMod: " .. math.floor( self.SpeedMod * 100 ) .. "%" + --self.BaseClass.BaseClass.SetOverlayText(self, txt) + self:SetOverlayText(txt) + + Motor:Fire( "Scale", Speed, 0 ) + Motor:GetTable().forcescale = Speed + Motor:Fire( "Activate", "" , 0 ) + + return true + +end + +--[[--------------------------------------------------------- + Reverse +---------------------------------------------------------]] +function ENT:Reverse( ) + return self:Forward( -1 ) +end + + +--[[--------------------------------------------------------- + Name: TriggerInput + Desc: the inputs +---------------------------------------------------------]] +function ENT:TriggerInput(iname, value) + if (iname == "A: Go") then + if ( value == self.Fwd ) then self.Go = 1 + elseif ( value == self.Bck ) then self.Go = -1 + elseif ( value == self.Stop ) then self.Go =0 end + elseif (iname == "B: Break") then + self.Breaking = value + elseif (iname == "C: SpeedMod") then + self.SpeedMod = (value / 100) + end + self:Forward( self.Go ) +end + + + +--[[--------------------------------------------------------- + Name: PhysicsUpdate + Desc: happy fun time breaking function +---------------------------------------------------------]] +function ENT:PhysicsUpdate( physobj ) + local vel = physobj:GetVelocity() + + if (self.Breaking > 0) then -- to prevent badness + if (self.Breaking >= 100) then --100% breaking!!! + vel.x = 0 --full stop! + vel.y = 0 + else + vel.x = vel.x * ((100.0 - self.Breaking)/100.0) + vel.y = vel.y * ((100.0 - self.Breaking)/100.0) + end + end + + physobj:SetVelocity(vel) +end + + + +--[[--------------------------------------------------------- + Todo? Scale Motor:GetTable().direction? +---------------------------------------------------------]] +function ENT:SetTorque( torque ) + + if ( self.BaseTorque == 0 ) then self.BaseTorque = 1 end + + self.TorqueScale = torque / self.BaseTorque + + local Motor = self:GetMotor() + if (!Motor || !Motor:IsValid()) then return end + Motor:Fire( "Scale", Motor:GetTable().direction * Motor:GetTable().forcescale * self.TorqueScale , 0 ) + + txt = "Torque: " .. math.floor( self.TorqueScale * self.BaseTorque ) + --self.BaseClass.BaseClass.SetOverlayText(self, txt) + self:SetOverlayText(txt) +end + +--[[--------------------------------------------------------- + Creates the direction arrows on the wheel +---------------------------------------------------------]] +function ENT:DoDirectionEffect() + + local Motor = self:GetMotor() + if (!Motor || !Motor:IsValid()) then return end + + local effectdata = EffectData() + effectdata:SetOrigin( self.Axis ) + effectdata:SetEntity( self.Entity ) + effectdata:SetScale( Motor.direction ) + util.Effect( "wheel_indicator", effectdata, true, true ) + +end + +--[[--------------------------------------------------------- + Reverse the wheel direction when a player uses the wheel +---------------------------------------------------------]] +function ENT:Use( activator, caller, type, value ) + + local Motor = self:GetMotor() + local Owner = self:GetPlayer() + + if (Motor and (Owner == nil or Owner == activator)) then + + if (Motor:GetTable().direction == 1) then + Motor:GetTable().direction = -1 + else + Motor:GetTable().direction = 1 + end + + Motor:Fire( "Scale", Motor:GetTable().direction * Motor:GetTable().forcescale * self.TorqueScale, 0 ) + self:SetDirection( Motor:GetTable().direction ) + + self:DoDirectionEffect() + + end + +end + + +function MakeWireWheel( pl, Pos, Ang, model, Vel, aVel, frozen, fwd, bck, stop, BaseTorque, direction, axis, Data ) + + if ( !pl:CheckLimit( "wire_wheels" ) ) then return false end + + local wheel = ents.Create( "gmod_wire_wheel" ) + if ( !wheel:IsValid() ) then return end + + wheel:SetModel( model ) + wheel:SetPos( Pos ) + wheel:SetAngles( Ang ) + wheel:Spawn() + + wheel:SetPlayer( pl ) + + duplicator.DoGenericPhysics( wheel, pl, Data ) + + wheel.fwd = fwd + wheel.bck = bck + wheel.stop = stop + + wheel:SetFwd( fwd ) + wheel:SetBck( bck ) + wheel:SetStop( stop ) + + if ( axis ) then + wheel.Axis = axis + end + + if ( direction ) then + wheel:SetDirection( direction ) + end + + wheel:SetBaseTorque( BaseTorque ) + wheel:UpdateOverlayText() + + pl:AddCount( "wire_wheels", wheel ) + + return wheel + +end + +duplicator.RegisterEntityClass( "gmod_wire_wheel", MakeWireWheel, "Pos", "Ang", "Model", "Vel", "aVel", "frozen", "fwd", "bck", "stop", "BaseTorque", "direction", "Axis", "Data" ) + +function ENT:SetWheelBase(Base) + Base:DeleteOnRemove( self ) + self.Base = Base +end + +function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + if ValidEntity(self.Base) then + info.Base = self.Base:EntIndex() + end + return info +end + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID) + local Base + if info.Base then + Base = GetEntByID(info.Base) + if not Base then + local Base = ents.GetByIndex(info.Base) + end + end + if ValidEntity(Base) then + self:SetWheelBase(Base) + end +end diff --git a/lua/entities/gmod_wire_wheel/shared.lua b/lua/entities/gmod_wire_wheel/shared.lua new file mode 100644 index 0000000000..7505491f2a --- /dev/null +++ b/lua/entities/gmod_wire_wheel/shared.lua @@ -0,0 +1,11 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wired Wheel" +ENT.Author = "tad2020" +ENT.Contact = "" +ENT.Purpose = "support wire input" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false diff --git a/lua/entities/gmod_wire_winch_controller/cl_init.lua b/lua/entities/gmod_wire_winch_controller/cl_init.lua new file mode 100644 index 0000000000..31cc889c11 --- /dev/null +++ b/lua/entities/gmod_wire_winch_controller/cl_init.lua @@ -0,0 +1,4 @@ + +include('shared.lua') + +ENT.RenderGroup = RENDERGROUP_BOTH diff --git a/lua/entities/gmod_wire_winch_controller/init.lua b/lua/entities/gmod_wire_winch_controller/init.lua new file mode 100644 index 0000000000..88c84cbb6d --- /dev/null +++ b/lua/entities/gmod_wire_winch_controller/init.lua @@ -0,0 +1,271 @@ + +AddCSLuaFile( "cl_init.lua" ) +AddCSLuaFile( "shared.lua" ) + +ENT.WireDebugName = "Winch" + +include('shared.lua') + +local MODEL = Model("models/jaanus/wiretool/wiretool_siren.mdl") + +DIR_BACKWARD = -1 +DIR_NONE = 0 +DIR_FORWARD = 1 + +TYPE_NORMAL = 0 +TYPE_MUSCLE = 1 + +/*--------------------------------------------------------- + Name: Initialize + Desc: First function called. Use to set up your entity +---------------------------------------------------------*/ +function ENT:Initialize() + self.Entity:SetModel( MODEL ) + self.Entity:PhysicsInit( SOLID_VPHYSICS ) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid( SOLID_VPHYSICS ) + + self.Inputs = Wire_CreateInputs( self.Entity, { "In", "Out", "Length" } ) + self.Outputs = Wire_CreateOutputs(self.Entity, { "Length" }) + self.Trigger = 0 + + self.last_time = CurTime() + self.init_time = CurTime() + self.min_length = self.min_length or 1 + self.type = self.type or TYPE_NORMAL + self.ctime = self.ctime or 0 +end + + +function ENT:Setup() + self.current_length = 0 + self.IsOn = true +end + +/*--------------------------------------------------------- + Name: KeyValue + Desc: Called when a keyvalue is added to us +---------------------------------------------------------*/ +function ENT:KeyValue( key, value ) + if (key == "minlength") then self.min_length = tonumber(value) + elseif (key == "maxlength") then self.max_length = tonumber(value) + elseif (key == "type") then self.type = tonumber(value) + end +end + +/*--------------------------------------------------------- + Name: Think + Desc: Entity's think function. +---------------------------------------------------------*/ +function ENT:Think() + + self.Entity:NextThink( CurTime() + 0.01 ) + local TimeDiff = CurTime() - self.last_time + self.last_time = CurTime() + + if (!self.constraint) then return end + if (!self.direction) then return end + if (self.direction == DIR_NONE) then return end + + local old_length = self.current_length + local current_length = self.current_length + + if (self.type == TYPE_NORMAL) then + + local speed = 0 + local dist = 0 + + if (self.direction == DIR_FORWARD) then + local speed = self.constraint:GetTable().fwd_speed + dist = speed * TimeDiff + elseif (self.direction == DIR_BACKWARD) then + local speed = self.constraint:GetTable().bwd_speed + dist = -speed * TimeDiff + end + + if (dist == 0) then return end + + current_length = current_length + dist + + if ( self.min_length && current_length < self.min_length ) then + + current_length = self.min_length + if (self.toggle) then self.direction = DIR_NONE end + + end + + if (self.max_length) then + + if (current_length > self.max_length) then + + current_length = self.max_length + self.isexpanded = true + if (self.toggle) then self.direction = DIR_NONE end + + else + + self.isexpanded = false + + end + + end + + elseif ( self.type == TYPE_MUSCLE ) then + + local amp = self.constraint:GetTable().amplitude + local per = self.constraint:GetTable().period + + local spos = ( math.sin( (self.ctime * math.pi * per )) + 1 ) * (amp / 2) + + if (spos > amp) then spos = amp end + if (spos < 0) then spos = 0 end + + current_length = self.min_length + spos + + self.ctime = self.ctime + TimeDiff + end + + self.current_length = current_length + + self.constraint:Fire("SetSpringLength", current_length, 0) + if (self.rope) then self.rope:Fire("SetLength", current_length, 0) end + + self:SetOverlayText( "Winch length : " .. current_length ) + Wire_TriggerOutput(self.Entity, "Length", current_length) + +end + + +function ENT:GetWPos( ent, phys, lpos ) + if (ent:EntIndex() == 0) then + return lpos + end + + if (phys) and (phys:IsValid()) then + return phys:LocalToWorld( lpos ) + else + return ent:LocalToWorld( lpos ) + end +end + + +function ENT:SetConstraint( c ) + self.constraint = c + self.direction = DIR_NONE + self.toggle = c:GetTable().toggle + + local p1 = self:GetWPos(c:GetTable().Ent1, c:GetTable().Phys1, c:GetTable().LPos1) + local p2 = self:GetWPos(c:GetTable().Ent2, c:GetTable().Phys2, c:GetTable().LPos2) + local dist = (p1 - p2) + + self.current_length = dist:Length() + + if (self.max_length) then + self.isexpanded = (self.current_length >= self.max_length) + end + + if (self.type == TYPE_MUSCLE) then + local amp = self.constraint:GetTable().amplitude + local per = self.constraint:GetTable().period + local spos = self.current_length - self.min_length + spos = spos / (amp*2) + spos = spos - 1 + spos = math.Clamp(spos, -1, 1) // just in case! + spos = math.asin(spos) + spos = spos / (per * math.pi) + self.ctime = spos + end + + self:ShowOutput( dist:Length() ) + self.constraint:Fire("SetSpringLength", self.current_length, 0) + if self.rope then self.rope:Fire("SetLength", self.current_length, 0) end + + self:SetOverlayText( "Winch length : " .. self.current_length ) + Wire_TriggerOutput(self.Entity, "Length", self.current_length) + +end + + +function ENT:SetRope( r ) + self.rope = r +end + + +function ENT:TriggerInput(iname, value) + if (iname == "In") then + if (value > 0) then + self.direction = -1 + elseif (value < 0) then + self.direction = 1 + else + self.direction = 0 + end + elseif (iname == "Out") then + if (value > 0) then + self.direction = 1 + elseif (value < 0) then + self.direction = -1 + else + self.direction = 0 + end + elseif (iname == "Length") then + self:ShowOutput( math.max(1, value) ) + end +end + + +function ENT:ShowOutput( Length ) + if ( Length ~= self.current_length and self.constraint ) then + self:SetOverlayText( "Winch length : " .. Length ) + Wire_TriggerOutput(self.Entity, "Length", Length) + self.current_length = Length + self.constraint:Fire("SetSpringLength", self.current_length, 0) + if self.rope then self.rope:Fire("SetLength", self.current_length, 0) end + end +end + + +/*function ENT:BuildDupeInfo() + local info = self.BaseClass.BuildDupeInfo(self) or {} + + if (self.constraint) and (self.constraint:IsValid()) then + info.constraint = self.constraint:EntIndex() + end + if (self.rope) and (self.rope:IsValid()) then + info.rope = self.rope:EntIndex() + end + + return info +end*/ + +function ENT:ApplyDupeInfo(ply, ent, info, GetEntByID, GetConstByID) + self.BaseClass.ApplyDupeInfo(self, ply, ent, info, GetEntByID, GetConstByID) + + if (GetConstByID) then + if (info.constraint) and (info.constraint > 0) then + local const = GetConstByID(info.constraint) + if (const) then + self:SetConstraint(const) + end + end + + if (info.rope) and (info.rope > 0) then + local rope = GetConstByTable(info.rope) + if (rope) then + self:SetConstraint(rope) + end + end + end +end + +function ENT:SetDirection( n ) + self.direction = n +end + +function ENT:GetDirection() + return self.direction +end + +function ENT:IsExpanded() + return self.isexpanded +end diff --git a/lua/entities/gmod_wire_winch_controller/shared.lua b/lua/entities/gmod_wire_winch_controller/shared.lua new file mode 100644 index 0000000000..5b1e9bfadf --- /dev/null +++ b/lua/entities/gmod_wire_winch_controller/shared.lua @@ -0,0 +1,37 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Wire Winch Controller" +ENT.Author = "" +ENT.Contact = "" +ENT.Purpose = "" +ENT.Instructions = "" + +ENT.Spawnable = false +ENT.AdminSpawnable = false + + +function ENT:SetEffect( name ) + self.Entity:SetNetworkedString( "Effect", name ) +end + +function ENT:GetEffect( name ) + return self.Entity:GetNetworkedString( "Effect" ) +end + + +function ENT:SetOn( boolon ) + self.Entity:SetNetworkedBool( "On", boolon, true ) +end + +function ENT:IsOn( name ) + return self.Entity:GetNetworkedBool( "On" ) +end + +function ENT:SetOffset( v ) + self.Entity:SetNetworkedVector( "Offset", v, true ) +end + +function ENT:GetOffset( name ) + return self.Entity:GetNetworkedVector( "Offset" ) +end diff --git a/lua/entities/sent_deployableballoons/cl_init.lua b/lua/entities/sent_deployableballoons/cl_init.lua new file mode 100644 index 0000000000..d0070a6374 --- /dev/null +++ b/lua/entities/sent_deployableballoons/cl_init.lua @@ -0,0 +1,5 @@ +include("shared.lua") + +language.Add( "Cleanup_wire_deployers", "Balloon Deployers" ) +language.Add( "Cleaned_wire_deployers", "Cleaned up Balloon Deployers" ) +language.Add( "SBoxLimit_wire_deployers", "You have hit the Balloon Deployers limit!" ) diff --git a/lua/entities/sent_deployableballoons/init.lua b/lua/entities/sent_deployableballoons/init.lua new file mode 100644 index 0000000000..11e66fc2db --- /dev/null +++ b/lua/entities/sent_deployableballoons/init.lua @@ -0,0 +1,163 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + +include("shared.lua") + +ENT.WireDebugName = "Balloon Deployer" + +local material = "cable/rope" + +CreateConVar('sbox_maxwire_deployers', 2) +local function MakeBalloonSpawner(pl, Data) + if not pl:CheckLimit("wire_deployers") then return nil end + + local ent = ents.Create("sent_deployableballoons") + if not ent:IsValid() then return end + duplicator.DoGeneric(ent, Data) + ent:SetPlayer(pl) + ent:Spawn() + ent:Activate() + + duplicator.DoGenericPhysics(ent, pl, Data) + + pl:AddCount("wire_deployers", ent) + pl:AddCleanup("wire_deployers", ent) + return ent +end + +duplicator.RegisterEntityClass("sent_deployableballoons", MakeBalloonSpawner, "Data") + +function ENT:SpawnFunction( ply, tr ) + if (not tr.Hit) then return end + local SpawnPos = tr.HitPos+tr.HitNormal*16 + local ent = MakeBalloonSpawner(ply, {Pos=SpawnPos}) + return ent +end + +function ENT:Initialize() + self.Entity:SetModel("models/props_junk/PropaneCanister001a.mdl") + self.Entity:PhysicsInit(SOLID_VPHYSICS) + self.Entity:SetMoveType( MOVETYPE_VPHYSICS ) + self.Entity:SetSolid(SOLID_VPHYSICS) + self.Deployed = 0 + self.Balloon = nil + self.Constraints = {} + self.force = 500 + self.weld = false + self.popable = false + self.rl = 64 + if WireAddon then + self.Inputs = Wire_CreateInputs(self.Entity,{ "Force", "Lenght", "Weld?", "Popable?", "Deploy" }) + self.Outputs = Wire_CreateOutputs(self.Entity,{ "Deployed" }) + Wire_TriggerOutput(self.Entity,"Deployed", self.Deployed) + --Wire_TriggerOutput(self.Entity,"Force", self.force) + end + local phys = self.Entity:GetPhysicsObject() + if(phys:IsValid()) then + phys:SetMass(250) + phys:Wake() + end + self:UpdateOverlay() +end + +function ENT:TriggerInput(key,value) + if (key == "Deploy") then + if value ~= 0 then + if self.Deployed == 0 then + self:DeployBalloons() + self.Deployed = 1 + end + Wire_TriggerOutput(self.Entity, "Deployed", self.Deployed) + else + if self.Deployed ~= 0 then + self:RetractBalloons() + self.Deployed = 0 + end + Wire_TriggerOutput(self.Entity, "Deployed", self.Deployed) + end + elseif (key == "Force") then + self.force = value + if self.Deployed ~= 0 then + self.Balloon:SetForce(value) + end + elseif (key == "Lenght") then + self.rl = value + elseif (key == "Weld?") then + self.weld = value ~= 0 + elseif (key == "Popable?") then + self.popable = value ~= 0 + end + self:UpdateOverlay() +end + +local balloon_registry = {} + +hook.Add("EntityRemoved", "balloon_deployer", function(ent) + local deployer = balloon_registry[ent] + if deployer then + deployer.Deployed = 0 + deployer:TriggerInput("Deploy", 0) + end +end) + +function ENT:DeployBalloons() + local balloon + if self.popable then + balloon = ents.Create("gmod_balloon") --normal balloon + else + balloon = ents.Create("gmod_iballoon") --invincible balloon + end + balloon:Spawn() + balloon:SetRenderMode( RENDERMODE_TRANSALPHA ) + balloon:SetColor(math.random(0,255), math.random(0,255), math.random(0,255), 255 ) + balloon:SetForce(self.force) + balloon:SetMaterial("models/balloon/balloon") + balloon:SetPlayer(self:GetPlayer()) + duplicator.DoGeneric(balloon,{Pos = self.Entity:GetPos() + (self.Entity:GetUp()*25)}) + duplicator.DoGenericPhysics(balloon,pl,{Pos = Pos}) + local spawnervec = (self.Entity:GetPos()-balloon:GetPos()):Normalize()*250 --just to be sure + local trace = util.QuickTrace(balloon:GetPos(),spawnervec,balloon) + local Pos = self.Entity:GetPos()+(self.Entity:GetUp()*25) + local attachpoint = Pos + Vector(0,0,-10) + local LPos1 = balloon:WorldToLocal(attachpoint) + local LPos2 = trace.Entity:WorldToLocal(trace.HitPos) + local phys = trace.Entity:GetPhysicsObjectNum(trace.PhysicsBone) + if phys and phys:IsValid() then + LPos2 = phys:WorldToLocal(trace.HitPos) + end + if self.weld then + local constraint = constraint.Weld( balloon, trace.Entity, 0, trace.PhysicsBone, 0) + balloon:DeleteOnRemove(constraint) + else + local constraint, rope = constraint.Rope(balloon,trace.Entity,0,trace.PhysicsBone,LPos1,LPos2,0,self.rl,0,1.5,material,nil) + balloon:DeleteOnRemove(constraint) + balloon:DeleteOnRemove(rope) + end + self.Entity:DeleteOnRemove(balloon) + self.Balloon = balloon + + balloon_registry[balloon] = self.Entity +end + +function ENT:OnRemove() + if self.Balloon then + balloon_registry[self.Balloon] = nil + end + Wire_Remove(self.Entity) +end + +function ENT:RetractBalloons() + if self.Balloon:IsValid() then + local effectdata = EffectData() + effectdata:SetOrigin( self.Balloon:GetPos() ) + effectdata:SetStart( Vector(self.Balloon:GetColor()) ) + util.Effect( "balloon_pop", effectdata ) + self.Balloon:Remove() + else + self.Balloon = nil + end +end + +function ENT:UpdateOverlay() + self:SetOverlayText( "Balloon Deployer\nDeployed = " .. ((self.Deployed ~= 0) and "yes" or "no") ) +end diff --git a/lua/entities/sent_deployableballoons/shared.lua b/lua/entities/sent_deployableballoons/shared.lua new file mode 100644 index 0000000000..887df6e9d1 --- /dev/null +++ b/lua/entities/sent_deployableballoons/shared.lua @@ -0,0 +1,14 @@ +ENT.Type = "anim" +ENT.Base = "base_wire_entity" + +ENT.PrintName = "Balloon Deployer" +ENT.Author = "LuaPinapple" +ENT.Contact = "evilpineapple@cox.net" +ENT.Purpose = "Figure it out man." +ENT.Instructions = "Use wire." + +ENT.Spawnable = true +ENT.AdminSpawnable = true + + +cleanup.Register("hoverdrivecontrolers") diff --git a/lua/weapons/RemoteController/shared.lua b/lua/weapons/RemoteController/shared.lua new file mode 100644 index 0000000000..b296a21bc4 --- /dev/null +++ b/lua/weapons/RemoteController/shared.lua @@ -0,0 +1,98 @@ +SWEP.Author = "ShaRose" +SWEP.Contact = "" +SWEP.Purpose = "Remote control for Adv. Pods in wire." +SWEP.Instructions = "Left Click on Adv. Pod to link up, and use to start controlling." + +SWEP.PrintName = "Remote Control" +SWEP.Slot = 0 +SWEP.SlotPos = 4 +SWEP.DrawAmmo = false + +SWEP.Weight = 1 +SWEP.Spawnable = true +SWEP.AdminSpawnable = true + +SWEP.Primary.ClipSize = -1 +SWEP.Primary.DefaultClip = -1 +SWEP.Primary.Automatic = false +SWEP.Primary.Ammo = "none" + +SWEP.Secondary.ClipSize = -1 +SWEP.Secondary.DefaultClip = -1 +SWEP.Secondary.Automatic = false +SWEP.Secondary.Ammo = "none" +SWEP.viewModel = "models/weapons/v_pistol.mdl" +SWEP.worldModel = "models/weapons/w_pistol.mdl" + +function SWEP:PrimaryAttack() + if !self.Owner.Active then + local tracedata = { + start = self.Owner:GetShootPos(), + endpos = self.Owner:GetShootPos()+(self.Owner:GetAimVector()*250), + filter = self.Owner + } + local trace = util.TraceLine(tracedata) + if trace.HitNonWorld and trace.Entity:GetClass() == "gmod_wire_adv_pod" then + if trace.Entity:Link(self.Owner,true) then + self.Owner:PrintMessage(HUD_PRINTTALK,"You are now linked!") + self.Owner.Linked = true + else + self.Owner:PrintMessage(HUD_PRINTTALK,"Link failed!") + end + end + end +end + +function SWEP:Reload() + if !self.Owner.Active then + self.Owner:PrintMessage(HUD_PRINTTALK,"Link reset!") + self.Owner.Linked = false + end +end + +function SWEP:Holster() + self.Owner.Active = false + return true +end + +function SWEP:OnDrop() + self.Owner.Active = false + self.Owner.Linked = false + self.Owner:PrintMessage(HUD_PRINTTALK,"SWEP reset!") +end + +function SWEP:Think() + if CLIENT then return end + if !self.Owner.Linked then return end + if self.Owner:KeyPressed(IN_USE) then + if self.Owner.Active then + self.Owner.Active = false + self.Owner:SetMoveType(2) + self.Owner:DrawViewModel(true) + else + self.Owner.Active = true + self.Owner:SetMoveType(0) + self.Owner:DrawViewModel(false) + end + end +end + +function SWEP:OnRestore() +end + +function SWEP:Precache() +end + +function SWEP:OwnerChanged() +end + +function SWEP:SecondaryAttack() +end + +function SWEP:Initialize() +end + +function SWEP:Deploy() + return true +end + diff --git a/lua/weapons/gmod_tool/stools/wire.lua b/lua/weapons/gmod_tool/stools/wire.lua new file mode 100644 index 0000000000..97dbe03135 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire.lua @@ -0,0 +1,407 @@ +if VERSION < 34 then + TOOL.Mode = "wire" + TOOL.Category = "Wire - Tools" + TOOL.Name = "Wire" + TOOL.Tab = "Wire" + function TOOL.BuildCPanel(panel) + panel:AddControl( "Label", { Text = "Avast!\nYer GMOD be too farrgin' ole ye scurvy sea dog!\nYaaarrr!" }) + end + return +end + +TOOL = nil + +-- Load our other stools first +-- This is a hack to load tools outside of the stools folder and in a many-in-one-file method +include( "wire/stools/tool_loader.lua" ) + +-- wire stool +TOOL = ToolObj:Create() +TOOL.Mode = "wire" +TOOL.Category = "Wire - Tools" +TOOL.Name = "Wire" +TOOL.Tab = "Wire" + +if CLIENT then + language.Add( "Tool_wire_name", "Wiring Tool" ) + language.Add( "Tool_wire_desc", "Used to connect wirable props." ) + language.Add( "Tool_wire_0", "Primary: Attach to selected input.\nSecondary: Next input.\nReload: Unlink selected input." ) + language.Add( "Tool_wire_1", "Primary: Attach to output.\nSecondary: Attach but continue.\nReload: Cancel." ) + language.Add( "Tool_wire_2", "Primary: Confirm attach to output.\nSecondary: Next output.\nReload: Cancel." ) + language.Add( "WireTool_width", "Width:" ) + language.Add( "WireTool_material", "Material:" ) + language.Add( "WireTool_colour", "Material:" ) + language.Add( "undone_wire", "Undone Wire" ) +end + +TOOL.ClientConVar = { + width = 2, + material = "cable/cable2", + color_r = 255, + color_g = 255, + color_b = 255, +} + +TOOL.CurrentComponent = nil +TOOL.CurrentInput = nil +TOOL.Inputs = nil +TOOL.CurrentOutput = nil +TOOL.Outputs = nil + +function TOOL:LeftClick( trace ) + if (trace.Entity:IsValid()) and (trace.Entity:IsPlayer()) then return end + + local stage = self:GetStage() + + if (stage == 0) then + if (CLIENT) then + if (self:GetWeapon():GetNetworkedString("WireCurrentInput")) then + self:SetStage(0) + return true + end + elseif (self.CurrentInput) then + local material = self:GetClientInfo("material") + local width = self:GetClientNumber("width") + local color = Color(self:GetClientNumber("color_r"), self:GetClientNumber("color_g"), self:GetClientNumber("color_b")) + if (Wire_Link_Start(self:GetOwner():UniqueID(), trace.Entity, trace.Entity:WorldToLocal(trace.HitPos), self.CurrentInput, material, color, width)) then + self:SetStage(1) + return true + end + end + + return + elseif (stage == 1) then + if (CLIENT) then + self:SetStage(0) + return true + end + + if (not trace.Entity.Outputs) then + self:SetStage(0) + + Wire_Link_Cancel(self:GetOwner():UniqueID()) + + WireLib.AddNotify(self:GetOwner(), "Wire source invalid!", NOTIFY_GENERIC, 7) + return + end + + self.Outputs = {} + self.OutputsDesc = {} + self.OutputsType = {} + for key,v in pairs(trace.Entity.Outputs) do + if v.Num then + self.Outputs[v.Num] = key + if (v.Desc) then + self.OutputsDesc[key] = v.Desc + end + if (v.Type) then + self.OutputsType[key] = v.Type + end + else + table.insert(self.Outputs, key) + end + end + + local oname = nil + for k,_ in pairs(trace.Entity.Outputs) do + if (oname) then + self:SelectComponent(nil) + self.CurrentOutput = self.Outputs[1] //oname + self.OutputEnt = trace.Entity + self.OutputPos = trace.Entity:WorldToLocal(trace.HitPos) + + local txt = "Output: "..self.CurrentOutput + if (self.OutputsDesc) and (self.OutputsDesc[self.CurrentOutput]) then + txt = txt.." ("..self.OutputsDesc[self.CurrentOutput]..")" + end + if (self.OutputsType) and (self.OutputsType[self.CurrentOutput]) + and (self.OutputsType[self.CurrentOutput] != "NORMAL") then + txt = txt.." ["..self.OutputsType[self.CurrentOutput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + self:SetStage(2) + return true + end + + oname = k + end + + Wire_Link_End(self:GetOwner():UniqueID(), trace.Entity, trace.Entity:WorldToLocal(trace.HitPos), oname, self:GetOwner()) + + self:SelectComponent(nil) + self:SetStage(0) + else + if (CLIENT) then + self:SetStage(0) + return true + end + + Wire_Link_End(self:GetOwner():UniqueID(), self.OutputEnt, self.OutputPos, self.CurrentOutput, self:GetOwner()) + + self:GetWeapon():SetNetworkedString("WireCurrentInput", "") + self.CurrentOutput = nil + self.OutputEnt = nil + self.OutputPos = nil + + self:SelectComponent(nil) + self:SetStage(0) + end + + return true +end + + +function TOOL:RightClick( trace ) + local stage = self:GetStage() + + if (stage < 2) then + if (not trace.Entity:IsValid()) or (trace.Entity:IsPlayer()) then return end + end + + if (stage == 0) then + if (CLIENT) then return end + + if (trace.Entity:IsValid()) then + self:SelectComponent(trace.Entity) + else + self:SelectComponent(nil) + end + if (not self.Inputs) or (not self.CurrentInput) then return end + + local iNextInput + for k,v in pairs(self.Inputs) do + if (v == self.CurrentInput) then iNextInput = k+1 end + end + if (iNextInput) then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if (iNextInput > table.getn(self.Inputs)) then iNextInput = 1 end + + self.CurrentInput = self.Inputs[iNextInput] + if (self.CurrentInput) then self.LastValidInput = self.CurrentInput end + + local txt = "" + if (self.CurrentComponent) and (self.CurrentComponent:IsValid()) and (self.CurrentInput) + and (self.CurrentComponent.Inputs) and (self.CurrentComponent.Inputs[self.CurrentInput]) + and (self.CurrentComponent.Inputs[self.CurrentInput].Src) then + txt = "%"..(self.CurrentInput or "") + else + txt = self.CurrentInput or "" + end + if (self.InputsDesc) and (self.InputsDesc[self.CurrentInput]) then + txt = txt.." ("..self.InputsDesc[self.CurrentInput]..")" + end + if (self.InputsType) and (self.InputsType[self.CurrentInput]) + and (self.InputsType[self.CurrentInput] != "NORMAL") then + txt = txt.." ["..self.InputsType[self.CurrentInput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + + if (self.CurrentComponent) and (self.CurrentComponent:IsValid()) then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", self.CurrentInput) + end + end + elseif (stage == 1) then + if (SERVER) then + Wire_Link_Node(self:GetOwner():UniqueID(), trace.Entity, trace.Entity:WorldToLocal(trace.HitPos+trace.HitNormal)) + end + elseif (self.Outputs) then + if (CLIENT) then return end + + local iNextOutput + for k,v in pairs(self.Outputs) do + if (v == self.CurrentOutput) then iNextOutput = k+1 end + end + + if (iNextOutput) then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if (iNextOutput > table.getn(self.Outputs)) then iNextOutput = 1 end + + self.CurrentOutput = self.Outputs[iNextOutput] or "" --if that's nil then somethis is wrong with the ent + + local txt = "Output: "..self.CurrentOutput + if (self.OutputsDesc) and (self.OutputsDesc[self.CurrentOutput]) then + txt = txt.." ("..self.OutputsDesc[self.CurrentOutput]..")" + end + if (self.OutputsType) and (self.OutputsType[self.CurrentOutput]) + and (self.OutputsType[self.CurrentOutput] != "NORMAL") then + txt = txt.." ["..self.OutputsType[self.CurrentOutput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + end + end +end + + +function TOOL:Reload(trace) + if (not trace.Entity:IsValid()) or (trace.Entity:IsPlayer()) then return false end + if (CLIENT) then return true end + + if (self:GetStage() == 0) then + if (not self.CurrentComponent) or (not self.CurrentComponent:IsValid()) then return end + if (not self.CurrentInput) or (self.CurrentInput == "") then return end + + Wire_Link_Clear(self.CurrentComponent, self.CurrentInput) + + return true + end + + Wire_Link_Cancel(self:GetOwner():UniqueID()) + self:SetStage(0) + + return true +end + +function TOOL:Holster() + self:SelectComponent(nil) +end + + +if (CLIENT) then + + function TOOL:DrawHUD() + local current_input = self:GetWeapon():GetNetworkedString("WireCurrentInput") or "" + if (current_input ~= "") then + if (string.sub(current_input, 1, 1) == "%") then + draw.WordBox(8, ScrW()/2+10, ScrH()/2+10, string.sub(current_input, 2), "Default", Color(150,50,50,192), Color(255,255,255,255) ) + else + draw.WordBox(8, ScrW()/2+10, ScrH()/2+10, current_input, "Default", Color(50,50,75,192), Color(255,255,255,255) ) + end + end + end + +end + + +function TOOL:Think() + if (self:GetStage() == 0) then + local player = self:GetOwner() + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if (trace.Hit) and (trace.Entity:IsValid()) then + self:SelectComponent(trace.Entity) + else + self:SelectComponent(nil) + end + end +end + + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_name", Description = "#Tool_wire_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire", + + Options = { + Default = { + wire_material = "cable/rope", + wire_width = "3", + } + }, + + CVars = { + [0] = "wire_width", + [1] = "wire_material", + } + }) + + panel:AddControl("Slider", { + Label = "#WireTool_width", + Type = "Float", + Min = ".1", + Max = "5", + Command = "wire_width" + }) + + panel:AddControl( "MatSelect", { + Height = "1", + Label = "#WireTool_material", + ItemWidth = 24, + ItemHeight = 64, + ConVar = "wire_material", + Options = list.Get( "WireMaterials" ) + } ) + + panel:AddControl("Color", { + Label = "#WireTool_colour", + Red = "wire_color_r", + Green = "wire_color_g", + Blue = "wire_color_b", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) +end + + +function TOOL:SelectComponent(ent) + if (CLIENT) then return end + + if (self.CurrentComponent == ent) then return end + + if (self.CurrentComponent) and (self.CurrentComponent:IsValid()) then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", "") + end + + self.CurrentComponent = ent + self.CurrentInput = nil + self.Inputs = {} + self.InputsDesc = {} + self.InputsType = {} + + local best = nil + local first = nil + if (ent) and (ent.Inputs) then + for k,v in pairs(ent.Inputs) do + if (not first) then first = k end + if (k == self.LastValidInput) then best = k end + if v.Num then + self.Inputs[v.Num] = k + else + table.insert(self.Inputs, k) + end + if (v.Desc) then + self.InputsDesc[k] = v.Desc + end + if (v.Type) then + self.InputsType[k] = v.Type + end + end + end + + first = self.Inputs[1] or first + + self.CurrentInput = best or first + if (self.CurrentInput) and (self.CurrentInput ~= "") then self.LastValidInput = self.CurrentInput end + + local txt = "" + if (self.CurrentComponent) and (self.CurrentComponent:IsValid()) and (self.CurrentInput) + and (self.CurrentComponent.Inputs) and (self.CurrentComponent.Inputs[self.CurrentInput]) + and (self.CurrentComponent.Inputs[self.CurrentInput].Src) then + txt = "%"..(self.CurrentInput or "") + else + txt = self.CurrentInput or "" + end + if (self.InputsDesc) and (self.InputsDesc[self.CurrentInput]) then + txt = txt.." ("..self.InputsDesc[self.CurrentInput]..")" + end + if (self.InputsType) and (self.InputsType[self.CurrentInput]) + and (self.InputsType[self.CurrentInput] != "NORMAL") then + txt = txt.." ["..self.InputsType[self.CurrentInput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + if (self.CurrentComponent) and (self.CurrentComponent:IsValid()) then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", self.CurrentInput) + end +end + + diff --git a/lua/weapons/gmod_tool/stools/wire_addressbus.lua b/lua/weapons/gmod_tool/stools/wire_addressbus.lua new file mode 100644 index 0000000000..c5b73a81da --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_addressbus.lua @@ -0,0 +1,297 @@ +TOOL.Category = "Wire - Advanced" +TOOL.Name = "Address Bus" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_addressbus_name", "Address bus tool (Wire)" ) + language.Add( "Tool_wire_addressbus_desc", "Spawns an address bus. Address spaces may overlap!" ) + language.Add( "Tool_wire_addressbus_0", "Primary: Create/Update address bus" ) + language.Add( "sboxlimit_wire_addressbuss", "You've hit address buses limit!" ) + language.Add( "undone_wiredatarate", "Undone Address Bus" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_addressbuss', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_gate.mdl" +TOOL.ClientConVar[ "addrspace1sz" ] = 0 +TOOL.ClientConVar[ "addrspace2sz" ] = 0 +TOOL.ClientConVar[ "addrspace3sz" ] = 0 +TOOL.ClientConVar[ "addrspace4sz" ] = 0 +TOOL.ClientConVar[ "addrspace1st" ] = 0 +TOOL.ClientConVar[ "addrspace2st" ] = 0 +TOOL.ClientConVar[ "addrspace3st" ] = 0 +TOOL.ClientConVar[ "addrspace4st" ] = 0 + +cleanup.Register( "wire_addressbuss" ) + +function TOOL:LeftClick( trace ) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_addressbus" && trace.Entity.ply == ply ) then + trace.Entity.MemStart[1] = self:GetClientNumber( "addrspace1st" ) + trace.Entity.MemStart[2] = self:GetClientNumber( "addrspace2st" ) + trace.Entity.MemStart[3] = self:GetClientNumber( "addrspace3st" ) + trace.Entity.MemStart[4] = self:GetClientNumber( "addrspace4st" ) + trace.Entity.MemEnd[1] = trace.Entity.MemStart[1] + self:GetClientNumber( "addrspace1sz" ) - 1 + trace.Entity.MemEnd[2] = trace.Entity.MemStart[2] + self:GetClientNumber( "addrspace2sz" ) - 1 + trace.Entity.MemEnd[3] = trace.Entity.MemStart[3] + self:GetClientNumber( "addrspace3sz" ) - 1 + trace.Entity.MemEnd[4] = trace.Entity.MemStart[4] + self:GetClientNumber( "addrspace4sz" ) - 1 + for i = 1,4 do + trace.Entity["Mem"..i.."st"] = trace.Entity.MemStart[i] + trace.Entity["Mem"..i.."sz"] = self:GetClientNumber( "addrspace"..i.."sz" ) + end + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_addressbuss" ) ) then return false end + + if (not util.IsValidModel(self:GetClientInfo( "model" ))) then return false end + if (not util.IsValidProp(self:GetClientInfo( "model" ))) then return false end + + local ply = self:GetOwner() + local Ang = trace.HitNormal:Angle() + local model = self:GetClientInfo( "model" ) + Ang.pitch = Ang.pitch + 90 + + wire_addressbus = MakeWireAddressBus( ply, trace.HitPos, Ang, model, + self:GetClientNumber( "addrspace1st" ), + self:GetClientNumber( "addrspace2st" ), + self:GetClientNumber( "addrspace3st" ), + self:GetClientNumber( "addrspace4st" ), + self:GetClientNumber( "addrspace1sz" ), + self:GetClientNumber( "addrspace2sz" ), + self:GetClientNumber( "addrspace3sz" ), + self:GetClientNumber( "addrspace4sz" ) + ) + local min = wire_addressbus:OBBMins() + wire_addressbus:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_addressbus, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireAddressBus") + undo.AddEntity( wire_addressbus ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_addressbuss", wire_addressbus ) + + return true +end + +function TOOL:RightClick( trace ) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_addressbus" && trace.Entity.ply == ply ) then + ply:ConCommand("wire_addressbus_addrspace1sz "..(trace.Entity.MemEnd[1]-trace.Entity.MemStart[1]+1)) + ply:ConCommand("wire_addressbus_addrspace1st "..(trace.Entity.MemStart[1])) + ply:ConCommand("wire_addressbus_addrspace2sz "..(trace.Entity.MemEnd[2]-trace.Entity.MemStart[2]+1)) + ply:ConCommand("wire_addressbus_addrspace2st "..(trace.Entity.MemStart[2])) + ply:ConCommand("wire_addressbus_addrspace3sz "..(trace.Entity.MemEnd[3]-trace.Entity.MemStart[3]+1)) + ply:ConCommand("wire_addressbus_addrspace3st "..(trace.Entity.MemStart[3])) + ply:ConCommand("wire_addressbus_addrspace4sz "..(trace.Entity.MemEnd[4]-trace.Entity.MemStart[4]+1)) + ply:ConCommand("wire_addressbus_addrspace4st "..(trace.Entity.MemStart[4])) + end + return true +end + +if (SERVER) then + + function MakeWireAddressBus( ply, Pos, Ang, model, Mem1st, Mem2st, Mem3st, Mem4st, Mem1sz, Mem2sz, Mem3sz, Mem4sz ) + + if ( !ply:CheckLimit( "wire_addressbuss" ) ) then return false end + + local wire_addressbus = ents.Create( "gmod_wire_addressbus" ) + if (!wire_addressbus:IsValid()) then return false end + wire_addressbus:SetModel(model) + + wire_addressbus:SetAngles( Ang ) + wire_addressbus:SetPos( Pos ) + wire_addressbus:Spawn() + + if (!Mem1st or Mem1st == "") then Mem1st = "0" end + if (!Mem2st or Mem2st == "") then Mem2st = "0" end + if (!Mem3st or Mem3st == "") then Mem3st = "0" end + if (!Mem4st or Mem4st == "") then Mem4st = "0" end + if (!Mem1sz or Mem1sz == "") then Mem1sz = "0" end + if (!Mem2sz or Mem2sz == "") then Mem2sz = "0" end + if (!Mem3sz or Mem3sz == "") then Mem3sz = "0" end + if (!Mem4sz or Mem4sz == "") then Mem4sz = "0" end + + wire_addressbus.MemStart[1] = tonumber(Mem1st) + wire_addressbus.MemStart[2] = tonumber(Mem2st) + wire_addressbus.MemStart[3] = tonumber(Mem3st) + wire_addressbus.MemStart[4] = tonumber(Mem4st) + wire_addressbus.MemEnd[1] = wire_addressbus.MemStart[1] + tonumber(Mem1sz) - 1 + wire_addressbus.MemEnd[2] = wire_addressbus.MemStart[2] + tonumber(Mem2sz) - 1 + wire_addressbus.MemEnd[3] = wire_addressbus.MemStart[3] + tonumber(Mem3sz) - 1 + wire_addressbus.MemEnd[4] = wire_addressbus.MemStart[4] + tonumber(Mem4sz) - 1 + + wire_addressbus:SetPlayer(ply) + + //KTNX TAD! + + local ttable = { + ply = ply, + model = model, + Mem1st = Mem1st, + Mem2st = Mem2st, + Mem3st = Mem3st, + Mem4st = Mem4st, + Mem1sz = Mem1sz, + Mem2sz = Mem2sz, + Mem3sz = Mem3sz, + Mem4sz = Mem4sz, + } + table.Merge(wire_addressbus:GetTable(), ttable ) + + ply:AddCount( "wire_addressbuss", wire_addressbus ) + + return wire_addressbus + + end + + duplicator.RegisterEntityClass("gmod_wire_addressbus", MakeWireAddressBus, "Pos", "Ang", "Model", "Mem1st", "Mem2st", "Mem3st", "Mem4st", "Mem1sz", "Mem2sz", "Mem3sz", "Mem4sz") + +end + +function TOOL:UpdateGhostWireAddressBus( ent, player ) + + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_addressbus" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" ) || (not self.GhostEntity:GetModel()) ) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireAddressBus( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_addressbus_name", Description = "#Tool_wire_addressbus_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_addressbus", + + Options = { + Default = { + wire_addressbus_addrspace1st = "0", + wire_addressbus_addrspace1sz = "0", + wire_addressbus_addrspace2st = "0", + wire_addressbus_addrspace2sz = "0", + wire_addressbus_addrspace3st = "0", + wire_addressbus_addrspace3sz = "0", + wire_addressbus_addrspace4st = "0", + wire_addressbus_addrspace4sz = "0", + } + }, + + CVars = { + [0] = "wire_addressbus_addrspace1st", + [1] = "wire_addressbus_addrspace1sz", + [2] = "wire_addressbus_addrspace2st", + [3] = "wire_addressbus_addrspace2sz", + [4] = "wire_addressbus_addrspace3st", + [5] = "wire_addressbus_addrspace3sz", + [6] = "wire_addressbus_addrspace4st", + [7] = "wire_addressbus_addrspace4sz", + } + }) + + panel:AddControl("Slider", { + Label = "Address space 1 offset", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace1st" + }) + + panel:AddControl("Slider", { + Label = "Address space 1 size", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace1sz" + }) + + panel:AddControl("Slider", { + Label = "Address space 2 offset", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace2st" + }) + + panel:AddControl("Slider", { + Label = "Address space 2 size", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace2sz" + }) + + panel:AddControl("Slider", { + Label = "Address space 3 offset", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace3st" + }) + + panel:AddControl("Slider", { + Label = "Address space 3 size", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace3sz" + }) + + panel:AddControl("Slider", { + Label = "Address space 4 offset", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace4st" + }) + + panel:AddControl("Slider", { + Label = "Address space 4 size", + Type = "Integer", + Min = "0", + Max = "16777216", + Command = "wire_addressbus_addrspace4sz" + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_adv.lua b/lua/weapons/gmod_tool/stools/wire_adv.lua new file mode 100644 index 0000000000..a693f1655f --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_adv.lua @@ -0,0 +1,932 @@ +TOOL.Category = "Wire - Tools" +TOOL.Name = "Wire Advanced" +TOOL.Tab = "Wire" + +if CLIENT then + language.Add( "Tool_wire_adv_name", "Advanced Wiring Tool" ) + language.Add( "Tool_wire_adv_desc", "Used to connect wirable props." ) + language.Add( "Tool_wire_adv_0", "Primary: Attach to selected input, Secondary: Next input, Reload: Unlink selected input." ) + language.Add( "Tool_wire_adv_1", "Primary: Attach to output, Secondary: Attach but continue, Reload: Cancel." ) + language.Add( "Tool_wire_adv_2", "Primary: Confirm attach to output, Secondary: Next output, Reload: Cancel." ) + language.Add( "WireTool_scrollwithoutmod", "Scroll without modifier key" ) +end + +TOOL.ClientConVar = { + width = 2, + material = "cable/cable2", + color_r = 255, + color_g = 255, + color_b = 255, + scrollwithoutmod = 1, +} + +-- these lines are pretty useless, but they serve as documentation :) +TOOL.CurrentComponent = nil +TOOL.CurrentInput = nil +TOOL.Inputs = nil +TOOL.CurrentOutput = nil +TOOL.Outputs = nil + +util.PrecacheSound("weapons/pistol/pistol_empty.wav") + +cleanup.Register( "wireconstraints" ) + +function TOOL:LeftClick( trace ) + if trace.Entity:IsValid() and trace.Entity:IsPlayer() then return end + + local stage = self:GetStage() + + if stage == 0 then + if CLIENT then + if self:GetWeapon():GetNetworkedString("WireCurrentInput") then + self:SetStage(0) + return true + end + elseif self.CurrentInput then + local material = self:GetClientInfo("material") + local width = self:GetClientNumber("width") + local color = Color(self:GetClientNumber("color_r"), self:GetClientNumber("color_g"), self:GetClientNumber("color_b")) + if Wire_Link_Start(self:GetOwner():UniqueID(), trace.Entity, trace.Entity:WorldToLocal(trace.HitPos), self.CurrentInput, material, color, width) then + self:SetStage(1) + self:GetWeapon():SetNetworkedInt("WireAdvStage",1) + return true + end + end + + return + elseif stage == 1 then + if CLIENT then + self:SetStage(0) + return true + end + + if not trace.Entity.Outputs then + self:SetStage(0) + self:GetWeapon():SetNetworkedInt("WireAdvStage",0) + + Wire_Link_Cancel(self:GetOwner():UniqueID()) + + local inp = self.CurrentComponent.Inputs[self.CurrentInput] + + if inp.Type == "ENTITY" then + -- TODO: trigger input with entity + inp.Value = trace.Entity + self.CurrentComponent:TriggerInput(self.CurrentInput, trace.Entity) + WireLib.AddNotify(self:GetOwner(), "Triggered entity input '"..self.CurrentInput.."' with '"..tostring(trace.Entity).."'.", NOTIFY_GENERIC, 7 ) + return + end + + WireLib.AddNotify(self:GetOwner(), "Wire source invalid!", NOTIFY_GENERIC, 7) + return + end + + self.Outputs = {} + self.OutputsDesc = {} + self.OutputsType = {} + for key,v in pairs_sortvalues(trace.Entity.Outputs, WireLib.PortComparator) do + if v.Num then + self.Outputs[v.Num] = key + if v.Desc then + self.OutputsDesc[key] = v.Desc + end + if v.Type then + self.OutputsType[key] = v.Type + end + else + table.insert(self.Outputs, key) + end + end + + local oname = nil + for k,_ in pairs_sortvalues(trace.Entity.Outputs, WireLib.PortComparator) do + if oname then + self:SelectComponent(nil) + self.CurrentOutput = self.Outputs[1] --oname + self.OutputEnt = trace.Entity + self.OutputPos = trace.Entity:WorldToLocal(trace.HitPos) + --New + + local tab = table.concat(self.Outputs,",")..table.concat(self.OutputsDesc,",")..table.concat(self.OutputsType,",") + if self.LastInputs ~= tab then + umsg.Start("wireoutputlist",self:GetOwner()) + umsg.String(self.OutputEnt:GetClass()) + umsg.Short(#self.Outputs) + for k,v in pairs(self.Outputs) do + local txt = v + if self.OutputsDesc and self.OutputsDesc[v] then + txt = txt.." ("..self.OutputsDesc[v]..")" + end + if self.OutputsType and self.OutputsType[v] + and self.OutputsType[v] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[v].."]" + end + umsg.String(txt) + end + umsg.End() + self.LastInputs = tab + end + --Old + + local txt = "Output: "..self.CurrentOutput + if self.OutputsDesc and self.OutputsDesc[self.CurrentOutput] then + txt = txt.." ("..self.OutputsDesc[self.CurrentOutput]..")" + end + if self.OutputsType and self.OutputsType[self.CurrentOutput] + and self.OutputsType[self.CurrentOutput] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[self.CurrentOutput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + self:SetStage(2) + self:GetWeapon():SetNetworkedInt("WireAdvStage",2) + return true + end + + oname = k + end + + Wire_Link_End(self:GetOwner():UniqueID(), trace.Entity, trace.Entity:WorldToLocal(trace.HitPos), oname, self:GetOwner()) + + self:SelectComponent(nil) + self:SetStage(0) + self:GetWeapon():SetNetworkedInt("WireAdvStage",0) + else + if CLIENT then + self:SetStage(0) + return true + end + + Wire_Link_End(self:GetOwner():UniqueID(), self.OutputEnt, self.OutputPos, self.CurrentOutput, self:GetOwner()) + + self:GetWeapon():SetNetworkedString("WireCurrentInput", "") + self.CurrentOutput = nil + self.OutputEnt = nil + self.OutputPos = nil + + self:SelectComponent(nil) + self:SetStage(0) + self:GetWeapon():SetNetworkedInt("WireAdvStage",0) + end + + return true +end + +if CLIENT then + local function check_override(pl) + -- find toolgun + local activeWep = pl:GetActiveWeapon() + if not ValidEntity(activeWep) then return end + + -- checks... + if activeWep:GetClass() ~= "gmod_tool" then return end + if activeWep:GetMode() ~= "wire_adv" then return end + + -- find tool + local tool = activeWep:GetToolObject("wire_adv") + local trace = pl:GetEyeTrace() + + -- allow override if conditions are met + if pl:GetInfo("wire_adv_scrollwithoutmod") == "0" and not pl:KeyDown(IN_SPEED) then return false end + --if tool.OutputEntClass then return true end + if activeWep:GetNetworkedInt("WireAdvStage") == 2 then return true end + if not trace.Entity:IsValid() then return false end + return trace.Entity:GetClass() == tool.InputEntClass + end + hook.Add("PlayerBindPress", "wire_wire_adv", function(pl, bind, pressed) + if not pressed then return end + + if bind == "invnext" then + if check_override(pl) then + RunConsoleCommand("wire_adv_next") + return true + end + elseif bind == "invprev" then + if check_override(pl) then + RunConsoleCommand("wire_adv_prev") + return true + end + end + end) +end + +if SERVER then + concommand.Add("wire_adv_next",function(pl) + if not pl:GetWeapon("gmod_tool"):IsValid() then return end + local self = pl:GetWeapon("gmod_tool").Tool.wire_adv + local stage = self:GetStage() + if stage == 0 then + if not self.Inputs or not self.CurrentInput then return end + + local iNextInput + for k,v in pairs(self.Inputs) do + if v == self.CurrentInput then iNextInput = k+1 end + end + if iNextInput then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if iNextInput > table.getn(self.Inputs) then iNextInput = 1 end + + self.CurrentInput = self.Inputs[iNextInput] + if self.CurrentInput then self.LastValidInput = self.CurrentInput end + + --[[if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput) + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput]) + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + self:GetWeapon():SetNetworkedString("WireCurrentInput", self.CurrentInput or "")) + else + self:GetWeapon():SetNetworkedString("WireCurrentInput", self.CurrentInput or "") + end]] + + local txt = "" + if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput] + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + txt = (self.CurrentInput or "") + else + txt = self.CurrentInput or "" + end + if self.InputsDesc and self.InputsDesc[self.CurrentInput] then + txt = txt.." ("..self.InputsDesc[self.CurrentInput]..")" + end + if self.InputsType and self.InputsType[self.CurrentInput] + and self.InputsType[self.CurrentInput] ~= "NORMAL" then + txt = txt.." ["..self.InputsType[self.CurrentInput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + + if self.CurrentComponent and self.CurrentComponent:IsValid() then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", self.CurrentInput) + end + end + elseif self.Outputs then + --New + + local tab = table.concat(self.Outputs,",")..table.concat(self.OutputsDesc,",")..table.concat(self.OutputsType,",") + if self.LastInputs ~= tab then + umsg.Start("wireoutputlist",self:GetOwner()) + umsg.String("") + umsg.Short(#self.Outputs) + for k,v in pairs(self.Outputs) do + local txt = v + if self.OutputsDesc and self.OutputsDesc[v] then + txt = txt.." ("..self.OutputsDesc[v]..")" + end + if self.OutputsType and self.OutputsType[v] + and self.OutputsType[v] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[v].."]" + end + umsg.String(txt) + end + umsg.End() + self.LastInputs = tab + end + --Old + + local iNextOutput + for k,v in pairs(self.Outputs) do + if v == self.CurrentOutput then iNextOutput = k+1 end + end + + if iNextOutput then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if iNextOutput > table.getn(self.Outputs) then iNextOutput = 1 end + + self.CurrentOutput = self.Outputs[iNextOutput] or "" --if that's nil then somethis is wrong with the ent + + local txt = "Output: "..self.CurrentOutput + if self.OutputsDesc and self.OutputsDesc[self.CurrentOutput] then + txt = txt.." ("..self.OutputsDesc[self.CurrentOutput]..")" + end + if self.OutputsType and self.OutputsType[self.CurrentOutput] + and self.OutputsType[self.CurrentOutput] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[self.CurrentOutput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + end + end + end) + concommand.Add("wire_adv_prev",function(pl) + if not pl:GetWeapon("gmod_tool"):IsValid() then return end + local self = pl:GetWeapon("gmod_tool").Tool.wire_adv + local stage = self:GetStage() + if stage == 0 then + if not self.Inputs or not self.CurrentInput then return end + + local iNextInput + for k,v in pairs(self.Inputs) do + if v == self.CurrentInput then iNextInput = k-1 end + end + if iNextInput then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if iNextInput < 1 then iNextInput = #self.Inputs end + + self.CurrentInput = self.Inputs[iNextInput] + if self.CurrentInput then self.LastValidInput = self.CurrentInput end + + --[[if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput) + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput]) + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + self:GetWeapon():SetNetworkedString("WireCurrentInput", self.CurrentInput or "")) + else + self:GetWeapon():SetNetworkedString("WireCurrentInput", self.CurrentInput or "") + end]] + + local txt = "" + if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput] + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + txt = self.CurrentInput or "" + else + txt = self.CurrentInput or "" + end + if self.InputsDesc and self.InputsDesc[self.CurrentInput] then + txt = txt.." ("..self.InputsDesc[self.CurrentInput]..")" + end + if self.InputsType and self.InputsType[self.CurrentInput] + and self.InputsType[self.CurrentInput] ~= "NORMAL" then + txt = txt.." ["..self.InputsType[self.CurrentInput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + + if self.CurrentComponent and self.CurrentComponent:IsValid() then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", self.CurrentInput) + end + end + elseif self.Outputs then + if CLIENT then return end + --New + + local tab = table.concat(self.Outputs,",")..table.concat(self.OutputsDesc,",")..table.concat(self.OutputsType,",") + if self.LastInputs ~= tab then + umsg.Start("wireoutputlist",self:GetOwner()) + umsg.String("") + umsg.Short(#self.Outputs) + for k,v in pairs(self.Outputs) do + local txt = v + if self.OutputsDesc and self.OutputsDesc[v] then + txt = txt.." ("..self.OutputsDesc[v]..")" + end + if self.OutputsType and self.OutputsType[v] + and self.OutputsType[v] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[v].."]" + end + umsg.String(txt) + end + umsg.End() + self.LastInputs = tab + end + --Old + + local iNextOutput + for k,v in pairs(self.Outputs) do + if v == self.CurrentOutput then iNextOutput = k-1 end + end + + if iNextOutput then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if iNextOutput < 1 then iNextOutput = #self.Outputs end + + self.CurrentOutput = self.Outputs[iNextOutput] or "" --if that's nil then somethis is wrong with the ent + + local txt = "Output: "..self.CurrentOutput + if self.OutputsDesc and self.OutputsDesc[self.CurrentOutput] then + txt = txt.." ("..self.OutputsDesc[self.CurrentOutput]..")" + end + if self.OutputsType and self.OutputsType[self.CurrentOutput] + and self.OutputsType[self.CurrentOutput] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[self.CurrentOutput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + end + end + end) + +end + +function TOOL:RightClick( trace ) + local stage = self:GetStage() + + if stage < 2 then + if not trace.Entity:IsValid() or trace.Entity:IsPlayer() then return end + end + + if stage == 0 then + if CLIENT then return end + + if trace.Entity:IsValid() then + self:SelectComponent(trace.Entity) + else + self:SelectComponent(nil) + end + if not self.Inputs or not self.CurrentInput then return end + + local iNextInput + for k,v in pairs(self.Inputs) do + if v == self.CurrentInput then iNextInput = k+1 end + end + if iNextInput then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if iNextInput > table.getn(self.Inputs) then iNextInput = 1 end + + self.CurrentInput = self.Inputs[iNextInput] + if self.CurrentInput then self.LastValidInput = self.CurrentInput end + + --[[if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput) + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput]) + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + self:GetWeapon():SetNetworkedString("WireCurrentInput", (self.CurrentInput or "")) + else + self:GetWeapon():SetNetworkedString("WireCurrentInput", self.CurrentInput or "") + end]] + + local txt = "" + if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput] + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + txt = (self.CurrentInput or "") + else + txt = self.CurrentInput or "" + end + if self.InputsDesc and self.InputsDesc[self.CurrentInput] then + txt = txt.." ("..self.InputsDesc[self.CurrentInput]..")" + end + if self.InputsType and self.InputsType[self.CurrentInput] + and self.InputsType[self.CurrentInput] ~= "NORMAL" then + txt = txt.." ["..self.InputsType[self.CurrentInput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + + if self.CurrentComponent and self.CurrentComponent:IsValid() then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", self.CurrentInput) + end + end + elseif stage == 1 then + if SERVER then + Wire_Link_Node(self:GetOwner():UniqueID(), trace.Entity, trace.Entity:WorldToLocal(trace.HitPos+trace.HitNormal)) + end + elseif self.Outputs then + if CLIENT then return end + --New + + local tab = table.concat(self.Outputs,",")..table.concat(self.OutputsDesc,",")..table.concat(self.OutputsType,",") + if self.LastInputs ~= tab then + umsg.Start("wireoutputlist",self:GetOwner()) + umsg.String(self.OutputEnt:GetClass()) + umsg.Short(#self.Outputs) + for k,v in pairs(self.Outputs) do + local txt = v + if self.OutputsDesc and self.OutputsDesc[v] then + txt = txt.." ("..self.OutputsDesc[v]..")" + end + if self.OutputsType and self.OutputsType[v] + and self.OutputsType[v] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[v].."]" + end + umsg.String(txt) + end + umsg.End() + self.LastInputs = tab + end + --Old + + local iNextOutput + for k,v in pairs(self.Outputs) do + if v == self.CurrentOutput then iNextOutput = k+1 end + end + + if iNextOutput then + self:GetOwner():EmitSound("weapons/pistol/pistol_empty.wav") + + if iNextOutput > table.getn(self.Outputs) then iNextOutput = 1 end + + self.CurrentOutput = self.Outputs[iNextOutput] or "" --if that's nil then somethis is wrong with the ent + + local txt = "Output: "..self.CurrentOutput + if self.OutputsDesc and self.OutputsDesc[self.CurrentOutput] then + txt = txt.." ("..self.OutputsDesc[self.CurrentOutput]..")" + end + if self.OutputsType and self.OutputsType[self.CurrentOutput] + and self.OutputsType[self.CurrentOutput] ~= "NORMAL" then + txt = txt.." ["..self.OutputsType[self.CurrentOutput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + end + end +end + + +function TOOL:Reload(trace) + if not trace.Entity:IsValid() or trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + if self:GetStage() == 0 then + if not self.CurrentComponent or not self.CurrentComponent:IsValid() then return end + if not self.CurrentInput or self.CurrentInput == "" then return end + + Wire_Link_Clear(self.CurrentComponent, self.CurrentInput) + + --New + local ent = self.CurrentComponent + local tab = "" + for k,v in pairs(self.Inputs) do + tab = tab..v.."|"..(( + ent and + ent:IsValid() and + ent.Inputs and + ent.Inputs[v] and + ent.Inputs[v].Src) and "1|" or "0|" + ) + end + + if self.LastInputs ~= tab then + umsg.Start("wireinputlist",self:GetOwner()) + umsg.String(ent:GetClass()) + umsg.Short(#self.Inputs) + for k,v in pairs(self.Inputs) do + + local txt = v + if self.InputsDesc and self.InputsDesc[v] then + txt = txt.." ("..self.InputsDesc[v]..")" + end + if self.InputsType and self.InputsType[v] + and self.InputsType[v] ~= "NORMAL" then + txt = txt.." ["..self.InputsType[v].."]" + end + + umsg.String(txt) + if + ent and + ent:IsValid() and + ent.Inputs and + ent.Inputs[v] and + ent.Inputs[v].Src + then + umsg.Bool(true) + else + umsg.Bool(false) + end + end + umsg.End() + self.LastInputs = tab + end + return true + end + + Wire_Link_Cancel(self:GetOwner():UniqueID()) + self:SetStage(0) + self:GetWeapon():SetNetworkedInt("WireAdvStage",0) + + return true +end + +function TOOL:Holster() + self:SelectComponent(nil) + self:GetWeapon():SetNetworkedInt("WireAdvStage",0) +end + + +if CLIENT then + + function TOOL:DrawHUD() + local current_input = self:GetWeapon():GetNetworkedString("WireCurrentInput" or "") + --[[ + if current_input ~= "" then + if string.sub(current_input, 1, 1 == "%") then -- I see a misplaced ")" + draw.WordBox(8, ScrW()/2+10, ScrH()/2+10, string.sub(current_input, 2), "Default", Color(150,50,50,192), Color(255,255,255,255) ) + else + draw.WordBox(8, ScrW()/2+10, ScrH()/2+10, current_input, "Default", Color(50,50,75,192), Color(255,255,255,255) ) + end + end + ]] + + --Begin + local stage = self:GetWeapon():GetNetworkedInt("WireAdvStage") + --draw.DrawText(stage,"Trebuchet24",0,0,Color(255,255,255,255),0) + if stage == 2 and type(self.Outputs) == "table" then + surface.SetFont("Trebuchet24") + local twa = surface.GetTextSize(self.WireCurrentInput) + draw.RoundedBox(8, + ScrW()/2+20, + ScrH()/2-12-4, + twa+16, + 24+8, + Color(50,50,75,192) + ) + draw.DrawText(self.WireCurrentInput,"Trebuchet24",ScrW()/2+20+8,ScrH()/2-12,Color(255,255,255,255),0) + twa = twa+16 + + surface.SetFont("Trebuchet24") + local tw = surface.GetTextSize(table.concat(self.Outputs,"\n")) + draw.RoundedBox(8, + twa+ScrW()/2+20, + ScrH()/2-#self.Outputs*24/2-8, + tw+16, + #self.Outputs*24+16, + Color(50,50,75,192) + ) + + for k,v in pairs(self.Outputs) do + if self:GetWeapon():GetNetworkedString("WireCurrentInput") == "Output: "..v then + draw.RoundedBox(4, + twa+ScrW()/2+20+4, + ScrH()/2-#self.Outputs*24/2+(k-1)*24, + tw+8, + 24, + Color(0,150,0,192) + ) + end + draw.DrawText( + v,"Trebuchet24", + twa+ScrW()/2+20+8, + ScrH()/2-#self.Outputs*24/2+(k-1)*24, + Color(255,255,255,255), + 0 + ) + end + elseif stage == 0 then + + local tr = utilx.GetPlayerTrace(LocalPlayer(), LocalPlayer():GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if trace.Hit and trace.Entity:IsValid() and trace.Entity:GetClass() == self.InputEntClass and type(self.Inputs) == "table" then + surface.SetFont("Trebuchet24") + local tw = surface.GetTextSize(table.concat(self.Inputs,"\n")) + draw.RoundedBox(8, + ScrW()/2+20, + ScrH()/2-#self.Inputs*24/2-8, + tw+16, + #self.Inputs*24+16, + Color(50,50,75,192) + ) + + for k,v in pairs(self.Inputs) do + local col = Color(255,255,255,255) + if self.InputsW[k] then + col = Color(255,0,0,255) + end + + if self:GetWeapon():GetNetworkedString("WireCurrentInput") == v then + draw.RoundedBox(4, + ScrW()/2+20+4, + ScrH()/2-#self.Inputs*24/2+(k-1)*24, + tw+8, + 24, + Color(0,150,0,192) + ) + self.WireCurrentInput = v + end + + draw.DrawText( + v,"Trebuchet24", + ScrW()/2+20+8, + ScrH()/2-#self.Inputs*24/2+(k-1)*24, + col, + 0 + ) + end + end + elseif stage == 1 then + surface.SetFont("Trebuchet24") + local tw = surface.GetTextSize(self.WireCurrentInput) + draw.RoundedBox(8, + ScrW()/2+20, + ScrH()/2-12-4, + tw+16, + 24+8, + Color(50,50,75,192) + ) + draw.DrawText(self.WireCurrentInput,"Trebuchet24",ScrW()/2+20+8,ScrH()/2-12,Color(255,255,255,255),0) + end + end + +end + + +function TOOL:Think() + if self:GetStage() == 0 then + local player = self:GetOwner() + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if trace.Hit and trace.Entity:IsValid() then + self:SelectComponent(trace.Entity) + else + self:SelectComponent(nil) + end + end +end + + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_name", Description = "#Tool_wire_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_adv", + + Options = { + Default = { + wire_adv_material = "cable/rope", + wire_adv_width = "3", + } + }, + + CVars = { + [0] = "wire_adv_width", + [1] = "wire_adv_material", + } + }) + + panel:AddControl("Slider", { + Label = "#WireTool_width", + Type = "Float", + Min = ".1", + Max = "5", + Command = "wire_adv_width" + }) + + panel:AddControl("MaterialGallery", { + Label = "#WireTool_material", + Height = "64", + Width = "24", + Rows = "1", + Stretch = "1", + + Options = { + ["Wire"] = { Material = "cable/rope_icon", wire_adv_material = "cable/rope" }, + ["Cable 2"] = { Material = "cable/cable_icon", wire_adv_material = "cable/cable2" }, + ["XBeam"] = { Material = "cable/xbeam", wire_adv_material = "cable/xbeam" }, + ["Red Laser"] = { Material = "cable/redlaser", wire_adv_material = "cable/redlaser" }, + ["Blue Electric"] = { Material = "cable/blue_elec", wire_adv_material = "cable/blue_elec" }, + ["Physics Beam"] = { Material = "cable/physbeam", wire_adv_material = "cable/physbeam" }, + ["Hydra"] = { Material = "cable/hydra", wire_adv_material = "cable/hydra" }, + + --new wire materials by Acegikmo + ["Arrowire"] = { Material = "arrowire/arrowire", wire_adv_material = "arrowire/arrowire" }, + ["Arrowire2"] = { Material = "arrowire/arrowire2", wire_adv_material = "arrowire/arrowire2" }, + }, + + CVars = { + [0] = "wire_adv_material" + } + }) + + panel:AddControl("Color", { + Label = "#WireTool_colour", + Red = "wire_adv_color_r", + Green = "wire_adv_color_g", + Blue = "wire_adv_color_b", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTool_scrollwithoutmod", + Command = "wire_adv_scrollwithoutmod" + }) +end + +if CLIENT then + usermessage.Hook("wireinputlist",function(um) + local self = LocalPlayer():GetActiveWeapon().Tool.wire_adv + local temp = um:ReadString() + if temp ~= "" then + self.InputEntClass = temp + end + self.Inputs = {} + self.InputsW = {} + for i = 1, um:ReadShort() do + self.Inputs[i] = um:ReadString() + self.InputsW[i] = um:ReadBool() + end + end) + usermessage.Hook("wireoutputlist",function(um) + local self = LocalPlayer():GetActiveWeapon().Tool.wire_adv + -- TODO: remove + local temp = um:ReadString() + if temp ~= "" then + self.OutputEntClass = temp + end + self.Outputs = {} + for i = 1, um:ReadShort() do + self.Outputs[i] = um:ReadString() + end + end) +end + +function TOOL:SelectComponent(ent) + if CLIENT then return end + + if self.CurrentComponent == ent then return end + + if self.CurrentComponent and self.CurrentComponent:IsValid() then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", "") + end + + self.CurrentComponent = ent + self.CurrentInput = nil + self.Inputs = {} + self.InputsDesc = {} + self.InputsType = {} + + local best = nil + local first = nil + if ent and ent.Inputs then + for k,v in pairs_sortvalues(ent.Inputs, WireLib.PortComparator) do + if not first then first = k end + if k == self.LastValidInput then best = k end + if v.Num then + self.Inputs[v.Num] = k + else + table.insert(self.Inputs, k) + end + if v.Desc then + self.InputsDesc[k] = v.Desc + end + if v.Type then + self.InputsType[k] = v.Type + end + end + + --New + + local tab = "" + for k,v in pairs(self.Inputs) do + tab = tab..v.."|"..(( + ent and + ent:IsValid() and + ent.Inputs and + ent.Inputs[v] and + ent.Inputs[v].Src) and "1|" or "0|" + ) + end + + if self.LastInputs ~= tab then + umsg.Start("wireinputlist",self:GetOwner()) + umsg.String(ent:GetClass()) + umsg.Short(#self.Inputs) + for k,v in pairs(self.Inputs) do + + local txt = v + if self.InputsDesc and self.InputsDesc[v] then + txt = txt.." ("..self.InputsDesc[v]..")" + end + if self.InputsType and self.InputsType[v] + and self.InputsType[v] ~= "NORMAL" then + txt = txt.." ["..self.InputsType[v].."]" + end + + umsg.String(txt) + if + ent and + ent:IsValid() and + ent.Inputs and + ent.Inputs[v] and + ent.Inputs[v].Src + then + umsg.Bool(true) + else + umsg.Bool(false) + end + end + umsg.End() + self.LastInputs = tab + end + end + + first = self.Inputs[1] or first + + self.CurrentInput = best or first + if self.CurrentInput and self.CurrentInput ~= "" then self.LastValidInput = self.CurrentInput end + + local txt = "" + if self.CurrentComponent and self.CurrentComponent:IsValid() and self.CurrentInput + and self.CurrentComponent.Inputs and self.CurrentComponent.Inputs[self.CurrentInput] + and self.CurrentComponent.Inputs[self.CurrentInput].Src then + txt = (self.CurrentInput or "") + else + txt = self.CurrentInput or "" + end + if self.InputsDesc and self.InputsDesc[self.CurrentInput] then + txt = txt.." ("..self.InputsDesc[self.CurrentInput]..")" + end + if self.InputsType and self.InputsType[self.CurrentInput] + and self.InputsType[self.CurrentInput] ~= "NORMAL" then + txt = txt.." ["..self.InputsType[self.CurrentInput].."]" + end + self:GetWeapon():SetNetworkedString("WireCurrentInput", txt) + + if self.CurrentComponent and self.CurrentComponent:IsValid() then + self.CurrentComponent:SetNetworkedBeamString("BlinkWire", self.CurrentInput) + end +end diff --git a/lua/weapons/gmod_tool/stools/wire_cam.lua b/lua/weapons/gmod_tool/stools/wire_cam.lua new file mode 100644 index 0000000000..c47192cd29 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_cam.lua @@ -0,0 +1,174 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Cam Controller" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_cam_name", "Cam Controller Tool (Wire)" ) + language.Add( "Tool_wire_cam_desc", "Spawns a constant Cam Controller prop for use with the wire system." ) + language.Add( "Tool_wire_cam_0", "Primary: Create/Update Cam Controller Secondary: Link a cam controller to a Pod." ) + language.Add( "Tool_wire_cam_1", "Now click a pod to link to." ) + language.Add( "WirecamTool_cam", "Camera Controller:" ) + language.Add( "WirecamTool_Static","Static") + language.Add( "sboxlimit_wire_cams", "You've hit Cam Controller limit!" ) + language.Add( "undone_Wire cam", "Undone Wire Cam Controller" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_cams', 20) +end + + +TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" + +TOOL.ClientConVar[ "Static" ] = "0" + +cleanup.Register( "wire_cams" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_cameracontroller" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_cams" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local Static = self:GetClientNumber("Static") + + local wire_cam = MakeWireCam( ply, trace.HitPos, Ang, self.Model, Static ) + + local min = wire_cam:OBBMins() + wire_cam:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_cam, trace.Entity, trace.PhysicsBone, true) + + if(Static == 1)then + local cam = ents.Create("prop_physics") + if (!cam:IsValid()) then return false end + + cam:SetAngles( Vector(0,0,0) ) + cam:SetPos( wire_cam:GetPos()+Vector(0,0,64) ) + cam:SetModel( Model("models/dav0r/camera.mdl") ) + cam:Spawn() + + wire_cam.CamEnt = cam + end + + undo.Create("Wire Cam") + undo.AddEntity( wire_cam ) + undo.AddEntity( const ) + undo.AddEntity( cam ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_cams", wire_cam ) + + return true +end + +function TOOL:RightClick( trace ) + if CLIENT then return true end + if not trace.Entity then return false end + if not trace.Entity:IsValid() then return false end + + if self:GetStage() == 0 then + if trace.Entity:GetClass() ~= "gmod_wire_cameracontroller" then return false end + self.Oldent = trace.Entity; + self:SetStage(1) + return true + elseif self:GetStage() == 1 then + if not trace.Entity:IsVehicle() then return false end + self.Oldent.CamPod = trace.Entity; + self.Oldent = nil; + self:SetStage(0) + return true + else + return false + end +end + +function TOOL:Reload( trace ) + self.Oldent = nil; + self:SetStage(0) + + if CLIENT then return true end + if not trace.Entity then return false end + if not trace.Entity:IsValid() then return false end + + self.trace.Entity.CamPod = nil; +end + +if (SERVER) then + + function MakeWireCam( pl, Pos, Ang, model, Static ) + if ( !pl:CheckLimit( "wire_cams" ) ) then return false end + + local wire_cam = ents.Create( "gmod_wire_cameracontroller" ) + if (!wire_cam:IsValid()) then return false end + + wire_cam:SetAngles( Ang ) + wire_cam:SetPos( Pos ) + wire_cam:SetModel( Model(model or "models/jaanus/wiretool/wiretool_siren.mdl") ) + wire_cam:Spawn() + wire_cam:Setup(pl,Static) + + wire_cam:GetTable():SetPlayer( pl ) + + local ttable = { + pl = pl, + Static=Static + } + table.Merge(wire_cam:GetTable(), ttable ) + + pl:AddCount( "wire_cams", wire_cam ) + + return wire_cam + end + + duplicator.RegisterEntityClass("gmod_wire_cameracontroller", MakeWireCam, "Pos", "Ang", "Model", "Static") + +end + +function TOOL:UpdateGhostWirecam( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_cameracontroller" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWirecam( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_cam_name", Description = "#Tool_wire_cam_desc" }) + panel:AddControl( "Checkbox", { Label = "#Wirecamtool_Static", Command = "wire_cam_static" } ) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_cd_disk.lua b/lua/weapons/gmod_tool/stools/wire_cd_disk.lua new file mode 100644 index 0000000000..e13a13907c --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_cd_disk.lua @@ -0,0 +1,164 @@ +TOOL.Category = "Wire - Data" +TOOL.Name = "CD Disk" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if (CLIENT) then + language.Add("Tool_wire_cd_disk_name", "CD Disk Tool (Wire)") + language.Add("Tool_wire_cd_disk_desc", "Spawns a CD Disk.") + language.Add("Tool_wire_cd_disk_0", "Primary: Create/Update CD Disk, Secondary: Change model") + language.Add("WireDataTransfererTool_cd_disk", "CD Disk:") + language.Add("sboxlimit_wire_cd_disks", "You've hit CD Disks limit!") + language.Add("undone_Wire CD Disk", "Undone Wire CD Disk") +end + +if (SERVER) then + CreateConVar('sbox_maxwire_cd_disks', 20) +end + +TOOL.ClientConVar["model"] = "models/venompapa/wirecd_medium.mdl" +TOOL.ClientConVar["skin"] = "0" +TOOL.ClientConVar["precision"] = 4 +TOOL.ClientConVar["iradius"] = 10 + +TOOL.FirstSelected = nil + +cleanup.Register("wire_cd_disks") + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_cd_disk" && trace.Entity:GetTable().pl == ply) then + trace.Entity.Precision = tonumber(self:GetClientInfo("precision")) + trace.Entity.IRadius = tonumber(self:GetClientInfo("iradius")) + trace.Entity:Setup() + return true + end + + if (!self:GetSWEP():CheckLimit("wire_cd_disks")) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_cd_disk = MakeWireCDDisk(ply, trace.HitPos, Ang , self:GetClientInfo("model"),tonumber(self:GetClientInfo("skin"))) + wire_cd_disk.Precision = tonumber(self:GetClientInfo("precision")) + wire_cd_disk.IRadius = tonumber(self:GetClientInfo("iradius")) + wire_cd_disk:Setup() + + local min = wire_cd_disk:OBBMins() + wire_cd_disk:SetPos(trace.HitPos - trace.HitNormal * min.z) + + local const = WireLib.Weld(wire_cd_disk, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire CD Disk") + undo.AddEntity(wire_cd_disk) + undo.AddEntity(const) + undo.SetPlayer(ply) + undo.Finish() + + ply:AddCleanup("wire_cd_disks", wire_cd_disk) + ply:AddCleanup("wire_cd_disks", const) + + return true +end + +function TOOL:RightClick(trace) + if (CLIENT) then return true end + + if (trace.Entity and trace.Entity:IsValid()) then + if (trace.Entity:GetClass() == "prop_physics") then + self:GetOwner():ConCommand('wire_cd_disk_model "'..trace.Entity:GetModel()..'"\n') + self:GetOwner():ConCommand('wire_cd_disk_skin "'..trace.Entity:GetSkin()..'"\n') + end + end + + return true +end + +if (SERVER) then + + function MakeWireCDDisk(pl, Pos, Ang, model, skin) + if (!pl:CheckLimit("wire_cd_disks")) then return false end + + local wire_cd_disk = ents.Create("gmod_wire_cd_disk") + if (!wire_cd_disk:IsValid()) then return false end + + wire_cd_disk:SetAngles(Ang) + wire_cd_disk:SetPos(Pos) + wire_cd_disk:SetSkin(skin) + wire_cd_disk:SetModel(model) + wire_cd_disk:Spawn() + + wire_cd_disk:SetPlayer(pl) + wire_cd_disk.pl = pl + + pl:AddCount("wire_cd_disks", wire_cd_disk) + + return wire_cd_disk + end + + duplicator.RegisterEntityClass("gmod_wire_cd_disk", MakeWireCDDisk, "Pos", "Ang", "Model", "skin") + +end + +function TOOL:UpdateGhostWireCDDisk(ent, player) + if (!ent || !ent:IsValid()) then return end + + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_cd_disk") then + ent:SetNoDraw(true) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos(trace.HitPos - trace.HitNormal * min.z) + ent:SetAngles(Ang) + + ent:SetNoDraw(false) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.model) then + self:MakeGhostEntity(self:GetClientInfo("model"), Vector(0,0,0), Angle(0,0,0)) + end + + self:UpdateGhostWireCDDisk(self.GhostEntity, self:GetOwner()) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_cd_disk_name", Description = "#Tool_wire_cd_disk_desc" }) + + panel:AddControl("Slider", { + Label = "Disk density (inches per block, ipb)", + Type = "Integer", + Min = "1", + Max = "16", + Command = "wire_cd_disk_precision" + }) + + panel:AddControl("Slider", { + Label = "Inner radius (disk hole radius)", + Type = "Integer", + Min = "1", + Max = "48", + Command = "wire_cd_disk_iradius" + }) + + panel:AddControl("Slider", { + Label = "Disk skin (0..8, standard disks only)", + Type = "Integer", + Min = "0", + Max = "8", + Command = "wire_cd_disk_skin" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_cd_ray.lua b/lua/weapons/gmod_tool/stools/wire_cd_ray.lua new file mode 100644 index 0000000000..828965e3e9 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_cd_ray.lua @@ -0,0 +1,214 @@ +TOOL.Category = "Wire - Data" +TOOL.Name = "CD Ray" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_cd_ray_name", "CD Ray Tool (Wire)" ) + language.Add( "Tool_wire_cd_ray_desc", "Spawns a CD Ray." ) + language.Add( "Tool_wire_cd_ray_0", "Primary: Create/Update CD Ray Secondary: Create CD lock (to keep CD in same spot)" ) + language.Add( "WireCDRayTool_cd_ray", "CD Ray:" ) + language.Add( "sboxlimit_wire_cd_rays", "You've hit CD Rays limit!" ) + language.Add( "undone_Wire CDRay", "Undone Wire CD Ray" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_cd_rays', 20) + CreateConVar('sbox_maxwire_cd_locks', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_beamcaster.mdl" +TOOL.ClientConVar[ "lockmodel" ] = "models/venompapa/wirecdlock.mdl" +TOOL.ClientConVar[ "Range" ] = "64" +TOOL.ClientConVar[ "DefaultZero" ] = "0" + +local transmodels = { + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_beamcaster.mdl"] = {}}; + +cleanup.Register("wire_cd_rays") + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_cd_ray" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_cd_rays" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local range = self:GetClientNumber("Range") + local defZero = (self:GetClientNumber("DefaultZero") ~= 0) + local model = self:GetClientInfo("model") + + local wire_cd_ray = MakeWireCDRay( ply, trace.HitPos, Ang, model, range, defZero ) + + local min = wire_cd_ray:OBBMins() + wire_cd_ray:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_cd_ray, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Data CD Ray") + undo.AddEntity( wire_cd_ray ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_cd_rays", wire_cd_ray ) + ply:AddCleanup( "wire_cd_rays", const ) + + return true +end + + +function TOOL:RightClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_cd_lock" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_cd_locks" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local range = self:GetClientNumber("Range") + local defZero = (self:GetClientNumber("DefaultZero") ~= 0) + local model = self:GetClientInfo("lockmodel") + + local wire_cd_lock = MakeWireCDLock( ply, trace.HitPos, Ang, model ) + + local min = wire_cd_lock:OBBMins() + wire_cd_lock:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_cd_lock, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Data CD Locky") + undo.AddEntity( wire_cd_lock ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_cd_locks", wire_cd_lock ) + ply:AddCleanup( "wire_cd_locks", const ) + + return true +end + +if (SERVER) then + + function MakeWireCDRay( pl, Pos, Ang, model, Range, DefaultZero) + if ( !pl:CheckLimit( "wire_cd_rays" ) ) then return false end + + local wire_cd_ray = ents.Create( "gmod_wire_cd_ray" ) + if (!wire_cd_ray:IsValid()) then return false end + + wire_cd_ray:SetAngles( Ang ) + wire_cd_ray:SetPos( Pos ) + wire_cd_ray:SetModel( model ) + wire_cd_ray:Spawn() + wire_cd_ray:Setup(Range,DefaultZero) + + wire_cd_ray:GetTable():SetPlayer( pl ) + + local ttable = { + Range = Range, + DefaultZero = DefaultZero, + pl = pl + } + table.Merge(wire_cd_ray:GetTable(), ttable ) + + pl:AddCount( "wire_cd_rays", wire_cd_ray ) + + return wire_cd_ray + end + duplicator.RegisterEntityClass("gmod_wire_cd_ray", MakeWireCDRay, "Pos", "Ang", "Model", "Range", "DefaultZero") + + function MakeWireCDLock( pl, Pos, Ang, model ) + if ( !pl:CheckLimit( "wire_cd_locks" ) ) then return false end + + local wire_cd_lock = ents.Create( "gmod_wire_cd_lock" ) + if (!wire_cd_lock:IsValid()) then return false end + + wire_cd_lock:SetAngles( Ang ) + wire_cd_lock:SetPos( Pos ) + wire_cd_lock:SetModel( model ) + wire_cd_lock:Spawn() + + wire_cd_lock:GetTable():SetPlayer( pl ) + + local ttable = { + Range = Range, + DefaultZero = DefaultZero, + pl = pl + } + table.Merge(wire_cd_lock:GetTable(), ttable ) + + pl:AddCount( "wire_cd_locks", wire_cd_lock ) + + return wire_cd_lock + end + duplicator.RegisterEntityClass("gmod_wire_cd_lock", MakeWireCDLock, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireCDRay( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_cd_ray" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model"), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireCDRay( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_cd_ray_name", Description = "#Tool_wire_cd_ray_desc" }) + + + panel:AddControl( "PropSelect", { Label = "#WireCDRayTool_Model", + ConVar = "wire_cd_ray_Model", + Category = "Wire Data CD Ray", + Models = transmodels } ) + + panel:AddControl("Slider", { + Label = "CD Ray range", + Type = "Float", + Min = "1", + Max = "512", + Command = "wire_cd_ray_Range" + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_colorer.lua b/lua/weapons/gmod_tool/stools/wire_colorer.lua new file mode 100644 index 0000000000..4ceec85d24 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_colorer.lua @@ -0,0 +1,169 @@ +TOOL.Category = "Wire - Render" +TOOL.Name = "Colorer" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_colorer_name", "Colorer Tool (Wire)" ) + language.Add( "Tool_wire_colorer_desc", "Spawns a constant colorer prop for use with the wire system." ) + language.Add( "Tool_wire_colorer_0", "Primary: Create/Update Colorer" ) + language.Add( "WireColorerTool_colorer", "Colorer:" ) + language.Add( "WireColorerTool_outColor", "Output Color" ) + language.Add( "WireColorerTool_Range", "Max Range:" ) + language.Add( "WireColorerTool_Model", "Choose a Model:") + language.Add( "sboxlimit_wire_colorers", "You've hit Colorers limit!" ) + language.Add( "undone_Wire Colorer", "Undone Wire Colorer" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_colorers', 20) +end + +TOOL.ClientConVar[ "Model" ] = "models/jaanus/wiretool/wiretool_siren.mdl" +TOOL.ClientConVar[ "outColor" ] = "0" +TOOL.ClientConVar[ "Range" ] = "2000" + +local colormodels = { + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_beamcaster.mdl"] = {}}; + +cleanup.Register( "wire_colorers" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_colorer" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_colorers" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local outColor = (self:GetClientNumber( "outColor" ) ~= 0) + local range = self:GetClientNumber("Range") + local model = self:GetClientInfo("Model") + + local wire_colorer = MakeWireColorer( ply, trace.HitPos, Ang, model, outColor, range ) + + local min = wire_colorer:OBBMins() + wire_colorer:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_colorer, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Colorer") + undo.AddEntity( wire_colorer ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_colorers", wire_colorer ) + ply:AddCleanup( "wire_colorers", const ) + + return true +end + +if (SERVER) then + + function MakeWireColorer( pl, Pos, Ang, model, outColor, Range ) + if ( !pl:CheckLimit( "wire_colorers" ) ) then return false end + + local wire_colorer = ents.Create( "gmod_wire_colorer" ) + if (!wire_colorer:IsValid()) then return false end + + wire_colorer:SetAngles( Ang ) + wire_colorer:SetPos( Pos ) + wire_colorer:SetModel( model ) + wire_colorer:Spawn() + wire_colorer:Setup(outColor,Range) + + wire_colorer:SetPlayer( pl ) + + local ttable = { + outColor = outColor, + Range = Range, + pl = pl + } + table.Merge(wire_colorer:GetTable(), ttable ) + + pl:AddCount( "wire_colorers", wire_colorer ) + + return wire_colorer + end + + duplicator.RegisterEntityClass("gmod_wire_colorer", MakeWireColorer, "Pos", "Ang", "Model", "outColor", "Range") + +end + +function TOOL:UpdateGhostWireColorer( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_colorer" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model"), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireColorer( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_colorer_name", Description = "#Tool_wire_colorer_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_colorer", + + Options = { + Default = { + wire_colorer_outColor = "0", + } + }, + CVars = { + [0] = "wire_colorer_outColor" + } + }) + + panel:AddControl( "PropSelect", { Label = "#WireColorerTool_Model", + ConVar = "wire_colorer_Model", + Category = "Wire Colorers", + Models = colormodels } ) + + panel:AddControl("CheckBox", { + Label = "#WireColorerTool_outColor", + Command = "wire_colorer_outColor" + }) + + panel:AddControl("Slider", { + Label = "#WireColorerTool_Range", + Type = "Float", + Min = "1", + Max = "10000", + Command = "wire_colorer_Range" + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_cpu.lua b/lua/weapons/gmod_tool/stools/wire_cpu.lua new file mode 100644 index 0000000000..d57a7b3d11 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_cpu.lua @@ -0,0 +1,638 @@ +TOOL.Category = "Wire - Control" +TOOL.Name = "Chip - CPU" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if (CLIENT) then + language.Add("Tool_wire_cpu_name", "CPU Tool (Wire)") + language.Add("Tool_wire_cpu_desc", "Spawns a central processing unit") + language.Add("Tool_wire_cpu_0", "Primary: Create / Update CPU, Secondary: Open editor")//; Secondary: Debug the CPU + language.Add("sboxlimit_wire_cpu", "You've hit CPU limit!") + language.Add("undone_wirecpu", "Undone the wire CPU") + language.Add( "ToolWirecpu_Model", "Model:" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_cpus', 20) +end + +TOOL.ClientConVar = { + model = "models/cheeze/wires/cpu.mdl", + filename = "", + packet_bandwidth = 300, + packet_rate_sp = 0.05, + packet_rate_mp = 0.4, + compile_rate = 0.05, + compile_bandwidth = 200, + rom = 1, + rom_present = 1, + dump_data = 0 +} + +cleanup.Register("wire_cpus") + +//============================================================================= +if (SERVER) then + CPU_SourceCode = {} + + local function AddSourceLine(pl, command, args) + CPU_SourceCode[tonumber(args[1])] = tostring(args[2]) + end + concommand.Add("wire_cpu_addsrc", AddSourceLine) + + local function ClearSource(pl, command, args) + CPU_SourceCode = {} + end + concommand.Add("wire_cpu_clearsrc", ClearSource) +end +//============================================================================= + +local function CPUStool_Version() + local SVNString = "$Revision$" + local rev = tonumber(string.sub(SVNString,12,14)) + if (rev) then + return rev + else + return 0 + end +end + +//============================================================================= + +local function CompileProgram_Timer(tool,firstpass) + if (firstpass && tool.FirstPassDone) then return end + if (!firstpass && tool.SecondPassDone) then return end + if (!tool:GetOwner()) then return end + if (!tool.LineNumber) then return end + + local SendLinesMax = tool.LineNumber + tool:GetOwner():GetInfo("wire_cpu_compile_bandwidth") + if (SendLinesMax > table.Count(CPU_SourceCode)) then SendLinesMax = table.Count(CPU_SourceCode) end + local Rate = 0 + + if (CPU_SourceCode[tostring(tool.LineNumber)]) then + if (string.len(CPU_SourceCode[tostring(tool.LineNumber)]) > 256) then + SendLinesMax = tool.LineNumber + end + end + + while (tool.LineNumber <= SendLinesMax) and (tool.CPU_Entity) do + local line = CPU_SourceCode[tonumber(tool.LineNumber)] + if (line) then + if (string.len(line) > 254) then + tool:GetOwner():PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Line "..tool.LineNumber.." too long! I compile it, but it may trigger infinite loop thing.\n") + end + if (tool.CPU_Entity.ParseProgram_ASM) then + tool.CPU_Entity:ParseProgram_ASM(line,tool.LineNumber) + end + end + + tool.LineNumber = tool.LineNumber + 1 + Rate = Rate + 1 + end + + local TimeLeft = (table.Count(CPU_SourceCode)*2 - tool.LineNumber) / Rate + if (not firstpass) then + TimeLeft = (table.Count(CPU_SourceCode) - tool.LineNumber) / Rate + end + tool.PrevRate = (tool.PrevRate*1.5+TimeLeft*0.5) / 2 + TimeLeft = math.floor(tool.PrevRate / 10) + + local TempPercent = ((tool.LineNumber-1)/table.Count(CPU_SourceCode))*100 + if (firstpass) then + if (!tool.FirstPassDone) then + tool:GetOwner():ConCommand('wire_cpu_vgui_status "Compiling ('.. TimeLeft ..' seconds left), '..tool.LineNumber..' lines processed"') + tool:GetOwner():ConCommand('wire_cpu_vgui_progress "'..math.floor(TempPercent/2)..'"') + end + else + if (!tool.SecondPassDone) then + tool:GetOwner():ConCommand('wire_cpu_vgui_status "Compiling ('.. TimeLeft ..' seconds left), '..tool.LineNumber..' lines processed"') + tool:GetOwner():ConCommand('wire_cpu_vgui_progress "'..math.floor(50+TempPercent/2)..'"') + end + end + + if (tool.LineNumber > table.Count(CPU_SourceCode)) || (TempPercent >= 100) then + if (!tool.FirstPassDone) then + tool.FirstPassDone = true + tool:Compile_Pass2() + end + if (!firstpass) && (!tool.SecondPassDone) then + tool.SecondPassDone = true + tool:Compile_End() + end + end + + if (tool.CPU_Entity.FatalError == true) then + timer.Destroy("CPUCompileTimer1") + timer.Destroy("CPUCompileTimer2") + tool:Compile_End() + end +end + +//============================================================================= + +-- TODO: shouldn't this take ent instead of pl? since pl = self:GetOwner() +function TOOL:StartCompile(pl) + local ent = self.CPU_Entity + if table.Count(CPU_SourceCode) == 0 then return end + + pl:PrintMessage(HUD_PRINTCONSOLE,"----> ZyeliosASM compiler - Version 2.0 (SVN REV "..CPUStool_Version().."/"..ent:CPUID_Version()..") <----\n") + pl:PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Compiling...\n") + + pl:ConCommand('wire_cpu_vgui_open') + pl:ConCommand('wire_cpu_vgui_title "ZyeliosASM - Compiling"') + pl:ConCommand('wire_cpu_vgui_status "Initializing"') + pl:ConCommand('wire_cpu_vgui_progress "0"') + + ent.UseROM = self:GetClientInfo("rom") == "1" + + if (self:GetClientInfo("dump_data") == "1") then + ent.MakeDump = true + ent.Dump = "Code listing:\n" + else + ent.MakeDump = false + end + + + self.FirstPassDone = false + self.SecondPassDone = false + + timer.Destroy("CPUCompileTimer1") + timer.Destroy("CPUCompileTimer2") + + ent:Compiler_Stage0(pl) + self:Compile_Pass1() +end + +function TOOL:Compile_Pass1() + if (!self:GetOwner()) then return end + self:GetOwner():PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Pass 1\n") + + self.Compiling = true + self.CPU_Entity:Compiler_Stage1() + + self.LineNumber = 1 + self.PrevRate = 0 + timer.Create("CPUCompileTimer1",self:GetOwner():GetInfo("wire_cpu_compile_rate"),0,CompileProgram_Timer,self,true) +end + +function TOOL:Compile_Pass2() + if (!self:GetOwner()) then return end + self:GetOwner():PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Pass 2\n") + + self.Compiling = true + self.CPU_Entity:Compiler_Stage2() + + self.LineNumber = 1 + timer.Create("CPUCompileTimer2",self:GetOwner():GetInfo("wire_cpu_compile_rate"),0,CompileProgram_Timer,self,false) +end + + +function TOOL:Compile_End() + local pl = self:GetOwner() + local ent = self.CPU_Entity + + if (ent.FatalError) then + pl:PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Compile aborted: fatal error has occured\n") + else + pl:PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Compile succeded! "..(table.Count(CPU_SourceCode)-1).." lines, "..ent.WIP.." bytes, "..table.Count(ent.Labels).." definitions.\n") + end + + pl:ConCommand('wire_cpu_vgui_close') + + if ((self:GetClientInfo("dump_data") == "1") && (SinglePlayer())) then + pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumping data\n") + local codedump = "" + for i = 0,ent.WIP do + if (ent.Memory[i]) then + codedump = codedump.."db "..ent.Memory[i].."\n" + end + end + file.Write("cdump.txt",codedump) + file.Write("ldump.txt",ent.Dump) + pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumped!\n") + end + + ent:Reset() + ent.Compiling = false +end + +local last_error = "Press Ctrl-Space to go to the last CPU/GPU error." + +usermessage.Hook("wire_cpu_error", function(um) + last_error = um:ReadString() +end) + +function wire_cpu_validate(buffer) + return last_error +end + +//============================================================================= +function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_cpu" && trace.Entity.pl == ply) then + trace.Entity.Memory = {} + trace.Entity.ROMMemory = {} + trace.Entity.PrecompileData = {} + trace.Entity.PrecompileMemory = {} + return true + end +end + +function TOOL:LeftClick(trace) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + self.CPU_Entity = trace.Entity + + if not trace.Entity:IsValid() or trace.Entity:GetClass() ~= "gmod_wire_cpu" or trace.Entity.pl ~= ply then + + if (!self:GetSWEP():CheckLimit("wire_cpus")) then return false end + if (not util.IsValidModel(self:GetClientInfo("model"))) then return false end + if (not util.IsValidProp(self:GetClientInfo("model"))) then return false end + + local ang = trace.HitNormal:Angle() + local model = self:GetClientInfo("model") + ang.pitch = ang.pitch + 90 + + wire_cpu = MakeWireCpu(ply, trace.HitPos, ang, model) + local min = wire_cpu:OBBMins() + wire_cpu:SetPos(trace.HitPos - trace.HitNormal * min.z) + + local const = WireLib.Weld(wire_cpu, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire CPU") + undo.AddEntity(wire_cpu) + undo.AddEntity(const) + undo.SetPlayer(ply) + undo.Finish() + + ply:AddCleanup("wire_cpus", wire_cpu) + ply:AddCleanup("wire_cpus", const) + + self.CPU_Entity = wire_cpu + end + + self:StartCompile(ply) + return true +end + +function TOOL:RightClick(trace) + if SERVER then self:GetOwner():SendLua("wire_cpu_OpenEditor()") end +end + +if (SERVER) then + function MakeWireCpu(pl, Pos, Ang, model) + if (!pl:CheckLimit("wire_cpus")) then return false end + + local wire_cpu = ents.Create("gmod_wire_cpu") + if (!wire_cpu:IsValid()) then return false end + wire_cpu:SetModel(model) + + wire_cpu:SetAngles(Ang) + wire_cpu:SetPos(Pos) + wire_cpu:Spawn() + + wire_cpu:SetPlayer(pl) + + local ttable = { + pl = pl, + } + table.Merge(wire_cpu:GetTable(), ttable) -- TODO: remove maybe? + pl:AddCount("wire_cpus", wire_cpu) + + return wire_cpu + end + duplicator.RegisterEntityClass("gmod_wire_cpu", MakeWireCpu, "Pos", "Ang", "Model") +end + +function TOOL:UpdateGhostWireCpu(ent, player) + if (!ent) then return end + if (!ent:IsValid()) then return end + + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_cpu" || trace.Entity:IsPlayer()) then + ent:SetNoDraw(true) + return + end + + local ang = trace.HitNormal:Angle() + ang.pitch = ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos(trace.HitPos - trace.HitNormal * min.z) + ent:SetAngles(ang) + + ent:SetNoDraw(false) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("model") || (not self.GhostEntity:GetModel())) then + self:MakeGhostEntity(self:GetClientInfo("model"), Vector(0,0,0), Angle(0,0,0)) + end + + self:UpdateGhostWireCpu(self.GhostEntity, self:GetOwner()) +end + +//============================================================================= +// Code sending +//============================================================================= +if (CLIENT) then + local Frame + local StatusLabel + local PLabel + local ProgressBar + local BGBar + + local function VGUI_Open(pl, command, args) + if (Frame) then + Frame:SetVisible(false) + end + + Frame = vgui.Create("Panel") + Frame:SetSize(400,50) + Frame:SetPos(150,150) + Frame:SetVisible(true) + + BGBar = vgui.Create("ProgressBar",Frame) + BGBar:SetVisible(true) + BGBar:SetSize(400,100) + BGBar:SetPos(0,0) + + StatusLabel = vgui.Create("Label",Frame) + StatusLabel:SetSize(380,30) + StatusLabel:SetPos(10,10) + StatusLabel:SetVisible(true) + + PLabel = vgui.Create("Label",Frame) + PLabel:SetSize(30,30) + PLabel:SetPos(360,10) + PLabel:SetVisible(true) + + ProgressBar = vgui.Create("ProgressBar",Frame) + ProgressBar:SetSize(280,30) + ProgressBar:SetPos(10,60) + ProgressBar:SetVisible(false) + end + concommand.Add("wire_cpu_vgui_open", VGUI_Open) + + local function VGUI_Close(pl, command, args) + Frame:SetVisible(false); + end + concommand.Add("wire_cpu_vgui_close", VGUI_Close) + + local function VGUI_Title(pl, command, args) + Frame:PostMessage("SetTitle", "text", args[1]); + end + concommand.Add("wire_cpu_vgui_title", VGUI_Title) + + local function VGUI_Status(pl, command, args) + StatusLabel:PostMessage("SetText", "text", args[1]); + end + concommand.Add("wire_cpu_vgui_status", VGUI_Status) + + local function VGUI_Progress(pl, command, args) + if (args[1]) then + ProgressBar:PostMessage("SetValue", "Float", tonumber(args[1])/100); + PLabel:PostMessage("SetText", "text", args[1] .. "%"); + end + end + concommand.Add("wire_cpu_vgui_progress", VGUI_Progress) +end + +if (CLIENT) then + + SourceLines = {} + SourceLineNumbers = {} + SourceLinesSent = 0 + SourcePrevCharRate = 0 + SourceTotalChars = 0 + SourceLoadedChars = 0 + + function wire_cpu_OpenEditor() + if not CPU_Editor then + CPU_Editor = vgui.Create( "Expression2EditorFrame") + CPU_Editor:Setup("CPU Editor", "CPUChip", "CPU") + end + CPU_Editor:Open() + end + + function CPU_UploadProgram(pl) + local SendLinesMax = SourceLinesSent + pl:GetInfo("wire_cpu_packet_bandwidth") + local TotalChars = 0 + if SendLinesMax > table.Count(SourceLines) then + SendLinesMax = table.Count(SourceLines) + end + + while (SourceLinesSent <= SendLinesMax) && (TotalChars < 1024) do + SourceLinesSent = SourceLinesSent + 1 + local line = SourceLines[SourceLinesSent] + local linen = SourceLinesSent + + if (line) && (line ~= "\n") && (string.gsub(line, "\n", "") ~= "") then + RunConsoleCommand("wire_cpu_addsrc",linen,string.gsub(line, "\n", "")) + TotalChars = TotalChars + string.len(line) + else + RunConsoleCommand("wire_cpu_addsrc",linen,"") + end + end + SourceLoadedChars = SourceLoadedChars + TotalChars + + local CharRate = (SourcePrevCharRate*1.95 + TotalChars*0.05) / 2 + SourcePrevCharRate = CharRate + + if SinglePlayer() then + CharRate = CharRate / pl:GetInfo("wire_cpu_packet_rate_sp") + else + CharRate = CharRate / pl:GetInfo("wire_cpu_packet_rate_mp") + end + + local TimeLeft = math.floor((SourceTotalChars - SourceLoadedChars) / CharRate) + local TempPercent = math.floor(((SourceLinesSent-1)/table.Count(SourceLines))*100) + + pl:ConCommand('wire_cpu_vgui_status "Uploading @ '..math.floor(CharRate / 1024)..' kb/sec, avg. '..TimeLeft..' sec left, '..SourceLinesSent..' lines sent"') + pl:ConCommand('wire_cpu_vgui_progress "'..TempPercent..'"') + + if (SourceLinesSent > table.Count(SourceLines)) then + pl:ConCommand('wire_cpu_vgui_close') + timer.Remove("CPUSendTimer") + end + end + + function CPU_LoadProgram(pl, command, args) + local fname = "CPUChip\\"..pl:GetInfo("wire_cpu_filename") + if (!file.Exists(fname)) then + fname = "CPUChip\\"..pl:GetInfo("wire_cpu_filename")..".txt" + end + + if (!file.Exists(fname)) then + pl:PrintMessage(HUD_PRINTTALK,"CPU -> Sorry! Requested file was not found\n") + return + end + + pl:ConCommand('wire_cpu_clearsrc') + + local filedata = file.Read(fname) + if (!filedata) then + pl:PrintMessage(HUD_PRINTTALK,"CPU -> Sorry! File was found, but leprechauns prevented it from getting read!\n") //This message occurs rarely enough to put something fun here + return + end + + SourceLines = string.Explode("\n", filedata) + SourceLinesSent = 0 + SourceTotalChars = string.len(filedata) + + //Parse include files + if (string.find(filedata,"##include##", 1, true)) then + for i=1,#SourceLines do + if (string.sub(SourceLines[i],1,12) == "##include## ") then + local fname2 = string.sub(SourceLines[i],13) + if (file.Exists("CPUChip\\"..fname2)) then + SourceLines[i] = "asmfile "..fname.."\n"..file.Read("CPUChip\\"..fname2).."\nasmend\n" + else + SourceLines[i] = "" + end + end + end + + filedata = string.Implode("\n", SourceLines) + SourceLines = string.Explode("\n", filedata) + SourceLinesSent = 0 + SourceTotalChars = string.len(filedata) + end + + SourcePrevCharRate = string.len(SourceLines[1]) + SourceLoadedChars = 0 + + pl:ConCommand('wire_cpu_vgui_open') + pl:ConCommand('wire_cpu_vgui_title "CPU - Uploading program"') + pl:ConCommand('wire_cpu_vgui_status "Initializing"') + pl:ConCommand('wire_cpu_vgui_progress "0"') + + //Send 50 lines + if (SinglePlayer()) then + timer.Create("CPUSendTimer",pl:GetInfo("wire_cpu_packet_rate_sp"),0,CPU_UploadProgram,pl,false) + else + timer.Create("CPUSendTimer",pl:GetInfo("wire_cpu_packet_rate_mp"),0,CPU_UploadProgram,pl,false) + end + end + +end + +local function LoadProgram(pl, command, args) + if (SERVER) then + pl:SendLua("CPU_LoadProgram(LocalPlayer())") + else + CPU_LoadProgram(pl, command, args) + end +end +concommand.Add("wire_cpu_load", LoadProgram) + +local function ClearProgram(pl, command, args) + pl:ConCommand('wire_cpu_clearsrc') +end +concommand.Add("wire_cpu_clear", ClearProgram) + +//end + +//============================================================================= +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_cpu_name", Description = "#Tool_wire_cpu_desc" }) + + panel:AddControl("TextBox", { + Label = "Source code file name", + Command = "wire_cpu_filename", + MaxLength = "128" + }) + + panel:AddControl("Button", { + Text = "Load into compiler", + Name = "Load", + Command = "wire_cpu_load" + }) + local New = vgui.Create("DButton" , panel) + panel:AddPanel(New) + New:SetText("New file") + New.DoClick = function(button) + wire_cpu_OpenEditor() + CPU_Editor:AutoSave() + CPU_Editor:ChosenFile() + CPU_Editor:SetCode("\n\n") + end + + panel:AddControl("Label", {Text = ""}) + + //panel:AddControl("Button", { + // Text = "Code editor" + //}) + + local OpenEditor = vgui.Create("DButton", panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetText("Code Editor") + OpenEditor.DoClick = wire_cpu_OpenEditor + + panel:AddControl("Label", {Text = ""}) + + panel:AddControl("Label", { + Text = "CPU settings:" + }) + + + panel:AddControl("CheckBox", { + Label = "Use CPU ROM", + Command = "wire_cpu_rom" + }) + panel:AddControl("Label", { + Text = "ROM data is saved with advanced duplicator and is stored between CPU resets" + }) + + panel:AddControl("ComboBox", { + Label = "CPU Model", + Options = { + ["AMD64"] = { wire_cpu_model = "models/cheeze/wires/cpu.mdl" }, + ["AMD64 Mini"] = { wire_cpu_model = "models/cheeze/wires/mini_cpu.mdl" }, + ["WireCPU"] = { wire_cpu_model = "models/cheeze/wires/cpu2.mdl" }, + ["WireCPU Mini"] = { wire_cpu_model = "models/cheeze/wires/mini_cpu2.mdl"}, + + } + }) + + panel:AddControl("Label", {Text = ""}) + + panel:AddControl("Label", { + Text = "These do not work yet:" + }) + + panel:AddControl("CheckBox", { + Label = "CPU ROM Present", + Command = "wire_cpu_rom_present" + }) + panel:AddControl("Label", { + Text = "CPU can be without internal ROM/RAM (you need to attach RAM/ROM manually)" + }) + + + panel:AddControl("CheckBox", { + Label = "Dump CPU data", + Command = "wire_cpu_dump_data" + }) + panel:AddControl("Label", { + Text = "Dumps CPU information and compiled code to pdump/cdump/ldump files in DATA folder (server host, or singleplayer only)" + }) + + + panel:AddControl("Button", { + Text = "ZCPU documentation (online)" + }) + panel:AddControl("Label", { + Text = "Loads online CPU documentation and tutorials" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_damage_detector.lua b/lua/weapons/gmod_tool/stools/wire_damage_detector.lua new file mode 100644 index 0000000000..ab8d62d222 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_damage_detector.lua @@ -0,0 +1,160 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "Damage Detector" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_damage_detector_name", "Damage Detector Tool (Wire)" ) + language.Add( "Tool_wire_damage_detector_desc", "Spawns a damage detector for use with the wire system" ) + language.Add( "Tool_wire_damage_detector_0", "Primary: Create/Update Detector, Secondary: Link Detector to an entity" ) + language.Add( "Tool_wire_damage_detector_1", "Now select the entity to link to." ) + language.Add( "WireDamageDetectorTool_includeconstrained", "Include Constrained Props" ) + language.Add( "sboxlimit_wire_damage_detectors", "You've hit damage detectors limit!" ) + language.Add( "undone_Wire Damage Detector", "Undone Wire Damage Detector" ) +end + +if ( SERVER ) then + CreateConVar('sbox_maxwire_damage_detectors', 20) + CreateConVar('sbox_wire_damage_detectors_includeconstrained',0) +end + +TOOL.ClientConVar[ "includeconstrained" ] = "0" +TOOL.ClientConVar[ "Model" ] = "models/jaanus/wiretool/wiretool_siren.mdl" + +cleanup.Register( "wire_damage_detectors" ) + +function TOOL:LeftClick( trace ) + if !trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if ( CLIENT ) then return true end + + self:SetStage(0) + + local model = self:GetClientInfo( "Model" ) + local ply = self:GetOwner() + local includeconstrained = self:GetClientNumber( "includeconstrained" ) + + if ( trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_damage_detector" and trace.Entity:GetTable().pl == ply ) then + -- Update the detector's settings + trace.Entity:Setup( includeconstrained ) + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_damage_detectors" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_damage_detector = MakeWireDamageDetector( ply, trace.HitPos, Ang, model, includeconstrained ) + + local min = wire_damage_detector:OBBMins() + wire_damage_detector:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_damage_detector, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Damage Detector") + undo.AddEntity( wire_damage_detector ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_damage_detectors", wire_damage_detector ) + + return true +end + +function TOOL:RightClick( trace ) + if ( !trace.HitPos ) then return false end + if ( trace.Entity:IsPlayer() ) then return false end + if ( CLIENT ) then return true end + + if ( ValidEntity(trace.Entity) ) then + if ( self:GetStage() == 0 and trace.Entity:GetClass() == "gmod_wire_damage_detector" ) then + self.detector = trace.Entity + self:SetStage(1) + return true + elseif ( self:GetStage() == 1 ) then + self.detector:LinkEntity( trace.Entity ) + self:SetStage(0) + self:GetOwner():PrintMessage( HUD_PRINTTALK,"Damage Detector linked" ) + return true + else + self:SetStage(0) + self:GetOwner():PrintMessage( HUD_PRINTTALK,"Invalid Target" ) + return false + end + end +end + +if ( SERVER ) then + + function MakeWireDamageDetector( pl, Pos, Ang, model, includeconstrained ) + if ( !pl:CheckLimit( "wire_damage_detectors" ) ) then return false end + + local wire_damage_detector = ents.Create( "gmod_wire_damage_detector" ) + if (!wire_damage_detector:IsValid()) then return false end + + wire_damage_detector:SetAngles( Ang ) + wire_damage_detector:SetPos( Pos ) + wire_damage_detector:SetModel( model ) + wire_damage_detector:Spawn() + + wire_damage_detector:Setup( includeconstrained ) + wire_damage_detector:LinkEntity( wire_damage_detector ) -- Link the detector to itself by default + + wire_damage_detector:GetTable():SetPlayer( pl ) + + local ttable = { + includeconstrained = includeconstrained, + pl = pl + } + table.Merge(wire_damage_detector:GetTable(), ttable ) + + pl:AddCount( "wire_damage_detectors", wire_damage_detector ) + + return wire_damage_detector + end + + duplicator.RegisterEntityClass("gmod_wire_damage_detector", MakeWireDamageDetector, "Pos", "Ang", "Model", "includeconstrained") + +end + +function TOOL:UpdateGhostWireDamageDetector( ent, player ) + if ( !ent or !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit or trace.Entity:IsPlayer() or trace.Entity:GetClass() == "gmod_wire_damage_detector" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity or !self.GhostEntity:IsValid() or self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model"), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireDamageDetector( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_damage_detector_name", Description = "#Tool_wire_damage_detector_desc" }) + + panel:AddControl("CheckBox", { + Label = "#WireDamageDetectorTool_includeconstrained", + Command = "wire_damage_detector_includeconstrained" + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_data_satellitedish.lua b/lua/weapons/gmod_tool/stools/wire_data_satellitedish.lua new file mode 100644 index 0000000000..4714b4b5b0 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_data_satellitedish.lua @@ -0,0 +1,197 @@ +TOOL.Category = "Wire - Data" +TOOL.Name = "Satellite Dish" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_data_satellitedish_name", "Satellite Dish Tool (Wire)" ) + language.Add( "Tool_wire_data_satellitedish_desc", "Spawns a Satellite Dish." ) + language.Add( "Tool_wire_data_satellitedish_0", "Primary: Create Satellite Dish/Display Link Info, Secondary: Link/Unlink Satellite Dish, Reload: Change model" ) + language.Add( "Tool_wire_data_satellitedish_1", "Now select the transmitter to link to" ) + language.Add( "WireDataTransfererTool_data_satellitedish", "Satellite Dish:" ) + language.Add( "sboxlimit_wire_data_satellitedishs", "You've hit Satellite Dishs limit!" ) + language.Add( "undone_Wire Data Satellite Dish", "Undone Wire Satellite Dish" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_data_satellitedishs', 20) +end + +TOOL.ClientConVar["Model"] = "models/kobilica/wiremonitorrtbig.mdl" + +TOOL.FirstSelected = nil + +cleanup.Register( "wire_data_satellitedishs" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + self:SetStage(0) + + if ( trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_data_satellitedish" and trace.Entity:GetTable().pl == ply ) then + local satellite_dish = trace.Entity + if IsValid(satellite_dish.Transmitter) then + self:GetWeapon():SetNetworkedEntity( "WireSatelliteDishTransmitter", satellite_dish.Transmitter ) + self:GetWeapon():SetNetworkedEntity( "WireSatelliteDish", satellite_dish ) + return true + else + ply:PrintMessage( HUD_PRINTTALK, "Satellite Dish not linked" ) + return false + end + end + + if ( !self:GetSWEP():CheckLimit( "wire_data_satellitedishs" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_data_satellitedish = MakeWireSatellitedish( ply, trace.HitPos, Ang, self:GetClientInfo("Model")) + + local min = wire_data_satellitedish:OBBMins() + wire_data_satellitedish:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_data_satellitedish, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Data Satellite Dish") + undo.AddEntity( wire_data_satellitedish ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_data_satellitedishs", wire_data_satellitedish ) + ply:AddCleanup( "wire_data_satellitedishs", const ) + + return true +end + +function TOOL:RightClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() ) then + if ( self:GetStage() == 0 and trace.Entity:GetClass() == "gmod_wire_data_satellitedish" ) then + self.selected_dish = trace.Entity + self:SetStage(1) + return true + elseif ( self:GetStage() == 1 ) then + self:SetStage(0) + if trace.Entity == self.selected_dish then + ply:PrintMessage( HUD_PRINTTALK, "Satellite Dish unlinked" ) + self.selected_dish.Transmitter = null + self.selected_dish:ShowOutput() + self:GetWeapon():SetNetworkedEntity( "WireSatelliteDishTransmitter", self.selected_dish ) + self:GetWeapon():SetNetworkedEntity( "WireSatelliteDish", self.selected_dish ) --Set both to same point so line won't draw + return true + elseif trace.Entity:GetClass() != "gmod_wire_data_transferer" then + ply:PrintMessage( HUD_PRINTTALK, "Satellite Dishes can only be linked to Wire Data Transferers!" ) + return true + end + + self.selected_dish.Transmitter = trace.Entity + self.selected_dish:ShowOutput() + self:SetStage(0) + self:GetOwner():PrintMessage( HUD_PRINTTALK,"Satellite Dish linked" ) + self:GetWeapon():SetNetworkedEntity( "WireSatelliteDishTransmitter", self.selected_dish.Transmitter ) + self:GetWeapon():SetNetworkedEntity( "WireSatelliteDish", self.selected_dish ) + return true + else + return false + end + end +end + +function TOOL:Reload( trace ) + if (CLIENT) then return true end + + local ply = self:GetOwner() + + if (trace.Entity and trace.Entity:IsValid()) then + if (trace.Entity:GetClass() == "prop_physics") then + ply:ConCommand('wire_data_satellitedish_Model "'..trace.Entity:GetModel()..'"\n') + ply:PrintMessage( HUD_PRINTTALK, "Satellite Dish model set to "..trace.Entity:GetModel() ) + else + ply:PrintMessage( HUD_PRINTTALK, "Satellite Dishes only accept physics models!" ) + end + end + + return true +end + +function TOOL:DrawHUD() + local selected_dish = self:GetWeapon():GetNetworkedEntity( "WireSatelliteDishTransmitter" ) + local transmitter = self:GetWeapon():GetNetworkedEntity( "WireSatelliteDish" ) + if ( !IsValid(transmitter) or !IsValid(selected_dish) ) then return end + + local selected_dish_pos = selected_dish:GetPos():ToScreen() + local transmitter_pos = transmitter:GetPos():ToScreen() + if ( transmitter_pos.x > 0 and transmitter_pos.y > 0 and transmitter_pos.x < ScrW() and transmitter_pos.y < ScrH() ) then + surface.SetDrawColor( 255, 255, 100, 255 ) + surface.DrawLine(selected_dish_pos.x, selected_dish_pos.y, transmitter_pos.x, transmitter_pos.y) + end +end + +if (SERVER) then + + function MakeWireSatellitedish( pl, Pos, Ang, model ) + if ( !pl:CheckLimit( "wire_data_satellitedishs" ) ) then return false end + + local wire_data_satellitedish = ents.Create( "gmod_wire_data_satellitedish" ) + if (!wire_data_satellitedish:IsValid()) then return false end + + wire_data_satellitedish:SetAngles( Ang ) + wire_data_satellitedish:SetPos( Pos ) + wire_data_satellitedish:SetModel( model ) + wire_data_satellitedish:Spawn() + + wire_data_satellitedish:SetPlayer( pl ) + wire_data_satellitedish.pl = pl + + pl:AddCount( "wire_data_satellitedishs", wire_data_satellitedish ) + + return wire_data_satellitedish + end + + duplicator.RegisterEntityClass("gmod_wire_data_satellitedish", MakeWireSatellitedish, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireSatellitedish( ent, player ) + if ( !ent or !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit or trace.Entity:IsPlayer() or trace.Entity:GetClass() == "gmod_wire_data_satellitedish" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity or !self.GhostEntity:IsValid() or self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model"), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireSatellitedish( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_data_satellitedish_name", Description = "#Tool_wire_data_satellitedish_desc" }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_data_store.lua b/lua/weapons/gmod_tool/stools/wire_data_store.lua new file mode 100644 index 0000000000..d5e531dc52 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_data_store.lua @@ -0,0 +1,115 @@ +TOOL.Category = "Wire - Data" +TOOL.Name = "Store" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_data_store_name", "Data Store Tool (Wire)" ) + language.Add( "Tool_wire_data_store_desc", "Spawns a data store." ) + language.Add( "Tool_wire_data_store_0", "Primary: Create/Update data store" ) + language.Add( "WireDataStoreTool_data_store", "Data Store:" ) + language.Add( "sboxlimit_wire_data_stores", "You've hit data stores limit!" ) + language.Add( "undone_Wire Data Store", "Undone Wire data store" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_data_stores', 20) +end + +TOOL.Model = "models/jaanus/wiretool/wiretool_range.mdl" + +cleanup.Register( "wire_data_stores" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_data_store" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_data_stores" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_data_store = MakeWireStore( ply, trace.HitPos, Ang, self.Model ) + + local min = wire_data_store:OBBMins() + wire_data_store:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_data_store, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Data Store") + undo.AddEntity( wire_data_store ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_data_stores", wire_data_store ) + ply:AddCleanup( "wire_data_stores", const ) + + return true +end + +if (SERVER) then + + function MakeWireStore( pl, Pos, Ang, model ) + if ( !pl:CheckLimit( "wire_data_stores" ) ) then return false end + + local wire_data_store = ents.Create( "gmod_wire_data_store" ) + if (!wire_data_store:IsValid()) then return false end + + wire_data_store:SetAngles( Ang ) + wire_data_store:SetPos( Pos ) + wire_data_store:SetModel( Model(model or "models/jaanus/wiretool/wiretool_range.mdl") ) + wire_data_store:Spawn() + + wire_data_store:SetPlayer( pl ) + wire_data_store.pl = pl + + pl:AddCount( "wire_data_stores", wire_data_store ) + + return wire_data_store + end + + duplicator.RegisterEntityClass("gmod_wire_data_store", MakeWireStore, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireStore( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_data_store" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireStore( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_data_store_name", Description = "#Tool_wire_data_store_desc" }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_data_transferer.lua b/lua/weapons/gmod_tool/stools/wire_data_transferer.lua new file mode 100644 index 0000000000..1be7a0ca96 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_data_transferer.lua @@ -0,0 +1,173 @@ +TOOL.Category = "Wire - Data" +TOOL.Name = "Transferer" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_data_transferer_name", "Data Transferer Tool (Wire)" ) + language.Add( "Tool_wire_data_transferer_desc", "Spawns a data transferer." ) + language.Add( "Tool_wire_data_transferer_0", "Primary: Create/Update data transferer" ) + language.Add( "WireDataTransfererTool_data_transferer", "Data Transferer:" ) + language.Add( "WireDataTransfererTool_Range", "Max Range:" ) + language.Add( "WireDataTransfererTool_DefaultZero","Default To Zero") + language.Add( "WireDataTransfererTool_IgnoreZero","Ignore Zero") + language.Add( "WireDataTransfererTool_Model", "Choose a Model:") + language.Add( "sboxlimit_wire_data_transferers", "You've hit the data transferers limit!" ) + language.Add( "undone_Wire Data Transferer", "Undone Wire data transferer" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_data_transferers', 20) +end + +TOOL.ClientConVar = { + Model = "models/jaanus/wiretool/wiretool_siren.mdl", + Range = "25000", + DefaultZero = 0, + IgnoreZero = 0, +} + +local transmodels = { + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_beamcaster.mdl"] = {}, +} + +cleanup.Register( "wire_data_transferers" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_data_transferer" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_data_transferers" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local range = self:GetClientNumber("Range") + local defZero = (self:GetClientNumber("DefaultZero") ~= 0) + local ignZero = (self:GetClientNumber("IgnoreZero") ~= 0) + local model = self:GetClientInfo("Model") + + local wire_data_transferer = MakeWireTransferer( ply, trace.HitPos, Ang, model, range, defZero, ignZero ) + + local min = wire_data_transferer:OBBMins() + wire_data_transferer:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_data_transferer, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Data Transferer") + undo.AddEntity( wire_data_transferer ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_data_transferers", wire_data_transferer ) + ply:AddCleanup( "wire_data_transferers", const ) + + return true +end + +if (SERVER) then + + function MakeWireTransferer( pl, Pos, Ang, model, Range, DefaultZero, IgnoreZero ) + if ( !pl:CheckLimit( "wire_data_transferers" ) ) then return false end + + local wire_data_transferer = ents.Create( "gmod_wire_data_transferer" ) + if (!wire_data_transferer:IsValid()) then return false end + + wire_data_transferer:SetAngles( Ang ) + wire_data_transferer:SetPos( Pos ) + wire_data_transferer:SetModel( model ) + wire_data_transferer:Spawn() + wire_data_transferer:Setup(Range,DefaultZero,IgnoreZero) + + wire_data_transferer:GetTable():SetPlayer( pl ) + + local ttable = { + Range = Range, + DefaultZero = DefaultZero, + IgnoreZero = IgnoreZero, + pl = pl + } + table.Merge(wire_data_transferer:GetTable(), ttable ) + + pl:AddCount( "wire_data_transferers", wire_data_transferer ) + + return wire_data_transferer + end + + duplicator.RegisterEntityClass("gmod_wire_data_transferer", MakeWireTransferer, "Pos", "Ang", "Model", "Range", "DefaultZero", "IgnoreZero") + +end + +function TOOL:UpdateGhostWireTransferer( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_data_transferer" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model"), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireTransferer( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_data_transferer_name", Description = "#Tool_wire_data_transferer_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_data_transferer", + + Options = { + Default = { + wire_data_transferer_data_transferer = "0", + } + }, + CVars = { + } + }) + + panel:AddControl( "PropSelect", { Label = "#WireDataTransfererTool_Model", + ConVar = "wire_data_transferer_Model", + Category = "Wire Data Transferer", + Models = transmodels } ) + + panel:AddControl("Slider", { + Label = "#WireDataTransfererTool_Range", + Type = "Float", + Min = "1", + Max = "1000000", + Command = "wire_data_transferer_Range" + }) + + panel:AddControl( "Checkbox", { Label = "#WireDataTransfererTool_DefaultZero", Command = "wire_data_transferer_DefaultZero" } ) + panel:AddControl( "Checkbox", { Label = "#WireDataTransfererTool_IgnoreZero", Command = "wire_data_transferer_IgnoreZero" } ) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_dataplug.lua b/lua/weapons/gmod_tool/stools/wire_dataplug.lua new file mode 100644 index 0000000000..7f5b907ba7 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_dataplug.lua @@ -0,0 +1,282 @@ +TOOL.Category = "Wire - Advanced" +TOOL.Name = "Data Plug" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_dataplug_name", "Data Plug Tool (Wire)" ) + language.Add( "Tool_wire_dataplug_desc", "Spawns plugs and sockets for use with the hi-speed wire system." ) + language.Add( "Tool_wire_dataplug_0", "Primary: Create/Update Socket Secondary: Create/Update Plug" ) + language.Add( "WirePlugTool_colour", "Colour:" ) + language.Add( "sboxlimit_wire_dataplugs", "You've hit plugs limit!" ) + language.Add( "sboxlimit_wire_datasockets", "You've hit sockets limit!" ) + language.Add( "undone_wiredataplug", "Undone Wire Data Plug" ) + language.Add( "undone_wiredatasocket", "Undone Wire Data Socket" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_dataplugs', 20) + CreateConVar('sbox_maxwire_datasockets', 20) +end + +TOOL.ClientConVar[ "a" ] = "0" +TOOL.ClientConVar[ "ar" ] = "255" +TOOL.ClientConVar[ "ag" ] = "255" +TOOL.ClientConVar[ "ab" ] = "255" +TOOL.ClientConVar[ "aa" ] = "255" + +TOOL.PlugModel = "models/hammy/pci_card.mdl" +TOOL.SocketModel = "models/hammy/pci_slot.mdl" + +cleanup.Register( "wire_dataplugs" ) + +// Create socket +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_dataplug") then + return false + end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + // Get client's CVars + local a = self:GetClientNumber("a") + local ar = math.min(self:GetClientNumber("ar"), 255) + local ag = math.min(self:GetClientNumber("ag"), 255) + local ab = math.min(self:GetClientNumber("ab"), 255) + local aa = math.min(self:GetClientNumber("aa"), 255) + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_datasocket" && trace.Entity.pl == ply ) then + trace.Entity:Setup(a,ar,ag,ab,aa) + + trace.Entity.a = a + trace.Entity.ar = ar + trace.Entity.ag = ag + trace.Entity.ab = ab + trace.Entity.aa = aa + trace.Entity.ReceivedValue = 0 + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_datasockets" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + local Pos = trace.HitPos + Ang.pitch = Ang.pitch + 90 + + local wire_datasocket = MakeWireDataSocket( ply, Pos, Ang, self.SocketModel, a, ar, ag, ab, aa ) + + local const = WireLib.Weld(wire_datasocket, trace.Entity, trace.PhysicsBone, true, false, true) + + undo.Create("WireSocket") + undo.AddEntity( wire_datasocket ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_datasockets", wire_datasocket ) + ply:AddCleanup( "wire_datasockets", const ) + + return true +end + +// Create plug +function TOOL:RightClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + // Get client's CVars + local a = self:GetClientNumber("a") + local ar = math.min(self:GetClientNumber("ar"), 255) + local ag = math.min(self:GetClientNumber("ag"), 255) + local ab = math.min(self:GetClientNumber("ab"), 255) + local aa = math.min(self:GetClientNumber("aa"), 255) + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_dataplug" && trace.Entity.pl == ply ) then + trace.Entity:Setup(a,ar,ag,ab,aa) + + trace.Entity.a = a + trace.Entity.ar = ar + trace.Entity.ag = ag + trace.Entity.ab = ab + trace.Entity.aa = aa + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_dataplugs" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + + local wire_dataplug = MakeWireDataPlug( ply, trace.HitPos, Ang, self.PlugModel, a, ar, ag, ab, aa ) + + local min = wire_dataplug:OBBMins() + wire_dataplug:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + undo.Create("WirePlug") + undo.AddEntity( wire_dataplug ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_dataplugs", wire_dataplug ) + + return true +end + +if (SERVER) then + + function MakeWireDataPlug( pl, Pos, Ang, model, a, ar, ag, ab, aa ) + if ( !pl:CheckLimit( "wire_dataplugs" ) ) then return false end + + local wire_dataplug = ents.Create( "gmod_wire_dataplug" ) + if (!wire_dataplug:IsValid()) then return false end + + wire_dataplug:SetAngles( Ang ) + wire_dataplug:SetPos( Pos ) + wire_dataplug:SetModel( Model(model or "models/hammy/pci_card.mdl") ) + wire_dataplug:Spawn() + + wire_dataplug:Setup( a, ar, ag, ab, aa) + wire_dataplug:SetPlayer( pl ) + + local ttable = { + a = a, + ar = ar, + ag = ag, + ab = ab, + aa = aa, + pl = pl, + MySocket = nil + } + table.Merge(wire_dataplug:GetTable(), ttable ) + + pl:AddCount( "wire_dataplug", wire_dataplug ) + + return wire_dataplug + end + + duplicator.RegisterEntityClass("gmod_wire_dataplug", MakeWireDataPlug, "Pos", "Ang", "Model", "a", "ar", "ag", "ab", "aa") + + + function MakeWireDataSocket( pl, Pos, Ang, model, a, ar, ag, ab, aa ) + if ( !pl:CheckLimit( "wire_datasockets" ) ) then return false end + + local wire_datasocket = ents.Create( "gmod_wire_datasocket" ) + if (!wire_datasocket:IsValid()) then return false end + + wire_datasocket:SetAngles( Ang ) + wire_datasocket:SetPos( Pos ) + wire_datasocket:SetModel( Model(model or "models/hammy/pci_slot.mdl") ) + wire_datasocket:Spawn() + + wire_datasocket:Setup( a, ar, ag, ab, aa) + wire_datasocket:SetPlayer( pl ) + + local ttable = { + a = a, + ar = ar, + ag = ag, + ab = ab, + aa = aa, + pl = pl, + ReceivedValue = 0 + } + table.Merge(wire_datasocket:GetTable(), ttable ) + + pl:AddCount( "wire_datasocket", wire_datasocket ) + + return wire_datasocket + end + + duplicator.RegisterEntityClass("gmod_wire_datasocket", MakeWireDataSocket, "Pos", "Ang", "Model", "a", "ar", "ag", "ab", "aa") + +end + +function TOOL:UpdateGhostWireDataSocket( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_datasocket" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + + local Pos = trace.HitPos + + Ang.pitch = Ang.pitch + 90 + + ent:SetPos( Pos ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Offset( ang, offsetvec ) + local offset = offsetvec + local stackdir = ang:Up() + + offset = ang:Up() * offset.X + ang:Forward() * -1 * offset.Z + ang:Right() * offset.Y + + return stackdir * 2 + offset +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.SocketModel ) then + self:MakeGhostEntity( self.SocketModel, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireDataSocket( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_dataplug_name", Description = "#Tool_wire_dataplug_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_dataplug", + + Options = { + ["#Default"] = { + wire_dataplug_a = "0", + wire_dataplug_ar = "255", + wire_dataplug_ag = "0", + wire_dataplug_ab = "0", + wire_dataplug_aa = "255", + } + }, + + CVars = { + [0] = "wire_dataplug_a", + [1] = "wire_dataplug_ar", + [2] = "wire_dataplug_ag", + [3] = "wire_dataplug_ab", + [4] = "wire_dataplug_aa", + } + }) + + panel:AddControl("Color", { + Label = "#WirePlugTool_colour", + Red = "wire_dataplug_ar", + Green = "wire_dataplug_ag", + Blue = "wire_dataplug_ab", + Alpha = "wire_dataplug_aa", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_dataport.lua b/lua/weapons/gmod_tool/stools/wire_dataport.lua new file mode 100644 index 0000000000..7375592557 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_dataport.lua @@ -0,0 +1,120 @@ +TOOL.Category = "Wire - Advanced" +TOOL.Name = "Data Port" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_dataport_name", "Data port tool (Wire)" ) + language.Add( "Tool_wire_dataport_desc", "Spawns data port consisting of 8 ports" ) + language.Add( "Tool_wire_dataport_0", "Primary: Create/Update data ports unit" ) + language.Add( "sboxlimit_wire_dataports", "You've hit data ports limit!" ) + language.Add( "undone_wire_dataport", "Undone Data Port" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_dataports', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_gate.mdl" + +cleanup.Register( "wire_dataports" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + if ( !self:GetSWEP():CheckLimit( "wire_dataports" ) ) then return false end + + if (not util.IsValidModel(self:GetClientInfo( "model" ))) then return false end + if (not util.IsValidProp(self:GetClientInfo( "model" ))) then return false end + + local ply = self:GetOwner() + local Ang = trace.HitNormal:Angle() + local model = self:GetClientInfo( "model" ) + Ang.pitch = Ang.pitch + 90 + + wire_dataport = MakeWireDataPort( ply, trace.HitPos, Ang, model ) + local min = wire_dataport:OBBMins() + wire_dataport:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_dataport, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireDataPort") + undo.AddEntity( wire_dataport ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_dataports", wire_dataport ) + + return true +end + +if (SERVER) then + + function MakeWireDataPort( pl, Pos, Ang, model ) + + if ( !pl:CheckLimit( "wire_dataports" ) ) then return false end + + local wire_dataport = ents.Create( "gmod_wire_dataport" ) + if (!wire_dataport:IsValid()) then return false end + wire_dataport:SetModel(model) + + wire_dataport:SetAngles( Ang ) + wire_dataport:SetPos( Pos ) + wire_dataport:Spawn() + + wire_dataport:SetPlayer(pl) + + local ttable = { + pl = pl, + } + table.Merge(wire_dataport:GetTable(), ttable ) -- TODO: remove? + + pl:AddCount( "wire_dataports", wire_dataport ) + + return wire_dataport + + end + + duplicator.RegisterEntityClass("gmod_wire_dataport", MakeWireDataPort, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireDataPort( ent, player ) + + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_dataport" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" ) || (not self.GhostEntity:GetModel()) ) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireDataPort( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_dataport_name", Description = "#Tool_wire_dataport_desc" }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_datarate.lua b/lua/weapons/gmod_tool/stools/wire_datarate.lua new file mode 100644 index 0000000000..1cf4650f95 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_datarate.lua @@ -0,0 +1,126 @@ +TOOL.Category = "Wire - Advanced" +TOOL.Name = "Data Transferrer" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_datarate_name", "Data transferrer tool (Wire)" ) + language.Add( "Tool_wire_datarate_desc", "Spawns data transferrer. Data transferrer acts like identity gate for hi-speed and regular links, but also provides data rate of data going through it" ) + language.Add( "Tool_wire_datarate_0", "Primary: Create/Update data trasnferrer" ) + language.Add( "sboxlimit_wire_datarates", "You've hit data trasnferrers limit!" ) + language.Add( "undone_wiredatarate", "Undone Data Transferrer" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_datarates', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_gate.mdl" + +cleanup.Register( "wire_datarates" ) + +function TOOL:LeftClick( trace ) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_datarate" && trace.Entity.pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_datarates" ) ) then return false end + + if (not util.IsValidModel(self:GetClientInfo( "model" ))) then return false end + if (not util.IsValidProp(self:GetClientInfo( "model" ))) then return false end + + local ply = self:GetOwner() + local Ang = trace.HitNormal:Angle() + local model = self:GetClientInfo( "model" ) + Ang.pitch = Ang.pitch + 90 + + wire_datarate = MakeWiredatarate( ply, trace.HitPos, Ang, model ) + local min = wire_datarate:OBBMins() + wire_datarate:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_datarate, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wiredatarate") + undo.AddEntity( wire_datarate ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_datarates", wire_datarate ) + + return true +end + +if (SERVER) then + + function MakeWiredatarate( pl, Pos, Ang, model ) + + if ( !pl:CheckLimit( "wire_datarates" ) ) then return false end + + local wire_datarate = ents.Create( "gmod_wire_datarate" ) + if (!wire_datarate:IsValid()) then return false end + wire_datarate:SetModel(model) + + wire_datarate:SetAngles( Ang ) + wire_datarate:SetPos( Pos ) + wire_datarate:Spawn() + + wire_datarate:SetPlayer(pl) + + local ttable = { + pl = pl, + } + table.Merge(wire_datarate:GetTable(), ttable ) -- TODO: remove? + + pl:AddCount( "wire_datarates", wire_datarate ) + + return wire_datarate + + end + + duplicator.RegisterEntityClass("gmod_wire_datarate", MakeWiredatarate, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWiredatarate( ent, player ) + + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_datarate" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" ) || (not self.GhostEntity:GetModel()) ) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWiredatarate( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_datarate_name", Description = "#Tool_wire_datarate_desc" }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_debugger.lua b/lua/weapons/gmod_tool/stools/wire_debugger.lua new file mode 100644 index 0000000000..7f50dd8a89 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_debugger.lua @@ -0,0 +1,674 @@ +TOOL.Category = "Wire - Tools" +TOOL.Name = "Debugger" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_debugger_name", "Debugging Tool" ) + language.Add( "Tool_wire_debugger_desc", "Shows selected components info on the HUD." ) + language.Add( "Tool_wire_debugger_0", "Primary: Add component to HUD, Secondary: Remove component from HUD, Reload: Clear HUD" ) + language.Add( "Tool_wire_debugger_showports", "Show overlay of ports in HUD" ) + language.Add( "Tool_wire_debugger_orientvertical", "Orient the Inputs/Outputs Vertically" ) +end + +TOOL.ClientConVar[ "showports" ] = "1" +TOOL.ClientConVar[ "orientvertical" ] = "1" + +local Components = {} + +local function IsWire(entity) --try to find out if the entity is wire + if entity.IsWire == true then return true end --this shold always be true if the ent is wire compatible, but only is if the base of the entity is "base_wire_entity" THIS NEEDS TO BE FIXED + if entity.Inputs or entity.Outputs then return true end --this is how the wire STool gun does it + return false +end + +function TOOL:LeftClick(trace) + if (not trace.Entity:IsValid()) then return end + if (CLIENT) then return true end + + if(!IsWire(trace.Entity)) then return end + + ply_idx = self:GetOwner() + Components[ply_idx] = Components[ply_idx] or {} + + for k,cmp in ipairs(Components[ply_idx]) do + if (cmp == trace.Entity) then return end + end + + table.insert(Components[ply_idx], trace.Entity) + + return true +end + + +function TOOL:RightClick(trace) + if (not trace.Entity:IsValid()) then return end + if (CLIENT) then return true end + + if(!IsWire(trace.Entity)) then return end + + ply_idx = self:GetOwner() + if not Components[ply_idx] then return end + + for k,cmp in ipairs(Components[ply_idx]) do + if (cmp == trace.Entity) then + table.remove(Components[ply_idx], k) + return true + end + end + if not next(Components[ply_idx]) then + umsg.Start("WireDbgLineCount", ply) + umsg.Short(0) + umsg.End() + Components[ply_idx] = nil + end +end + +local LastOverBoxInput = "" +local LastOverBoxOutput = "" + +function TOOL:Think() --get and transmit the info on the overlay, but only when needed + local ply = self:GetOwner() + if(CLIENT) then return end + local Trace = ply:GetEyeTraceNoCursor() + + local ShowPorts = self:GetClientNumber("showports") ~= 0 + + if(Trace.Hit and Trace.Entity and Trace.Entity:IsValid() and IsWire(Trace.Entity) and ShowPorts) then + local Ent = Trace.Entity + --get the inputs and put their current use state in-between + if(Ent.Inputs) then + local InputString = "" + for InputIdx, CurInput in pairs_sortvalues(Ent.Inputs, WireLib.PortComparator) do + InputString = InputString..InputIdx + if(CurInput.Type != "NORMAL") then + InputString = InputString.." ["..CurInput.Type.."]" + end + if(CurInput.Src and CurInput.Src:IsValid()) then -- check whether the input is wired up or not + InputString = InputString..",W\n" + else + InputString = InputString..",N\n" + end + end + + if(InputString != LastOverBoxInput) then + self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", InputString) + LastOverBoxInput = InputString + end + else + if(LastOverBoxInput != "") then + LastOverBoxInput = "" + self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", "") + end + end + + --get the outputs + if(Ent.Outputs) then + local OutputString = "" + for OutputIdx, CurOutput in pairs_sortvalues(Ent.Outputs, WireLib.PortComparator) do + OutputString = OutputString..OutputIdx + if(CurOutput.Type != "NORMAL") then + OutputString = OutputString.." ["..CurOutput.Type.."]" + end + OutputString = OutputString.."\n" + end + + if(OutputString != LastOverBoxOutput) then + self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", OutputString) + LastOverBoxOutput = OutputString + end + else + if(LastOverBoxOutput != "") then + LastOverBoxOutput = "" + self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", "") + end + end + else + if(LastOverBoxInput != "") then + LastOverBoxInput = "" + self:GetWeapon():SetNetworkedString("WireDebugOverlayInputs", "") + end + + if(LastOverBoxOutput != "") then + LastOverBoxOutput = "" + self:GetWeapon():SetNetworkedString("WireDebugOverlayOutputs", "") + end + end + +end + +if CLIENT then + + function TOOL:DrawHUD() + local InputText = self:GetWeapon():GetNetworkedString("WireDebugOverlayInputs") or "" + local OutputText = self:GetWeapon():GetNetworkedString("WireDebugOverlayOutputs") or "" + + if(InputText != "") then + surface.SetFont("Trebuchet24") + local Inputs = string.Explode("\n",InputText) + local InputsUsed = {} + for i, Input in ipairs(Inputs) do + InputsUsed[i] = string.Right(Input,2) == ",W" + Inputs[i] = string.sub(Inputs[i],1,-3) + end + + local FontHeight = draw.GetFontHeight("Trebuchet24")+1 + local MaxWidth = 0 + for i, Input in ipairs(Inputs) do + local W, H = surface.GetTextSize(Input) + if(W > MaxWidth) then + MaxWidth = W + end + end + + + draw.RoundedBox(8, + ScrW()/2-(MaxWidth+16)-20, + ScrH()/2-#Inputs*FontHeight/2-8, + MaxWidth+16, + (#Inputs-1)*FontHeight+16, + Color(109,146,129,192) + ) + + for i, Input in ipairs(Inputs) do + local TextCol = Color(255,255,255) + if(InputsUsed[i] == true) then + TextCol = Color(255,0,0) + end + draw.Text({ + text = Input or "", + font = "Trebuchet24", + pos = {ScrW()/2-(MaxWidth+16)-12, (FontHeight)*(i-1)+(ScrH()/2-#Inputs*FontHeight/2)}, + color = TextCol + }) + end + + end + + if(OutputText != "") then + surface.SetFont("Trebuchet24") + local Outputs = string.Explode("\n",OutputText) + + local FontHeight = draw.GetFontHeight("Trebuchet24")+1 + local MaxWidth = 0 + for i, Output in ipairs(Outputs) do + local W, H = surface.GetTextSize(Output) + if(W > MaxWidth) then + MaxWidth = W + end + end + + + draw.RoundedBox(8, + ScrW()/2+20, + ScrH()/2-#Outputs*FontHeight/2-8, + MaxWidth+16, + (#Outputs-1)*FontHeight+16, + Color(109,146,129,192) + ) + + for i, Output in ipairs(Outputs) do + draw.Text({ + text = Output or "", + font = "Trebuchet24", + pos = {ScrW()/2+28, (FontHeight)*(i-1)+(ScrH()/2-#Outputs*FontHeight/2)}, + color = Color(255,255,255) + }) + end + end + + + end +end + +function TOOL:Reload(trace) + if (CLIENT) then return end + umsg.Start("WireDbgLineCount", ply) + umsg.Short(0) + umsg.End() + Components[self:GetOwner()] = nil +end + + +if (SERVER) then + + local dbg_line_cache = {} + local dbg_line_time = {} + + local formatPort = {} + function formatPort.NORMAL(value) + return string.format("%.3f",value) + end + + function formatPort.STRING(value) + return '"' .. value .. '"' + end + + function formatPort.VECTOR(value) + return string.format("(%.1f,%.1f,%.1f)", value[1], value[2], value[3]) + end + + function formatPort.ANGLE(value) + return string.format("(%.1f,%.1f,%.1f)", value.p, value.y, value.r) + end + + formatPort.ENTITY = function(ent) + if not validEntity(ent) then return "(null)" end -- this uses validEntity from E2, which is faster, but maybe we shouldn't use it. + return tostring(ent) + end + formatPort.BONE = e2_tostring_bone + + function formatPort.MATRIX(value) + local RetText = "[11="..value[1]..",12="..value[2]..",13="..value[3] + RetText = RetText..",21="..value[4]..",22="..value[5]..",23="..value[6] + RetText = RetText..",31="..value[7]..",32="..value[8]..",33="..value[9].."]" + return RetText + end + + function formatPort.MATRIX2(value) + local RetText = "[11="..value[1]..",12="..value[2] + RetText = RetText..",21="..value[3]..",22="..value[4].."]" + return RetText + end + + function formatPort.MATRIX4(value) + local RetText = "[11="..value[1]..",12="..value[2]..",13="..value[3]..",14="..value[4] + RetText = RetText..",21="..value[5]..",22="..value[6]..",23="..value[7]..",24="..value[8] + RetText = RetText..",31="..value[9]..",32="..value[10]..",33="..value[11]..",34="..value[12] + RetText = RetText..",41="..value[13]..",42="..value[14]..",43="..value[15]..",44="..value[16].."]" + return RetText + end + + function formatPort.ARRAY(value, OrientVertical) + local RetText = "" + local ElementCount = 0 + for Index, Element in ipairs(value) do + ElementCount = ElementCount+1 + if(ElementCount > 10) then + break + end + RetText = RetText..Index.."=" + --Check for array element type + if(type(Element) == "number") then --number + RetText = RetText..formatPort.NORMAL(Element) + elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Vector") then --vector + RetText = RetText..formatPort.VECTOR(Element) + elseif(type(Element) == "table" and #Element == 2) then --vector2 + RetText = RetText..formatPort.VECTOR2(Element) + elseif(type(Element) == "table" and #Element == 4) then --vector4 + RetText = RetText..formatPort.VECTOR4(Element) + elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Angle") then --angle + if(type(Element) == "Angle") then + RetText = RetText..formatPort.ANGLE(Element) + else + RetText = RetText.."(" .. math.Round(Element[1]*10)/10 .. "," .. math.Round(Element[2]*10)/10 .. "," .. math.Round(Element[3]*10)/10 .. ")" + end + elseif(type(Element) == "table" and #Element == 9) then --matrix + RetText = RetText..formatPort.MATRIX(Element) + elseif(type(Element) == "table" and #Element == 16) then --matrix4 + RetText = RetText..formatPort.MATRIX4(Element) + elseif(type(Element) == "string") then --string + RetText = RetText..formatPort.STRING(Element) + elseif(type(Element) == "Entity") then --entity + RetText = RetText..formatPort.ENTITY(Element) + elseif(type(Element) == "Player") then --player + RetText = RetText..tostring(Element) + elseif(type(Element) == "Weapon") then --weapon + RetText = RetText..tostring(Element)..Element:GetClass() + elseif(type(Element) == "PhysObj" and e2_tostring_bone(Element) != "(null)") then --Bone + RetText = RetText..formatPort.BONE(Element) + else + RetText = RetText.."No Display for "..type(Element) + end + --TODO: add matrix 2 + if OrientVertical then + RetText = RetText..",\n" + else + RetText = RetText..", " + end + end + RetText = string.sub(RetText,1,-3) + return "{"..RetText.."}" + end + + function formatPort.TABLE(value, OrientVertical) + local RetText = "" + local ElementCount = 0 + for Index, Element in pairs(value) do + ElementCount = ElementCount+1 + if(ElementCount > 7) then + break + end + + local long_typeid = string.sub(Index,1,1) == "x" + local typeid = string.sub(Index,1,long_typeid and 3 or 1) + local IdxID = string.sub(Index,(long_typeid and 3 or 1)+1) + + RetText = RetText..IdxID.."=" + --Check for array element type + if(typeid == "n") then --number + RetText = RetText..formatPort.NORMAL(Element) + elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Vector") then --vector + RetText = RetText..formatPort.VECTOR(Element) + elseif(type(Element) == "table" and #Element == 2) then --vector2 + RetText = RetText..formatPort.VECTOR2(Element) + elseif(type(Element) == "table" and #Element == 4 and typeid == "v4") then --vector4 + RetText = RetText..formatPort.VECTOR4(Element) + elseif((type(Element) == "table" and #Element == 3) or type(Element) == "Angle") then --angle + if(type(Element) == "Angle") then + RetText = RetText..formatPort.ANGLE(Element) + else + RetText = RetText.."(" .. math.Round(Element[1]*10)/10 .. "," .. math.Round(Element[2]*10)/10 .. "," .. math.Round(Element[3]*10)/10 .. ")" + end + elseif(type(Element) == "table" and #Element == 9) then --matrix + RetText = RetText..formatPort.MATRIX(Element) + elseif(type(Element) == "table" and #Element == 16) then --matrix4 + RetText = RetText..formatPort.MATRIX4(Element) + elseif(typeid == "s") then --string + RetText = RetText..formatPort.STRING(Element) + elseif(type(Element) == "Entity" and typeid == "e") then --entity + RetText = RetText..formatPort.ENTITY(Element) + elseif(type(Element) == "Player") then --player + RetText = RetText..tostring(Element) + elseif(type(Element) == "Weapon") then --weapon + RetText = RetText..tostring(Element)..Element:GetClass() + elseif(typeid == "b") then + RetText = RetText..formatPort.BONE(Element) + else + RetText = RetText.."No Display for "..type(Element) + end + --TODO: add matrix 2 + if OrientVertical then + RetText = RetText..",\n" + else + RetText = RetText..", " + end + end + RetText = string.sub(RetText,1,-3) + return "{"..RetText.."}" + end + + -- Shouldn't this be in WireLib instead??? + function WireLib.registerDebuggerFormat(typename, func) + formatPort[typename:upper()] = func + end + + hook.Add("Think", "Wire_DebuggerThink", function() + for ply,cmps in pairs(Components) do + + if ( !ply ) or ( !ply:IsValid() ) or ( !ply:IsPlayer() ) then -- if player has left, clear the hud + + Components[ply] = nil + + else + + OrientVertical = ply:GetInfoNum("wire_debugger_orientvertical") ~= 0 + + -- TODO: Add EntityRemoved hook to clean up Components array. + table.Compact(cmps, function(cmp) return cmp:IsValid() end) + + -- TODO: only send in TOOL:*Click/Reload hooks maybe. + umsg.Start("WireDbgLineCount", ply) + umsg.Short(#cmps) + umsg.End() + + if #cmps == 0 then Components[ply] = nil end + + for l,cmp in ipairs(cmps) do + local dbginfo = cmp.WireDebugName + if not dbginfo or dbginfo == "No Name" then + dbginfo = cmp:GetClass() + end + dbginfo = dbginfo .. " (" ..cmp:EntIndex() .. ") - " + + if (cmp.Inputs and table.Count(cmp.Inputs) > 0) then + if OrientVertical then + dbginfo = dbginfo .. "\n" + end + dbginfo = dbginfo .. "IN: " + if OrientVertical then + dbginfo = dbginfo .. "\n" + end + for k, Input in pairs_sortvalues(cmp.Inputs, WireLib.PortComparator) do + if formatPort[Input.Type] then + dbginfo = dbginfo .. k .. ":" .. formatPort[Input.Type](Input.Value, OrientVertical) + if OrientVertical then + dbginfo = dbginfo .. "\n" + else + dbginfo = dbginfo .. " " + end + end + end + end + + if (cmp.Outputs and table.Count(cmp.Outputs) > 0) then + if(cmp.Inputs and table.Count(cmp.Inputs) > 0) then + dbginfo = dbginfo .. "\n " + end + if(!cmp.Inputs and OrientVertical) then + dbginfo = dbginfo .. "\n" + end + dbginfo = dbginfo .. "OUT: " + if OrientVertical then + dbginfo = dbginfo .. "\n" + end + for k, Output in pairs_sortvalues(cmp.Outputs, WireLib.PortComparator) do + if formatPort[Output.Type] then + dbginfo = dbginfo .. k .. ":" .. formatPort[Output.Type](Output.Value, OrientVertical) + if OrientVertical then + dbginfo = dbginfo .. "\n" + else + dbginfo = dbginfo .. " " + end + end + end + end + + if (not cmp.Inputs) and (not cmp.Outputs) then + dbginfo = dbginfo .. "No info" + end + + dbg_line_cache[ply] = dbg_line_cache[ply] or {} + dbg_line_time[ply] = dbg_line_time[ply] or {} + if (dbg_line_cache[ply][l] ~= dbginfo) then + if (not dbg_line_time[ply][l]) or (CurTime() > dbg_line_time[ply][l]) then + --split the message up into managable chuncks and send them + local NumOfUMSG = 1 + if(string.len(dbginfo)>200) then + NumOfUMSG = math.ceil(string.len(dbginfo)/200) + end + + for i=1, NumOfUMSG do + local MsgPart = string.sub(dbginfo,(i-1)*200+1, i*200) + umsg.Start("WireDbgLine", ply) + umsg.Short(l) + umsg.Short(NumOfUMSG) + umsg.Short(i) + umsg.Bool(OrientVertical) + umsg.String(MsgPart) + umsg.End() + end + + dbg_line_cache[ply][l] = dbginfo + if (SinglePlayer()) then + dbg_line_time[ply][l] = CurTime() + 0.05 + else + dbg_line_time[ply][l] = CurTime() + 0.2 + end + end + end + end + + end + + end + end) + +end + + +if (CLIENT) then + + local dbg_line_count = 0 + local dbg_lines = {} + local UMSG_Buffer = {} + local dgb_orient_vert = false + local BoxWidth = 300 + local LastBoxUpdate = CurTime()-5 + + local function DebuggerDrawHUD() + local dbginfo = "" + if (dbg_line_count <= 0) then return end + + --setup the font + surface.SetFont("Default") + + --buid the table of entries + local Entry_Count = dbg_line_count + local Line_Count = 0 + local ColorType = 0 + local Entries = {} + for i = 1,dbg_line_count do + local Line = dbg_lines[i] + if(Line) then + local CurEntry = {} + CurEntry.Lines = {} + + local ExplodeLines = string.Explode("\n", Line) + + for Index, ExplodeLine in ipairs(ExplodeLines) do --break it into multible lines for 1 entry + if(string.Trim(ExplodeLine) != "") then + + local XPos = 0 + if(Index > 1) then + if dgb_orient_vert then --if the string is not the first and it is vertical, line it up acordingly + if(string.Trim(ExplodeLine) == "OUT:" or string.Trim(ExplodeLine) == "IN:") then + XPos = 17 + else + XPos = 42 + end + else --if the string is not the first and it is not vertical, line it up with the IN on the first line + if(CurEntry.Lines[1].LineText and string.find(CurEntry.Lines[1].LineText,"IN:")) then + local TextPos = string.find(CurEntry.Lines[1].LineText,"IN:")-1 + XPos = surface.GetTextSize( string.Left(CurEntry.Lines[1].LineText, TextPos) ) + end + end + + end + + local TrimLine = { + LineText = string.Trim(ExplodeLine), + OffsetPos = { XPos, Line_Count*14 } --move the next text down some for each line + } + table.insert(CurEntry.Lines, TrimLine ) + Line_Count = Line_Count+1 + + end + end + + --set the color + if(ColorType == 0) then + CurEntry.TextColor = Color(255,255,255) + else + CurEntry.TextColor = Color(130,255,158) + end + + --put it in the table + table.insert(Entries, CurEntry) + + --switch the color + ColorType = 1-ColorType + end + end + + + + --determine the box width every second + if(LastBoxUpdate < CurTime()-1) then + local LongestWidth = 0 + local TextWidth, TextHeight + for EntryIndex, Entry in ipairs(Entries) do + for LineIndex, Line in ipairs(Entry.Lines) do + TextWidth, TextHeight = surface.GetTextSize(string.Trim(Line.LineText)) + TextWidth = TextWidth+Line.OffsetPos[1] --offset it with the text's offset + + if(TextWidth > LongestWidth) then + LongestWidth = TextWidth + end + end + end + BoxWidth = LongestWidth+16 + LastBoxUpdate = CurTime() + end + + + --move the box down if the active weapon is the tool gun + local MoveBox = 0 + if(LocalPlayer():IsValid() and LocalPlayer():IsPlayer()) then + if ValidEntity(LocalPlayer():GetActiveWeapon()) and LocalPlayer():GetActiveWeapon():GetClass() == "gmod_tool" then + MoveBox = 1 + end + else --return if the player is dead or non-existant + return + end + + + -- TODO: account for larger usage info boxes. + --draw the box + draw.RoundedBox(8, 2, 2+143*MoveBox, BoxWidth, Line_Count*14+16, Color(50, 50, 50, 128)) + + --step through all of the entries and their text to print them + for EntryIndex, Entry in ipairs(Entries) do + for LineIndex, Line in ipairs(Entry.Lines) do + draw.Text({ + text = string.Trim(Line.LineText) or "", + font = "Default", + pos = { Line.OffsetPos[1]+10, 143*MoveBox+10+Line.OffsetPos[2] }, + color = Entry.TextColor + }) + + end + end + + end + hook.Add("HUDPaint", "DebuggerDrawHUD", DebuggerDrawHUD) + + local function Debugger_Msg_LineCount(um) + dbg_line_count = um:ReadShort() + end + usermessage.Hook("WireDbgLineCount", Debugger_Msg_LineCount) + + local function Debugger_Msg_Line(um) + local i = um:ReadShort() + local NumOfUMSG = um:ReadShort() + local UMSGCount = um:ReadShort() + dgb_orient_vert = um:ReadBool() + if(!UMSG_Buffer[i]) then + UMSG_Buffer[i] = "" + end + UMSG_Buffer[i] = UMSG_Buffer[i]..(um:ReadString() or "") + if(NumOfUMSG == UMSGCount) then + dbg_lines[i] = UMSG_Buffer[i] + UMSG_Buffer[i] = "" + end + end + usermessage.Hook("WireDbgLine", Debugger_Msg_Line) + +end + + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_debugger_name", Description = "#Tool_wire_debugger_desc" }) + + panel:AddControl("CheckBox", { + Label = "#Tool_wire_debugger_showports", + Command = "wire_debugger_showports" + }) + + panel:AddControl("CheckBox", { + Label = "#Tool_wire_debugger_orientvertical", + Command = "wire_debugger_orientvertical" + }) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_emarker.lua b/lua/weapons/gmod_tool/stools/wire_emarker.lua new file mode 100644 index 0000000000..83f6dd3e89 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_emarker.lua @@ -0,0 +1,204 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "Entity Marker" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_emarker_name", "Entity Marker Tool (Wire)" ) + language.Add( "Tool_wire_emarker_desc", "Spawns an Entity Marker for use with the wire system." ) + language.Add( "Tool_wire_emarker_0", "Primary: Create Entity Marker/Display Link Info, Secondary: Link Entity Marker, Reload: Unlink Entity Marker" ) + language.Add( "Tool_wire_emarker_1", "Now select the entity to link to.") + language.Add( "sboxlimit_wire_emarker", "You've hit entity marker limit!" ) + language.Add( "undone_wireemarker", "Undone Wire Entity Marker" ) +elseif ( SERVER ) then + CreateConVar('sbox_maxwire_emarkers',30) +end + +TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" + +cleanup.Register( "wire_emarkers" ) + +// Variable "marker" refers to the entity marker +// Variable "mark" refers to the linked entity + +local EntityMarkers = {} + +function Add_EntityMarker( r ) + table.insert( EntityMarkers, r ) +end + +function EntityMarker_Removed(entity) + for i, o in ipairs( EntityMarkers ) do + if !IsEntity(o.Entity) then + table.remove(EntityMarkers, i) + elseif o.mark==entity then + o:UnLinkEMarker() + end + end +end +hook.Add("EntityRemoved","EntityMarkerEntRemoved",EntityMarker_Removed) + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:GetClass() == "gmod_wire_emarker" ) then + + self.marker = trace.Entity + + if ( !self.marker.mark || !self.marker.mark:IsValid() ) then + ply:PrintMessage(HUD_PRINTTALK, "Entity Marker not linked") + return false + end + + ply:PrintMessage( HUD_PRINTTALK, "Linked model: " .. self.marker.mark:GetModel() ) + self:GetWeapon():SetNetworkedEntity( "WireEntityMark", self.marker.mark ) + self:GetWeapon():SetNetworkedEntity( "WireEntityMarker", self.marker ) + return true + else + if ( !self:GetSWEP():CheckLimit( "wire_emarkers" ) ) then return false end + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_emarker = MakeWireEmarker( ply, trace.HitPos, Ang, self.Model ) + + local min = wire_emarker:OBBMins() + wire_emarker:SetPos( trace.HitPos - trace.HitNormal * (min.z) ) + + local const = WireLib.Weld( wire_emarker, trace.Entity, trace.PhysicsBone, true ) + undo.Create("WireEmarker") + undo.AddEntity( wire_emarker ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_emarkers", wire_emarker ) + + return true + end +end + +function TOOL:RightClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + if ( trace.Entity:IsValid() ) then + if ( self:GetStage() == 0 && trace.Entity:GetClass() == "gmod_wire_emarker" ) then + self.marker = trace.Entity + self:SetStage(1) + return true + elseif ( self:GetStage() == 1 ) then + self.marker:LinkEMarker(trace.Entity) + self:SetStage(0) + self:GetOwner():PrintMessage( HUD_PRINTTALK,"Entity Marker linked" ) + self:GetWeapon():SetNetworkedEntity( "WireEntityMark", self.marker.mark ) + self:GetWeapon():SetNetworkedEntity( "WireEntityMarker", self.marker ) + return true + else + return false + end + end +end + +function TOOL:Reload(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + self:SetStage(0) + local marker = trace.Entity + if (!marker || !marker:IsValid()) then return false end + if (marker:GetClass() == "gmod_wire_emarker") then + marker:UnLinkEMarker() + self:GetOwner():PrintMessage( HUD_PRINTTALK,"Entity Marker unlinked" ) + self:GetWeapon():SetNetworkedEntity( "WireEntityMark", self.marker ) // Substitute for null, which won't set + self:GetWeapon():SetNetworkedEntity( "WireEntityMarker", self.marker ) // Set same point so line won't draw + return true + end +end + +function TOOL:DrawHUD() + local mark = self:GetWeapon():GetNetworkedEntity( "WireEntityMark" ) + local marker = self:GetWeapon():GetNetworkedEntity( "WireEntityMarker" ) + if ( !mark || !marker || !mark:IsValid() || !marker:IsValid() ) then return end + + local markerpos = marker:GetPos():ToScreen() + local markpos = mark:GetPos():ToScreen() + if ( markpos.x > 0 && markpos.y > 0 && markpos.x < ScrW() && markpos.y < ScrH( ) ) then + surface.SetDrawColor( 255, 255, 100, 255 ) + surface.DrawLine(markerpos.x, markerpos.y, markpos.x, markpos.y) + end +end + +if SERVER then + + function MakeWireEmarker( pl, Pos, Ang, model, nocollide ) + if (!pl:CheckLimit("wire_emarkers")) then return false end + + local wire_emarker = ents.Create("gmod_wire_emarker") + wire_emarker:SetPos(Pos) + wire_emarker:SetAngles(Ang) + wire_emarker:SetModel( Model(model or "models/jaanus/wiretool/wiretool_siren.mdl") ) + wire_emarker:Spawn() + wire_emarker:Activate() + + wire_emarker:LinkEMarker() + wire_emarker:SetPlayer(pl) + + if ( nocollide == true ) then wire_emarker:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + pl = pl, + nocollide = nocollide, + } + table.Merge( wire_emarker:GetTable(), ttable ) + + pl:AddCount( "wire_emarkers", wire_emarker ) + + return wire_emarker + end + + duplicator.RegisterEntityClass( "gmod_wire_emarker", MakeWireEmarker, "Pos", "Ang", "Model", "nocollide" ) + +end + +function TOOL:UpdateGhostEmarker( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_emarker" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostEmarker( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_emarker_name", Description = "#Tool_wire_emarker_desc" }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_explosive.lua b/lua/weapons/gmod_tool/stools/wire_explosive.lua new file mode 100644 index 0000000000..1e6d162da0 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_explosive.lua @@ -0,0 +1,363 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Explosives" +TOOL.Command = nil +TOOL.ConfigName = nil +TOOL.Tab = "Wire" + +TOOL.ClientConVar[ "model" ] = "models/props_c17/oildrum001_explosive.mdl" +TOOL.ClientConVar[ "modelman" ] = "" +TOOL.ClientConVar[ "usemodelman" ] = 0 +TOOL.ClientConVar[ "effect" ] = "Explosion" +TOOL.ClientConVar[ "tirgger" ] = 1 // Current tirgger +TOOL.ClientConVar[ "damage" ] = 200 // Damage to inflict +TOOL.ClientConVar[ "doblastdamage" ] = 1 +TOOL.ClientConVar[ "radius" ] = 300 +TOOL.ClientConVar[ "removeafter" ] = 0 +TOOL.ClientConVar[ "affectother" ] = 0 +TOOL.ClientConVar[ "notaffected" ] = 0 +TOOL.ClientConVar[ "delaytime" ] = 0 +TOOL.ClientConVar[ "delayreloadtime" ] = 0 +TOOL.ClientConVar[ "freeze" ] = 0 +TOOL.ClientConVar[ "weld" ] = 1 +TOOL.ClientConVar[ "maxhealth" ] = 100 +TOOL.ClientConVar[ "bulletproof" ] = 0 +TOOL.ClientConVar[ "explosionproof" ] = 0 +TOOL.ClientConVar[ "weight" ] = 400 +TOOL.ClientConVar[ "explodeatzero" ] = 1 +TOOL.ClientConVar[ "resetatexplode" ] = 1 +TOOL.ClientConVar[ "fireeffect" ] = 1 +TOOL.ClientConVar[ "coloreffect" ] = 1 +TOOL.ClientConVar[ "nocollide" ] = 0 +TOOL.ClientConVar[ "noparentremove" ] = 0 +TOOL.ClientConVar[ "invisibleatzero" ] = 0 + +cleanup.Register( "wire_explosive" ) + +if ( CLIENT ) then + language.Add( "Tool_wire_explosive_name", "Wired Explosives Tool" ) + language.Add( "Tool_wire_explosive_desc", "Creates a variety of different explosives for wire system." ) + language.Add( "Tool_wire_explosive_0", "Left click to place the bomb. Right click update." ) + language.Add( "WireExplosiveTool_Model", "Model:" ) + language.Add( "WireExplosiveTool_modelman", "Manual model selection:" ) + --language.Add( "WireExplosiveTool_usemodelman", "Use manual model selection:" ) + language.Add( "WireExplosiveTool_Effects", "Effect:" ) + language.Add( "WireExplosiveTool_tirgger", "Trigger value:" ) + language.Add( "WireExplosiveTool_damage", "Damage:" ) + language.Add( "WireExplosiveTool_delay", "On fire time (delay after triggered before explosion):" ) + language.Add( "WireExplosiveTool_delayreload", "Delay after explosion before it can be triggered again:" ) + language.Add( "WireExplosiveTool_remove", "Remove on explosion" ) + language.Add( "WireExplosiveTool_doblastdamage", "Do blast damage" ) + language.Add( "WireExplosiveTool_affectother", "Damaged/moved by other wired explosives" ) + language.Add( "WireExplosiveTool_notaffected", "Not moved by any phyiscal damage" ) + language.Add( "WireExplosiveTool_radius", "Blast radius:" ) + language.Add( "WireExplosiveTool_freeze", "Freeze" ) + language.Add( "WireExplosiveTool_weld", "Weld" ) + language.Add( "WireExplosiveTool_noparentremove", "Don't remove on parent remove" ) + language.Add( "WireExplosiveTool_nocollide", "No collide all but world" ) + language.Add( "WireExplosiveTool_maxhealth", "Max health:" ) + language.Add( "WireExplosiveTool_weight", "Weight:" ) + language.Add( "WireExplosiveTool_bulletproof", "Bullet proof" ) + language.Add( "WireExplosiveTool_explosionproof", "Explosion proof" ) + --language.Add( "WireExplosiveTool_fallproof", "Fall proof" ) + language.Add( "WireExplosiveTool_explodeatzero", "Explode when health = zero" ) + language.Add( "WireExplosiveTool_resetatexplode", "Reset health then" ) + language.Add( "WireExplosiveTool_fireeffect", "Enable fire effect on triggered" ) + language.Add( "WireExplosiveTool_coloreffect", "Enable color change effect on damage" ) + language.Add( "WireExplosiveTool_invisibleatzero", "Become invisible when health reaches 0" ) + language.Add( "Undone_WireExplosive", "Wired Explosive undone" ) + language.Add( "sbox_maxwire_explosive", "You've hit wired explosives limit!" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_explosive', 30) +end + + +function TOOL:LeftClick( trace ) + + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( !self:GetSWEP():CheckLimit( "wire_explosive" ) ) then return false end + + // Get client's CVars + local _tirgger = self:GetClientNumber( "tirgger" ) + local _damage = math.Clamp( self:GetClientNumber( "damage" ), 0, 1500 ) + local _removeafter = self:GetClientNumber( "removeafter" ) == 1 + local _delaytime = self:GetClientNumber( "delaytime" ) + local _delayreloadtime = self:GetClientNumber( "delayreloadtime" ) + local _doblastdamage = self:GetClientNumber( "doblastdamage" ) == 1 + local _radius = self:GetClientNumber( "radius" ) + local _affectother = self:GetClientNumber( "affectother" ) == 1 + local _notaffected = self:GetClientNumber( "notaffected" ) == 1 + local _freeze = self:GetClientNumber( "freeze" ) == 1 + local _weld = self:GetClientNumber( "weld" ) == 1 + local _maxhealth = self:GetClientNumber( "maxhealth" ) + local _bulletproof = self:GetClientNumber( "bulletproof" ) == 1 + local _explosionproof = self:GetClientNumber( "explosionproof" ) == 1 + local _fallproof = self:GetClientNumber( "fallproof" ) == 1 + local _explodeatzero = self:GetClientNumber( "explodeatzero" ) == 1 + local _resetatexplode = self:GetClientNumber( "resetatexplode" ) == 1 + local _fireeffect = self:GetClientNumber( "fireeffect" ) == 1 + local _coloreffect = self:GetClientNumber( "coloreffect" ) == 1 + local _noparentremove = self:GetClientNumber( "noparentremove" ) == 1 + local _nocollide = self:GetClientNumber( "nocollide" ) == 1 + local _weight = self:GetClientNumber( "weight" ) + local _invisibleatzero = self:GetClientNumber( "invisibleatzero" ) == 1 + + //Check Radius + if (_radius > 10000) then return false end + + //get & check selected model + _model = self:GetSelModel( true ) + if (!_model) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local explosive = MakeWireExplosive( ply, trace.HitPos, Ang, _model, _tirgger, _damage, _removeafter, _delaytime, _doblastdamage, _radius, _affectother, _notaffected, _delayreloadtime, _maxhealth, _bulletproof, _explosionproof, _fallproof, _explodeatzero, _resetatexplode, _fireeffect, _coloreffect, _invisibleatzero, _nocollide ) + + local min = explosive:OBBMins() + explosive:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + if ( _freeze ) then + explosive:GetPhysicsObject():Sleep() //will freeze the explosive till something touches it + end + + // Don't weld to world + local const, nocollid + if ( trace.Entity:IsValid() && _weld ) then + if (_noparentremove) then + const, nocollide = constraint.Weld( explosive, trace.Entity, 0, trace.PhysicsBone, 0, collision == 0 ) + else + const, nocollide = constraint.Weld( explosive, trace.Entity, 0, trace.PhysicsBone, 0, collision == 0, true ) + end + end + + if (_weight <= 0) then _weight = 1 end + explosive.Entity:GetPhysicsObject():SetMass(_weight) + // Make sure the weight is duplicated as well (TheApathetic) + duplicator.StoreEntityModifier( explosive, "MassMod", {Mass = _weight} ) + + undo.Create("WireExplosive") + undo.AddEntity( explosive ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_explosive", explosive ) + ply:AddCleanup( "wire_explosive", const ) + + return true + +end + + +function TOOL:GetSelModel( showerr ) + + local model = self:GetClientInfo( "model" ) + + if (model == "usemanmodel") then + local _modelman = self:GetClientInfo( "modelman" ) + if (_modelman && string.len(_modelman) > 0) then + model = _modelman + else + local message = "You need to define a model." + if (showerr) then + self:GetOwner():PrintMessage(3, message) + self:GetOwner():PrintMessage(2, message) + end + return false + end + elseif (model == "usereloadmodel") then + if (self.reloadmodel && string.len(self.reloadmodel) > 0) then + model = self.reloadmodel + else + local message = "You need to select a model model." + if (showerr) then + self:GetOwner():PrintMessage(3, message) + self:GetOwner():PrintMessage(2, message) + end + return false + end + end + + if (not util.IsValidModel(model)) then + //something fucked up, notify user of that + local message = "This is not a valid model."..model + if (showerr) then + self:GetOwner():PrintMessage(3, message) + self:GetOwner():PrintMessage(2, message) + end + return false + end + if (not util.IsValidProp(model)) then return false end + + return model +end + + +function TOOL:RightClick( trace ) + if (CLIENT) then return true end + + local ply = self:GetOwner() + //shot an explosive, update it + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_explosive" && trace.Entity:GetTable().pl == ply ) then + //double you code double your fun (copy from above) + // Get client's CVars + local tirgger = self:GetClientNumber( "tirgger" ) + local damage = math.Clamp( self:GetClientNumber( "damage" ), 0, 1500 ) + local removeafter = self:GetClientNumber( "removeafter" ) == 1 + local delaytime = self:GetClientNumber( "delaytime" ) + local delayreloadtime = self:GetClientNumber( "delayreloadtime" ) + local doblastdamage = self:GetClientNumber( "doblastdamage" ) == 1 + local radius = self:GetClientNumber( "radius" ) + local affectother = self:GetClientNumber( "affectother" ) == 1 + local notaffected = self:GetClientNumber( "notaffected" ) == 1 + local freeze = self:GetClientNumber( "freeze" ) == 1 + local weld = self:GetClientNumber( "weld" ) == 1 + local maxhealth = self:GetClientNumber( "maxhealth" ) + local bulletproof = self:GetClientNumber( "bulletproof" ) == 1 + local explosionproof = self:GetClientNumber( "explosionproof" ) == 1 + local fallproof = self:GetClientNumber( "fallproof" ) == 1 + local explodeatzero = self:GetClientNumber( "explodeatzero" ) == 1 + local resetatexplode = self:GetClientNumber( "resetatexplode" ) == 1 + local fireeffect = self:GetClientNumber( "fireeffect" ) == 1 + local coloreffect = self:GetClientNumber( "coloreffect" ) == 1 + local noparentremove = self:GetClientNumber( "noparentremove" ) == 1 + local nocollide = self:GetClientNumber( "nocollide" ) == 1 + local weight = self:GetClientNumber( "weight" ) + local invisibleatzero = self:GetClientNumber( "invisibleatzero" ) == 1 + + UpdateWireExplosive(trace.Entity, tirgger, damage, delaytime, removeafter, doblastdamage, radius, affectother, notaffected, delayreloadtime, maxhealth, bulletproof, explosionproof, fallproof, explodeatzero, resetatexplode, fireeffect, coloreffect, invisibleatzero, nocollide ) + + if (weight <= 0) then weight = 1 end + trace.Entity:GetPhysicsObject():SetMass(_weight) + // Make sure the weight is duplicated as well (TheApathetic) + duplicator.StoreEntityModifier( trace.Entity, "MassMod", {Mass = weight} ) + + //reset color in case we turned the color effect off and it's still red + trace.Entity:SetColor(255, 255, 255, 255) + + return true + end + +end + +function TOOL:Reload( trace ) + //get the model of what was shot and set our reloadmodel to that + //model info getting code mostly copied from OverloadUT's What Is That? STool + if !trace.Entity then return false end + local ent = trace.Entity + local ply = self:GetOwner() + local class = ent:GetClass() + if class == "worldspawn" then + return false + else + local model = ent:GetModel() + local message = "Model selected: "..model + self.reloadmodel = model + ply:PrintMessage(3, message) + ply:PrintMessage(2, message) + end + return true +end + +if SERVER then + + function UpdateWireExplosive(explosive, trigger, damage, delaytime, removeafter, doblastdamage, radius, affectother, notaffected, delayreloadtime, maxhealth, bulletproof, explosionproof, fallproof, explodeatzero, resetatexplode, fireeffect, coloreffect, invisibleatzero, nocollide ) + + explosive:Setup( damage, delaytime, removeafter, doblastdamage, radius, affectother, notaffected, delayreloadtime, maxhealth, bulletproof, explosionproof, fallproof, explodeatzero, resetatexplode, fireeffect, coloreffect, invisibleatzero, nocollide ) + + local ttable = { + key = trigger, + nocollide = nocollide, + damage = damage, + removeafter = removeafter, + delaytime = delaytime, + doblastdamage = doblastdamage, + radius = radius, + affectother = affectother, + notaffected = notaffected, + delayreloadtime = delayreloadtime, + maxhealth = maxhealth, + bulletproof = bulletproof, + explosionproof = explosionproof, + fallproof = fallproof, + explodeatzero = explodeatzero, + resetatexplode = resetatexplode, + fireeffect = fireeffect, + coloreffect = coloreffect, + invisibleatzero = invisibleatzero + } + table.Merge( explosive:GetTable(), ttable ) + + end + + + function MakeWireExplosive(pl, Pos, Ang, model, trigger, damage, removeafter, delaytime, doblastdamage, radius, affectother, notaffected, delayreloadtime, maxhealth, bulletproof, explosionproof, fallproof, explodeatzero, resetatexplode, fireeffect, coloreffect, invisibleatzero, nocollide ) + + if ( !pl:CheckLimit( "wire_explosive" ) ) then return nil end + + local explosive = ents.Create( "gmod_wire_explosive" ) + + explosive:SetModel( model ) + explosive:SetPos( Pos ) + explosive:SetAngles( Ang ) + explosive:Spawn() + explosive:Activate() + + explosive:SetPlayer( pl ) + explosive.pl = pl + + UpdateWireExplosive( explosive, trigger, damage, delaytime, removeafter, doblastdamage, radius, affectother, notaffected, delayreloadtime, maxhealth, bulletproof, explosionproof, fallproof, explodeatzero, resetatexplode, fireeffect, coloreffect, invisibleatzero, nocollide ) + + pl:AddCount( "wire_explosive", explosive ) + + return explosive + + end + + duplicator.RegisterEntityClass( "gmod_wire_explosive", MakeWireExplosive, "Pos", "Ang", "Model", "key", "damage", "removeafter", "delaytime", "doblastdamage", "radius", "affectother", "notaffected", "delayreloadtime", "maxhealth", "bulletproof", "explosionproof", "fallproof", "explodeatzero", "resetatexplode", "fireeffect", "coloreffect", "invisibleatzero", "nocollide" ) + +end + +function TOOL:UpdateGhostWireExplosive( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() ) then -- || trace.Entity:GetClass() == "gmod_wire_explosive" + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetSelModel()) then + + local _model = self:GetSelModel() + if (!_model) then return end + + self:MakeGhostEntity( _model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireExplosive( self.GhostEntity, self:GetOwner() ) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_expression2.lua b/lua/weapons/gmod_tool/stools/wire_expression2.lua new file mode 100644 index 0000000000..e371731142 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_expression2.lua @@ -0,0 +1,539 @@ +TOOL.Category = "Wire - Control" +TOOL.Name = "Chip - Expression 2" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +TOOL.ClientConVar = { + model = "models/beer/wiremod/gate_e2.mdl", + size = "", + select = "", + autoindent = 1, +} + +if CLIENT then + language.Add("Tool_wire_expression2_name", "Expression 2 Tool (Wire)") + language.Add("Tool_wire_expression2_desc", "Spawns an Expression 2 chip for use with the wire system.") + language.Add("Tool_wire_expression2_0", "Primary: Create/Update Expression, Secondary: Open Expression in Editor") + language.Add("sboxlimit_wire_expression", "You've hit the Expression limit!") + language.Add("Undone_wire_expression2", "Undone Expression 2") + language.Add("Cleanup_wire_expressions", "Expression 1+2" ) + language.Add("Cleaned_wire_expressions", "Cleaned up all Wire Expressions" ) +end + +cleanup.Register("wire_expressions") + +if SERVER then + CreateConVar('sbox_maxwire_expressions', 20) + local wire_expression2_protected = CreateConVar('wire_expression2_protected', 1) + + function TOOL:LeftClick(trace) + if trace.Entity:IsPlayer() then return false end + + local player = self:GetOwner() + local model = self:GetModel() + local pos = trace.HitPos + local ang = trace.HitNormal:Angle() + ang.pitch = ang.pitch + 90 + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_expression2" && (trace.Entity:GetPlayer() == player || wire_expression2_protected:GetFloat() == 0)) then + trace.Entity:SetPlayer(player) + trace.Entity.player = player + trace.Entity:Prepare(player) + player:SendLua("wire_expression2_upload()") + return true + end + + if !self:GetSWEP():CheckLimit("wire_expressions") then return false end + + local entity = ents.Create("gmod_wire_expression2") + if !entity:IsValid() then return false end + + player:AddCount("wire_expressions", entity) + + entity:SetModel(model) + entity:SetAngles(ang) + entity:SetPos(pos) + entity:Spawn() + entity:SetPlayer(player) + entity.player = player + + if !entity then return false end + + entity:SetPos(trace.HitPos - trace.HitNormal * entity:OBBMins().z) + local constraint = WireLib.Weld(entity, trace.Entity, trace.PhysicsBone, true) + + undo.Create("wire_expression2") + undo.AddEntity(entity) + undo.SetPlayer(player) + undo.AddEntity(constraint) + undo.Finish() + + player:AddCleanup("wire_expressions", entity) + + entity:Prepare(player) + player:SendLua("wire_expression2_upload()") + return true + end + + function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local player = self:GetOwner() + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_expression2" && (trace.Entity:GetPlayer() == player || wire_expression2_protected:GetFloat() == 0)) then + trace.Entity:Reset() + return true + else + return false + end + end + + function MakeWireExpression2(player, Pos, Ang, model, buffer, name, inputs, outputs, vars) + if !player:CheckLimit("wire_expressions") then return false end + + local entity = ents.Create("gmod_wire_expression2") + if !entity:IsValid() then return false end + + entity:SetModel(model) + entity:SetAngles(Ang) + entity:SetPos(Pos) + entity:Spawn() + entity:SetPlayer(player) + entity.player = player + + buffer = string.Replace(string.Replace(buffer,"£","\""),"€","\n") + + entity:SetOverlayText("Expression 2\n" .. name) + entity.buffer = buffer + + entity.Inputs = WireLib.AdjustSpecialInputs(entity, inputs[1], inputs[2]) + entity.Outputs = WireLib.AdjustSpecialOutputs(entity, outputs[1], outputs[2]) + entity:Setup(buffer, true) + + if !entity.error then + for k,v in pairs(vars) do + entity.context.vars[k] = v + end + + entity.duped = true + entity:Execute() + entity.duped = false + end + + player:AddCount("wire_expressions", entity) + player:AddCleanup("wire_expressions", entity) + return entity + end + + duplicator.RegisterEntityClass("gmod_wire_expression2", MakeWireExpression2, "Pos", "Ang", "Model", "_original", "_name", "_inputs", "_outputs", "_vars") + + function TOOL:RightClick(trace) + if trace.Entity:IsPlayer() then return false end + + local player = self:GetOwner() + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_expression2" && (trace.Entity:GetPlayer() == player || wire_expression2_protected:GetFloat() == 0 || wire_expression2_protected:GetFloat() == 2)) then + trace.Entity:SendCode(player) + trace.Entity:Prepare(player) + return true + end + + player:SendLua("openE2Editor()") + return false + end + + function TOOL:Think() + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() || self.GhostEntity:GetModel() != self:GetModel() then + self:MakeGhostEntity(self:GetModel(), Vector(0, 0, 0), Angle(0, 0, 0)) + end + self:UpdateGhostWireExpression2(self.GhostEntity, self:GetOwner()) + end + + function TOOL:UpdateGhostWireExpression2(entity, player) + if !entity or !entity:IsValid() then return end + + local trace = util.TraceLine(utilx.GetPlayerTrace(player, player:GetCursorAimVector())) + if !trace.Hit then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_expression2" || trace.Entity:IsPlayer()) then + entity:SetNoDraw(true) + else + local ang = trace.HitNormal:Angle() + ang.pitch = ang.pitch + 90 + + entity:SetPos(trace.HitPos - trace.HitNormal * entity:OBBMins().z) + entity:SetAngles(ang) + entity:SetNoDraw(false) + end + end + + local prevmodel,prevvalid + function validModelCached(model) + if model ~= prevmodel then + prevmodel = model + prevvalid = util.IsValidModel(model) + end + return prevvalid + end + + function TOOL:GetModel() + local model = self:GetClientInfo("model") + local size = self:GetClientInfo("size") + + if model and size then + local modelname, modelext = model:match("(.*)(%..*)") + if not modelext then return model end + local newmodel = modelname .. size .. modelext + if validModelCached(newmodel) then + return newmodel + else + return model + end + end + end + +elseif CLIENT then + + local dir + local lastclick = CurTime() + local download = {} + local Validation + + function wire_expression2_upload() + if( wire_expression2_editor == nil ) then initE2Editor() end + local result = wire_expression2_validate(wire_expression2_editor:GetCode()) + if result then + WireLib.AddNotify(result, NOTIFY_ERROR, 7, NOTIFYSOUND_DRIP3) + return + end + + transfer(wire_expression2_editor:GetCode()) + end + + function wire_expression2_download(um) + if(!um) then return end + local chunks = um:ReadShort() + if(!download.downloading) then + download.downloading = true + download.chunks = chunks + download.current = -1 + download.code = "" + download.name = um:ReadString() + return + end + + if( download.current + 1 == chunks ) then + download.current = chunks + download.code = download.code .. um:ReadString() + else + download = {} + end + + if( download.downloading && download.chunks == chunks) then + if( wire_expression2_editor == nil ) then initE2Editor() end + wire_expression2_editor:Open(download.name, download.code) + wire_expression2_editor.chip = true + download = {} + return + end + + end + + usermessage.Hook("wire_expression2_download", wire_expression2_download) + + function TOOL.BuildCPanel(panel) + panel:ClearControls() + panel:AddControl("Header", { Text = "#Tool_wire_expression2_name", Description = "#Tool_wire_expression2_desc" }) + + //ModelPlug_AddToCPanel(panel, "expr2", "wire_expression2", nil, nil, nil, 2) + + panel:AddControl("ComboBox", { + MenuButton = "0", + Options = { + ["Normal"] = { wire_expression2_size = "" }, + ["Mini"] = { wire_expression2_size = "_mini" }, + ["Nano"] = { wire_expression2_size = "_nano" }, + } + }) + + panel:AddControl("MaterialGallery", { + Height = "100", + Width = "100", + Rows = 2, + Stretch = false, + ConVar = "wire_expression2_select", + Options = { + ["Modern"] = { wire_expression2_select = "Modern", Value = "Modern", Material = "beer/wiremod/gate_e2", wire_expression2_model = "models/beer/wiremod/gate_e2.mdl" }, + ["Expression"] = { wire_expression2_select = "Expression", Value = "Expression", Material = "models/expression 2/exprssn", wire_expression2_model = "models/expression 2/cpu_expression.mdl" }, + ["Microchip"] = { wire_expression2_select = "Microchip", Value = "Microchip", Material = "models/expression 2/mcrochp", wire_expression2_model = "models/expression 2/cpu_microchip.mdl" }, + ["Interface"] = { wire_expression2_select = "Interface", Value = "Interface", Material = "models/expression 2/intrfce", wire_expression2_model = "models/expression 2/cpu_interface.mdl" }, + ["Controller"] = { wire_expression2_select = "Controller", Value = "Controller", Material = "models/expression 2/cntrllr", wire_expression2_model = "models/expression 2/cpu_controller.mdl" }, + ["Processor"] = { wire_expression2_select = "Processor", Value = "Processor", Material = "models/expression 2/prcssor", wire_expression2_model = "models/expression 2/cpu_processor.mdl" }, + } + }) + + if( wire_expression2_editor == nil ) then initE2Editor() end + + local FileBrowser = vgui.Create("wire_expression2_browser" , panel) + panel:AddPanel(FileBrowser) + FileBrowser:Setup("Expression2") + FileBrowser:SetSize(235,400) + function FileBrowser:OnFileClick() + if( wire_expression2_editor == nil ) then initE2Editor() end + + if(dir == self.File.FileDir and CurTime() - lastclick < 1) then + wire_expression2_editor:Open(dir) + else + lastclick = CurTime() + dir = self.File.FileDir + wire_expression2_editor:LoadFile(dir) + Validation:Validate() + end + end + + Validation = vgui.Create("Label" , panel) + panel:AddPanel(Validation) + Validation.OnMousePressed = function(panel) panel:Validate() end + Validation.Validate = function(panel) + local errors = wire_expression2_validate(wire_expression2_editor:GetCode()) + if(!errors) then + panel:SetText("Validation Successful") + else + panel:SetText("Error in file") + end + end + Validation:SetText("Click to validate...") + local OpenEditor = vgui.Create("DButton" , panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetTall(30) + OpenEditor:SetText("Open Editor") + OpenEditor.DoClick = function(button) + wire_expression2_editor:Open() + end + + local NewExpression = vgui.Create("DButton" , panel) + panel:AddPanel(NewExpression) + NewExpression:SetTall(30) + NewExpression:SetText("New Expression") + NewExpression.DoClick = function(button) + wire_expression2_editor:Open() + wire_expression2_editor:NewScript() + end + + end + + function initE2Editor() + wire_expression2_editor = vgui.Create( "Expression2EditorFrame") + wire_expression2_editor:Setup("Expression 2 Editor","Expression2","E2") + end + + function openE2Editor() + if( wire_expression2_editor == nil ) then initE2Editor() end + wire_expression2_editor:Open() + end + + function transfer(code) + local encoded = E2Lib.encode(code) + local length = encoded:len() + local chunks = math.ceil(length / 480) + + Expression2SetProgress(0) + RunConsoleCommand("wire_expression_upload_begin", code:len(), chunks) + + timer.Create("wire_expression_upload", 1/60, chunks, transfer_callback, { encoded, 1, chunks }) + end + + function transfer_callback(state) + local i = state[2] - 1 + + Expression2SetProgress(math.Round((state[2] / state[3]) * 100)) + RunConsoleCommand("wire_expression_upload_data", state[1]:sub(i * 480 + 1, (i + 1) * 480)) + + if state[2] == state[3] then + timer.Create("wire_expression_upload_reset", 0.5, 1, function() Expression2SetProgress(nil) end ) + + timer.Destroy("wire_expression_upload") + RunConsoleCommand("wire_expression_upload_end") + end + + state[2] = state[2] + 1 + end + + /******************************************************************************\ + Expression 2 Tool Screen for Garry's Mod + Andreas "Syranide" Svensson, me@syranide.com + \******************************************************************************/ + + surface.CreateFont("Arial", 40, 1000, true, false, "Expression2ToolScreenFont") + surface.CreateFont("Arial", 30, 1000, true, false, "Expression2ToolScreenSubFont") + + local percent = nil + local name = "Unnamed" + + function Expression2SetName(n) + name = n + if !name then + name = "Unnamed" + return + end + + surface.SetFont("Expression2ToolScreenSubFont") + local ww = surface.GetTextSize("...") + + local w, h = surface.GetTextSize(name) + if w < 240 then return end + + while true do + local w, h = surface.GetTextSize(name) + if w < 240 - ww then break end + name = string.sub(name, 1, -2) + end + + name = string.Trim(name) .. "..." + end + + function Expression2SetProgress(p) + percent = p + end + + function DrawTextOutline(text, font, x, y, color, xalign, yalign, bordercolor, border) + for i=0,8 do + draw.SimpleText(text, font, x + border * math.sin(i * math.pi / 4), y + border * math.cos(i * math.pi / 4), bordercolor, xalign, yalign) + end + + draw.SimpleText(text, font, x, y, color, xalign, yalign) + end + + local CogColor = Color(150, 34, 34, 255) + local CogTexture = surface.GetTextureID("expression 2/cog") + if CogTexture == surface.GetTextureID("texturemissing") then CogTexture = nil end + + function TOOL:RenderToolScreen() + cam.Start2D() + + surface.SetDrawColor(32, 32, 32, 255) + surface.DrawRect(0, 0, 256, 256) + + if CogTexture then + if percent then + ToColor = Color(34, 150, 34, 255) + else + ToColor = Color(150, 34, 34, 255) + end + + CogDelta = 750 * FrameTime() + + CogColor.r = CogColor.r + math.max(-CogDelta, math.min(CogDelta, ToColor.r - CogColor.r)) + CogColor.g = CogColor.g + math.max(-CogDelta, math.min(CogDelta, ToColor.g - CogColor.g)) + CogColor.b = CogColor.b + math.max(-CogDelta, math.min(CogDelta, ToColor.b - CogColor.b)) + + surface.SetTexture(CogTexture) + surface.SetDrawColor(CogColor.r, CogColor.g, CogColor.b, 255) + surface.DrawTexturedRectRotated(256, 256, 455, 455, RealTime() * 10) + surface.DrawTexturedRectRotated(30, 30, 227.5, 227.5, RealTime() * -20 + 12.5) + end + + surface.SetFont("Expression2ToolScreenFont") + local w, h = surface.GetTextSize(" ") + surface.SetFont("Expression2ToolScreenSubFont") + local w2, h2 = surface.GetTextSize(" ") + + if percent then + surface.SetFont("Expression2ToolScreenFont") + local w, h = surface.GetTextSize("Uploading") + DrawTextOutline("Uploading", "Expression2ToolScreenFont", 128, 128, Color(224, 224, 224, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, Color(0, 0, 0, 255), 4) + draw.RoundedBox(4, 128 - w/2 - 2, 128 + h / 2 - 0, (w*percent)/100 + 4, h2 - 4, Color(0, 0, 0, 255)) + draw.RoundedBox(2, 128 - w/2 + 2, 128 + h / 2 + 4, (w*percent)/100 - 4, h2 - 12, Color(224, 224, 224, 255)) + elseif name then + DrawTextOutline("Expression 2", "Expression2ToolScreenFont", 128, 128, Color(224, 224, 224, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, Color(0, 0, 0, 255), 4) + DrawTextOutline(name, "Expression2ToolScreenSubFont", 128, 128 + (h+h2) / 2 - 4, Color(224, 224, 224, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, Color(0, 0, 0, 255), 4) + end + + cam.End2D() + end +end + +/*************************** 'in editor' animation ****************************/ + +if SERVER then + + /************************* client-side event handling *************************/ + -- this might fit better elsewhere + + local wire_expression2_event = {} + + concommand.Add("wire_expression2_event", function(ply, command, args) + local handler = wire_expression2_event[args[1]] + if not handler then return end + return handler(ply, args) + end) + + -- actual editor open/close handlers + + function wire_expression2_event.editor_open(ply, args) + local rp = RecipientFilter() + rp:AddAllPlayers() + + umsg.Start("wire_expression2_editor_status", rp) + umsg.Entity(ply) + umsg.Bool(true) + umsg.End() + end + + function wire_expression2_event.editor_close(ply, args) + local rp = RecipientFilter() + rp:AddAllPlayers() + + umsg.Start("wire_expression2_editor_status", rp) + umsg.Entity(ply) + umsg.Bool(false) + umsg.End() + end + +elseif CLIENT then + + local busy_players = {} + hook.Add("EntityRemoved", "wire_expression2_busy_animation", function(ply) + busy_players[ply] = nil + end) + + local emitter = ParticleEmitter(Vector(0,0,0)) + + usermessage.Hook("wire_expression2_editor_status", function(um) + local ply = um:ReadEntity() + local status = um:ReadBool() + + if not ply:IsValid() then return end + if ply == LocalPlayer() then return end + + busy_players[ply] = status or nil + end) + + local rolldelta = math.rad(80) + timer.Create("wire_expression2_editor_status", 1, 0, function() + rolldelta = -rolldelta + for ply,_ in pairs(busy_players) do + local BoneIndx = ply:LookupBone("ValveBiped.Bip01_Head1") + local BonePos, BoneAng = ply:GetBonePosition( BoneIndx ) + local particle = emitter:Add("expression 2/cog_world", BonePos+Vector(0,0,16)) + if particle then + particle:SetColor(150,34,34,255) + particle:SetVelocity(Vector(0,0,17)) + + particle:SetDieTime(3) + particle:SetLifeTime(0) + + particle:SetStartSize(10) + particle:SetEndSize(10) + + particle:SetStartAlpha(255) + particle:SetEndAlpha(0) + + particle:SetRollDelta(rolldelta) + end + end + end) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_eyepod.lua b/lua/weapons/gmod_tool/stools/wire_eyepod.lua new file mode 100644 index 0000000000..54a4c1e51e --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_eyepod.lua @@ -0,0 +1,342 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Eye Pod" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" + +/* If we're running on the client, setup the description strings */ +if ( CLIENT ) then + //tool hud lang + language.Add( "Tool_wire_eyepod_name", "Eye Pod Tool (Wire)" ) + language.Add( "Tool_wire_eyepod_desc", "Spawns an Eye Pod Mouse Controller." ) + language.Add( "Tool_wire_eyepod_0", "Primary: Create/Update Controller Secondary: Link controller Reload: Unlink EyePod/Cancel Current Link" ) + language.Add("Tool_wire_eyepod_1", "Now select the pod to link to.") + + //panel control lang + language.Add( "WireEyePod_DefaultToZero", "Default Outputs To Zero When Inactive" ) + language.Add( "WireEyePod_CumulativeOutput", "Output Cumulative Mouse Position" ) + + //management lang + language.Add( "undone_Wire Eye Pod", "Undone Wire Eye Pod" ) +elseif (SERVER) then + CreateConVar('sbox_maxwire_eyepods', 15) +end + +//console varibles +TOOL.ClientConVar[ "DefaultToZero" ] = "1" +TOOL.ClientConVar[ "CumulativeOutput" ] = "0" + +//clamps +TOOL.ClientConVar[ "XMin" ] = "0" +TOOL.ClientConVar[ "XMax" ] = "0" +TOOL.ClientConVar[ "YMin" ] = "0" +TOOL.ClientConVar[ "YMax" ] = "0" + + +cleanup.Register( "wire_eyepods" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if (CLIENT) then return true end + + if ( !self:GetSWEP():CheckLimit( "wire_eyepods" ) ) then return false end + + /* Setup all of our local variables */ + local ply = self:GetOwner() + + //get numbers from client + local DefaultToZero = self:GetClientNumber("DefaultToZero") + local CumulativeOutput = self:GetClientNumber("CumulativeOutput") + local ShowRateOfChange = 1 + if (CumulativeOutput == 1) then + ShowRateOfChange = 0 + else + ShowRateOfChange = 1 + end + //set the default to zero to one if you are showing the mouse position instead + if (ShowRateOfChange == 1) then DefaultToZero = 1 end + //get clamp + local ClampXMin = self:GetClientNumber("XMin") + local ClampXMax = self:GetClientNumber("XMax") + local ClampYMin = self:GetClientNumber("YMin") + local ClampYMax = self:GetClientNumber("YMax") + local ClampX = 0 + local ClampY = 0 + //test clamp + if ( (ClampXMin != 0 or ClampXMax != 0) and (ClampYMin != 0 or ClampYMax != 0) and + ClampXMin != ClampXMax and ClampYMin != ClampYMax and + ClampXMin < ClampXMax and ClampYMin < ClampYMax ) then + + ClampXMin = self:GetClientNumber("XMin") + ClampXMax = self:GetClientNumber("XMax") + ClampYMin = self:GetClientNumber("YMin") + ClampYMax = self:GetClientNumber("YMax") + + ClampX = 1 + ClampY = 1 + elseif( (ClampXMin == 0 and ClampXMax == 0) or (ClampYMin == 0 or ClampYMax == 0) )then + if(ClampXMin == 0 and ClampXMax == 0 and (ClampYMin != 0 or ClampYMax != 0)) then + ClampX = 0 + ClampY = 1 + ClampYMin = self:GetClientNumber("YMin") + ClampYMax = self:GetClientNumber("YMax") + elseif(ClampYMin == 0 and ClampYMax == 0 and (ClampXMin != 0 or ClampXMax != 0)) then + ClampX = 1 + ClampY = 0 + ClampXMin = self:GetClientNumber("XMin") + ClampXMax = self:GetClientNumber("XMax") + else + ClampX = 0 + ClampY = 0 + end + else + WireLib.AddNotify(ply, "Invalid Clamping of Wire EyePod Values!", NOTIFY_ERROR, 5, NOTIFYSOUND_DRIP1) + return false + end + + + //update the eyepod + if (trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_eyepod" and trace.Entity.pl == ply) then + trace.Entity:Setup(DefaultToZero,ShowRateOfChange, ClampXMin, ClampXMax, ClampYMin, ClampYMax, ClampX, ClampY) + return true + end + + /* Normal to hit surface */ + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + /* Make the EyePod */ + local ent = MakeWireEyePod(ply, trace.HitPos, Ang, self.Model, DefaultToZero, ShowRateOfChange, ClampXMin, ClampXMax, ClampYMin, ClampYMax, ClampX, ClampY) + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + /* Weld it to the surface, as long as it isn't the ground */ + if (!trace.HitWorld) then + local const = WireLib.Weld(ent, trace.Entity, trace.PhysicsBone, true) + end + + /* Add it to the undo list */ + undo.Create("Wire Eye Pod") + undo.AddEntity(ent) + if (!(const == nil)) then + undo.AddEntity(const) + end + undo.SetPlayer(ply) + undo.Finish() + + ply:AddCleanup( "wire_eyepods", ent ) + + return true +end + +//link the eyepod to the vehicle +function TOOL:RightClick(trace) + if ( CLIENT ) then return true end + if self:GetStage() == 0 and trace.Entity:GetClass() == "gmod_wire_eyepod" then + self.PodCont = trace.Entity + if self.PodCont.pod and self.PodCont.pod:IsValid() and self.PodCont.pod.AttachedWireEyePod then + self.PodCont.pod.AttachedWireEyePod = nil + self.PodCont.pod = nil + end + self:SetStage(1) + return true + elseif self:GetStage() == 1 and trace.Entity.GetPassenger then + if trace.Entity.AttachedWireEyePod then + self:GetOwner():ChatPrint("Pod Already Has An EyePod Linked To It!") + return false + end + local Success = self.PodCont:PodLink(trace.Entity) + if (Success == false) then + self:GetOwner():ChatPrint("Error: Cannot Link Eye Pod!") + return false + end + self:SetStage(0) + self.PodCont = nil + self:GetOwner():ChatPrint("Wire Eye Pod Linked") + return true + else + return false + end +end + +function TOOL:Reload(trace) + if ( CLIENT ) then return true end + + if self:GetStage() == 1 then + self:SetStage(0) + self.PodCont = nil + return false + elseif self:GetStage() == 0 and trace.Entity and trace.Entity:GetClass() == "gmod_wire_eyepod" then + self:SetStage(0) + self.PodCont = nil + trace.Entity:PodLink(nil) + self:GetOwner():ChatPrint("Wire Eye Pod Unlinked") + return true + end +end + +if (SERVER) then + /* Makes an EyePod */ + function MakeWireEyePod(pl, Pos, Ang, model, DefaultToZero, ShowRateOfChange, ClampXMin, ClampXMax, ClampYMin, ClampYMax, ClampX, ClampY) + if !pl:CheckLimit( "wire_eyepods" ) then return false end + local ent = ents.Create("gmod_wire_eyepod") + if !ent:IsValid() then return false end + ent:SetAngles(Ang) + ent:SetPos(Pos) + ent:SetModel(model) + ent:Spawn() + ent:SetPlayer(pl) + ent:Setup(DefaultToZero,ShowRateOfChange,ClampXMin,ClampXMax,ClampYMin,ClampYMax, ClampX, ClampY) + + ent:GetTable():SetPlayer( pl ) + + local ttable = { + DefaultToZero = DefaultToZero, + ShowRateOfChange = ShowRateOfChange, + ClampXMin = ClampXMin, + ClampXMax = ClampXMax, + ClampYMin = ClampYMin, + ClampYMax = ClampYMax, + ClampX = ClampX, + ClampY = ClampY, + pl = pl + } + table.Merge(ent:GetTable(), ttable ) + + pl:AddCount( "wire_eyepods", ent ) + + return ent + end + + /* Register us for duplicator compatibility */ + duplicator.RegisterEntityClass("gmod_wire_eyepod", MakeWireEyePod, "Pos", "Ang", "Model", "DefaultToZero", "ShowRateOfChange" , "ClampXMin" , "ClampXMax" , "ClampYMin" , "ClampYMax" , "ClampX", "ClampY") +end + +function TOOL:UpdateGhostWireEyePod( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_eyepod" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireEyePod( self.GhostEntity, self:GetOwner() ) +end + +-------------------------------------- TOOL Menu --------------------------------------------------- +//TODO: Figure out a way for dynamic panels to work with check boxes (check boxes that use concommands instead of convars default to 1 allways) +//check for client +if (CLIENT) then + + function Wire_EyePod_Menu(panel) + panel:ClearControls() + + panel:AddControl("Header", { + Text = "#Tool_wire_eyepod_name", + Description = "#Tool_wire_eyepod_desc" + }) + + //preset chooser + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_eyepod", + + Options = { + Default = { + wire_eyepod_DefaultToZero = "1", + wire_eyepod_CumulativeOutput = "0", + wire_eyepod_XMin = "0", + wire_eyepod_XMax = "0", + wire_eyepod_YMin = "0", + wire_eyepod_YMax = "0" + } + }, + + CVars = { + [0] = "wire_eyepod_DefaultToZero", + [1] = "wire_eyepod_CumulativeOutput", + [2] = "wire_eyepod_XMin", + [3] = "wire_eyepod_XMax", + [4] = "wire_eyepod_YMin", + [5] = "wire_eyepod_YMax" + } + }) + + panel:AddControl("CheckBox", { + Label = "#WireEyePod_CumulativeOutput", + Command = "wire_eyepod_CumulativeOutput" + }) + + panel:AddControl("CheckBox", { + Label = "#WireEyePod_DefaultToZero", + Command = "wire_eyepod_DefaultToZero" + }) + + //clamps + panel:AddControl( "Label", { + Text = "\nClamp the output of the EyePod. \nSet both sliders to 0 to remove the clamp in that axis.", + Description = "Clamps the outputs of the EyePod. Set to 0 not to clamp in that axis"} ) + + panel:AddControl( "Slider", { + Label = "X Min", + Type = "Float", + Min = -2000, + Max = 2000, + Command = "wire_eyepod_XMin", + Description = "Clamps the output of the EyePod's X to this minimum"} ) + + panel:AddControl( "Slider", { + Label = "X Max", + Type = "Float", + Min = -2000, + Max = 2000, + Command = "wire_eyepod_XMax", + Description = "Clamps the output of the EyePod's X to this maximum"} ) + panel:AddControl( "Slider", { + Label = "Y Min", + Type = "Float", + Min = -2000, + Max = 2000, + Command = "wire_eyepod_YMin", + Description = "Clamps the output of the EyePod's Y to this minimum"} ) + + panel:AddControl( "Slider", { + Label = "Y Max", + Type = "Float", + Min = -2000, + Max = 2000, + Command = "wire_eyepod_YMax", + Description = "Clamps the output of the EyePod's Y to this maximum"} ) + end + + function TOOL.BuildCPanel( panel ) + Wire_EyePod_Menu(panel) + end + +end diff --git a/lua/weapons/gmod_tool/stools/wire_fx_emitter.lua b/lua/weapons/gmod_tool/stools/wire_fx_emitter.lua new file mode 100644 index 0000000000..d43eb53e7a --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_fx_emitter.lua @@ -0,0 +1,208 @@ + +TOOL.Category = "Wire - Render" +TOOL.Name = "Wire FX Emitter" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +TOOL.ClientConVar[ "Effect" ] = "sparks" +TOOL.ClientConVar[ "Delay" ] = "0.07" +TOOL.ClientConVar[ "Weldworld" ] = "0" + +if SERVER then + CreateConVar('sbox_maxwire_fx_emitter', 20) +end + +cleanup.Register( "wire_fx_emitter" ) + +// Add Default Language translation (saves adding it to the txt files) +if ( CLIENT ) then + + language.Add( "Tool_wire_fx_emitter_name", "Wire FX Emitter" ) + language.Add( "Tool_wire_fx_emitter_desc", "Wire FX Emitter Emits effects eh?" ) + language.Add( "Tool_wire_fx_emitter_0", "Click somewhere to spawn a wire fx emitter. Click on an existing wire fx emitter to update it." ) + + language.Add( "Undone_wire_fx_emitter", "Undone Wire FX Emitter" ) + language.Add( "Cleanup_wire_fx_emitter", "Wire FX Emitter" ) + language.Add( "Cleaned_wire_fx_emitter", "Cleaned up all Wire FX Emitters" ) + +end + + +function TOOL:LeftClick( trace ) + + worldweld = worldweld or false + + if ( trace.Entity && trace.Entity:IsPlayer() ) then return false end + + if (CLIENT) then return true end + + if !self:GetSWEP():CheckLimit( "wire_fx_emitter" ) then return false end + + // If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + local ply = self:GetOwner() + local delay = self:GetClientNumber( "Delay" ) + local effect = self:GetClientInfo( "Effect" ) + local worldweld = self:GetClientNumber( "Weldworld" ) ~= 0 + + effect = ComboBox_Wire_FX_Emitter_Options[effect] + + if effect<1 then return false end + + // Safe(ish) limits + delay = math.Clamp( delay, 0.05, 20 ) + + // We shot an existing emitter - just change its values + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_fx_emitter" && trace.Entity:GetTable().pl == ply ) then + + if !trace.Entity.Inputs.Delay.Src then trace.Entity:GetTable():SetDelay( delay ) end + if !trace.Entity.Inputs.On.Src then trace.Entity:GetTable():SetEffect( effect ) end + return true + + end + + if ( !self:GetSWEP():CheckLimit( "emitters" ) ) then return false end + + if ( trace.Entity != NULL && (!trace.Entity:IsWorld() || worldweld) ) then + + trace.HitPos = trace.HitPos + trace.HitNormal * -5 + + else + + trace.HitPos = trace.HitPos + trace.HitNormal * 1.75 + + end + + local ang = trace.HitNormal:Angle() + + local wire_fx_emitter = MakeWireFXEmitter( ply, trace.HitPos, ang, "models/props_lab/tpplug.mdl", delay, effect ) + + local weld + + // Don't weld to world + --TODO: use wirelib weld. + if ( trace.Entity != NULL && (!trace.Entity:IsWorld() || worldweld) ) then + + weld = constraint.Weld( wire_fx_emitter, trace.Entity, 0, trace.PhysicsBone, 0, true, true ) + + // >:( + wire_fx_emitter:GetPhysicsObject():EnableCollisions( false ) + wire_fx_emitter:GetTable().nocollide = true + + end + + undo.Create("wire_fx_emitter") + undo.AddEntity( wire_fx_emitter ) + undo.AddEntity( weld ) + undo.SetPlayer( ply ) + undo.Finish() + + return true + +end + +function TOOL:RightClick( trace ) + return self:LeftClick( trace, true ) +end + +if (SERVER) then + + function MakeWireFXEmitter( ply, Pos, Ang, model, delay, effect, nocollide ) + + if ( !ply:CheckLimit( "wire_fx_emitter" ) ) then return nil end + + local wire_fx_emitter = ents.Create( "gmod_wire_fx_emitter" ) + if (!wire_fx_emitter:IsValid()) then return false end + + wire_fx_emitter:SetAngles( Ang ) + wire_fx_emitter:SetPos( Pos ) + wire_fx_emitter:SetModel(model or "models/props_lab/tpplug.mdl") + wire_fx_emitter:Spawn() + + wire_fx_emitter:GetTable():SetDelay( delay ) + wire_fx_emitter:GetTable():SetEffect( effect ) + wire_fx_emitter:GetTable():SetPlayer( ply ) + + if ( nocollide == true ) then wire_fx_emitter:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + pl = ply, + nocollide = nocollide + } + + table.Merge( wire_fx_emitter:GetTable(), ttable ) + + ply:AddCount( "wire_fx_emitters", wire_fx_emitter ) + ply:AddCleanup( "wire_fx_emitter", wire_fx_emitter ) + + return wire_fx_emitter + + end + + duplicator.RegisterEntityClass( "gmod_wire_fx_emitter", MakeWireFXEmitter, "Pos", "Ang", "Model", "effect", "nocollide" ) + + function TOOL:UpdateGhostWireFXEmitter( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_fx_emitter" ) then + ent:SetNoDraw( true ) + return + end + + local worldweld = self:GetClientNumber( "Weldworld" ) ~= 0 + if ( !trace.Entity:IsWorld() || worldweld ) then + ent:SetPos( trace.HitPos + trace.HitNormal * -5 ) + else + ent:SetPos( trace.HitPos + trace.HitNormal * 1.75 ) + end + + ent:SetAngles( trace.HitNormal:Angle() ) + + ent:SetNoDraw( false ) + end + + function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() ) then + self:MakeGhostEntity( "models/props_lab/tpplug.mdl", Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireFXEmitter( self.GhostEntity, self:GetOwner() ) + end + +end + + +// NOTE the . instead of : here - there is no 'self' argument!! +// This is just a function on the table - not a member function! + +function TOOL.BuildCPanel( CPanel ) + // HEADER + CPanel:AddControl( "Header", { Text = "#Tool_wire_fx_emitter_name", Description = "#Tool_wire_fx_emitter_desc" } ) + + // EMITTERS + local params = { Label = "#Effect", Height = "250", MenuButton="0", Options = {} } + + for k,_ in pairs(ComboBox_Wire_FX_Emitter_Options) do + params.Options[ "#wire_fx_emitter_" .. k ] = { wire_fx_emitter_Effect = k } + end + + CPanel:AddControl( "ListBox", params ) + + // DELAY + CPanel:AddControl( "Slider", { Label = "How much time between the effect", + Type = "Float", + Min = 0.05, + Max = 5, + Command = "wire_fx_emitter_Delay" } ) + + CPanel:AddControl("CheckBox", { + Label = "Allow weld to world", + Command = "wire_fx_emitter_Weldworld" + }) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_gate_expression.lua b/lua/weapons/gmod_tool/stools/wire_gate_expression.lua new file mode 100644 index 0000000000..cf86d7528d --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_gate_expression.lua @@ -0,0 +1,798 @@ +// Written by Syranide, me@syranide.com + +-- high speed links to memory some how would be nice read() write() ? +-- fix playsound for errors in "init.lua" +-- EXPECT should report line of PREVIOUS token (not the one that it is on when reporting an error) +-- the parser should verify which functions actually exists +-- make better transfer from client->server (server->client needed?) ... limit of like 100 chars per line now + +TOOL.Category = "Wire - Control" +TOOL.Name = "Chip - Expression 1" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +--[[function StringTrimRight(str) + local i = string.len(str) + while i > 0 do + local s = string.sub(str, i, i) + if s == " " or s == "\n" or s == "\r" or s == "\t" then else break end + i = i - 1 + end + return string.sub(str, 1, i) +end--]] + +local function StringExplode(sep, str) + local lines = {} + + while str and str ~= "" do + local pos = string.find(str, sep, 1, true) + if pos == nil then + table.insert(lines, str) + break + end + + if pos > 1 then + table.insert(lines, string.sub(str, 1, pos - 1)) + else + table.insert(lines, "") + end + + str = string.sub(str, pos + 1) + end + + return lines +end + +local function WireExpressionGetLines(player) + local lines = {} + local blank = 0 + for i = 1,60 do + local line = player:GetInfo('wire_gate_expression_line' .. i) + if line and line ~= "" then + while blank > 0 do table.insert(lines, "") blank = blank - 1 end + table.insert(lines, line) + else + blank = blank + 1 + end + end + return lines +end + +function MakeWireGateExpressionParser(lines, inputs, outputs) + local code = "" + for _,line in ipairs(lines) do + local pos = string.find(line, '#', 1, true) + if pos then line = string.sub(line, 0, pos - 1) end + + code = code .. line .. "\n" + end + return WireGateExpressionParser:New(code, inputs, outputs) +end + +if CLIENT then + language.Add("Tool_wire_gate_expression_name", "Expression Gate Tool (Wire)") + language.Add("Tool_wire_gate_expression_desc", "Spawns an expression gate for use with the wire system.") + language.Add("Tool_wire_gate_expression_0", "Primary: Create/Update Expression Gate, Secondary: Load Expression Gate, Reload: Reset Variables") + language.Add("sboxlimit_wire_gate_expression", "You've hit expression gates limit!") + language.Add("undone_wiregateexpression", "Undone Wire Expression Gate") +end + + +TOOL.ClientConVar["model"] = "models/cheeze/wires/cpu.mdl" +TOOL.ClientConVar["filename"] = "" +TOOL.ClientConVar["label"] = "" +TOOL.ClientConVar["inputs"] = "" +TOOL.ClientConVar["outputs"] = "" +TOOL.ClientConVar["hintrev"] = 0 + +for i = 1,60 do + TOOL.ClientConVar["line" .. i] = "" +end + +cleanup.Register("wire_expressions") + +function TOOL:LeftClick(trace) + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local player = self:GetOwner() + + local name = self:GetClientInfo("label") + local inputs = self:GetClientInfo("inputs") + local outputs = self:GetClientInfo("outputs") + + local lines = WireExpressionGetLines(player) + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_expression" && trace.Entity.player == player) then + local parser = VerifyWireGateExpression(player, lines, inputs, outputs) + if !parser then return false end + SetupWireGateExpression(trace.Entity, parser, name, lines, inputs, outputs) + return true + end + + if !self:GetSWEP():CheckLimit("wire_expressions") then return false end + if !util.IsValidModel(self:GetClientInfo("model")) then return false end + if !util.IsValidProp(self:GetClientInfo("model")) then return false end + + local model = self:GetClientInfo("model") + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + wire_gate = MakeWireGateExpression(player, trace.HitPos, Ang, model, name, lines, inputs, outputs) + if !wire_gate then return false end + + --wire_gate:GetPhysicsObject():EnableMotion(false) + wire_gate:SetPos(trace.HitPos - trace.HitNormal * wire_gate:OBBMins().z) + local constraint = WireLib.Weld(wire_gate, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireGateExpression") + undo.AddEntity(wire_gate) + undo.SetPlayer(player) + undo.AddEntity(constraint) + undo.Finish() + + player:AddCleanup("wire_expressions", wire_gate) + + return true +end + +function TOOL:RightClick(trace) + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local player = self:GetOwner() + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_expression" && trace.Entity.player == player) then + player:ConCommand('wire_gate_expression_filename ""') + player:ConCommand('wire_gate_expression_label "' .. trace.Entity.GateName .. '"') + player:ConCommand('wire_gate_expression_inputs "' .. trace.Entity.GateInputs .. '"') + player:ConCommand('wire_gate_expression_outputs "' .. trace.Entity.GateOutputs .. '"') + + for i = 1,60 do + local line = trace.Entity.GateLines[i] + if (line and line ~= "") then + player:SendLua('LocalPlayer():ConCommand("wire_gate_expression_line' .. i .. ' \\\"' .. line .. '\\\"")') + else + player:ConCommand('wire_gate_expression_line' .. i .. ' ""') + end + end + + player:SendLua('wire_gate_expression_filename = "(fetched expression)"') + player:SendLua('wire_gate_expression_status = "Successfully fetched \\"' .. trace.Entity.GateName .. '\\""') + player:SendLua('wire_gate_expression_label = "' .. trace.Entity.GateName .. '"') + player:SendLua('wire_gate_expression_inputs = "' .. trace.Entity.GateInputs .. '"') + player:SendLua('wire_gate_expression_outputs = "' .. trace.Entity.GateOutputs .. '"') + player:SendLua('WireGateExpressionRebuildCPanel()') + return true + else + return false + end +end + +function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local player = self:GetOwner() + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_expression" && trace.Entity.player == player) then + trace.Entity:Reset() + return true + else + return false + end +end + +function TOOL:UpdateGhostWireGateExpression(ent, player) + if !ent or !ent:IsValid() then return end + + local trace = util.TraceLine(utilx.GetPlayerTrace(player, player:GetCursorAimVector())) + if !trace.Hit then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_expression" || trace.Entity:IsPlayer()) then + ent:SetNoDraw(true) + else + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + ent:SetPos(trace.HitPos - trace.HitNormal * ent:OBBMins().z) + ent:SetAngles(Ang) + ent:SetNoDraw(false) + end +end + +function TOOL:Think() + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() || self.GhostEntity:GetModel() != self:GetClientInfo("model") then + self:MakeGhostEntity(self:GetClientInfo("model"), Vector(0, 0, 0), Angle(0, 0, 0)) + end + + self:UpdateGhostWireGateExpression(self.GhostEntity, self:GetOwner()) +end + +function TOOL.BuildCPanel(panel) + WireGateExpressionRebuildCPanel(panel) +end + +if SERVER then + function SetupWireGateExpression(entity, parser, name, lines, inputs, outputs) + entity.GateName = name + entity.GateLines = lines + entity.GateInputs = inputs + entity.GateOutputs = outputs + + entity:Setup(name, parser) + end + + function VerifyWireGateExpression(player, lines, inputs, outputs) + local parser = MakeWireGateExpressionParser(lines, inputs, outputs) + if !parser:GetError() then + return parser + else + player:SendLua('wire_gate_expression_status = "' .. parser:GetError() .. '"') + player:SendLua('WireGateExpressionRebuildCPanel()') + WireLib.AddNotify(player, '"' .. parser:GetError() .. '"', NOTIFY_ERROR, 7) + return + end + end + + function MakeWireGateExpression(player, Pos, Ang, model, name, lines, inputs, outputs) + if !player:CheckLimit("wire_expressions") then return false end + + local parser = VerifyWireGateExpression(player, lines, inputs, outputs) + if !parser then return false end + + local entity = ents.Create("gmod_wire_expression") + if !entity:IsValid() then return false end + + entity:SetModel(model) + entity:SetAngles(Ang) + entity:SetPos(Pos) + entity:Spawn() + entity:SetPlayer(player) + + SetupWireGateExpression(entity, parser, name, lines, inputs, outputs) + + table.Merge(entity:GetTable(), { player = player }) + player:AddCount("wire_expressions", entity) + return entity + end + + duplicator.RegisterEntityClass("gmod_wire_expression", MakeWireGateExpression, "Pos", "Ang", "Model", "GateName", "GateLines", "GateInputs", "GateOutputs") + +elseif CLIENT then + + function WireGateExpressionUpdateFilelist() + local fileindex, foldindex = 1, 1 + local filelist, filemap = {}, {} + local foldlist, foldmap = {}, {} + + if (file.Exists(wire_gate_expression_basefolder .. wire_gate_expression_folder) && file.IsDir(wire_gate_expression_basefolder .. wire_gate_expression_folder)) then + for key,value in pairs(file.Find(wire_gate_expression_basefolder .. wire_gate_expression_folder .. "/*")) do + if (file.IsDir(wire_gate_expression_basefolder .. wire_gate_expression_folder .. "/" .. value)) then + foldlist[value] = { wire_gate_expression_folder_cl = foldindex } + foldmap[foldindex] = wire_gate_expression_folder .. "/" .. value + foldindex = foldindex + 1 + elseif (string.sub(value, -4) == ".txt") then + filelist[string.sub(value, 1, -5)] = { wire_gate_expression_select_cl = fileindex } + filemap[fileindex] = wire_gate_expression_folder .. "/" .. string.sub(value, 1, -5) + fileindex = fileindex + 1 + end + end + end + + wire_gate_expression_filelist = filelist + wire_gate_expression_filemap = filemap + wire_gate_expression_foldlist = foldlist + wire_gate_expression_foldmap = foldmap + end + + function WireGateExpressionPanelFoldUp(player, command, args) + local lasthit + local pos = 1 + while pos <= string.len(wire_gate_expression_folder) do + if string.sub(wire_gate_expression_folder, pos, pos) == "/" then lasthit = pos end + pos = pos + 1 + end + + if lasthit and lasthit > 1 then + wire_gate_expression_folder = string.sub(wire_gate_expression_folder, 1, lasthit - 1) + else + wire_gate_expression_folder = "" + end + + WireGateExpressionUpdateFilelist() + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_foldup_cl", WireGateExpressionPanelFoldUp) + + function WireGateExpressionPanelFolder(player, command, args) + wire_gate_expression_folder = wire_gate_expression_foldmap[tonumber(args[1])] + if !wire_gate_expression_folder then wire_gate_expression_folder = "" end + + WireGateExpressionUpdateFilelist() + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_folder_cl", WireGateExpressionPanelFolder) + + function WireGateExpressionPanelValidate(player, command, args) + local inputs = player:GetInfo("wire_gate_expression_inputs") + local outputs = player:GetInfo("wire_gate_expression_outputs") + local lines = WireExpressionGetLines(player) + + local parser = MakeWireGateExpressionParser(lines, inputs, outputs) + local status = parser:GetError() + + if !status then status = "Successfully validated" end + wire_gate_expression_status = status + + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_validate_cl", WireGateExpressionPanelValidate) + + function WireGateExpressionLoad(filename) + local player = LocalPlayer() + + local code + if file.Exists(wire_gate_expression_basefolder .. "/" .. filename .. ".txt") then + code = file.Read(wire_gate_expression_basefolder .. "/" .. filename .. ".txt") + if code == nil then + wire_gate_expression_status = "Could not load \"" .. filename .. "\"" + WireGateExpressionRebuildCPanel() + return + end + else + wire_gate_expression_status = "Unable to find \"" .. filename .. "\"" + WireGateExpressionRebuildCPanel() + return + end + + local lines = StringExplode("\n", code) + + wire_gate_expression_filename = filename + player:ConCommand('wire_gate_expression_filename "' .. filename .. '"') + + if lines[1] and string.sub(lines[1], 1, 2) == "N@" then + str = string.sub(table.remove(lines, 1), 3) + player:ConCommand('wire_gate_expression_label "' .. str .. '"') + wire_gate_expression_label = str + else + player:ConCommand('wire_gate_expression_label "' .. filename .. '"') + wire_gate_expression_label = filename + end + + if lines[1] and string.sub(lines[1], 1, 2) == "I@" then + str = string.sub(table.remove(lines, 1), 3) + player:ConCommand('wire_gate_expression_inputs "' ..str .. '"') + wire_gate_expression_inputs = str + else + player:ConCommand('wire_gate_expression_inputs ""') + wire_gate_expression_inputs = "" + end + + if lines[1] and string.sub(lines[1], 1, 2) == "O@" then + str = string.sub(table.remove(lines, 1), 3) + player:ConCommand('wire_gate_expression_outputs "' .. str .. '"') + wire_gate_expression_outputs = str + else + player:ConCommand('wire_gate_expression_outputs ""') + wire_gate_expression_outputs = "" + end + + for i,line in ipairs(lines) do + player:ConCommand('wire_gate_expression_line' .. i .. ' "' .. line .. '"') + end + + for i = #lines+1,60 do + player:ConCommand('wire_gate_expression_line' .. i .. ' ""') + end + + wire_gate_expression_status = "Successfully loaded \"" .. filename .. "\"" + WireGateExpressionRebuildCPanel() + end + + function WireGateExpressionPanelLoad(player, command, args) + if (!player:IsValid() or !player:IsPlayer()) then return end + + WireGateExpressionLoad(player:GetInfo('wire_gate_expression_filename')) + end + + concommand.Add("wire_gate_expression_load_cl", WireGateExpressionPanelLoad) + + function WireGateExpressionPanelSave(player, command, args) + local str = "" + local name = player:GetInfo('wire_gate_expression_label') + local inputs = player:GetInfo('wire_gate_expression_inputs') + local outputs = player:GetInfo('wire_gate_expression_outputs') + + if name and name ~= "" then str = str .. "N@" .. name .. "\n" end + if inputs and inputs ~= "" then str = str .. "I@" .. inputs .. "\n" end + if outputs and outputs ~= "" then str = str .. "O@" .. outputs .. "\n" end + + local lines = WireExpressionGetLines(player) + for _,line in ipairs(lines) do str = str .. line .. "\n" end + + local filename = player:GetInfo('wire_gate_expression_filename') + + wire_gate_expression_filename = filename + wire_gate_expression_label = name + wire_gate_expression_inputs = inputs + wire_gate_expression_outputs = outputs + + file.Write("/" .. wire_gate_expression_basefolder .. "/" .. filename .. ".txt", str) + if file.Exists("/" .. wire_gate_expression_basefolder .. "/" .. filename .. ".txt") then + wire_gate_expression_status = "Successfully saved \"" .. filename .. "\"" + else + wire_gate_expression_status = "Could not save \"" .. filename .. "\"" + end + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_save_cl", WireGateExpressionPanelSave) + + function WireGateExpressionPanelDelete(player, command, args) + local filename = player:GetInfo('wire_gate_expression_filename') + if file.Exists(wire_gate_expression_basefolder .. "/" .. filename .. ".txt") then + file.Delete(wire_gate_expression_basefolder .. "/" .. filename .. ".txt") + if file.Exists(wire_gate_expression_basefolder .. "/" .. filename .. ".txt") then + wire_gate_expression_status = "Could not delete \"" .. filename .. "\"" + else + wire_gate_expression_status = "Successfully deleted \"" .. filename .. "\"" + end + else + wire_gate_expression_status = "Unable to find \"" .. filename .. "\"" + end + + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_delete_cl", WireGateExpressionPanelDelete) + + function WireGateExpressionPanelSelect(player, command, args) + WireGateExpressionLoad(wire_gate_expression_filemap[tonumber(args[1])]) + end + + concommand.Add("wire_gate_expression_select_cl", WireGateExpressionPanelSelect) + + function WireGateExpressionPanelRefresh(player, command, args) + WireGateExpressionUpdateFilelist() + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_refresh_cl", WireGateExpressionPanelRefresh) + + function WireGateExpressionPanelProcess(player, command, args) + local lines = {} + for i = 1,60 do + local line = player:GetInfo('wire_gate_expression_line' .. i) + if line and line ~= "" then + local split = StringExplode("\\", line) + for _,value in ipairs(split) do + table.insert(lines, value) + end + end + end + + for i,line in ipairs(lines) do + player:ConCommand('wire_gate_expression_line' .. i .. ' "' .. line .. '"') + end + + for i = #lines+1,60 do + player:ConCommand('wire_gate_expression_line' .. i .. ' ""') + end + + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_process_cl", WireGateExpressionPanelProcess) + + function WireGateExpressionPanelEdit(player, command, args) + wire_gate_expression_state = 1 + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_edit_cl", WireGateExpressionPanelEdit) + + function WireGateExpressionPanelBrowse(player, command, args) + wire_gate_expression_state = 0 + wire_gate_expression_label = player:GetInfo('wire_gate_expression_label') + wire_gate_expression_inputs = player:GetInfo('wire_gate_expression_inputs') + wire_gate_expression_outputs = player:GetInfo('wire_gate_expression_outputs') + + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_browse_cl", WireGateExpressionPanelBrowse) + + function WireGateExpressionPanelNew(player, command, args) + wire_gate_expression_state = 1 + wire_gate_expression_filename = "" + wire_gate_expression_label = "" + wire_gate_expression_inputs = "" + wire_gate_expression_outputs = "" + wire_gate_expression_status = "New expression created" + + player:ConCommand('wire_gate_expression_filename ""') + player:ConCommand('wire_gate_expression_label ""') + player:ConCommand('wire_gate_expression_inputs ""') + player:ConCommand('wire_gate_expression_outputs ""') + + for i = 1,60 do + player:ConCommand('wire_gate_expression_line' .. i .. ' ""') + end + + WireGateExpressionRebuildCPanel() + end + + concommand.Add("wire_gate_expression_new_cl", WireGateExpressionPanelNew) + + function WireGateExpressionRebuildCPanel(panel) + if panel then + WireGateExpressionDoRebuildCPanel(panel) + else + panel = GetControlPanel("wire_gate_expression") + if panel then + WireGateExpressionDoRebuildCPanel(panel) + end + + panel = GetControlPanel("wiretab_wire_gate_expression") + if panel then + WireGateExpressionDoRebuildCPanel(panel) + end + end + end + + function WireGateExpressionHide(player, command, args) + wire_gate_expression_hintrev = 1 + player:ConCommand('wire_gate_expression_hintrev 1') + WireGateExpressionRebuildCPanel(); + end + + concommand.Add("wire_gate_expression_hide_cl", WireGateExpressionHide) + + + function WireGateExpressionDoRebuildCPanel(panel) + if wire_gate_expression_state == nil then + wire_gate_expression_state = 0 + wire_gate_expression_filename = GetConVarString('wire_gate_expression_filename') + wire_gate_expression_label = GetConVarString('wire_gate_expression_label') + wire_gate_expression_inputs = GetConVarString('wire_gate_expression_inputs') + wire_gate_expression_outputs = GetConVarString('wire_gate_expression_outputs') + wire_gate_expression_basefolder = "ExpressionGate" + wire_gate_expression_folder = "" + wire_gate_expression_status = "Previous expression resumed" + wire_gate_expression_hintrev = GetConVarNumber('wire_gate_expression_hintrev') + WireGateExpressionUpdateFilelist() + + if !wire_gate_expression_filename then wire_gate_expression_filename = "" end + if !wire_gate_expression_label then wire_gate_expression_label = "" end + if !wire_gate_expression_inputs then wire_gate_expression_inputs = "" end + if !wire_gate_expression_outputs then wire_gate_expression_outputs = "" end + if !wire_gate_expression_status then wire_gate_expression_status = "" end + end + + panel:ClearControls() + + panel:AddControl("Header", { + Text = "#Tool_wire_gate_expression_name", + Description = "Written by Syranide, me@syranide.com" + }) + + ModelPlug_AddToCPanel(panel, "gate", "wire_gate_expression", "Model:", nil, "Model:") + + if wire_gate_expression_state == 0 then + --[[ + local configs = { + { 1200, 7, 19 }, + { 1080, 5, 17 }, + { 1050, 5, 16 }, + { 1024, 5, 15 }, + { 768, 3, 7 }, + { 720, 3, 5 }, + { 600, 3, 8 }, + { 480, 3, 5 }, + } + --]] + + local configs = { + { 1200, 6, 12 }, + { 1080, 4, 10 }, + { 1050, 4, 9 }, + { 1024, 4, 8 }, + { 768, 6, 12 }, + { 720, 5, 11 }, + { 600, 3, 8 }, + { 480, 3, 5 }, + } + + local config = { 0, 2, 4 } + for _,v in ipairs(configs) do + if ScrH() >= v[1] then config = v break end + end + + + panel:AddControl("Button", { + Text = "New Expression...", + Name = "New Expression...", + Command = "wire_gate_expression_new_cl" + }) + + panel:AddControl("Label", { + Label = "Location:", + Text = " " .. wire_gate_expression_folder .. "/", + }) + + panel:AddControl("Button", { + Text = "Parent Directory", + Name = "Parent Directory", + Command = "wire_gate_expression_foldup_cl" + }) + + panel:AddControl("ListBox", { + Label = "Folders", + Height = config[2] * 15 + 26, + Options = wire_gate_expression_foldlist + }) + + panel:AddControl("ListBox", { + Label = "Expressions", + Height = config[3] * 15 + 26, + Options = wire_gate_expression_filelist + }) + + panel:AddControl("Button", { + Text = "Refresh", + Name = "Refresh", + Command = "wire_gate_expression_refresh_cl" + }) + + if wire_gate_expression_filename == "" then + panel:AddControl("Label", { + Label = "Filename:", + Text = " " .. "(new expression)", + }) + else + panel:AddControl("Label", { + Label = "Filename:", + Text = " " .. wire_gate_expression_filename, + }) + end + + panel:AddControl("Label", { + Label = "Label:", + Text = " " .. wire_gate_expression_label, + }) + + panel:AddControl("Label", { + Label = "Inputs:", + Text = " " .. wire_gate_expression_inputs, + }) + + panel:AddControl("Label", { + Label = "Outputs:", + Text = " " .. wire_gate_expression_outputs, + }) + + panel:AddControl("Button", { + Text = "Edit Expression...", + Name = "Edit Expression...", + Command = "wire_gate_expression_edit_cl" + }) + + panel:AddControl("Label", { + Text = " Documentation available at wiremod.com" + }) + + panel:AddControl("Label", { + Text = " Written by Syranide, me@syranide.com" + }) + elseif wire_gate_expression_state == 1 then + panel:AddControl("Button", { + Text = "Browse Expressions...", + Name = "Browse Expressions...", + Command = "wire_gate_expression_browse_cl" + }) + + panel:AddControl("TextBox", { + Label = "Filename:", + Command = "wire_gate_expression_filename", + MaxLength = 100 + }) + + panel:AddControl("Button", { + Text = "Load", + Name = "Load", + Command = "wire_gate_expression_load_cl" + }) + + panel:AddControl("Button", { + Text = "Save", + Name = "Save", + Command = "wire_gate_expression_save_cl" + }) + + panel:AddControl("Button", { + Text = "Delete", + Name = "Delete", + Command = "wire_gate_expression_delete_cl" + }) + + panel:AddControl("Label", { + Label = "Status:", + Text = " " .. wire_gate_expression_status, + }) + + panel:AddControl("Button", { + Text = "Validate", + Name = "Validate", + Command = "wire_gate_expression_validate_cl" + }) + + //if wire_gate_expression_hintrev < 1 then + panel:AddControl("Label", { + Text = "" + }) + + panel:AddControl("Label", { + Text = "Expression Gate 1 will become deprecated and removed from WireMod sometime in the future. We advice you to use Expression 2 instead, it has a proper editor, is faster, more powerful and the syntax is almost exactly the same." + }) + + panel:AddControl("Label", { + Text = "If you have any questions or concerns feel free to visit the community at www.wiremod.com, thank you! -- Syranide" + }) + + panel:AddControl("Label", { + Text = "" + }) + + /*panel:AddControl("Button", { + Text = " Click here to hide this notification!", + Name = "Hide", + Command = "wire_gate_expression_hide_cl" + })*/ + //end + + panel:AddControl("TextBox", { + Label = "Label:", + Command = "wire_gate_expression_label", + MaxLength = 40 + }) + + panel:AddControl("TextBox", { + Label = "Inputs:", + Command = "wire_gate_expression_inputs", + MaxLength = 100 + }) + + panel:AddControl("TextBox", { + Label = "Outputs:", + Command = "wire_gate_expression_outputs", + MaxLength = 100 + }) + + panel:AddControl("Button", { + Text = "Process (split lines with a backslash \\ into multiple rows)", + Name = "Process", + Command = "wire_gate_expression_process_cl" + }) + + for i = 1,60 do + panel:AddControl("TextBox", { + Label = "Line " .. i .. ":", + Command = "wire_gate_expression_line" .. i, + MaxLength = 100 + }) + end + + panel:AddControl("Label", { + Text = " Documentation available at wiremod.com" + }) + + panel:AddControl("Label", { + Text = " Written by Syranide, me@syranide.com" + }) + end + end +end diff --git a/lua/weapons/gmod_tool/stools/wire_gps.lua b/lua/weapons/gmod_tool/stools/wire_gps.lua new file mode 100644 index 0000000000..62fb061688 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_gps.lua @@ -0,0 +1,151 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "GPS" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_gps_name", "GPS Tool (Wire)" ) + language.Add( "Tool_wire_gps_desc", "Spawns a gps for use with the wire system." ) + language.Add( "Tool_wire_gps_0", "Primary: Create/Update GPS" ) + language.Add( "sboxlimit_wire_gpss", "You've hit gpss limit!" ) + language.Add( "undone_wiregps", "Undone Wire GPS" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_gpss', 10) + ModelPlug_Register("GPS") +end + +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/gps.mdl" +TOOL.ClientConVar[ "modelsize" ] = "" +local ModelInfo = {"","",""} + +cleanup.Register( "wire_gpss" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + // If we shot a wire_gps do nothing + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_gps" && trace.Entity.pl == ply ) then + trace.Entity:Setup() + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_gpss" ) ) then return false end + + if ( !util.IsValidModel( ModelInfo[3] ) ) then return false end + if ( !util.IsValidProp( ModelInfo[3] ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + wire_gps = MakeWireGPS( ply, trace.HitPos, Ang, ModelInfo[3] ) + + local min = wire_gps:OBBMins() + wire_gps:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_gps, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireGPS") + undo.AddEntity( wire_gps ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_gpss", wire_gps ) + + return true +end + +if (SERVER) then + + function MakeWireGPS( pl, Pos, Ang, model, nocollide ) + if ( !pl:CheckLimit( "wire_gpss" ) ) then return false end + + local wire_gps = ents.Create( "gmod_wire_gps" ) + if (!wire_gps:IsValid()) then return false end + + wire_gps:SetAngles( Ang ) + wire_gps:SetPos( Pos ) + if(!model) then + wire_gps:SetModel( Model("models/jaanus/wiretool/wiretool_speed.mdl") ) + else + wire_gps:SetModel( Model(model) ) + end + wire_gps:Spawn() + + wire_gps:Setup() + wire_gps:SetPlayer(pl) + wire_gps.pl = pl + + if ( nocollide == true ) then wire_gps:GetPhysicsObject():EnableCollisions( false ) end + + pl:AddCount( "wire_gpss", wire_gps ) + + return wire_gps + end + + duplicator.RegisterEntityClass("gmod_wire_gps", MakeWireGPS, "Pos", "Ang", "Model", "nocollide") + +end //end server if + +function TOOL:UpdateGhostWireGPS( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_gps" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if ModelInfo[1]!= self:GetClientInfo( "model" ) || ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + ModelInfo[1] = self:GetClientInfo( "model" ) + ModelInfo[2] = self:GetClientInfo( "modelsize" ) + ModelInfo[3] = ModelInfo[1] + if (ModelInfo[1] && ModelInfo[2] && ModelInfo[2]!="") then + local test = string.sub(ModelInfo[1], 1, -5) .. ModelInfo[2] .. string.sub(ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + ModelInfo[3] = test + end + end + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireGPS( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_gps_name", Description = "#Tool_wire_gps_desc" }) + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_gps_modelsize = "" }, + ["mini"] = { wire_gps_modelsize = "_mini" }, + ["nano"] = { wire_gps_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "GPS", "wire_gps", "#ToolWireIndicator_Model") +end diff --git a/lua/weapons/gmod_tool/stools/wire_gpu.lua b/lua/weapons/gmod_tool/stools/wire_gpu.lua new file mode 100644 index 0000000000..8c686668d0 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_gpu.lua @@ -0,0 +1,607 @@ +TOOL.Category = "Wire - Advanced" +TOOL.Name = "Display - GPU" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if (CLIENT) then + language.Add("Tool_wire_gpu_name", "GPU Tool (Wire)") + language.Add("Tool_wire_gpu_desc", "Spawns a graphics processing unit") + language.Add("Tool_wire_gpu_0", "Primary: Create / update GPU, Secondary: Open editor")//; Secondary: Debug the GPU + language.Add("sboxlimit_wire_gpu", "You've hit GPU limit!") + language.Add("undone_wiregpu", "Undone the wire GPU") +end + +if (SERVER) then + CreateConVar('sbox_maxwire_gpus', 20) +end + +TOOL.ClientConVar["model"] = "models/props_lab/monitor01b.mdl" +TOOL.ClientConVar["filename"] = "" +TOOL.ClientConVar["packet_bandwidth"] = 60 +TOOL.ClientConVar["packet_rate_sp"] = 0.05 +TOOL.ClientConVar["packet_rate_mp"] = 0.4 +TOOL.ClientConVar["compile_rate"] = 0.05 +TOOL.ClientConVar["compile_bandwidth"] = 200 +TOOL.ClientConVar["rom"] = 1 +TOOL.ClientConVar["rom_present"] = 1 +TOOL.ClientConVar["dump_data"] = 0 + +cleanup.Register("wire_gpus") + +//============================================================================= +if (SERVER) then + GPU_SourceCode = {} + + local function AddSourceLine(pl, command, args) + GPU_SourceCode[tonumber(args[1])] = tostring(args[2]) + end + concommand.Add("wire_gpu_addsrc", AddSourceLine) + + local function ClearSource(pl, command, args) + GPU_SourceCode = {} + end + concommand.Add("wire_gpu_clearsrc", ClearSource) +end +//============================================================================= + +local function GPUStool_Version() + local SVNString = "$Revision$" + local rev = tonumber(string.sub(SVNString,12,14)) + if (rev) then + return rev + else + return 0 + end +end + +//============================================================================= + +local function CompileProgram_Timer(tool,firstpass) + if (firstpass && tool.FirstPassDone) then return end + if (!firstpass && tool.SecondPassDone) then return end + if (!tool:GetOwner()) then return end + if (!tool.LineNumber) then return end + + local SendLinesMax = tool.LineNumber + tool:GetOwner():GetInfo("wire_gpu_compile_bandwidth") + if (SendLinesMax > table.Count(GPU_SourceCode)) then SendLinesMax = table.Count(GPU_SourceCode) end + local Rate = 0 + + if (GPU_SourceCode[tostring(tool.LineNumber)]) then + if (string.len(GPU_SourceCode[tostring(tool.LineNumber)]) > 256) then + SendLinesMax = tool.LineNumber + end + end + + while (tool.LineNumber <= SendLinesMax) and (tool.GPU_Entity) do + local line = GPU_SourceCode[tonumber(tool.LineNumber)] + if (line) then + if (string.len(line) > 254) then + tool:GetOwner():PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Line "..tool.LineNumber.." too long! I compile it, but it may trigger infinite loop thing.\n") + end + if (tool.GPU_Entity.ParseProgram_ASM) then + tool.GPU_Entity:ParseProgram_ASM(line,tool.LineNumber) + end + end + + tool.LineNumber = tool.LineNumber + 1 + Rate = Rate + 1 + end + + local TimeLeft = (table.Count(GPU_SourceCode)*2 - tool.LineNumber) / Rate + if (not firstpass) then + TimeLeft = (table.Count(GPU_SourceCode) - tool.LineNumber) / Rate + end + tool.PrevRate = (tool.PrevRate*1.5+TimeLeft*0.5) / 2 + TimeLeft = math.floor(tool.PrevRate / 10) + + local TempPercent = ((tool.LineNumber-1)/table.Count(GPU_SourceCode))*100 + if (firstpass) then + if (!tool.FirstPassDone) then + tool:GetOwner():ConCommand('wire_gpu_vgui_status "Compiling ('.. TimeLeft ..' seconds left), '..tool.LineNumber..' lines processed"') + tool:GetOwner():ConCommand('wire_gpu_vgui_progress "'..math.floor(TempPercent/2)..'"') + end + else + if (!tool.SecondPassDone) then + tool:GetOwner():ConCommand('wire_gpu_vgui_status "Compiling ('.. TimeLeft ..' seconds left), '..tool.LineNumber..' lines processed"') + tool:GetOwner():ConCommand('wire_gpu_vgui_progress "'..math.floor(50+TempPercent/2)..'"') + end + end + + if (tool.LineNumber > table.Count(GPU_SourceCode)) || (TempPercent >= 100) then + if (!tool.FirstPassDone) then + tool.FirstPassDone = true + tool:Compile_Pass2() + end + if (!firstpass) && (!tool.SecondPassDone) then + tool.SecondPassDone = true + tool:Compile_End() + end + end + + if (tool.GPU_Entity.FatalError == true) then + timer.Destroy("GPUCompileTimer1") + timer.Destroy("GPUCompileTimer2") + tool:Compile_End() + end +end + +//============================================================================= + +function TOOL:StartCompile(pl) + local ent = self.GPU_Entity + if table.Count(GPU_SourceCode) == 0 then return end + + pl:PrintMessage(HUD_PRINTCONSOLE,"----> ZyeliosASM compiler - Version 2.0 (SVN REV "..GPUStool_Version()..") <----\n") + pl:PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Compiling...\n") + + pl:ConCommand('wire_gpu_vgui_open') + pl:ConCommand('wire_gpu_vgui_title "ZyeliosASM - Compiling"') + pl:ConCommand('wire_gpu_vgui_status "Initializing"') + pl:ConCommand('wire_gpu_vgui_progress "0"') + + if (self:GetClientInfo("rom") == "1") then ent.UseROM = true + else ent.UseROM = false + end + + if (self:GetClientInfo("dump_data") == "1") then + ent.MakeDump = true + ent.Dump = "Code listing:\n" + else + ent.MakeDump = false + end + + + self.FirstPassDone = false + self.SecondPassDone = false + + timer.Destroy("GPUCompileTimer1") + timer.Destroy("GPUCompileTimer2") + + ent:WriteCell(65535,0) + ent:Compiler_Stage0(pl) + self:Compile_Pass1() +end + +function TOOL:Compile_Pass1() + if (!self:GetOwner()) then return end + self:GetOwner():PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Pass 1\n") + + self.Compiling = true + self.GPU_Entity:Compiler_Stage1() + + self.LineNumber = 1 + self.PrevRate = 0 + timer.Create("GPUCompileTimer1",self:GetOwner():GetInfo("wire_gpu_compile_rate"),0,CompileProgram_Timer,self,true) +end + +function TOOL:Compile_Pass2() + if (!self:GetOwner()) then return end + self:GetOwner():PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Pass 2\n") + + self.Compiling = true + self.GPU_Entity:Compiler_Stage2() + + self.LineNumber = 1 + timer.Create("GPUCompileTimer2",self:GetOwner():GetInfo("wire_gpu_compile_rate"),0,CompileProgram_Timer,self,false) +end + + +function TOOL:Compile_End() + local pl = self:GetOwner() + local ent = self.GPU_Entity + + if (ent.FatalError) then pl:PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Compile aborted: fatal error has occured\n") + else pl:PrintMessage(HUD_PRINTCONSOLE,"-> ZyeliosASM: Compile succeded! "..(table.Count(GPU_SourceCode)-1).." lines, "..ent.WIP.." bytes, "..table.Count(ent.Labels).." definitions.\n") + end + + pl:ConCommand('wire_gpu_vgui_close') + + ent:WriteCell(65535,1) + ent:FlushCache() + + //if (self:GetClientInfo("dump_data") == "1") then + // pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumping data\n") + // local codedump = "Count: "..ent.WIP.."\n" + // //local pointerdump = "Count: "..table.Count(ent.Labels).."\n" + // for i = 0,ent.WIP do + // if (ent.Memory[i]) then + // codedump = codedump.."["..i.."]".."="..ent.Memory[i].."\n" + // end + // end + // /*for k,v in pairs(ent.Labels) do + // pointerdump = pointerdump.."#pointer "..k.." "..v.."\n" + // end*/ + // file.Write("cdump.txt",codedump) + // file.Write("ldump.txt",ent.Dump) + // //file.Write("pdump.txt",pointerdump) + // pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumped!\n") + //end + + if ((self:GetClientInfo("dump_data") == "1") && (SinglePlayer())) then + pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumping data\n") + local codedump = "db " + for i = 0,ent.WIP do + if (i % 32 == 31) then + if (!ent.Memory[i]) then + codedump = codedump.."0\ndb " + else + codedump = codedump..ent.Memory[i].."\ndb " + end + else + if (!ent.Memory[i]) then + codedump = codedump.."0," + else + codedump = codedump..ent.Memory[i].."," + end + end + end + + file.Write("cdump.txt",codedump) + file.Write("ldump.txt",ent.Dump) + pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumped!\n") + end + +// if (self:GetClientInfo("dump_data") == "1") then +// pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumping data\n") +// local codedump = "" +// for i = 0,ent.WIP do +// if (ent.Memory[i]) then +// codedump = codedump.."db "..ent.Memory[i].."\n" +// end +// end +// file.Write("cdump.txt",codedump) +// pl:PrintMessage(HUD_PRINTCONSOLE,"ZyeliosASM: Dumped!\n") +// end + + ent:Reset() + ent.Compiling = false +end + +//============================================================================= +function TOOL:Reload(trace) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_gpu" && trace.Entity.pl == ply) then + trace.Entity.Memory = {} + return true + end +end + +function TOOL:LeftClick(trace) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + self.GPU_Entity = trace.Entity + + if not trace.Entity:IsValid() or trace.Entity:GetClass() ~= "gmod_wire_gpu" or trace.Entity.pl ~= ply then + + if not self:GetSWEP():CheckLimit("wire_gpus") then return false end + if not util.IsValidModel(self:GetClientInfo("model")) then return false end + if not util.IsValidProp(self:GetClientInfo("model")) then return false end + + local ang = trace.HitNormal:Angle() + local model = self:GetClientInfo("model") + ang.pitch = ang.pitch + 90 + + wire_gpu = MakeWireGpu(ply, trace.HitPos, ang, model) + local min = wire_gpu:OBBMins() + wire_gpu:SetPos(trace.HitPos - trace.HitNormal * min.z) + + local const = WireLib.Weld(wire_gpu, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire GPU") + undo.AddEntity(wire_gpu) + undo.AddEntity(const) + undo.SetPlayer(ply) + undo.Finish() + + ply:AddCleanup("wire_gpus", wire_gpu) + ply:AddCleanup("wire_gpus", const) + self.GPU_Entity = wire_gpu + end + + self:StartCompile(ply) + return true +end + +function TOOL:RightClick(trace) + if SERVER then self:GetOwner():SendLua("wire_gpu_OpenEditor()") end +end + +if (SERVER) then + function MakeWireGpu(pl, Pos, Ang, model) + if (!pl:CheckLimit("wire_gpus")) then return false end + + local wire_gpu = ents.Create("gmod_wire_gpu") + if (!wire_gpu:IsValid()) then return false end + wire_gpu:SetModel(model) + + wire_gpu:SetAngles(Ang) + wire_gpu:SetPos(Pos) + wire_gpu:Spawn() + + wire_gpu:SetPlayer(pl) + + local ttable = { + pl = pl, + model = model, + } + table.Merge(wire_gpu:GetTable(), ttable) + pl:AddCount("wire_gpus", wire_gpu) + + return wire_gpu + end + duplicator.RegisterEntityClass("gmod_wire_gpu", MakeWireGpu, "Pos", "Ang", "Model") +end + +function TOOL:UpdateGhostWireGpu(ent, player) + if (!ent) then return end + if (!ent:IsValid()) then return end + + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_gpu" || trace.Entity:IsPlayer()) then + ent:SetNoDraw(true) + return + end + + local ang = trace.HitNormal:Angle() + ang.pitch = ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos(trace.HitPos - trace.HitNormal * min.z) + ent:SetAngles(ang) + + ent:SetNoDraw(false) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("model") || (not self.GhostEntity:GetModel())) then + self:MakeGhostEntity(self:GetClientInfo("model"), Vector(0,0,0), Angle(0,0,0)) + end + + self:UpdateGhostWireGpu(self.GhostEntity, self:GetOwner()) +end + +//============================================================================= +// Code sending +//============================================================================= +if (CLIENT) then + local Frame + local StatusLabel + local PLabel + local ProgressBar + local BGBar + + function wire_gpu_OpenEditor() + if not GPU_Editor then + GPU_Editor = vgui.Create( "Expression2EditorFrame") + GPU_Editor:Setup("GPU Editor", "GPUChip", "GPU") + end + GPU_Editor:Open() + end + + local function VGUI_Open(pl, command, args) + if (Frame) then + Frame:SetVisible(false) + end + + Frame = vgui.Create("Panel") + Frame:SetSize(400,50) + Frame:SetPos(150,150) + Frame:SetVisible(true) + + BGBar = vgui.Create("ProgressBar",Frame) + BGBar:SetVisible(true) + BGBar:SetSize(400,100) + BGBar:SetPos(0,0) + + StatusLabel = vgui.Create("Label",Frame) + StatusLabel:SetSize(380,30) + StatusLabel:SetPos(10,10) + StatusLabel:SetVisible(true) + + PLabel = vgui.Create("Label",Frame) + PLabel:SetSize(30,30) + PLabel:SetPos(360,10) + PLabel:SetVisible(true) + + ProgressBar = vgui.Create("ProgressBar",Frame) + ProgressBar:SetSize(280,30) + ProgressBar:SetPos(10,60) + ProgressBar:SetVisible(false) + end + concommand.Add("wire_gpu_vgui_open", VGUI_Open) + + local function VGUI_Close(pl, command, args) + Frame:SetVisible(false); + end + concommand.Add("wire_gpu_vgui_close", VGUI_Close) + + local function VGUI_Title(pl, command, args) + Frame:PostMessage("SetTitle", "text", args[1]); + end + concommand.Add("wire_gpu_vgui_title", VGUI_Title) + + local function VGUI_Status(pl, command, args) + StatusLabel:PostMessage("SetText", "text", args[1]); + end + concommand.Add("wire_gpu_vgui_status", VGUI_Status) + + local function VGUI_Progress(pl, command, args) + if (args[1]) then + ProgressBar:PostMessage("SetValue", "Float", tonumber(args[1])/100); + PLabel:PostMessage("SetText", "text", args[1] .. "%"); + end + end + concommand.Add("wire_gpu_vgui_progress", VGUI_Progress) +end + +if (CLIENT) then + +SourceLines = {} +SourceLineNumbers = {} +SourceLinesSent = 0 +SourcePrevCharRate = 0 +SourceTotalChars = 0 +SourceLoadedChars = 0 + +function GPU_UploadProgram(pl) + local SendLinesMax = SourceLinesSent + pl:GetInfo("wire_gpu_packet_bandwidth") + local TotalChars = 0 + if SendLinesMax > table.Count(SourceLines) then + SendLinesMax = table.Count(SourceLines) + end + + while (SourceLinesSent <= SendLinesMax) && (TotalChars < 1024) do + SourceLinesSent = SourceLinesSent + 1 + local line = SourceLines[SourceLinesSent] + local linen = SourceLinesSent + + if (line) && (line ~= "\n") && (string.gsub(line, "\n", "") ~= "") then + RunConsoleCommand("wire_gpu_addsrc",linen,string.gsub(line, "\n", "")) + TotalChars = TotalChars + string.len(line) + else + RunConsoleCommand("wire_gpu_addsrc",linen,"") + end + end + SourceLoadedChars = SourceLoadedChars + TotalChars + + local CharRate = (SourcePrevCharRate*1.95 + TotalChars*0.05) / 2 + SourcePrevCharRate = CharRate + + if SinglePlayer() then CharRate = CharRate / pl:GetInfo("wire_gpu_packet_rate_sp") + else CharRate = CharRate / pl:GetInfo("wire_gpu_packet_rate_mp") + end + + local TimeLeft = math.floor((SourceTotalChars - SourceLoadedChars) / CharRate) + local TempPercent = math.floor(((SourceLinesSent-1)/table.Count(SourceLines))*100) + + pl:ConCommand('wire_gpu_vgui_status "Uploading @ '..math.floor(CharRate / 1024)..' kb/sec, avg. '..TimeLeft..' sec left, '..SourceLinesSent..' lines sent"') + pl:ConCommand('wire_gpu_vgui_progress "'..TempPercent..'"') + + if (SourceLinesSent > table.Count(SourceLines)) then + pl:ConCommand('wire_gpu_vgui_close') + timer.Remove("GPUSendTimer") + end +end + +function GPU_LoadProgram(pl, command, args) + local fname = "GPUChip\\"..pl:GetInfo("wire_gpu_filename"); + if (!file.Exists(fname)) then + fname = "GPUChip\\"..pl:GetInfo("wire_gpu_filename")..".txt"; + end + + if (!file.Exists(fname)) then + pl:PrintMessage(HUD_PRINTTALK,"GPU -> Sorry! Requested file was not found\n") + return + end + + pl:ConCommand('wire_gpu_clearsrc') + + local filedata = file.Read(fname) + if (!filedata) then + return + end + + SourceLines = string.Explode("\n", filedata) + SourceLinesSent = 0 + SourceTotalChars = string.len(filedata) + + SourcePrevCharRate = string.len(SourceLines[1]) + SourceLoadedChars = 0 + + pl:ConCommand('wire_gpu_vgui_open') + pl:ConCommand('wire_gpu_vgui_title "GPU - Uploading program"') + pl:ConCommand('wire_gpu_vgui_status "Initializing"') + pl:ConCommand('wire_gpu_vgui_progress "0"') + + //Send 50 lines + if (SinglePlayer()) then timer.Create("GPUSendTimer",pl:GetInfo("wire_gpu_packet_rate_sp"),0,GPU_UploadProgram,pl,false) + else timer.Create("GPUSendTimer",pl:GetInfo("wire_gpu_packet_rate_mp"),0,GPU_UploadProgram,pl,false) + end +end + +end + +local function LoadProgram(pl, command, args) + if (SERVER) then + pl:SendLua("GPU_LoadProgram(LocalPlayer())") + else + GPU_LoadProgram(pl, command, args) + end +end +concommand.Add("wire_gpu_load", LoadProgram) + +local function ClearProgram(pl, command, args) + pl:ConCommand('wire_gpu_clearsrc') +end +concommand.Add("wire_gpu_clear", ClearProgram) + +//============================================================================= +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_gpu_name", Description = "#Tool_wire_gpu_desc" }) + + panel:AddControl("TextBox", { + Label = "Source code file name", + Command = "wire_gpu_filename", + MaxLength = "128" + }) + + panel:AddControl("Button", { + Text = "Quick Load", + Name = "Load", + Command = "wire_gpu_load" + }) + local New = vgui.Create("DButton" , panel) + panel:AddPanel(New) + New:SetText("New file") + New.DoClick = function(button) + wire_gpu_OpenEditor() + GPU_Editor:AutoSave() + GPU_Editor:ChosenFile() + GPU_Editor:SetCode("\n\n") + end + + panel:AddControl("Label", {Text = ""}) + + local OpenEditor = vgui.Create("DButton", panel) + panel:AddPanel(OpenEditor) + OpenEditor:SetText("Code Editor") + OpenEditor.DoClick = wire_gpu_OpenEditor + + panel:AddControl("Label", {Text = ""}) + + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + MenuButton = "0", + + Options = { + ["#Small tv"] = { wire_gpu_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv (16:10)"] = { wire_gpu_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#Plasma tv (4:3)"] = { wire_gpu_model = "models/blacknecro/tv_plasma_4_3.mdl" }, + ["#LCD Monitor (4:3)"] = { wire_gpu_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big"] = { wire_gpu_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small"] = { wire_gpu_model = "models/kobilica/wiremonitorsmall.mdl" }, + ["#Billboard"] = { wire_gpu_model = "models/props/cs_assault/Billboard.mdl" }, + ["#LCD Screen"] = { wire_gpu_model = "models/blacknecro/ledboard60.mdl" }, + } + }) + + panel:AddControl("Label", { + Text = "" + }) + panel:AddControl("Button", { + Text = "ZGPU documentation (online)" + }) + panel:AddControl("Label", { + Text = "Loads online GPU documentation and tutorials (WILL WORK SOON)" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_graphics_tablet.lua b/lua/weapons/gmod_tool/stools/wire_graphics_tablet.lua new file mode 100644 index 0000000000..99a0f6057d --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_graphics_tablet.lua @@ -0,0 +1,153 @@ +--Wire graphics tablet by greenarrow +--http://gmodreviews.googlepages.com/ +--http://forums.facepunchstudios.com/greenarrow +--There may be a few bits of code from the wire panel here and there as i used it as a starting point. +--Credit to whoever created the first wire screen, from which all others seem to use the lagacy clientside drawing code (this one included) + +TOOL.Category = "Wire - I/O" +TOOL.Name = "Graphics Tablet" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_graphics_tablet_name", "Graphics Tablet Tool (Wire)" ) + language.Add( "Tool_wire_graphics_tablet_desc", "Spawns a grphics tablet, which outputs cursor coordinates" ) + language.Add( "Tool_wire_graphics_tablet_0", "Primary: Create/Update graphics tablet" ) + language.Add( "sboxlimit_wire_graphics_tablets", "You've hit graphics tablets limit!" ) + language.Add( "undone_wire_graphics_tablet", "Undone Wire Graphics Tablet" ) + language.Add( "Tool_wire_graphics_tablet_mode", "Output mode: -1 to 1 (ticked), 0 to 1 (unticked)" ) + language.Add("Tool_wire_graphics_tablet_createflat", "Create flat to surface") +end + +if (SERVER) then + CreateConVar('sbox_maxwire_graphics_tablets', 20) +end + +TOOL.ClientConVar["model"] = "models/kobilica/wiremonitorbig.mdl" +TOOL.ClientConVar["outmode"] = 0 +TOOL.ClientConVar["createflat"] = 1 + +cleanup.Register( "wire_graphics_tablets" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + if ( !self:GetSWEP():CheckLimit( "wire_graphics_tablets" ) ) then return false end + + if (not util.IsValidModel(self:GetClientInfo( "model" ))) then return false end + if (not util.IsValidProp(self:GetClientInfo( "model" ))) then return false end + + local ply = self:GetOwner() + local Ang = trace.HitNormal:Angle() + local model = self:GetClientInfo("model") + local gmode = (self:GetClientNumber("outmode") ~= 0) + local CreateFlat = self:GetClientNumber("createflat") + + if (trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_graphics_tablet" && trace.Entity.pl == ply) then + trace.Entity:Setup(gmode) + return true + end + + if (CreateFlat == 0) then + Ang.pitch = Ang.pitch + 90 + end + + local wire_graphics_tablet = MakeWireGraphicsTablet(ply, trace.HitPos, Ang, model, gmode) + local min = wire_graphics_tablet:OBBMins() + wire_graphics_tablet:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_graphics_tablet, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireGraphicsTablet") + undo.AddEntity( wire_graphics_tablet ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_graphics_tablets", wire_graphics_tablet ) + + return true +end + +if (SERVER) then + function MakeWireGraphicsTablet( pl, Pos, Ang, model, gmode ) + if ( !pl:CheckLimit( "wire_graphics_tablets" ) ) then return false end + + local wire_graphics_tablet = ents.Create( "gmod_wire_graphics_tablet" ) + if (!wire_graphics_tablet:IsValid()) then return false end + wire_graphics_tablet:SetModel(model) + + wire_graphics_tablet:SetAngles( Ang ) + wire_graphics_tablet:SetPos( Pos ) + wire_graphics_tablet:Setup(gmode) + wire_graphics_tablet:Spawn() + wire_graphics_tablet:SetPlayer(pl) + + local ttable = { + pl = pl, + model = model, + gmode = gmode + } + table.Merge(wire_graphics_tablet:GetTable(), ttable ) + pl:AddCount( "wire_graphics_tablets", wire_graphics_tablet ) + return wire_graphics_tablet + end + duplicator.RegisterEntityClass("gmod_wire_graphics_tablet", MakeWireGraphicsTablet, "Pos", "Ang", "Model", "gmode") +end + +function TOOL:UpdateGhostWireGraphicsTablet( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_graphics_tablet" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + if (self:GetClientNumber("createflat") == 0) then + Ang.pitch = Ang.pitch + 90 + end + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" ) || (not self.GhostEntity:GetModel()) ) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireGraphicsTablet( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_graphics_tablet_name", Description = "#Tool_wire_graphics_tablet_desc" }) + + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + MenuButton = "0", + + Options = { + ["#Small tv"] = { wire_graphics_tablet_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv"] = { wire_graphics_tablet_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#LCD monitor"] = { wire_graphics_tablet_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big"] = { wire_graphics_tablet_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small"] = { wire_graphics_tablet_model = "models/kobilica/wiremonitorsmall.mdl" }, + } + }) + panel:AddControl("CheckBox", { + Label = "#Tool_wire_graphics_tablet_mode", + Command = "wire_graphics_tablet_outmode" + }) + panel:AddControl("Checkbox", { + Label = "#Tool_wire_graphics_tablet_createflat", + Command = "wire_graphics_tablet_createflat" + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_gyroscope.lua b/lua/weapons/gmod_tool/stools/wire_gyroscope.lua new file mode 100644 index 0000000000..b19c8500a4 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_gyroscope.lua @@ -0,0 +1,135 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "Gyroscope" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_gyroscope_name", "Gyroscope Tool (Wire)" ) + language.Add( "Tool_wire_gyroscope_desc", "Spawns a gyroscope for use with the wire system." ) + language.Add( "Tool_wire_gyroscope_0", "Primary: Create/Update Gyroscope" ) + language.Add( "Tool_wire_gyroscope_out180", "Output -180 to 180 instead of 0 to 360" ) + language.Add( "sboxlimit_wire_gyroscopes", "You've hit gyroscopes limit!" ) + language.Add( "undone_wiregyroscope", "Undone Wire Gyroscope" ) +end + +TOOL.ClientConVar[ "out180" ] = 0 + +if (SERVER) then + CreateConVar('sbox_maxwire_gyroscopes', 10) +end + +TOOL.Model = "models/cheeze/wires/gyroscope.mdl" + +cleanup.Register( "wire_gyroscopes" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + local _out180 = self:GetClientNumber( "out180" ) == 1 + + // If we shot a wire_gyroscope change its "Use +/-180?" property (TheApathetic) + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_gyroscope" && trace.Entity.pl == ply ) then + trace.Entity:Setup( _out180 ) + trace.Entity.out180 = _out180 + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_gyroscopes" ) ) then return false end + + if (not util.IsValidModel(self.Model)) then return false end + if (not util.IsValidProp(self.Model)) then return false end // Allow ragdolls to be used? + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_gyroscope = MakeWireGyroscope( ply, trace.HitPos, Ang, self.Model, _out180 ) + + local min = wire_gyroscope:OBBMins() + wire_gyroscope:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_gyroscope, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireGyroscope") + undo.AddEntity( wire_gyroscope ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_gyroscopes", wire_gyroscope ) + + return true +end + +if (SERVER) then + + function MakeWireGyroscope( pl, Pos, Ang, model, out180, nocollide, Vel, aVel, frozen ) + if ( !pl:CheckLimit( "wire_gyroscopes" ) ) then return false end + + local wire_gyroscope = ents.Create( "gmod_wire_gyroscope" ) + if (!wire_gyroscope:IsValid()) then return false end + + wire_gyroscope:SetAngles(Ang) + wire_gyroscope:SetPos(Pos) + wire_gyroscope:SetModel(model) + wire_gyroscope:Spawn() + + wire_gyroscope:Setup( out180 ) + wire_gyroscope:SetPlayer(pl) + + if ( nocollide == true ) then wire_gyroscope:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + pl = pl, + out180 = out180, + } + table.Merge(wire_gyroscope:GetTable(), ttable ) + + pl:AddCount( "wire_gyroscopes", wire_gyroscope ) + + return wire_gyroscope + end + + duplicator.RegisterEntityClass("gmod_wire_gyroscope", MakeWireGyroscope, "Pos", "Ang", "Model", "out180", "nocollide", "Vel", "aVel", "frozen") + +end + +function TOOL:UpdateGhostWireGyroscope( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_gyroscope" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireGyroscope( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_gyroscope_name", Description = "#Tool_wire_gyroscope_desc" }) + panel:AddControl( "Checkbox", { Label = "#Tool_wire_gyroscope_out180", Command = "wire_gyroscope_out180" } ) +end diff --git a/lua/weapons/gmod_tool/stools/wire_hdd.lua b/lua/weapons/gmod_tool/stools/wire_hdd.lua new file mode 100644 index 0000000000..78ecb1ea55 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_hdd.lua @@ -0,0 +1,251 @@ +TOOL.Category = "Wire - Advanced" +TOOL.Name = "Flash (EEPROM)" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_hdd_name", "Flash (EEPROM) tool (Wire)" ) + language.Add( "Tool_wire_hdd_desc", "Spawns flash memory. It is used for permanent storage of data (carried over sessions)" ) + language.Add( "Tool_wire_hdd_0", "Primary: Create/Update flash memory" ) + language.Add( "sboxlimit_wire_hdds", "You've hit flash memory limit!" ) + language.Add( "undone_wiredigitalscreen", "Undone Flash (EEPROM)" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_hdds', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_gate.mdl" +TOOL.ClientConVar[ "driveid" ] = 0 +TOOL.ClientConVar[ "client_driveid" ] = 0 +TOOL.ClientConVar[ "drivecap" ] = 128 + +TOOL.ClientConVar[ "packet_bandwidth" ] = 100 +TOOL.ClientConVar[ "packet_rate" ] = 0.4 + +cleanup.Register( "wire_hdds" ) + +function TOOL:LeftClick( trace ) + if trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_hdd" && trace.Entity.pl == ply ) then + trace.Entity.DriveID = tonumber(self:GetClientInfo( "driveid" )) + trace.Entity.DriveCap = tonumber(self:GetClientInfo( "drivecap" )) + trace.Entity:UpdateCap() + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_hdds" ) ) then return false end + + if (not util.IsValidModel(self:GetClientInfo( "model" ))) then return false end + if (not util.IsValidProp(self:GetClientInfo( "model" ))) then return false end + + local ply = self:GetOwner() + local Ang = trace.HitNormal:Angle() + local model = self:GetClientInfo( "model" ) + Ang.pitch = Ang.pitch + 90 + + local wire_hdd = MakeWirehdd( ply, trace.HitPos, Ang, model, self:GetClientInfo( "driveid" ), self:GetClientInfo( "drivecap" ) ) + local min = wire_hdd:OBBMins() + wire_hdd:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + wire_hdd.DriveID = tonumber(self:GetClientInfo( "driveid" )) + wire_hdd.DriveCap = tonumber(self:GetClientInfo( "drivecap" )) + + local const = WireLib.Weld(wire_hdd, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wirehdd") + undo.AddEntity( wire_hdd ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_hdds", wire_hdd ) + + return true +end + +if (SERVER) then + + function MakeWirehdd( pl, Pos, Ang, model, DriveID, DriveCap) + + if ( !pl:CheckLimit( "wire_hdds" ) ) then return false end + + local wire_hdd = ents.Create( "gmod_wire_hdd" ) + if (!wire_hdd:IsValid()) then return false end + wire_hdd:SetModel(model) + + wire_hdd:SetAngles( Ang ) + wire_hdd:SetPos( Pos ) + wire_hdd:Spawn() + + wire_hdd:SetPlayer(pl) + + local ttable = { + pl = pl, + model = model, + DriveID = DriveID, + DriveCap = DriveCap, + } + + table.Merge(wire_hdd:GetTable(), ttable ) + + pl:AddCount( "wire_hdds", wire_hdd ) + + return wire_hdd + + end + + duplicator.RegisterEntityClass("gmod_wire_hdd", MakeWirehdd, "Pos", "Ang", "Model", "DriveID", "DriveCap") + +end + +function TOOL:UpdateGhostWirehdd( ent, player ) + + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_hdd" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" ) || (not self.GhostEntity:GetModel()) ) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWirehdd( self.GhostEntity, self:GetOwner() ) +end + +/*"wire_hdd_driveid" +"wire_hdd_client_driveid" +"wire_hdd_downloadhdd" +"wire_hdd_uploadhdd" +"wire_hdd_clearhdd" +"wire_hdd_clearhdd_client" + +local function UploadHDDData(pl, command, args) + //Send drive cap + //SP only: + //SourceCode = string.Explode("\n", file.Read(fname) ) + + pl:ConCommand('wire_cpu_clearsrc') + + local filedata = file.Read(fname) + SourceLines = string.Explode("\n", filedata ) + SourceLinesSent = 0 + SourceTotalChars = string.len(filedata) + + SourcePrevCharRate = string.len(SourceLines[1]) + SourceLoadedChars = 0 + + pl:ConCommand('wire_cpu_vgui_open') + pl:ConCommand('wire_cpu_vgui_title "CPU - Uploading program"') + pl:ConCommand('wire_cpu_vgui_status "Initializing"') + pl:ConCommand('wire_cpu_vgui_progress "0"') + + //Send 50 lines + if (SinglePlayer()) then + timer.Create("CPUSendTimer",pl:GetInfo("wire_cpu_packet_rate_sp"),0,UploadProgram,pl,true) + else + timer.Create("CPUSendTimer",pl:GetInfo("wire_cpu_packet_rate_mp"),0,UploadProgram,pl,true) + end + end + concommand.Add( "wire_cpu_loadcompile", LoadCompileProgram ) + + local function StoreProgram(pl, command, args) + Msg("Storing program is disabled - its readonly!\n") + end + concommand.Add("wire_cpu_store", StoreProgram) + +if (SERVER) then + local function Send_DriveCap(pl, command, args) + + end + concommand.Add("wire_hdd_send_drivecap", Send_DriveCap ) +end*/ + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_hdd_name", Description = "#Tool_wire_hdd_desc" }) + + panel:AddControl("Slider", { + Label = "Drive ID", + Type = "Integer", + Min = "0", + Max = "3", + Command = "wire_hdd_driveid" + }) + + panel:AddControl("Slider", { + Label = "Capacity (KB)", + Type = "Integer", + Min = "0", + Max = "128", + Command = "wire_hdd_drivecap" + }) + +/* panel:AddControl("Label", { + Text = "Hard drive manager:" + }) + + panel:AddControl("Slider", { + Label = "Server drive ID", + Type = "Integer", + Min = "0", + Max = "3", + Command = "wire_hdd_driveid" + }) + + panel:AddControl("Slider", { + Label = "Client drive ID", + Type = "Integer", + Min = "0", + Max = "3", + Command = "wire_hdd_client_driveid" + }) + + panel:AddControl("Button", { + Text = "Download server drive to client drive", + Name = "Clear", + Command = "wire_hdd_downloadhdd" + }) + + panel:AddControl("Button", { + Text = "Upload client drive to server drive", + Name = "Clear", + Command = "wire_hdd_uploadhdd" + }) + + panel:AddControl("Button", { + Text = "Clear server drive", + Name = "Clear", + Command = "wire_hdd_clearhdd" + }) + + panel:AddControl("Button", { + Text = "Clear client drive", + Name = "Clear", + Command = "wire_hdd_clearhdd_client" + })*/ + +end + diff --git a/lua/weapons/gmod_tool/stools/wire_hudindicator.lua b/lua/weapons/gmod_tool/stools/wire_hudindicator.lua new file mode 100644 index 0000000000..99456420ad --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_hudindicator.lua @@ -0,0 +1,633 @@ +// Created by TheApathetic, so you know who to +// blame if something goes wrong (someone else :P) +TOOL.Category = "Wire - Display" +TOOL.Name = "Hud Indicator" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_hudindicator_name", "Hud Indicator Tool (Wire)" ) + language.Add( "Tool_wire_hudindicator_desc", "Spawns a Hud Indicator for use with the wire system." ) + language.Add( "Tool_wire_hudindicator_0", "Primary: Create/Update Hud Indicator Secondary: Hook/Unhook someone else's Hud Indicator Reload: Link Hud Indicator to vehicle" ) + language.Add( "Tool_wire_hudindicator_1", "Now use Reload on a vehicle to link this Hud Indicator to it, or on the same Hud Indicator to unlink it" ) + /* Don't need this stuff + language.Add( "ToolWireIndicator_Model", "Model:" ) + language.Add( "ToolWireIndicator_a_value", "A Value:" ) + language.Add( "ToolWireIndicator_a_colour", "A Colour:" ) + language.Add( "ToolWireIndicator_b_value", "B Value:" ) + language.Add( "ToolWireIndicator_b_colour", "B Colour:" ) + language.Add( "ToolWireIndicator_Material", "Material:" ) + language.Add( "ToolWireIndicator_90", "Rotate segment 90" ) + language.Add( "sboxlimit_wire_indicators", "You've hit indicators limit!" ) + */ + language.Add( "undone_wirehudindicator", "Undone Wire Hud Indicator" ) + + // HUD Indicator stuff + language.Add( "ToolWireHudIndicator_showinhud", "Show in my HUD") + language.Add( "ToolWireHudIndicator_hudheaderdesc", "HUD Indicator Settings:") + language.Add( "ToolWireHudIndicator_huddesc", "Description:") + language.Add( "ToolWireHudIndicator_hudaddname", "Add description as Name") + language.Add( "ToolWireHudIndicator_hudaddnamedesc", "Also adds description as name of indicator (like Wire Namer)") + language.Add( "ToolWireHudIndicator_hudshowvalue", "Show Value as:") + language.Add( "ToolWireHudIndicator_hudshowvaluedesc", "How to display value in HUD readout along with description") + language.Add( "ToolWireHudIndicator_hudx", "HUD X:") + language.Add( "ToolWireHudIndicator_hudxdesc", "X of the upper-left corner of HUD display") + language.Add( "ToolWireHudIndicator_hudy", "HUD Y:") + language.Add( "ToolWireHudIndicator_hudydesc", "Y of the upper-left corner of HUD display") + language.Add( "ToolWireHudIndicator_hudstyle", "HUD Style:") + language.Add( "ToolWireHudIndicator_allowhook", "Allow others to hook") + language.Add( "ToolWireHudIndicator_allowhookdesc", "Allows others to hook this indicator with right-click") + language.Add( "ToolWireHudIndicator_hookhidehud", "Allow HideHUD on hooked") + language.Add( "ToolWireHudIndicator_hookhidehuddesc", "Whether your next hooked indicator will be subject to the HideHUD input of that indicator") + language.Add( "ToolWireHudIndicator_fullcircleangle", "Start angle for full circle gauge (deg):") + language.Add( "ToolWireHudIndicator_registeredindicators", "Registered Indicators:") + language.Add( "ToolWireHudIndicator_deleteselected", "Unregister Selected Indicator") +end + +if (SERVER) then + // Hud indicators use the original indicator CVar + //CreateConVar('sbox_maxwire_indicators', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_siren.mdl" +TOOL.ClientConVar[ "a" ] = "0" +TOOL.ClientConVar[ "ar" ] = "255" +TOOL.ClientConVar[ "ag" ] = "0" +TOOL.ClientConVar[ "ab" ] = "0" +TOOL.ClientConVar[ "aa" ] = "255" +TOOL.ClientConVar[ "b" ] = "1" +TOOL.ClientConVar[ "br" ] = "0" +TOOL.ClientConVar[ "bg" ] = "255" +TOOL.ClientConVar[ "bb" ] = "0" +TOOL.ClientConVar[ "ba" ] = "255" +TOOL.ClientConVar[ "rotate90" ] = "0" +TOOL.ClientConVar[ "material" ] = "models/debug/debugwhite" +// HUD Indicator stuff +TOOL.ClientConVar[ "showinhud" ] = "0" +TOOL.ClientConVar[ "huddesc" ] = "" +TOOL.ClientConVar[ "hudaddname" ] = "0" +TOOL.ClientConVar[ "hudshowvalue" ] = "0" +TOOL.ClientConVar[ "hudx" ] = "22" +TOOL.ClientConVar[ "hudy" ] = "200" +TOOL.ClientConVar[ "hudstyle" ] = "0" +TOOL.ClientConVar[ "allowhook" ] = "1" +TOOL.ClientConVar[ "hookhidehud" ] = "0" // Couldn't resist this name :P +TOOL.ClientConVar[ "fullcircleangle" ] = "0" +TOOL.ClientConVar[ "registerdelete" ] = "0" + +cleanup.Register( "wire_indicators" ) + +function TOOL:LeftClick( trace ) + + if trace.Entity && trace.Entity:IsPlayer() then return false end + + // If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + if (CLIENT) then return true end + + local ply = self:GetOwner() + + local model = self:GetClientInfo( "model" ) + local a = self:GetClientNumber("a") + local ar = math.min(self:GetClientNumber("ar"), 255) + local ag = math.min(self:GetClientNumber("ag"), 255) + local ab = math.min(self:GetClientNumber("ab"), 255) + local aa = math.min(self:GetClientNumber("aa"), 255) + local b = self:GetClientNumber("b") + local br = math.min(self:GetClientNumber("br"), 255) + local bg = math.min(self:GetClientNumber("bg"), 255) + local bb = math.min(self:GetClientNumber("bb"), 255) + local ba = math.min(self:GetClientNumber("ba"), 255) + local material = self:GetClientInfo( "material" ) + + local showinhud = (self:GetClientNumber( "showinhud" ) > 0) + local huddesc = self:GetClientInfo( "huddesc" ) + local hudaddname = (self:GetClientNumber( "hudaddname" ) > 0) + local hudshowvalue = self:GetClientNumber( "hudshowvalue" ) + local hudstyle = self:GetClientNumber( "hudstyle" ) + local allowhook = (self:GetClientNumber( "allowhook" ) > 0) + local fullcircleangle = self:GetClientNumber( "fullcircleangle" ) + + // If we shot a wire_indicator change its force + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_hudindicator" && trace.Entity.pl == ply ) then + + trace.Entity:Setup(a, ar, ag, ab, aa, b, br, bg, bb, ba) + trace.Entity:SetMaterial( material ) + + trace.Entity.a = a + trace.Entity.ar = ar + trace.Entity.ag = ag + trace.Entity.ab = ab + trace.Entity.aa = aa + trace.Entity.b = b + trace.Entity.br = br + trace.Entity.bg = bg + trace.Entity.bb = bb + trace.Entity.ba = ba + + // This will un-register if showinhud is false + trace.Entity:HUDSetup(showinhud, huddesc, hudaddname, hudshowvalue, hudstyle, allowhook, fullcircleangle) + + trace.Entity.showinhud = showinhud + trace.Entity.huddesc = huddesc + trace.Entity.hudaddname = hudaddname + trace.Entity.hudshowvalue = hudshowvalue + trace.Entity.hudstyle = hudstyle + trace.Entity.allowhook = allowhook + trace.Entity.fullcircleangle = fullcircleangle + + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_indicators" ) ) then return false end + + if (not util.IsValidModel(model)) then return false end + if (not util.IsValidProp(model)) then return false end // Allow ragdolls to be used? + + //local Ang = trace.HitNormal:Angle() + local Ang = self:GetSelectedAngle(trace.HitNormal:Angle()) + Ang.pitch = Ang.pitch + 90 + + wire_indicator = MakeWireHudIndicator( ply, trace.HitPos, Ang, model, a, ar, ag, ab, aa, b, br, bg, bb, ba, material, showinhud, huddesc, hudaddname, hudshowvalue, hudstyle, allowhook, fullcircleangle ) + + local min = wire_indicator:OBBMins() + wire_indicator:SetPos( trace.HitPos - trace.HitNormal * self:GetSelectedMin(min) ) + + local const = WireLib.Weld(wire_indicator, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireHudIndicator") + undo.AddEntity( wire_indicator ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_indicators", wire_indicator ) + + return true +end + +function TOOL:RightClick( trace ) + // Can only right-click on HUD Indicators + if (!trace.Entity || !trace.Entity:IsValid() || trace.Entity:GetClass() != "gmod_wire_hudindicator") then return false end + + if (CLIENT) then return true end + + local ply = self:GetOwner() + local hookhidehud = (self:GetClientNumber( "hookhidehud" ) > 0) + + // Can't hook your own HUD Indicators + if (ply == trace.Entity:GetPlayer()) then + WireLib.AddNotify(self:GetOwner(), "You cannot hook your own HUD Indicators!", NOTIFY_GENERIC, 7) + return false + end + + if (!trace.Entity:GetTable():CheckRegister(ply)) then + // Has the creator allowed this HUD Indicator to be hooked? + if (!trace.Entity:GetTable().AllowHook) then + WireLib.AddNotify(self:GetOwner(), "You are not allowed to hook this HUD Indicator.", NOTIFY_GENERIC, 7) + return false + end + + trace.Entity:GetTable():RegisterPlayer(ply, hookhidehud) + else + trace.Entity:GetTable():UnRegisterPlayer(ply) + end + + return true +end + +// Hook HUD Indicator to vehicle +function TOOL:Reload( trace ) + // Can only use this on HUD Indicators and vehicles + // The class checks are done later on, no need to do it twice + if (!trace.Entity || !trace.Entity:IsValid()) then return false end + + if (CLIENT) then return true end + + local iNum = self:NumObjects() + + if (iNum == 0) then + if (trace.Entity:GetClass() != "gmod_wire_hudindicator") then + WireLib.AddNotify(self:GetOwner(), "You must select a HUD Indicator to link first.", NOTIFY_GENERIC, 7) + return false + end + + local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + self:SetObject( 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal ) + self:SetStage(1) + elseif (iNum == 1) then + if (trace.Entity != self:GetEnt(1)) then + if (!string.find(trace.Entity:GetClass(), "prop_vehicle_")) then + WireLib.AddNotify(self:GetOwner(), "HUD Indicators can only be linked to vehicles.", NOTIFY_GENERIC, 7) + self:ClearObjects() + self:SetStage(0) + return false + end + + local ent = self:GetEnt(1) + local bool = ent:GetTable():LinkVehicle(trace.Entity) + + if (!bool) then + WireLib.AddNotify(self:GetOwner(), "Could not link HUD Indicator!", NOTIFY_GENERIC, 7) + return false + end + else + // Unlink HUD Indicator from this vehicle + trace.Entity:GetTable():UnLinkVehicle() + end + + self:ClearObjects() + self:SetStage(0) + end + + return true +end + +if (SERVER) then + + function MakeWireHudIndicator( pl, Pos, Ang, model, a, ar, ag, ab, aa, b, br, bg, bb, ba, material, showinhud, huddesc, hudaddname, hudshowvalue, hudstyle, allowhook, fullcircleangle, nocollide, Vel, aVel, frozen ) + if ( !pl:CheckLimit( "wire_indicators" ) ) then return false end + + local wire_indicator = ents.Create( "gmod_wire_hudindicator" ) + if (!wire_indicator:IsValid()) then return false end + + wire_indicator:SetModel( model ) + wire_indicator:SetMaterial( material ) + wire_indicator:SetAngles( Ang ) + wire_indicator:SetPos( Pos ) + wire_indicator:Spawn() + + wire_indicator:Setup(a, ar, ag, ab, aa, b, br, bg, bb, ba) + wire_indicator:SetPlayer(pl) + + wire_indicator:HUDSetup(showinhud, huddesc, hudaddname, hudshowvalue, hudstyle, allowhook, fullcircleangle) + + if (nocollide) then + local phys = wire_indicator:GetPhysicsObject() + if ( phys:IsValid() ) then phys:EnableCollisions(false) end + end + + local ttable = { + a = a, + ar = ar, + ag = ag, + ab = ab, + aa = aa, + b = b, + br = br, + bg = bg, + bb = bb, + ba = ba, + material = material, + pl = pl, + nocollide = nocollide, + showinhud = showinhud, + huddesc = huddesc, + hudaddname = hudaddname, + hudshowvalue = hudshowvalue, + hudstyle = hudstyle, + allowhook = allowhook, + fullcircleangle = fullcircleangle + } + table.Merge(wire_indicator:GetTable(), ttable ) + + pl:AddCount( "wire_indicators", wire_indicator ) + + return wire_indicator + end + + duplicator.RegisterEntityClass("gmod_wire_hudindicator", MakeWireHudIndicator, "Pos", "Ang", "Model", "a", "ar", "ag", "ab", "aa", "b", "br", + "bg", "bb", "ba", "material", "showinhud", "huddesc", "hudaddname", "hudshowvalue", "hudstyle", "allowhook", "fullcircleangle", "nocollide", "Vel", "aVel", "frozen") + +end + +function TOOL:UpdateGhostWireHudIndicator( ent, player ) + + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_hudindicator" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = self:GetSelectedAngle(trace.HitNormal:Angle()) + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * self:GetSelectedMin(min) ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:GetSelectedAngle( Ang ) + local Model = self:GetClientInfo( "model" ) + //these models get mounted differently + if (Model == "models/props_borealis/bluebarrel001.mdl" || Model == "models/props_junk/PopCan01a.mdl") then + return Ang + Angle(180, 0, 0) + elseif (Model == "models/props_trainstation/trainstation_clock001.mdl" || Model == "models/segment.mdl" || Model == "models/segment2.mdl") then + return Ang + Angle(-90, 0, (self:GetClientNumber("rotate90") * 90)) + else + return Ang + end +end + +function TOOL:GetSelectedMin( min ) + local Model = self:GetClientInfo( "model" ) + //these models are different + if (Model == "models/props_trainstation/trainstation_clock001.mdl" || Model == "models/segment.mdl" || Model == "models/segment2.mdl") then + return min.x + else + return min.z + end +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" )) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), self:GetSelectedAngle(Angle(0,0,0)) ) + end + + self:UpdateGhostWireHudIndicator( self.GhostEntity, self:GetOwner() ) + + if (SERVER) then + // Add check to see if player is registered with + // the HUD Indicator at which he is pointing + if ((self.NextCheckTime or 0) < CurTime()) then + local ply = self:GetOwner() + local tr = utilx.GetPlayerTrace(ply, ply:GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if (trace.Hit && trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_hudindicator" && trace.Entity:GetPlayer() != ply) then + local currentcheck = trace.Entity:GetTable():CheckRegister(ply) + if (currentcheck != self.LastRegisterCheck) then + self.LastRegisterCheck = currentcheck + self:GetWeapon():SetNetworkedBool("HUDIndicatorCheckRegister", currentcheck) + end + else + if (self.LastRegisterCheck == true) then + // Don't need to set this every 1/10 of a second + self.LastRegisterCheck = false + self:GetWeapon():SetNetworkedBool("HUDIndicatorCheckRegister", false) + end + end + self.NextCheckTime = CurTime() + 0.10 + end + end +end + +if (CLIENT) then + function TOOL:DrawHUD() + local isregistered = self:GetWeapon():GetNetworkedBool("HUDIndicatorCheckRegister") + + if (isregistered) then + draw.WordBox(8, ScrW() / 2 + 10, ScrH() / 2 + 10, "Registered", "Default", Color(50, 50, 75, 192), Color(255, 255, 255, 255)) + end + end +end + +function TOOL:Holster() + self:ReleaseGhostEntity() + self:GetWeapon():SetNetworkedBool("HUDIndicatorCheckRegister", false) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_hudindicator_name", Description = "#Tool_wire_hudindicator_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_hudindicator", + + Options = { + ["#Default"] = { + wire_hudindicator_a = "0", + wire_hudindicator_ar = "255", + wire_hudindicator_ag = "0", + wire_hudindicator_ab = "0", + wire_hudindicator_aa = "255", + wire_hudindicator_b = "1", + wire_hudindicator_br = "0", + wire_hudindicator_bg = "255", + wire_hudindicator_bb = "0", + wire_hudindicator_ba = "255", + wire_hudindicator_model = "models/jaanus/wiretool/wiretool_siren.mdl", + wire_hudindicator_material = "models/debug/debugwhite", + wire_hudindicator_rotate90 = "0", + wire_hudindicator_showinhud = "0", + wire_hudindicator_huddesc = "", + wire_hudindicator_hudaddname = "0", + wire_hudindicator_hudshowvalue = "0", + wire_hudindicator_hudstyle = "0" + } + }, + + CVars = { + [0] = "wire_hudindicator_a", + [1] = "wire_hudindicator_ar", + [2] = "wire_hudindicator_ag", + [3] = "wire_hudindicator_ab", + [4] = "wire_hudindicator_aa", + [5] = "wire_hudindicator_b", + [6] = "wire_hudindicator_br", + [7] = "wire_hudindicator_bg", + [8] = "wire_hudindicator_bb", + [9] = "wire_hudindicator_ba", + [10] = "wire_hudindicator_model", + [11] = "wire_hudindicator_material", + [12] = "wire_hudindicator_rotate90", + [13] = "wire_hudindicator_showinhud", + [14] = "wire_hudindicator_huddesc", + [15] = "wire_hudindicator_hudaddname", + [16] = "wire_hudindicator_hudshowvalue", + [17] = "wire_hudindicator_hudstyle" + } + }) + + panel:AddControl("Slider", { + Label = "#ToolWireIndicator_a_value", + Type = "Float", + Min = "-10", + Max = "10", + Command = "wire_hudindicator_a" + }) + panel:AddControl("Color", { + Label = "#ToolWireIndicator_a_colour", + Red = "wire_hudindicator_ar", + Green = "wire_hudindicator_ag", + Blue = "wire_hudindicator_ab", + Alpha = "wire_hudindicator_aa", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:AddControl("Slider", { + Label = "#ToolWireIndicator_b_value", + Type = "Float", + Min = "-10", + Max = "10", + Command = "wire_hudindicator_b" + }) + panel:AddControl("Color", { + Label = "#ToolWireIndicator_b_colour", + Red = "wire_hudindicator_br", + Green = "wire_hudindicator_bg", + Blue = "wire_hudindicator_bb", + Alpha = "wire_hudindicator_ba", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:AddControl("ComboBox", { + Label = "#ToolWireIndicator_Model", + MenuButton = "0", + + Options = { + ["Siren"] = { wire_hudindicator_model = "models/jaanus/wiretool/wiretool_siren.mdl" }, + ["Medium 7-seg bar"] = { wire_hudindicator_model = "models/segment2.mdl" }, + ["Small 7-seg bar"] = { wire_hudindicator_model = "models/segment.mdl" }, + ["Barrel"] = { wire_hudindicator_model = "models/props_borealis/bluebarrel001.mdl" }, + ["Grave stone"] = { wire_hudindicator_model = "models/props_c17/gravestone004a.mdl" }, + ["Pop can"] = { wire_hudindicator_model = "models/props_junk/PopCan01a.mdl" }, + ["Traffic Cone"] = { wire_hudindicator_model = "models/props_junk/TrafficCone001a.mdl" }, + ["Big Clock"] = { wire_hudindicator_model = "models/props_trainstation/trainstation_clock001.mdl" } + } + }) + + panel:AddControl("ComboBox", { + Label = "#ToolWireIndicator_Material", + MenuButton = "0", + + Options = { + ["Matte"] = { wire_hudindicator_material = "models/debug/debugwhite" }, + ["Shiny"] = { wire_hudindicator_material = "models/shiny" }, + ["Metal"] = { wire_hudindicator_material = "models/props_c17/metalladder003" } + } + }) + + panel:AddControl("CheckBox", { + Label = "#ToolWireIndicator_90", + Command = "wire_hudindicator_rotate90" + }) + + panel:AddControl("Header", { + Text = "#ToolWireHudIndicator_hudheaderdesc", + Description = "#ToolWireHudIndicator_hudheaderdesc" + }) + + panel:AddControl("CheckBox", { + Label = "#ToolWireHudIndicator_showinhud", + Command = "wire_hudindicator_showinhud" + }) + + panel:AddControl("TextBox", { + Label = "#ToolWireHudIndicator_huddesc", + Command = "wire_hudindicator_huddesc", + MaxLength = "20" + }) + + panel:AddControl("ComboBox", { + Label = "#ToolWireHudIndicator_hudstyle", + MenuButton = "0", + Options = { + ["Basic"] = { wire_hudindicator_hudstyle = "0" }, + ["Gradient"] = { wire_hudindicator_hudstyle = "1" }, + ["Percent Bar"] = { wire_hudindicator_hudstyle = "2" }, + ["Full Circle"] = { wire_hudindicator_hudstyle = "3" }, + ["Semi-circle"] = { wire_hudindicator_hudstyle = "4" } + } + }) + + panel:AddControl("CheckBox", { + Label = "#ToolWireHudIndicator_hudaddname", + Command = "wire_hudindicator_hudaddname", + Description = "#ToolWireHudIndicator_hudaddnamedesc" + }) + + panel:AddControl("ComboBox", { + Label = "#ToolWireHudIndicator_hudshowvalue", + MenuButton = "0", + Options = { + ["Do Not Show"] = { wire_hudindicator_hudshowvalue = "0" }, + ["Percent"] = { wire_hudindicator_hudshowvalue = "1" }, + ["Value"] = { wire_hudindicator_hudshowvalue = "2" } + }, + Description = "#ToolWireHudIndicator_hudshowvaluedesc" + }) + + panel:AddControl("CheckBox", { + Label = "#ToolWireHudIndicator_allowhook", + Command = "wire_hudindicator_allowhook", + Description = "#ToolWireHudIndicator_allowhookdesc" + }) + + panel:AddControl("CheckBox", { + Label = "#ToolWireHudIndicator_hookhidehud", + Command = "wire_hudindicator_hookhidehud", + Description = "#ToolWireHudIndicator_hookhidehuddesc" + }) + + panel:AddControl("Slider", { + Label = "#ToolWireHudIndicator_fullcircleangle", + Type = "Float", + Min = "0", + Max = "360", + Command = "wire_hudindicator_fullcircleangle" + }) + + // Get the currently registered HUD Indicators for this player that can be unregistered + local registered = HUDIndicator_GetCurrentRegistered() + if (#registered > 0) then + local options = {} + for eindex,indinfo in pairs(registered) do + local txt = indinfo.Description or ("Indicator #"..indinfo.EIndex) + options[txt] = { wire_hudindicator_registerdelete = tostring(indinfo.EIndex) } + end + + panel:AddControl("ListBox", { + Label = "#ToolWireHudIndicator_registeredindicators", + MenuButton = 0, + Height = 120, + Options = options + }) + + panel:AddControl("Button", { + Text = "#ToolWireHudIndicator_deleteselected", + Command = "wire_hudindicator_delete" + }) + end + + panel:AddControl("TextBox", { + Label = "#ToolWireHudIndicator_hudx", + Command = "wire_hudindicator_hudx", + Description = "#ToolWireHudIndicator_hudxdesc" + }) + + panel:AddControl("TextBox", { + Label = "#ToolWireHudIndicator_hudy", + Command = "wire_hudindicator_hudy", + Description = "#ToolWireHudIndicator_hudydesc" + }) +end + +// Concommand to unregister HUD Indicator through control panel +local function HUDIndicator_RemoteUnRegister(ply, cmd, arg) + local eindex = ply:GetInfoNum("wire_hudindicator_registerdelete") + if (eindex == 0) then return end + local ent = ents.GetByIndex(eindex) + if (ent && ent:IsValid()) then + ent:UnRegisterPlayer(ply) + end +end +concommand.Add("wire_hudindicator_delete", HUDIndicator_RemoteUnRegister) diff --git a/lua/weapons/gmod_tool/stools/wire_hydraulic.lua b/lua/weapons/gmod_tool/stools/wire_hydraulic.lua new file mode 100644 index 0000000000..3be41f54ae --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_hydraulic.lua @@ -0,0 +1,403 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Hydraulic" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +TOOL.ClientConVar[ "material" ] = "cable/rope" +TOOL.ClientConVar[ "width" ] = "3" +TOOL.ClientConVar[ "fixed" ] = "0" +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/hydraulic.mdl" +TOOL.ClientConVar[ "modelsize" ] = "" + +if SERVER then + ModelPlug_Register("Hydraulic") +end + +if CLIENT then + language.Add( "Tool_wire_hydraulic_name", "Hydraulic Tool (Wire)" ) + language.Add( "Tool_wire_hydraulic_desc", "Makes a controllable hydraulic" ) + language.Add( "Tool_wire_hydraulic_0", "Primary: Place hydraulic\nSecondary: Place hydraulic along the hit normal" ) + language.Add( "Tool_wire_hydraulic_1", "Left click on the second point" ) + language.Add( "Tool_wire_hydraulic_2", "Left click to place the controller" ) + language.Add( "WireHydraulicTool_width", "Width:" ) + language.Add( "WireHydraulicTool_material", "Material:" ) + language.Add( "WireHydraulicTool_fixed", "Fixed" ) + language.Add( "undone_wirehydraulic", "Undone Wire Hydraulic" ) +end + +function TOOL:LeftClick( trace ) + if ( trace.Entity:IsValid() && trace.Entity:IsPlayer() ) then return end + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + local iNum = self:NumObjects() + + local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + self:SetObject( iNum + 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal ) + + if ( iNum > 1 ) then + + if ( CLIENT ) then + self:ClearObjects() + return true + end + + local ply = self:GetOwner() + local Ent1, Ent2, Ent3 = self:GetEnt(1), self:GetEnt(2), trace.Entity + local const, rope = self.constraint, self.rope + + if ( !const ) or ( !const:IsValid() ) then + WireLib.AddNotify(self:GetOwner(), "Wire Hydraulic Invalid!", NOTIFY_GENERIC, 7) + self:ClearObjects() + self:SetStage(0) + return + end + + local model = self:GetClientInfo( "model" ) + local modelsize = self:GetClientInfo( "modelsize" ) + + if (modelsize!="") then + local test = string.sub(model, 1, -5) .. modelsize .. string.sub(model, -4) + if ( util.IsValidModel( test ) || util.IsValidProp( test ) ) then + model = test + end + end + + if ( !util.IsValidModel( model ) ) then return false end + if ( !util.IsValidProp( model ) ) then return false end + + // Attach our Controller to the Elastic constraint + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + local controller = MakeWireHydraulicController(ply, trace.HitPos, Ang, model, nil, const, rope) + + local min = controller:OBBMins() + controller:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const2 = WireLib.Weld(controller, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireHydraulic") + undo.AddEntity( controller ) + undo.AddEntity( const ) + undo.AddEntity( rope ) + undo.AddEntity( const2 ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "ropeconstraints", controller ) + ply:AddCleanup( "ropeconstraints", const2 ) + + if const then controller:DeleteOnRemove( const ) end + if rope then controller:DeleteOnRemove( rope ) end + + self:ClearObjects() + self:SetStage(0) + + elseif ( iNum == 1 ) then + + if ( CLIENT ) then + return true + end + + // Get client's CVars + local material = self:GetClientInfo( "material" ) or "cable/rope" + local width = self:GetClientNumber( "width" ) or 3 + local fixed = self:GetClientNumber( "fixed" ) or 0 + + // Get information we're about to use + local Ent1, Ent2 = self:GetEnt(1), self:GetEnt(2) + local Bone1, Bone2 = self:GetBone(1), self:GetBone(2) + local LPos1, LPos2 = self:GetLocalPos(1),self:GetLocalPos(2) + + local const,rope = MakeWireHydraulic( self:GetOwner(), Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, material, fixed ) + + self.constraint, self.rope = const,rope + + undo.Create("WireHydraulic") + if constraint then undo.AddEntity( const ) end + if rope then undo.AddEntity( rope ) end + undo.SetPlayer( self:GetOwner() ) + undo.Finish() + + if const then self:GetOwner():AddCleanup( "ropeconstraints", const ) end + if rope then self:GetOwner():AddCleanup( "ropeconstraints", rope ) end + + self:SetStage(2) + + else + + self:SetStage( iNum+1 ) + + end + + return true + +end + +function TOOL:RightClick( trace ) + + local iNum = self:NumObjects() + + if ( iNum > 1 ) then + if ( !self.constraint ) or ( !self.constraint:IsValid() ) then + self:ClearObjects() + self:SetStage(0) + else + return false + end + end + + // If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + self:SetObject( 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal ) + + local tr = {} + tr.start = trace.HitPos + tr.endpos = tr.start + (trace.HitNormal * 16384) + tr.filter = {} + tr.filter[1] = self:GetOwner() + if (trace.Entity:IsValid()) then + tr.filter[2] = trace.Entity + end + + local tr = util.TraceLine( tr ) + + if ( !tr.Hit ) then + self:ClearObjects() + return + end + + // Don't try to constrain world to world + if ( trace.HitWorld && tr.HitWorld ) then + self:ClearObjects() + return + end + + if ( trace.Entity:IsValid() && trace.Entity:IsPlayer() ) then + self:ClearObjects() + return + end + if ( tr.Entity:IsValid() && tr.Entity:IsPlayer() ) then + self:ClearObjects() + return + end + + local Phys2 = tr.Entity:GetPhysicsObjectNum( tr.PhysicsBone ) + self:SetObject( 2, tr.Entity, tr.HitPos, Phys2, tr.PhysicsBone, trace.HitNormal ) + + if ( CLIENT ) then + return true + end + + // Get client's CVars + local material = self:GetClientInfo( "material" ) or "cable/rope" + local width = self:GetClientNumber( "width" ) or 3 + local fixed = self:GetClientNumber( "fixed" ) or 0 + + // Get information we're about to use + local Ent1, Ent2 = self:GetEnt(1), self:GetEnt(2) + local Bone1, Bone2 = self:GetBone(1), self:GetBone(2) + local LPos1, LPos2 = self:GetLocalPos(1),self:GetLocalPos(2) + + local const,rope = MakeWireHydraulic( self:GetOwner(), Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, material, fixed ) + + self.constraint, self.rope = const,rope + + undo.Create("WireHydraulic") + if const then undo.AddEntity( const ) end + if rope then undo.AddEntity( rope ) end + if controller then undo.AddEntity( controller ) end + undo.SetPlayer( self:GetOwner() ) + undo.Finish() + + if constraint then self:GetOwner():AddCleanup( "ropeconstraints", const ) end + if rope then self:GetOwner():AddCleanup( "ropeconstraints", rope ) end + + self:SetStage(2) + + return true + +end + +if SERVER then + + local function CalcElasticConsts(Phys1, Phys2, Ent1, Ent2) + local minMass = 0; + + if ( Ent1:IsWorld() ) then minMass = Phys2:GetMass() + elseif ( Ent2:IsWorld() ) then minMass = Phys1:GetMass() + else + minMass = math.min( Phys1:GetMass(), Phys2:GetMass() ) + end + + // const, damp + local const = minMass * 100 + local damp = const * 0.2 + + if ( iFixed == 0 ) then + + const = minMass * 50 + damp = const * 0.1 + + end + + return const, damp + end + + //need for the const to find the controler after being duplicator pasted + WireHydraulicTracking = {} + + function MakeWireHydraulicController( pl, Pos, Ang, model, MyEntId, const, rope ) + local controller = ents.Create("gmod_wire_hydraulic") + + controller:SetPos( Pos ) + controller:SetAngles( Ang ) + controller:Setup() + controller:SetPlayer(pl) + + if (!const) then + WireHydraulicTracking[ MyEntId ] = controller + else + controller.MyId = controller:EntIndex() + const.MyCrtl = controller:EntIndex() + controller:SetConstraint( const ) + controller:DeleteOnRemove( const ) + end + + controller:SetModel( Model(model or "models/jaanus/wiretool/wiretool_siren.mdl") ) + + if (rope) then + controller:SetRope( rope ) + controller:DeleteOnRemove( rope ) + end + + controller:Spawn() + + return controller + end + + duplicator.RegisterEntityClass("gmod_wire_hydraulic", MakeWireHydraulicController, "Pos", "Ang", "Model", "MyId" ) + + function MakeWireHydraulic( pl, Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, material, fixed, MyCrtl ) + if ( !constraint.CanConstrain( Ent1, Bone1 ) ) then return false end + if ( !constraint.CanConstrain( Ent2, Bone2 ) ) then return false end + + local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) + local Phys2 = Ent2:GetPhysicsObjectNum( Bone2) + local WPos1 = Phys1:LocalToWorld( LPos1 ) + local WPos2 = Phys2:LocalToWorld( LPos2 ) + + if ( Phys1 == Phys2 ) then return false end + + local constant, dampen = CalcElasticConsts( Phys1, Phys2, Ent1, Ent2 ) + + local const, rope = constraint.Elastic( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, constant, dampen, 0, material, width, false ) + + if fixed == 1 then + local slider = constraint.Slider( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, 0 ) + slider:SetTable( {} ) + const:DeleteOnRemove( slider ) + end + + if ( !const ) then return nil, rope end + + local ctable = { + Type = "WireHydraulic", + pl = pl, + Ent1 = Ent1, + Ent2 = Ent2, + Bone1 = Bone1, + Bone2 = Bone2, + LPos1 = LPos1, + LPos2 = LPos2, + width = width, + material = material, + fixed = fixed + } + const:SetTable( ctable ) + + if (MyCrtl) then + Msg("finding crtl for this wired hyd const\n") + local controller = WireHydraulicTracking[ MyCrtl ] + + const.MyCrtl = controller:EntIndex() + controller.MyId = controller:EntIndex() + + controller:SetConstraint( const ) + controller:DeleteOnRemove( const ) + if (rope) then + controller:SetRope( rope ) + controller:DeleteOnRemove( rope ) + end + + Ent1:DeleteOnRemove( controller ) + Ent2:DeleteOnRemove( controller ) + const:DeleteOnRemove( controller ) + end + + return const, rope + end + + duplicator.RegisterConstraint( "WireHydraulic", MakeWireHydraulic, "pl", "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "width", "material", "fixed", "MyCrtl" ) + +end + +function TOOL:Reload( trace ) + + if (!trace.Entity:IsValid() || trace.Entity:IsPlayer() ) then return false end + if ( CLIENT ) then return true end + + local bool = constraint.RemoveConstraints( trace.Entity, "WireHydraulic" ) + return bool + +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_hydraulic_modelsize = "" }, + ["mini"] = { wire_hydraulic_modelsize = "_mini" }, + ["nano"] = { wire_hydraulic_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "Hydraulic", "wire_hydraulic", "#ToolWireIndicator_Model") + panel:AddControl("CheckBox", { + Label = "#WireHydraulicTool_fixed", + Command = "wire_hydraulic_fixed" + }) + + panel:AddControl("Slider", { + Label = "#WireHydraulicTool_width", + Type = "Float", + Min = "1", + Max = "20", + Command = "wire_hydraulic_width" + }) + + panel:AddControl("MaterialGallery", { + Label = "#WireHydraulicTool_material", + Height = "64", + Width = "28", + Rows = "1", + Stretch = "1", + + Options = { + ["Wire"] = { Material = "cable/rope_icon", wire_hydraulic_material = "cable/rope" }, + ["Cable 2"] = { Material = "cable/cable_icon", wire_hydraulic_material = "cable/cable2" }, + ["XBeam"] = { Material = "cable/xbeam", wire_hydraulic_material = "cable/xbeam" }, + ["Red Laser"] = { Material = "cable/redlaser", wire_hydraulic_material = "cable/redlaser" }, + ["Blue Electric"] = { Material = "cable/blue_elec", wire_hydraulic_material = "cable/blue_elec" }, + ["Physics Beam"] = { Material = "cable/physbeam", wire_hydraulic_material = "cable/physbeam" }, + ["Hydra"] = { Material = "cable/hydra", wire_hydraulic_material = "cable/hydra" }, + }, + + CVars = { + [0] = "wire_hydraulic_material" + } + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_keyboard.lua b/lua/weapons/gmod_tool/stools/wire_keyboard.lua new file mode 100644 index 0000000000..3cc038d8ee --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_keyboard.lua @@ -0,0 +1,174 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Wired Keyboard" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_keyboard_name", "Wired Keyboard Tool (Wire)" ) + language.Add( "Tool_wire_keyboard_desc", "Spawns a keyboard input for use with the hi-speed wire system." ) + language.Add( "Tool_wire_keyboard_0", "Primary: Create/Update Keyboard, Secondary: Link Keyboard to pod, Reload: Unlink" ) + language.Add( "Tool_wire_keyboard_1", "Now select the pod to link to.") + language.Add( "sboxlimit_wire_keyboard", "You've hit wired keyboards limit!" ) + language.Add( "undone_wirekeyboard", "Undone Wire Keyboard" ) +end + +if (SERVER) then + ModelPlug_Register("Keyboard") + CreateConVar('sbox_maxwire_keyboards', 20) +end + +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/keyboard.mdl" +TOOL.ClientConVar[ "sync" ] = "1" + +cleanup.Register( "wire_keyboards" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_keyboard" && trace.Entity.pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_keyboards" ) ) then return false end + + local model = self:GetClientInfo( "model" ) + + if ( !util.IsValidModel( model ) ) then return false end + if ( !util.IsValidProp( model ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_keyboard = MakeWireKeyboard( ply, trace.HitPos, Ang, model ) + + local min = wire_keyboard:OBBMins() + wire_keyboard:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_keyboard, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireKeyboard") + undo.AddEntity( wire_keyboard ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_keyboards", wire_keyboard ) + + return true + +end + +function TOOL:RightClick( trace ) + if (!trace.HitPos) then return false end + local ent = trace.Entity + if (ent:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if self:GetStage() == 0 then + if ( not ent:IsValid() or ent:GetClass() ~= "gmod_wire_keyboard" or ent.pl ~= ply ) then return false end + + self:SetStage(1) + self.LinkSource = ent + return true + else + --TODO: player check is missing. done by the prop protection plugin? + if ( not ent:IsValid() or not ent:IsVehicle() ) then return false end + + self.LinkSource:LinkPod(ent) + + self:SetStage(0) + self.LinkSource = nil + return true + end +end + +function TOOL:Reload(trace) + self:SetStage(0) + self.LinkSource = nil + + if (!trace.HitPos) then return false end + local ent = trace.Entity + if (ent:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( not ent:IsValid() or ent:GetClass() ~= "gmod_wire_keyboard" or ent.pl ~= ply ) then return false end + ent:LinkPod(nil) + return true +end + +if (SERVER) then + + function MakeWireKeyboard( pl, Pos, Ang, model ) + if ( !pl:CheckLimit( "wire_keyboards" ) ) then return false end + + local wire_keyboard = ents.Create( "gmod_wire_keyboard" ) + if (!wire_keyboard:IsValid()) then return false end + + wire_keyboard:SetAngles( Ang ) + wire_keyboard:SetPos( Pos ) + if(!model) then + wire_keyboard:SetModel( Model("models/jaanus/wiretool/wiretool_input.mdl") ) + else + wire_keyboard:SetModel( Model(model) ) + end + wire_keyboard:Spawn() + + wire_keyboard:SetPlayer( pl ) + wire_keyboard.pl = pl + + pl:AddCount( "wire_keyboards", wire_keyboard ) + + return wire_keyboard + end + + duplicator.RegisterEntityClass("gmod_wire_keyboard", MakeWireKeyboard, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireKeyboard( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_keyboard" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" ) ) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireKeyboard( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_keyboard_name", Description = "#Tool_wire_keyboard_desc" }) + ModelPlug_AddToCPanel(panel, "Keyboard", "wire_keyboard", "#ToolWireIndicator_Model") + + panel:AddControl( "CheckBox", { Label = "Synchronous keyboard", + Description = "Pause user input when keyboard is active (clientside)", + Command = "wire_keyboard_sync" } ) +end diff --git a/lua/weapons/gmod_tool/stools/wire_las_reciever.lua b/lua/weapons/gmod_tool/stools/wire_las_reciever.lua new file mode 100644 index 0000000000..c7d8841aeb --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_las_reciever.lua @@ -0,0 +1,129 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "Laser Pointer Receiver" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_las_reciever_name", "Laser Receiver Tool (Wire)" ) + language.Add( "Tool_wire_las_reciever_desc", "Spawns a constant laser receiver prop for use with the wire system." ) + language.Add( "Tool_wire_las_reciever_0", "Primary: Create/Update Laser Receiver" ) + language.Add( "WireILaserRecieverTool_ilas_reciever", "Laser Receiver:" ) + language.Add( "sboxlimit_wire_las_recievers", "You've hit laser receivers limit!" ) + language.Add( "undone_wireigniter", "Undone Wire Laser Receiver" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_las_receivers', 20) +end + +TOOL.Model = "models/jaanus/wiretool/wiretool_range.mdl" + +cleanup.Register( "wire_las_receivers" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_las_reciever" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_las_receivers" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_las_reciever = MakeWireLaserReciever( ply, trace.HitPos, Ang, self.Model ) + + local min = wire_las_reciever:OBBMins() + wire_las_reciever:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_las_reciever, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Laser Receiver") + undo.AddEntity( wire_las_reciever ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_las_receivers", wire_las_reciever ) + + return true +end + +if (SERVER) then + + function MakeWireLaserReciever( pl, Pos, Ang, model ) + if ( !pl:CheckLimit( "wire_las_receivers" ) ) then return false end + + local wire_las_reciever = ents.Create( "gmod_wire_las_reciever" ) + if (!wire_las_reciever:IsValid()) then return false end + + wire_las_reciever:SetAngles( Ang ) + wire_las_reciever:SetPos( Pos ) + wire_las_reciever:SetModel( Model(model or "models/jaanus/wiretool/wiretool_range.mdl") ) + wire_las_reciever:Spawn() + + wire_las_reciever:SetPlayer( pl ) + wire_las_reciever.pl = pl + + pl:AddCount( "wire_las_receivers", wire_las_reciever ) + + return wire_las_reciever + end + + duplicator.RegisterEntityClass("gmod_wire_las_reciever", MakeWireLaserReciever, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireLaserReciever( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_las_reciever" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireLaserReciever( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_las_reciever_name", Description = "#Tool_wire_las_reciever_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_las_reciever", + + Options = { + Default = { + wire_las_reciever_las_reciever = "0", + } + }, + CVars = { + } + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_latch.lua b/lua/weapons/gmod_tool/stools/wire_latch.lua new file mode 100644 index 0000000000..b89f6d2124 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_latch.lua @@ -0,0 +1,137 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Weld/Constraint Latch" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if CLIENT then + language.Add( "Tool_wire_latch_name", "Latch Tool (Wire)" ) + language.Add( "Tool_wire_latch_desc", "Makes a controllable latch" ) + language.Add( "Tool_wire_latch_0", "Primary: Click on first entity to be latched" ) + language.Add( "Tool_wire_latch_1", "Left click on the second entity" ) + language.Add( "Tool_wire_latch_2", "Left click to place the controller" ) + language.Add( "undone_wirelatch", "Undone Wire Latch" ) +end + +function TOOL:LeftClick( trace ) + if ( trace.Entity:IsValid() && trace.Entity:IsPlayer() ) then return end + + // If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + local iNum = self:NumObjects() + + local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + self:SetObject( iNum + 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal ) + + if ( iNum > 1 ) then + + if ( CLIENT ) then + self:ClearObjects() + return true + end + + local ply = self:GetOwner() + local Ent1, Ent2, Ent3 = self:GetEnt(1), self:GetEnt(2), trace.Entity + local const = self.constraint + + if ( !const ) or ( !const:IsValid() ) then + WireLib.AddNotify(self:GetOwner(), "Latch Weld Invalid!", NOTIFY_GENERIC, 7) + self:ClearObjects() + self:SetStage(0) + return + end + + // Attach our Controller to the weld constraint + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + local controller = MakeWireLatchController( ply, trace.HitPos, Ang, "models/jaanus/wiretool/wiretool_siren.mdl" ) + + // Send Entity and Constraint info over to the controller + controller:SendVars(self.Ent1, self.Ent2, self.Bone1, self.Bone2, self.constraint) + + local min = controller:OBBMins() + controller:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const2 = WireLib.Weld(controller, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireLatch") + undo.AddEntity( controller ) + undo.AddEntity( const ) + undo.AddEntity( const2 ) + undo.SetPlayer( ply ) + undo.Finish() + + if const then controller:DeleteOnRemove( const ) end + + self:ClearObjects() + self:SetStage(0) + + elseif ( iNum == 1 ) then + + if ( CLIENT ) then + return true + end + + // Get information we're about to use + self.Ent1, self.Ent2 = self:GetEnt(1), self:GetEnt(2) + self.Bone1, self.Bone2 = self:GetBone(1), self:GetBone(2) + + local const = MakeWireLatch( self.Ent1, self.Ent2, self.Bone1, self.Bone2 ) + + self.constraint = const + + undo.Create("WireLatch") + if constraint then undo.AddEntity( const ) end + undo.SetPlayer( self:GetOwner() ) + undo.Finish() + + self:SetStage(2) + + else + + self:SetStage( iNum+1 ) + + end + + return true + +end + +if SERVER then + + function MakeWireLatchController( pl, Pos, Ang, model ) + local controller = ents.Create("gmod_wire_latch") + + controller:SetPos( Pos ) + controller:SetAngles( Ang ) + controller:SetModel( Model(model or "models/jaanus/wiretool/wiretool_siren.mdl") ) + controller:SetPlayer(pl) + + controller:Spawn() + + return controller + end + duplicator.RegisterEntityClass("gmod_wire_latch", MakeWireLatchController, "Pos", "Ang", "Model") + + function MakeWireLatch( Ent1, Ent2, Bone1, Bone2 ) + if ( !constraint.CanConstrain( Ent1, Bone1 ) ) then return false end + if ( !constraint.CanConstrain( Ent2, Bone2 ) ) then return false end + + local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) + local Phys2 = Ent2:GetPhysicsObjectNum( Bone2) + + if ( Phys1 == Phys2 ) then return false end + + local const = constraint.Weld( Ent1, Ent2, Bone1, Bone2, 0 ) + + if ( !const ) then return nil end + + return const + end + +end + +function TOOL.BuildCPanel( panel ) + panel:AddControl( "Header", { Text = "#Tool_wire_latch_name", Description = "#Tool_wire_latch_desc" } ) +end diff --git a/lua/weapons/gmod_tool/stools/wire_locator.lua b/lua/weapons/gmod_tool/stools/wire_locator.lua new file mode 100644 index 0000000000..b58b884209 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_locator.lua @@ -0,0 +1,122 @@ +TOOL.Category = "Wire - Beacon" +TOOL.Name = "Locator" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_locator_name", "Locator Beacon Tool (Wire)" ) + language.Add( "Tool_wire_locator_desc", "Spawns a locator beacon for use with the wire system." ) + language.Add( "Tool_wire_locator_0", "Primary: Create/Update Locator Beacon" ) + language.Add( "sboxlimit_wire_locators", "You've hit locator beacons limit!" ) + language.Add( "undone_wirelocator", "Undone Wire Locator Beacon" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_locators',30) +end + +TOOL.Model = "models/props_lab/powerbox02d.mdl" + +cleanup.Register( "wire_locators" ) + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_locator" && trace.Entity.pl == ply ) then + trace.Entity:Setup() + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_locators" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + + local wire_locator = MakeWireLocator( ply, trace.HitPos, Ang, self.Model ) + + local min = wire_locator:OBBMins() + wire_locator:SetPos( trace.HitPos - trace.HitNormal * (min.z) ) + + undo.Create("WireLocator") + undo.AddEntity( wire_locator ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_locators", wire_locator ) + + return true + +end + +if SERVER then + + function MakeWireLocator(pl, Pos, Ang, model, nocollide ) + if (!pl:CheckLimit("wire_locators")) then return end + + local wire_locator = ents.Create("gmod_wire_locator") + wire_locator:SetPos(Pos) + wire_locator:SetAngles(Ang) + wire_locator:SetModel( Model(model or "models/props_lab/powerbox02d.mdl") ) + wire_locator:Spawn() + wire_locator:Activate() + + wire_locator:SetOverlayText("Locator Beacon") + wire_locator:SetPlayer(pl) + + if ( nocollide == true ) then wire_light:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + pl = pl, + nocollide = nocollide, + } + table.Merge( wire_locator:GetTable(), ttable ) + + pl:AddCount( "wire_locators", wire_locator ) + + return wire_locator + end + + duplicator.RegisterEntityClass("gmod_wire_locator", MakeWireLocator, "Pos", "Ang", "Model", "nocollide") + +end + +function TOOL:UpdateGhostWireLocator( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_locator" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireLocator( self.GhostEntity, self:GetOwner() ) + +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_locator_name", Description = "#Tool_wire_locator_desc" }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_nailer.lua b/lua/weapons/gmod_tool/stools/wire_nailer.lua new file mode 100644 index 0000000000..a5cc02a5dc --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_nailer.lua @@ -0,0 +1,158 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Nailer" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_nailer_name", "Nailer Tool (Wire)" ) + language.Add( "Tool_wire_nailer_desc", "Spawns a constant nailer prop for use with the wire system." ) + language.Add( "Tool_wire_nailer_0", "Primary: Create/Update Nailer" ) + language.Add( "WireNailerTool_nailer", "Nailer:" ) + language.Add( "WireNailerTool_Model", "Choose a Model:") + language.Add( "sboxlimit_wire_nailers", "You've hit nailers limit!" ) + language.Add( "undone_wirenailer", "Undone Wire Nailer" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_nailers', 20) +end + +TOOL.ClientConVar[ "forcelim" ] = "0" +TOOL.ClientConVar[ "Model" ] = "models/jaanus/wiretool/wiretool_siren.mdl" + +local nailermodels = { + ["models/jaanus/wiretool/wiretool_beamcaster.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {}}; + +cleanup.Register( "wire_nailers" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local model = self:GetClientInfo( "Model" ) + local flim = self:GetClientNumber( "forcelim" ) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_nailer" && trace.Entity:GetTable().pl == ply ) then + trace.Entity:Setup( flim ) + trace.Entity.Flim = flim + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_nailers" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_nailer = MakeWireNailer( ply, trace.HitPos, Ang, model, flim ) + + local min = wire_nailer:OBBMins() + wire_nailer:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_nailer, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Nailer") + undo.AddEntity( wire_nailer ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_nailers", wire_nailer ) + + return true +end + +if (SERVER) then + + function MakeWireNailer( pl, Pos, Ang, model, flim ) + if ( !pl:CheckLimit( "wire_nailers" ) ) then return false end + + local wire_nailer = ents.Create( "gmod_wire_nailer" ) + if (!wire_nailer:IsValid()) then return false end + + wire_nailer:SetAngles( Ang ) + wire_nailer:SetPos( Pos ) + wire_nailer:SetModel( model ) + wire_nailer:Spawn() + + wire_nailer:Setup( flim ) + wire_nailer:SetPlayer( pl ) + + local ttable = { + pl = pl, + Flim = flim + } + table.Merge(wire_nailer:GetTable(), ttable ) + + pl:AddCount( "wire_nailers", wire_nailer ) + + return wire_nailer + end + + duplicator.RegisterEntityClass("gmod_wire_nailer", MakeWireNailer, "Pos", "Ang", "Model", "Flim") + +end + +function TOOL:UpdateGhostWireNailer( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_nailer" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model") , Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireNailer( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_nailer_name", Description = "#Tool_wire_nailer_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_nailer", + + Options = { + Default = { + wire_nailer_nailer = "0", + } + }, + CVars = { + } + }) + + panel:AddControl( "PropSelect", { Label = "#WireNailerTool_Model", + ConVar = "wire_nailer_Model", + Category = "Wire Nailer", + Models = nailermodels } ) + + panel:AddControl("Slider", { + Label = "Force Limit", + Type = "Float", + Min = "0", + Max = "10000", + Command = "wire_nailer_forcelim" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_namer.lua b/lua/weapons/gmod_tool/stools/wire_namer.lua new file mode 100644 index 0000000000..ee92d60279 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_namer.lua @@ -0,0 +1,59 @@ +TOOL.Category = "Wire - Tools" +TOOL.Name = "Namer" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_namer_name", "Naming Tool" ) + language.Add( "Tool_wire_namer_desc", "Names components." ) + language.Add( "Tool_wire_namer_0", "Primary: Set name\nSecondary: Get name" ) + language.Add( "WireNamerTool_name", "Name:" ) +end + +TOOL.ClientConVar[ "name" ] = "" + +local function SetName( Player, Entity, Data ) + if ( Data and Data.name ) then + Entity:SetNetworkedString("WireName", Data.name) + duplicator.StoreEntityModifier( Entity, "WireName", Data ) + end +end +duplicator.RegisterEntityModifier( "WireName", SetName ) + +function TOOL:LeftClick(trace) + if (not trace.Entity:IsValid()) then return end + if (CLIENT) then return end + if (not trace.Entity.IsWire) then return end + + local name = self:GetClientInfo("name") + + //trace.Entity:SetNetworkedString("WireName", name) + + //made the WireName duplicatable entmod (TAD2020) + SetName( Player, trace.Entity, {name = name} ) + + return true +end + + +function TOOL:RightClick(trace) + if (not trace.Entity:IsValid()) then return end + if (CLIENT) then return end + + local name = trace.Entity:GetNetworkedString("WireName") + if (not name) then return end + + self:GetOwner():ConCommand('wire_namer_name "' .. name .. '"') +end + + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_namer_name", Description = "#Tool_wire_namer_desc" }) + + panel:AddControl("TextBox", { + Label = "#WireNamerTool_name", + Command = "wire_namer_name", + MaxLength = "20" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_numpad.lua b/lua/weapons/gmod_tool/stools/wire_numpad.lua new file mode 100644 index 0000000000..3a6349f188 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_numpad.lua @@ -0,0 +1,217 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Wired Numpad" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_numpad_name", "Wired Numpad Tool (Wire)" ) + language.Add( "Tool_wire_numpad_desc", "Spawns a numpad input for use with the wire system." ) + language.Add( "Tool_wire_numpad_0", "Primary: Create/Update Numpad" ) + language.Add( "WireNumpadTool_toggle", "Toggle" ) + language.Add( "WireNumpadTool_value_on", "Value On:" ) + language.Add( "WireNumpadTool_value_off", "Value Off:" ) + language.Add( "sboxlimit_wire_numpad", "You've hit wired numpads limit!" ) + language.Add( "undone_wirenumpad", "Undone Wire Numpad" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_numpads', 20) + ModelPlug_Register("Numpad") +end + +TOOL.ClientConVar[ "toggle" ] = "0" +TOOL.ClientConVar[ "value_off" ] = "0" +TOOL.ClientConVar[ "value_on" ] = "1" +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/numpad.mdl" +TOOL.ClientConVar[ "modelsize" ] = "" +local ModelInfo = {"","",""} + +cleanup.Register( "wire_numpads" ) + +function TOOL:LeftClick( trace ) + + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local _toggle = self:GetClientNumber( "toggle" ) + local _value_off = self:GetClientNumber( "value_off" ) + local _value_on = self:GetClientNumber( "value_on" ) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_numpad" && trace.Entity.pl == ply ) then + trace.Entity:Setup( _toggle, _value_off, _value_on ) + trace.Entity.toggle = _toggle + trace.Entity.value_off = _value_off + trace.Entity.value_on = _value_on + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_numpads" ) ) then return false end + + if ( !util.IsValidModel( ModelInfo[3] ) ) then return false end + if ( !util.IsValidProp( ModelInfo[3] ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_numpad = MakeWireNumpad( ply, trace.HitPos, Ang, ModelInfo[3], _toggle, _value_off, _value_on ) + + local min = wire_numpad:OBBMins() + wire_numpad:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_numpad, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireNumpad") + undo.AddEntity( wire_numpad ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_numpads", wire_numpad ) + + return true + +end + +if (SERVER) then + + function MakeWireNumpad( pl, Pos, Ang, model, toggle, value_off, value_on ) + if ( !pl:CheckLimit( "wire_numpads" ) ) then return false end + + local wire_numpad = ents.Create( "gmod_wire_numpad" ) + if (!wire_numpad:IsValid()) then return false end + + wire_numpad:SetAngles( Ang ) + wire_numpad:SetPos( Pos ) + if(!model) then + wire_numpad:SetModel( Model("models/jaanus/wiretool/wiretool_input.mdl") ) + else + wire_numpad:SetModel( Model(model) ) + end + wire_numpad:Spawn() + + wire_numpad:Setup(toggle, value_off, value_on ) + wire_numpad:SetPlayer( pl ) + + wire_numpad.impulses = {} + for k = 0, 16 do + table.insert(wire_numpad.impulses, numpad.OnDown( pl, k, "WireNumpad_On", wire_numpad, k )) + table.insert(wire_numpad.impulses, numpad.OnUp( pl, k, "WireNumpad_Off", wire_numpad, k )) + end + + local ttable = { + toggle = toggle, + value_off = value_off, + value_on = value_on, + pl = pl + } + table.Merge(wire_numpad, ttable ) + + pl:AddCount( "wire_numpads", wire_numpad ) + + return wire_numpad + end + + duplicator.RegisterEntityClass("gmod_wire_numpad", MakeWireNumpad, "Pos", "Ang", "Model", "toggle", "value_off", "value_on") + +end //end server if + +function TOOL:UpdateGhostWireNumpad( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_numpad" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if ModelInfo[1]!= self:GetClientInfo( "model" ) || ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + ModelInfo[1] = self:GetClientInfo( "model" ) + ModelInfo[2] = self:GetClientInfo( "modelsize" ) + ModelInfo[3] = ModelInfo[1] + if (ModelInfo[1] && ModelInfo[2] && ModelInfo[2]!="") then + local test = string.sub(ModelInfo[1], 1, -5) .. ModelInfo[2] .. string.sub(ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + ModelInfo[3] = test + end + end + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireNumpad( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_numpad_name", Description = "#Tool_wire_numpad_desc" }) + + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_numpad_modelsize = "" }, + ["mini"] = { wire_numpad_modelsize = "_mini" }, + ["nano"] = { wire_numpad_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "Numpad", "wire_numpad", "#ToolWireIndicator_Model") + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_numpad", + + Options = { + Default = { + wire_numpad_toggle = "0", + wire_numpad_value_on = "1", + wire_numpad_value_off = "0" + } + }, + + CVars = { + [0] = "wire_numpad_toggle", + [1] = "wire_numpad_value_on", + [2] = "wire_numpad_value_off" + } + }) + + panel:AddControl("CheckBox", { + Label = "#WireNumpadTool_toggle", + Command = "wire_numpad_toggle" + }) + + panel:AddControl("Slider", { + Label = "#WireNumpadTool_value_on", + Type = "Float", + Min = "-10", + Max = "10", + Command = "wire_numpad_value_on" + }) + panel:AddControl("Slider", { + Label = "#WireNumpadTool_value_off", + Type = "Float", + Min = "-10", + Max = "10", + Command = "wire_numpad_value_off" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_output.lua b/lua/weapons/gmod_tool/stools/wire_output.lua new file mode 100644 index 0000000000..a114f44f09 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_output.lua @@ -0,0 +1,183 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Numpad Output" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_output_name", "Output Tool (Wire)" ) + language.Add( "Tool_wire_output_desc", "Spawns an output for use with the wire system." ) + language.Add( "Tool_wire_output_0", "Primary: Create/Update Output" ) + language.Add( "WireOutput_keygroup", "Key:" ) + language.Add( "sboxlimit_wire_outputs", "You've hit outputs limit!" ) + language.Add( "undone_wireoutput", "Undone Wire Output" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_outputs', 10) + ModelPlug_Register("Numpad") +end + +TOOL.ClientConVar[ "keygroup" ] = "1" +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/numpad.mdl" +TOOL.ClientConVar[ "modelsize" ] = "" +local ModelInfo = {"","",""} + +cleanup.Register( "wire_outputs" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + local key = self:GetClientNumber( "keygroup" ) + + // If we shot a wire_output do nothing + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_output" && trace.Entity.pl == ply ) then + trace.Entity.key = key + if (numpad.GetModifiedKey) then key = numpad.GetModifiedKey(ply, key) end + trace.Entity:SetKey(key) + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_outputs" ) ) then return false end + + if ( !util.IsValidModel( ModelInfo[3] ) ) then return false end + if ( !util.IsValidProp( ModelInfo[3] ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_output = MakeWireOutput( ply, trace.HitPos, Ang, ModelInfo[3], key ) + + local min = wire_output:OBBMins() + wire_output:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_output, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireOutput") + undo.AddEntity( wire_output ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_outputs", wire_output ) + + return true +end + +if (SERVER) then + + function MakeWireOutput( pl, Pos, Ang, model, key ) + if (numpad.GetModifiedKey) then key = numpad.GetModifiedKey(pl, key) end + + if ( !pl:CheckLimit( "wire_outputs" ) ) then return false end + + local wire_output = ents.Create( "gmod_wire_output" ) + if (!wire_output:IsValid()) then return false end + + wire_output:SetAngles( Ang ) + wire_output:SetPos( Pos ) + if(!model) then + wire_output:SetModel( Model("models/jaanus/wiretool/wiretool_output.mdl") ) + else + wire_output:SetModel( Model(model) ) + end + wire_output:Spawn() + + wire_output:SetPlayer(pl) + wire_output:SetKey(key) + + local ttable = { + key = key, + pl = pl, + } + table.Merge(wire_output:GetTable(), ttable ) + + wire_output:GetTable():ShowOutput() + pl:AddCount( "wire_outputs", wire_output ) + + return wire_output + end + + duplicator.RegisterEntityClass("gmod_wire_output", MakeWireOutput, "Pos", "Ang", "Model", "key") + +end + +function TOOL:UpdateGhostWireOutput( ent, player ) + + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_output" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:GetModel() + local model = self:GetClientInfo("model") + local size = self:GetClientInfo("modelsize") +if (model=="models/jaanus/wiretool/wiretool_output.mdl") then return model end +if (model && size) then + if (size!="") then + return string.sub(model, 1, -5) .. size .. string.sub(model, -4) + end + return model +end +end + +function TOOL:Think() + if ModelInfo[1]!= self:GetClientInfo( "model" ) || ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + ModelInfo[1] = self:GetClientInfo( "model" ) + ModelInfo[2] = self:GetClientInfo( "modelsize" ) + ModelInfo[3] = ModelInfo[1] + if (ModelInfo[1] && ModelInfo[2] && ModelInfo[2]!="") then + local test = string.sub(ModelInfo[1], 1, -5) .. ModelInfo[2] .. string.sub(ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + ModelInfo[3] = test + end + end + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireOutput( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_output_name", Description = "#Tool_wire_output_desc" }) + + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_output_modelsize = "" }, + ["mini"] = { wire_output_modelsize = "_mini" }, + ["nano"] = { wire_output_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "Numpad", "wire_output", "#ToolWireIndicator_Model") + panel:AddControl("Numpad", { + Label = "#WireOutput_keygroup", + Command = "wire_output_keygroup", + ButtonSize = "22" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_plug.lua b/lua/weapons/gmod_tool/stools/wire_plug.lua new file mode 100644 index 0000000000..1efc311ead --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_plug.lua @@ -0,0 +1,280 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Plug" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_plug_name", "Plug Tool (Wire)" ) + language.Add( "Tool_wire_plug_desc", "Spawns plugs and sockets for use with the wire system." ) + language.Add( "Tool_wire_plug_0", "Primary: Create/Update Socket Secondary: Create/Update Plug" ) + language.Add( "WirePlugTool_colour", "Colour:" ) + language.Add( "sboxlimit_wire_plugs", "You've hit plugs limit!" ) + language.Add( "sboxlimit_wire_sockets", "You've hit sockets limit!" ) + language.Add( "undone_wireplug", "Undone Wire Plug" ) + language.Add( "undone_wiresocket", "Undone Wire Socket" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_plugs', 20) + CreateConVar('sbox_maxwire_sockets', 20) +end + +TOOL.ClientConVar[ "a" ] = "0" +TOOL.ClientConVar[ "ar" ] = "255" +TOOL.ClientConVar[ "ag" ] = "255" +TOOL.ClientConVar[ "ab" ] = "255" +TOOL.ClientConVar[ "aa" ] = "255" + +TOOL.PlugModel = "models/props_lab/tpplug.mdl" +TOOL.SocketModel = "models/props_lab/tpplugholder_single.mdl" + +cleanup.Register( "wire_plugs" ) + +// Create socket +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_plug") then + return false + end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local a = self:GetClientNumber("a") + local ar = math.min(self:GetClientNumber("ar"), 255) + local ag = math.min(self:GetClientNumber("ag"), 255) + local ab = math.min(self:GetClientNumber("ab"), 255) + local aa = math.min(self:GetClientNumber("aa"), 255) + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_socket" && trace.Entity.pl == ply ) then + trace.Entity:Setup(a,ar,ag,ab,aa) + + trace.Entity.a = a + trace.Entity.ar = ar + trace.Entity.ag = ag + trace.Entity.ab = ab + trace.Entity.aa = aa + trace.Entity.ReceivedValue = 0 + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_sockets" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + local Pos = trace.HitPos + Pos = Pos + self:Offset( trace.HitNormal:Angle(), Vector(-12, 13, 0) ) + + local wire_socket = MakeWireSocket( ply, Pos, Ang, self.SocketModel, a, ar, ag, ab, aa ) + + local const = WireLib.Weld(wire_socket, trace.Entity, trace.PhysicsBone, true, false, true) + + undo.Create("WireSocket") + undo.AddEntity( wire_socket ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_sockets", wire_socket ) + + return true +end + +// Create plug +function TOOL:RightClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local a = self:GetClientNumber("a") + local ar = math.min(self:GetClientNumber("ar"), 255) + local ag = math.min(self:GetClientNumber("ag"), 255) + local ab = math.min(self:GetClientNumber("ab"), 255) + local aa = math.min(self:GetClientNumber("aa"), 255) + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_plug" && trace.Entity.pl == ply ) then + trace.Entity:Setup(a,ar,ag,ab,aa) + + trace.Entity.a = a + trace.Entity.ar = ar + trace.Entity.ag = ag + trace.Entity.ab = ab + trace.Entity.aa = aa + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_plugs" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + + local wire_plug = MakeWirePlug( ply, trace.HitPos, Ang, self.PlugModel, a, ar, ag, ab, aa ) + + local min = wire_plug:OBBMins() + wire_plug:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + undo.Create("WirePlug") + undo.AddEntity( wire_plug ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_plugs", wire_plug ) + + return true +end + +if (SERVER) then + + function MakeWirePlug( pl, Pos, Ang, model, a, ar, ag, ab, aa ) + if ( !pl:CheckLimit( "wire_plugs" ) ) then return false end + + local wire_plug = ents.Create( "gmod_wire_plug" ) + if (!wire_plug:IsValid()) then return false end + + wire_plug:SetAngles( Ang ) + wire_plug:SetPos( Pos ) + wire_plug:SetModel( Model(model or "models/props_lab/tpplug.mdl") ) + wire_plug:Spawn() + + wire_plug:Setup( a, ar, ag, ab, aa) + wire_plug:SetPlayer( pl ) + + local ttable = { + a = a, + ar = ar, + ag = ag, + ab = ab, + aa = aa, + pl = pl, + MySocket = nil + } + table.Merge(wire_plug:GetTable(), ttable ) + + pl:AddCount( "wire_plug", wire_plug ) + + return wire_plug + end + + duplicator.RegisterEntityClass("gmod_wire_plug", MakeWirePlug, "Pos", "Ang", "Model", "a", "ar", "ag", "ab", "aa") + + + function MakeWireSocket( pl, Pos, Ang, model, a, ar, ag, ab, aa ) + if ( !pl:CheckLimit( "wire_sockets" ) ) then return false end + + local wire_socket = ents.Create( "gmod_wire_socket" ) + if (!wire_socket:IsValid()) then return false end + + wire_socket:SetAngles( Ang ) + wire_socket:SetPos( Pos ) + wire_socket:SetModel( Model(model or "models/props_lab/tpplugholder_single.mdl") ) + wire_socket:Spawn() + + wire_socket:Setup( a, ar, ag, ab, aa) + wire_socket:SetPlayer( pl ) + + local ttable = { + a = a, + ar = ar, + ag = ag, + ab = ab, + aa = aa, + pl = pl, + ReceivedValue = 0 + } + table.Merge(wire_socket:GetTable(), ttable ) + + pl:AddCount( "wire_socket", wire_socket ) + + return wire_socket + end + + duplicator.RegisterEntityClass("gmod_wire_socket", MakeWireSocket, "Pos", "Ang", "Model", "a", "ar", "ag", "ab", "aa") + +end + +function TOOL:UpdateGhostWireSocket( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_socket" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + + local Pos = trace.HitPos + + Pos = Pos + self:Offset( trace.HitNormal:Angle(), Vector(-12, 13, 0) ) + + ent:SetPos( Pos ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Offset( ang, offsetvec ) + local offset = offsetvec + local stackdir = ang:Up() + + offset = ang:Up() * offset.X + ang:Forward() * -1 * offset.Z + ang:Right() * offset.Y + + return stackdir * 2 + offset +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.SocketModel ) then + self:MakeGhostEntity( self.SocketModel, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireSocket( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_plug_name", Description = "#Tool_wire_plug_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_plug", + + Options = { + ["#Default"] = { + wire_plug_a = "0", + wire_plug_ar = "255", + wire_plug_ag = "0", + wire_plug_ab = "0", + wire_plug_aa = "255", + } + }, + + CVars = { + [0] = "wire_plug_a", + [1] = "wire_plug_ar", + [2] = "wire_plug_ag", + [3] = "wire_plug_ab", + [4] = "wire_plug_aa", + } + }) + + panel:AddControl("Color", { + Label = "#WirePlugTool_colour", + Red = "wire_plug_ar", + Green = "wire_plug_ag", + Blue = "wire_plug_ab", + Alpha = "wire_plug_aa", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_pod.lua b/lua/weapons/gmod_tool/stools/wire_pod.lua new file mode 100644 index 0000000000..df9c624008 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_pod.lua @@ -0,0 +1,179 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Pod Controller" +TOOL.Command = nil -- What is this for? +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if CLIENT then + language.Add("Tool_wire_pod_name", "Pod Controller Tool (Wire)") + language.Add("Tool_wire_pod_desc", "Spawn/link a Wire Pod controller.") + language.Add("Tool_wire_pod_0", "Primary: Create Pod controller. Secondary: Link controller.") + language.Add("Tool_wire_pod_1", "Now select the pod to link to.") + language.Add("WirePodTool_pod", "Pod:") + language.Add("WirePodTool_Keys", "Outputs:") + language.Add("sboxlimit_wire_pods", "You've hit your Pod Controller limit!") + language.Add("Undone_Wire Pod", "Undone Wire Pod Controller") +end + +if SERVER then + CreateConVar('sbox_maxwire_pods', 20) +end + +TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" +TOOL.ClientConVar["Keys"] = "W=0,1;A=0,1;S=0,1;D=0,1;" + +cleanup.Register("wire_pods") + +local keytable = {} +keytable["attack"] = IN_ATTACK +keytable["attack1"] = IN_ATTACK +keytable["mouse"] = IN_ATTACK +keytable["mouse1"] = IN_ATTACK +keytable["attack2"] = IN_ATTACK2 +keytable["mouse2"] = IN_ATTACK2 +keytable["forward"] = IN_FORWARD +keytable["w"] = IN_FORWARD +keytable["left"] = IN_MOVELEFT +keytable["a"] = IN_MOVELEFT +keytable["back"] = IN_BACK +keytable["s"] = IN_BACK +keytable["right"] = IN_MOVERIGHT +keytable["d"] = IN_MOVERIGHT +keytable["reload"] = IN_RELOAD +keytable["r"] = IN_RELOAD +keytable["jump"] = IN_JUMP +keytable["space"] = IN_JUMP +keytable["duck"] = IN_DUCK +keytable["ctrl"] = IN_DUCK +keytable["sprint"] = IN_SPEED +keytable["shift"] = IN_SPEED +keytable["zoom"] = IN_ZOOM + +local function ParseKeys(str) + local keys = {} + for key, off, on in string.gmatch(str, "(%a+)=(%d+),(%d+);") do + local l = key:lower() + if keytable[l] then + keys[key] = {keytable[l], tonumber(on), tonumber(off)} + end + end + return keys +end + +function TOOL:LeftClick(trace) + if not trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local ply = self:GetOwner() + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_pod" and trace.Entity:GetTable().pl == ply then + trace.Entity:SetKeys(ParseKeys(self:GetClientInfo("Keys"))) + return true + end + + if not self:GetSWEP():CheckLimit("wire_pods") then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_pod = MakeWirePod(ply, trace.HitPos, Ang, self.Model, ParseKeys(self:GetClientInfo("Keys"))) + + wire_pod:SetPos(trace.HitPos - trace.HitNormal * wire_pod:OBBMins().z) + + local const = WireLib.Weld(wire_pod, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Pod") + undo.AddEntity(wire_pod) + undo.AddEntity(const) + undo.SetPlayer(ply) + undo.Finish() + + ply:AddCleanup("wire_pods", wire_pod) + + return true +end + +function TOOL:RightClick(trace) + if (self:GetStage() == 0) and trace.Entity:GetClass() == "gmod_wire_pod" then + self.PodCont = trace.Entity + self:SetStage(1) + return true + elseif self:GetStage() == 1 and trace.Entity.GetPassenger then + self.PodCont:Setup(trace.Entity) + self:SetStage(0) + self.PodCont = nil + return true + else + return false + end +end + +function TOOL:Reload(trace) + self:SetStage(0) + self.PodCont = nil +end + +if SERVER then + + function MakeWirePod(pl, Pos, Ang, model, Keys) + if not pl:CheckLimit("wire_pods") then return false end + + local wire_pod = ents.Create("gmod_wire_pod") + if not wire_pod:IsValid() then return false end + + wire_pod:SetAngles(Ang) + wire_pod:SetPos(Pos) + wire_pod:Spawn() + wire_pod:SetPlayer(pl) + wire_pod.pl = pl + + pl:AddCount("wire_pods", wire_pod) + + if Keys then + wire_pod:SetKeys(Keys) + end + + return wire_pod + end + + duplicator.RegisterEntityClass("gmod_wire_pod", MakeWirePod, "Pos", "Ang", "Model", "Keys") +end + +function TOOL:UpdateGhostWirePod(ent, player) + if not ent or not ent:IsValid() then return end + + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if not trace.Hit or trace.Entity:IsPlayer() or trace.Entity:GetClass() == "gmod_wire_pod" then + ent:SetNoDraw(true) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + ent:SetPos(trace.HitPos - trace.HitNormal * ent:OBBMins().z) + ent:SetAngles(Ang) + + ent:SetNoDraw(false) +end + +function TOOL:Think() + if not self.GhostEntity or not self.GhostEntity:IsValid() or self.GhostEntity:GetModel() ~= self.Model then + self:MakeGhostEntity(self.Model, Vector(0,0,0), Angle(0,0,0)) + end + + self:UpdateGhostWirePod(self.GhostEntity, self:GetOwner()) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_pod_name", Description = "#Tool_wire_pod_desc" }) + + panel:AddControl("TextBox", { + Label = "#WirePodTool_Keys", + Command = "wire_pod_Keys", + Disabled = "true" -- Does this work? + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_radio.lua b/lua/weapons/gmod_tool/stools/wire_radio.lua new file mode 100644 index 0000000000..b1948fd62f --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_radio.lua @@ -0,0 +1,181 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Radio" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_radio_name", "Radio Tool (Wire)" ) + language.Add( "Tool_wire_radio_desc", "Spawns a radio for use with the wire system." ) + language.Add( "Tool_wire_radio_0", "Primary: Create/Update Radio" ) + language.Add( "WireRadioTool_channel", "Channel:" ) + language.Add( "WireRadioTool_model", "Model:" ); + language.Add( "WireRadioTool_values", "Values:" ); + language.Add( "WireRadioTool_secure", "Secure" ); + language.Add( "sboxlimit_wire_radios", "You've hit the radio limit!" ) + language.Add( "undone_wireradio", "Undone Wire Radio" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_radioes',30) + ModelPlug_Register("radio") +end + +TOOL.ClientConVar = { + channel = 1, + values = 4, + secure = 0, + model = "models/props_lab/binderblue.mdl" +} + +TOOL.Model = "models/props_lab/binderblue.mdl" + +cleanup.Register( "wire_radioes" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local _channel = self:GetClientInfo( "channel" ) + local model = self:GetClientInfo( "model" ) + local values = self:GetClientNumber("values") + local secure = (self:GetClientNumber("secure") ~= 0) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_radio" && trace.Entity.pl == ply ) then + trace.Entity:Setup( _channel,values,secure) + trace.Entity.channel = _channel + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_radioes" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_radio = MakeWireRadio( ply, trace.HitPos, Ang, model, _channel,values,secure) + + local min = wire_radio:OBBMins() + wire_radio:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_radio, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireRadio") + undo.AddEntity( wire_radio ) + undo.SetPlayer( ply ) + undo.AddEntity(const) + undo.Finish() + + ply:AddCleanup( "wire_radioes", wire_radio ) + + return true +end + +if SERVER then + + function MakeWireRadio(pl, Pos, Ang, model, channel, values, secure ) + if ( !pl:CheckLimit( "wire_radioes" ) ) then return nil end + + local wire_radio = ents.Create( "gmod_wire_radio" ) + wire_radio:SetPos( Pos ) + wire_radio:SetAngles( Ang ) + wire_radio:SetModel(model) + wire_radio:Spawn() + wire_radio:Activate() + + local ttable = { + channel = channel, + values = values, + secure = secure, + pl = pl, + nocollide = nocollide, + } + table.Merge( wire_radio:GetTable(), ttable ) + + wire_radio:Setup( channel ,values ,secure ) + wire_radio:SetPlayer( pl ) + + pl:AddCount( "wire_radioes", wire_radio ) + + return wire_radio + end + + duplicator.RegisterEntityClass("gmod_wire_radio", MakeWireRadio, "Pos", "Ang", "Model", "channel", "values", "secure") + +end + +function TOOL:UpdateGhostWireRadio( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local trace = player:GetEyeTrace() + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_radio" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + ent:SetAngles( Ang ) + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" )) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireRadio( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_radio_name", Description = "#Tool_wire_radio_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_radio", + + Options = { + Default = { + wire_radio_channel = "1", + } + }, + + CVars = { + [0] = "wire_radio_channel", + } + }) + + panel:AddControl("Slider", { + Label = "#WireRadioTool_channel", + Type = "Integer", + Min = "1", + Max = "30", + Command = "wire_radio_channel" + }) + + ModelPlug_AddToCPanel(panel, "radio", "wire_radio", "#WireRadioTool_model", nil, "#WireRadioTool_model") + + panel:AddControl("Slider", { + Label = "#WireRadioTool_values", + Type = "Integer", + Min = "1", + Max = "20", + Command = "wire_radio_values" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRadioTool_secure", + Command = "wire_radio_secure" + }) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_ranger.lua b/lua/weapons/gmod_tool/stools/wire_ranger.lua new file mode 100644 index 0000000000..9b83896bc0 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_ranger.lua @@ -0,0 +1,276 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "Ranger" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_ranger_name", "Ranger Tool (Wire)" ) + language.Add( "Tool_wire_ranger_desc", "Spawns a ranger for use with the wire system." ) + language.Add( "Tool_wire_ranger_0", "Primary: Create/Update Ranger" ) + language.Add( "WireRangerTool_range", "Range:" ) + language.Add( "WireRangerTool_default_zero", "Default to zero" ) + language.Add( "WireRangerTool_show_beam", "Show Beam" ) + language.Add( "WireRangerTool_ignore_world", "Ignore world" ) + language.Add( "WireRangerTool_trace_water", "Hit water" ) + language.Add( "WireRangerTool_out_dist", "Output Distance" ) + language.Add( "WireRangerTool_out_pos", "Output Position" ) + language.Add( "WireRangerTool_out_vel", "Output Velocity" ) + language.Add( "WireRangerTool_out_ang", "Output Angle" ) + language.Add( "WireRangerTool_out_col", "Output Color" ) + language.Add( "WireRangerTool_out_val", "Output Value" ) + language.Add( "WireRangerTool_out_sid", "Output SteamID(number)" ) + language.Add( "WireRangerTool_out_uid", "Output UniqueID" ) + language.Add( "WireRangerTool_out_eid", "Output Entity+EntID" ) + language.Add( "WireRangerTool_out_hnrm", "Output HitNormal" ) + language.Add( "WireRangerTool_hires", "High Resolution") + language.Add( "sboxlimit_wire_rangers", "You've hit rangers limit!" ) + language.Add( "undone_wireranger", "Undone Wire Ranger" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_rangers', 10) +end + +TOOL.ClientConVar[ "range" ] = "1500" +TOOL.ClientConVar[ "default_zero" ] = "1" +TOOL.ClientConVar[ "show_beam" ] = "0" +TOOL.ClientConVar[ "ignore_world" ] = "0" +TOOL.ClientConVar[ "trace_water" ] = "0" +TOOL.ClientConVar[ "out_dist" ] = "1" +TOOL.ClientConVar[ "out_pos" ] = "0" +TOOL.ClientConVar[ "out_vel" ] = "0" +TOOL.ClientConVar[ "out_ang" ] = "0" +TOOL.ClientConVar[ "out_col" ] = "0" +TOOL.ClientConVar[ "out_val" ] = "0" +TOOL.ClientConVar[ "out_sid" ] = "0" +TOOL.ClientConVar[ "out_uid" ] = "0" +TOOL.ClientConVar[ "out_eid" ] = "0" +TOOL.ClientConVar[ "out_hnrm" ] = "0" +TOOL.ClientConVar[ "hires" ] = "0" + +TOOL.Model = "models/jaanus/wiretool/wiretool_range.mdl" + +cleanup.Register( "wire_rangers" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + if (CLIENT) then return true end + + local ply = self:GetOwner() + + local range = self:GetClientNumber("range") + local default_zero = (self:GetClientNumber("default_zero") ~= 0) + local show_beam = (self:GetClientNumber("show_beam") ~= 0) + local ignore_world = (self:GetClientNumber("ignore_world") ~= 0) + local trace_water = (self:GetClientNumber("trace_water") ~= 0) + local out_dist = (self:GetClientNumber("out_dist") ~= 0) + local out_pos = (self:GetClientNumber("out_pos") ~= 0) + local out_vel = (self:GetClientNumber("out_vel") ~= 0) + local out_ang = (self:GetClientNumber("out_ang") ~= 0) + local out_col = (self:GetClientNumber("out_col") ~= 0) + local out_val = (self:GetClientNumber("out_val") ~= 0) + local out_sid = (self:GetClientNumber("out_sid") ~= 0) + local out_uid = (self:GetClientNumber("out_uid") ~= 0) + local out_eid = (self:GetClientNumber("out_eid") ~= 0) + local out_hnrm = (self:GetClientNumber("out_hnrm") ~= 0) + local hires = (self:GetClientNumber("hires") ~= 0) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_ranger" && trace.Entity.pl == ply ) then + trace.Entity:Setup( range, default_zero, show_beam, ignore_world, trace_water, out_dist, out_pos, out_vel, out_ang, out_col, out_val, out_sid, out_uid, out_eid,hires ) + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_rangers" ) ) then return false end + + if (not util.IsValidModel(self.Model)) then return false end + if (not util.IsValidProp(self.Model)) then return false end // Allow ragdolls to be used? + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_ranger = MakeWireRanger( ply, trace.HitPos, Ang, self.Model, range, default_zero, show_beam, ignore_world, trace_water, out_dist, out_pos, out_vel, out_ang, out_col, out_val, out_sid, out_uid, out_eid, out_hnrm ) + + local min = wire_ranger:OBBMins() + wire_ranger:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_ranger, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireRanger") + undo.AddEntity( wire_ranger ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_rangers", wire_ranger ) + + return true +end + +if (SERVER) then + + function MakeWireRanger( pl, Pos, Ang, model, range, default_zero, show_beam, ignore_world, trace_water, out_dist, out_pos, out_vel, out_ang, out_col, out_val, out_sid, out_uid, out_eid, out_hnrm, hires, nocollide ) + if ( !pl:CheckLimit( "wire_rangers" ) ) then return false end + + local wire_ranger = ents.Create( "gmod_wire_ranger" ) + if (!wire_ranger:IsValid()) then return false end + + wire_ranger:SetAngles( Ang ) + wire_ranger:SetPos( Pos ) + wire_ranger:SetModel( Model(model or "models/jaanus/wiretool/wiretool_range.mdl") ) + wire_ranger:Spawn() + + wire_ranger:Setup( range, default_zero, show_beam, ignore_world, trace_water, out_dist, out_pos, out_vel, out_ang, out_col, out_val, out_sid, out_uid, out_eid, out_hnrm, hires ) + wire_ranger:SetPlayer( pl ) + + if ( nocollide == true ) then wire_ranger:GetPhysicsObject():EnableCollisions( false ) end + + wire_ranger.pl = pl + wire_ranger.nocollide = nocollide + + pl:AddCount( "wire_rangers", wire_ranger ) + + return wire_ranger + end + + duplicator.RegisterEntityClass("gmod_wire_ranger", MakeWireRanger, "Pos", "Ang", "Model", "range", "default_zero", "show_beam", "ignore_world", "trace_water", "out_dist", "out_pos", "out_vel", "out_ang", "out_col", "out_val", "out_sid", "out_uid", "out_eid", "out_hnrm", "hires", "nocollide") + +end + +function TOOL:UpdateGhostWireRanger( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_ranger" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireRanger( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_ranger_name", Description = "#Tool_wire_ranger_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_ranger", + + Options = { + Default = { + wire_ranger_range = "20", + wire_ranger_default_zero = "0", + } + }, + + CVars = { + [0] = "wire_ranger_range", + [1] = "wire_ranger_default_zero" + } + }) + + panel:AddControl("Slider", { + Label = "#WireRangerTool_range", + Type = "Float", + Min = "1", + Max = "1000", + Command = "wire_ranger_range" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_default_zero", + Command = "wire_ranger_default_zero" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_show_beam", + Command = "wire_ranger_show_beam" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_ignore_world", + Command = "wire_ranger_ignore_world" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_trace_water", + Command = "wire_ranger_trace_water" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_dist", + Command = "wire_ranger_out_dist" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_pos", + Command = "wire_ranger_out_pos" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_vel", + Command = "wire_ranger_out_vel" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_ang", + Command = "wire_ranger_out_ang" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_col", + Command = "wire_ranger_out_col" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_val", + Command = "wire_ranger_out_val" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_sid", + Command = "wire_ranger_out_sid" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_uid", + Command = "wire_ranger_out_uid" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_eid", + Command = "wire_ranger_out_eid" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_out_hnrm", + Command = "wire_ranger_out_hnrm" + }) + + panel:AddControl("CheckBox", { + Label = "#WireRangerTool_hires", + Command = "wire_ranger_hires" + }) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_relay.lua b/lua/weapons/gmod_tool/stools/wire_relay.lua new file mode 100644 index 0000000000..4b34456614 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_relay.lua @@ -0,0 +1,291 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Relay" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_relay_name", "Relay" ) + language.Add( "Tool_wire_relay_desc", "Spawns a multi pole, multi throw relay switch." ) + language.Add( "Tool_wire_relay_0", "Primary: Create/Update Relay" ) + language.Add( "WireRelayTool_keygroup1", "Input 1 Key:" ) + language.Add( "WireRelayTool_keygroup2", "Input 2 Key:" ) + language.Add( "WireRelayTool_keygroup3", "Input 3 Key:" ) + language.Add( "WireRelayTool_keygroup4", "Input 4 Key:" ) + language.Add( "WireRelayTool_keygroup5", "Input 5 Key:" ) + language.Add( "WireRelayTool_keygroupoff", "Open (off) Key:" ) + language.Add( "WireRelayTool_nokey", "No Key switching" ) + language.Add( "WireRelayTool_toggle", "Toggle" ) + language.Add( "WireRelayTool_normclose", "Normaly:" ) + language.Add( "WireRelayTool_poles", "Number of poles:" ) + language.Add( "WireRelayTool_throws", "Number of throws:" ) + language.Add( "sboxlimit_wire_relays", "You've hit the wire relays limit!" ) + language.Add( "undone_wirerelay", "Undone Wire Relay" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_relays', 20) +end +TOOL.ClientConVar = { + keygroupoff = "0", + keygroup1 = "1", + keygroup2 = "2", + keygroup3 = "3", + keygroup4 = "4", + keygroup5 = "5", + nokey = "0", + toggle = "1", + normclose = "0", + poles = "1", + throws = "2", + model = "models/kobilica/relay.mdl", +} + +cleanup.Register( "wire_relays" ) + +function TOOL:LeftClick( trace ) + + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local _keygroup1 = self:GetClientNumber( "keygroup1" ) + local _keygroup2 = self:GetClientNumber( "keygroup2" ) + local _keygroup3 = self:GetClientNumber( "keygroup3" ) + local _keygroup4 = self:GetClientNumber( "keygroup4" ) + local _keygroup5 = self:GetClientNumber( "keygroup5" ) + local _keygroupoff = self:GetClientNumber( "keygroupoff" ) + local _nokey = self:GetClientNumber( "nokey" ) == 1 + local _toggle = self:GetClientNumber( "toggle" ) == 1 + local _normclose = self:GetClientNumber( "normclose" ) + local _value_off = self:GetClientNumber( "value_off" ) + local _poles = self:GetClientNumber( "poles" ) + local _throws = self:GetClientNumber( "throws" ) + local _model = self:GetClientInfo( "model" ) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_relay" && trace.Entity.pl == ply ) then + trace.Entity:Setup( _keygroup1, _keygroup2, _keygroup3, _keygroup4, _keygroup5, _keygroupoff, _toggle, _normclose, _poles, _throws, _nokey ) + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_relays" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_relay = MakeWireRelay( ply, trace.HitPos, Ang, _model, _keygroup1, _keygroup2, _keygroup3, _keygroup4, _keygroup5, _keygroupoff, _toggle, _normclose, _poles, _throws, _nokey ) + + local min = wire_relay:OBBMins() + wire_relay:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_relay, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireRelay") + undo.AddEntity( wire_relay ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_relays", wire_relay ) + + return true +end + +if (SERVER) then + + function MakeWireRelay( pl, Pos, Ang, model, keygroup1, keygroup2, keygroup3, keygroup4, keygroup5, keygroupoff, toggle, normclose, poles, throws, nokey) + print(model) + if ( !pl:CheckLimit( "wire_relays" ) ) then return false end + + local wire_relay = ents.Create( "gmod_wire_relay" ) + if (!wire_relay:IsValid()) then return false end + + wire_relay:SetAngles( Ang ) + wire_relay:SetPos( Pos ) + wire_relay:SetModel( Model(model) ) + wire_relay:Spawn() + + wire_relay:Setup( keygroup1, keygroup2, keygroup3, keygroup4, keygroup5, keygroupoff, toggle, normclose, poles, throws ) + wire_relay:SetPlayer( pl ) + + if (!nokey) then + if (keygroupoff) then + numpad.OnDown( pl, keygroupoff, "WireRelay_On", wire_relay, 0 ) + numpad.OnUp( pl, keygroupoff, "WireRelay_Off", wire_relay, 0 ) + end + if (keygroup1) then + numpad.OnDown( pl, keygroup1, "WireRelay_On", wire_relay, 1 ) + numpad.OnUp( pl, keygroup1, "WireRelay_Off", wire_relay, 1 ) + end + if (keygroup2) then + numpad.OnDown( pl, keygroup2, "WireRelay_On", wire_relay, 2 ) + numpad.OnUp( pl, keygroup2, "WireRelay_Off", wire_relay, 2 ) + end + if (keygroup3) then + numpad.OnDown( pl, keygroup3, "WireRelay_On", wire_relay, 3 ) + numpad.OnUp( pl, keygroup3, "WireRelay_Off", wire_relay, 3 ) + end + if (keygroup4) then + numpad.OnDown( pl, keygroup4, "WireRelay_On", wire_relay, 4 ) + numpad.OnUp( pl, keygroup4, "WireRelay_Off", wire_relay, 4 ) + end + if (keygroup5) then + numpad.OnDown( pl, keygroup5, "WireRelay_On", wire_relay, 5 ) + numpad.OnUp( pl, keygroup5, "WireRelay_Off", wire_relay, 5 ) + end + end + + local ttable = { + keygroup1 = keygroup1, + keygroup2 = keygroup2, + keygroup3 = keygroup3, + keygroup4 = keygroup4, + keygroup5 = keygroup5, + keygroupoff = keygroupoff, + toggle = toggle, + normclose = normclose, + poles = poles, + throws = throws, + nokey = nokey, + pl = pl + } + table.Merge(wire_relay, ttable ) + + pl:AddCount( "wire_relays", wire_relay ) + + return wire_relay + end + + duplicator.RegisterEntityClass("gmod_wire_relay", MakeWireRelay, "Pos", "Ang", "Model", "keygroup1", "keygroup2", "keygroup3", "keygroup4", "keygroup5", "keygroupoff", "toggle", "normclose", "poles", "throws", "nokey") + +end + +function TOOL:UpdateGhost( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local trace = player:GetEyeTrace() + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_relay" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + local model = self:GetClientInfo("model") + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != model ) then + self:MakeGhostEntity( model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhost( self.GhostEntity, self:GetOwner() ) + +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_relay_name", Description = "#Tool_wire_relay_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_relay", + + Options = { + Default = { + wire_relay_keygroup1 = "1", + wire_relay_keygroup2 = "2", + wire_relay_keygroup3 = "3", + wire_relay_keygroup4 = "4", + wire_relay_keygroup5 = "5", + wire_relay_keygroupoff = "0", + wire_relay_toggle = "0", + wire_relay_nromclose = "0", + wire_relay_poles = "1", + wire_relay_throws = "2" + } + }, + + CVars = { + [0] = "wire_relay_keygroup1", + [1] = "wire_relay_keygroup2", + [2] = "wire_relay_keygroup3", + [3] = "wire_relay_keygroup4", + [4] = "wire_relay_keygroup5", + [5] = "wire_relay_keygroupoff", + [6] = "wire_relay_toggle", + [7] = "wire_relay_close", + [8] = "wire_relay_poles", + [9] = "wire_relay_throws" + } + }) + + + + panel:AddControl("Slider", { + Label = "#WireRelayTool_poles", + Type = "Integer", + Min = "1", + Max = "8", + Command = "wire_relay_poles" + }) + + panel:AddControl("Slider", { + Label = "#WireRelayTool_throws", + Type = "Integer", + Min = "1", + Max = "10", + Command = "wire_relay_throws" + }) + + + panel:AddControl("CheckBox", { + Label = "#WireRelayTool_toggle", + Command = "wire_relay_toggle" + }) + + panel:AddControl("ComboBox", { + Label = "#WireRelayTool_normclose", + Options = { + ["Open"] = { wire_relay_normclose = "0" }, + ["Closed to 1"] = { wire_relay_normclose = "1" }, + ["Closed to 2"] = { wire_relay_normclose = "2" }, + ["Closed to 3"] = { wire_relay_normclose = "3" }, + ["Closed to 4"] = { wire_relay_normclose = "4" }, + ["Closed to 5"] = { wire_relay_normclose = "5" } + } + }) + + panel:AddControl("CheckBox", { + Label = "#WireRelayTool_nokey", + Command = "wire_relay_nokey" + }) + + panel:AddControl("Numpad", { + Label = "#WireRelayTool_keygroupoff", Label2 = "#WireRelayTool_keygroup1", + Command = "wire_relay_keygroupoff", Command2 = "wire_relay_keygroup1", + ButtonSize = "22" + }) + panel:AddControl("Numpad", { + Label = "#WireRelayTool_keygroup2", Label2 = "#WireRelayTool_keygroup3", + Command = "wire_relay_keygroup2", Command2 = "wire_relay_keygroup3", + ButtonSize = "22" + }) + panel:AddControl("Numpad", { + Label = "#WireRelayTool_keygroup4", Label2 = "#WireRelayTool_keygroup5", + Command = "wire_relay_keygroup4", Command2 = "wire_relay_keygroup5", + ButtonSize = "22" + }) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_sensor.lua b/lua/weapons/gmod_tool/stools/wire_sensor.lua new file mode 100644 index 0000000000..fbaa72532b --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_sensor.lua @@ -0,0 +1,244 @@ +TOOL.Category = "Wire - Beacon" +TOOL.Name = "Beacon Sensor" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_sensor_name", "Beacon Sensor Tool (Wire)" ) + language.Add( "Tool_wire_sensor_desc", "Returns distance and/or bearing to a beacon" ) + language.Add( "Tool_wire_sensor_0", "Primary: Create Sensor Secondary: Link Sensor" ) + language.Add( "Tool_wire_sensor_1", "Click on the beacon to link to." ) + language.Add( "WireSensorTool_xyz_mode", "Split X,Y,Z" ) + language.Add( "WireSensorTool_outdist", "Output distance" ) + language.Add( "WireSensorTool_outbrng", "Output bearing" ) + language.Add( "WireSensorTool_gpscord", "Output world position (gps cords)" ) + language.Add( "WireSensorTool_direction_vector", "Output direction Vector" ) + language.Add( "WireSensorTool_direction_normalized", "Normalize direction Vector" ) + language.Add( "WireSensorTool_target_velocity", "Output target's velocity" ) + language.Add( "WireSensorTool_velocity_normalized", "Normalize velocity" ) + language.Add( "WireSensorTool_vector_in", "Vector Input Instead" ) + --language.Add( "WireSensorTool_swapyz", "Swap Y and Z cords:" ) + language.Add( "sboxlimit_wire_sensors", "You've hit sensors limit!" ) + language.Add( "undone_wiresensor", "Undone Wire Sensor" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_sensors',30) +end + +TOOL.ClientConVar[ "xyz_mode" ] = "0" +TOOL.ClientConVar[ "outdist" ] = "1" +TOOL.ClientConVar[ "outbrng" ] = "0" +TOOL.ClientConVar[ "gpscord" ] = "0" +TOOL.ClientConVar[ "SwapYZ" ] = "0" +TOOL.ClientConVar[ "direction_vector" ] = "0" +TOOL.ClientConVar[ "direction_normalized" ] = "0" +TOOL.ClientConVar[ "target_velocity" ] = "0" +TOOL.ClientConVar[ "velocity_normalized" ] = "0" +TOOL.ClientConVar[ "vector_in" ] = "0" + +TOOL.Model = "models/props_lab/huladoll.mdl" + +TOOL.SelectingPeer = false +TOOL.FirstPeer = nil + +cleanup.Register( "wire_sensors" ) + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local xyz_mode = (self:GetClientNumber("xyz_mode") ~= 0) + local outdist = (self:GetClientNumber("outdist") ~= 0) + local outbrng = (self:GetClientNumber("outbrng") ~= 0) + local gpscord = (self:GetClientNumber("gpscord") ~= 0) + local swapyz = (self:GetClientNumber("SwapYZ") ~= 0) + local direction_vector = (self:GetClientNumber("direction_vector") ~= 0) + local direction_normalized = (self:GetClientNumber("direction_normalized") ~= 0) + local target_velocity = (self:GetClientNumber("target_velocity") ~= 0) + local velocity_normalized = (self:GetClientNumber("velocity_normalized") ~= 0) + local vector_in = (self:GetClientNumber("vector_in") ~= 0) + + if (self:GetStage() == 1) then + if ( trace.Entity:IsValid() && trace.Entity.GetBeaconPos ) then + self.Sensor:SetBeacon(trace.Entity) + self:SetStage(0) + return true + end + + return + end + + -- Update a beacon + if (trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_sensor" and trace.Entity.pl == ply ) then + trace.Entity.xyz_mode = xyz_mode + trace.Entity.outdist = outdist + trace.Entity.outbrng = outbrng + trace.Entity.gpscord = gpscord + trace.Entity.swapyz = swapyz + trace.Entity.direction_vector = direction_vector + trace.Entity.direction_normalized = direction_normalized + trace.Entity.target_velocity = target_velocity + trace.Entity.velocity_normalized = velocity_normalized + trace.Entity.vector_in = vector_in + trace.Entity:Setup(xyz_mode, outdist, outbrng, gpscord, swapyz, direction_vector, direction_normalized, target_velocity, velocity_normalized, vector_in) + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_sensors" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_sensor = MakeWireSensor( ply, trace.HitPos, Ang, self.Model, xyz_mode, outdist, outbrng, gpscord, swapyz, direction_vector, direction_normalized, target_velocity,velocity_normalized, vector_in ) + + local min = wire_sensor:OBBMins() + wire_sensor:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_sensor, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireSensor") + undo.AddEntity( wire_sensor ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_sensors", wire_sensor ) + + return true +end + +function TOOL:RightClick(trace) + if (self:GetStage() ~= 0) then return self:LeftClick(trace) end + + if (trace.Entity:IsValid()) and (trace.Entity:GetClass() == "gmod_wire_sensor") and (trace.Entity.pl == self:GetOwner()) then + self:SetStage(1) + self.Sensor = trace.Entity + return true + end +end + +if SERVER then + + function MakeWireSensor(pl, Pos, Ang, model, xyz_mode, outdist, outbrng, gpscord, swapyz, direction_vector, direction_normalized, target_velocity, velocity_normalized, vector_in) + if ( !pl:CheckLimit( "wire_sensors" ) ) then return nil end + + local wire_sensor = ents.Create( "gmod_wire_sensor" ) + wire_sensor:SetPos( Pos ) + wire_sensor:SetAngles( Ang ) + wire_sensor:SetModel( Model(model or "models/props_lab/huladoll.mdl") ) + wire_sensor:Spawn() + wire_sensor:Activate() + + wire_sensor:Setup( xyz_mode, outdist, outbrng, gpscord, swapyz, direction_vector, direction_normalized, target_velocity, velocity_normalized, vector_in ) + wire_sensor:SetPlayer( pl ) + + local ttable = { + xyz_mode = xyz_mode, + outdist = outdist, + outbrng = outbrng, + gpscord = gpscord, + swapyz = swapyz, + direction_vector = direction_vector, + direction_normalized = direction_normalized, + target_velocity = target_velocity, + velocity_normalized = velocity_normalized, + vector_in = vector_in, + pl = pl, + } + table.Merge( wire_sensor:GetTable(), ttable ) + + pl:AddCount( "wire_sensors", wire_sensor ) + + return wire_sensor + end + + duplicator.RegisterEntityClass("gmod_wire_sensor", MakeWireSensor, "Pos", "Ang", "Model", "xyz_mode", "outdist", "outbrng", "gpscord", "swapyz", "direction_vector", "direction_normalized", "target_velocity", "velocity_normalized", "vector_in") + +end + +function TOOL:UpdateGhostWireSensor( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_sensor" || trace.Entity:GetClass() == "gmod_wire_beacon" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireSensor( self.GhostEntity, self:GetOwner() ) + +end + +function TOOL.BuildCPanel( panel ) + panel:AddControl( "Header", { Text = "#Tool_wire_sensor_name", Description = "#Tool_wire_sensor_desc" } ) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_xyz_mode", + Command = "wire_sensor_xyz_mode" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_outdist", + Command = "wire_sensor_outdist" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_outbrng", + Command = "wire_sensor_outbrng" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_gpscord", + Command = "wire_sensor_gpscord" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_direction_vector", + Command = "wire_sensor_direction_vector" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_direction_normalized", + Command = "wire_sensor_direction_normalized" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_target_velocity", + Command = "wire_sensor_target_velocity" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_velocity_normalized", + Command = "wire_sensor_velocity_normalized" + }) + + panel:AddControl("CheckBox", { + Label = "#WireSensorTool_vector_in", + Command = "wire_sensor_vector_in" + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_spawner.lua b/lua/weapons/gmod_tool/stools/wire_spawner.lua new file mode 100644 index 0000000000..530784800f --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_spawner.lua @@ -0,0 +1,165 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Prop Spawner" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +TOOL.ClientConVar = { + delay = 0, + undo_delay = 0, +} + +if CLIENT then + language.Add( "Tool_wire_spawner_name", "Prop Spawner (Wire)" ) + language.Add( "Tool_wire_spawner_desc", "Spawns a prop at a pre-defined location" ) + language.Add( "Tool_wire_spawner_0", "Click a prop to turn it into a prop spawner." ) + language.Add( "Undone_gmod_wire_spawner", "Undone Wire Spawner" ) + language.Add( "Cleanup_gmod_wire_spawner", "Wire Spawners" ) + language.Add( "Cleaned_gmod_wire_spawner", "Cleaned up Wire Spawners" ) +end + +if SERVER then + CreateConVar("sbox_maxwire_spawners",10) +end + +cleanup.Register("gmod_wire_spawner") + +function TOOL:LeftClick(trace) + local ent = trace.Entity + if !ent or !ent:IsValid() then return false end + if ent:GetClass() != "prop_physics" && ent:GetClass() != "gmod_wire_spawner" then return false end + if CLIENT then return true end + + local pl = self:GetOwner() + local delay = self:GetClientNumber("delay", 0) + local undo_delay = self:GetClientNumber("undo_delay", 0) + + if ent:GetClass() == "gmod_wire_spawner" && ent:GetTable().pl == pl then + local spawner = ent + + // In multiplayer we clamp the delay to help prevent people being idiots + if !SinglePlayer() and delay < 0.1 then + delay = 0.1 + end + + spawner:Setup(delay, undo_delay) + return true + end + + if !self:GetSWEP():CheckLimit("wire_spawners") then return false end + + local phys = ent:GetPhysicsObject() + if !phys:IsValid() then return false end + + local model = ent:GetModel() + local frozen = not phys:IsMoveable() + local Pos = ent:GetPos() + local Ang = ent:GetAngles() + local mat = ent:GetMaterial() + local r,g,b,a = ent:GetColor() + local skin = ent:GetSkin() or 0 + + local wire_spawner = MakeWireSpawner( pl, Pos, Ang, model, delay, undo_delay, mat, r, g, b, a, skin, frozen ) + if !wire_spawner:IsValid() then return end + + ent:Remove() + + undo.Create("gmod_wire_spawner") + undo.AddEntity( wire_spawner ) + undo.SetPlayer( pl ) + undo.Finish() + + return true +end + +if SERVER then + + function MakeWireSpawner( pl, Pos, Ang, model, delay, undo_delay, mat, r, g, b, a, skin, frozen ) + + if !pl:CheckLimit("wire_spawners") then return nil end + + local spawner = ents.Create("gmod_wire_spawner") + if !spawner:IsValid() then return end + spawner:SetPos(Pos) + spawner:SetAngles(Ang) + spawner:SetModel(model) + spawner:SetRenderMode(3) + spawner:SetMaterial(mat or "") + spawner:SetSkin(skin or 0) + spawner:SetColor((r or 255),(g or 255),(b or 255),100) + spawner:Spawn() + + if spawner:GetPhysicsObject():IsValid() then + local Phys = spawner:GetPhysicsObject() + Phys:EnableMotion(!frozen) + end + + // In multiplayer we clamp the delay to help prevent people being idiots + if not SinglePlayer() and delay < 0.1 then + delay = 0.1 + end + + spawner:SetPlayer(pl) + spawner:Setup(delay, undo_delay) + + local tbl = { + pl = pl, + delay = delay, + undo_delay = undo_delay, + mat = mat, + skin = skin, + r = r, + g = g, + b = b, + a = a, + } + table.Merge(spawner:GetTable(), tbl) + + pl:AddCount("wire_spawners", spawner) + pl:AddCleanup("gmod_wire_spawner", spawner) + + return spawner + end + + duplicator.RegisterEntityClass("gmod_wire_spawner", MakeWireSpawner, "Pos", "Ang", "Model", "delay", "undo_delay", "mat", "r", "g", "b", "a", "skin", "frozen") + +end + +function TOOL.BuildCPanel( CPanel ) + + local params = { + Label = "#Presets", + MenuButton = 1, + Folder = "wire_spawner", + Options = { + default = { + wire_spawner_delay = 0, + wire_spawner_undo_delay = 0, + } + }, + CVars = { + "wire_spawner_delay", + "wire_spawner_undo_delay", + } + } + CPanel:AddControl( "ComboBox", params ) + + local params = { + Label = "#Spawn Delay", + Type = "Float", + Min = "0", + Max = "100", + Command = "wire_spawner_delay", + } + CPanel:AddControl( "Slider", params ) + + local params = { + Label = "#Automatic Undo Delay", + Type = "Float", + Min = "0", + Max = "100", + Command = "wire_spawner_undo_delay", + } + CPanel:AddControl( "Slider", params ) + +end diff --git a/lua/weapons/gmod_tool/stools/wire_target_finder.lua b/lua/weapons/gmod_tool/stools/wire_target_finder.lua new file mode 100644 index 0000000000..74c26f8ed7 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_target_finder.lua @@ -0,0 +1,503 @@ + +TOOL.Category = "Wire - Beacon" +TOOL.Name = "Target Finder" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_target_finder_name", "Target Finder Beacon Tool (Wire)" ) + language.Add( "Tool_wire_target_finder_desc", "Spawns a target finder beacon for use with the wire system." ) + language.Add( "Tool_wire_target_finder_0", "Primary: Create/Update Target Finder Beacon" ) + + language.Add( "WireTargetFinderTool_minrange", "Minimum Range:" ) + language.Add( "WireTargetFinderTool_maxrange", "Maximum Range:" ) + language.Add( "WireTargetFinderTool_maxtargets", "Maximum number of targets to track:" ) + language.Add( "WireTargetFinderTool_MaxBogeys", "Max number of bogeys (closest):" ) + language.Add( "WireTargetFinderTool_MaxBogeys_desc", "Set to 0 for all within range, this needs to be atleast as many as Max Targets." ) + language.Add( "WireTargetFinderTool_players", "Target players" ) + language.Add( "WireTargetFinderTool_notowner", "Do not target owner" ) + language.Add( "WireTargetFinderTool_notownersstuff", "Do not target owner's stuff" ) + language.Add( "WireTargetFinderTool_npcs", "Target NPCs" ) + language.Add( "WireTargetFinderTool_npcname", "NPC Filter:" ) + language.Add( "WireTargetFinderTool_beacons", "Target Locators" ) + language.Add( "WireTargetFinderTool_hoverballs", "Target Hoverballs" ) + language.Add( "WireTargetFinderTool_thrusters", "Target Thrusters" ) + language.Add( "WireTargetFinderTool_props", "Target Props" ) + language.Add( "WireTargetFinderTool_propmodel", "Prop Model Filter:" ) + language.Add( "WireTargetFinderTool_vehicles", "Target Vehicles" ) + language.Add( "WireTargetFinderTool_rpgs", "Target RPGs" ) + --language.Add( "WireTargetFinderTool_OutDistance", "Output Distance/Bearing/Elevation:" ) + language.Add( "WireTargetFinderTool_PaintTarget", "Paint Target" ) + language.Add( "WireTargetFinderTool_PaintTarget_desc", "Paints currently selected target(s)." ) + language.Add( "WireTargetFinderTool_casesen", "Case Sensitive" ) + language.Add( "WireTargetFinderTool_playername", "Name Filter:" ) + language.Add( "WireTargetFinderTool_entity", "Entity Name:" ) + language.Add( "WireTargetFinderTool_steamname", "SteamID Filter:" ) + language.Add( "WireTargetFinderTool_colorcheck", "Color Filter") + language.Add( "WireTargetFinderTool_colortarget", "Color Target/Skip") + language.Add( "WireTargetFinderTool_pcolR", "Red:") + language.Add( "WireTargetFinderTool_pcolG", "Green:") + language.Add( "WireTargetFinderTool_pcolB", "Blue:") + language.Add( "WireTargetFinderTool_pcolA", "Alpha:") + language.Add( "WireTargetFinderTool_checkbuddylist", "Check Propprotection Buddy List (EXPERIMENTAL!)" ) + language.Add( "WireTargetFinderTool_onbuddylist", "Target Only Buddys (EXPERIMENTAL!)" ) + + language.Add( "sboxlimit_wire_target_finders", "You've hit target finder beacons limit!" ) + language.Add( "undone_wiretargetfinder", "Undone Wire Target Finder Beacon" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_target_finders',30) + CreateConVar("wire_target_finders_maxtargets",10) + CreateConVar("wire_target_finders_maxbogeys",30) + ModelPlug_Register("TargetFinder") +end + +TOOL.ClientConVar[ "minrange" ] = "1" +TOOL.ClientConVar[ "maxrange" ] = "1000" +TOOL.ClientConVar[ "players" ] = "0" +TOOL.ClientConVar[ "npcs" ] = "1" +TOOL.ClientConVar[ "npcname" ] = "" +TOOL.ClientConVar[ "beacons" ] = "0" +TOOL.ClientConVar[ "hoverballs" ] = "0" +TOOL.ClientConVar[ "thrusters" ] = "0" +TOOL.ClientConVar[ "props" ] = "0" +TOOL.ClientConVar[ "propmodel" ] = "" +TOOL.ClientConVar[ "vehicles" ] = "0" +TOOL.ClientConVar[ "playername" ] = "" +TOOL.ClientConVar[ "steamname" ] = "" +TOOL.ClientConVar[ "colorcheck" ] = "0" +TOOL.ClientConVar[ "colortarget" ] = "0" +TOOL.ClientConVar[ "pcolR" ] = "255" +TOOL.ClientConVar[ "pcolG" ] = "255" +TOOL.ClientConVar[ "pcolB" ] = "255" +TOOL.ClientConVar[ "pcolA" ] = "255" +TOOL.ClientConVar[ "casesen" ] = "0" +TOOL.ClientConVar[ "rpgs" ] = "0" +TOOL.ClientConVar[ "painttarget" ] = "1" +TOOL.ClientConVar[ "maxtargets" ] = "1" +TOOL.ClientConVar[ "maxbogeys" ] = "1" +TOOL.ClientConVar[ "notargetowner" ] = "0" +TOOL.ClientConVar[ "notownersstuff" ] = "0" +TOOL.ClientConVar[ "entityfil" ] = "" +TOOL.ClientConVar[ "checkbuddylist" ] = "0" +TOOL.ClientConVar[ "onbuddylist" ] = "0" +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/targetfinder.mdl" +TOOL.ClientConVar[ "modelsize" ] = "" +local ModelInfo = {"","",""} + +cleanup.Register( "wire_target_finders" ) + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + // Get client's CVars + local minrange = self:GetClientNumber("minrange") + local range = self:GetClientNumber("maxrange") + local players = (self:GetClientNumber("players") ~= 0) + local npcs = (self:GetClientNumber("npcs") ~= 0) + local npcname = self:GetClientInfo("npcname") + local beacons = (self:GetClientNumber("beacons") ~= 0) + local hoverballs = (self:GetClientNumber("hoverballs") ~= 0) + local thrusters = (self:GetClientNumber("thrusters") ~= 0) + local props = (self:GetClientNumber("props") ~= 0) + local propmodel = self:GetClientInfo("propmodel") + local vehicles = (self:GetClientNumber("vehicles") ~= 0) + local playername = self:GetClientInfo("playername") + local steamname = self:GetClientInfo("steamname") + local colorcheck = (self:GetClientNumber("colorcheck") ~= 0) + local colortarget = (self:GetClientNumber("colortarget") ~= 0) + local pcolR = self:GetClientNumber("pcolR") + local pcolG = self:GetClientNumber("pcolG") + local pcolB = self:GetClientNumber("pcolB") + local pcolA = self:GetClientNumber("pcolA") + local casesen = (self:GetClientNumber("casesen") ~= 0) + local rpgs = (self:GetClientNumber("rpgs") ~= 0) + local painttarget = (self:GetClientNumber("painttarget") ~= 0) + local maxtargets = self:GetClientNumber("maxtargets") + local maxbogeys = self:GetClientNumber("maxbogeys") + local notargetowner = (self:GetClientNumber("notargetowner") != 0) + local notownersstuff = (self:GetClientNumber("notownersstuff") != 0) + local entity = self:GetClientInfo("entityfil") + local checkbuddylist = (self:GetClientNumber("checkbuddylist") != 0) + local onbuddylist = (self:GetClientNumber("onbuddylist") != 0) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_target_finder" && trace.Entity.pl == ply ) then + //trace.Entity:Setup(range, players, npcs, npcname, beacons, hoverballs, thrusters, rpgs, painttarget) + trace.Entity:Setup(range, players, npcs, npcname, beacons, hoverballs, thrusters, props, propmodel, vehicles, playername, casesen, rpgs, painttarget, minrange, maxtargets, maxbogeys, notargetowner, entity, notownersstuff, steamname, colorcheck, colortarget, pcolR, pcolG, pcolB, pcolA, checkbuddylist, onbuddylist) + + trace.Entity.range = range + trace.Entity.players = players + trace.Entity.npcs = npcs + trace.Entity.npcname = npcname + trace.Entity.beacons = beacons + trace.Entity.hoverballs = hoverballs + trace.Entity.thrusters = thrusters + trace.Entity.props = props + trace.Entity.propmodel = propmodel + trace.Entity.vehicles = vehicles + trace.Entity.playername = playername + trace.Entity.steamname = steamname + trace.Entity.colorcheck = colorcheck + trace.Entity.colortarget = colortarget + trace.Entity.pcolR = pcolR + trace.Entity.pcolG = pcolG + trace.Entity.pcolB = pcolB + trace.Entity.pcolA = pcolA + trace.Entity.casesen = casesen + trace.Entity.rpgs = rpgs + trace.Entity.painttarget = painttarget + trace.Entity.minrange = minrange + trace.Entity.maxtargets = maxtargets + trace.Entity.maxbogeys = maxbogeys + trace.Entity.notargetowner = notargetowner + trace.Entity.notownersstuff = notownersstuff + trace.Entity.checkbuddylist = checkbuddylist + trace.Entity.onbuddylist = onbuddylist + trace.Entity.entity = entity + + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_target_finders" ) ) then return false end + + if ( !util.IsValidModel( ModelInfo[3] ) ) then return false end + if ( !util.IsValidProp( ModelInfo[3] ) ) then return false end + + local Ang = trace.HitNormal:Angle() + if(ModelInfo[1]!="models/props_lab/powerbox02d.mdl")then + Ang.p = Ang.p + 90 + end + + local wire_target_finder = MakeWireTargetFinder( ply, trace.HitPos, Ang, ModelInfo[3], range, players, npcs, npcname, beacons, hoverballs, thrusters, props, propmodel, vehicles, playername, casesen, rpgs, painttarget, minrange, maxtargets, maxbogeys, notargetowner, entity, notownersstuff, steamname, colorcheck, colortarget, pcolR, pcolG, pcolB, pcolA, checkbuddylist, onbuddylist ) + + local min = wire_target_finder:OBBMins() + wire_target_finder:SetPos( trace.HitPos - trace.HitNormal*min.z ) + + local const = WireLib.Weld(wire_target_finder, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireTargetFinder") + undo.AddEntity( wire_target_finder ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_target_finders", wire_target_finder ) + + return true + +end + +function TOOL:Reload(trace) + if trace.Entity:IsValid() then + self:GetOwner():ConCommand("wire_target_finder_entityfil "..trace.Entity:GetClass().."\n") + else + self:GetOwner():ConCommand("wire_target_finder_entityfil \n") + end + return true +end + +function TOOL:RightClick(trace) + return self:LeftClick(trace) +end + + +if SERVER then + + function MakeWireTargetFinder(pl, Pos, Ang, model, range, players, npcs, npcname, beacons, hoverballs, thrusters, props, propmodel, vehicles, playername, casesen, rpgs, painttarget, minrange, maxtargets, maxbogeys, notargetowner, entity, notownersstuff, steamname, colorcheck, colortarget, pcolR, pcolG, pcolB, pcolA, checkbuddylist, onbuddylist) + if (!pl:CheckLimit("wire_target_finders")) then return end + + local wire_target_finder = ents.Create("gmod_wire_target_finder") + wire_target_finder:SetPos(Pos) + wire_target_finder:SetAngles(Ang) + wire_target_finder:SetModel( Model(model or "models/props_lab/powerbox02d.mdl") ) + wire_target_finder:Spawn() + wire_target_finder:Activate() + + wire_target_finder:Setup(range, players, npcs, npcname, beacons, hoverballs, thrusters, props, propmodel, vehicles, playername, casesen, rpgs, painttarget, minrange, maxtargets, maxbogeys, notargetowner, entity, notownersstuff, steamname, colorcheck, colortarget, pcolR, pcolG, pcolB, pcolA, checkbuddylist, onbuddylist) + wire_target_finder:SetPlayer(pl) + + local ttable = { + range = range, + players = players, + npcs = npcs, + npcname = npcname, + beacons = beacons, + hoverballs = hoverballs, + thrusters = thrusters, + props = props, + propmodel = propmodel, + vehicles = vehicles, + playername = playername, + steamname = steamname, + colorcheck = colorcheck, + colortarget = colortarget, + pcolR = pcolR, + pcolG = pcolG, + pcolB = pcolB, + pcolA = pcolA, + casesen = casesen, + rpgs = rpgs, + painttarget = painttarget, + pl = pl, + nocollide = nocollide, + description = description, + minrange = minrange, + maxtargets = maxtargets, + maxbogeys = maxbogeys, + notargetowner = notargetowner, + notownersstuff = notownersstuff, + checkbuddylist = checkbuddylist, + onbuddylist = onbuddylist, + entity = entity, + } + + table.Merge( wire_target_finder:GetTable(), ttable ) + + pl:AddCount( "wire_target_finders", wire_target_finder ) + + return wire_target_finder + end + + duplicator.RegisterEntityClass("gmod_wire_target_finder", MakeWireTargetFinder, "Pos", "Ang", "Model", "range", "players", "npcs", "npcname", "beacons", "hoverballs", "thrusters", "props", "propmodel", "vehicles", "playername", "casesen", "rpgs", "painttarget", "minrange", "maxtargets", "maxbogeys", "notargetowner", "entity", "notownersstuff", "steamname", "colorcheck", "colortarget", "pcolR", "pcolG", "pcolB", "pcolA", "checkbuddylist", "onbuddylist") + +end + +function TOOL:UpdateGhostWireTargetFinder( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_target_finder" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + if(ModelInfo[1]!="models/props_lab/powerbox02d.mdl")then + Ang.p = Ang.p + 90 + end + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if ModelInfo[1]!= self:GetClientInfo( "model" ) || ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + ModelInfo[1] = self:GetClientInfo( "model" ) + ModelInfo[2] = self:GetClientInfo( "modelsize" ) + ModelInfo[3] = ModelInfo[1] + if (ModelInfo[1] && ModelInfo[2] && ModelInfo[2]!="") then + local test = string.sub(ModelInfo[1], 1, -5) .. ModelInfo[2] .. string.sub(ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + ModelInfo[3] = test + end + end + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireTargetFinder( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_target_finder_name", Description = "#Tool_wire_target_finder_desc" }) + + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_target_finder_modelsize = "" }, + ["mini"] = { wire_target_finder_modelsize = "_mini" }, + ["nano"] = { wire_target_finder_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "TargetFinder", "wire_target_finder", "#ToolWireIndicator_Model") + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_minrange", + Type = "Float", + Min = "1", + Max = "1000", + Command = "wire_target_finder_minrange" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_maxrange", + Type = "Float", + Min = "1", + Max = "1000", + Command = "wire_target_finder_maxrange" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_maxtargets", + Type = "Integer", + Min = "1", + Max = "10", + Command = "wire_target_finder_maxtargets" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_MaxBogeys", + Description = "#WireTargetFinderTool_MaxBogeys_desc", + Type = "Integer", + Min = "0", + Max = "30", + Command = "wire_target_finder_maxbogeys" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_players", + Command = "wire_target_finder_players" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_notowner", + Command = "wire_target_finder_notargetowner" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_notownersstuff", + Command = "wire_target_finder_notownersstuff" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_npcs", + Command = "wire_target_finder_npcs" + }) + + panel:AddControl("TextBox", { + Label = "#WireTargetFinderTool_npcname", + Command = "wire_target_finder_npcname", + MaxLength = "20" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_beacons", + Command = "wire_target_finder_beacons" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_hoverballs", + Command = "wire_target_finder_hoverballs" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_thrusters", + Command = "wire_target_finder_thrusters" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_props", + Command = "wire_target_finder_props" + }) + + panel:AddControl("TextBox", { + Label = "#WireTargetFinderTool_propmodel", + Command = "wire_target_finder_propmodel", + MaxLength = "100" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_vehicles", + Command = "wire_target_finder_vehicles" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_rpgs", + Command = "wire_target_finder_rpgs" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_PaintTarget", + Description = "#WireTargetFinderTool_PaintTarget_desc", + Command = "wire_target_finder_painttarget" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_casesen", + Command = "wire_target_finder_casesen" + }) + + panel:AddControl("TextBox", { + Label = "#WireTargetFinderTool_playername", + Command = "wire_target_finder_playername", + MaxLength = "50" + }) + + panel:AddControl("TextBox", { + Label = "#WireTargetFinderTool_entity", + Command = "wire_target_finder_entityfil", + MaxLength = "50" + }) + + panel:AddControl("TextBox", { + Label = "#WireTargetFinderTool_steamname", + Command = "wire_target_finder_steamname", + MaxLength = "50" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_colorcheck", + Command = "wire_target_finder_colorcheck" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_colortarget", + Command = "wire_target_finder_colortarget" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_pcolR", + Type = "Integer", + Min = "0", + Max = "255", + Command = "wire_target_finder_pcolR" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_pcolG", + Type = "Integer", + Min = "0", + Max = "255", + Command = "wire_target_finder_pcolG" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_pcolB", + Type = "Integer", + Min = "0", + Max = "255", + Command = "wire_target_finder_pcolB" + }) + + panel:AddControl("Slider", { + Label = "#WireTargetFinderTool_pcolA", + Type = "Integer", + Min = "0", + Max = "255", + Command = "wire_target_finder_pcolA" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_checkbuddylist", + Command = "wire_target_finder_checkbuddylist" + }) + + panel:AddControl("CheckBox", { + Label = "#WireTargetFinderTool_onbuddylist", + Command = "wire_target_finder_onbuddylist" + }) + +end + diff --git a/lua/weapons/gmod_tool/stools/wire_textreceiver.lua b/lua/weapons/gmod_tool/stools/wire_textreceiver.lua new file mode 100644 index 0000000000..00d6692cda --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_textreceiver.lua @@ -0,0 +1,321 @@ + +TOOL.Category = "Wire - I/O" +TOOL.Name = "TextReceiver" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_textreceiver_name", "TextReceiver Tool (Wire)" ) + language.Add( "Tool_wire_textreceiver_desc", "Spawns a TextReceiver for use with the wire system." ) + language.Add( "Tool_wire_textreceiver_0", "Primary: Create/Update TextReceiver Secondary: Copy Settings" ) + language.Add( "WiretextreceiverTool_global", "Global" ) + language.Add("Tool_wire_textreceiver_text1", "Text 1:") + language.Add("Tool_wire_textreceiver_text2", "Text 2:") + language.Add("Tool_wire_textreceiver_text3", "Text 3:") + language.Add("Tool_wire_textreceiver_text4", "Text 4:") + language.Add("Tool_wire_textreceiver_text5", "Text 5:") + language.Add("Tool_wire_textreceiver_text6", "Text 6:") + language.Add("Tool_wire_textreceiver_text7", "Text 7:") + language.Add("Tool_wire_textreceiver_text8", "Text 8:") + language.Add("Tool_wire_textreceiver_text9", "Text 9:") + language.Add("Tool_wire_textreceiver_text10", "Text 10:") + language.Add("Tool_wire_textreceiver_text11", "Text 12:") + language.Add("Tool_wire_textreceiver_text12", "Text 12:") + language.Add("WiretextreceiverTool_trigger","Trigger:") + language.Add("Tool_wire_textreceiver_parsetext","Parser Text:") + language.Add("WiretextreceiverTool_utrigger","Default:") + language.Add("WiretextreceiverTool_hold","Trigger Hold Length:") + language.Add("WiretextreceiverTool_outputtext", "Display Output Text") + language.Add("WiretextreceiverTool_SELF", "Include Self") + language.Add("WiretextreceiverTool_toggle", "Toggle") + language.Add("WiretextreceiverTool_sensitivity", "Sensitivity:") + language.Add("exact","Exact") + language.Add("case_insensitive","Case Insensitive") + language.Add("anywhere_exact","Anywhere Exact") + language.Add("anywhere_case_insensitive","Anywhere Case Insensitive") + language.Add( "sboxlimit_wire_textreceivers", "You've hit TextReceiver limit!" ) + language.Add( "Undone_TextReceiver", "Undone Wire TextReceiver" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_textreceivers', 10) +end + +TOOL.ClientConVar[ "global" ] = "0" +TOOL.ClientConVar[ "hold" ] = ".1" +TOOL.ClientConVar[ "trigger" ] = "1" +TOOL.ClientConVar[ "secure" ] = "0" +for i = 1, 12 do + TOOL.ClientConVar["text"..i] = "" +end +TOOL.ClientConVar[ "outputtext" ] = "1" +TOOL.ClientConVar["SELF"] = "1" +TOOL.ClientConVar["sensitivity"] = "1" +TOOL.ClientConVar["toggle"] = "0" +TOOL.ClientConVar["utrigger"] = "0" +TOOL.ClientConVar["parsetext"] = "" +TOOL.ClientConVar["playerout"] = "0" + +TOOL.Model = "models/jaanus/wiretool/wiretool_range.mdl" + +local MaxTextLength = 500 + +cleanup.Register( "wire_textreceivers" ) + +function TOOL:LeftClick( trace ) + if trace.Entity && trace.Entity:IsPlayer() then return false end + + // If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + if (CLIENT) then return true end + + local ply = self:GetOwner() + + local global = (self:GetClientNumber("global") ~= 0) + local hold = self:GetClientNumber("hold") + local trigger = self:GetClientNumber("trigger") + local lines = {} + for i = 1,12 do + if (self:GetClientInfo("text"..i) != "") then + table.insert(lines,self:GetClientInfo("text"..i)) + end + end + local outputtext = (self:GetClientNumber("outputtext") ~= 0) + local SELF = (self:GetClientNumber("SELF") ~= 0) + local sensitivity = self:GetClientNumber("sensitivity") + local toggle = (self:GetClientNumber("toggle") ~= 0) + local secure = (self:GetClientNumber("secure") ~= 0) + local utrigger = self:GetClientNumber("utrigger") + local parsetext = self:GetClientInfo("parsetext") + local playerout = (self:GetClientNumber("playerout") ~= 0) + + if (parsetext == "") then parsetext = '""' end + + if (string.len(parsetext) == 1) then parsetext = parsetext .. parsetext end + + if (string.len(parsetext) > 2) then WireLib.AddNotify(self:GetOwner(), "Parse text cannot be more than 2 characters!", NOTIFY_GENERIC, 7) return false end + + if (table.Count(lines)==0) then return false end + + if (sensitivity <1 || sensitivity > 4) then WireLib.AddNotify(self:GetOwner(), "Invalid Sensitivity!", NOTIFY_GENERIC, 7) return false end + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_textreceiver" && trace.Entity.pl == ply ) then + + trace.Entity:Setup( lines,global,outputtext,hold,trigger,SELF,sensitivity,toggle,utrigger,parsetext,secure,playerout) + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_textreceivers" ) ) then return false end + + if (not util.IsValidModel(self.Model)) then return false end + if (not util.IsValidProp(self.Model)) then return false end // Allow ragdolls to be used? + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + text_receiver = MakeWireReceiver( ply, trace.HitPos, Ang, self.Model, lines, global, outputtext, hold, trigger, SELF, sensitivity, toggle, utrigger, parsetext, secure, playerout) + + local min = text_receiver:OBBMins() + text_receiver:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(text_receiver, trace.Entity, trace.PhysicsBone, true) + + undo.Create("TextReceiver") + undo.AddEntity( text_receiver ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "text_receivers", text_receiver ) + ply:AddCleanup( "text_receivers", const ) + ply:AddCleanup( "text_receivers", nocollide ) + + return true +end + +local function BtoI(bool) + if (bool == nil) then return 0 end + if (bool == true) then return 1 end + if (bool == false) then return 0 end +end + +function TOOL:RightClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_textreceiver" ) then + local Receiver = trace.Entity + if (Receiver.CLines == nil) then WireLib.AddNotify(self:GetOwner(), "No Lines", NOTIFY_GENERIC, 7) return true end + for i = 1,12 do + self:GetOwner():ConCommand("wire_textreceiver_text"..i.." "..(Receiver.CLines[i] or '""')) + end + self:GetOwner():ConCommand("wire_textreceiver_sensitivity "..(Receiver.Sensitivity or 1)) + self:GetOwner():ConCommand("wire_textreceiver_SELF "..BtoI(Receiver.Iself)) + self:GetOwner():ConCommand("wire_textreceiver_toggle "..BtoI(Receiver.Toggle)) + self:GetOwner():ConCommand("wire_textreceiver_outputtext "..BtoI(Receiver.OutputText)) + self:GetOwner():ConCommand("wire_textreceiver_global "..BtoI(Receiver.global)) + self:GetOwner():ConCommand("wire_textreceiver_secure "..BtoI(Receiver.secure)) + self:GetOwner():ConCommand("wire_textreceiver_parsetext "..'"' .. (Receiver.char1 or "") .. (Receiver.char2 or "") .. '"') + self:GetOwner():ConCommand("wire_textreceiver_hold "..(Receiver.Hold or 0.1)) + self:GetOwner():ConCommand("wire_textreceiver_trigger "..(Receiver.Trig or 1)) + self:GetOwner():ConCommand("wire_textreceiver_utrigger "..(Receiver.UTrig or 0)) + self:GetOwner():ConCommand("wire_textreceiver_secure "..(Receiver.secure or 0)) + self:GetOwner():ConCommand("wire_textreceiver_playerout "..(Receiver.playerout or 0)) + return true + end + return false +end + +if (SERVER) then + + function MakeWireReceiver( pl, Pos, Ang, model, liness, globall, outputtextt, holdd, triggerr, SELFF, sensitivityy, togglee, utriggerr, parsetextt, secure, playerout) + if ( !pl:CheckLimit( "wire_textreceivers" ) ) then return false end + + local text_receiver = ents.Create( "gmod_wire_textreceiver" ) + if (!text_receiver:IsValid()) then return false end + text_receiver:SetAngles( Ang ) + text_receiver:SetPos( Pos ) + text_receiver:SetModel( Model(model or "models/jaanus/wiretool/wiretool_range.mdl") ) + text_receiver:Spawn() + + text_receiver:Setup(liness, globall,outputtextt,holdd,triggerr,SELFF,sensitivityy,togglee,utriggerr,parsetextt,secure,playerout) + text_receiver:SetPlayer( pl ) + + local ttable = { + pl = pl, + liness = liness, + globall = globall, + outputtextt = outputtextt, + holdd = holdd, + triggerr = triggerr, + SELFF = SELFF, + sensitivityy = sensitivityy, + togglee = togglee, + utriggerr = utriggerr, + parsetextt = parsetextt, + } + + table.Merge(text_receiver:GetTable(), ttable ) + + pl:AddCount( "wire_textreceivers", text_receiver ) + + return text_receiver + end + + duplicator.RegisterEntityClass("gmod_wire_textreceiver", MakeWireReceiver, "Pos", "Ang", "Model", "liness", "globall", "outputtextt", "holdd", "triggerr", "SELFF", "sensitivityy", "togglee", "utriggerr", "parsetextt", "secure", "playerout") + +end + +function TOOL:UpdateGhostWireTextReceiver( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_textreceiver" || trace.Entity:IsPlayer()) then + + ent:SetNoDraw( true ) + return + + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireTextReceiver( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_textreceiver_name", Description = "#Tool_wire_textreceiver_desc" }) + + panel:AddControl("Slider", { + Label = "#WiretextreceiverTool_utrigger", + Type = "Float", + Min = "0", + Max = "10", + Command = "wire_textreceiver_utrigger" + }) + + panel:AddControl("Slider", { + Label = "#WiretextreceiverTool_trigger", + Type = "Float", + Min = "1", + Max = "10", + Command = "wire_textreceiver_trigger" + }) + + panel:AddControl("Slider", { + Label = "#WiretextreceiverTool_hold", + Type = "Float", + Min = "0.1", + Max = "10", + Command = "wire_textreceiver_hold" + }) + + panel:AddControl("TextBox", {Label = "#Tool_wire_textreceiver_parsetext", MaxLength = tostring(2), Command = "wire_textreceiver_parsetext"}) + + panel:AddControl("CheckBox", { + Label = "#WiretextreceiverTool_global", + Command = "wire_textreceiver_global" + }) + + panel:AddControl("CheckBox", { + Label = "#WiretextreceiverTool_toggle", + Command = "wire_textreceiver_toggle" + }) + + panel:AddControl("CheckBox", { + Label = "#WiretextreceiverTool_outputtext", + Command = "wire_textreceiver_outputtext" + }) + + panel:AddControl("CheckBox", { + Label = "#WiretextreceiverTool_SELF", + Command = "wire_textreceiver_SELF" + }) + + panel:AddControl("CheckBox", { + Label = "Secure Args", + Command = "wire_textreceiver_secure" + }) + + panel:AddControl("CheckBox", { + Label = "Player Outputs", + Command = "wire_textreceiver_playerout" + }) + + panel:AddControl("ComboBox", { + Label = "#WiretextreceiverTool_sensitivity", + MenuButton = "0", + Options = { + ["#exact"] = { wire_textreceiver_sensitivity = "1" }, + ["#case_insensitive"] = { wire_textreceiver_sensitivity = "2" }, + ["#anywhere_exact"] = { wire_textreceiver_sensitivity = "3" }, + ["#anywhere_case_insensitive"] = { wire_textreceiver_sensitivity = "4" }, + } + }) + for i = 1,12 do + panel:AddControl("TextBox", {Label = "#Tool_wire_textreceiver_text"..i, MaxLength = tostring(MaxTextLength), Command = "wire_textreceiver_text"..i}) + end + +end diff --git a/lua/weapons/gmod_tool/stools/wire_twoway_radio.lua b/lua/weapons/gmod_tool/stools/wire_twoway_radio.lua new file mode 100644 index 0000000000..bdb1cb9d52 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_twoway_radio.lua @@ -0,0 +1,172 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Two-way Radio" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_twoway_radio_name", "Two-Way Radio Tool (Wire)" ) + language.Add( "Tool_wire_twoway_radio_desc", "Spawns a two-way radio for use with the wire system." ) + language.Add( "Tool_wire_twoway_radio_0", "Primary: Create/Update Two-way Radio\nSecondary: Select a two-way radio to pair up with another two-way radio." ) + language.Add( "Tool_wire_twoway_radio_1", "Select the second two-way radio." ); + language.Add( "WireRadioTwoWayTool_model", "Model:" ); + language.Add( "sboxlimit_wire_twoway_radios", "You've hit the two-way radio limit!" ) + language.Add( "undone_wiretwowayradio", "Undone Wire Two-way Radio" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_twoway_radioes',30) + ModelPlug_Register("radio") +end + +TOOL.ClientConVar[ "model" ] = "models/props_lab/bindergreen.mdl" + +TOOL.FirstPeer = nil + +cleanup.Register( "wire_twoway_radioes" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + local model = self:GetClientInfo( "model" ) + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_twoway_radio" && trace.Entity.pl == ply ) then + if (self.FirstPeer) and (self.FirstPeer:IsValid()) then + local first = self.FirstPeer + local second = trace.Entity + + -- Set the two entities to point to each other. + local id = Radio_GetTwoWayID() + first:RadioLink(second, id) + second:RadioLink(first, id) + + WireLib.AddNotify(self:GetOwner(), "Radios paired up. Pair ID is " .. tostring(id) .. ".", NOTIFY_GENERIC, 7) + + self.FirstPeer = nil + + return true + else + trace.Entity:Setup( _channel ) + return true + end + else + if self.FirstPeer then + self.FirstPeer = nil + return + end + end + + if ( !self:GetSWEP():CheckLimit( "wire_twoway_radioes" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_twoway_radio = MakeWireTwoWay_Radio( ply, trace.HitPos, Ang, model ) + + local min = wire_twoway_radio:OBBMins() + wire_twoway_radio:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_twoway_radio, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireTwoWay_Radio") + undo.AddEntity( wire_twoway_radio ) + undo.SetPlayer( ply ) + undo.AddEntity( const ) + undo.Finish() + + ply:AddCleanup( "wire_twoway_radioes", wire_twoway_radio ) + + return true + +end + +function TOOL:RightClick( trace ) + if (self.FirstPeer) then return self:LeftClick( trace ) end + + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_twoway_radio" && trace.Entity.pl == ply ) then + self.FirstPeer = trace.Entity + + return true + end +end + +if SERVER then + + // Having PeerID and Other in the duplicator was making it error out + // by trying to reference a two-way radio that didn't exist yet + // Build/ApplyDupeInfo now handle this (TheApathetic) + function MakeWireTwoWay_Radio(pl, Pos, Ang, model) + if ( !pl:CheckLimit( "wire_twoway_radioes" ) ) then return nil end + + local wire_twoway_radio = ents.Create( "gmod_wire_twoway_radio" ) + wire_twoway_radio:SetPos( Pos ) + wire_twoway_radio:SetAngles( Ang ) + wire_twoway_radio:SetModel(model) + wire_twoway_radio:Spawn() + wire_twoway_radio:Activate() + + wire_twoway_radio:Setup( channel ) + wire_twoway_radio:SetPlayer( pl ) + + local ttable = { + pl = pl, + nocollide = nocollide, + description = description + } + + table.Merge( wire_twoway_radio:GetTable(), ttable ) + + pl:AddCount( "wire_twoway_radioes", wire_twoway_radio ) + + return wire_twoway_radio + end + + duplicator.RegisterEntityClass("gmod_wire_twoway_radio", MakeWireTwoWay_Radio, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireTwoWay_Radio( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_twoway_radio" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + ent:SetAngles( Ang ) + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" )) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireTwoWay_Radio( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_twoway_radio_name", Description = "#Tool_wire_twoway_radio_desc" }) + + ModelPlug_AddToCPanel(panel, "radio2", "wire_twoway_radio", "#WireRadioTwoWayTool_model", nil, "#WireRadioTwoWayTool_model") +end diff --git a/lua/weapons/gmod_tool/stools/wire_user.lua b/lua/weapons/gmod_tool/stools/wire_user.lua new file mode 100644 index 0000000000..aefaf68ee6 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_user.lua @@ -0,0 +1,152 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "User" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_user_name", "User Tool (Wire)" ) + language.Add( "Tool_wire_user_desc", "Spawns a constant user prop for use with the wire system." ) + language.Add( "Tool_wire_user_0", "Primary: Create/Update User" ) + language.Add( "WireUserTool_user", "User:" ) + language.Add( "WireUserTool_Range", "Max Range:" ) + language.Add( "WireUserTool_Model", "Choose a Model:") + language.Add( "sboxlimit_wire_users", "You've hit Users limit!" ) + language.Add( "undone_Wire User", "Undone Wire User" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_users', 20) +end + +--this should use the model pack system instead +local usermodels = { + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_beamcaster.mdl"] = {} +} + +TOOL.ClientConVar[ "Model" ] = "models/jaanus/wiretool/wiretool_siren.mdl" +TOOL.ClientConVar[ "Range" ] = "200" + +cleanup.Register( "wire_users" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local range = self:GetClientNumber("Range") + local model = self:GetClientInfo("Model") + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_user" && trace.Entity:GetTable().pl == ply ) then + trace.Entity:Setup(range) + trace.Entity.Range = range + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_users" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_user = MakeWireUser( ply, trace.HitPos, Ang, model, range ) + + local min = wire_user:OBBMins() + wire_user:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_user, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire User") + undo.AddEntity( wire_user ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_users", wire_user ) + + return true +end + +function TOOL:RightClick( trace ) + return false +end + +if (SERVER) then + + function MakeWireUser( pl, Pos, Ang, model, Range ) + if ( !pl:CheckLimit( "wire_users" ) ) then return false end + + local wire_user = ents.Create( "gmod_wire_user" ) + if (!wire_user:IsValid()) then return false end + + wire_user:SetAngles( Ang ) + wire_user:SetPos( Pos ) + wire_user:SetModel( Model(model) ) + wire_user:Spawn() + wire_user:Setup(Range) + wire_user:SetPlayer( pl ) + + local ttable = { + Range = Range, + pl = pl + } + table.Merge(wire_user:GetTable(), ttable ) + + pl:AddCount( "wire_users", wire_user ) + + return wire_user + end + + duplicator.RegisterEntityClass("gmod_wire_user", MakeWireUser, "Pos", "Ang", "Model", "Range") + +end + +function TOOL:UpdateGhostWireUser( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_user" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo("Model") ) then + self:MakeGhostEntity( self:GetClientInfo("Model"), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireUser( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_user_name", Description = "#Tool_wire_user_desc" }) + + panel:AddControl( "PropSelect", { Label = "#WireUserTool_Model", + ConVar = "wire_user_Model", + Category = "Wire Users", + Models = usermodels } ) + + panel:AddControl("Slider", { + Label = "#WireUserTool_Range", + Type = "Float", + Min = "1", + Max = "1000", + Command = "wire_user_Range" + }) + +end + diff --git a/lua/weapons/gmod_tool/stools/wire_value.lua b/lua/weapons/gmod_tool/stools/wire_value.lua new file mode 100644 index 0000000000..d429cd18b3 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_value.lua @@ -0,0 +1,214 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Constant Value" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_value_name", "Value Tool (Wire)" ) + language.Add( "Tool_wire_value_desc", "Spawns a constant value prop for use with the wire system." ) + language.Add( "Tool_wire_value_0", "Primary: Create/Update Value Secondary: Copy Settings" ) + language.Add( "WireValueTool_value", "Value:" ) + language.Add( "WireValueTool_model", "Model:" ) + language.Add( "WireValueTool_desc", "In addition to specifying numbers, you can also specify strings, all kinds of vectors and angles. Write \"type:value\" to use this feature." ) + language.Add( "sboxlimit_wire_values", "You've hit values limit!" ) + language.Add( "undone_wirevalue", "Undone Wire Value" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_values', 20) + ModelPlug_Register("value") +end + +TOOL.ClientConVar[ "model" ] = "models/kobilica/value.mdl" +TOOL.ClientConVar[ "numvalues" ] = "1" +TOOL.ClientConVar[ "value1" ] = "0" +TOOL.ClientConVar[ "value2" ] = "0" +TOOL.ClientConVar[ "value3" ] = "0" +TOOL.ClientConVar[ "value4" ] = "0" +TOOL.ClientConVar[ "value5" ] = "0" +TOOL.ClientConVar[ "value6" ] = "0" +TOOL.ClientConVar[ "value7" ] = "0" +TOOL.ClientConVar[ "value8" ] = "0" +TOOL.ClientConVar[ "value9" ] = "0" +TOOL.ClientConVar[ "value10" ] = "0" +TOOL.ClientConVar[ "value11" ] = "0" +TOOL.ClientConVar[ "value12" ] = "0" + +cleanup.Register( "wire_values" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + local model = self:GetClientInfo( "model" ) + local numvalues = self:GetClientNumber( "numvalues" ) + + //value is a table of strings so we can save a step later in adjusting the outputs + local value = {} + if (numvalues < 1) then + numvalues = 1 + elseif (numvalues > 12) then + numvalues = 12 + end + for i = 1, numvalues do + value[i] = self:GetClientInfo( "value"..i ) + end + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_value" && trace.Entity.pl == ply ) then + trace.Entity:Setup(value) + trace.Entity.value = value + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_values" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_value = MakeWireValue( ply, trace.HitPos, Ang, model, value ) + + local min = wire_value:OBBMins() + wire_value:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_value, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireValue") + undo.AddEntity( wire_value ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_values", wire_value ) + + return true +end + +function TOOL:RightClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_value" ) then + local i = 0 + for k,v in pairs(trace.Entity.value) do + ply:ConCommand("wire_value_value"..k.." "..v) + i = i + 1 + end + ply:ConCommand("wire_value_numvalues "..i) + return true + end +end + +if (SERVER) then + + function MakeWireValue( pl, Pos, Ang, model, value ) + if ( !pl:CheckLimit( "wire_values" ) ) then return false end + + local wire_value = ents.Create( "gmod_wire_value" ) + if (!wire_value:IsValid()) then return false end + + wire_value:SetAngles( Ang ) + wire_value:SetPos( Pos ) + wire_value:SetModel( model ) + wire_value:Spawn() + + wire_value:Setup(value) + wire_value:SetPlayer( pl ) + + local ttable = { + value = value, + pl = pl + } + table.Merge(wire_value:GetTable(), ttable ) + + pl:AddCount( "wire_values", wire_value ) + + return wire_value + end + + duplicator.RegisterEntityClass("gmod_wire_value", MakeWireValue, "Pos", "Ang", "Model", "value") + +end + +function TOOL:UpdateGhostWireValue( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_value" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" )) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireValue( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_value_name", Description = "#Tool_wire_value_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_value", + + Options = { + Default = { + wire_value_value = "0", + } + }, + + CVars = { + [0] = "wire_value_value", + } + }) + + panel:AddControl("Button", { + Text = "Reset values to zero", + Name = "Reset", + Command = "wire_value_value1 0;wire_value_value2 0;wire_value_value3 0;wire_value_value4 0;wire_value_value5 0;wire_value_value6 0;wire_value_value7 0;wire_value_value8 0;wire_value_value9 0;wire_value_value10 0;wire_value_value11 0;wire_value_value12 0;", + }) + + panel:AddControl("Slider", { + Label = "Num of Values", + Type = "Integer", + Min = "1", + Max = "12", + Command = "wire_value_numvalues", + }) + + panel:AddControl("Label", { + Text = "#WireValueTool_desc", + }) + for i = 1,12 do + panel:AddControl("TextBox", { + Label = "Value"..i..":", + Text = "test", + Command = "wire_value_value"..i, + WaitForEnter = true, + }) + end + + ModelPlug_AddToCPanel(panel, "value", "wire_value", "#WireValueTool_model", nil, "#WireValueTool_model") +end diff --git a/lua/weapons/gmod_tool/stools/wire_vehicle.lua b/lua/weapons/gmod_tool/stools/wire_vehicle.lua new file mode 100644 index 0000000000..99fa2ddecf --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_vehicle.lua @@ -0,0 +1,131 @@ +TOOL.Category = "Wire - I/O" +TOOL.Name = "Vehicle Controller" +TOOL.Command = nil -- What is this for? +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if CLIENT then + language.Add("Tool_wire_vehicle_name", "Vehicle Controller Tool (Wire)") + language.Add("Tool_wire_vehicle_desc", "Spawn/link a Wire Vehicle controller.") + language.Add("Tool_wire_vehicle_0", "Primary: Create Vehicle controller. Secondary: Link controller.") + language.Add("Tool_wire_vehicle_1", "Now select the Vehicle to link to.") + language.Add("WireVehicleTool_Vehicle", "Vehicle:") + language.Add("sboxlimit_wire_vehicles", "You've hit your Vehicle Controller limit!") + language.Add("Undone_Wire Vehicle", "Undone Wire Vehicle Controller") +end + +if SERVER then + CreateConVar('sbox_maxwire_vehicles', 20) +end + +TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" + +cleanup.Register("wire_vehicles") + +function TOOL:LeftClick(trace) + if not trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local ply = self:GetOwner() + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_vehicle" and trace.Entity:GetTable().pl == ply then + return true + end + + if not self:GetSWEP():CheckLimit("wire_vehicles") then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_vehicle = MakeWireVehicle(ply, trace.HitPos, Ang, self.Model) + + wire_vehicle:SetPos(trace.HitPos - trace.HitNormal * wire_vehicle:OBBMins().z) + + local const = WireLib.Weld(wire_vehicle, trace.Entity, trace.PhysicsBone, true) + + undo.Create("Wire Vehicle") + undo.AddEntity(wire_vehicle) + undo.AddEntity(const) + undo.SetPlayer(ply) + undo.Finish() + + ply:AddCleanup("wire_vehicles", wire_vehicle) + + return true +end + +function TOOL:RightClick(trace) + if (self:GetStage() == 0) and trace.Entity:GetClass() == "gmod_wire_vehicle" then + self.VehicleCont = trace.Entity + self:SetStage(1) + return true + elseif self:GetStage() == 1 and trace.Entity.GetPassenger then + self.VehicleCont:Setup(trace.Entity) + self:SetStage(0) + self.VehicleCont = nil + return true + else + return false + end +end + +function TOOL:Reload(trace) + self:SetStage(0) + self.VehicleCont = nil +end + +if SERVER then + + function MakeWireVehicle(pl, Pos, Ang, model) + if not pl:CheckLimit("wire_vehicles") then return false end + + local wire_vehicle = ents.Create("gmod_wire_vehicle") + if not wire_vehicle:IsValid() then return false end + + wire_vehicle:SetAngles(Ang) + wire_vehicle:SetPos(Pos) + wire_vehicle:SetModel(Model(model or "models/jaanus/wiretool/wiretool_siren.mdl")) + wire_vehicle:Spawn() + wire_vehicle:SetPlayer(pl) + wire_vehicle.pl = pl + + pl:AddCount("wire_vehicles", wire_vehicle) + + return wire_vehicle + end + + duplicator.RegisterEntityClass("gmod_wire_vehicle", MakeWireVehicle, "Pos", "Ang", "Model") +end + +function TOOL:UpdateGhostWireVehicle(ent, player) + if not ent or not ent:IsValid() then return end + + local tr = utilx.GetPlayerTrace(player, player:GetCursorAimVector()) + local trace = util.TraceLine(tr) + + if not trace.Hit or trace.Entity:IsPlayer() or trace.Entity:GetClass() == "gmod_wire_vehicle" then + ent:SetNoDraw(true) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + ent:SetPos(trace.HitPos - trace.HitNormal * ent:OBBMins().z) + ent:SetAngles(Ang) + + ent:SetNoDraw(false) +end + +function TOOL:Think() + if not self.GhostEntity or not self.GhostEntity:IsValid() or self.GhostEntity:GetModel() ~= self.Model then + self:MakeGhostEntity(self.Model, Vector(0,0,0), Angle(0,0,0)) + end + + self:UpdateGhostWireVehicle(self.GhostEntity, self:GetOwner()) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_vehicle_name", Description = "#Tool_wire_vehicle_desc" }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_vthruster.lua b/lua/weapons/gmod_tool/stools/wire_vthruster.lua new file mode 100644 index 0000000000..cd49459e94 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_vthruster.lua @@ -0,0 +1,474 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Vector Thruster" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_vthruster_name", "Vector Thruster Tool (Wire)" ) + language.Add( "Tool_wire_vthruster_desc", "Spawns a vector thruster for use with the wire system." ) + language.Add( "Tool_wire_vthruster_0", "Primary: Create/Update Vector Thruster" ) + language.Add( "Tool_wire_vthruster_1", "Primary: Finish" ) + language.Add( "WireVThrusterTool_Mode", "Mode:" ) + language.Add( "WireVThrusterTool_Angle", "Use Yaw/Pitch Inputs Instead" ) + language.Add( "undone_wirevthruster", "Undone Wire Vector Thruster" ) +end + +TOOL.ClientConVar[ "force" ] = "1500" +TOOL.ClientConVar[ "force_min" ] = "0" +TOOL.ClientConVar[ "force_max" ] = "10000" +TOOL.ClientConVar[ "model" ] = "models/jaanus/wiretool/wiretool_speed.mdl" +TOOL.ClientConVar[ "bidir" ] = "1" +TOOL.ClientConVar[ "collision" ] = "0" +TOOL.ClientConVar[ "sound" ] = "0" +TOOL.ClientConVar[ "oweffect" ] = "fire" +TOOL.ClientConVar[ "uweffect" ] = "same" +TOOL.ClientConVar[ "owater" ] = "1" +TOOL.ClientConVar[ "uwater" ] = "1" +TOOL.ClientConVar[ "mode" ] = "0" +TOOL.ClientConVar[ "angleinputs" ] = "0" + +local degrees = 0 + +function TOOL:LeftClick( trace ) + local numobj = self:NumObjects() + + local ply = self:GetOwner() + + local force = self:GetClientNumber( "force" ) + local force_min = self:GetClientNumber( "force_min" ) + local force_max = self:GetClientNumber( "force_max" ) + local model = self:GetClientInfo( "model" ) + local bidir = self:GetClientNumber( "bidir" ) ~= 0 + local nocollide = self:GetClientNumber( "collision" ) == 0 + local sound = self:GetClientNumber( "sound" ) ~= 0 + local oweffect = self:GetClientInfo( "oweffect" ) + local uweffect = self:GetClientInfo( "uweffect" ) + local owater = self:GetClientNumber( "owater" ) ~= 0 + local uwater = self:GetClientNumber( "uwater" ) ~= 0 + local mode = self:GetClientNumber( "mode" ) + local angleinputs = self:GetClientNumber( "angleinputs" ) ~= 0 + + if (numobj == 0) then + if trace.Entity && trace.Entity:IsPlayer() then return false end + + // If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + if (CLIENT) then return true end + + // If we shot a wire_thruster change its force + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_vectorthruster" && trace.Entity.pl == ply ) then + + trace.Entity:SetForce( force ) + trace.Entity:SetEffect( effect ) + trace.Entity:Setup(force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, mode, angleinputs) + + trace.Entity.force = force + trace.Entity.force_min = force_min + trace.Entity.force_max = force_max + trace.Entity.bidir = bidir + trace.Entity.sound = sound + trace.Entity.oweffect = oweffect + trace.Entity.uweffect = uweffect + trace.Entity.owater = owater + trace.Entity.uwater = uwater + trace.Entity.nocollide = nocollide + trace.Entity.mode = mode + trace.Entity.angleinputs = angleinputs + + if ( nocollide == true ) then trace.Entity:GetPhysicsObject():EnableCollisions( false ) end + + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_thrusters" ) ) then return false end + + if (not util.IsValidModel(model)) then return false end + if (not util.IsValidProp(model)) then return false end // Allow ragdolls to be used? + + local ang = trace.HitNormal:Angle() + ang.pitch = ang.pitch + 90 + + local wire_thruster = MakeWireVectorThruster( ply, trace.HitPos, ang, model, force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, nocollide, mode, angleinputs ) + + local min = wire_thruster:OBBMins() + wire_thruster:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + undo.Create("WireVThruster") + undo.AddEntity( wire_thruster ) + undo.SetPlayer( ply ) + undo.Finish() + + local Phys = wire_thruster:GetPhysicsObject() + Phys:EnableMotion( false ) + Phys:Wake() + + if ( !trace.Entity:IsValid() ) then return true end + + self:ReleaseGhostEntity() + + self:SetObject(1, trace.Entity, trace.HitPos, trace.Entity:GetPhysicsObjectNum(trace.PhysicsBone), trace.PhysicsBone, trace.HitNormal) + self:SetObject(2, wire_thruster, trace.HitPos, Phys, 0, trace.HitNormal) + self:SetStage(1) + + else + if (CLIENT) then return true end + + local anchor, wire_thruster = self:GetEnt(1), self:GetEnt(2) + local anchorbone = self:GetBone(1) + local normal = self:GetNormal(1) + + local const = WireLib.Weld(wire_thruster, anchor, trace.PhysicsBone, true, nocollide) + + local Phys = wire_thruster:GetPhysicsObject() + Phys:EnableMotion( true ) + + undo.Create("WireVThruster") + undo.AddEntity( wire_thruster ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_thrusters", wire_thruster ) + ply:AddCleanup( "wire_thrusters", const ) + + self:ClearObjects() + end + + return true +end + +if (SERVER) then + + function MakeWireVectorThruster( pl, Pos, Ang, model, force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, nocollide, mode, angleinputs) + print("angleinputs=",angleinputs) + if ( !pl:CheckLimit( "wire_thrusters" ) ) then return false end + mode = mode or 0 + + local wire_thruster = ents.Create( "gmod_wire_vectorthruster" ) + if (!wire_thruster:IsValid()) then return false end + wire_thruster:SetModel( model ) + + wire_thruster:SetAngles( Ang ) + wire_thruster:SetPos( Pos ) + wire_thruster:Spawn() + + wire_thruster:Setup(force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, mode, angleinputs) + wire_thruster:SetPlayer( pl ) + + if ( nocollide == true ) then wire_thruster:GetPhysicsObject():EnableCollisions( false ) end + + local ttable = { + force = force, + force_min = force_min, + force_max = force_max, + bidir = bidir, + sound = sound, + pl = pl, + oweffect = oweffect, + uweffect = uweffect, + owater = owater, + uwater = uwater, + nocollide = nocollide, + mode = mode, + angleinputs = angleinputs, + } + table.Merge(wire_thruster:GetTable(), ttable ) + + pl:AddCount( "wire_thrusters", wire_thruster ) + + return wire_thruster + end + duplicator.RegisterEntityClass("gmod_wire_vectorthruster", MakeWireVectorThruster, "Pos", "Ang", "Model", "force", "force_min", "force_max", "oweffect", "uweffect", "owater", "uwater", "bidir", "sound", "nocollide", "mode", "angleinputs") + +end + +function TOOL:UpdateGhostWireThruster( ent, player ) + if ( !ent ) then return end + if ( !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if (!trace.Hit) then return end + + if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_thruster" || trace.Entity:IsPlayer()) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + + +function TOOL:Think() + + if (self:NumObjects() > 0) then + if ( SERVER ) then + local Phys2 = self:GetPhys(2) + local Norm2 = self:GetNormal(2) + local cmd = self:GetOwner():GetCurrentCommand() + degrees = degrees + cmd:GetMouseX() * 0.05 + local ra = degrees + if (self:GetOwner():KeyDown(IN_SPEED)) then ra = math.Round(ra/45)*45 end + local Ang = Norm2:Angle() + Ang.pitch = Ang.pitch + 90 + Ang:RotateAroundAxis(Norm2, ra) + Phys2:SetAngle( Ang ) + Phys2:Wake() + end + else + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetClientInfo( "model" )) then + self:MakeGhostEntity( self:GetClientInfo( "model" ), Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireThruster( self.GhostEntity, self:GetOwner() ) + end + +end + +if (CLIENT) then + function TOOL:FreezeMovement() + return self:GetStage() == 1 + end +end + +function TOOL:Holster() + self:ClearObjects() +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_vthruster_name", Description = "#Tool_wire_vthruster_desc" }) + + panel:AddControl("ComboBox", { + Label = "#Presets", + MenuButton = "1", + Folder = "wire_vthruster", + + Options = { + Default = { + wire_vthruster_force = "20", + wire_vthruster_model = "models/jaanus/wiretool/wiretool_speed.mdl", + wire_vthruster_effect = "fire", + } + }, + + CVars = { + [0] = "wire_vthruster_model", + [1] = "wire_vthruster_force", + [2] = "wire_vthruster_effect" + } + }) + + /*panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + MenuButton = "0", + + Options = { + ["#Spedo"] = { wire_vthruster_model = "models/jaanus/wiretool/wiretool_speed.mdl" }, + ["#Thruster"] = { wire_vthruster_model = "models/dav0r/thruster.mdl" }, + ["#Paint_Bucket"] = { wire_vthruster_model = "models/props_junk/plasticbucket001a.mdl" }, + ["#Small_Propane_Canister"] = { wire_vthruster_model = "models/props_junk/PropaneCanister001a.mdl" }, + ["#Medium_Propane_Tank"] = { wire_vthruster_model = "models/props_junk/propane_tank001a.mdl" }, + ["#Cola_Can"] = { wire_vthruster_model = "models/props_junk/PopCan01a.mdl" }, + ["#Bucket"] = { wire_vthruster_model = "models/props_junk/MetalBucket01a.mdl" }, + ["#Vitamin_Jar"] = { wire_vthruster_model = "models/props_lab/jar01a.mdl" }, + ["#Lamp_Shade"] = { wire_vthruster_model = "models/props_c17/lampShade001a.mdl" }, + ["#Fat_Can"] = { wire_vthruster_model = "models/props_c17/canister_propane01a.mdl" }, + ["#Black_Canister"] = { wire_vthruster_model = "models/props_c17/canister01a.mdl" }, + ["#Red_Canister"] = { wire_vthruster_model = "models/props_c17/canister02a.mdl" } + } + })*/ + /*panel:AddControl( "PropSelect", { Label = "#WireThrusterTool_Model", + ConVar = "wire_vthruster_model", + Category = "Thrusters", + Models = list.Get( "ThrusterModels" ) + })*/ + + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_OWEffects", + MenuButton = "0", + + Options = { + ["#No_Effects"] = { wire_vthruster_oweffect = "none" }, + ["#Flames"] = { wire_vthruster_oweffect = "fire" }, + ["#Plasma"] = { wire_vthruster_oweffect = "plasma" }, + ["#Smoke"] = { wire_vthruster_oweffect = "smoke" }, + ["#Smoke Random"] = { wire_vthruster_oweffect = "smoke_random" }, + ["#Smoke Do it Youself"] = { wire_vthruster_oweffect = "smoke_diy" }, + ["#Rings"] = { wire_vthruster_oweffect = "rings" }, + ["#Rings Growing"] = { wire_vthruster_oweffect = "rings_grow" }, + ["#Rings Shrinking"] = { wire_vthruster_oweffect = "rings_shrink" }, + ["#Bubbles"] = { wire_vthruster_oweffect = "bubble" }, + ["#Magic"] = { wire_vthruster_oweffect = "magic" }, + ["#Magic Random"] = { wire_vthruster_oweffect = "magic_color" }, + ["#Magic Do It Yourself"] = { wire_vthruster_oweffect = "magic_diy" }, + ["#Colors"] = { wire_vthruster_oweffect = "color" }, + ["#Colors Random"] = { wire_vthruster_oweffect = "color_random" }, + ["#Colors Do It Yourself"] = { wire_vthruster_oweffect = "color_diy" }, + ["#Blood"] = { wire_vthruster_oweffect = "blood" }, + ["#Money"] = { wire_vthruster_oweffect = "money" }, + ["#Sperms"] = { wire_vthruster_oweffect = "sperm" }, + ["#Feathers"] = { wire_vthruster_oweffect = "feather" }, + ["#Candy Cane"] = { wire_vthruster_oweffect = "candy_cane" }, + ["#Goldstar"] = { wire_vthruster_oweffect = "goldstar" }, + ["#Water Small"] = { wire_vthruster_oweffect = "water_small" }, + ["#Water Medium"] = { wire_vthruster_oweffect = "water_medium" }, + ["#Water Big"] = { wire_vthruster_oweffect = "water_big" }, + ["#Water Huge"] = { wire_vthruster_oweffect = "water_huge" }, + ["#Striderblood Small"] = { wire_vthruster_oweffect = "striderblood_small" }, + ["#Striderblood Medium"] = { wire_vthruster_oweffect = "striderblood_medium" }, + ["#Striderblood Big"] = { wire_vthruster_oweffect = "striderblood_big" }, + ["#Striderblood Huge"] = { wire_vthruster_oweffect = "striderblood_huge" }, + ["#More Sparks"] = { wire_vthruster_oweffect = "more_sparks" }, + ["#Spark Fountain"] = { wire_vthruster_oweffect = "spark_fountain" }, + ["#Jetflame"] = { wire_vthruster_oweffect = "jetflame" }, + ["#Jetflame Advanced"] = { wire_vthruster_oweffect = "jetflame_advanced" }, + ["#Jetflame Blue"] = { wire_vthruster_oweffect = "jetflame_blue" }, + ["#Jetflame Red"] = { wire_vthruster_oweffect = "jetflame_red" }, + ["#Jetflame Purple"] = { wire_vthruster_oweffect = "jetflame_purple" }, + ["#Comic Balls"] = { wire_vthruster_oweffect = "balls" }, + ["#Comic Balls Random"] = { wire_vthruster_oweffect = "balls_random" }, + ["#Comic Balls Fire Colors"] = { wire_vthruster_oweffect = "balls_firecolors" }, + ["#Souls"] = { wire_vthruster_oweffect = "souls" }, + ["#Debugger 10 Seconds"] = { wire_vthruster_oweffect = "debug_10" }, + ["#Debugger 30 Seconds"] = { wire_vthruster_oweffect = "debug_30" }, + ["#Debugger 60 Seconds"] = { wire_vthruster_oweffect = "debug_60" }, + ["#Fire and Smoke"] = { wire_vthruster_oweffect = "fire_smoke" }, + ["#Fire and Smoke Huge"] = { wire_vthruster_oweffect = "fire_smoke_big" }, + ["#5 Growing Rings"] = { wire_vthruster_oweffect = "rings_grow_rings" }, + ["#Color and Magic"] = { wire_vthruster_oweffect = "color_magic" }, + } + }) + + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_UWEffects", + MenuButton = "0", + + Options = { + ["#No_Effects"] = { wire_vthruster_uweffect = "none" }, + ["#Same as over water"] = { wire_vthruster_uweffect = "same" }, + ["#Flames"] = { wire_vthruster_uweffect = "fire" }, + ["#Plasma"] = { wire_vthruster_uweffect = "plasma" }, + ["#Smoke"] = { wire_vthruster_uweffect = "smoke" }, + ["#Smoke Random"] = { wire_vthruster_uweffect = "smoke_random" }, + ["#Smoke Do it Youself"] = { wire_vthruster_uweffect = "smoke_diy" }, + ["#Rings"] = { wire_vthruster_uweffect = "rings" }, + ["#Rings Growing"] = { wire_vthruster_uweffect = "rings_grow" }, + ["#Rings Shrinking"] = { wire_vthruster_uweffect = "rings_shrink" }, + ["#Bubbles"] = { wire_vthruster_uweffect = "bubble" }, + ["#Magic"] = { wire_vthruster_uweffect = "magic" }, + ["#Magic Random"] = { wire_vthruster_uweffect = "magic_color" }, + ["#Magic Do It Yourself"] = { wire_vthruster_uweffect = "magic_diy" }, + ["#Colors"] = { wire_vthruster_uweffect = "color" }, + ["#Colors Random"] = { wire_vthruster_uweffect = "color_random" }, + ["#Colors Do It Yourself"] = { wire_vthruster_uweffect = "color_diy" }, + ["#Blood"] = { wire_vthruster_uweffect = "blood" }, + ["#Money"] = { wire_vthruster_uweffect = "money" }, + ["#Sperms"] = { wire_vthruster_uweffect = "sperm" }, + ["#Feathers"] = { wire_vthruster_uweffect = "feather" }, + ["#Candy Cane"] = { wire_vthruster_uweffect = "candy_cane" }, + ["#Goldstar"] = { wire_vthruster_uweffect = "goldstar" }, + ["#Water Small"] = { wire_vthruster_uweffect = "water_small" }, + ["#Water Medium"] = { wire_vthruster_uweffect = "water_medium" }, + ["#Water Big"] = { wire_vthruster_uweffect = "water_big" }, + ["#Water Huge"] = { wire_vthruster_uweffect = "water_huge" }, + ["#Striderblood Small"] = { wire_vthruster_uweffect = "striderblood_small" }, + ["#Striderblood Medium"] = { wire_vthruster_uweffect = "striderblood_medium" }, + ["#Striderblood Big"] = { wire_vthruster_uweffect = "striderblood_big" }, + ["#Striderblood Huge"] = { wire_vthruster_uweffect = "striderblood_huge" }, + ["#More Sparks"] = { wire_vthruster_uweffect = "more_sparks" }, + ["#Spark Fountain"] = { wire_vthruster_uweffect = "spark_fountain" }, + ["#Jetflame"] = { wire_vthruster_uweffect = "jetflame" }, + ["#Jetflame Advanced"] = { wire_vthruster_uweffect = "jetflame_advanced" }, + ["#Jetflame Blue"] = { wire_vthruster_uweffect = "jetflame_blue" }, + ["#Jetflame Red"] = { wire_vthruster_uweffect = "jetflame_red" }, + ["#Jetflame Purple"] = { wire_vthruster_uweffect = "jetflame_purple" }, + ["#Comic Balls"] = { wire_vthruster_uweffect = "balls" }, + ["#Comic Balls Random"] = { wire_vthruster_uweffect = "balls_random" }, + ["#Comic Balls Fire Colors"] = { wire_vthruster_uweffect = "balls_firecolors" }, + ["#Souls"] = { wire_vthruster_uweffect = "souls" }, + ["#Debugger 10 Seconds"] = { wire_vthruster_uweffect = "debug_10" }, + ["#Debugger 30 Seconds"] = { wire_vthruster_uweffect = "debug_30" }, + ["#Debugger 60 Seconds"] = { wire_vthruster_uweffect = "debug_60" }, + ["#Fire and Smoke"] = { wire_vthruster_uweffect = "fire_smoke" }, + ["#Fire and Smoke Huge"] = { wire_vthruster_uweffect = "fire_smoke_big" }, + ["#5 Growing Rings"] = { wire_vthruster_uweffect = "rings_grow_rings" }, + ["#Color and Magic"] = { wire_vthruster_uweffect = "color_magic" }, + } + }) + + panel:AddControl("Slider", { + Label = "#WireThrusterTool_force", + Type = "Float", + Min = "1", + Max = "10000", + Command = "wire_vthruster_force" + }) + + panel:AddControl("Slider", { + Label = "#WireThrusterTool_force_min", + Type = "Float", + Min = "-10000", + Max = "10000", + Command = "wire_vthruster_force_min" + }) + + panel:AddControl("Slider", { + Label = "#WireThrusterTool_force_max", + Type = "Float", + Min = "-10000", + Max = "10000", + Command = "wire_vthruster_force_max" + }) + + panel:AddControl("CheckBox", { + Label = "#WireThrusterTool_bidir", + Command = "wire_vthruster_bidir" + }) + + panel:AddControl("CheckBox", { + Label = "#WireThrusterTool_collision", + Command = "wire_vthruster_collision" + }) + + panel:AddControl("CheckBox", { + Label = "#WireThrusterTool_sound", + Command = "wire_vthruster_sound" + }) + + panel:AddControl("CheckBox", { + Label = "#WireThrusterTool_owater", + Command = "wire_vthruster_owater" + }) + + panel:AddControl("CheckBox", { + Label = "#WireThrusterTool_uwater", + Command = "wire_vthruster_uwater" + }) + + panel:AddControl("ComboBox", { + Label = "#WireVThrusterTool_Mode", + MenuButton = "0", + + Options = { + ["#XYZ Local"] = { wire_vthruster_mode = "0" }, + ["#XYZ World"] = { wire_vthruster_mode = "1" }, + ["#XY Local, Z World"] = { wire_vthruster_mode = "2" }, + } + }) + + panel:AddControl("CheckBox", { + Label = "#WireVThrusterTool_Angle", + Command = "wire_vthruster_angleinputs" + }) + +end + +list.Set( "ThrusterModels", "models/jaanus/wiretool/wiretool_speed.mdl", {} ) diff --git a/lua/weapons/gmod_tool/stools/wire_watersensor.lua b/lua/weapons/gmod_tool/stools/wire_watersensor.lua new file mode 100644 index 0000000000..5dbb670f62 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_watersensor.lua @@ -0,0 +1,146 @@ +TOOL.Category = "Wire - Detection" +TOOL.Name = "Water Sensor" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_watersensor_name", "Water Sensor Tool (Wire)" ) + language.Add( "Tool_wire_watersensor_desc", "Spawns a constant Water Sensor prop for use with the wire system." ) + language.Add( "Tool_wire_watersensor_0", "Primary: Create/Update Water Sensor" ) + language.Add( "WireWatersensorTool_watersensor", "Water Sensor:" ) + language.Add( "sboxlimit_wire_watersensors", "You've hit Water Sensors limit!" ) + language.Add( "undone_Wire Water Sensor", "Undone Wire Water Sensor" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_watersensors', 20) + ModelPlug_Register("WaterSensor") +end + +TOOL.ClientConVar[ "model" ] = "models/beer/wiremod/watersensor.mdl" +TOOL.ClientConVar[ "modelsize" ] = "" +local ModelInfo = {"","",""} + +cleanup.Register( "wire_watersensors" ) + +function TOOL:LeftClick( trace ) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_watersensor" && trace.Entity:GetTable().pl == ply ) then + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_watersensors" ) ) then return false end + + if ( !util.IsValidModel( ModelInfo[3] ) ) then return false end + if ( !util.IsValidProp( ModelInfo[3] ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_watersensor = MakeWireWatersensor( ply, trace.HitPos, Ang, ModelInfo[3] ) + + local min = wire_watersensor:OBBMins() + wire_watersensor:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const = WireLib.Weld(wire_watersensor, trace.Entity, trace.PhysicsBone, true, true) + + undo.Create("Wire Water Sensor") + undo.AddEntity( wire_watersensor ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_watersensors", wire_watersensor ) + + return true +end + +if (SERVER) then + + function MakeWireWatersensor( pl, Pos, Ang, model ) + if ( !pl:CheckLimit( "wire_watersensors" ) ) then return false end + + local wire_watersensor = ents.Create( "gmod_wire_watersensor" ) + if (!wire_watersensor:IsValid()) then return false end + + wire_watersensor:SetAngles( Ang ) + wire_watersensor:SetPos( Pos ) + if(!model) then + wire_watersensor:SetModel( Model("models/jaanus/wiretool/wiretool_range.mdl") ) + else + wire_watersensor:SetModel( Model(model) ) + end + wire_watersensor:Spawn() + + wire_watersensor:SetPlayer( pl ) + wire_watersensor.pl = pl + + pl:AddCount( "wire_watersensors", wire_watersensor ) + + return wire_watersensor + end + + duplicator.RegisterEntityClass("gmod_wire_watersensor", MakeWireWatersensor, "Pos", "Ang", "Model") + +end + +function TOOL:UpdateGhostWireWatersensor( ent, player ) + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_watersensor" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) +end + +function TOOL:Think() + if ModelInfo[1]!= self:GetClientInfo( "model" ) || ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + ModelInfo[1] = self:GetClientInfo( "model" ) + ModelInfo[2] = self:GetClientInfo( "modelsize" ) + ModelInfo[3] = ModelInfo[1] + if (ModelInfo[1] && ModelInfo[2] && ModelInfo[2]!="") then + local test = string.sub(ModelInfo[1], 1, -5) .. ModelInfo[2] .. string.sub(ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + ModelInfo[3] = test + end + end + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhostWireWatersensor( self.GhostEntity, self:GetOwner() ) +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_watersensor_name", Description = "#Tool_wire_watersensor_desc" }) + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_watersensor_modelsize = "" }, + ["mini"] = { wire_watersensor_modelsize = "_mini" }, + ["nano"] = { wire_watersensor_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "WaterSensor", "wire_watersensor", "#ToolWireIndicator_Model") +end diff --git a/lua/weapons/gmod_tool/stools/wire_waypoint.lua b/lua/weapons/gmod_tool/stools/wire_waypoint.lua new file mode 100644 index 0000000000..4d390b48e6 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_waypoint.lua @@ -0,0 +1,174 @@ +TOOL.Category = "Wire - Beacon" +TOOL.Name = "Waypoint" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if ( CLIENT ) then + language.Add( "Tool_wire_waypoint_name", "Waypoint Beacon Tool (Wire)" ) + language.Add( "Tool_wire_waypoint_desc", "Spawns a waypoint beacon for use with the wire system." ) + language.Add( "Tool_wire_waypoint_0", "Primary: Create/Update Waypoint Beacon Secondary: Link to next waypoint" ) + language.Add( "Tool_wire_waypoint_1", "Primary: Select waypoint to go to after this one" ) + language.Add( "WireWaypointTool_range", "Range:" ) + language.Add( "WireWaypointTool_alink", "Auto-link previous" ) + language.Add( "sboxlimit_wire_waypoints", "You've hit waypoint beacons limit!" ) + language.Add( "undone_wirewaypoint", "Undone Wire Waypoint Beacon" ) +end + +if (SERVER) then + CreateConVar('sbox_maxwire_waypoints',30) +end + +TOOL.ClientConVar[ "range" ] = "150" +TOOL.ClientConVar[ "alink" ] = "0" + +TOOL.Model = "models/props_lab/powerbox02d.mdl" + +cleanup.Register( "wire_waypoints" ) + +function TOOL:LeftClick(trace) + if (!trace.HitPos) then return false end + if (trace.Entity:IsPlayer()) then return false end + if ( CLIENT ) then return true end + + local ply = self:GetOwner() + + if (self:GetStage() == 1) then + self:SetStage(0) + + if (trace.Entity:IsValid()) and (trace.Entity:GetClass() == "gmod_wire_waypoint") and (self.SrcWaypoint) and (self.SrcWaypoint:IsValid()) then + self.SrcWaypoint:SetNextWaypoint(trace.Entity) + self.SrcWaypoint = nil + + return true + end + + self.SrcWaypoint = nil + + return + end + + local range = self:GetClientNumber("range") + + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_waypoint" && trace.Entity.pl == ply ) then + trace.Entity:Setup(range) + trace.Entity.range = range + return true + end + + if ( !self:GetSWEP():CheckLimit( "wire_waypoints" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + + local wire_waypoint = MakeWireWaypoint( ply, trace.HitPos, Ang, self.Model, range ) + + local min = wire_waypoint:OBBMins() + wire_waypoint:SetPos( trace.HitPos - trace.HitNormal * (min.z) ) + + // Auto-link (itsbth) + if ( self.OldWaypoint && self.OldWaypoint:IsValid() and self:GetClientNumber("alink") == 1 ) then + self.OldWaypoint:SetNextWaypoint(wire_waypoint) + end + + self.OldWaypoint = wire_waypoint + + undo.Create("WireWaypoint") + undo.AddEntity( wire_waypoint ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_waypoints", wire_waypoint ) + + return true +end + +function TOOL:RightClick(trace) + if (self:GetStage() == 0) and (trace.Entity:IsValid()) and (trace.Entity:GetClass() == "gmod_wire_waypoint") then + self.SrcWaypoint = trace.Entity + self:SetStage(1) + + return true + end + + return self:LeftClick(trace) +end + +if SERVER then + + function MakeWireWaypoint(pl, Pos, Ang, model, range ) + if (!pl:CheckLimit("wire_waypoints")) then return end + + local wire_waypoint = ents.Create("gmod_wire_waypoint") + wire_waypoint:SetPos(Pos) + wire_waypoint:SetAngles(Ang) + wire_waypoint:SetModel( Model(model or "models/props_lab/powerbox02d.mdl") ) + wire_waypoint:Spawn() + wire_waypoint:Activate() + + wire_waypoint:Setup(range) + wire_waypoint:SetPlayer(pl) + + local ttable = { + pl = pl, + range = range, + nocollide = nocollide, + } + table.Merge( wire_waypoint:GetTable(), ttable ) + + pl:AddCount( "wire_waypoints", wire_waypoint ) + + return wire_waypoint + end + + duplicator.RegisterEntityClass("gmod_wire_waypoint", MakeWireWaypoint, "Pos", "Ang", "Model", "range") + +end + +function TOOL:UpdateGhostWireWaypoint( ent, player ) + + if ( !ent || !ent:IsValid() ) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + + if (!trace.Hit || trace.Entity:IsPlayer() || trace.Entity:GetClass() == "gmod_wire_waypoint" ) then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + ent:SetAngles( Ang ) + + ent:SetNoDraw( false ) + +end + +function TOOL:Think() + + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self.Model ) then + self:MakeGhostEntity( self.Model, Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireWaypoint( self.GhostEntity, self:GetOwner() ) + +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_waypoint_name", Description = "#Tool_wire_waypoint_desc" }) + + panel:AddControl("Slider", { + Label = "#WireWaypointTool_range", + Type = "Float", + Min = "1", + Max = "1000", + Command = "wire_waypoint_range" + }) + panel:AddControl("Checkbox", { + Label = "#WireWaypointTool_alink", + Command = "wire_waypoint_alink" + }) +end + diff --git a/lua/weapons/gmod_tool/stools/wire_winch.lua b/lua/weapons/gmod_tool/stools/wire_winch.lua new file mode 100644 index 0000000000..58614d6c0d --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_winch.lua @@ -0,0 +1,371 @@ +TOOL.Category = "Wire - Physics" +TOOL.Name = "Winch" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +TOOL.ClientConVar[ "material" ] = "cable/rope" +TOOL.ClientConVar[ "width" ] = "3" +TOOL.ClientConVar[ "fwd_speed" ] = "64" +TOOL.ClientConVar[ "bwd_speed" ] = "64" + +if CLIENT then + language.Add( "Tool_wire_winch_name", "Winch Tool (Wire)" ) + language.Add( "Tool_wire_winch_desc", "Makes a controllable winch" ) + language.Add( "Tool_wire_winch_0", "Primary: Place winch\nSecondary: Place winch along the hit normal" ) + language.Add( "Tool_wire_winch_1", "Left click on the second point" ) + language.Add( "Tool_wire_winch_2", "Left click to place the controller" ) + language.Add( "WireWinchTool_width", "Width:" ) + language.Add( "WireWinchTool_material", "Material:" ) + language.Add( "WireWinchTool_fixed", "Fixed" ) + language.Add( "undone_wirewinch", "Undone Wire Winch" ) +end + + +function TOOL:LeftClick( trace ) + if ( trace.Entity:IsValid() && trace.Entity:IsPlayer() ) then return end + + -- If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + local iNum = self:NumObjects() + + local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + self:SetObject( iNum + 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal ) + + if ( iNum > 1 ) then + + if ( CLIENT ) then + self:ClearObjects() + return true + end + + local ply = self:GetOwner() + local Ent1, Ent2, Ent3 = self:GetEnt(1), self:GetEnt(2), trace.Entity + local const, rope = self.constraint, self.rope + + if ( !const ) or ( !const:IsValid() ) then + WireLib.AddNotify(self:GetOwner(), "Wire Winch Invalid!", NOTIFY_GENERIC, 7) + self:ClearObjects() + self:SetStage(0) + return + end + + -- Attach our Controller to the Elastic constraint + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + local controller = MakeWireWinchController(ply, trace.HitPos, Ang, "models/jaanus/wiretool/wiretool_siren.mdl", nil, const, rope) + + local min = controller:OBBMins() + controller:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + local const2 = WireLib.Weld(controller, trace.Entity, trace.PhysicsBone, true) + + undo.Create("WireWinch") + undo.AddEntity( controller ) + undo.AddEntity( const ) + undo.AddEntity( rope ) + undo.AddEntity( const2 ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "ropeconstraints", controller ) + ply:AddCleanup( "ropeconstraints", const2 ) + + if const then controller:DeleteOnRemove( const ) end + if rope then controller:DeleteOnRemove( rope ) end + + self:ClearObjects() + self:SetStage(0) + + elseif ( iNum == 1 ) then + + if ( CLIENT ) then + return true + end + + -- Get client's CVars + local material = self:GetClientInfo( "material" ) or "cable/rope" + local width = self:GetClientNumber( "width" ) or 3 + local fwd_speed = self:GetClientNumber( "fwd_speed" ) or 64 + local bwd_speed = self:GetClientNumber( "bwd_speed" ) or 64 + + -- Get information we're about to use + local Ent1, Ent2 = self:GetEnt(1), self:GetEnt(2) + local Bone1, Bone2 = self:GetBone(1), self:GetBone(2) + local LPos1, LPos2 = self:GetLocalPos(1),self:GetLocalPos(2) + + local const,rope = MakeWireWinch( self:GetOwner(), Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, fwd_speed, bwd_speed, material ) + + self.constraint, self.rope = const,rope + + undo.Create("WireWinch") + if constraint then undo.AddEntity( const ) end + if rope then undo.AddEntity( rope ) end + undo.SetPlayer( self:GetOwner() ) + undo.Finish() + + + if const then self:GetOwner():AddCleanup( "ropeconstraints", const ) end + if rope then self:GetOwner():AddCleanup( "ropeconstraints", rope ) end + + self:SetStage(2) + + else + + self:SetStage( iNum+1 ) + + end + + return true + +end + +function TOOL:RightClick( trace ) + + local iNum = self:NumObjects() + + if ( iNum > 1 ) then + if ( !self.constraint ) or ( !self.constraint:IsValid() ) then + self:ClearObjects() + self:SetStage(0) + else + return false + end + end + + -- If there's no physics object then we can't constraint it! + if ( SERVER && !util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) ) then return false end + + local Phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + self:SetObject( 1, trace.Entity, trace.HitPos, Phys, trace.PhysicsBone, trace.HitNormal ) + + local tr = {} + tr.start = trace.HitPos + tr.endpos = tr.start + (trace.HitNormal * 16384) + tr.filter = {} + tr.filter[1] = self:GetOwner() + if (trace.Entity:IsValid()) then + tr.filter[2] = trace.Entity + end + + local tr = util.TraceLine( tr ) + + if ( !tr.Hit ) then + self:ClearObjects() + return + end + + -- Don't try to constrain world to world + if ( trace.HitWorld && tr.HitWorld ) then + self:ClearObjects() + return + end + + if ( trace.Entity:IsValid() && trace.Entity:IsPlayer() ) then + self:ClearObjects() + return + end + if ( tr.Entity:IsValid() && tr.Entity:IsPlayer() ) then + self:ClearObjects() + return + end + + local Phys2 = tr.Entity:GetPhysicsObjectNum( tr.PhysicsBone ) + self:SetObject( 2, tr.Entity, tr.HitPos, Phys2, tr.PhysicsBone, trace.HitNormal ) + + if ( CLIENT ) then + return true + end + + -- Get client's CVars + local material = self:GetClientInfo( "material" ) or "cable/rope" + local width = self:GetClientNumber( "width" ) or 3 + local fwd_speed = self:GetClientNumber( "fwd_speed" ) or 64 + local bwd_speed = self:GetClientNumber( "bwd_speed" ) or 64 + + -- Get information we're about to use + local Ent1, Ent2 = self:GetEnt(1), self:GetEnt(2) + local Bone1, Bone2 = self:GetBone(1), self:GetBone(2) + local LPos1, LPos2 = self:GetLocalPos(1),self:GetLocalPos(2) + + local const,rope = MakeWireWinch( self:GetOwner(), Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, fwd_speed, bwd_speed, material ) + + self.constraint, self.rope = const,rope + + undo.Create("WireWinch") + if const then undo.AddEntity( const ) end + if rope then undo.AddEntity( rope ) end + if controller then undo.AddEntity( controller ) end + undo.SetPlayer( self:GetOwner() ) + undo.Finish() + + if constraint then self:GetOwner():AddCleanup( "ropeconstraints", const ) end + if rope then self:GetOwner():AddCleanup( "ropeconstraints", rope ) end + + self:SetStage(2) + + return true + +end + +if SERVER then + + local function CalcElasticConsts(Phys1, Phys2, Ent1, Ent2) + local minMass = 0; + + if ( Ent1:IsWorld() ) then minMass = Phys2:GetMass() + elseif ( Ent2:IsWorld() ) then minMass = Phys1:GetMass() + else + minMass = math.min( Phys1:GetMass(), Phys2:GetMass() ) + end + + -- const, damp + local const = minMass * 100 + local damp = const * 0.2 + + if ( iFixed == 0 ) then + + const = minMass * 50 + damp = const * 0.1 + + end + + return const, damp + end + + --need for the const to find the controler after being duplicator pasted + WireWinchTracking = {} + + local function SetWinchParameters(controller, const, rope) + controller.MyId = controller:EntIndex() + + if const then + const.MyCrtl = controller:EntIndex() + controller:SetConstraint( const ) + controller:DeleteOnRemove( const ) + end + + if rope then + controller:SetRope( rope ) + controller:DeleteOnRemove( rope ) + end + end + + function MakeWireWinchController( pl, Pos, Ang, model, MyId, const, rope ) + local controller = ents.Create("gmod_wire_winch_controller") + + controller:SetPos( Pos ) + controller:SetAngles( Ang ) + controller:SetModel( Model(model) ) + controller:Setup() + controller:SetPlayer(pl) + controller:Spawn() + + SetWinchParameters(controller, const, rope) + if MyId then + WireWinchTracking[ MyId ] = controller + end + + return controller + end + + duplicator.RegisterEntityClass("gmod_wire_winch_controller", MakeWireWinchController, "Pos", "Ang", "Model", "MyId") + + function MakeWireWinch( pl, Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, width, fwd_speed, bwd_speed, material, MyCrtl ) + if ( !constraint.CanConstrain( Ent1, Bone1 ) ) then return false end + if ( !constraint.CanConstrain( Ent2, Bone2 ) ) then return false end + + local Phys1 = Ent1:GetPhysicsObjectNum( Bone1 ) + local Phys2 = Ent2:GetPhysicsObjectNum( Bone2 ) + local WPos1 = Phys1:LocalToWorld( LPos1 ) + local WPos2 = Phys2:LocalToWorld( LPos2 ) + + if ( Phys1 == Phys2 ) then return false end + + local constant, dampen = CalcElasticConsts( Phys1, Phys2, Ent1, Ent2 ) + + local const, rope = constraint.Elastic( Ent1, Ent2, Bone1, Bone2, LPos1, LPos2, constant, dampen, 0, material, width, true ) + + if ( !const ) then return nil, rope end + + local ctable = { + Type = "WireWinch", + pl = pl, + Ent1 = Ent1, + Ent2 = Ent2, + Bone1 = Bone1, + Bone2 = Bone2, + LPos1 = LPos1, + LPos2 = LPos2, + width = width, + fwd_speed = fwd_speed, + bwd_speed = bwd_speed, + material = material + } + const:SetTable( ctable ) -- Shouldn't this be merged instead of replaced? + + if (MyCrtl) then + Msg("finding crtl for this wired wnc const\n") + local controller = WireWinchTracking[ MyCrtl ] + + SetWinchParameters(controller, const, rope) + + Ent1:DeleteOnRemove( controller ) + Ent2:DeleteOnRemove( controller ) + const:DeleteOnRemove( controller ) + end + + return const, rope + end + + duplicator.RegisterConstraint( "WireWinch", MakeWireWinch, "pl", "Ent1", "Ent2", "Bone1", "Bone2", "LPos1", "LPos2", "width", "fwd_speed", "bwd_speed", "material", "MyCrtl" ) + +end + +function TOOL:Reload( trace ) + + if (!trace.Entity:IsValid() || trace.Entity:IsPlayer() ) then return false end + if ( CLIENT ) then return true end + + local bool = constraint.RemoveConstraints( trace.Entity, "WireWinch" ) + return bool + +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("CheckBox", { + Label = "#WireWinchTool_fixed", + Command = "wire_winch_fixed" + }) + + panel:AddControl("Slider", { + Label = "#WireWinchTool_width", + Type = "Float", + Min = "1", + Max = "20", + Command = "wire_winch_width" + }) + + panel:AddControl("MaterialGallery", { + Label = "#WireWinchTool_material", + Height = "64", + Width = "28", + Rows = "1", + Stretch = "1", + + Options = { + ["Wire"] = { Material = "cable/rope_icon", wire_winch_material = "cable/rope" }, + ["Cable 2"] = { Material = "cable/cable_icon", wire_winch_material = "cable/cable2" }, + ["XBeam"] = { Material = "cable/xbeam", wire_winch_material = "cable/xbeam" }, + ["Red Laser"] = { Material = "cable/redlaser", wire_winch_material = "cable/redlaser" }, + ["Blue Electric"] = { Material = "cable/blue_elec", wire_winch_material = "cable/blue_elec" }, + ["Physics Beam"] = { Material = "cable/physbeam", wire_winch_material = "cable/physbeam" }, + ["Hydra"] = { Material = "cable/hydra", wire_winch_material = "cable/hydra" }, + }, + + CVars = { + [0] = "wire_winch_material" + } + }) +end diff --git a/lua/weapons/gmod_tool/stools/wire_wirelink.lua b/lua/weapons/gmod_tool/stools/wire_wirelink.lua new file mode 100644 index 0000000000..a1f250c7e5 --- /dev/null +++ b/lua/weapons/gmod_tool/stools/wire_wirelink.lua @@ -0,0 +1,155 @@ +TOOL.Category = "Wire - Tools" +TOOL.Name = "Expression 2 - Wirelink" +TOOL.Command = nil +TOOL.ConfigName = "" +TOOL.Tab = "Wire" + +if CLIENT then + language.Add( "Tool_wire_wirelink_name", "Expression 2 Wirelink Tool (Wire)" ) + language.Add( "Tool_wire_wirelink_desc", "Adds a wirelink output to any wire compatible device, for use with Expression 2" ) + language.Add( "Tool_wire_wirelink_0", "Primary: Add wirelink, Secondary: Remove wirelink" ) +end + +if SERVER then + local _Wire_Link_End = WireLib.Link_End + local _Wire_CreateSpecialOutputs = WireLib.CreateSpecialOutputs + local _Wire_AdjustSpecialOutputs = WireLib.AdjustSpecialOutputs + local _Wire_BuildDupeInfo = WireLib.BuildDupeInfo + local _Wire_ApplyDupeInfo = WireLib.ApplyDupeInfo + + function RefreshSpecialOutputs(ent) + local names = {} + local types = {} + local descs = {} + + if ent.Outputs then + local index = 1 + + for _,output in pairs(ent.Outputs) do + names[index] = output.Name + types[index] = output.Type + descs[index] = output.Desc + index = index + 1 + end + + ent.Outputs = WireLib.AdjustSpecialOutputs(ent, names, types, descs) + else + ent.Outputs = WireLib.CreateSpecialOutputs(ent, names, types, descs) + end + + WireLib.TriggerOutput(ent, "link", ent) + end + + function InfuseSpecialOutputs(func, ent, names, types, desc) + if ent.extended == nil then + return func(ent, names, types, desc) + end + + if types == nil then + types = {} + for i,_ in ipairs(names) do + types[i] = "NORMAL" + end + end + + table.insert(names, "link") + table.insert(types, "WIRELINK") + local outputs = func(ent, names, types, desc) + table.remove(names) + table.remove(types) + + return outputs + end + + function WireLib.BuildDupeInfo(ent) + local info = _Wire_BuildDupeInfo(ent) + if ent.extended then + if info == nil then info = {} end + info.extended = true + end + return info + end + + function WireLib.ApplyDupeInfo(ply, ent, info, GetEntByID) + if info.extended and ent.extended == nil then + ent.extended = true + RefreshSpecialOutputs(ent) + end + + _Wire_ApplyDupeInfo(ply, ent, info, GetEntByID) + end + + function WireLib.CreateSpecialOutputs(ent, names, types, desc) + return InfuseSpecialOutputs(_Wire_CreateSpecialOutputs, ent, names, types, desc) + end + + function WireLib.AdjustSpecialOutputs(ent, names, types, desc) + return InfuseSpecialOutputs(_Wire_AdjustSpecialOutputs, ent, names, types, desc) + end + + function WireLib.CreateOutputs(ent, names, desc) + return WireLib.CreateSpecialOutputs(ent, names, nil, desc) + end + + function WireLib.AdjustOutputs(ent, names, desc) + return WireLib.AdjustSpecialOutputs(ent, names, nil, desc) + end + + function WireLib.Link_End(idx, ent, pos, oname, pl) + if oname == "link" and ent.extended == nil then + ent.extended = true + RefreshSpecialOutputs(ent) + end + + return _Wire_Link_End(idx, ent, pos, oname, pl) + end + + Wire_Link_End = WireLib.Link_End + Wire_CreateOutputs = WireLib.CreateOutputs + Wire_AdjustOutputs = WireLib.AdjustOutputs + Wire_BuildDupeInfo = WireLib.BuildDupeInfo + Wire_ApplyDupeInfo = WireLib.ApplyDupeInfo +end + + +function TOOL:LeftClick(trace) + if !trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local ply = self:GetOwner() + if ( trace.Entity:IsValid() && (trace.Entity.Base == "base_wire_entity" || trace.Entity.TriggerInput) && (trace.Entity.pl == ply || trace.Entity.pl == nil) ) then + local ent = trace.Entity + if ent.extended then return false end + + ent.extended = true + RefreshSpecialOutputs(ent) + + return true + end + + return false +end + +function TOOL:RightClick(trace) + if !trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local ply = self:GetOwner() + if ( trace.Entity:IsValid() && trace.Entity.TriggerInput && (trace.Entity.pl == ply || trace.Entity.pl == nil) ) then + local ent = trace.Entity + if !ent.extended then return false end + + ent.extended = false + RefreshSpecialOutputs(ent) + + return true + end + + return false +end + +function TOOL.BuildCPanel(panel) + panel:AddControl("Header", { Text = "#Tool_wire_wirelink_name", Description = "#Tool_wire_wirelink_desc" }) +end diff --git a/lua/weapons/laserPointer/cl_init.lua b/lua/weapons/laserPointer/cl_init.lua new file mode 100644 index 0000000000..b960b700c5 --- /dev/null +++ b/lua/weapons/laserPointer/cl_init.lua @@ -0,0 +1,24 @@ +include('shared.lua') +SWEP.PrintName = "Laser Pointer" +SWEP.Slot = 0 +SWEP.SlotPos = 4 +SWEP.DrawAmmo = false +SWEP.DrawCrosshair = true + +local LASER = Material('cable/redlaser') + +function SWEP:Initialize() + local ply = LocalPlayer() + self.VM = ply:GetViewModel() + local attachmentIndex = self.VM:LookupAttachment("muzzle") + if attachmentIndex == 0 then attachmentIndex = self.VM:LookupAttachment("1") end + self.Attach = attachmentIndex +end + +function SWEP:ViewModelDrawn() + if(self.Weapon:GetNWBool("Active")) then + //Draw the laser beam. + render.SetMaterial( LASER ) + render.DrawBeam(self.VM:GetAttachment(self.Attach).Pos, self.Owner:GetEyeTrace().HitPos, 2, 0, 12.5, Color(255, 0, 0, 255)) + end +end diff --git a/lua/weapons/laserPointer/init.lua b/lua/weapons/laserPointer/init.lua new file mode 100644 index 0000000000..5335423e29 --- /dev/null +++ b/lua/weapons/laserPointer/init.lua @@ -0,0 +1,62 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +resource.AddFile("materials/VGUI/entities/laserPointer.vmt") +resource.AddFile("materials/VGUI/entities/laserPointer.vtf") +include('shared.lua') + +SWEP.Weight = 8 +SWEP.AutoSwitchTo = false +SWEP.AutoSwitchFrom = false + +SWEP.Receiver = nil +SWEP.Pointing = false + +function SWEP:Initialize() + self.Pointing = false +end + +function SWEP:Reload() + +end + +function SWEP:Equip( newOwner ) + if(newOwner.LasReceiver && newOwner.LasReceiver:IsValid())then + self.Receiver = newOwner.LasReceiver + newOwner.LasReceiver = nil + newOwner:PrintMessage( HUD_PRINTTALK, "Relinked Sucessfully" ) + end +end + +function SWEP:PrimaryAttack() + self.Pointing = !self.Pointing + self.Weapon:SetNWBool("Active", self.Pointing) + if(self.Pointing && self.Receiver && self.Receiver:IsValid())then + Wire_TriggerOutput(self.Receiver,"Active",1) + else + Wire_TriggerOutput(self.Receiver,"Active",0) + end +end + +function SWEP:SecondaryAttack() + local trace = self.Owner:GetEyeTrace() + + if (trace.Entity:GetClass() == "gmod_wire_las_reciever") then + self.Receiver = trace.Entity + self.Owner:PrintMessage( HUD_PRINTTALK, "Linked Sucessfully" ) + return true + end +end + +function SWEP:Think() + if(self.Pointing && self.Receiver && self.Receiver:IsValid())then + local trace = self.Owner:GetEyeTrace() + local point = trace.HitPos + if (COLOSSAL_SANDBOX) then point = point * 6.25 end + Wire_TriggerOutput(self.Receiver, "X", point.x) + Wire_TriggerOutput(self.Receiver, "Y", point.y) + Wire_TriggerOutput(self.Receiver, "Z", point.z) + Wire_TriggerOutput(self.Receiver, "Pos", point) + Wire_TriggerOutput(self.Receiver, "RangerData", trace) + self.Receiver.VPos = point + end +end diff --git a/lua/weapons/laserPointer/shared.lua b/lua/weapons/laserPointer/shared.lua new file mode 100644 index 0000000000..ac46dc4925 --- /dev/null +++ b/lua/weapons/laserPointer/shared.lua @@ -0,0 +1,20 @@ +SWEP.Author = "" +SWEP.Contact = "" +SWEP.Purpose = "" +SWEP.Instructions = "Left Click to designate targets. Right click to select laser receiver." + +SWEP.Spawnable = true; +SWEP.AdminSpawnable = true; + +SWEP.viewModel = "models/weapons/v_pistol.mdl"; +SWEP.worldModel = "models/weapons/w_pistol.mdl"; + +SWEP.Primary.ClipSize = -1 +SWEP.Primary.DefaultClip = -1 +SWEP.Primary.Automatic = false +SWEP.Primary.Ammo = "none" + +SWEP.Secondary.ClipSize = -1 +SWEP.Secondary.DefaultClip = -1 +SWEP.Secondary.Automatic = false +SWEP.Secondary.Ammo = "none" diff --git a/lua/wire/Beam_NetVars.lua b/lua/wire/Beam_NetVars.lua new file mode 100644 index 0000000000..0355ae5c1c --- /dev/null +++ b/lua/wire/Beam_NetVars.lua @@ -0,0 +1,408 @@ +-- $Rev: 1303 $ +-- $LastChangedDate: 2009-07-08 19:10:33 -0700 (Wed, 08 Jul 2009) $ +-- $LastChangedBy: tad2020 $ + +-- this is all crap D: + +/////////////////////////////////////////////// +// ===BeamNetVars=== // +// Custom Networked Vars Module // +// Based off Garry's Networked Vars Module // +// Modification by: TAD2020 // +/////////////////////////////////////////////// +// How to use: // +// Just like NetVars, ent:SetNetworkedBeam* // +// and ent:GetNetworkedBeam* // +// Key should be short or a small number. // +// These functions should only be used instead // +// standard NetVars when very large quanity // +// of rarly updated values need to be sent with // +// low importantance. Mainly this is used by // +// wire's client side beams and rapidly updating // +// overlay text. // +// Data is sent at a tick interval, if too much // +// is in the outgoing query, the delay between // +// sends is increased. // +// On player joins, all current data is queried to // +// to send to them in the lowest priority stack. // +// Low priority stack sends a few entities's // +// vars each tick. // +/////////////////////////////////////////////// + +//RD header for multi distro-ablity +local ThisBeamNetVarsVersion = 0.71 +if (BeamNetVars) and (BeamNetVars.Version) and (BeamNetVars.Version > ThisBeamNetVarsVersion) then + Msg("======== A Newer Version of BeamNetVars Detected ========\n".. + "======== This ver: "..ThisBeamNetVarsVersion.." || Detected ver: "..BeamNetVars.Version.." || Skipping\n") + return +elseif (BeamNetVars) and (BeamNetVars.Version) and (BeamNetVars.Version == ThisBeamNetVarsVersion) then + Msg("======== The Same Version of BeamNetVars Detected || Skipping ========\n") + return +elseif (BeamNetVars) and (BeamNetVars.Version) then + Msg("======== Am Older Version of BeamNetVars Detected ========\n".. + "======== This ver: "..ThisBeamNetVarsVersion.." || Detected ver: "..BeamNetVars.Version.." || Overriding\n") +end + + +BeamNetVars = {} +BeamNetVars.Version = ThisBeamNetVarsVersion + +if (SERVER) then + //we want this + //sv_usermessage_maxsize = 1024 + game.ConsoleCommand( "sv_usermessage_maxsize 1024\n" ) +end + +local meta = FindMetaTable( "Entity" ) + +// Return if there's nothing to add on to +if (!meta) then return end + +local Vector_Default = Vector(0,0,0) +local Angle_Default = Angle(0,0,0) + +local NetworkVars = {} + +local NetworkFunction = {} +local DelayedUpdates = {} +local DelayedUpdatesNum = 0 +local ExtraDelayedUpdates = {} + +local NextCleanup = CurTime() + +if ( CLIENT ) then + local function Dump() + Msg("Networked Beam Vars...\n") + PrintTable( NetworkVars ) + end + concommand.Add( "networkbeamvars_dump", Dump ) +end + +local function AttemptToSwitchTables( Ent, EntIndex ) + if ( NetworkVars[ EntIndex ] == nil ) then return end + // We have an old entindex based entry! Move it over! + NetworkVars[ Ent ] = NetworkVars[ EntIndex ] + NetworkVars[ EntIndex ] = nil +end + +local function CleaupNetworkVars() + if ( NextCleanup > CurTime() ) then return end + NextCleanup = CurTime() + 30 + for k, v in pairs( NetworkVars ) do + if ( type( k ) != "number" && type( k ) != "string" ) then + if ( !k:IsValid() ) then + NetworkVars[ k ] = nil + end + end + end +end + +local function GetNetworkTable( ent, name ) + if ( CLIENT ) then + CleaupNetworkVars() + end + if ( !NetworkVars[ ent ] ) then + NetworkVars[ ent ] = {} + // This is the first time this entity has been created. + // Check whether we previously had an entindex based table + if ( CLIENT && type( ent ) != "number" && type( ent ) != "string" ) then + AttemptToSwitchTables( ent, ent:EntIndex() ) + end + end + NetworkVars[ ent ][ name ] = NetworkVars[ ent ][ name ] or {} + return NetworkVars[ ent ][ name ] +end + +local function SendNetworkUpdate( VarType, Index, Key, Value, Player ) + if(Player and not (Player:IsValid() and Player:IsPlayer())) then return end // Be sure, Player is not a NULL-Entity, or the server will crash! + + umsg.Start( "RcvEntityVarBeam_"..VarType, Player ) + umsg.Short( Index ) + umsg.String( Key ) + umsg[ NetworkFunction[VarType].SetFunction ]( Value ) + umsg.End() + + +end + +local function AddDelayedNetworkUpdate( VarType, Ent, Key, Value ) + if (Wire_FastOverlayTextUpdate) then + SendNetworkUpdate( VarType, Ent, Key, Value ) + elseif (Ent) and (VarType) then + DelayedUpdates[Ent] = DelayedUpdates[Ent] or {} + DelayedUpdates[Ent][ VarType ] = DelayedUpdates[Ent][ VarType ] or {} + DelayedUpdates[Ent][ VarType ][Key] = Value + DelayedUpdatesNum = DelayedUpdatesNum + 1 + + if (ExtraDelayedUpdates[Ent]) + and (ExtraDelayedUpdates[Ent][VarType]) + and (ExtraDelayedUpdates[Ent][VarType][Key]) then + ExtraDelayedUpdates[Ent][VarType][Key] = nil + end + end +end + +local function AddExtraDelayedNetworkUpdate( VarType, Ent, Key, Value, Player ) + if (Wire_FastOverlayTextUpdate) then + SendNetworkUpdate( VarType, Ent, Key, Value ) + elseif (Ent) and (VarType) and (Key) then + ExtraDelayedUpdates[Ent] = ExtraDelayedUpdates[Ent] or {} + ExtraDelayedUpdates[Ent][VarType] = ExtraDelayedUpdates[Ent][VarType] or {} + ExtraDelayedUpdates[Ent][VarType][Key] = ExtraDelayedUpdates[Ent][VarType][Key] or {} + ExtraDelayedUpdates[Ent][VarType][Key].Value = Value + ExtraDelayedUpdates[Ent][VarType][Key].Player = Player + --ExtraDelayedUpdatesNum = ExtraDelayedUpdatesNum + 1 + end +end + + + +// +// make all the ent.Get/SetNetworkedBeamVarCrap +// +local function AddNetworkFunctions( name, SetFunction, GetFunction, Default ) + + NetworkFunction[ name ] = {} + NetworkFunction[ name ].SetFunction = SetFunction + NetworkFunction[ name ].GetFunction = GetFunction + + // SetNetworkedBlah + meta[ "SetNetworkedBeam" .. name ] = function ( self, key, value, urgent ) + + key = tostring(key) + + // The same - don't waste our time. + if ( value == GetNetworkTable( self, name )[ key ] ) then return end + + // Clients can set this too, but they should only really be setting it + // when they expect the exact same result coming over the wire (ie prediction) + GetNetworkTable( self, name )[key] = value + + if ( SERVER ) then + + local Index = self:EntIndex() + if (Index <= 0) then return end + + if ( urgent ) then + SendNetworkUpdate( name, Index, key, value ) + else + AddDelayedNetworkUpdate( name, Index, key, value ) + end + + end + + end + + meta[ "SetNWB" .. name ] = meta[ "SetNetworkedBeam" .. name ] + + // GetNetworkedBlah + meta[ "GetNetworkedBeam" .. name ] = function ( self, key, default ) + + key = tostring(key) + + local out = GetNetworkTable( self, name )[ key ] + if ( out != nil ) then return out end + if ( default == nil ) then return Default end + //default = default or Default + + return default + + end + + meta[ "GetNWB" .. name ] = meta[ "GetNetworkedBeam" .. name ] + + + // SetGlobalBlah + _G[ "SetGlobalBeam"..name ] = function ( key, value, urgent ) + + key = tostring(key) + + if ( value == GetNetworkTable( "G", name )[key] ) then return end + GetNetworkTable( "G", name )[key] = value + + if ( SERVER ) then + if ( urgent ) then + SendNetworkUpdate( name, -1, key, value ) + else + AddDelayedNetworkUpdate( name, -1, key, value ) + end + end + + end + + + // GetGlobalBlah + _G[ "GetGlobalBeam"..name ] = function ( key ) + + key = tostring(key) + + local out = GetNetworkTable( "G", name )[key] + if ( out != nil ) then return out end + + return Default + + end + + + if ( SERVER ) then + // Pool the name of the function. + // Makes it send a number representing the string rather than the string itself. + // Only do this with strings that you send quite a bit and always stay the same. + umsg.PoolString( "RcvEntityBeamVar_"..name ) + end + + // Client Receive Function + if ( CLIENT ) then + + local function RecvFunc( m ) + local EntIndex = m:ReadShort() + local Key = m:ReadString() + local Value = m[GetFunction]( m ) + + local IndexKey + if ( EntIndex <= 0 ) then + IndexKey = "G" + else + IndexKey = Entity( EntIndex ) + // No entity yet - store using entindex + if ( IndexKey == NULL ) then IndexKey = EntIndex end + end + GetNetworkTable( IndexKey, name )[Key] = Value + end + usermessage.Hook( "RcvEntityVarBeam_"..name, RecvFunc ) + + end + +end + +AddNetworkFunctions( "Vector", "Vector", "ReadVector", Vector_Default ) +AddNetworkFunctions( "Angle", "Angle", "ReadAngle", Angle_Default ) +AddNetworkFunctions( "Float", "Float", "ReadFloat", 0 ) +AddNetworkFunctions( "Int", "Short", "ReadShort", 0 ) +AddNetworkFunctions( "Entity", "Entity", "ReadEntity", NULL ) +AddNetworkFunctions( "Bool", "Bool", "ReadBool", false ) +AddNetworkFunctions( "String", "String", "ReadString", "" ) + + + + + + +// +// We want our networked vars to save don't we? Yeah - we do - stupid. +// +local function Save( save ) + // Remove baggage + for k, v in pairs(NetworkVars) do + if ( k == NULL ) then + NetworkVars[k] = nil + end + end + saverestore.WriteTable( NetworkVars, save ) +end +local function Restore( restore ) + NetworkVars = saverestore.ReadTable( restore ) + //PrintTable(NetworkVars) +end +saverestore.AddSaveHook( "EntityNetworkedBeamVars", Save ) +saverestore.AddRestoreHook( "EntityNetworkedBeamVars", Restore ) + +if (SERVER) then +// +// send the netvars queried in the stack +// +local NextBeamVarsDelayedSendTime = 0 +local NormalOpMode = true +local function NetworkVarsSend() + if (CurTime() >= NextBeamVarsDelayedSendTime) then + + if (NormalOpMode) and (DelayedUpdatesNum > 75) then + Msg("========BeamVars leaving NormalOpMode | "..DelayedUpdatesNum.."\n") + NormalOpMode = false + elseif (!NormalOpMode) and (DelayedUpdatesNum < 50) then + Msg("========BeamVars returning NormalOpMode | "..DelayedUpdatesNum.."\n") + NormalOpMode = true + end + + + if (DelayedUpdatesNum > 0) then + for Index, a in pairs(DelayedUpdates) do + for VarType, b in pairs(a) do + for Key, Value in pairs(b) do + SendNetworkUpdate( VarType, Index, Key, Value ) + end + end + end + DelayedUpdatesNum = 0 + DelayedUpdates = {} + end + + //we send one entity's ExtraDelayedUpdates each tick + local i = 0 + for Index, a in pairs(ExtraDelayedUpdates) do + for VarType, b in pairs(a) do + for Key, data in pairs(b) do + SendNetworkUpdate( VarType, Index, Key, data.Value, data.Player ) + end + end + ExtraDelayedUpdates[Index] = nil + i = i + 1 + if (i >= 2) then + break + end + end + + if (!NormalOpMode) then + NextBeamVarsDelayedSendTime = CurTime() + .25 + else + NextBeamVarsDelayedSendTime = CurTime() + .1 + end + + end +end +hook.Add("Think", "NetBeamLib_Think", NetworkVarsSend) + + +// +// Send a full update to player that have just joined the server +// +local function FullUpdateEntityNetworkVars( ply ) + --Msg("==sending netbeamvar var data to "..tostring(ply).."\n") + --Msg("\n===Size: "..table.Count(NetworkVars).."\n") + for Ent, EntTable in pairs(NetworkVars) do + for Type, TypeTable in pairs(EntTable) do + for Key, Value in pairs(TypeTable) do + local Index = Ent + if ( type(Ent) != "string" ) then + Index = Ent:EntIndex() + end + --SendNetworkUpdate( Type, Index , Key, Value, ply ) + AddExtraDelayedNetworkUpdate( Type, Index , Key, Value, ply ) + end + end + + end +end +local function DelayedFullUpdateEntityNetworkVars( ply ) + --Msg("==starting timer for sending var data too "..tostring(ply).."\n") + timer.Simple(4, FullUpdateEntityNetworkVars, ply) +end +hook.Add( "PlayerInitialSpawn", "FullUpdateEntityNetworkBeamVars", DelayedFullUpdateEntityNetworkVars ) +concommand.Add( "networkbeamvars_SendAll", DelayedFullUpdateEntityNetworkVars ) +concommand.Add( "networkbeamvars_SendAllNow", FullUpdateEntityNetworkVars ) + + +// +// Listen out for dead entities so we can remove their vars +// +local function NetworkVarsCleanup( ent ) + NetworkVars[ ent ] = nil +end +hook.Add( "EntityRemoved", "NetworkBeamVarsCleanup", NetworkVarsCleanup ) + + +end //end SERVER olny + + + +Msg("======== Beam NetVars Lib v"..BeamNetVars.Version.." Installed ========\n") diff --git a/lua/wire/UpdateCheck.lua b/lua/wire/UpdateCheck.lua new file mode 100644 index 0000000000..d806d2e94b --- /dev/null +++ b/lua/wire/UpdateCheck.lua @@ -0,0 +1,84 @@ +-- $Rev: 1621 $ +-- $LastChangedDate: 2009-09-03 15:24:56 -0700 (Thu, 03 Sep 2009) $ +-- $LastChangedBy: TomyLobo $ + + +local rss_url = "http://www.wiremod.com:8060/changelog/~rss,feedmax=1/Wiremod/wire/rss.xml" + + +WireVersion = "1621" --manual revision, change this value to the revision-to-be once changes are committed +WireVersion = WireVersion .. " (exported)" -- leave this alone, it's to differentiate SVN checkouts from SVN Exported or downloaded versions of wire when a player types "wire_PrintVersion" +if file.Exists("../lua/wire/.svn/entries") then + WireVersion = tonumber(string.Explode("\n", file.Read( "../lua/wire/.svn/entries"))[4]) --get svn revision, stolen from ULX + SVNver = WireVersion -- this is for the sv_tags changing function at the bottom of WireLib.lua +end +WireLib.Version = WireVersion + + +if SERVER then + local function initplayer(pl) + umsg.Start("wire_rev", pl) + umsg.Short(WireVersion) + umsg.End() + end + hook.Add( "PlayerInitialSpawn", "WirePlayerInitSpawn", initplayer ) + + local function PrintWireVersion(pl,cmd,args) + if (pl and pl:IsValid()) then + pl:PrintMessage(HUD_PRINTTALK, "Wire revision: "..WireVersion) + else + print("Wire revision: "..WireVersion) + end + end + concommand.Add( "Wire_PrintVersion", PrintWireVersion ) + + MsgN("================================\n=== Wire "..WireVersion.." Installed ===\n================================") +end + +if CLIENT then + WireVersionLocal = WireVersion + local function initplayer(um) + WIRE_SERVER_INSTALLED = true + WireVersion = um:ReadShort() + MsgN("================================\n=== Wire revision: "..WireVersion.." ===\n=== Local Wire revision:"..WireVersion.." ===\n================================") + end + usermessage.Hook( "wire_rev", initplayer ) +end + + +local update_check_lbl + +-- http.Get Callback +local function CheckForUpdateCallback(contents, size) + local rev = string.match(contents, "http://www%.wiremod%.com:8060/changelog/Wiremod%?cs=(%d+)&csize=1") + if rev then + if tonumber(rev) > WireVersion then + update_check_lbl:SetText("There's a newer rev of wire!\nYou have: "..WireVersion.."\n"..rev.." is current.") + else + update_check_lbl:SetText("You have: "..WireVersion.."\nYour Wire is up to date!") + end + else + update_check_lbl:SetText("Unable to contact SVN server.\nD:") + end + update_check_lbl:GetParent():PerformLayout() +end + +local function CheckForUpdateCP(Panel) + local update_check_btn = vgui.Create("DButton") + update_check_btn:SetText("Check for Update") + update_check_btn.DoClick = function(button) + http.Get(rss_url, "", CheckForUpdateCallback) + button:SetDisabled(true) + button:SetText("Checking....") + end + Panel:AddItem(update_check_btn) + + update_check_lbl = vgui.Create("DLabel") + update_check_lbl:SetText("") + update_check_lbl:SetAutoStretchVertical(true) + Panel:AddItem(update_check_lbl) +end + +hook.Add("PopulateToolMenu", "AddWireAdminUpdateCheck", function() + spawnmenu.AddToolMenuOption("Wire", "Administration", "WireAdminUpdateCheck", "Check For Update", "", "", CheckForUpdateCP, {}) +end) diff --git a/lua/wire/WireGates.lua b/lua/wire/WireGates.lua new file mode 100644 index 0000000000..d4070f1e3b --- /dev/null +++ b/lua/wire/WireGates.lua @@ -0,0 +1,4372 @@ +--*********************************************************** +-- Gate Action Functions Module +-- define all gate actions here +-- TODO: loader function to grab external gate action defines +--*********************************************************** +GateActions = {} + + + + +--*********************************************************** +-- Arithmetic Gates +--*********************************************************** +GateActions["increment"] = { + group = "Arithmetic", + name = "Increment", + inputs = { "A", "Clk", "Reset" }, + output = function(gate, A, Clk, Reset) + local clk = ( Clk > 0 ) + local reset = ( Reset > 0 ) + + if ( gate.PrevValue ~= clk ) then + gate.PrevValue = clk + if ( clk ) then + if ( gate.Memory == nil ) then + gate.Memory = A + else + gate.Memory = gate.Memory + 1 + end + end + end + + if( gate.PrevReset ~= reset ) then + gate.PrevReset = reset + if ( reset ) then + gate.Memory = 0 + end + end + + return gate.Memory + end, + label = function(Out, A) + return "(" .. A .. " + LastNum)++ = " .. Out + end +} + +GateActions["identity"] = { + group = "Arithmetic", + name = "Identity (No change)", + inputs = { "A" }, + output = function(gate, A) + return A + end, + label = function(Out, A) + return A.." = "..Out + end +} + +GateActions["negate"] = { + group = "Arithmetic", + name = "Negate", + inputs = { "A" }, + output = function(gate, A) + return -A + end, + label = function(Out, A) + return "-"..A.." = "..Out + end +} + +GateActions["inverse"] = { + group = "Arithmetic", + name = "Inverse", + inputs = { "A" }, + output = function(gate, A) + if (A) and (math.abs(A) >= 0.0001) then return 1/A end + return 0 + end, + label = function(Out, A) + return "1/"..A.." = "..Out + end +} + +GateActions["sqrt"] = { + group = "Arithmetic", + name = "Square Root", + inputs = { "A" }, + output = function(gate, A) + return math.sqrt(math.abs(A)) -- Negatives are possible, use absolute value + end, + label = function(Out, A) + --[[if ( A < 0 ) then + return "sqrt("..A..") = i"..Out -- Display as imaginary if A is negative + else]] + return "sqrt("..A..") = "..Out + --end + end +} + +GateActions["log"] = { + group = "Arithmetic", + name = "Log", + inputs = { "A" }, + output = function(gate, A) + return math.log(A) + end, + label = function(Out, A) + return "log("..A..") = "..Out + end +} + +GateActions["log10"] = { + group = "Arithmetic", + name = "Log 10", + inputs = { "A" }, + output = function(gate, A) + return math.log10(A) + end, + label = function(Out, A) + return "log10("..A..") = "..Out + end +} + +GateActions["abs"] = { + group = "Arithmetic", + name = "Absolute", + inputs = { "A" }, + output = function(gate, A) + return math.abs(A) + end, + label = function(Out, A) + return "abs("..A..") = "..Out + end +} + +GateActions["sgn"] = { + group = "Arithmetic", + name = "Sign (-1,0,1)", + inputs = { "A" }, + output = function(gate, A) + if (A > 0) then return 1 end + if (A < 0) then return -1 end + return 0 + end, + label = function(Out, A) + return "sgn("..A..") = "..Out + end +} + +GateActions["floor"] = { + group = "Arithmetic", + name = "Floor (Round down)", + inputs = { "A" }, + output = function(gate, A) + return math.floor(A) + end, + label = function(Out, A) + return "floor("..A..") = "..Out + end +} + +GateActions["round"] = { + group = "Arithmetic", + name = "Round", + inputs = { "A" }, + output = function(gate, A) + return math.Round(A) + end, + label = function(Out, A) + return "round("..A..") = "..Out + end +} + +GateActions["ceil"] = { + group = "Arithmetic", + name = "Ceiling (Round up)", + inputs = { "A" }, + output = function(gate, A) + return math.ceil(A) + end, + label = function(Out, A) + return "ceil("..A..") = "..Out + end +} + +GateActions["+"] = { + group = "Arithmetic", + name = "Add", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + local result = 0 + for k,v in ipairs(arg) do + if (v) then result = result+v end + end + return result + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." + " end + end + return string.sub(txt, 1, -4).." = "..Out + end +} + +GateActions["-"] = { + group = "Arithmetic", + name = "Subtract", + inputs = { "A", "B" }, + colors = { Color(255, 0, 0, 255), Color(0, 0, 255, 255) }, + output = function(gate, A, B) + return A-B + end, + label = function(Out, A, B) + return A.." - "..B.." = "..Out + end +} + +GateActions["*"] = { + group = "Arithmetic", + name = "Multiply", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + local result = 1 + for k,v in ipairs(arg) do + if (v) then result = result*v end + end + return result + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." * " end + end + return string.sub(txt, 1, -4).." = "..Out + end +} + +GateActions["/"] = { + group = "Arithmetic", + name = "Divide", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (math.abs(B) < 0.0001) then return 0 end + return A/B + end, + label = function(Out, A, B) + return A.." / "..B.." = "..Out + end +} + +GateActions["%"] = { + group = "Arithmetic", + name = "Modulo", + inputs = { "A", "B" }, + output = function(gate, A, B) + if ( B == 0 ) then return 0 end + return math.fmod(A,B) + end, + label = function(Out, A, B) + return A.." % "..B.." = "..Out + end +} + +GateActions["rand"] = { + group = "Arithmetic", + name = "Random", + inputs = { "A", "B" }, + timed = true, + output = function(gate, A, B) + return math.random()*(B-A)+A + end, + label = function(Out, A, B) + return "random("..A.." - "..B..") = "..Out + end +} + +GateActions["PI"] = { + group = "Arithmetic", + name = "PI", + inputs = { }, + output = function(gate) + return math.pi + end, + label = function(Out) + return "PI = "..Out + end +} + +GateActions["exp"] = { + group = "Arithmetic", + name = "Exp", + inputs = { "A" }, + output = function(gate, A) + return math.exp(A) + end, + label = function(Out, A) + return "exp("..A..") = "..Out + end +} + +GateActions["pow"] = { + group = "Arithmetic", + name = "Exponential Powers", + inputs = { "A", "B" }, + output = function(gate, A, B) + return math.pow(A, B) + end, + label = function(Out, A, B) + return "pow("..A..", "..B..") = "..Out + end +} + +GateActions["and/add"] = { + group = "Arithmetic", + name = "And/Add", + inputs = { "A", "B"}, + output = function(gate, A, B) + if ((A) and (A <= 0)) or ((B) and (B <= 0)) then return 0 end + return A+B + end, + label = function(Out, A, B) + return A.." and/and "..B.." = "..Out + end +} + +GateActions["Percent"] = { + group = "Arithmetic", + name = "Percent", + inputs = { "Value", "Max" }, + compact_inputs = 2, + output = function(gate, Value, Max) + if (math.abs(Max) < 0.0001) then return 0 end + return Value / Max * 100 + end, + label = function(Out, Value, Max) + return Value.." / "..Max.." * 100 = "..Out.."%" + end +} + +GateActions["Delta"] = { + group = "Arithmetic", + name = "Delta", + inputs = { "A" }, + output = function(gate, A) + gate.PrevValue = gate.PrevValue or 0 + local delta = A - gate.PrevValue + gate.PrevValue = A + return delta + end, + reset = function(gate) + gate.PrevValue = 0 + end, + label = function(Out, A) + return "Delta("..A..") " + end +} + +GateActions["Delta360"] = { + group = "Arithmetic", + name = "Delta (Rectified)", + inputs = { "A" }, + output = function(gate, A) + gate.PrevValue = gate.PrevValue or 0 + local delta = A - gate.PrevValue + gate.PrevValue = A + return ( math.fmod( (math.fmod( delta, 360 ) + 540 ), 360 ) - 180 ) + end, + reset = function(gate) + gate.PrevValue = 0 + end, + label = function(Out, A) + return "Delta("..A..") " + end +} + +GateActions["Average"] = { + group = "Arithmetic", + name = "Average", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + vals = 0 + value = 0 + -- function doavg (argument) + -- vals += 1 + -- value += argument + -- end + --table.foreach(arg, doavg) + for k,v in ipairs(arg) do + vals = vals + 1 + value = value + v + end + return value / vals + --Msg("Rape!") + --Msg("It was "..value.."!") + --return 34 + end, + label = function(Out, ...) + vals = 0 + value = 0 + message = "(" + for k,v in ipairs(arg) do + vals = vals + 1 + --value = value + v + message = message .. v .. " + " + end + message = string.sub(message,1,-4) + message = message .. ") / " .. vals .. " = " .. Out + return message + --return "I ARE FAIL" + end +} + + +GateActions["increment/decrement"] = { + group = "Arithmetic", + name = "Increment/Decrement", + inputs = { "A", "Increment", "Decrement", "Reset" }, + output = function(gate, A, Increment, Decrement, Reset) + local increment = ( Increment > 0 ) + local decrement = ( Decrement > 0 ) + local reset = (Reset > 0) + + if ( gate.PrevValue ~= increment ) then + gate.PrevValue = increment + if ( increment ) then + gate.Memory = (gate.Memory or 0) + A + end + end + + if ( gate.PrevValue ~= decrement ) then + gate.PrevValue = decrement + if ( decrement ) then + gate.Memory = (gate.Memory or 0) - A + end + end + + if( gate.PrevReset ~= reset ) then + gate.PrevReset = reset + if ( reset ) then + gate.Memory = 0 + end + end + + return gate.Memory + end, + label = function(Out, A) + return "(" .. A .. " +/- LastNum) = " .. Out + end +} + + + + +--*********************************************************** +-- Comparison Gates +--*********************************************************** +GateActions["="] = { + group = "Comparison", + name = "Equal", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (math.abs(A-B) < 0.001) then return 1 end + return 0 + end, + label = function(Out, A, B) + return A.." == "..B.." = "..Out + end +} + +GateActions["!="] = { + group = "Comparison", + name = "Not Equal", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (math.abs(A-B) < 0.001) then return 0 end + return 1 + end, + label = function(Out, A, B) + return A.." ~= "..B.." = "..Out + end +} + +GateActions["<"] = { + group = "Comparison", + name = "Less Than", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (A < B) then return 1 end + return 0 + end, + label = function(Out, A, B) + return A.." < "..B.." = "..Out + end +} + +GateActions[">"] = { + group = "Comparison", + name = "Greater Than", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (A > B) then return 1 end + return 0 + end, + label = function(Out, A, B) + return A.." > "..B.." = "..Out + end +} + +GateActions["<="] = { + group = "Comparison", + name = "Less or Equal", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (A <= B) then return 1 end + return 0 + end, + label = function(Out, A, B) + return A.." <= "..B.." = "..Out + end +} + +GateActions[">="] = { + group = "Comparison", + name = "Greater or Equal", + inputs = { "A", "B" }, + output = function(gate, A, B) + if (A >= B) then return 1 end + return 0 + end, + label = function(Out, A, B) + return A.." >= "..B.." = "..Out + end +} + +GateActions["inrangei"] = { + group = "Comparison", + name = "Is In Range (Inclusive)", + inputs = { "Min", "Max", "Value" }, + output = function(gate, Min, Max, Value) + if (Max < Min) then + local temp = Max + Max = Min + Min = temp + end + if ((Value >= Min) && (Value <= Max)) then return 1 end + return 0 + end, + label = function(Out, Min, Max, Value) + return Min.." <= "..Value.." <= "..Max.." = "..Out + end +} + +GateActions["inrangee"] = { + group = "Comparison", + name = "Is In Range (Exclusive)", + inputs = { "Min", "Max", "Value" }, + output = function(gate, Min, Max, Value) + if (Max < Min) then + local temp = Max + Max = Min + Min = temp + end + if ((Value > Min) && (Value < Max)) then return 1 end + return 0 + end, + label = function(Out, Min, Max, Value) + return Min.." < "..Value.." < "..Max.." = "..Out + end +} + + + + +--*********************************************************** +-- Logic Gates +--*********************************************************** +GateActions["not"] = { + group = "Logic", + name = "Not (Invert)", + inputs = { "A" }, + output = function(gate, A) + if (A > 0) then return 0 end + return 1 + end, + label = function(Out, A) + return "not "..A.." = "..Out + end +} + +GateActions["and"] = { + group = "Logic", + name = "And (All)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + for k,v in ipairs(arg) do + if (v) and (v <= 0) then return 0 end + end + return 1 + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." and " end + end + return string.sub(txt, 1, -6).." = "..Out + end +} + +GateActions["or"] = { + group = "Logic", + name = "Or (Any)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + for k,v in ipairs(arg) do + if (v) and (v > 0) then return 1 end + end + return 0 + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." or " end + end + return string.sub(txt, 1, -5).." = "..Out + end +} + +GateActions["xor"] = { + group = "Logic", + name = "Exclusive Or (Odd)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + local result = 0 + for k,v in ipairs(arg) do + if (v) and (v > 0) then result = (1-result) end + end + return result + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." xor " end + end + return string.sub(txt, 1, -6).." = "..Out + end +} + +GateActions["nand"] = { + group = "Logic", + name = "Not And (Not All)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + for k,v in ipairs(arg) do + if (v) and (v <= 0) then return 1 end + end + return 0 + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." nand " end + end + return string.sub(txt, 1, -7).." = "..Out + end +} + +GateActions["nor"] = { + group = "Logic", + name = "Not Or (None)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + for k,v in ipairs(arg) do + if (v) and (v > 0) then return 0 end + end + return 1 + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." nor " end + end + return string.sub(txt, 1, -6).." = "..Out + end +} + +GateActions["xnor"] = { + group = "Logic", + name = "Exclusive Not Or (Even)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + local result = 1 + for k,v in ipairs(arg) do + if (v) and (v > 0) then result = (1-result) end + end + return result + end, + label = function(Out, ...) + local txt = "" + for k,v in ipairs(arg) do + if (v) then txt = txt..v.." xnor " end + end + return string.sub(txt, 1, -7).." = "..Out + end +} + + + + +--*********************************************************** +-- Memory Gates +--*********************************************************** +GateActions["latch"] = { + group = "Memory", + name = "Latch (Edge triggered)", + inputs = { "Data", "Clk" }, + output = function(gate, Data, Clk) + local clk = (Clk > 0) + if (gate.PrevValue ~= clk) then + gate.PrevValue = clk + if (clk) then + gate.LatchStore = Data + end + end + return gate.LatchStore or 0 + end, + reset = function(gate) + gate.LatchStore = 0 + gate.PrevValue = nil + end, + label = function(Out, Data, Clk) + return "Latch Data:"..Data.." Clock:"..Clk.." = "..Out + end +} + +GateActions["dlatch"] = { + group = "Memory", + name = "D-Latch", + inputs = { "Data", "Clk" }, + output = function(gate, Data, Clk) + if (Clk > 0) then + gate.LatchStore = Data + end + return gate.LatchStore or 0 + end, + reset = function(gate) + gate.LatchStore = 0 + end, + label = function(Out, Data, Clk) + return "D-Latch Data:"..Data.." Clock:"..Clk.." = "..Out + end +} + +GateActions["srlatch"] = { + group = "Memory", + name = "SR-Latch", + inputs = { "S", "R" }, + output = function(gate, S, R) + if (S > 0) and (R <= 0) then + gate.LatchStore = 1 + elseif (S <= 0) and (R > 0) then + gate.LatchStore = 0 + end + return gate.LatchStore + end, + reset = function(gate) + gate.LatchStore = 0 + end, + label = function(Out, S, R) + return "S:"..S.." R:"..R.." == "..Out + end +} + +GateActions["toggle"] = { + group = "Memory", + name = "Toggle (Edge triggered)", + inputs = { "Clk", "OnValue", "OffValue" }, + output = function(gate, Clk, OnValue, OffValue) + local clk = (Clk > 0) + if (gate.PrevValue ~= clk) then + gate.PrevValue = clk + if (clk) then + gate.LatchStore = (not gate.LatchStore) + end + end + + if (gate.LatchStore) then return OnValue end + return OffValue + end, + reset = function(gate) + gate.LatchStore = 0 + gate.PrevValue = nil + end, + label = function(Out, Clk, OnValue, OffValue) + return "Off:"..OffValue.." On:"..OnValue.." Clock:"..Clk.." = "..Out + end +} + +GateActions["wom4"] = { + group = "Memory", + name = "Write Only Memory(4 store)", + inputs = { "Clk", "AddrWrite", "Data" }, + output = function( gate, Clk, AddrWrite, Data ) + AddrWrite = math.floor(tonumber(AddrWrite)) + if ( Clk > 0 ) then + if ( AddrWrite >= 0 ) and ( AddrWrite < 4 ) then + gate.LatchStore[AddrWrite] = Data + end + end + return 0 + end, + reset = function( gate ) + gate.LatchStore = {} + for i = 0, 3 do + gate.LatchStore[i] = 0 + end + end, + label = function() + return "Write Only Memory - 4 store" + end +} + +GateActions["ram8"] = { + group = "Memory", + name = "RAM(8 store)", + inputs = { "Clk", "AddrRead", "AddrWrite", "Data", "Reset" }, + output = function(gate, Clk, AddrRead, AddrWrite, Data, Reset ) + if (Reset > 0) then + gate.LatchStore = {} + end + + AddrRead = math.floor(tonumber(AddrRead)) + AddrWrite = math.floor(tonumber(AddrWrite)) + + if (Clk > 0) then + if (AddrWrite >= 0) and (AddrWrite < 8) then + gate.LatchStore[AddrWrite] = Data + end + end + + if (AddrRead < 0) or (AddrRead >= 8) then return 0 end + + return gate.LatchStore[AddrRead] or 0 + end, + reset = function(gate) + gate.LatchStore = {} + end, + label = function(Out, Clk, AddrRead, AddrWrite, Data, Reset) + return "WriteAddr:"..AddrWrite.." Data:"..Data.." Clock:"..Clk.." Reset:"..Reset.. + "\nReadAddr:"..AddrRead.." = "..Out + end, + ReadCell = function(dummy,gate,Address) + if (Address < 0) || (Address >= 8) then + return 0 + else + return gate.LatchStore[Address] or 0 + end + end, + WriteCell = function(dummy,gate,Address,value) + if (Address < 0) || (Address >= 8) then + return false + else + gate.LatchStore[Address] = value + return true + end + end +} + +GateActions["ram64"] = { + group = "Memory", + name = "RAM(64 store)", + inputs = { "Clk", "AddrRead", "AddrWrite", "Data", "Reset" }, + output = function(gate, Clk, AddrRead, AddrWrite, Data, Reset ) + if (Reset > 0) then + gate.LatchStore = {} + end + + AddrRead = math.floor(tonumber(AddrRead)) + AddrWrite = math.floor(tonumber(AddrWrite)) + if (Clk > 0) then + if (AddrWrite < 64) then + gate.LatchStore[AddrWrite] = Data + end + end + return gate.LatchStore[AddrRead] or 0 + end, + reset = function(gate) + gate.LatchStore = {} + end, + label = function(Out, Clk, AddrRead, AddrWrite, Data, Reset) + return "WriteAddr:"..AddrWrite.." Data:"..Data.." Clock:"..Clk.." Reset:"..Reset.. + "\nReadAddr:"..AddrRead.." = "..Out + end, + ReadCell = function(dummy,gate,Address) + if (Address < 0) || (Address >= 64) then + return 0 + else + return gate.LatchStore[Address] or 0 + end + end, + WriteCell = function(dummy,gate,Address,value) + if (Address < 0) || (Address >= 64) then + return false + else + gate.LatchStore[Address] = value + return true + end + end +} + +GateActions["ram32k"] = { + group = "Memory", + name = "RAM(32kb)", + inputs = { "Clk", "AddrRead", "AddrWrite", "Data", "Reset" }, + output = function(gate, Clk, AddrRead, AddrWrite, Data, Reset ) + if (Reset > 0) then + gate.LatchStore = {} + end + + AddrRead = math.floor(tonumber(AddrRead)) + AddrWrite = math.floor(tonumber(AddrWrite)) + if (Clk > 0) then + if (AddrWrite < 32768) then + gate.LatchStore[AddrWrite] = Data + end + end + return gate.LatchStore[AddrRead] or 0 + end, + reset = function(gate) + gate.LatchStore = {} + end, + label = function(Out, Clk, AddrRead, AddrWrite, Data, Reset ) + return "WriteAddr:"..AddrWrite.." Data:"..Data.." Clock:"..Clk.." Reset:"..Reset.. + "\nReadAddr:"..AddrRead.." = "..Out + end, + ReadCell = function(dummy,gate,Address) + if (Address < 0) || (Address >= 32768) then + return 0 + else + return gate.LatchStore[Address] or 0 + end + end, + WriteCell = function(dummy,gate,Address,value) + if (Address < 0) || (Address >= 32768) then + return false + else + gate.LatchStore[Address] = value + return true + end + end +} + +GateActions["ram128k"] = { + group = "Memory", + name = "RAM(128kb)", + inputs = { "Clk", "AddrRead", "AddrWrite", "Data", "Reset" }, + output = function(gate, Clk, AddrRead, AddrWrite, Data, Reset ) + if (Reset > 0) then + gate.LatchStore = {} + end + + AddrRead = math.floor(tonumber(AddrRead)) + AddrWrite = math.floor(tonumber(AddrWrite)) + if (Clk > 0) then + if (AddrWrite < 131072) then + gate.LatchStore[AddrWrite] = Data + end + end + return gate.LatchStore[AddrRead] or 0 + end, + reset = function(gate) + gate.LatchStore = {} + end, + label = function(Out, Clk, AddrRead, AddrWrite, Data) + return "WriteAddr:"..AddrWrite.." Data:"..Data.." Clock:"..Clk.. + "\nReadAddr:"..AddrRead.." = "..Out + end, + ReadCell = function(dummy,gate,Address) + if (Address < 0) || (Address >= 131072) then + return 0 + else + return gate.LatchStore[Address] or 0 + end + end, + WriteCell = function(dummy,gate,Address,value) + if (Address < 0) || (Address >= 131072) then + return false + else + gate.LatchStore[Address] = value + return true + end + end +} + +GateActions["ram64x64"] = { + group = "Memory", + name = "RAM(64x64 store)", + inputs = { "Clk", "AddrReadX", "AddrReadY", "AddrWriteX", "AddrWriteY", "Data", "Reset" }, + output = function(gate, Clk, AddrReadX, AddrReadY, AddrWriteX, AddrWriteY, Data, Reset ) + if (Reset > 0) then + gate.LatchStore = {} + end + + AddrReadX = math.floor(tonumber(AddrReadX)) + AddrReadY = math.floor(tonumber(AddrReadY)) + AddrWriteX = math.floor(tonumber(AddrWriteX)) + AddrWriteY = math.floor(tonumber(AddrWriteY)) + if (Clk > 0) then + if (AddrWriteX >= 0) and (AddrWriteX < 64) or (AddrWriteY >= 0) and (AddrWriteY < 64) then + gate.LatchStore[AddrWriteX + AddrWriteY*64] = Data + end + end + + if (AddrReadX < 0) or (AddrReadX >= 64) or (AddrReadY < 0) or (AddrReadY >= 64) then + return 0 + end + + return gate.LatchStore[AddrReadX + AddrReadY*64] or 0 + end, + reset = function(gate) + gate.LatchStore = {} + end, + label = function(Out, Clk, AddrReadX, AddrReadY, AddrWriteX, AddrWriteY, Data, Reset) + return "WriteAddr:"..AddrWriteX..", "..AddrWriteY.." Data:"..Data.." Clock:"..Clk.." Reset:"..Reset.. + "\nReadAddr:"..AddrReadX..", "..AddrReadY.." = "..Out + end, + ReadCell = function(dummy,gate,Address) + if (Address < 0) || (Address >= 4096) then + return 0 + else + return gate.LatchStore[Address] or 0 + end + end, + WriteCell = function(dummy,gate,Address,value) + if (Address < 0) || (Address >= 4096) then + return false + else + gate.LatchStore[Address] = value + return true + end + end +} + +GateActions["udcounter"] = { + group = "Memory", + name = "Up/Down Counter", + inputs = { "Increment", "Decrement", "Clk", "Reset"}, + output = function(gate, Inc, Dec, Clk, Reset) + local lInc = (Inc > 0) + local lDec = (Dec > 0) + local lClk = (Clk > 0) + local lReset = (Reset > 0) + if ((gate.PrevInc ~= lInc || gate.PrevDec ~= lDec || gate.PrevClk ~= lClk) && lClk) then + if (lInc) and (!lDec) and (!lReset) then + gate.countStore = (gate.countStore or 0) + 1 + elseif (!lInc) and (lDec) and (!lReset) then + gate.countStore = (gate.countStore or 0) - 1 + end + gate.PrevInc = lInc + gate.PrevDec = lDec + gate.PrevClk = lClk + end + if (lReset) then + gate.countStore = 0 + end + return gate.countStore + end, + label = function(Out, Inc, Dec, Clk, Reset) + return "Increment:"..Inc.." Decrement:"..Dec.." Clk:"..Clk.." Reset:"..Reset.." = "..Out + end +} + +GateActions["togglewhile"] = { + group = "Memory", + name = "Toggle While(Edge triggered)", + inputs = { "Clk", "OnValue", "OffValue", "While" }, + output = function(gate, Clk, OnValue, OffValue, While) + local clk = (Clk > 0) + + if (While <= 0) then + clk = false + gate.LatchStore = false + end + + if (gate.PrevValue ~= clk) then + gate.PrevValue = clk + if (clk) then + gate.LatchStore = (not gate.LatchStore) + end + end + + if (gate.LatchStore) then return OnValue end + return OffValue + end, + reset = function(gate) + gate.LatchStore = 0 + gate.PrevValue = nil + end, + label = function(Out, Clk, OnValue, OffValue, While) + return "Off:"..OffValue.." On:"..OnValue.." Clock:"..Clk.." While:"..While.." = "..Out + end +} + + + + +--*********************************************************** +-- Selection Gates +--*********************************************************** +GateActions["min"] = { + group = "Selection", + name = "Minimum (Smallest)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + return math.min(unpack(arg)) + end, + label = function(Out, ...) + local txt = "min(" + for k,v in ipairs(arg) do + if (v) then txt = txt..v..", " end + end + return string.sub(txt, 1, -3)..") = "..Out + end +} + +GateActions["max"] = { + group = "Selection", + name = "Maximum (Largest)", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + compact_inputs = 2, + output = function(gate, ...) + return math.max(unpack(arg)) + end, + label = function(Out, ...) + local txt = "max(" + for k,v in ipairs(arg) do + if (v) then txt = txt..v..", " end + end + return string.sub(txt, 1, -3)..") = "..Out + end +} + +GateActions["minmax"] = { + group = "Selection", + name = "Value Range", + inputs = { "Min", "Max", "Value" }, + output = function(gate, Min, Max, Value) + local temp = Min + if Min > Max then + Min = Max + Max = temp + end + if Value < Min then return Min end + if Value > Max then return Max end + return Value + end, + label = function(Out, Min, Max, Value) + local temp = Min + if Min > Max then + Min = Max + Max = temp + end + return "Min: "..Min.." Max: "..Max.." Value: "..Value.." = "..Out + end +} + +GateActions["if"] = { + group = "Selection", + name = "If Then Else", + inputs = { "A", "B", "C" }, + output = function(gate, A, B, C) + if (A) and (A > 0) then return B end + return C + end, + label = function(Out, A, B, C) + return "if "..A.." then "..B.." else "..C.." = "..Out + end +} + +GateActions["select"] = { + group = "Selection", + name = "Select (Choice)", + inputs = { "Choice", "A", "B", "C", "D", "E", "F", "G", "H" }, + output = function(gate, Choice, ...) + local idx = math.floor(Choice) + if (idx > 0) and (idx <= 8) then + return arg[idx] + end + + return 0 + end, + label = function(Out, Choice) + return "Select Choice:"..Choice.." Out:"..Out + end +} + +GateActions["router"] = { + group = "Selection", + name = "Router", + inputs = { "Path", "Data" }, + outputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + output = function(gate, Path, Data) + local result = { 0, 0, 0, 0, 0, 0, 0, 0 } + + local idx = math.floor(Path) + if (idx > 0) and (idx <= 8) then + result[idx] = Data + end + + return unpack(result) + end, + label = function(Out, Path, Data) + return "Router Path:"..Path.." Data:"..Data + end +} + +local SegmentInfo = { + None = { 0, 0, 0, 0, 0, 0, 0 }, + [0] = { 1, 1, 1, 1, 1, 1, 0 }, + [1] = { 0, 1, 1, 0, 0, 0, 0 }, + [2] = { 1, 1, 0, 1, 1, 0, 1 }, + [3] = { 1, 1, 1, 1, 0, 0, 1 }, + [4] = { 0, 1, 1, 0, 0, 1, 1 }, + [5] = { 1, 0, 1, 1, 0, 1, 1 }, + [6] = { 1, 0, 1, 1, 1, 1, 1 }, + [7] = { 1, 1, 1, 0, 0, 0, 0 }, + [8] = { 1, 1, 1, 1, 1, 1, 1 }, + [9] = { 1, 1, 1, 1, 0, 1, 1 }, +} + +GateActions["7seg"] = { + group = "Selection", + name = "7 Segment Decoder", + inputs = { "A", "Clear" }, + outputs = { "A", "B", "C", "D", "E", "F", "G" }, + output = function(gate, A, Clear) + if (Clear > 0) then return unpack(SegmentInfo.None) end + + local idx = math.fmod(math.abs(math.floor(A)), 10) + if idx > #SegmentInfo then return unpack(SegmentInfo.None) end + return unpack(SegmentInfo[idx]) -- same as: return SegmentInfo[idx][1], SegmentInfo[idx][2], ... + end, + label = function(Out, A) + return "7-Seg In:" .. A .. " Out:" .. Out.A .. Out.B .. Out.C .. Out.D .. Out.E .. Out.F .. Out.G + end +} + +GateActions["timedec"] = { + group = "Selection", + name = "Time/Date decoder", + inputs = { "Time", "Date" }, + outputs = { "Hours","Minutes","Seconds","Year","Day" }, + output = function(gate, Time, Date) + return math.floor(Time / 3600),math.floor(Time / 60) % 60,math.floor(Time) % 60,math.floor(Date / 366),math.floor(Date) % 366 + end, + label = function(Out, A) + return "Date decoder" + end +} + + +--*********************************************************** +-- Time Gates +--*********************************************************** +GateActions["accumulator"] = { + group = "Time", + name = "Accumulator", + inputs = { "A", "Hold", "Reset" }, + timed = true, + output = function(gate, A, Hold, Reset) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + if (Reset > 0) then + gate.Accum = 0 + elseif (Hold <= 0) then + gate.Accum = gate.Accum+A*DeltaTime + end + return gate.Accum or 0 + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, A, Hold, Reset) + return "A:"..A.." Hold:"..Hold.." Reset:"..Reset.." = "..Out + end +} + +GateActions["smoother"] = { + group = "Time", + name = "Smoother", + inputs = { "A", "Rate" }, + timed = true, + output = function(gate, A, Rate) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + local Delta = A-gate.Accum + if (Delta > 0) then + gate.Accum = gate.Accum+math.min(Delta, Rate*DeltaTime) + elseif (Delta < 0) then + gate.Accum = gate.Accum+math.max(Delta, -Rate*DeltaTime) + end + return gate.Accum or 0 + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, A, Rate) + return "A:"..A.." Rate:"..Rate.." = "..Out + end +} + +GateActions["timer"] = { + group = "Time", + name = "Timer", + inputs = { "Run", "Reset" }, + timed = true, + output = function(gate, Run, Reset) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + if ( Reset > 0 ) then + gate.Accum = 0 + elseif ( Run > 0 ) then + gate.Accum = gate.Accum+DeltaTime + end + return gate.Accum or 0 + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, Run, Reset) + return "Run:"..Run.." Reset:"..Reset.." = "..Out + end +} + +GateActions["ostime"] = { + group = "Time", + name = "OS Time", + inputs = { }, + timed = true, + output = function(gate) + return os.date("%H")*3600+os.date("%M")*60+os.date("%S") + end, + label = function(Out) + return "OS Time = "..Out + end +} + +GateActions["osdate"] = { + group = "Time", + name = "OS Date", + inputs = { }, + timed = true, + output = function(gate) + return os.date("%Y")*366+os.date("%j") + end, + label = function(Out) + return "OS Date = "..Out + end +} + +GateActions["pulser"] = { + group = "Time", + name = "Pulser", + inputs = { "Run", "Reset", "TickTime" }, + timed = true, + output = function(gate, Run, Reset, TickTime) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + if ( Reset > 0 ) then + gate.Accum = 0 + elseif ( Run > 0 ) then + gate.Accum = gate.Accum+DeltaTime + if (gate.Accum >= TickTime) then + gate.Accum = gate.Accum - TickTime + return 1 + end + end + return 0 + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, Run, Reset, TickTime) + return "Run:"..Run.." Reset:"..Reset.."TickTime:"..TickTime.." = "..Out + end +} + +GateActions["squarepulse"] = { + group = "Time", + name = "Square Pulse", + inputs = { "Run", "Reset", "PulseTime", "GapTime", "Min", "Max" }, + timed = true, + output = function(gate, Run, Reset, PulseTime, GapTime, Min, Max) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + + if (Reset > 0) then + gate.Accum = 0 + elseif (Run > 0) then + gate.Accum = gate.Accum+DeltaTime + if (gate.Accum <= PulseTime) then + return Max + end + if (gate.Accum >= PulseTime + GapTime) then + gate.Accum = 0 + end + end + return Min + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, Run, Reset, PulseTime, GapTime) + return "Run:"..Run.." Reset:"..Reset.." PulseTime:"..PulseTime.." GapTime:"..GapTime.." = "..Out + end +} + +GateActions["sawpulse"] = { + group = "Time", + name = "Saw Pulse", + inputs = { "Run", "Reset", "SlopeRaiseTime", "PulseTime", "SlopeDescendTime", "GapTime", "Min", "Max" }, + timed = true, + output = function(gate, Run, Reset, SlopeRaiseTime, PulseTime, SlopeDescendTime, GapTime, Min, Max) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + + if (Reset > 0) then + gate.Accum = 0 + elseif (Run > 0) then + local val = Min + gate.Accum = gate.Accum+DeltaTime + if (gate.Accum >= 0) && (gate.Accum < SlopeRaiseTime) then + if (SlopeRaiseTime != 0) then + val = Min + (Max-Min) * (gate.Accum-0) / SlopeRaiseTime + end + end + if (gate.Accum >= SlopeRaiseTime) && (gate.Accum < SlopeRaiseTime+PulseTime) then + return Max + end + if (gate.Accum >= SlopeRaiseTime+PulseTime) && (gate.Accum < SlopeRaiseTime+PulseTime+SlopeDescendTime) then + if (SlopeDescendTime != 0) then + val = Min + (Max-Min) * (gate.Accum-SlopeRaiseTime+PulseTime) / SlopeDescendTime + end + end + if (gate.Accum >= SlopeRaiseTime+PulseTime+SlopeDescendTime) then + end + if (gate.Accum >= SlopeRaiseTime+PulseTime+SlopeDescendTime+GapTime) then + gate.Accum = 0 + end + return val + end + return Min + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, Run, Reset, PulseTime, GapTime) + return "Run:"..Run.." Reset:"..Reset.." PulseTime:"..PulseTime.." GapTime:"..GapTime.." = "..Out + end +} + + +GateActions["derive"] = { + group = "Time", + name = "Derivative", + inputs = {"A"}, + timed = false, + output = function(gate, A) + local t = CurTime() + local dT = t - gate.LastT + gate.LastT = t + local dA = A - gate.LastA + gate.LastA = A + if (dT != 0) then + return dA/dT + else + return 0; + end + end, + reset = function(gate) + gate.LastT = CurTime() + gate.LastA = 0 + end, + label = function(Out, A) + return "d/dt["..A.."] = "..Out + end +} + +GateActions["delay"] = { + group = "Time", + name = "Delay", + inputs = { "Clk", "Delay", "Hold", "Reset" }, + outputs = { "Out", "TimeElapsed", "Remaining" }, + timed = true, + output = function(gate, Clk, Delay, Hold, Reset) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + local out = 0 + + if ( Reset > 0 ) then + gate.Stage = 0 + gate.Accum = 0 + end + + if ( gate.Stage == 1 ) then + if ( gate.Accum >= Delay ) then + gate.Stage = 2 + gate.Accum = 0 + out = 1 + else + gate.Accum = gate.Accum+DeltaTime + end + elseif ( gate.Stage == 2 ) then + if ( gate.Accum >= Hold ) then + gate.Stage = 0 + gate.Accum = 0 + out = 0 + else + out = 1 + gate.Accum = gate.Accum+DeltaTime + end + else + if ( Clk > 0 ) then + gate.Stage = 1 + gate.Accum = 0 + end + end + + return out, gate.Accum, Delay-gate.Accum + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + gate.Stage = 0 + end, + label = function(Out, Clk, Delay, Hold, Reset) + return "Clk: "..Clk.." Delay: "..Delay.. + "\nHold: "..Hold.." Reset: "..Reset.. + "\nTime Elapsed: "..Out.TimeElapsed.." = "..Out.Out + end +} + +GateActions["Definite Integral"] = { + group = "Time", + name = "Integral", + inputs = { "A", "Points" }, + timed = true, + output = function(gate, A, Points) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + if(Points<=0) then + Points=2 + data = {} + end + data = data or {} + integral=A*DeltaTime + if (index == nil) then + index=1 + else + index=(index+1)%Points + end + data[index]=integral + i=0 + totalintegral=0 + while (i 100000) then + totalintegral = 100000 + end + if (totalintegral < -100000) then + totalintegral = -100000 + end + return totalintegral or 0 + end, + reset = function(gate) + gate.PrevTime = CurTime() + data = {} + end, + label = function(Out, A, Reset) + return "A: "..A.." Reset: "..Reset.." Output: "..Out + end, +} + +GateActions["Average Derivative"] = { + group = "Time", + name = "Average Derivative", + inputs = { "A", "Window" }, + timed = true, + output = function(gate, A, Window) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + if(Window<=0) then + Window=2 + data = {} + end + data = data or {} + prevA=currentA or A + currentA=A + derivative=(currentA-prevA)/DeltaTime + if (index == nil) then + index=1 + else + index=(index+1)%Window + end + data[index]=derivative + i=0 + sum=0 + while (i 0 ) then + gate.Accum = 0 + elseif ( gate.Accum > 0 || Run > 0 ) then + gate.Accum = gate.Accum+DeltaTime + if(gate.Accum > Time) then + gate.Accum = 0 + end + end + if(gate.Accum > 0)then + return 1 + else + return 0 + end + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, Run, Time, Reset) + return "Run:"..Run.." Time:"..Time.." Reset:"..Reset.." = "..Out + end +} + +GateActions["bstimer"] = { + group = "Time", + name = "BS_Timer", + inputs = { "Run", "Reset" }, + timed = true, + output = function(gate, Run, Reset) + local DeltaTime = CurTime()-(gate.PrevTime or CurTime()) + gate.PrevTime = (gate.PrevTime or CurTime())+DeltaTime + if ( Reset > 0 ) then + gate.Accum = 0 + elseif ( Run > 0 ) then + gate.Accum = gate.Accum+DeltaTime + end + + for i = 1,50 do + local bs = gate.Entity:GetPos() + local bs1 = gate.Entity:GetAngles() + end + + return gate.Accum or 0 + end, + reset = function(gate) + gate.PrevTime = CurTime() + gate.Accum = 0 + end, + label = function(Out, Run, Reset) + return "Run:"..Run.." Reset:"..Reset.." = "..Out + end +} + +--*********************************************************** +-- Trig Gates +--*********************************************************** +GateActions["quadratic"] = { + group = "Trig", + name = "Quadratic Formula", + inputs = { "A", "B", "C" }, + outputs = { "Pos", "Neg" }, + output = function(gate, A, B, C) + return ( -B + ( math.sqrt( math.abs( math.exp( B, 2 ) - ( 4*A )*C ) ) ) / 2*A ) + end, + output = function(gate, A, B, C) + return ( -B - ( math.sqrt( math.abs( math.exp( B, 2 ) - ( 4*A )*C ) ) ) / 2*A ) + end, + label = function(Out, A, B, C) + return "-" .. A .. " +/- sqrt( " .. B .. "^2 - ( 4*" .. A .. " )*" .. C .. " ) / 2*" .. A + end +} + +GateActions["sin"] = { + group = "Trig", + name = "Sin(Rad)", + inputs = { "A" }, + output = function(gate, A) + return math.sin(A) + end, + label = function(Out, A) + return "sin("..A.."rad) = "..Out + end +} + +GateActions["cos"] = { + group = "Trig", + name = "Cos(Rad)", + inputs = { "A" }, + output = function(gate, A) + return math.cos(A) + end, + label = function(Out, A) + return "cos("..A.."rad) = "..Out + end +} + +GateActions["tan"] = { + group = "Trig", + name = "Tan(Rad)", + inputs = { "A" }, + output = function(gate, A) + return math.tan(A) + end, + label = function(Out, A) + return "tan("..A.."rad) = "..Out + end +} + +GateActions["asin"] = { + group = "Trig", + name = "Asin(Rad)", + inputs = { "A" }, + output = function(gate, A) + return math.asin(A) + end, + label = function(Out, A) + return "asin("..A..") = "..Out.."rad" + end +} + +GateActions["acos"] = { + group = "Trig", + name = "Acos(Rad)", + inputs = { "A" }, + output = function(gate, A) + return math.acos(A) + end, + label = function(Out, A) + return "acos("..A..") = "..Out.."rad" + end +} + +GateActions["atan"] = { + group = "Trig", + name = "Atan(Rad)", + inputs = { "A" }, + output = function(gate, A) + return math.atan(A) + end, + label = function(Out, A) + return "atan("..A..") = "..Out.."rad" + end +} + +GateActions["sin_d"] = { + group = "Trig", + name = "Sin(Deg)", + inputs = { "A" }, + output = function(gate, A) + return math.sin(math.rad(A)) + end, + label = function(Out, A) + return "sin("..A.."deg) = "..Out + end +} + +GateActions["cos_d"] = { + group = "Trig", + name = "Cos(Deg)", + inputs = { "A" }, + output = function(gate, A) + return math.cos(math.rad(A)) + end, + label = function(Out, A) + return "cos("..A.."deg) = "..Out + end +} + +GateActions["tan_d"] = { + group = "Trig", + name = "Tan(Deg)", + inputs = { "A" }, + output = function(gate, A) + return math.tan(math.rad(A)) + end, + label = function(Out, A) + return "tan("..A.."deg) = "..Out + end +} + +GateActions["asin_d"] = { + group = "Trig", + name = "Asin(Deg)", + inputs = { "A" }, + output = function(gate, A) + return math.deg(math.asin(A)) + end, + label = function(Out, A) + return "asin("..A..") = "..Out.."deg" + end +} + +GateActions["acos_d"] = { + group = "Trig", + name = "Acos(Deg)", + inputs = { "A" }, + output = function(gate, A) + return math.deg(math.acos(A)) + end, + label = function(Out, A) + return "acos("..A..") = "..Out.."deg" + end +} + +GateActions["atan_d"] = { + group = "Trig", + name = "Atan(Deg)", + inputs = { "A" }, + output = function(gate, A) + return math.deg(math.atan(A)) + end, + label = function(Out, A) + return "atan("..A..") = "..Out.."deg" + end +} + +GateActions["rad2deg"] = { + group = "Trig", + name = "Radians to Degrees", + inputs = { "A" }, + output = function(gate, A) + return math.deg(A) + end, + label = function(Out, A) + return A.."rad = "..Out.."deg" + end +} + +GateActions["deg2rad"] = { + group = "Trig", + name = "Degrees to Radians", + inputs = { "A" }, + output = function(gate, A) + return math.rad(A) + end, + label = function(Out, A) + return A.."deg = "..Out.."rad" + end +} + +GateActions["angdiff"] = { + group = "Trig", + name = "Difference(rad)", + inputs = { "A", "B" }, + output = function(gate, A, B) + return math.rad(math.AngleDifference(math.deg(A), math.deg(B))) + end, + label = function(Out, A, B) + return A .. "deg - " .. B .. "deg = " .. Out .. "deg" + end +} + +GateActions["angdiff_d"] = { + group = "Trig", + name = "Difference(deg)", + inputs = { "A", "B" }, + output = function(gate, A, B) + return math.AngleDifference(A, B) + end, + label = function(Out, A, B) + return A .. "deg - " .. B .. "deg = " .. Out .. "deg" + end +} + + + + +--*********************************************************** +-- Array Gates +--*********************************************************** + +GateActions["table_8merge"] = { + group = "Array", + name = "8x merger", + timed = true, + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + outputs = { "Tbl" }, + outputtypes = { "ARRAY" }, + output = function(gate, A, B, C, D, E, F, G, H) + if A then return { A, B, C, D, E, F, G, H } + else return {} + end + end, +} + +GateActions["table_8split"] = { + group = "Array", + name = "8x splitter", + timed = true, + inputs = { "Tbl" }, + inputtypes = { "ARRAY" }, + outputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + output = function(gate, Tbl) + if Tbl then return unpack( Tbl ) + else return 0,0,0,0,0,0,0,0 + end + end, +} + +GateActions["table_8duplexer"] = { + group = "Array", + name = "8x duplexer", + timed = true, + inputs = { "Tbl", "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "BIDIRARRAY" }, + outputs = { "Tbl", "A", "B", "C", "D", "E", "F", "G", "H" }, + outputtypes = { "BIDIRARRAY" }, + output = function(gate, Tbl, A, B, C, D, E, F, G, H) + local t,v = {0,0,0,0,0,0,0,0}, {} + if Tbl then t = Tbl end + if A then v = { A, B, C, D, E, F, G, H } end + return v, unpack( t ) + end, +} + +GateActions["table_valuebyidx"] = { + group = "Array", + name = "Value retriever", + timed = true, + inputs = { "Tbl", "Index" }, + inputtypes = { "ARRAY" }, + outputs = { "Data" }, + output = function(gate, Tbl, idx) + if Tbl && idx && Tbl[idx] then return Tbl[idx] + else return 0 + end + end, +} + + + + +------------------------------------------------------------------------------- +-- Vector gates +------------------------------------------------------------------------------- + +-- Add +GateActions["vector_add"] = { + group = "Vector", + name = "Addition", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR" }, + compact_inputs = 2, + outputtypes = { "VECTOR" }, + output = function(gate, ...) + local sum = Vector (0, 0, 0) + for _, v in pairs (arg) do + if (v and IsVector (v)) then + sum = sum + v + end + end + return sum + end, + label = function(Out, ...) + local tip = "" + for _, v in ipairs (arg) do + if (v) then tip = tip .. " + " .. v end + end + return string.format ("%s = (%d,%d,%d)", string.sub (tip, 3), + Out.x, Out.y, Out.z) + end +} + +-- Subtract +GateActions["vector_sub"] = { + group = "Vector", + name = "Subtraction", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + return (A - B) + end, + label = function(Out, A, B) + return string.format ("%s - %s = (%d,%d,%d)", A, B, Out.x, Out.y, Out.z) + end +} + +-- Negate +GateActions["vector_neg"] = { + group = "Vector", + name = "Negate", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return Vector (-A.x, -A.y, -A.z) + end, + label = function(Out, A) + return string.format ("-%s = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +-- Multiply/Divide by constant +GateActions["vector_mul"] = { + group = "Vector", + name = "Multiplication", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "NORMAL" }, + outputtypes = { "VECTOR" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + return (A * B) + end, + label = function(Out, A, B) + return string.format ("%s * %s = (%d,%d,%d)", A, B, Out.x, Out.y, Out.z) + end +} + +GateActions["vector_divide"] = { + group = "Vector", + name = "Division", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "NORMAL" }, + outputtypes = { "VECTOR" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if (B) then + return (A / B) + end + return Vector (0, 0, 0) + end, + label = function(Out, A, B) + return string.format ("%s / %s = (%d,%d,%d)", A, B, Out.x, Out.y, Out.z) + end +} + +-- Dot/Cross Product +GateActions["vector_dot"] = { + group = "Vector", + name = "Dot Product", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + return A:Dot (B) + end, + label = function(Out, A, B) + return string.format ("dot(%s, %s) = %d", A, B, Out) + end +} + +GateActions["vector_cross"] = { + group = "Vector", + name = "Cross Product", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + return A:Cross (B) + end, + label = function(Out, A, B) + return string.format ("cross(%s, %s) = (%d,%d,%d)", A, B, Out.x, Out.y, Out.z) + end +} + +-- Yaw/Pitch +GateActions["vector_ang"] = { + group = "Vector", + name = "Angles (Degree)", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputs = { "Yaw", "Pitch" }, + outputtypes = { "NORMAL", "NORMAL" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + local ang = A:Angle () + return ang.y, ang.p + end, + label = function(Out, A) + return string.format ("ang(%s) = %d, %d", A, Out.Yaw, Out.Pitch) + end +} + +-- Yaw/Pitch (Radian) +GateActions["vector_angrad"] = { + group = "Vector", + name = "Angles (Radian)", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputs = { "Yaw", "Pitch" }, + outputtypes = { "NORMAL", "NORMAL" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + local ang = A:Angle () + return (ang.y * math.pi / 180), (ang.p * math.pi / 180) + end, + label = function(Out, A) + return string.format ("angr(%s) = %d, %d", A, Out.Yaw, Out.Pitch) + end +} + +-- Magnitude +GateActions["vector_mag"] = { + group = "Vector", + name = "Magnitude", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "NORMAL" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return A:Length () + end, + label = function(Out, A) + return string.format ("|%s| = %d", A, Out) + end +} + +-- Conversion To/From +GateActions["vector_convto"] = { + group = "Vector", + name = "Compose", + inputs = { "X", "Y", "Z" }, + inputtypes = { "NORMAL", "NORMAL", "NORMAL" }, + outputtypes = { "VECTOR" }, + output = function(gate, X, Y, Z) + return Vector (X, Y, Z) + end, + label = function(Out, X, Y, Z) + return string.format ("vector(%s,%s,%s) = (%d,%d,%d)", X, Y, Z, Out.x, Out.y, Out.z) + end +} + +GateActions["vector_convfrom"] = { + group = "Vector", + name = "Decompose", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputs = { "X", "Y", "Z" }, + outputtypes = { "NORMAL", "NORMAL", "NORMAL" }, + output = function(gate, A) + if (A and IsVector (A)) then + return A.x, A.y, A.z + end + return 0, 0, 0 + end, + label = function(Out, A) + return string.format ("%s -> X:%d Y:%d Z:%d", A, Out.X, Out.Y, Out.Z) + end +} + +-- Normalise +GateActions["vector_norm"] = { + group = "Vector", + name = "Normalise", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return A:Normalize () + end, + label = function(Out, A) + return string.format ("norm(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +-- Identity +GateActions["vector_ident"] = { + group = "Vector", + name = "Identity", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return A + end, + label = function(Out, A) + return string.format ("%s = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +-- Random (really needed?) +GateActions["vector_rand"] = { + group = "Vector", + name = "Random", + inputs = { }, + inputtypes = { }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate) + local vec = Vector (math.random (), math.random (), math.random ()) + return vec:Normalize () + end, + label = function(Out) + return "Random Vector" + end +} + +-- Component Derivative +GateActions["vector_derive"] = { + group = "Vector", + name = "Component Derivative", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, A) + local t = CurTime () + if !IsVector (A) then A = Vector (0, 0, 0) end + local dT, dA = t - gate.LastT, A - gate.LastA + gate.LastT, gate.LastA = t, A + if (dT) then + return Vector (dA.x/dT, dA.y/dT, dA.z/dT) + else + return Vector (0, 0, 0) + end + end, + reset = function(gate) + gate.LastT, gate.LastA = CurTime (), Vector (0, 0, 0) + end, + label = function(Out, A) + return string.format ("diff(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +-- Component Integral +GateActions["vector_cint"] = { + group = "Vector", + name = "Component Integral", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, A) + local t = CurTime () + if !IsVector (A) then A = Vector (0, 0, 0) end + local dT = t - (gate.LastT or t) + gate.LastT, gate.Integral = t, (gate.Integral or Vector (0, 0, 0)) + A * dT + -- Lifted (kinda) from wiregates.lua to prevent massive values + local TempInt = gate.Integral:Length () + if (TempInt > 100000) then + gate.Integral = gate.Integral:Normalize () * 100000 + end + if (TempInt < -100000) then + gate.Integral = gate.Integral:Normalize () * -100000 + end + return gate.Integral + end, + reset = function(gate) + gate.Integral, gate.LastT = Vector (0, 0, 0), CurTime () + end, + label = function(Out, A) + return string.format ("int(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +-- Multiplexer +GateActions["vector_mux"] = { + group = "Vector", + name = "Multiplexer", + inputs = { "Sel", "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "NORMAL", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR" }, + compact_inputs = 3, + outputtypes = { "VECTOR" }, + output = function(gate, Sel, ...) + Sel = math.floor (Sel) + if (Sel > 0 && Sel <= 8) then + return arg[Sel] + end + return Vector (0, 0, 0) + end, + label = function(Out, Sel, ...) + return string.format ("Select: %s Out: (%d,%d,%d)", + Sel, Out.x, Out.y, Out.z) + end +} + +-- Demultiplexer +GateActions["vector_dmx"] = { + group = "Vector", + name = "Demultiplexer", + inputs = { "Sel", "In" }, + inputtypes = { "NORMAL", "VECTOR" }, + outputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + outputtypes = { "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR" }, + output = function(gate, Sel, In) + local Out = { Vector (0, 0, 0), Vector (0, 0, 0), Vector (0, 0, 0), Vector (0, 0, 0), + Vector (0, 0, 0), Vector (0, 0, 0), Vector (0, 0, 0), Vector (0, 0, 0) } + Sel = math.floor (Sel) + if (Sel > 0 && Sel <= 8) then + Out[Sel] = In + end + return unpack (Out) + end, + label = function(Out, Sel, In) + if !IsVector (In) then In = Vector (0, 0, 0) end + if !Sel then Sel = 0 end + return string.format ("Select: %s, In: (%d,%d,%d)", + Sel, In.x, In.y, In.z) + end +} + +-- Latch +GateActions["vector_latch"] = { + group = "Vector", + name = "Latch", + inputs = { "In", "Clk" }, + inputtypes = { "VECTOR", "NORMAL" }, + outputtypes = { "VECTOR" }, + output = function(gate, In, Clk) + Clk = (Clk > 0) + if (gate.PrevClk != Clk) then + gate.PrevClk = Clk + if (Clk) then + if !IsVector (In) then In = Vector (0, 0, 0) end + gate.LatchStore = In + end + end + return gate.LatchStore or Vector (0, 0, 0) + end, + reset = function(gate) + gate.LatchStore = Vector (0, 0, 0) + gate.PrevValue = 0 + end, + label = function(Out, In, Clk) + return string.format ("Latch Data: %s Clock: %s Out: (%d,%d,%d)", + In, Clk, Out.x, Out.y, Out.z) + end +} + +-- D-latch +GateActions["vector_dlatch"] = { + group = "Vector", + name = "D-Latch", + inputs = { "In", "Clk" }, + inputtypes = { "VECTOR", "NORMAL" }, + outputtypes = { "VECTOR" }, + output = function(gate, In, Clk) + if (Clk > 0) then + if !IsVector (In) then In = Vector (0, 0, 0) end + gate.LatchStore = In + end + return gate.LatchStore or Vector (0, 0, 0) + end, + reset = function(gate) + gate.LatchStore = Vector (0, 0, 0) + end, + label = function(Out, In, Clk) + return string.format ("Latch Data: %s Clock: %s Out: (%d,%d,%d)", + In, Clk, Out.x, Out.y, Out.z) + end +} + +-- Equal +GateActions["vector_compeq"] = { + group = "Vector", + name = "Equal", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if (A == B) then return 1 end + return 0 + end, + label = function(Out, A, B) + return string.format ("(%s == %s) = %d", A, B, Out) + end +} + +-- Inequal +GateActions["vector_compineq"] = { + group = "Vector", + name = "Inequal", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if (A == B) then return 0 end + return 1 + end, + label = function(Out, A, B) + return string.format ("(%s != %s) = %d", A, B, Out) + end +} + +-- Less-than +GateActions["vector_complt"] = { + group = "Vector", + name = "Less Than", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + if (A:Length () < B:Length ()) then return 1 end + end, + label = function(Out, A, B) + return string.format ("(|%s| < |%s|) = %d", A, B, Out) + end +} + +-- Less-than or Equal-to +GateActions["vector_complteq"] = { + group = "Vector", + name = "Less Than or Equal To", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + if (A:Length () <= B:Length ()) then return 1 end + return 0 + end, + label = function(Out, A, B) + return string.format ("(|%s| <= |%s|) = %d", A, B, Out) + end +} + +-- Greater-than +GateActions["vector_compgt"] = { + group = "Vector", + name = "Greater Than", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + if (A:Length () > B:Length ()) then return 1 end + return 0 + end, + label = function(Out, A, B) + return string.format ("(|%s| > |%s|) = %d", A, B, Out) + end +} + +-- Greater-than or Equal-to +GateActions["vector_compgteq"] = { + group = "Vector", + name = "Greater Than or Equal To", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "VECTOR" }, + output = function(gate, A, B) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + if (A:Length () < B:Length ()) then return 1 end + return 0 + end, + label = function(Out, A, B) + return string.format ("(|%s| >= |%s|) = %d", A, B, Out) + end +} + +-- Returns a positive vector. +GateActions["vector_positive"] = { + group = "Vector", + name = "Positive", + inputs = { "A" }, + inputtypes = { "VECTOR"}, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return Vector(math.abs(A.x),math.abs(A.y),math.abs(A.z)) + end, + label = function(Out, A) + return string.format ("abs(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + + +-- Returns a rounded vector. +GateActions["vector_round"] = { + group = "Vector", + name = "Round", + inputs = { "A" }, + inputtypes = { "VECTOR"}, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return Vector(math.Round(A.x),math.Round(A.y),math.Round(A.z)) + end, + label = function(Out, A) + return string.format ("round(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + + +-- Returns the largest vector. +GateActions["vector_max"] = { + group = "Vector", + name = "Largest", + inputs = { "A" , "B" }, + inputtypes = { "VECTOR" , "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + if A:Length() > B:Length() then return A else return B end + end, + label = function(Out, A , B) + return string.format ("max(%s , %s) = (%d,%d,%d)", A , B, Out.x, Out.y, Out.z) + end +} + +-- Returns the smallest vector. +GateActions["vector_min"] = { + group = "Vector", + name = "Smallest", + inputs = { "A" , "B" }, + inputtypes = { "VECTOR" , "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + if !IsVector (B) then B = Vector (0, 0, 0) end + if A:Length() < B:Length() then return A else return B end + end, + label = function(Out, A , B) + return string.format ("min(%s , %s) = (%d,%d,%d)", A , B, Out.x, Out.y, Out.z) + end +} + +-- Shifts the components left. +GateActions["vector_shiftl"] = { + group = "Vector", + name = "Shift Components Left", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return Vector(A.y,A.z,A.x) + end, + label = function(Out, A ) + return string.format ("shiftL(%s) = (%d,%d,%d)", A , Out.x, Out.y, Out.z) + end +} + +-- Shifts the components right. +GateActions["vector_shiftr"] = { + group = "Vector", + name = "Shift Components Right", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + return Vector(A.z,A.x,A.y) + end, + label = function(Out, A ) + return string.format ("shiftR(%s) = (%d,%d,%d)", A , Out.x, Out.y, Out.z) + end +} + + +-- Returns 1 if a vector is on world. +GateActions["vector_isinworld"] = { + group = "Vector", + name = "Is In World", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + output = function(gate, A) + if !IsVector (A) then A = Vector (0, 0, 0) end + if util.IsInWorld(A) then return 1 else return 0 end + end, + label = function(Out, A ) + return string.format ("isInWorld(%s) = %d", A , Out) + end +} + +GateActions["vector_tostr"] = { + group = "Vector", + name = "To String", + inputs = { "A" }, + inputtypes = { "VECTOR" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !IsVector(A) then A = Vector (0, 0, 0) end + return "["..tostring(A.x)..","..tostring(A.y)..","..tostring(A.z).."]" + end, + label = function(Out, A ) + return string.format ("toString(%s) = \""..Out.."\"", A) + end +} + +GateActions["vector_select"] = { + group = "Vector", + name = "Select", + inputs = { "Choice", "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "NORMAL", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR", "VECTOR" }, + outputtypes = { "VECTOR" }, + output = function(gate, Choice, ...) + math.Clamp(Choice,1,8) + return arg[Choice] + end, + label = function(Out, Choice) + return string.format ("select(%s) = %s", Choice, Out) + end +} + +GateActions["vector_rotate"] = { + group = "Vector", + name = "Rotate", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "ANGLE" }, + outputtypes = { "VECTOR" }, + output = function(gate, A, B) + if !A then A = Vector(0, 0, 0) end + if !B then B = Angle(0, 0, 0) end + A:Rotate(B) + return A + end, + label = function(Out, A, B) + return string.format ("rotate(%s, %s) = "..tostring(Out), A, B ) + end +} + +GateActions["vector_mulcomp"] = { + group = "Vector", + name = "Multiplication (component)", + inputs = { "A", "B" }, + inputtypes = { "VECTOR", "NORMAL" }, + outputtypes = { "VECTOR" }, + output = function(gate, A, B) + if !A then A = Vector(0, 0, 0) end + if !B then B = 0 end + return Vector( A.x * B, A.y * B, A.z * B ) + end, + label = function(Out, A, B) + return string.format ("%s * %s = "..tostring(Out), A, B ) + end +} + +------------------------------------------------------------------------------- +-- Entity gates +------------------------------------------------------------------------------- + + +GateActions["entity_applyf"] = { + group = "Entity", + name = "Apply Force", + inputs = { "Ent" , "Vec" }, + inputtypes = { "ENTITY" , "VECTOR" }, + timed = true, + output = function(gate, Ent , Vec ) + if !Ent then return nil end + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return end + if !(E2Lib.getOwner(gate, Ent) == E2Lib.getOwner(gate, gate)) then return nil end + if !IsVector(Vec) then Vec = Vector (0, 0, 0) end + Ent:GetPhysicsObject():ApplyForceCenter(Vec) + end, + label = function() + return "" + end +} + +GateActions["entity_applyof"] = { + group = "Entity", + name = "Apply Offset Force", + inputs = { "Ent" , "Vec" , "Offset" }, + inputtypes = { "ENTITY" , "VECTOR" , "VECTOR" }, + timed = true, + output = function(gate, Ent , Vec , Offset ) + if !Ent then return nil end + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return end + if !(E2Lib.getOwner(gate, Ent) == E2Lib.getOwner(gate, gate)) then return nil end + if !IsVector(Vec) then Vec = Vector (0, 0, 0) end + if !IsVector(Offset) then Offset = Vector (0, 0, 0) end + Ent:GetPhysicsObject():ApplyForceOffset(Vec, Offset) + end, + label = function() + return "" + end +} + +-- Base code taken from Expression 2 + +GateActions["entity_applyaf"] = { + group = "Entity", + name = "Apply Angular Force", + inputs = { "Ent" , "Ang" }, + inputtypes = { "ENTITY" , "ANGLE" }, + timed = true, + output = function(gate, Ent , Ang ) + if !Ent then return nil end + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return end + if !(E2Lib.getOwner(gate, Ent) == E2Lib.getOwner(gate, gate)) then return nil end + if !Ang then Ang = Angle (0, 0, 0) end + local phys = Ent:GetPhysicsObject() + local pos = Ent:LocalToWorld(phys:GetMassCenter()) + local up = Ent:GetUp() + local right = Ent:GetRight() + local forward = Ent:GetForward() + + local pitch = up * (Ang.p*0.5) + local yaw = forward * (Ang.y*0.5) + local roll = right * (Ang.r*0.5) + + if not phys:IsValid() then return end + -- apply pitch force + phys:ApplyForceOffset( forward, pos + pitch ) + phys:ApplyForceOffset( forward * -1, pos - pitch ) + + -- apply yaw force + phys:ApplyForceOffset( right, pos - yaw ) + phys:ApplyForceOffset( right * -1, pos + yaw ) + + -- apply roll force + phys:ApplyForceOffset( up, pos - roll ) + phys:ApplyForceOffset( up * -1, pos + roll ) + + end, + label = function() + return "" + end +} + + +-- Taken from Expression 2 + +GateActions["entity_applytorq"] = { + group = "Entity", + name = "Apply Torque", + inputs = { "Ent" , "Vec" }, + inputtypes = { "ENTITY" , "VECTOR" }, + timed = true, + output = function(gate, Ent , Vec ) + if !Ent then return nil end + if not Ent:IsValid() then return end + if !(E2Lib.getOwner(gate, Ent) == E2Lib.getOwner(gate, gate)) then return nil end + local phys = Ent:GetPhysicsObject() + if not phys:IsValid() then return end + + if not !IsVector(Vec) then Vec = Vector( 0, 0, 0 ) end + + local tq = Vec + local torqueamount = tq:Length() + local off + if abs(torque[3]) > torqueamount*0.1 or abs(Vec.x) > torqueamount*0.1 then + off = Vector(-Vec.z, 0, Vec.x) + else + off = Vector(-Vec.y, Vec.x, 0) + end + off:Normalize() + local dir = tq:Cross(off) + + dir = phys:LocalToWorld(dir)-phys:GetPos() + local masscenter = phys:GetMassCenter() + phys:ApplyForceOffset( dir * 0.5, phys:LocalToWorld(masscenter+off) ) + phys:ApplyForceOffset( dir * -0.5, phys:LocalToWorld(masscenter-off) ) + end, + label = function() + return "" + end +} + + + +GateActions["entity_class"] = { + group = "Entity", + name = "Class", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "STRING" }, + output = function(gate, Ent) + if !Ent:IsValid() then return "" else return Ent:GetClass() end + end, + label = function(Out) + return string.format ("Class = %q", Out) + end +} + +GateActions["entity_entid"] = { + group = "Entity", + name = "Entity ID", + inputs = { "A" }, + inputtypes = { "ENTITY" }, + output = function(gate, A) + if (A and IsEntity (A) and A:IsValid ()) then return A:EntIndex () end + return 0 + end, + label = function(Out, A) + return string.format ("entID(%s) = %d", A, Out) + end +} + +GateActions["entity_model"] = { + group = "Entity", + name = "Model", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "STRING" }, + output = function(gate, Ent) + if !Ent:IsValid() then return "" else return Ent:GetModel() end + end, + label = function(Out) + return string.format ("Model = %q", Out) + end +} + +GateActions["entity_steamid"] = { + group = "Entity", + name = "SteamID", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "STRING" }, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:IsPlayer() then return "" else return Ent:SteamID() end + end, + label = function(Out) + return string.format ("SteamID = %q", Out) + end +} + +GateActions["entity_pos"] = { + group = "Entity", + name = "Position", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return Vector(0,0,0) else return Ent:GetPos() end + end, + label = function(Out) + return string.format ("Position = (%d,%d,%d)", Out.x , Out.y , Out.z ) + end +} + +GateActions["entity_fruvecs"] = { + group = "Entity", + name = "Direction - (forward, right, up)", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputs = { "Forward", "Right" , "Up" }, + outputtypes = { "VECTOR" , "VECTOR" , "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return Vector(0,0,0) , Vector(0,0,0) , Vector(0,0,0) else return Ent:GetForward() , Ent:GetRight() , Ent:GetUp() end + end, + label = function(Out) + return string.format ("Forward = (%f , %f , %f)\nUp = (%f , %f , %f)\nRight = (%f , %f , %f)", Out.Forward.x , Out.Forward.y , Out.Forward.z, Out.Up.x , Out.Up.y , Out.Up.z, Out.Right.x , Out.Right.y , Out.Right.z) + end +} + +GateActions["entity_isvalid"] = { + group = "Entity", + name = "Is Valid", + inputs = { "A" }, + inputtypes = { "ENTITY" }, + timed = true, + output = function(gate, A) + if (A and IsEntity (A) and A:IsValid ()) then + return 1 + end + return 0 + end, + label = function(Out, A) + return string.format ("isValid(%s) = %s", A, Out) + end +} + +GateActions["entity_vell"] = { + group = "Entity", + name = "Velocity (local)", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return Vector(0,0,0) else return Ent:WorldToLocal(Ent:GetVelocity() + Ent:GetPos()) end + end, + label = function(Out) + return string.format ("Velocity (local) = (%f , %f , %f)", Out.x , Out.y , Out.z ) + end +} + +GateActions["entity_vel"] = { + group = "Entity", + name = "Velocity", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return Vector(0,0,0) else return Ent:GetVelocity() end + end, + label = function(Out) + return string.format ("Velocity = (%f , %f , %f)", Out.x , Out.y , Out.z ) + end +} + +GateActions["entity_angvel"] = { + group = "Entity", + name = "Angular Velocity", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "ANGLE" }, + timed = true, + output = function(gate, Ent) + local Vec + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then Vec = Vector(0,0,0) else Vec = Ent:GetPhysicsObject():GetAngleVelocity() end + return Angle(Vec.y, Vec.z, Vec.x) + end, + label = function(Out) + return string.format ("Angular Velocity = (%f , %f , %f)", Out.p , Out.y , Out.r ) + end +} + +GateActions["entity_angvelvec"] = { + group = "Entity", + name = "Angular Velocity (vector)", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + local phys = this:GetPhysicsObject() + if not phys:IsValid() then return Vector( 0, 0, 0 ) end + return phys:GetAngleVelocity() + end, + label = function(Out) + return string.format ("Angular Velocity = (%f , %f , %f)", Out.x , Out.y , Out.z ) + end +} + +GateActions["entity_wor2loc"] = { + group = "Entity", + name = "World To Local (vector)", + inputs = { "Ent" , "Vec" }, + inputtypes = { "ENTITY" , "VECTOR" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent , Vec ) + if Ent:IsValid() and IsVector(Vec) then return Ent:WorldToLocal(Vec) else return Vector(0,0,0) end + end, + label = function(Out) + return string.format ("World To Local = (%f , %f , %f)", Out.x , Out.y , Out.z ) + end +} + +GateActions["entity_loc2wor"] = { + group = "Entity", + name = "Local To World", + inputs = { "Ent" , "Vec" }, + inputtypes = { "ENTITY" , "VECTOR" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent , Vec ) + if Ent:IsValid() and IsVector(Vec) then return Ent:LocalToWorld(Vec) else return Vector(0,0,0) end + end, + label = function(Out) + return string.format ("Local To World = (%f , %f , %f)", Out.x , Out.y , Out.z ) + end +} + +GateActions["entity_loc2worang"] = { + group = "Entity", + name = "World To Local (angle)", + inputs = { "Ent" , "Ang" }, + inputtypes = { "ENTITY" , "ANGLE" }, + outputtypes = { "ANGLE" }, + timed = true, + output = function(gate, Ent , Ang ) + if Ent:IsValid() and Ang then return Ent:LocalToWorldAngles(Ang) else return Angle(0,0,0) end + end, + label = function(Out) + return string.format ("localToWorld = (%d,%d,%d)", Out.p , Out.y , Out.r ) + end +} + +GateActions["entity_health"] = { + group = "Entity", + name = "Health", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 else return Ent:Health() end + end, + label = function(Out) + return string.format ("Health = %d", Out) + end +} + +GateActions["entity_radius"] = { + group = "Entity", + name = "Radius", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 else return Ent:BoundingRadius() end + end, + label = function(Out) + return string.format ("Radius = %d", Out) + end +} + +GateActions["entity_mass"] = { + group = "Entity", + name = "Mass", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return 0 else return Ent:GetPhysicsObject():GetMass() end + end, + label = function(Out) + return string.format ("Mass = %d", Out) + end +} + +GateActions["entity_masscenter"] = { + group = "Entity", + name = "Mass Center", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return Vector(0,0,0) else return Ent:LocalToWorld(Ent:GetPhysicsObject():GetMassCenter()) end + end, + label = function(Out) + return string.format ("Mass Center = (%d,%d,%d)", Out.x , Out.y , Out.z) + end +} + +GateActions["entity_masscenterlocal"] = { + group = "Entity", + name = "Mass Center (local)", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return Vector(0,0,0) else return Ent:GetPhysicsObject():GetMassCenter() end + end, + label = function(Out) + return string.format ("Mass Center (local) = (%d,%d,%d)", Out.x , Out.y , Out.z) + end +} + +GateActions["entity_isplayer"] = { + group = "Entity", + name = "Is Player", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsPlayer() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is Player = %d", Out) + end +} + +GateActions["entity_isnpc"] = { + group = "Entity", + name = "Is NPC", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsNPC() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is NPC = %d", Out) + end +} + +GateActions["entity_isvehicle"] = { + group = "Entity", + name = "Is Vehicle", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsVehicle() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is Vehicle = %d", Out) + end +} + +GateActions["entity_isworld"] = { + group = "Entity", + name = "Is World", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsWorld() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is World = %d", Out) + end +} + +GateActions["entity_isongrnd"] = { + group = "Entity", + name = "Is On Ground", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsOnGround() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is On Ground = %d", Out) + end +} + +GateActions["entity_isunderwater"] = { + group = "Entity", + name = "Is Under Water", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:WaterLevel() > 0 then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is Under Water = %d", Out) + end +} + +GateActions["entity_angles"] = { + group = "Entity", + name = "Angles", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "ANGLE" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return Angle(0,0,0) else return Ent:GetAngles() end + end, + label = function(Out) + return string.format ("Angles = (%d,%d,%d)", Out.p , Out.y , Out.r) + end +} + +GateActions["entity_material"] = { + group = "Entity", + name = "Material", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "STRING" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return "" else return Ent:GetMaterial() end + end, + label = function(Out) + return string.format ("Material = %q", Out) + end +} + +GateActions["entity_owner"] = { + group = "Entity", + name = "Owner", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "ENTITY" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return E2Lib.getOwner(gate,gate) end + return E2Lib.getOwner(gate,Ent) + end, + label = function(Out,Ent) + return string.format ("owner(%s) = %s", Ent, tostring(Out)) + end +} +GateActions["entity_player"] = GateActions["entity_owner"] + +GateActions["entity_isheld"] = { + group = "Entity", + name = "Is Player Holding", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsPlayerHolding() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is Player Holding = %d", Out) + end +} + +GateActions["entity_isonfire"] = { + group = "Entity", + name = "Is On Fire", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsOnFire()then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is On Fire = %d", Out) + end +} + +GateActions["entity_isweapon"] = { + group = "Entity", + name = "Is Weapon", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsWeapon() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is Weapon = %d", Out) + end +} + +GateActions["player_invehicle"] = { + group = "Entity", + name = "Is In Vehicle", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsPlayer() and Ent:InVehicle() then return 1 else return 0 end + end, + label = function(Out) + return string.format ("Is In Vehicle = %d", Out) + end +} + +GateActions["player_connected"] = { + group = "Entity", + name = "Time Connected", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "NORMAL" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return 0 end + if Ent:IsPlayer() then return Ent:TimeConnected() else return 0 end + end, + label = function(Out) + return string.format ("Time Connected = %d", Out) + end +} +GateActions["entity_aimentity"] = { + group = "Entity", + name = "AimEntity", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "ENTITY" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return nil end + local EntR = Ent:GetEyeTraceNoCursor().Entity + if !EntR:IsValid() then return nil end + return EntR + end, + label = function(Out) + return string.format ("Aim Entity = %s", tostring(Out)) + end +} + +GateActions["entity_eye"] = { + group = "Entity", + name = "Veiw Direction", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return nil end + if (Ent:IsPlayer()) then + return Ent:GetAimVector() + else + return Ent:GetForward() + end + end, + label = function(Out) + return "eye(".. tostring(Out)")" + end +} + +GateActions["entity_aimenormal"] = { + group = "Entity", + name = "AimNormal", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:IsPlayer() then return Vector(0,0,0) end + return Ent:GetEyeTraceNoCursor().HitNormal + end, + label = function(Out, A) + return string.format ("Aim Normal (%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +GateActions["entity_aimedirection"] = { + group = "Entity", + name = "AimDirection", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:IsPlayer() then return Vector(0,0,0) end + return Ent:GetEyeTraceNoCursor().Normal + end, + label = function(Out, A) + return string.format ("Aim Direction (%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +GateActions["entity_inertia"] = { + group = "Entity", + name = "Inertia", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:GetPhysicsObject():IsValid() then return Vector(0,0,0) end + return Ent:GetPhysicsObject():GetInertia() + end, + label = function(Out, A) + return string.format ("inertia(%s) = (%d,%d,%d)", Ent, Out.x, Out.y, Out.z) + end +} + +GateActions["entity_setmass"] = { + group = "Entity", + name = "Set Mass", + inputs = { "Ent" , "Val" }, + inputtypes = { "ENTITY" , "NORMAL" }, + timed = true, + output = function(gate, Ent, Val ) + if !Ent:IsValid() then return nil end + if !Ent:GetPhysicsObject():IsValid() then return nil end + if !(E2Lib.getOwner(gate, gate) == E2Lib.getOwner(gate, Ent)) then return nil end + if !Val then Val = Ent:GetPhysicsObject():GetMass() end + Val = math.Clamp(Val, 0.001, 50000) + Ent:GetPhysicsObject():SetMass(Val) + end, + label = function(Out, Ent , Val) + return string.format ("setMass(%s , %s)", Ent, Val) + end +} + +GateActions["entity_equal"] = { + group = "Entity", + name = "Equal", + inputs = { "A" , "B" }, + inputtypes = { "ENTITY" , "ENTITY" }, + output = function(gate, A, B ) + if A == B then return 1 else return 0 end + end, + label = function(Out, A , B) + return string.format ("(%s = = %s) = %d", A, B, Out) + end +} + +GateActions["entity_inequal"] = { + group = "Entity", + name = "Inequal", + inputs = { "A" , "B" }, + inputtypes = { "ENTITY" , "ENTITY" }, + output = function(gate, A, B ) + if A ~= B then return 1 else return 0 end + end, + label = function(Out, A , B) + return string.format ("(%s ! = %s) = %d", A, B, Out) + end +} + +GateActions["entity_setcol"] = { + group = "Entity", + name = "Set Color", + inputs = { "Ent" , "Col" }, + inputtypes = { "ENTITY" , "VECTOR" }, + timed = true, + output = function(gate, Ent, Col ) + if !Ent:IsValid() then return nil end + if !(E2Lib.getOwner(gate, gate) == E2Lib.getOwner(gate, Ent)) then return nil end + if !IsVector(Col) then Col = Vector(255,255,255) end + Ent:SetColor(Col.x,Col.y,Col.z,255) + return nil + end, + label = function(Out, Ent , Col) + if !IsVector(Col) then Col = Vector(0,0,0) end + return string.format ("setColor(%s ,(%d,%d,%d) )", Ent , Col.x, Col.y, Col.z) + end +} + +GateActions["entity_clr"] = { + group = "Entity", + name = "Color", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() then return Vector(0,0,0) end + local r,g,b = Ent:GetColor() + if !Vector(r,g,b) then return Vector(0,0,0) end + return Vector(r,g,b) + end, + label = function(Out, Ent) + return string.format ("color(%s) = (%d,%d,%d)", Ent , Out.x, Out.y, Out.z) + end +} + + + +GateActions["entity_name"] = { + group = "Entity", + name = "Name", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "STRING" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:IsPlayer() then return "" else return Ent:Nick() end + end, + label = function(Out, Ent) + return string.format ("name(%s) = %s", Ent, Out) + end +} + +GateActions["entity_aimpos"] = { + group = "Entity", + name = "AimPosition", + inputs = { "Ent" }, + inputtypes = { "ENTITY" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, Ent) + if !Ent:IsValid() or !Ent:IsPlayer() then return Vector(0,0,0) else return Ent:GetEyeTraceNoCursor().HitPos end + end, + label = function(Out) + return string.format ("Aim Position = (%f , %f , %f)", Out.x , Out.y , Out.z) + end +} + +GateActions["entity_select"] = { + group = "Entity", + name = "Select", + inputs = { "Choice", "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "NORMAL", "ENTITY", "ENTITY", "ENTITY", "ENTITY", "ENTITY", "ENTITY", "ENTITY", "ENTITY" }, + outputtypes = { "ENTITY" }, + output = function(gate, Choice, ...) + math.Clamp(Choice,1,8) + return arg[Choice] + end, + label = function(Out, Choice) + return string.format ("select(%s) = %s", Choice, Out) + end +} + + +------------------------------------------------------------------------------- +-- Angle gates +------------------------------------------------------------------------------- + +-- Add +GateActions["angle_add"] = { + group = "Angle", + name = "Addition", + inputs = { "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE" }, + compact_inputs = 2, + outputtypes = { "ANGLE" }, + output = function(gate, A , B , C , D , E , F , G , H) + if !A then A = Angle (0, 0, 0) end + if !B then B = Angle (0, 0, 0) end + if !C then C = Angle (0, 0, 0) end + if !D then D = Angle (0, 0, 0) end + if !E then E = Angle (0, 0, 0) end + if !F then F = Angle (0, 0, 0) end + if !G then G = Angle (0, 0, 0) end + if !H then H = Angle (0, 0, 0) end + return (A + B + C + D + E + F + G + H) + end, + label = function(Out) + return string.format ("Addition = (%d,%d,%d)", + Out.p, Out.y, Out.r) + end +} + +-- Subtract +GateActions["angle_sub"] = { + group = "Angle", + name = "Subtraction", + inputs = { "A", "B" }, + inputtypes = { "ANGLE", "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A, B) + if !A then A = Angle (0, 0, 0) end + if !B then B = Angle (0, 0, 0) end + return (A - B) + end, + label = function(Out, A, B) + return string.format ("%s - %s = (%d,%d,%d)", A, B, Out.p, Out.y, Out.r) + end +} + +-- Negate +GateActions["angle_neg"] = { + group = "Angle", + name = "Negate", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return Angle (-A.p, -A.y, -A.r) + end, + label = function(Out, A) + return string.format ("-%s = (%d,%d,%d)", A, Out.p, Out.y, Out.r) + end +} + +-- Multiply/Divide by constant +GateActions["angle_mul"] = { + group = "Angle", + name = "Multiplication", + inputs = { "A", "B" }, + inputtypes = { "ANGLE", "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A, B) + if !A then A = Angle (0, 0, 0) end + if !B then B = Angle (0, 0, 0) end + return Angle(A.p * B.p , A.y * B.y , A.r * B.r) + end, + label = function(Out, A, B) + return string.format ("%s * %s = (%d,%d,%d)", A, B, Out.p, Out.y, Out.r) + end +} + +GateActions["angle_divide"] = { + group = "Angle", + name = "Division", + inputs = { "A", "B" }, + inputtypes = { "ANGLE", "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A, B) + if !A then A = Angle (0, 0, 0) end + if !B or B == Angle (0, 0, 0) then B = Angle (0, 0, 0) return B end + return Angle(A.p / B.p , A.y / B.y , A.r / B.r) + end, + label = function(Out, A, B) + return string.format ("%s / %s = (%d,%d,%d)", A, B, Out.p, Out.y, Out.r) + end +} +-- Conversion To/From +GateActions["angle_convto"] = { + group = "Angle", + name = "Compose", + inputs = { "Pitch", "Yaw", "Roll" }, + inputtypes = { "NORMAL", "NORMAL", "NORMAL" }, + outputtypes = { "ANGLE" }, + output = function(gate, Pitch, Yaw, Roll) + return Angle (Pitch, Yaw, Roll) + end, + label = function(Out, Pitch, Yaw, Roll) + return string.format ("angle(%s,%s,%s) = (%d,%d,%d)", Pitch, Yaw, Roll, Out.p, Out.y, Out.r) + end +} + +GateActions["angle_convfrom"] = { + group = "Angle", + name = "Decompose", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputs = { "Pitch", "Yaw", "Roll" }, + output = function(gate, A) + if A then + return A.p, A.y, A.r + end + return 0, 0, 0 + end, + label = function(Out, A) + return string.format ("%s -> Pitch:%d Yaw:%d Roll:%d", A, Out.Pitch, Out.Yaw, Out.Roll) + end +} + +-- Identity +GateActions["angle_ident"] = { + group = "Angle", + name = "Identity", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return A + end, + label = function(Out, A) + return string.format ("%s = (%d,%d,%d)", A, Out.p, Out.y, Out.r) + end +} + +GateActions["angle_round"] = { + group = "Angle", + name = "Round", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return Angle(math.Round(A.p),math.Round(A.y),math.Round(A.r)) + end, + label = function(Out, A) + return string.format ("%s = (%d,%d,%d)", A, Out.p, Out.y, Out.r) + end +} + +-- Shifts the components left. +GateActions["angle_shiftl"] = { + group = "Angle", + name = "Shift Components Left", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return Angle(A.y,A.r,A.p) + end, + label = function(Out, A ) + return string.format ("shiftL(%s) = (%d,%d,%d)", A , Out.p, Out.y, Out.r) + end +} + +-- Shifts the components right. +GateActions["angle_shiftr"] = { + group = "Angle", + name = "Shift Components Right", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return Angle(A.r,A.p,A.y) + end, + label = function(Out, A ) + return string.format ("shiftR(%s) = (%d,%d,%d)", A , Out.p, Out.y, Out.r) + end +} + +GateActions["angle_fruvecs"] = { + group = "Angle", + name = "Direction - (forward, up, right)", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputs = { "Forward", "Up" , "Right" }, + outputtypes = { "VECTOR" , "VECTOR" , "VECTOR" }, + timed = true, + output = function(gate, A ) + if !A then return Vector(0,0,0) , Vector(0,0,0) , Vector(0,0,0) else return A:Forward() , A:Up() , A:Right() end + end, + label = function(Out) + return string.format ("Forward = (%f , %f , %f)\nUp = (%f , %f , %f)\nRight = (%f , %f , %f)", Out.Forward.x , Out.Forward.y , Out.Forward.z, Out.Up.x , Out.Up.y , Out.Up.z, Out.Right.x , Out.Right.y , Out.Right.z) + end +} + +GateActions["angle_norm"] = { + group = "Angle", + name = "Normalize", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return Angle(math.NormalizeAngle(A.p),math.NormalizeAngle(A.y),math.NormalizeAngle(A.r)) + end, + label = function(Out, A ) + return string.format ("normalize(%s) = (%d,%d,%d)", A , Out.p, Out.y, Out.r) + end +} + +GateActions["angle_tostr"] = { + group = "Angle", + name = "To String", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = Angle (0, 0, 0) end + return "["..tostring(A.p)..","..tostring(A.y)..","..tostring(A.r).."]" + end, + label = function(Out, A ) + return string.format ("toString(%s) = \""..Out.."\"", A) + end +} + + +-- Equal +GateActions["angle_compeq"] = { + group = "Angle", + name = "Equal", + inputs = { "A", "B" }, + inputtypes = { "ANGLE", "ANGLE" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if (A == B) then return 1 end + return 0 + end, + label = function(Out, A, B) + return string.format ("(%s == %s) = %d", A, B, Out) + end +} + +-- Inequal +GateActions["angle_compineq"] = { + group = "Angle", + name = "Inequal", + inputs = { "A", "B" }, + inputtypes = { "ANGLE", "ANGLE" }, + outputtypes = { "NORMAL" }, + output = function(gate, A, B) + if (A == B) then return 0 end + return 1 + end, + label = function(Out, A, B) + return string.format ("(%s != %s) = %d", A, B, Out) + end +} + +-- Returns a rounded angle. +GateActions["angle_round"] = { + group = "Angle", + name = "Round", + inputs = { "A" }, + inputtypes = { "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, A) + if !A then A = Angle(0, 0, 0) end + return Angle(math.Round(A.p),math.Round(A.y),math.Round(A.r)) + end, + label = function(Out, A) + return string.format ("round(%s) = (%d,%d,%d)", A, Out.p, Out.y, Out.r) + end +} + +GateActions["angle_select"] = { + group = "Angle", + name = "Select", + inputs = { "Choice", "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "NORMAL", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE", "ANGLE" }, + outputtypes = { "ANGLE" }, + output = function(gate, Choice, ...) + math.Clamp(Choice,1,8) + return arg[Choice] + end, + label = function(Out, Choice) + return string.format ("select(%s) = %s", Choice, Out) + end +} + + +GateActions["angle_mulcomp"] = { + group = "Angle", + name = "Multiplication (component)", + inputs = { "A", "B" }, + inputtypes = { "ANGLE", "NORMAL" }, + outputtypes = { "ANGLE" }, + output = function(gate, A, B) + if !A then A = Angle(0, 0, 0) end + if !B then B = 0 end + return Angle( A.p * B, A.y * B, A.r * B ) + end, + label = function(Out, A, B) + return string.format ("%s * %s = "..tostring(Out), A, B ) + end +} + + +------------------------------------------------------------------------------- +-- Rangerdata gates +------------------------------------------------------------------------------- +GateActions["rd_trace"] = { + group = "Ranger", + name = "Trace", + inputs = { "Startpos", "Endpos" }, + inputtypes = { "VECTOR", "VECTOR" }, + outputtypes = { "RANGER" }, + timed = true, + output = function(gate, Startpos, Endpos) + if !IsVector(Startpos) then Startpos = Vector (0, 0, 0) end + if !IsVector(Endpos) then Endpos = Vector (0, 0, 0) end + local tracedata = {} + tracedata.start = Startpos + tracedata.endpos = Endpos + return util.TraceLine(tracedata) + end, + label = function(Out, Startpos, Endpos) + return string.format ("trace(%s , %s)", Startpos, Endpos) + end +} + +GateActions["rd_hitpos"] = { + group = "Ranger", + name = "Hit Position", + inputs = { "A" }, + inputtypes = { "RANGER" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, A) + if !A then return Vector(0,0,0) end + if A.StartSolid then return A.StartPos end + return A.HitPos + end, + label = function(Out, A) + return string.format ("hitpos(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +GateActions["rd_hitnorm"] = { + group = "Ranger", + name = "Hit Normal", + inputs = { "A" }, + inputtypes = { "RANGER" }, + outputtypes = { "VECTOR" }, + timed = true, + output = function(gate, A) + if !A then return Vector(0,0,0) end + return A.HitNormal + end, + label = function(Out, A) + return string.format ("hitnormal(%s) = (%d,%d,%d)", A, Out.x, Out.y, Out.z) + end +} + +GateActions["rd_entity"] = { + group = "Ranger", + name = "Entity", + inputs = { "A" }, + inputtypes = { "RANGER" }, + outputtypes = { "ENTITY" }, + timed = true, + output = function(gate, A) + if !A then return nil end + return A.Entity + end, + label = function(Out, A) + return string.format ("hitentity(%s) = %s", A, Out) + end +} + +GateActions["rd_hitworld"] = { + group = "Ranger", + name = "Hit World", + inputs = { "A" }, + inputtypes = { "RANGER" }, + outputtypes = { "NUMBER" }, + timed = true, + output = function(gate, A) + if !A then return 0 end + return A.HitWorld + end, + label = function(Out, A) + return string.format ("hitworld(%s) = %d", A, Out) + end +} + +GateActions["rd_hit"] = { + group = "Ranger", + name = "Hit", + inputs = { "A" }, + inputtypes = { "RANGER" }, + outputtypes = { "NUMBER" }, + timed = true, + output = function(gate, A) + if !A then return 0 end + return A.Hit + end, + label = function(Out, A) + return string.format ("hit(%s) = %d", A, Out) + end +} + +GateActions["rd_distance"] = { + group = "Ranger", + name = "Distance", + inputs = { "A" }, + inputtypes = { "RANGER" }, + outputtypes = { "NUMBER" }, + timed = true, + output = function(gate, A) + if !A then return 0 end + if A.StartSolid then return A.StartPos:Distance(A.HitPos)*(1/(1-A.FractionLeftSolid)-1) end + return A.StartPos:Distance(A.HitPos) + end, + label = function(Out, A) + return string.format ("distance(%s) = %d", A, Out) + end +} + +------------------------------------------------------------------------------- +-- String gates ! :P +------------------------------------------------------------------------------- +GateActions["string_ceq"] = { + group = "String", + name = "Equal", + inputs = { "A" , "B" }, + inputtypes = { "STRING" , "STRING" }, + output = function(gate, A, B) + if A == B then return 1 else return 0 end + end, + label = function(Out, A, B) + return string.format ("(%s == %s) = %d", A, B, Out) + end +} + +GateActions["string_cineq"] = { + group = "String", + name = "Inequal", + inputs = { "A" , "B" }, + inputtypes = { "STRING" , "STRING" }, + output = function(gate, A, B) + if A ~= B then return 1 else return 0 end + end, + label = function(Out, A, B) + return string.format ("(%s != %s) = %d", A, B, Out) + end +} + +GateActions["string_index"] = { + group = "String", + name = "Index", + inputs = { "A" , "Index" }, + inputtypes = { "STRING" , "NORMAL" }, + outputtypes = { "STRING" }, + output = function(gate, A, B) + if !A then A = "" end + if !B then B = 0 end + return string.sub(A,B,B) + end, + label = function(Out, A, B) + return string.format ("index(%s , %s) = %q", A, B, Out) + end +} + +GateActions["string_length"] = { + group = "String", + name = "Length", + inputs = { "A" }, + inputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = "" end + if string.len(A) then return string.len(A) else return 0 end + end, + label = function(Out, A) + return string.format ("length(%s) = %d", A, Out) + end +} + +GateActions["string_upper"] = { + group = "String", + name = "Uppercase", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = "" end + return string.upper(A) + end, + label = function(Out, A) + return string.format ("upper(%s) = %q", A, Out) + end +} + +GateActions["string_lower"] = { + group = "String", + name = "Lowercase", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = "" end + return string.lower(A) + end, + label = function(Out, A) + return string.format ("lower(%s) = %q", A, Out) + end +} + +GateActions["string_sub"] = { + group = "String", + name = "Substring", + inputs = { "A" , "Start" , "End" }, + inputtypes = { "STRING" , "NORMAL" , "NORMAL" }, + outputtypes = { "STRING" }, + output = function(gate, A, B, C) + if !A then A = "" end + if !B then B = 1 end -- defaults to start of string + if !C then C = -1 end -- defaults to end of string + return string.sub(A,B,C) + end, + label = function(Out, A, B, C) + return string.format ("%s:sub(%s , %s) = %q", A, B, C, Out) + end +} + +GateActions["string_explode"] = { + group = "String", + name = "Explode", + inputs = { "A" , "Separator" }, + inputtypes = { "STRING" , "STRING" }, + outputtypes = { "ARRAY" }, + output = function(gate, A, B) + if !A then A = "" end + if !B then B = "" end + return string.Explode(B,A) + end, + label = function(Out, A, B) + return string.format ("explode(%s , %s)", A, B) + end +} + +GateActions["string_find"] = { + group = "String", + name = "Find", + inputs = { "A", "B", "StartIndex" }, + inputtypes = { "STRING", "STRING" }, + outputs = { "Out" }, + output = function(gate, A, B, StartIndex) + local r = string.find(A,B,StartIndex) + if r==nil then r=0 end + return r + end, + label = function(Out, A, B) + return string.format ("find(%s , %s) = %d", A, B, Out) + end +} + + +GateActions["string_concat"] = { + group = "String", + name = "Concatenate", + inputs = { "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" }, + inputtypes = { "STRING" , "STRING" , "STRING" , "STRING" , "STRING" , "STRING" , "STRING" , "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, A, B, C, D, E, F, G, H) + local T = {A,B,C,D,E,F,G,H} + return table.concat(T) + end, + label = function(Out) + return string.format ("concat = %q", Out) + end +} + +GateActions["string_trim"] = { + group = "String", + name = "Trim", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = "" end + return string.Trim(A) + end, + label = function(Out, A) + return string.format ("trim(%s) = %q", A, Out) + end +} + +GateActions["string_replace"] = { + group = "String", + name = "Replace", + inputs = { "String" , "ToBeReplaced" , "Replacer" }, + inputtypes = { "STRING" , "STRING" , "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, A, B, C) + if !A then A = "" end + if !B then B = "" end + if !C then C = "" end + return string.gsub(A,B,C) + end, + label = function(Out, A, B, C) + return string.format ("%s:replace(%s , %s) = %q", A, B, C, Out) + end +} + +GateActions["string_reverse"] = { + group = "String", + name = "Reverse", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = "" end + return string.reverse(A) + end, + label = function(Out, A) + return string.format ("reverse(%s) = %q", A, Out) + end +} + +GateActions["string_tonum"] = { + group = "String", + name = "To Number", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "NORMAL" }, + output = function(gate, A) + if !A then A = "" end + return tonumber(A) + end, + label = function(Out, A) + return string.format ("tonumber(%s) = %d", A, Out) + end +} + +GateActions["string_tostr"] = { + group = "String", + name = "Number to String", + inputs = { "A" }, + inputtypes = { "NORMAL" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = 0 end + return tostring(A) + end, + label = function(Out, A) + return string.format ("tostring(%s) = %q", A, Out) + end +} + +GateActions["string_tobyte"] = { + group = "String", + name = "To Byte", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "NORMAL" }, + output = function(gate, A) + if !A then A = "" end + return string.byte(A) + end, + label = function(Out, A) + return string.format ("tobyte(%s) = %d", A, Out) + end +} + +GateActions["string_tochar"] = { + group = "String", + name = "To Character", + inputs = { "A" }, + inputtypes = { "NORMAL" }, + outputtypes = { "STRING" }, + output = function(gate, A) + if !A then A = 0 end + return string.char(A) + end, + label = function(Out, A) + return string.format ("tochar(%s) = %q", A, Out) + end +} + +GateActions["string_repeat"] = { + group = "String", + name = "Repeat", + inputs = { "A" , "Num"}, + inputtypes = { "STRING" , "NORMAL" }, + outputtypes = { "STRING" }, + output = function(gate, A, B) + if !A then A = "" end + if !B or B<1 then B = 1 end + return string.rep(A,B) + end, + label = function(Out, A) + return string.format ("repeat(%s) = %q", A, Out) + end +} + +GateActions["string_find"] = { + group = "String", + name = "Find", + inputs = { "A", "B", "StartIndex" }, + inputtypes = { "STRING", "STRING" }, + output = function(gate, A, B, C) + local R = string.find(A, B, C) + if !R then R = 0 end + return R + end, + label = function(Out, A, B) + return string.format ("find(%s , %s) = %d", A, B, Out) + end +} + +GateActions["string_ident"] = { + group = "String", + name = "Identity", + inputs = { "A" }, + inputtypes = { "STRING" }, + outputtypes = { "STRIN" }, + output = function(gate, A ) + return A + end, + label = function(Out, A) + return string.format ("%s = %s", A, Out) + end +} + +GateActions["string_select"] = { + group = "String", + name = "Select", + inputs = { "Choice", "A", "B", "C", "D", "E", "F", "G", "H" }, + inputtypes = { "NORMAL", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING" }, + outputtypes = { "STRING" }, + output = function(gate, Choice, ...) + math.Clamp(Choice,1,8) + return arg[Choice] + end, + label = function(Out, Choice) + return string.format ("select(%s) = %s", Choice, Out) + end +} + + +WireGatesSorted = {} +for name,gate in pairs(GateActions) do + if !WireGatesSorted[gate.group] then WireGatesSorted[gate.group] = {} end + WireGatesSorted[gate.group][name] = gate +end diff --git a/lua/wire/WireMonitors.lua b/lua/wire/WireMonitors.lua new file mode 100644 index 0000000000..43452a1ea1 --- /dev/null +++ b/lua/wire/WireMonitors.lua @@ -0,0 +1,42 @@ +-- $Rev: 1303 $ +-- $LastChangedDate: 2009-07-08 19:10:33 -0700 (Wed, 08 Jul 2009) $ +-- $LastChangedBy: tad2020 $ + +WireGPU_Monitors = {} + +function WireGPU_AddMonitor(name,model,tof,tou,tor,trs,x1,x2,y1,y2,rot90) + WireGPU_Monitors[model] = {} + WireGPU_Monitors[model].Name = name + WireGPU_Monitors[model].OF = tof + WireGPU_Monitors[model].OU = tou + WireGPU_Monitors[model].OR = tor + WireGPU_Monitors[model].RS = trs + WireGPU_Monitors[model].RatioX = math.abs((y1-y2)/(x2-x1)) + + WireGPU_Monitors[model].x1 = x1 + WireGPU_Monitors[model].x2 = x2 + WireGPU_Monitors[model].y1 = y1 + WireGPU_Monitors[model].y2 = y2 + + WireGPU_Monitors[model].z = tof + + WireGPU_Monitors[model].rot90 = rot90 +end + +-- Offset front, offset up, offset right, resolution/scale OF OU OR SCALE LOWX HIGHX LOWY HIGHY ROTATE90 +WireGPU_AddMonitor("Small TV", "models/props_lab/monitor01b.mdl", 6.4, 0.45, 1.00, 0.018, -5.535, 3.5, 5.091, -4.1) +WireGPU_AddMonitor("Monitor Small", "models/kobilica/wiremonitorsmall.mdl", 0.3, 5.0, 0.0, 0.0175, -4.4, 4.5, 9.5, 0.6) +WireGPU_AddMonitor("LCD Monitor (4:3)", "models/props/cs_office/computer_monitor.mdl", 3.7, 16.7, 2.4, 0.031, -10.5, 10.5, 24.7, 8.6) +WireGPU_AddMonitor("Monitor Big", "models/kobilica/wiremonitorbig.mdl", 0.2, 13, 0.0, 0.045, -11.5, 11.6, 24.5, 1.6) +WireGPU_AddMonitor("Plasma TV (4:3)", "models/blacknecro/tv_plasma_4_3.mdl", 0.1, -0.5, 6.5, 0.082, -27.87, 27.87, 20.93, -20.93) +WireGPU_AddMonitor("Plasma TV (16:10)", "models/props/cs_office/tv_plasma.mdl", 6.1, 18.93, 11.0, 0.065, -28.5, 28.5, 36, 2) +WireGPU_AddMonitor("Billboard", "models/props/cs_assault/billboard.mdl", 1, 0, 52, 0.23, -110.512, 110.512,57.647, -57.647) + +-- WireGPU_AddMonitor("LED Board (1:1)", "models/blacknecro/ledboard60.mdl", 6.1, 18.5, 11.0, 0.065, -60, 60, -60, 60) +WireGPU_AddMonitor("Cube 1x1x1", "models/hunter/blocks/cube1x1x1.mdl", 24, 0, 0, 0.09, -48, 48, -48, 48) +WireGPU_AddMonitor("Panel 1x1", "models/hunter/plates/plate1x1.mdl", 0, 1.7, 0, 0.09, -48, 48, -48, 48, true) +WireGPU_AddMonitor("Panel 2x2", "models/hunter/plates/plate2x2.mdl", 0, 1.7, 0, 0.182, -48, 48, -48, 48, true) +WireGPU_AddMonitor("Panel 0.5x0.5", "models/hunter/plates/plate05x05.mdl", 0, 1.7, 0, 0.045, -48, 48, -48, 48, true) + +WireGPU_AddMonitor("Tray", "models\props\cs_militia\reload_bullet_tray.mdl", 0, 0.8, 1.6, 0.009, 0, 100, 0, 60, true) +-- Offset front, offset up, offset right, resolution/scale OF OU OR SCALE LOWX HIGHX LOWY HIGHY ROTATE90 diff --git a/lua/wire/WireShared.lua b/lua/wire/WireShared.lua new file mode 100644 index 0000000000..ddbeef2115 --- /dev/null +++ b/lua/wire/WireShared.lua @@ -0,0 +1,250 @@ +-- $Rev: 1781 $ +-- $LastChangedDate: 2009-10-05 04:19:53 -0700 (Mon, 05 Oct 2009) $ +-- $LastChangedBy: TomyLobo $ + +WireLib = {} + +-- extra table functions + +-- Compacts an array by rejecting entries according to cb. +function table.Compact(tbl, cb, n) + n = n or table.getn(tbl) + local cpos = 1 + for i = 1, n do + if cb(tbl[i]) then + tbl[cpos] = tbl[i] + cpos = cpos + 1 + end + end + + local new_n = cpos-1 + while (cpos <= n) do + tbl[cpos] = nil + cpos = cpos + 1 + end +end + +-- I don't even know if I need this one. +-- HUD indicator needs this one +function table.MakeSortedKeys(tbl) + local result = {} + + for k,_ in pairs(tbl) do table.insert(result, k) end + table.sort(result) + + return result +end + +-- works like pairs() except that it iterates sorted by keys. +-- criterion is optional and should be a function(a,b) returning whether a is less than b. (same as table.sort's criterions) +function pairs_sortkeys(tbl, criterion) + tmp = {} + for k,v in pairs(tbl) do table.insert(tmp,k) end + table.sort(tmp, criterion) + + local iter, state, index, k = ipairs(tmp) + return function() + index,k = iter(state, index) + if index == nil then return nil end + return k,tbl[k] + end +end + +-- sorts by values +function pairs_sortvalues(tbl, criterion) + local crit = criterion and + function(a,b) + return criterion(tbl[a],tbl[b]) + end + or + function(a,b) + return tbl[a] < tbl[b] + end + + tmp = {} + for k,v in pairs(tbl) do table.insert(tmp,k) end + table.sort(tmp, crit) + + local iter, state, index, k = ipairs(tmp) + return function() + index,k = iter(state, index) + if index == nil then return nil end + return k,tbl[k] + end +end + +-- like ipairs, except it maps the value with mapfunction before returning. +function ipairs_map(tbl, mapfunction) + local iter, state, k = ipairs(tbl) + return function(state, k) + local v + k,v = iter(state, k) + if k == nil then return nil end + return k,mapfunction(v) + end, state, k +end + +-- like pairs, except it maps the value with mapfunction before returning. +function pairs_map(tbl, mapfunction) + local iter, state, k = pairs(tbl) + return function(state, k) + local v + k,v = iter(state, k) + if k == nil then return nil end + return k,mapfunction(v) + end, state, k +end + +/******************************************************************************/ + +WireLib.containers = {} + +function WireLib.containers.new(metatable, ...) + local tbl = {} + setmetatable(tbl, metatable) + if tbl.Initialize then tbl:Initialize(...) end + return tbl +end + +do -- class deque + local deque = {} + WireLib.containers.deque = deque + + deque.__index = deque + + function deque:Initialize() + self.offset = 0 + end + + function deque:size() + return #self-self.offset + end + + function deque:shift() + --do return table.remove(self, 1) end + local offset = self.offset + 1 + local ret = self[offset] + if not ret then self.offset = offset-1 return nil end + self.offset = offset + if offset > 127 then + for i = offset+1,#self-offset do + self[i-offset] = self[i] + end + for i = #self-offset+1,#self do + self[i-offset],self[i] = self[i],nil + end + self.offset = 0 + end + return ret + end + + function deque:push(value) + self[#self+1] = value + end + + function deque:pop() + local ret = self[#self] + self[#self] = nil + return ret + end + + function deque:top() + return self[#self] + end +end + +/******************************************************************************/ + +-- end extra table functions + +-- WireLib.AddNotify([ply, ]Message, Type, Duration[, Sound]) +-- If ply is left out, the notification is sent to everyone. If Sound is left out, no sound is played. +-- On the client, only the local player can be notified. + +-- The following sounds can be used: +NOTIFYSOUND_NONE = 0 -- optional +NOTIFYSOUND_DRIP1 = 1 +NOTIFYSOUND_DRIP2 = 2 +NOTIFYSOUND_DRIP3 = 3 +NOTIFYSOUND_DRIP4 = 4 +NOTIFYSOUND_DRIP5 = 5 +NOTIFYSOUND_ERROR1 = 6 +NOTIFYSOUND_CONFIRM1 = 7 +NOTIFYSOUND_CONFIRM2 = 8 +NOTIFYSOUND_CONFIRM3 = 9 +NOTIFYSOUND_CONFIRM4 = 10 + +if CLIENT then + + local sounds = { + [NOTIFYSOUND_DRIP1 ] = "ambient/water/drip1.wav", + [NOTIFYSOUND_DRIP2 ] = "ambient/water/drip2.wav", + [NOTIFYSOUND_DRIP3 ] = "ambient/water/drip3.wav", + [NOTIFYSOUND_DRIP4 ] = "ambient/water/drip4.wav", + [NOTIFYSOUND_DRIP5 ] = "ambient/water/drip5.wav", + [NOTIFYSOUND_ERROR1 ] = "buttons/button10.wav", + [NOTIFYSOUND_CONFIRM1] = "buttons/button3.wav", + [NOTIFYSOUND_CONFIRM2] = "buttons/button14.wav", + [NOTIFYSOUND_CONFIRM3] = "buttons/button15.wav", + [NOTIFYSOUND_CONFIRM4] = "buttons/button17.wav", + } + + function WireLib.AddNotify(ply, Message, Type, Duration, Sound) + if type(ply) == "string" then + Message, Type, Duration, Sound = ply, Message, Type, Duration + elseif ply ~= LocalPlayer() then + return + end + GAMEMODE:AddNotify(Message, Type, Duration) + if Sound and sounds[Sound] then surface.PlaySound(sounds[Sound]) end + end + + usermessage.Hook("wl_addnotify", function(um) + local Message = um:ReadString() + local Type = um:ReadChar() + local Duration = um:ReadFloat() + local Sound = um:ReadChar() + + WireLib.AddNotify(LocalPlayer(), Message, Type, Duration, Sound) + end) + +elseif SERVER then + + NOTIFY_GENERIC = 0 + NOTIFY_ERROR = 1 + NOTIFY_UNDO = 2 + NOTIFY_HINT = 3 + NOTIFY_CLEANUP = 4 + + function WireLib.AddNotify(ply, Message, Type, Duration, Sound) + if type(ply) == "string" then ply, Message, Type, Duration, Sound = nil, ply, Message, Type, Duration end + umsg.Start("wl_addnotify", ply) + umsg.String(Message) + umsg.Char(Type) + umsg.Float(Duration) + umsg.Char(Sound or 0) + umsg.End() + end + +end + +if CLIENT then + usermessage.Hook("wirelib_clienterror", function(um) + local message = um:ReadString() + print("sv: "..message) + local lines = string.Explode("\n", message) + for i,line in ipairs(lines) do + if i == 1 then + WireLib.AddNotify(line, NOTIFY_ERROR, 7, NOTIFYSOUND_ERROR1) + else + WireLib.AddNotify(line, NOTIFY_ERROR, 7) + end + end + end) +elseif SERVER then + function WireLib.ClientError(message, player) + umsg.Start("wirelib_clienterror", player) + umsg.String(message) + umsg.End() + end +end diff --git a/lua/wire/client/TextEditor.lua b/lua/wire/client/TextEditor.lua new file mode 100644 index 0000000000..81f0d7aa6c --- /dev/null +++ b/lua/wire/client/TextEditor.lua @@ -0,0 +1,1778 @@ +/******************************************************************************\ + Expression 2 Text Editor for Garry's Mod + Andreas "Syranide" Svensson, me@syranide.com +\******************************************************************************/ + +local EDITOR = {} + +surface.CreateFont("Courier New", 16, 400, false, false, "Expression2EditorFont") +surface.CreateFont("Courier New", 16, 700, false, false, "Expression2EditorFontBold") + +function EDITOR:Init() + self:SetCursor("beam") + + surface.SetFont("Expression2EditorFont") + self.FontWidth, self.FontHeight = surface.GetTextSize(" ") + + self.Rows = {""} + self.Caret = {1, 1} + self.Start = {1, 1} + self.Scroll = {1, 1} + self.Size = {1, 1} + self.Undo = {} + self.Redo = {} + self.PaintRows = {} + + self.Blink = RealTime() + + self.ScrollBar = vgui.Create("DVScrollBar", self) + self.ScrollBar:SetUp(1, 1) + + self.TextEntry = vgui.Create("TextEntry", self) + self.TextEntry:SetMultiline(true) + self.TextEntry:SetSize(0, 0) + + self.TextEntry.OnLoseFocus = function (self) self.Parent:_OnLoseFocus() end + self.TextEntry.OnTextChanged = function (self) self.Parent:_OnTextChanged() end + self.TextEntry.OnKeyCodeTyped = function (self, code) self.Parent:_OnKeyCodeTyped(code) end + + self.TextEntry.Parent = self + + self.LastClick = 0 +end + +function EDITOR:RequestFocus() + self.TextEntry:RequestFocus() +end + +function EDITOR:OnGetFocus() + self.TextEntry:RequestFocus() +end + +function EDITOR:CursorToCaret() + local x, y = self:CursorPos() + + x = x - (self.FontWidth * 3 + 6) + if x < 0 then x = 0 end + if y < 0 then y = 0 end + + local line = math.floor(y / self.FontHeight) + local char = math.floor(x / self.FontWidth+0.5) + + line = line + self.Scroll[1] + char = char + self.Scroll[2] + + if line > #self.Rows then line = #self.Rows end + local length = string.len(self.Rows[line]) + if char > length + 1 then char = length + 1 end + + return { line, char } +end + +function EDITOR:OnMousePressed(code) + if code == MOUSE_LEFT then + if((CurTime() - self.LastClick) < 1 and self.tmp and self:CursorToCaret()[1] == self.Caret[1] and self:CursorToCaret()[2] == self.Caret[2]) then + self.Start = self:getWordStart(self.Caret) + self.Caret = self:getWordEnd(self.Caret) + self.tmp = false + return + end + + self.tmp = true + + self.LastClick = CurTime() + self:RequestFocus() + self.Blink = RealTime() + self.MouseDown = true + + self.Caret = self:CursorToCaret() + if !input.IsKeyDown(KEY_LSHIFT) and !input.IsKeyDown(KEY_RSHIFT) then + self.Start = self:CursorToCaret() + end + elseif code == MOUSE_RIGHT then + local menu = DermaMenu() + + if self:CanUndo() then + menu:AddOption("Undo", function() + self:DoUndo() + end) + end + if self:CanRedo() then + menu:AddOption("Redo", function() + self:DoRedo() + end) + end + + if self:CanUndo() or self:CanRedo() then + menu:AddSpacer() + end + + if self:HasSelection() then + menu:AddOption("Cut", function() + if self:HasSelection() then + self.clipboard = self:GetSelection() + self.clipboard = string.Replace(self.clipboard, "\n", "\r\n") + SetClipboardText(self.clipboard) + self:SetSelection() + end + end) + menu:AddOption("Copy", function() + if self:HasSelection() then + self.clipboard = self:GetSelection() + self.clipboard = string.Replace(self.clipboard, "\n", "\r\n") + SetClipboardText(self.clipboard) + end + end) + end + + menu:AddOption("Paste", function() + if self.clipboard then + self:SetSelection(self.clipboard) + else + self:SetSelection() + end + end) + + if self:HasSelection() then + menu:AddOption("Delete", function() + self:SetSelection() + end) + end + + menu:AddSpacer() + + menu:AddOption("Select all", function() + self:SelectAll() + end) + + menu:AddSpacer() + + menu:AddOption("Indent", function() + self:Indent(false) + end) + menu:AddOption("Outdent", function() + self:Indent(true) + end) + + if self:HasSelection() then + menu:AddSpacer() + + menu:AddOption("Comment Block", function() + self:CommentSelection(false) + end) + menu:AddOption("Uncomment Block", function() + self:CommentSelection(true) + end) + end + + menu:Open() + end +end + +function EDITOR:OnMouseReleased(code) + if !self.MouseDown then return end + + if code == MOUSE_LEFT then + self.MouseDown = nil + if(!self.tmp) then return end + self.Caret = self:CursorToCaret() + end +end + +function EDITOR:SetText(text) + self.Rows = string.Explode("\n", text) + if self.Rows[#self.Rows] != "" then + self.Rows[#self.Rows + 1] = "" + end + + self.Caret = {1, 1} + self.Start = {1, 1} + self.Scroll = {1, 1} + self.Undo = {} + self.Redo = {} + self.PaintRows = {} + + self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1) +end + +function EDITOR:GetValue() + return table.concat(self.Rows, "\n") +end + +function EDITOR:PaintLine(row) + if row > #self.Rows then return end + + if !self.PaintRows[row] then + self.PaintRows[row] = self:SyntaxColorLine(row) + end + + local width, height = self.FontWidth, self.FontHeight + + if row == self.Caret[1] and self.TextEntry:HasFocus() then + surface.SetDrawColor(48, 48, 48, 255) + surface.DrawRect(width * 3 + 5, (row - self.Scroll[1]) * height, self:GetWide() - (width * 3 + 5), height) + end + + if self:HasSelection() then + local start, stop = self:MakeSelection(self:Selection()) + local line, char = start[1], start[2] + local endline, endchar = stop[1], stop[2] + + surface.SetDrawColor(0, 0, 160, 255) + local length = self.Rows[row]:len() - self.Scroll[2] + 1 + + char = char - self.Scroll[2] + endchar = endchar - self.Scroll[2] + if char < 0 then char = 0 end + if endchar < 0 then endchar = 0 end + + if row == line and line == endline then + surface.DrawRect(char * width + width * 3 + 6, (row - self.Scroll[1]) * height, width * (endchar - char), height) + elseif row == line then + surface.DrawRect(char * width + width * 3 + 6, (row - self.Scroll[1]) * height, width * (length - char + 1), height) + elseif row == endline then + surface.DrawRect(width * 3 + 6, (row - self.Scroll[1]) * height, width * endchar, height) + elseif row > line and row < endline then + surface.DrawRect(width * 3 + 6, (row - self.Scroll[1]) * height, width * (length + 1), height) + end + end + + draw.SimpleText(tostring(row), "Expression2EditorFont", width * 3, (row - self.Scroll[1]) * height, Color(128, 128, 128, 255), TEXT_ALIGN_RIGHT) + + local offset = -self.Scroll[2] + 1 + for i,cell in ipairs(self.PaintRows[row]) do + if offset < 0 then + if cell[1]:len() > -offset then + line = cell[1]:sub(1-offset) + offset = line:len() + + if cell[2][2] then + draw.SimpleText(line, "Expression2EditorFontBold", width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1]) + else + draw.SimpleText(line, "Expression2EditorFont", width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1]) + end + else + offset = offset + cell[1]:len() + end + else + if cell[2][2] then + draw.SimpleText(cell[1], "Expression2EditorFontBold", offset * width + width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1]) + else + draw.SimpleText(cell[1], "Expression2EditorFont", offset * width + width * 3 + 6, (row - self.Scroll[1]) * height, cell[2][1]) + end + + offset = offset + cell[1]:len() + end + end + + +end + +function EDITOR:PerformLayout() + self.ScrollBar:SetSize(16, self:GetTall()) + self.ScrollBar:SetPos(self:GetWide() - 16, 0) + + self.Size[1] = math.floor(self:GetTall() / self.FontHeight) - 1 + self.Size[2] = math.floor((self:GetWide() - (self.FontWidth * 3 + 6) - 16) / self.FontWidth) - 1 + + self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1) +end + +function EDITOR:PaintTextOverlay() + + if self.TextEntry:HasFocus() and self.Caret[2] - self.Scroll[2] >= 0 then + local width, height = self.FontWidth, self.FontHeight + + if (RealTime() - self.Blink) % 0.8 < 0.4 then + surface.SetDrawColor(240, 240, 240, 255) + surface.DrawRect((self.Caret[2] - self.Scroll[2]) * width + width * 3 + 6, (self.Caret[1] - self.Scroll[1]) * height, 1, height) + end + + -- Bracket highlighting by: {Jeremydeath} + local WindowText = self:GetValue() + local LinePos = table.concat(self.Rows, "\n", 1, self.Caret[1]-1):len() + local CaretPos = LinePos+self.Caret[2]+1 + + local BracketPairs = { + ["{"] = "}", + ["}"] = "{", + ["["] = "]", + ["]"] = "[", + ["("] = ")", + [")"] = "(" + } + + local CaretChars = WindowText:sub(CaretPos-1, CaretPos) + local BrackSt, BrackEnd = CaretChars:find("[%(%){}%[%]]") + + local Bracket = false + if BrackSt and BrackSt != 0 then + Bracket = CaretChars:sub(BrackSt or 0,BrackEnd or 0) + end + if Bracket and BracketPairs[Bracket] then + local End = 0 + local EndX = 1 + local EndLine = 1 + local StartX = 1 + + if Bracket == "(" or Bracket == "[" or Bracket == "{" then + BrackSt,End = WindowText:find("%b"..Bracket..BracketPairs[Bracket], CaretPos-1) + + if BrackSt and End then + local OffsetSt = 1 + + local BracketLines = string.Explode("\n",WindowText:sub(BrackSt, End)) + + EndLine = self.Caret[1]+#BracketLines-1 + + EndX = End-LinePos-2 + if #BracketLines>1 then + EndX = BracketLines[#BracketLines]:len()-1 + end + + if Bracket == "{" then + OffsetSt = 0 + end + + if (CaretPos - BrackSt) >= 0 and (CaretPos - BrackSt) <= 1 then + local width, height = self.FontWidth, self.FontHeight + local StartX = BrackSt - LinePos - 2 + surface.SetDrawColor(255, 0, 0, 50) + surface.DrawRect((StartX-(self.Scroll[2]-1)) * width + width * 4 + OffsetSt - 1, (self.Caret[1] - self.Scroll[1]) * height+1, width-2, height-2) + surface.DrawRect((EndX-(self.Scroll[2]-1)) * width + width * 3 + 6, (EndLine - self.Scroll[1]) * height+1, width-2, height-2) + end + end + elseif Bracket == ")" or Bracket == "]" or Bracket == "}" then + BrackSt,End = WindowText:reverse():find("%b"..Bracket..BracketPairs[Bracket], -CaretPos) + if BrackSt and End then + local len = WindowText:len() + End = len-End+1 + BrackSt = len-BrackSt+1 + local BracketLines = string.Explode("\n",WindowText:sub(End, BrackSt)) + + EndLine = self.Caret[1]-#BracketLines+1 + + local OffsetSt = -1 + + EndX = End-LinePos-2 + if #BracketLines>1 then + local PrevText = WindowText:sub(1, End):reverse() + + EndX = (PrevText:find("\n",1,true) or 2)-2 + end + + if Bracket != "}" then + OffsetSt = 0 + end + + if (CaretPos - BrackSt) >= 0 and (CaretPos - BrackSt) <= 1 then + local width, height = self.FontWidth, self.FontHeight + local StartX = BrackSt - LinePos - 2 + surface.SetDrawColor(255, 0, 0, 50) + surface.DrawRect((StartX-(self.Scroll[2]-1)) * width + width * 4 - 2, (self.Caret[1] - self.Scroll[1]) * height+1, width-2, height-2) + surface.DrawRect((EndX-(self.Scroll[2]-1)) * width + width * 3 + 8 + OffsetSt, (EndLine - self.Scroll[1]) * height+1, width-2, height-2) + end + end + end + end + end +end + +function EDITOR:Paint() + if !input.IsMouseDown(MOUSE_LEFT) then + self:OnMouseReleased(MOUSE_LEFT) + end + + if !self.PaintRows then + self.PaintRows = {} + end + + if self.MouseDown then + self.Caret = self:CursorToCaret() + end + + surface.SetDrawColor(0, 0, 0, 255) + surface.DrawRect(0, 0, self.FontWidth * 3 + 4, self:GetTall()) + + surface.SetDrawColor(32, 32, 32, 255) + surface.DrawRect(self.FontWidth * 3 + 5, 0, self:GetWide() - (self.FontWidth * 3 + 5), self:GetTall()) + + self.Scroll[1] = math.floor(self.ScrollBar:GetScroll() + 1) + + for i=self.Scroll[1],self.Scroll[1]+self.Size[1]+1 do + self:PaintLine(i) + end + + -- Paint the overlay of the text (bracket highlighting and carret postition) + self:PaintTextOverlay() + + return true +end + + +function EDITOR:SetCaret(caret) + self.Caret = self:CopyPosition(caret) + self.Start = self:CopyPosition(caret) + self:ScrollCaret() +end + + +function EDITOR:CopyPosition(caret) + return { caret[1], caret[2] } +end + +function EDITOR:MovePosition(caret, offset) + local caret = { caret[1], caret[2] } + + if offset > 0 then + while true do + local length = string.len(self.Rows[caret[1]]) - caret[2] + 2 + if offset < length then + caret[2] = caret[2] + offset + break + elseif caret[1] == #self.Rows then + caret[2] = caret[2] + length - 1 + break + else + offset = offset - length + caret[1] = caret[1] + 1 + caret[2] = 1 + end + end + elseif offset < 0 then + offset = -offset + + while true do + if offset < caret[2] then + caret[2] = caret[2] - offset + break + elseif caret[1] == 1 then + caret[2] = 1 + break + else + offset = offset - caret[2] + caret[1] = caret[1] - 1 + caret[2] = string.len(self.Rows[caret[1]]) + 1 + end + end + end + + return caret +end + + +function EDITOR:HasSelection() + return self.Caret[1] != self.Start[1] || self.Caret[2] != self.Start[2] +end + +function EDITOR:Selection() + return { { self.Caret[1], self.Caret[2] }, { self.Start[1], self.Start[2] } } +end + +function EDITOR:MakeSelection(selection) + local start, stop = selection[1], selection[2] + + if start[1] < stop[1] or (start[1] == stop[1] and start[2] < stop[2]) then + return start, stop + else + return stop, start + end +end + + +function EDITOR:GetArea(selection) + local start, stop = self:MakeSelection(selection) + + if start[1] == stop[1] then + return string.sub(self.Rows[start[1]], start[2], stop[2] - 1) + else + local text = string.sub(self.Rows[start[1]], start[2]) + + for i=start[1]+1,stop[1]-1 do + text = text .. "\n" .. self.Rows[i] + end + + return text .. "\n" .. string.sub(self.Rows[stop[1]], 1, stop[2] - 1) + end +end + +function EDITOR:SetArea(selection, text, isundo, isredo, before, after) + local start, stop = self:MakeSelection(selection) + + local buffer = self:GetArea(selection) + + if start[1] != stop[1] or start[2] != stop[2] then + // clear selection + self.Rows[start[1]] = string.sub(self.Rows[start[1]], 1, start[2] - 1) .. string.sub(self.Rows[stop[1]], stop[2]) + self.PaintRows[start[1]] = false + + for i=start[1]+1,stop[1] do + table.remove(self.Rows, start[1] + 1) + table.remove(self.PaintRows, start[1] + 1) + self.PaintRows = {} // TODO: fix for cache errors + end + + // add empty row at end of file (TODO!) + if self.Rows[#self.Rows] != "" then + self.Rows[#self.Rows + 1] = "" + self.PaintRows[#self.Rows + 1] = false + end + end + + if !text or text == "" then + self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1) + + self.PaintRows = {} + + self:OnTextChanged() + + if isredo then + self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(start) }, buffer, after, before } + return before + elseif isundo then + self.Redo[#self.Redo + 1] = { { self:CopyPosition(start), self:CopyPosition(start) }, buffer, after, before } + return before + else + self.Redo = {} + self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(start) }, buffer, self:CopyPosition(selection[1]), self:CopyPosition(start) } + return start + end + end + + // insert text + local rows = string.Explode("\n", text) + + local remainder = string.sub(self.Rows[start[1]], start[2]) + self.Rows[start[1]] = string.sub(self.Rows[start[1]], 1, start[2] - 1) .. rows[1] + self.PaintRows[start[1]] = false + + for i=2,#rows do + table.insert(self.Rows, start[1] + i - 1, rows[i]) + table.insert(self.PaintRows, start[1] + i - 1, false) + self.PaintRows = {} // TODO: fix for cache errors + end + + local stop = { start[1] + #rows - 1, string.len(self.Rows[start[1] + #rows - 1]) + 1 } + + self.Rows[stop[1]] = self.Rows[stop[1]] .. remainder + self.PaintRows[stop[1]] = false + + // add empty row at end of file (TODO!) + if self.Rows[#self.Rows] != "" then + self.Rows[#self.Rows + 1] = "" + self.PaintRows[#self.Rows + 1] = false + self.PaintRows = {} // TODO: fix for cache errors + end + + self.ScrollBar:SetUp(self.Size[1], #self.Rows - 1) + + self.PaintRows = {} + + self:OnTextChanged() + + if isredo then + self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(stop) }, buffer, after, before } + return before + elseif isundo then + self.Redo[#self.Redo + 1] = { { self:CopyPosition(start), self:CopyPosition(stop) }, buffer, after, before } + return before + else + self.Redo = {} + self.Undo[#self.Undo + 1] = { { self:CopyPosition(start), self:CopyPosition(stop) }, buffer, self:CopyPosition(selection[1]), self:CopyPosition(stop) } + return stop + end +end + + +function EDITOR:GetSelection() + return self:GetArea(self:Selection()) +end + +function EDITOR:SetSelection(text) + self:SetCaret(self:SetArea(self:Selection(), text)) +end + +function EDITOR:OnTextChanged() +end + +function EDITOR:_OnLoseFocus() + if self.TabFocus then + self:RequestFocus() + self.TabFocus = nil + end +end + +-- removes the first 0-4 spaces from a string and returns it +local function unindent(line) + --local i = line:find("%S") + --if i == nil or i > 5 then i = 5 end + --return line:sub(i) + return line:match("^ ? ? ? ?(.*)$") +end + +function EDITOR:_OnTextChanged() + local ctrlv = false + local text = self.TextEntry:GetValue() + self.TextEntry:SetText("") + + if (input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL)) and not (input.IsKeyDown(KEY_LALT) or input.IsKeyDown(KEY_RALT)) then + -- ctrl+[shift+]key + if input.IsKeyDown(KEY_V) then + -- ctrl+[shift+]V + ctrlv = true + else + -- ctrl+[shift+]key with key ~= V + return + end + end + + if text == "" then return end + if not ctrlv then + if text == "\n" then return end + if text == "}" and GetConVarNumber('wire_expression2_autoindent') ~= 0 then + self:SetSelection(text) + local row = self.Rows[self.Caret[1]] + if string.match("{" .. row .. "}", "^%b{}.*$") then + local newrow = unindent(row) + self.Rows[self.Caret[1]] = newrow + self.Caret[2] = self.Caret[2] + newrow:len()-row:len() + self.Start[2] = self.Caret[2] + end + return + end + end + + self:SetSelection(text) +end + +function EDITOR:OnMouseWheeled(delta) + self.Scroll[1] = self.Scroll[1] - 4 * delta + if self.Scroll[1] < 1 then self.Scroll[1] = 1 end + if self.Scroll[1] > #self.Rows then self.Scroll[1] = #self.Rows end + self.ScrollBar:SetScroll(self.Scroll[1] - 1) +end + +function EDITOR:OnShortcut() +end + +function EDITOR:ScrollCaret() + if self.Caret[1] - self.Scroll[1] < 2 then + self.Scroll[1] = self.Caret[1] - 2 + if self.Scroll[1] < 1 then self.Scroll[1] = 1 end + end + + if self.Caret[1] - self.Scroll[1] > self.Size[1] - 2 then + self.Scroll[1] = self.Caret[1] - self.Size[1] + 2 + if self.Scroll[1] < 1 then self.Scroll[1] = 1 end + end + + if self.Caret[2] - self.Scroll[2] < 4 then + self.Scroll[2] = self.Caret[2] - 4 + if self.Scroll[2] < 1 then self.Scroll[2] = 1 end + end + + if self.Caret[2] - 1 - self.Scroll[2] > self.Size[2] - 4 then + self.Scroll[2] = self.Caret[2] - 1 - self.Size[2] + 4 + if self.Scroll[2] < 1 then self.Scroll[2] = 1 end + end + + self.ScrollBar:SetScroll(self.Scroll[1] - 1) +end + +function EDITOR:FindFunction(self,reversed,searchterm,MatchCase) + //local reversed = self:GetParent().Reversed + //local searchterm = self:GetParent().String:GetValue() + if searchterm=="" then return end + //local oldself = self + //self = self:GetParent():GetParent() + if !MatchCase then + searchterm = string.lower(searchterm) + end + local Num,Row = 1,1 + local find = false + local currentrow = Row + if !reversed then + if self.Caret[1] < self.Start[1] then + Row=self.Caret[1] + else + Row=self.Start[1] + end + if self.Caret[2] < self.Start[2] then + Num=self.Caret[2] + else + Num=self.Start[2] + end + if (MatchCase and self:GetSelection()==searchterm) or (!MatchCase and string.lower(self:GetSelection())==searchterm) then + Num=Num+1 + end + for i=Row, #self.Rows do + local row = self.Rows[i] + if !MatchCase then + row = string.lower(row) + end + find = string.find(row,searchterm,Num,true) + currentrow = i + Num=1 + if find then break end + end + else + if self.Caret[1] > self.Start[1] then + Row=self.Caret[1] + else + Row=self.Start[1] + end + if self.Caret[2] > self.Start[2] then + Num=self.Caret[2] + else + Num=self.Start[2] + end + if (MatchCase and self:GetSelection()==searchterm) or (!MatchCase and string.lower(self:GetSelection())==searchterm) then + Num=Num-1 + end + searchterm = string.reverse(searchterm) + Num=#self.Rows[Row] - Num +2 + for i=1, Row do + local now = Row-i+1 + local row = self.Rows[now] + row = string.reverse(row) + if !MatchCase then + row = string.lower(row) + end + find = string.find(row,searchterm,Num,true) + currentrow = now + Num=1 + if find then + find = #self.Rows[now] - (find - 2) - #searchterm + break + end + end + end + if find then + self.Caret[1] = currentrow + self.Caret[2] = find+#searchterm + self.Start[1] = currentrow + self.Start[2] = find + self:ScrollCaret() + /* + else + if self.eof && type(self.eof)=="Panel" && self.eof:IsValid() then + self.eof:Close() + end + self.eof = vgui.Create("DFrame", oldself) + local popup = self.eof + popup:SetSize(200,100) + popup:Center() + popup:SetTitle("End of file") + popup:MakePopup() + popup.Text = vgui.Create("DLabel", popup) + popup.Text:SetPos(20,20) + popup.Text:SetSize(200,20) + popup.Text:SetText("File end has been reached") + //*/ + end +end + +function EDITOR:ReplaceNextFunction(self,ToRep,RepWith,MatchCase) + local oldcoords = {self.Caret[1],self.Caret[2],self.Start[1],self.Start[2]} + if ToRep == "" then return end + self:FindFunction(self,false,ToRep,MatchCase) + if oldcoords[1]!=self.Caret[1] or oldcoords[2]!=self.Caret[2] or oldcoords[3]!=self.Start[1] or oldcoords[4]!=self.Start[2] then + self:SetArea(self:Selection(),RepWith) + self.Caret[2]=self.Caret[2]-(#ToRep-#RepWith) + self:ScrollCaret() + end +end + +function EDITOR:ReplaceAllFunction(self,ToRep,RepWith,MatchCase) + if ToRep == "" then return end + if MatchCase then + local text = string.gsub(self:GetValue(),ToRep,RepWith) + self:SetArea({{1,1},{#self.Rows, string.len(self.Rows[#self.Rows]) + 1}},text) + self:ScrollCaret() + return + end + local originaltext = self:GetValue() + local text = string.lower(originaltext) + ToRep = string.lower(ToRep) + local offset = #ToRep-#RepWith + local totaloffset = 0 + local curpos = 1 + local chardiff = #ToRep + local success = false + repeat + local find = string.find(text,ToRep,curpos,true) + if find then + success = true + originaltext = string.sub(originaltext,1,find+totaloffset-1)..RepWith..string.sub(originaltext,find+totaloffset+#ToRep) + totaloffset=totaloffset-offset + curpos = find+chardiff + end + until !find + if success then + self:SetArea({{1,1},{#self.Rows, string.len(self.Rows[#self.Rows]) + 1}},originaltext) + self:ScrollCaret() + end +end + +function EDITOR:FindWindow() + // Does a find box already exist? Kill it + if self.FW && type(self.FW)=="Panel" && self.FW:IsValid() then + self.FW:Close() + end + + // Create the frame, make it highlight the line and show cursor + FW = vgui.Create("DFrame",self) + self.FW = FW + FW.OldThink = FW.Think + FW.Think = function(self) + self:GetParent().ForceDrawCursor = true + self:OldThink() + end + FW.OldClose = FW.Close + FW.Close = function(self) + self:GetParent().ForceDrawCursor = false + self:OldClose(self) + end + FW.Reversed = false + FW:SetSize(250,100) + FW:ShowCloseButton(true) + FW:SetTitle("Search") + FW:MakePopup() + FW:Center() + + // Search Textbox + FW.String = vgui.Create("DTextEntry",FW) + FW.String:SetPos(10,30) + FW.String:SetSize(230,20) + FW.String:RequestFocus() + FW.String.OnKeyCodeTyped = function(self,code) + if ( code == KEY_ENTER ) then + self:GetParent().Next.DoClick(self:GetParent().Next) + end + end + + // Forward Checkbox + FW.Forw = vgui.Create("DCheckBox",FW) + FW.Forw:SetPos(115,55) + FW.Forw:SetValue(true) + FW.Forw.OnMousePressed = function(self) + if !self:GetChecked() then + self:GetParent().Back:SetValue(self:GetChecked()) + self:GetParent().Reversed = false + self:SetValue(!self:GetChecked()) + end + end + + // Backward Checkbox + FW.Back = vgui.Create("DCheckBox",FW) + FW.Back:SetPos(115,75) + FW.Back:SetValue(false) + FW.Back.OnMousePressed = function(self) + if !self:GetChecked() then + self:GetParent().Forw:SetValue(self:GetChecked()) + self:GetParent().Reversed = true + self:SetValue(!self:GetChecked()) + end + end + + // Case Sensitive Checkbox + FW.Case = vgui.Create("DCheckBoxLabel",FW) + FW.Case:SetPos(10,75) + FW.Case:SetValue(false) + FW.Case:SetText("Case Sensitive") + FW.Case:SizeToContents() + + // Checkbox Labels + local Label = vgui.Create("DLabel",FW) + local xpos, ypos = FW.Forw:GetPos() + Label:SetPos(xpos+20,ypos-3) + Label:SetText("Forward") + Label = vgui.Create("DLabel",FW) + local xpos, ypos = FW.Back:GetPos() + Label:SetPos(xpos+20,ypos-3) + Label:SetText("Backward") + + // Cancel Button + FW.CloseB = vgui.Create("DButton",FW) + FW.CloseB:SetText("Cancel") + FW.CloseB:SetPos(190,75) + FW.CloseB:SetSize(50,20) + FW.CloseB.DoClick = function(self) + self:GetParent():Close() + end + + // Find Button + FW.Next = vgui.Create("DButton",FW) + FW.Next:SetText("Find") + FW.Next:SetPos(190,52) + FW.Next:SetSize(50,20) + FW.Next.DoClick = function(self) + self = self:GetParent():GetParent() + self:FindFunction(self,self.FW.Reversed,self.FW.String:GetValue(),self.FW.Case:GetChecked()) + end +end + +function EDITOR:FindAndReplaceWindow() + // Does a find box already exist? Kill it + if self.FRW && type(self.FRW)=="Panel" && self.FRW:IsValid() then + self.FRW:Close() + end + + // Create the frame, make it highlight the line and show cursor + FRW = vgui.Create("DFrame",self) + self.FRW = FRW + FRW.OldThink = FRW.Think + FRW.Think = function(self) + self:GetParent().ForceDrawCursor = true + self:OldThink() + end + FRW.OldClose = FRW.Close + FRW.Close = function(self) + self:GetParent().ForceDrawCursor = false + self:OldClose(self) + end + FRW:SetSize(250,142) + FRW:ShowCloseButton(true) + FRW:SetTitle("Replace") + FRW:MakePopup() + FRW:Center() + + // ToReplace Textentry + FRW.ToRep = vgui.Create("DTextEntry",FRW) + FRW.ToRep:SetPos(10,30) + FRW.ToRep:SetSize(230,20) + FRW.ToRep:RequestFocus() + FRW.ToRep.OnKeyCodeTyped = function(self,code) + if ( code == KEY_ENTER ) then + //self:GetParent().Replace.DoClick(self:GetParent().Next) + self:GetParent().RepWith:RequestFocus() + end + end + + // ReplaceWith Textentry + FRW.RepWith = vgui.Create("DTextEntry",FRW) + FRW.RepWith:SetPos(10,64) + FRW.RepWith:SetSize(230,20) + + // Text Labels + local Label = vgui.Create("DLabel",FRW) + Label:SetPos(12,50) + Label:SetText("Replace With:") + Label:SizeToContents() + + // Case Sensitive Checkbox + FRW.Case = vgui.Create("DCheckBoxLabel",FRW) + FRW.Case:SetPos(10,117) + FRW.Case:SetValue(false) + FRW.Case:SetText("Case Sensitive") + FRW.Case:SizeToContents() + + // Cancel Button + FRW.CloseB = vgui.Create("DButton",FRW) + FRW.CloseB:SetText("Cancel") + FRW.CloseB:SetPos(190,115) + FRW.CloseB:SetSize(50,20) + FRW.CloseB.DoClick = function(self) + self:GetParent():Close() + end + + // Replace Button + FRW.Replace = vgui.Create("DButton",FRW) + FRW.Replace:SetText("Replace") + FRW.Replace:SetPos(190,90) + FRW.Replace:SetSize(50,21) + FRW.Replace.DoClick = function(self) + self = self:GetParent():GetParent() + self:ReplaceNextFunction(self,self.FRW.ToRep:GetValue(),self.FRW.RepWith:GetValue(),self.FRW.Case:GetChecked()) + end + + // Replace All Button + FRW.ReplaceAll = vgui.Create("DButton",FRW) + FRW.ReplaceAll:SetText("Replace All") + FRW.ReplaceAll:SetPos(127,90) + FRW.ReplaceAll:SetSize(60,21) + FRW.ReplaceAll.DoClick = function(self) + self = self:GetParent():GetParent() + self:ReplaceAllFunction(self,self.FRW.ToRep:GetValue(),self.FRW.RepWith:GetValue(),self.FRW.Case:GetChecked()) + end + +end + + +function EDITOR:CanUndo() + return #self.Undo > 0 +end + +function EDITOR:DoUndo() + if #self.Undo > 0 then + local undo = self.Undo[#self.Undo] + self.Undo[#self.Undo] = nil + + self:SetCaret(self:SetArea(undo[1], undo[2], true, false, undo[3], undo[4])) + end +end + +function EDITOR:CanRedo() + return #self.Redo > 0 +end + +function EDITOR:DoRedo() + if #self.Redo > 0 then + local redo = self.Redo[#self.Redo] + self.Redo[#self.Redo] = nil + + self:SetCaret(self:SetArea(redo[1], redo[2], false, true, redo[3], redo[4])) + end +end + +function EDITOR:SelectAll() + self.Caret = {#self.Rows, string.len(self.Rows[#self.Rows]) + 1} + self.Start = {1, 1} + self:ScrollCaret() +end + +function EDITOR:Indent(shift) + -- TAB with a selection -- + -- remember scroll position + local tab_scroll = self:CopyPosition(self.Scroll) + + -- normalize selection, so it spans whole lines + local tab_start, tab_caret = self:MakeSelection(self:Selection()) + tab_start[2] = 1 + + if (tab_caret[2] ~= 1) then + tab_caret[1] = tab_caret[1] + 1 + tab_caret[2] = 1 + end + + -- remember selection + self.Caret = self:CopyPosition(tab_caret) + self.Start = self:CopyPosition(tab_start) + -- (temporarily) adjust selection, so there is no empty line at its end. + if (self.Caret[2] == 1) then + self.Caret = self:MovePosition(self.Caret, -1) + end + if shift then + -- shift-TAB with a selection -- + local tmp = self:GetSelection():gsub("\n ? ? ? ?", "\n") + + -- makes sure that the first line is outdented + self:SetSelection(unindent(tmp)) + else + -- plain TAB with a selection -- + self:SetSelection(" " .. self:GetSelection():gsub("\n", "\n ")) + end + -- restore selection + self.Caret = self:CopyPosition(tab_caret) + self.Start = self:CopyPosition(tab_start) + -- restore scroll position + self.Scroll = self:CopyPosition(tab_scroll) + -- trigger scroll bar update (TODO: find a better way) + self:ScrollCaret() +end + +function EDITOR:CommentSelection(shift) -- Multi-line comment feature ((shift-)ctrl-k) (idea by Jeremydeath) + if not self:HasSelection() then return end + + local comment_char = self:GetParent().E2 and "#" or "//" + + -- Comment a selection -- + -- remember scroll position + local scroll = self:CopyPosition(self.Scroll) + + -- normalize selection, so it spans whole lines + local sel_start, sel_caret = self:MakeSelection(self:Selection()) + sel_start[2] = 1 + + if (sel_caret[2] ~= 1) then + sel_caret[1] = sel_caret[1] + 1 + sel_caret[2] = 1 + end + + -- remember selection + self.Caret = self:CopyPosition(sel_caret) + self.Start = self:CopyPosition(sel_start) + -- (temporarily) adjust selection, so there is no empty line at its end. + if (self.Caret[2] == 1) then + self.Caret = self:MovePosition(self.Caret, -1) + end + if shift then + -- shift-TAB with a selection -- + local tmp = string.gsub("\n"..self:GetSelection(), "\n"..comment_char, "\n") + + -- makes sure that the first line is outdented + self:SetSelection(tmp:sub(2)) + else + -- plain TAB with a selection -- + self:SetSelection(comment_char .. self:GetSelection():gsub("\n", "\n"..comment_char)) + end + -- restore selection + self.Caret = self:CopyPosition(sel_caret) + self.Start = self:CopyPosition(sel_start) + -- restore scroll position + self.Scroll = self:CopyPosition(scroll) + -- trigger scroll bar update (TODO: find a better way) + self:ScrollCaret() +end + +function EDITOR:ContextHelp() + local word + if self:HasSelection() then + word = self:GetSelection() + else + local row, col = unpack(self.Caret) + local line = self.Rows[row] + if not line:sub(col, col):match("^[a-zA-Z0-9_]$") then + col = col - 1 + end + if not line:sub(col, col):match("^[a-zA-Z0-9_]$") then + surface.PlaySound("buttons/button19.wav") + return + end + + -- TODO substitute this for getWordStart, if it fits. + local startcol = col + while startcol > 1 and line:sub(startcol-1, startcol-1):match("^[a-zA-Z0-9_]$") do + startcol = startcol - 1 + end + + -- TODO substitute this for getWordEnd, if it fits. + local _,endcol = line:find("[^a-zA-Z0-9_]", col) + endcol = (endcol or 0) - 1 + + word = line:sub(startcol, endcol) + end + if self:GetParent().E2 then + E2Helper.Show(word) + else + -- TODO: Add CPU/GPU context help + WireLib.AddNotify('"'..word..'"', NOTIFY_GENERIC, 5) -- TODO: comment this notify once the CPU context help is in + end +end + +function EDITOR:_OnKeyCodeTyped(code) + self.Blink = RealTime() + + local alt = input.IsKeyDown(KEY_LALT) or input.IsKeyDown(KEY_RALT) + if alt then return end + + local shift = input.IsKeyDown(KEY_LSHIFT) or input.IsKeyDown(KEY_RSHIFT) + local control = input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL) + + -- allow ctrl-ins and shift-del (shift-ins, like ctrl-v, is handled by vgui) + if not shift and control and code == KEY_INSERT then + shift,control,code = true,false,KEY_C + elseif shift and not control and code == KEY_DELETE then + shift,control,code = false,true,KEY_X + end + + if control then + if code == KEY_A then + self:SelectAll() + elseif code == KEY_Z then + self:DoUndo() + elseif code == KEY_Y then + self:DoRedo() + elseif code == KEY_X then + if self:HasSelection() then + self.clipboard = self:GetSelection() + self.clipboard = string.Replace(self.clipboard, "\n", "\r\n") + SetClipboardText(self.clipboard) + self:SetSelection() + end + elseif code == KEY_C then + if self:HasSelection() then + self.clipboard = self:GetSelection() + self.clipboard = string.Replace(self.clipboard, "\n", "\r\n") + SetClipboardText(self.clipboard) + end + -- pasting is now handled by the textbox that is used to capture input + --[[ + elseif code == KEY_V then + if self.clipboard then + self:SetSelection(self.clipboard) + end + ]] + elseif code == KEY_F then + self:FindWindow() + elseif code == KEY_H then + self:FindAndReplaceWindow() + elseif code == KEY_K then + self:CommentSelection(shift) + elseif code == KEY_Q then + self:GetParent():Close() + elseif code == KEY_UP then + self.Scroll[1] = self.Scroll[1] - 1 + if self.Scroll[1] < 1 then self.Scroll[1] = 1 end + elseif code == KEY_DOWN then + self.Scroll[1] = self.Scroll[1] + 1 + elseif code == KEY_LEFT then + if self:HasSelection() and !shift then + self.Start = self:CopyPosition(self.Caret) + else + self.Caret = self:wordLeft(self.Caret) + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_RIGHT then + if self:HasSelection() and !shift then + self.Start = self:CopyPosition(self.Caret) + else + self.Caret = self:wordRight(self.Caret) + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + --[[ -- old code that scrolls on ctrl-left/right: + elseif code == KEY_LEFT then + self.Scroll[2] = self.Scroll[2] - 1 + if self.Scroll[2] < 1 then self.Scroll[2] = 1 end + elseif code == KEY_RIGHT then + self.Scroll[2] = self.Scroll[2] + 1 + ]] + elseif code == KEY_HOME then + self.Caret[1] = 1 + self.Caret[2] = 1 + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_END then + self.Caret[1] = #self.Rows + self.Caret[2] = 1 + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + end + + else + + if code == KEY_ENTER then + local row = self.Rows[self.Caret[1]]:sub(1,self.Caret[2]-1) + local diff = (row:find("%S") or (row:len()+1))-1 + local tabs = string.rep(" ", math.floor(diff / 4)) + if GetConVarNumber('wire_expression2_autoindent') ~= 0 and (string.match("{" .. row .. "}", "^%b{}.*$") == nil) then tabs = tabs .. " " end + self:SetSelection("\n" .. tabs) + elseif code == KEY_UP then + if self.Caret[1] > 1 then + self.Caret[1] = self.Caret[1] - 1 + + local length = string.len(self.Rows[self.Caret[1]]) + if self.Caret[2] > length + 1 then + self.Caret[2] = length + 1 + end + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_DOWN then + if self.Caret[1] < #self.Rows then + self.Caret[1] = self.Caret[1] + 1 + + local length = string.len(self.Rows[self.Caret[1]]) + if self.Caret[2] > length + 1 then + self.Caret[2] = length + 1 + end + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_LEFT then + if self:HasSelection() and !shift then + self.Start = self:CopyPosition(self.Caret) + else + self.Caret = self:MovePosition(self.Caret, -1) + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_RIGHT then + if self:HasSelection() and !shift then + self.Start = self:CopyPosition(self.Caret) + else + self.Caret = self:MovePosition(self.Caret, 1) + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_PAGEUP then + self.Caret[1] = self.Caret[1] - math.ceil(self.Size[1] / 2) + self.Scroll[1] = self.Scroll[1] - math.ceil(self.Size[1] / 2) + if self.Caret[1] < 1 then self.Caret[1] = 1 end + + local length = string.len(self.Rows[self.Caret[1]]) + if self.Caret[2] > length + 1 then self.Caret[2] = length + 1 end + if self.Scroll[1] < 1 then self.Scroll[1] = 1 end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_PAGEDOWN then + self.Caret[1] = self.Caret[1] + math.ceil(self.Size[1] / 2) + self.Scroll[1] = self.Scroll[1] + math.ceil(self.Size[1] / 2) + if self.Caret[1] > #self.Rows then self.Caret[1] = #self.Rows end + if self.Caret[1] == #self.Rows then self.Caret[2] = 1 end + + local length = string.len(self.Rows[self.Caret[1]]) + if self.Caret[2] > length + 1 then self.Caret[2] = length + 1 end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_HOME then + local row = self.Rows[self.Caret[1]] + local first_char = row:find("%S") or row:len()+1 + if self.Caret[2] == first_char then + self.Caret[2] = 1 + else + self.Caret[2] = first_char + end + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_END then + local length = string.len(self.Rows[self.Caret[1]]) + self.Caret[2] = length + 1 + + self:ScrollCaret() + + if !shift then + self.Start = self:CopyPosition(self.Caret) + end + elseif code == KEY_BACKSPACE then + if self:HasSelection() then + self:SetSelection() + else + local buffer = self:GetArea({self.Caret, {self.Caret[1], 1}}) + if self.Caret[2] % 4 == 1 and string.len(buffer) > 0 and string.rep(" ", string.len(buffer)) == buffer then + self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, -4)})) + else + self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, -1)})) + end + end + elseif code == KEY_DELETE then + if self:HasSelection() then + self:SetSelection() + else + local buffer = self:GetArea({{self.Caret[1], self.Caret[2] + 4}, {self.Caret[1], 1}}) + if self.Caret[2] % 4 == 1 and string.rep(" ", string.len(buffer)) == buffer and string.len(self.Rows[self.Caret[1]]) >= self.Caret[2] + 4 - 1 then + self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, 4)})) + else + self:SetCaret(self:SetArea({self.Caret, self:MovePosition(self.Caret, 1)})) + end + end + elseif code == KEY_F1 then + self:ContextHelp() + end + end + + if code == KEY_TAB or (control and (code == KEY_I or code == KEY_O)) then + if code == KEY_O then shift = not shift end + if code == KEY_TAB and control then shift = not shift end + if self:HasSelection() then + self:Indent(shift) + else + -- TAB without a selection -- + if shift then + local newpos = self.Caret[2]-4 + if newpos < 1 then newpos = 1 end + self.Start = { self.Caret[1], newpos } + if self:GetSelection():find("%S") then + -- TODO: what to do if shift-tab is pressed within text? + self.Start = self:CopyPosition(self.Caret) + else + self:SetSelection("") + end + else + local count = (self.Caret[2] + 2) % 4 + 1 + self:SetSelection(string.rep(" ", count)) + end + end + -- signal that we want our focus back after (since TAB normally switches focus) + if code == KEY_TAB then self.TabFocus = true end + end + + if control then + self:OnShortcut(code) + end +end + +// Auto-completion + +function EDITOR:IsVarLine() + local first = string.Explode(" ", self.Rows[self.Caret[1]])[1] + if(first == "@inputs" or first == "@outputs" or first == "@persist") then return true end + return false +end + +function EDITOR:getWordStart(caret) + local line = string.ToTable(self.Rows[caret[1]]) + if(#line < caret[2]) then return caret end + for i=0,caret[2] do + if(!line[caret[2]-i]) then return {caret[1],caret[2]-i+1} end + if(line[caret[2]-i] >= "a" and line[caret[2]-i] <= "z" or line[caret[2]-i] >= "A" and line[caret[2]-i] <= "Z" or line[caret[2]-i] >= "0" and line[caret[2]-i] <= "9") then else return {caret[1],caret[2]-i+1} end + end + return {caret[1],1} +end + +function EDITOR:getWordEnd(caret) + local line = string.ToTable(self.Rows[caret[1]]) + if(#line < caret[2]) then return caret end + for i=caret[2],#line do + if(!line[i]) then return {caret[1],i} end + if(line[i] >= "a" and line[i] <= "z" or line[i] >= "A" and line[i] <= "Z" or line[i] >= "0" and line[i] <= "9") then else return {caret[1],i} end + end + return {caret[1],#line+1} +end + +-- helpers for ctrl-left/right +function EDITOR:wordLeft(caret) + local row = self.Rows[caret[1]] + if caret[2] == 1 then + if caret[1] == 1 then return caret end + caret = { caret[1]-1, #self.Rows[caret[1]-1] } + row = self.Rows[caret[1]] + end + local pos = row:sub(1,caret[2]-1):match("[^%w@]()[%w@]+[^%w@]*$") + caret[2] = pos or 1 + return caret +end + +function EDITOR:wordRight(caret) + local row = self.Rows[caret[1]] + if caret[2] > #row then + if caret[1] == #self.Rows then return caret end + caret = { caret[1]+1, 1 } + row = self.Rows[caret[1]] + if row:sub(1,1) ~= " " then return caret end + end + local pos = row:match("[^%w@]()[%w@]",caret[2]) + caret[2] = pos or (#row+1) + return caret +end + +/***************************** Syntax highlighting ****************************/ + +function EDITOR:ResetTokenizer(row) + self.line = self.Rows[row] + self.position = 0 + self.character = "" + self.tokendata = "" +end + +function EDITOR:NextCharacter() + if not self.character then return end + + self.tokendata = self.tokendata .. self.character + self.position = self.position + 1 + + if self.position <= self.line:len() then + self.character = self.line:sub(self.position, self.position) + else + self.character = nil + end +end + +function EDITOR:NextPattern(pattern) + if !self.character then return false end + local startpos,endpos,text = self.line:find(pattern, self.position) + + if startpos ~= self.position then return false end + local buf = self.line:sub(startpos, endpos) + if not text then text = buf end + + self.tokendata = self.tokendata .. text + + + self.position = endpos + 1 + if self.position <= #self.line then + self.character = self.line:sub(self.position, self.position) + else + self.character = nil + end + return true +end + +do -- E2 Syntax highlighting + local function istype(tp) + return wire_expression_types[tp:upper()] or tp == "number" + end + + -- keywords[name][nextchar!="("] + local keywords = { + -- keywords that can be followed by a "(": + ["if"] = { [true] = true, [false] = true }, + ["elseif"] = { [true] = true, [false] = true }, + ["while"] = { [true] = true, [false] = true }, + ["for"] = { [true] = true, [false] = true }, + + -- keywords that cannot be followed by a "(": + ["else"] = { [true] = true }, + ["break"] = { [true] = true }, + ["continue"] = { [true] = true }, + } + + -- fallback for nonexistant entries: + setmetatable(keywords, { __index=function(tbl,index) return {} end }) + + local directives = { + ["@name"] = 0, + ["@inputs"] = 1, + ["@outputs"] = 1, + ["@persist"] = 1, + ["@trigger"] = 2, + } + + local colors = { + ["directive"] = { Color(240, 240, 160), false}, + ["number"] = { Color(240, 160, 160), false}, + ["function"] = { Color(160, 160, 240), false}, + ["notfound"] = { Color(240, 96, 96), false}, + ["variable"] = { Color(160, 240, 160), false}, + ["string"] = { Color(128, 128, 128), false}, + ["keyword"] = { Color(160, 240, 240), false}, + ["operator"] = { Color(224, 224, 224), false}, + ["comment"] = { Color(128, 128, 128), false}, + ["ppcommand"] = { Color(240, 96, 240), false}, + ["typename"] = { Color(240, 160, 96), false}, + } + + function EDITOR:SyntaxColorLine(row) + -- cols[n] = { tokendata, color } + local cols = {} + + self:ResetTokenizer(row) + self:NextCharacter() + + local directive = nil + if self:NextPattern("^@[^ ]*") then + directive = directives[self.tokendata] + + -- check for unknown directives + if not directive then + return { + { "@", colors.directive }, + { self.line:sub(2), colors.notfound } + } + end + + -- check for plain text directives + if directive == 0 then return {{ self.line, colors.directive }} end + + -- parse the rest like regular code + cols = {{ self.tokendata, colors.directive }} + end + while self.character do + local tokenname = "" + self.tokendata = "" + + -- eat all spaces + self.NextPattern(" *") + if !self.character then break end + + -- eat next token + if self:NextPattern("^[0-9][0-9.e]*") then + tokenname = "number" + + elseif self:NextPattern("^[a-z][a-zA-Z0-9_]*") then + local sstr = string.Trim(self.tokendata) + if directive then + if directive == 1 and istype(sstr) then + tokenname = "typename" + elseif directive == 2 and (sstr == "all" or sstr == "none") then + tokenname = "directive" + else + tokenname = "notfound" + end + else + -- is this a keyword or a function? + local char = self.character or "" + local keyword = char != "(" + + self:NextPattern(" *") + + if self.character == "]" then + -- X[Y,typename] + tokenname = istype(sstr) and "typename" or "notfound" + elseif keywords[sstr][keyword] then + tokenname = "keyword" + elseif wire_expression2_funclist[sstr] then + tokenname = "function" + else + tokenname = "notfound" + end + end + + elseif self:NextPattern("^[A-Z][a-zA-Z0-9_]*") then + tokenname = "variable" + + elseif self.character == "\"" then + self:NextCharacter() + while self.character and self.character != "\"" do + if self.character == "\\" then self:NextCharacter() end + self:NextCharacter() + end + self:NextCharacter() + + tokenname = "string" + + elseif self:NextPattern("#[^ ]*") then + if PreProcessor["PP_"..self.tokendata:sub(2)] then + -- there is a preprocessor command by that name => mark as such + tokenname = "ppcommand" + else + -- eat the rest and mark as a comment + self:NextPattern(".*") + tokenname = "comment" + end + else + self:NextCharacter() + + tokenname = "operator" + end + + color = colors[tokenname] + if #cols > 1 and color == cols[#cols][2] then + cols[#cols][1] = cols[#cols][1] .. self.tokendata + else + cols[#cols + 1] = {self.tokendata, color} + end + end + + return cols + end -- EDITOR:SyntaxColorLine +end -- do... + +do + local colors = { + ["normal"] = { Color(240, 240, 160), false}, + ["number"] = { Color(240, 160, 160), false}, + ["opcode"] = { Color(160, 160, 240), false}, + ["compare"] = { Color(190, 190, 240), false}, + ["register"] = { Color(160, 240, 160), false}, + ["string"] = { Color(128, 128, 128), false}, + ["label"] = { Color(160, 240, 255), false}, + ["macro"] = { Color(240, 160, 255), false}, + ["comment"] = { Color(128, 128, 128), false}, + ["white"] = { Color(224, 224, 224), false}, + } + + local directives = { + DATA = true, + CODE = true, + db = true, + define = true, + alloc = true, + } + + function EDITOR:CPUGPUSyntaxColorLine(row) + -- cols[n] = { tokendata, color } + local cols = {} + self:ResetTokenizer(row) + self:NextCharacter() + + local gpu = self:GetParent().EditorType == "GPU" + + while self.character do + local tokenname = "" + self.tokendata = "" + + self.NextPattern(" *") + if !self.character then break end + + if self:NextPattern("^[0-9][0-9.]*") then + tokenname = "number" + + elseif self:NextPattern("^[a-zA-Z0-9_]+:") then + tokenname = "label" + + elseif self:NextPattern("^[a-zA-Z0-9_]+") then + local sstr = self.tokendata:Trim() + local opcode = WireLib.CPU.opcodes[sstr] + if opcode then + tokenname = "opcode" + if opcode >= 1 and opcode <= 7 or opcode == 15 then + tokenname = "compare" + end + elseif gpu and WireLib.CPU.gpuopcodes[sstr] then + tokenname = "opcode" + elseif WireLib.CPU.registers[sstr] then + tokenname = "register" + elseif directives[sstr] then + tokenname = "macro" + else + tokenname = "normal" + end + + elseif self.character == "'" then + self:NextCharacter() + while self.character and self.character != "'" do + if self.character == "\\" then self:NextCharacter() end + self:NextCharacter() + end + self:NextCharacter() + tokenname = "string" + + elseif self:NextPattern("^//.*$") then + tokenname = "comment" + + else + self:NextCharacter() + tokenname = "white" + end + + color = colors[tokenname] + if #cols > 1 and color == cols[#cols][2] then + cols[#cols][1] = cols[#cols][1] .. self.tokendata + else + cols[#cols + 1] = {self.tokendata, color} + end + end + return cols + end -- EDITOR:SyntaxColorLine +end -- do... + +-- register editor panel +vgui.Register("Expression2Editor", EDITOR, "Panel"); + +concommand.Add("wire_expression2_reloadeditor", function(ply, command, args) + local code = wire_expression2_editor and wire_expression2_editor:GetCode() + wire_expression2_editor = nil + CPU_Editor = nil + GPU_Editor = nil + include("wire/client/TextEditor.lua") + include("wire/client/wire_expression2_editor.lua") + initE2Editor() + if code then wire_expression2_editor:SetCode(code) end +end) diff --git a/lua/wire/client/WireDermaExts.lua b/lua/wire/client/WireDermaExts.lua new file mode 100644 index 0000000000..b3b918e87f --- /dev/null +++ b/lua/wire/client/WireDermaExts.lua @@ -0,0 +1,109 @@ +-- $Rev: 1453 $ +-- $LastChangedDate: 2009-08-01 18:42:53 -0700 (Sat, 01 Aug 2009) $ +-- $LastChangedBy: TomyLobo $ + +WireDermaExts = {} + +-- Shortcut functions for Wire tools to make their model select controls +-- TODO: redo category system +function ModelPlug_AddToCPanel(panel, category, toolname, _, _, textbox_label, height) + local list = list.Get("Wire_"..category.."_Models") + if table.Count(list) > 1 then + local ModelSelect = vgui.Create("DWireModelSelect", self) + ModelSelect:SetModelList(list, toolname .. "_model") + ModelSelect:SetHeight(height) + panel:AddPanel(ModelSelect) + end + if textbox_label and GetConVarNumber("cl_showmodeltextbox") > 0 then + panel:TextEntry(textbox_label, toolname .. "_model") + end +end + +function ModelPlug_AddToCPanel_Multi(panel, categories, toolname, _, _, textbox_label, height) + local ModelSelect = vgui.Create("DWireModelSelectMulti", panel) + ModelSelect:SetHeight(height) + panel:AddPanel(ModelSelect) + local cvar = toolname .. "_model" + for category, name in pairs_sortkeys(categories) do + local list = list.Get("Wire_"..category.."_Models") + if list then + ModelSelect:AddModelList(name, list, cvar) + end + end + if textbox_label and GetConVarNumber("cl_showmodeltextbox") > 0 then + panel:TextEntry(textbox_label, toolname .. "_model") + end +end + + +function WireDermaExts.ModelSelect(panel, convar, list, height, show_textbox) + if table.Count(list) > 1 then + local ModelSelect = vgui.Create("DWireModelSelect", panel) + ModelSelect:SetModelList(list, convar) + ModelSelect:SetHeight(height) + panel:AddPanel(ModelSelect) + if show_textbox and GetConVarNumber("cl_showmodeltextbox") > 0 then + panel:TextEntry("Model:", convar) + end + end +end + + +-- +-- Additional Derma controls +-- +-- This are under testing +-- I will try to have them included in to GMod when they are stable +-- + + +-- +-- DWireModelSelect +-- sexy model select +local PANEL = {} + +function PANEL:Init() + self:EnableVerticalScrollbar() + self:SetTall(66 * 2 + 2) +end + +function PANEL:SetHeight(height) + self:SetTall(66 * (height or 2) + 2) +end + +function PANEL:SetModelList( list, cvar ) + for model,v in pairs(list) do + local icon = vgui.Create("SpawnIcon") + icon:SetModel(model) + icon.Model = model + icon:SetSize(64, 64) + icon:SetTooltip(model) + self:AddPanel(icon, {[cvar] = model}) + end + self:SortByMember("Model", false) +end + +derma.DefineControl( "DWireModelSelect", "", PANEL, "DPanelSelect" ) + +-- +-- DWireModelSelectMulti +-- sexy tabbed model select with categories +local PANEL = {} + +function PANEL:Init() + self.ModelPanels = {} + self:SetTall(66 * 2 + 26) +end + +function PANEL:SetHeight(height) + self:SetTall(66 * (height or 2) + 26) +end + +function PANEL:AddModelList( Name, list, cvar ) + local PanelSelect = vgui.Create("DWireModelSelect", self) + PanelSelect:SetModelList(list, cvar) + self:AddSheet(Name, PanelSelect) + self.ModelPanels[Name] = PanelSelect +end + +derma.DefineControl( "DWireModelSelectMulti", "", PANEL, "DPropertySheet" ) diff --git a/lua/wire/client/WireMenus.lua b/lua/wire/client/WireMenus.lua new file mode 100644 index 0000000000..f9bd0d7c7f --- /dev/null +++ b/lua/wire/client/WireMenus.lua @@ -0,0 +1,96 @@ +-- $Rev: 1366 $ +-- $LastChangedDate: 2009-07-20 00:09:28 -0700 (Mon, 20 Jul 2009) $ +-- $LastChangedBy: tad2020 $ + +local Wire_Categories = { + "Wire - Advanced", + "Wire - Beacon", + "Wire - Control", + "Wire - Data", + "Wire - Detection", + "Wire - Display", + "Wire - Render", + "Wire - I/O", + "Wire - Physics", + "Wire - Tools", + "Administration", +} + +local function WireTab() + spawnmenu.AddToolTab( "Wire", "Wire" ) + for _,Category in ipairs(Wire_Categories) do + spawnmenu.AddToolCategory("Wire", Category, Category) + end + + --start: UGLY HACK, BAD BAD BAD D: + local oldspawnmenuAddToolMenuOption = spawnmenu.AddToolMenuOption + function spawnmenu.AddToolMenuOption(tab, category, ...) + if tab == "Main" and string.lower(string.Left(category, 4)) == "wire" then tab = "Wire" end + oldspawnmenuAddToolMenuOption(tab, category, ...) + end + --end: UGLY HACK, BAD BAD BAD D: +end +hook.Add( "AddToolMenuTabs", "WireTab", WireTab) + + + +-- TODO: add these to the device files themselves??? +local devs = { + ["#Max Wiremod Wheels"] = "wheels", + ["#Max Wiremod Waypoints"] = "waypoints", + ["#Max Wiremod Values"] = "values", + ["#Max Wiremod Two-way Radios"] = "twoway_radioes", + ["#Max Wiremod Turrets"] = "turrets", + ["#Max Wiremod Thrusters"] = "thrusters", + ["#Max Wiremod Target Finders"] = "target_finders", + ["#Max Wiremod Speedometers"] = "speedometers", + ["#Max Wiremod Spawners"] = "spawners", + ["#Max Wiremod Simple Explosives"] = "simple_explosive", + ["#Max Wiremod Sensors"] = "sensors", + ["#Max Wiremod Relays"] = "relays", + ["#Max Wiremod Rangers"] = "rangers", + ["#Max Wiremod Radios"] = "radioes", + ["#Max Wiremod Pods"] = "pods", + ["#Max Wiremod Sockets"] = "sockets", + ["#Max Wiremod Plugs"] = "plugs", + ["#Max Wiremod Outputs"] = "outputs", + ["#Max Wiremod Oscilloscopes"] = "oscilloscopes", + ["#Max Wiremod Numpads"] = "numpads", + ["#Max Wiremod Nailers"] = "nailers", + ["#Max Wiremod Locators"] = "locators", + ["#Max Wiremod Inputs"] = "inputs", + ["#Max Wiremod Hoverballs"] = "hoverballs", + ["#Max Wiremod Gyroscopes"] = "gyroscopes", + ["#Max Wiremod GPSes"] = "gpss", + ["#Max Wiremod Gates - Trig"] = "gate_trigs", + ["#Max Wiremod Gates - Time"] = "gate_times", + ["#Max Wiremod Gates - Selection"] = "gate_selections", + ["#Max Wiremod Gates - Memory"] = "gate_memorys", + ["#Max Wiremod Gates - Logic"] = "gate_logics", + ["#Max Wiremod Gates - Comparison"] = "gate_logics", + ["#Max Wiremod Gates"] = "gates", + ["#Max Wiremod Forcers"] = "forcers", + ["#Max Wiremod Explosives"] = "explosive", + ["#Max Wiremod Dual Inputs"] = "dual_inputs", + ["#Max Wiremod Detonators"] = "detonators", + ["#Max Wiremod CPUs"] = "cpus", + ["#Max Wiremod Buttons"] = "buttons", + ["#Max Wiremod Adv. Inputs"] = "adv_inputs", +} + +function AddWireAdminMaxDevice(pluralname, dev) + devs["Max Wiremod "..pluralname] = dev +end + +local function BuildAdminControlPanel(Panel) + for name,dev in pairs(devs) do + local slider = Panel:NumSlider(name, "sbox_maxwire_"..dev, 0, 999, 0) + slider.dev = dev + end +end + +local function AddWireAdminControlPanelMenu() + spawnmenu.AddToolMenuOption("Wire", "Administration", "WireAdminControlPanel", "Max Wire Devices", "", "", BuildAdminControlPanel, {}) +end +hook.Add("PopulateToolMenu", "AddAddWireAdminControlPanelMenu", AddWireAdminControlPanelMenu) + diff --git a/lua/wire/client/cl_gpulib.lua b/lua/wire/client/cl_gpulib.lua new file mode 100644 index 0000000000..12c15f1977 --- /dev/null +++ b/lua/wire/client/cl_gpulib.lua @@ -0,0 +1,131 @@ +local RT_CACHE_SIZE = 16 + +// +// Create rendertarget cache +// +if (!RenderTargetCache) then + RenderTargetCache = {} + for i=1,RT_CACHE_SIZE do + RenderTargetCache[i] = {} + RenderTargetCache[i].Target = GetRenderTarget("WireGPU_RT_"..i, 512, 512) + RenderTargetCache[i].Used = nil + end +end + +// +// Create basic fonts +// +surface.CreateFont("lucida console", 20, 800, true, false, "WireGPU_ConsoleFont") + +// +// Create screen textures and materials +// +WireGPU_matScreen = Material("models\duckeh\buttons\0") +WireGPU_texScreen = surface.GetTextureID("models\duckeh\buttons\0") + +// +// Rendertarget cache management +// +function WireGPU_NeedRenderTarget(entindex) + for i=1,#RenderTargetCache do + if (not RenderTargetCache[i].Used) then + RenderTargetCache[i].Used = entindex + return RenderTargetCache[i].Target + end + end + + //Need to create new. PANIC?! + //print("RENDER TARGET PANIC. RENDER TARGET PANIC!") + return RenderTargetCache[1].Target +end + +function WireGPU_GetMyRenderTarget(entindex) + for i=1,#RenderTargetCache do + if ((RenderTargetCache[i].Used) and + (RenderTargetCache[i].Used == entindex)) then + return RenderTargetCache[i].Target + end + end + return WireGPU_NeedRenderTarget(entindex) +end + +function WireGPU_ReturnRenderTarget(entindex) + for i=1,#RenderTargetCache do + if ((RenderTargetCache[i].Used) and + (RenderTargetCache[i].Used == entindex)) then + RenderTargetCache[i].Used = nil + end + end +end + +// +// Misc helper functions +// +function WireGPU_DrawScreen(x,y,w,h,rotation,scale) + vertex = {} + + //Generate vertex data + vertex[1] = {} + vertex[1]["x"] = x + vertex[1]["y"] = y + + vertex[2] = {} + vertex[2]["x"] = x+w + vertex[2]["y"] = y + + vertex[3] = {} + vertex[3]["x"] = x+w + vertex[3]["y"] = y+h + + vertex[4] = {} + vertex[4]["x"] = x + vertex[4]["y"] = y+h + + //Rotation + if (rotation == 0) then + vertex[4]["u"] = 0-scale + vertex[4]["v"] = 1+scale + vertex[3]["u"] = 1+scale + vertex[3]["v"] = 1+scale + vertex[2]["u"] = 1+scale + vertex[2]["v"] = 0-scale + vertex[1]["u"] = 0-scale + vertex[1]["v"] = 0-scale + end + + if (rotation == 1) then + vertex[2]["u"] = 0-scale + vertex[2]["v"] = 0-scale + vertex[3]["u"] = 1+scale + vertex[3]["v"] = 0-scale + vertex[4]["u"] = 1+scale + vertex[4]["v"] = 1+scale + vertex[1]["u"] = 0-scale + vertex[1]["v"] = 1+scale + end + + if (rotation == 2) then + vertex[3]["u"] = 0-scale + vertex[3]["v"] = 0-scale + vertex[4]["u"] = 1+scale + vertex[4]["v"] = 0-scale + vertex[1]["u"] = 1+scale + vertex[1]["v"] = 1+scale + vertex[2]["u"] = 0-scale + vertex[2]["v"] = 1+scale + end + + if (rotation == 3) then + vertex[4]["u"] = 0-scale + vertex[4]["v"] = 0-scale + vertex[1]["u"] = 1+scale + vertex[1]["v"] = 0-scale + vertex[2]["u"] = 1+scale + vertex[2]["v"] = 1+scale + vertex[3]["u"] = 0-scale + vertex[3]["v"] = 1+scale + end + + surface.DrawPoly(vertex) +end + diff --git a/lua/wire/client/cl_modelplug.lua b/lua/wire/client/cl_modelplug.lua new file mode 100644 index 0000000000..7a4fd75c34 --- /dev/null +++ b/lua/wire/client/cl_modelplug.lua @@ -0,0 +1,174 @@ +-- $Rev: 1504 $ +-- $LastChangedDate: 2009-08-10 07:26:51 -0700 (Mon, 10 Aug 2009) $ +-- $LastChangedBy: TomyLobo $ + +Msg("=== Loading Wire Model Packs ===\n") + +CreateConVar("cl_showmodeltextbox", "0") + +ModelPlugInfo = {} + +for _,filename in ipairs( file.Find("WireModelPacks/*.txt") ) do + local packtbl = util.KeyValuesToTable(file.Read("WireModelPacks/" .. filename) or {}) + for name,entry in pairs(packtbl) do + local categorytable = string.Explode(",", entry.categories or "none") or { "none" } + for _,cat in pairs(categorytable) do + list.Set( "Wire_"..cat.."_Models", entry.model, {} ) + end + end + MsgN("\tLoaded: "..filename) +end + + + +-- +-- Add some more options to the stools +-- + +--screens +list.Set( "WireScreenModels", "models/props_lab/monitor01b.mdl", {} ) +list.Set( "WireScreenModels", "models/props/cs_office/TV_plasma.mdl", {} ) +list.Set( "WireScreenModels", "models/props/cs_office/computer_monitor.mdl", {} ) +list.Set( "WireScreenModels", "models/kobilica/wiremonitorbig.mdl", {} ) +list.Set( "WireScreenModels", "models/kobilica/wiremonitorsmall.mdl", {} ) + +--sounds +local WireSounds = { + ["Warning"] = "common/warning.wav", + ["Talk"] = "common/talk.wav", + ["Button"] = "buttons/button15.wav", + ["Denied"] = "buttons/weapon_cant_buy.wav", + ["Zap"] = "ambient/energy/zap2.wav", + ["Oh No"] = "vo/npc/male01/ohno.wav", + ["Yeah"] = "vo/npc/male01/yeah02.wav", + ["apc alarm"] = "ambient/alarms/apc_alarm_loop1.wav", + ["Coast Siren"] = "coast.siren_citizen", + ["Bunker Siren"] = "coast.bunker_siren1", + ["Alarm Bell"] = "d1_canals.Floodgate_AlarmBellLoop", + ["Engine Start"] = "ATV_engine_start", + ["Engine Stop"] = "ATV_engine_stop", + ["Zombie Breathe"] = "NPC_PoisonZombie.Moan1", + ["Idle Zombies"] = "Zombie.Idle", + ["Turret Alert"] = "NPC_FloorTurret.Alert", + ["Helicopter Rotor"] = "NPC_CombineGunship.RotorSound", + ["Heartbeat"] = "k_lab.teleport_heartbeat", + ["Breathing"] = "k_lab.teleport_breathing", +} +for k,v in pairs(WireSounds) do + list.Set("WireSounds",k,{wire_soundemitter_sound=v}); +end + + +--some extra wheels that wired wheels have +local wastelandwheels = { + "models/props_wasteland/wheel01a.mdl", + "models/props_wasteland/wheel02a.mdl", + "models/props_wasteland/wheel03a.mdl", + "models/props_wasteland/wheel03b.mdl" +} +for k,v in pairs(wastelandwheels) do + if file.Exists("../"..v) then + list.Set( "WheelModels", v, { wheel_rx = 90, wheel_ry = 0, wheel_rz = 90} ) + end +end + + +--Cheeze's Buttons Pack or Wire Model Pack 1 +MsgN("\tAdding Cheeze's Buttons Pack") +local CheezesButtons = { + "models/cheeze/buttons/button_0.mdl", + "models/cheeze/buttons/button_1.mdl", + "models/cheeze/buttons/button_2.mdl", + "models/cheeze/buttons/button_3.mdl", + "models/cheeze/buttons/button_4.mdl", + "models/cheeze/buttons/button_5.mdl", + "models/cheeze/buttons/button_6.mdl", + "models/cheeze/buttons/button_7.mdl", + "models/cheeze/buttons/button_8.mdl", + "models/cheeze/buttons/button_9.mdl", + "models/cheeze/buttons/button_arm.mdl", + "models/cheeze/buttons/button_clear.mdl", + "models/cheeze/buttons/button_enter.mdl", + "models/cheeze/buttons/button_fire.mdl", + "models/cheeze/buttons/button_minus.mdl", + "models/cheeze/buttons/button_muffin.mdl", + "models/cheeze/buttons/button_plus.mdl", + "models/cheeze/buttons/button_reset.mdl", + "models/cheeze/buttons/button_set.mdl", + "models/cheeze/buttons/button_start.mdl", + "models/cheeze/buttons/button_stop.mdl", +} +for k,v in ipairs(CheezesButtons) do + if file.Exists("../"..v) then + list.Set( "ButtonModels", v, {} ) + list.Set( "Wire_button_Models", v, {} ) + end +end + +MsgN("\tAdding various Buttons from HL2 and Portal") +local Buttons = { + "models/props_citizen_tech/Firetrap_button01a.mdl", + --animated buttons from here + "models/props_lab/freightelevatorbutton.mdl", + "models/props/switch001.mdl", + "models/props_combine/combinebutton.mdl", + "models/props_mining/control_lever01.mdl", + "models/props_mining/freightelevatorbutton01.mdl", + "models/props_mining/freightelevatorbutton02.mdl", + "models/props_mining/switch01.mdl", + "models/props_mining/switch_updown01.mdl" +} +for k,v in ipairs(Buttons) do + if file.Exists("../"..v) then + list.Set( "Wire_button_Models", v, {} ) + end +end + + +--Thrusters +--Jaanus Thruster Pack +MsgN("\tJaanus' Thruster Pack") +local JaanusThrusters = { + "models/props_junk/garbage_metalcan001a.mdl", + "models/jaanus/thruster_flat.mdl", + "models/jaanus/thruster_invisi.mdl", + "models/jaanus/thruster_shoop.mdl", + "models/jaanus/thruster_smile.mdl", + "models/jaanus/thruster_muff.mdl", + "models/jaanus/thruster_rocket.mdl", + "models/jaanus/thruster_megaphn.mdl", + "models/jaanus/thruster_stun.mdl" +} +for k,v in pairs(JaanusThrusters) do + if file.Exists("../"..v) then + list.Set( "ThrusterModels", v, {} ) + end +end + +--Beer's models +MsgN("\tBeer's Model pack") + +--Keyboard +list.Set( "Wire_Keyboard_Models", "models/beer/wiremod/keyboard.mdl", {} ) +list.Set( "Wire_Keyboard_Models", "models/jaanus/wiretool/wiretool_input.mdl", {} ) + +--Hydraulic +list.Set( "Wire_Hydraulic_Models", "models/beer/wiremod/hydraulic.mdl", {} ) +list.Set( "Wire_Hydraulic_Models", "models/jaanus/wiretool/wiretool_siren.mdl", {} ) + +--GPS +list.Set( "Wire_GPS_Models", "models/beer/wiremod/gps.mdl", {} ) +list.Set( "Wire_GPS_Models", "models/jaanus/wiretool/wiretool_speed.mdl", {} ) + +--Numpad +list.Set( "Wire_Numpad_Models", "models/beer/wiremod/numpad.mdl", {} ) +list.Set( "Wire_Numpad_Models", "models/jaanus/wiretool/wiretool_input.mdl", {} ) +list.Set( "Wire_Numpad_Models", "models/jaanus/wiretool/wiretool_output.mdl", {} ) + +--Water Sensor +list.Set( "Wire_WaterSensor_Models", "models/beer/wiremod/watersensor.mdl", {} ) +list.Set( "Wire_WaterSensor_Models", "models/jaanus/wiretool/wiretool_range.mdl", {} ) + +--Target Finder +list.Set( "Wire_TargetFinder_Models", "models/beer/wiremod/targetfinder.mdl", {} ) +list.Set( "Wire_TargetFinder_Models", "models/props_lab/powerbox02d.mdl", {} ) diff --git a/lua/wire/client/cl_wirelib.lua b/lua/wire/client/cl_wirelib.lua new file mode 100644 index 0000000000..0f0513568d --- /dev/null +++ b/lua/wire/client/cl_wirelib.lua @@ -0,0 +1,315 @@ +-- $Rev: 1662 $ +-- $LastChangedDate: 2009-09-11 19:28:17 -0700 (Fri, 11 Sep 2009) $ +-- $LastChangedBy: TomyLobo $ + +local WIRE_SCROLL_SPEED = 0.5 +local WIRE_BLINKS_PER_SECOND = 2 +local CurPathEnt = {} +local Wire_DisableWireRender = 2 --bug with mode 0 and gmod2007beta + +WIRE_CLIENT_INSTALLED = 1 + +Msg("loading materials\n") +list.Add( "WireMaterials", "cable/rope_icon" ) +list.Add( "WireMaterials", "cable/cable2" ) +list.Add( "WireMaterials", "cable/xbeam" ) +list.Add( "WireMaterials", "cable/redlaser" ) +list.Add( "WireMaterials", "cable/blue_elec" ) +list.Add( "WireMaterials", "cable/physbeam" ) +list.Add( "WireMaterials", "cable/hydra" ) +--new wire materials by Acegikmo +list.Add( "WireMaterials", "arrowire/arrowire" ) +list.Add( "WireMaterials", "arrowire/arrowire2" ) + +local mats = { + ["tripmine_laser"] = Material("tripmine_laser"), + ["Models/effects/comball_tape"] = Material("Models/effects/comball_tape") +} +for _,mat in pairs(list.Get( "WireMaterials" )) do + Msg("loading material: ",mat,"\n") + mats[mat] = Material(mat) +end +local function getmat( mat ) + if mats[mat] == nil then + mats[mat] = Material(mat) + end + return mats[mat] +end +local beam_mat = mats["tripmine_laser"] +local beamhi_mat = mats["Models/effects/comball_tape"] + +function Wire_Render(ent) + if (not ent:IsValid()) then return end + if (Wire_DisableWireRender == 1) then return end + + if (Wire_DisableWireRender == 0) then + local path_count = ent:GetNetworkedBeamInt("wpn_count") or 0 + if (path_count <= 0) then return end + + local w,f = math.modf(CurTime()*WIRE_BLINKS_PER_SECOND) + local blink = nil + if (f < 0.5) then + blink = ent:GetNetworkedBeamString("BlinkWire") + end + + for i = 1,path_count do + local path_name = ent:GetNetworkedBeamString("wpn_" .. i) + if (blink ~= path_name) then + local net_name = "wp_"..path_name + local len = ent:GetNetworkedBeamInt(net_name) or 0 + + if (len > 0) then + local start = ent:GetNetworkedBeamVector(net_name .. "_start") + if (ent:IsValid()) then start = ent:LocalToWorld(start) end + local color_v = ent:GetNetworkedBeamVector(net_name .. "_col") + local color = Color(color_v.x, color_v.y, color_v.z, 255) + local width = ent:GetNetworkedBeamFloat(net_name .. "_width") + + local scroll = CurTime()*WIRE_SCROLL_SPEED + + render.SetMaterial(getmat(ent:GetNetworkedBeamString(net_name .. "_mat"))) + render.StartBeam(len+1) + render.AddBeam(start, width, scroll, color) + + for j=1,len do + local node_ent = ent:GetNetworkedBeamEntity(net_name .. "_" .. j .. "_ent") + local endpos = ent:GetNetworkedBeamVector(net_name .. "_" .. j .. "_pos") + if (node_ent:IsValid()) then + endpos = node_ent:LocalToWorld(endpos) + + scroll = scroll+(endpos-start):Length()/10 + + render.AddBeam(endpos, width, scroll, color) + + start = endpos + end + end + render.EndBeam() + end + end + end + + else + local p = ent.ppp + if p == nil then p = {next = -100} end + + if p.next < CurTime() then + p.next = CurTime() + 0.25 + p.paths = {} + + local path_count = ent:GetNetworkedBeamInt("wpn_count") or 0 + if (path_count <= 0) then return end + + for i = 1,path_count do + local x = {} + local path_name = ent:GetNetworkedBeamString("wpn_" .. i) + x.path_name = path_name + local net_name = "wp_"..path_name + local len = ent:GetNetworkedBeamInt(net_name) or 0 + + if (len > 0) then + + local start = ent:GetNetworkedBeamVector(net_name .. "_start") + x.startx = start + if (ent:IsValid()) then start = ent:LocalToWorld(start) end + local color_v = ent:GetNetworkedBeamVector(net_name .. "_col") + local color = Color(color_v.x, color_v.y, color_v.z, 255) + local width = ent:GetNetworkedBeamFloat(net_name .. "_width") + + local scroll = CurTime()*WIRE_SCROLL_SPEED + + x.material = getmat(ent:GetNetworkedBeamString(net_name .. "_mat")) + x.startbeam = len + 1 + x.start = start + x.width = width + x.scroll = scroll + x.color = color + x.beams = {} + + for j=1,len do + local v = {} + local node_ent = ent:GetNetworkedBeamEntity(net_name .. "_" .. j .. "_ent") + local endpos = ent:GetNetworkedBeamVector(net_name .. "_" .. j .. "_pos") + v.node_ent = node_ent + v.node_endpos = endpos + if (node_ent:IsValid()) then + endpos = node_ent:LocalToWorld(endpos) + + scroll = scroll+(endpos-start):Length()/10 + + v.endpos = endpos + v.width = width + v.scroll = scroll + v.color = color + table.insert(x.beams, v) + + start = endpos + end + end + + table.insert(p.paths, x) + + end + end + + ent.ppp = p + end + + + local w,f = math.modf(CurTime()*WIRE_BLINKS_PER_SECOND) + local blink = f < 0.5 + local blinkname = ent:GetNetworkedBeamString("BlinkWire") + for _,k in ipairs(p.paths) do + if not (blink and blinkname == k.path_name) then + k.scroll = CurTime()*WIRE_SCROLL_SPEED + k.start = ent:LocalToWorld(k.startx) + render.SetMaterial(k.material) + render.StartBeam(k.startbeam) + render.AddBeam(k.start, k.width, k.scroll, k.color) + for _,v in ipairs(k.beams) do + if (v.node_ent:IsValid()) then + local endpos = v.node_ent:LocalToWorld(v.node_endpos) + local scroll = k.scroll+(endpos-k.start):Length()/10 + render.AddBeam(endpos, v.width, scroll, v.color) + end + end + render.EndBeam() + end + end + + end +end + + +local function Wire_GetWireRenderBounds(ent) + if (not ent:IsValid()) then return end + + local paths = ent.WirePaths + local bbmin = ent:OBBMins() + local bbmax = ent:OBBMaxs() + + local path_count = ent:GetNetworkedBeamInt("wpn_count") or 0 + if (path_count > 0) then + for i = 1,path_count do + local path_name = ent:GetNetworkedBeamString("wpn_" .. i) + local net_name = "wp_"..path_name + local len = ent:GetNetworkedBeamInt(net_name) or 0 + + if (len > 0) then + for j=1,len do + local node_ent = ent:GetNetworkedBeamEntity(net_name .. "_" .. j .. "_ent") + local nodepos = ent:GetNetworkedBeamVector(net_name .. "_" .. j .. "_pos") + if (node_ent:IsValid()) then + nodepos = ent:WorldToLocal(node_ent:LocalToWorld(nodepos)) + + if (nodepos.x < bbmin.x) then bbmin.x = nodepos.x end + if (nodepos.y < bbmin.y) then bbmin.y = nodepos.y end + if (nodepos.z < bbmin.z) then bbmin.z = nodepos.z end + if (nodepos.x > bbmax.x) then bbmax.x = nodepos.x end + if (nodepos.y > bbmax.y) then bbmax.y = nodepos.y end + if (nodepos.z > bbmax.z) then bbmax.z = nodepos.z end + end + end + end + end + end + + if (ent.ExtraRBoxPoints) then + for _,point_l in ipairs( ent.ExtraRBoxPoints ) do + local point = point_l + if (point.x < bbmin.x) then bbmin.x = point.x end + if (point.y < bbmin.y) then bbmin.y = point.y end + if (point.z < bbmin.z) then bbmin.z = point.z end + if (point.x > bbmax.x) then bbmax.x = point.x end + if (point.y > bbmax.y) then bbmax.y = point.y end + if (point.z > bbmax.z) then bbmax.z = point.z end + end + end + + return bbmin, bbmax +end + + +function Wire_UpdateRenderBounds(ent) + local bbmin, bbmax = Wire_GetWireRenderBounds(ent) + ent:SetRenderBounds(bbmin, bbmax) +end + +local function WireDisableRender(pl, cmd, args) + if args[1] then + Wire_DisableWireRender = tonumber(args[1]) + end + Msg("\nWire DisableWireRender/WireRenderMode = "..tostring(Wire_DisableWireRender).."\n") +end +concommand.Add( "cl_Wire_DisableWireRender", WireDisableRender ) +concommand.Add( "cl_Wire_SetWireRenderMode", WireDisableRender ) + + +function Wire_DrawTracerBeam( ent, beam_num, hilight, beam_length ) + local beam_length = beam_length or ent:GetBeamLength(beam_num) + if (beam_length > 0) then + + local x, y = 0, 0 + if (ent.GetSkewX and ent.GetSkewY) then + x, y = ent:GetSkewX(beam_num), ent:GetSkewY(beam_num) + end + + local start, ang = ent:GetPos(), ent:GetAngles() + + if (ent.ls != start or ent.la != ang or ent.ll != beam_length or ent.lx != x or ent.ly != y) then + ent.ls, ent.la = start, ang + + if (ent.ll != beam_length or ent.lx != x or ent.ly != y) then + ent.ll, ent.lx, ent.ly = beam_length, x, y + + if (x == 0 and y == 0) then + ent.endpos = start + (ent:GetUp() * beam_length) + else + local skew = Vector(x, y, 1) + skew = skew*(beam_length/skew:Length()) + local beam_x = ent:GetRight()*skew.x + local beam_y = ent:GetForward()*skew.y + local beam_z = ent:GetUp()*skew.z + ent.endpos = start + beam_x + beam_y + beam_z + end + ent.ExtraRBoxPoints = ent.ExtraRBoxPoints or {} + ent.ExtraRBoxPoints[beam_num] = ent:WorldToLocal(ent.endpos) + else + ent.endpos = ent:LocalToWorld(ent.ExtraRBoxPoints[beam_num]) + end + end + + local trace = {} + trace.start = start + trace.endpos = ent.endpos + trace.filter = { ent } + if (ent:GetNetworkedBool("TraceWater")) then trace.mask = MASK_ALL end + trace = util.TraceLine(trace) + + render.SetMaterial(beam_mat) + render.DrawBeam(start, trace.HitPos, 6, 0, 10, Color(ent:GetColor())) + if (hilight) then + render.SetMaterial(beamhi_mat) + render.DrawBeam(start, trace.HitPos, 6, 0, 10, Color(255,255,255,255)) + end + end +end + +hook.Add("InitPostEntity", "language_strings", function() + for class, tbl in pairs(scripted_ents.GetList()) do + if tbl.t.PrintName and tbl.t.PrintName ~= "" then + language.Add( class, tbl.t.PrintName ) + end + end +end) + +if not CanRunConsoleCommand then + function CanRunConsoleCommand() return false end + hook.Add("OnEntityCreated", "CanRunConsoleCommand", function(ent) + if not ValidEntity(ent) then return end + if ent ~= LocalPlayer() then return end + + function CanRunConsoleCommand() return true end + hook.Remove("OnEntityCreated", "CanRunConsoleCommand") + end) +end diff --git a/lua/wire/client/e2descriptions.lua b/lua/wire/client/e2descriptions.lua new file mode 100644 index 0000000000..704c925804 --- /dev/null +++ b/lua/wire/client/e2descriptions.lua @@ -0,0 +1,488 @@ +if E2Helper then +E2Helper.Descriptions["mod"] = "Modulo, returns the Remainder after Argument 1 has been divided by Argument 2. Note \"mod(-1, 3) = -1\"" +E2Helper.Descriptions["sqrt"] = "Returns the Square Root of the Argument" +E2Helper.Descriptions["cbrt"] = "Returns the Cube Root of the Argument" +E2Helper.Descriptions["root"] = "Returns the Nth Root of the first Argument" +E2Helper.Descriptions["e"] = "Returns Euler's Constant" +E2Helper.Descriptions["exp"] = "Returns e to the power of the Argument (same as e()^N but shorter and faster this way)" +E2Helper.Descriptions["ln"] = " Returns the logarithm to base e of the Argument" +E2Helper.Descriptions["log2"] = "Returns the logarithm to base 2 of the Argument" +E2Helper.Descriptions["log10"] = " Returns the logarithm to base 10 of the Argument" +E2Helper.Descriptions["log"] = " Returns the logarithm to base Argument 2 of Argument 1" +E2Helper.Descriptions["abs"] = "Returns the Magnitude of the Argument" +E2Helper.Descriptions["ceil"] = "Rounds the Argument up to the nearest Integer" +E2Helper.Descriptions["ceil"] = "Rounds Argument 1 up to Argument 2's decimal precision" +E2Helper.Descriptions["floor"] = "Rounds the Argument down to the nearest Integer" +E2Helper.Descriptions["floor"] = "Rounds Argument 1 down to Argument 2's decimal precision" +E2Helper.Descriptions["round"] = "Rounds the Argument to the nearest Integer" +E2Helper.Descriptions["round"] = " Rounds Argument 1 to Argument 2's decimal precision" +E2Helper.Descriptions["int"] = "Returns the Integer part of the Argument (same as floor)" +E2Helper.Descriptions["frac"] = "Returns the Fractional part of the Argument (same as floor)" +E2Helper.Descriptions["clamp"] = "If Arg1 <= Arg2 (min) returns Arg2; If Arg1 >= Arg3 (max) returns Arg3; otherwise returns Arg1." +E2Helper.Descriptions["inrange"] = "Returns 1 if N is in the interval [N2; N3], 0 otherwise." +E2Helper.Descriptions["sign"] = "Returns the sign of argument (-1,0,1) [sign(N) = N / abs(N) ]" +E2Helper.Descriptions["min"] = "Returns the lowest value Argument" +E2Helper.Descriptions["min"] = "Returns the lowest value Argument" +E2Helper.Descriptions["min"] = "Returns the lowest value Argument" +E2Helper.Descriptions["max"] = "Returns the highest value Argument" +E2Helper.Descriptions["max"] = "Returns the highest value Argument" +E2Helper.Descriptions["max"] = "Returns the highest value Argument" +E2Helper.Descriptions["random"] = "Returns a random floating-point number between 0 and 1 [0 <= x < 1 ]" +E2Helper.Descriptions["random"] = "Returns a random floating-point number between 0 and the specified value [0 <= x < a ]" +E2Helper.Descriptions["random"] = "Returns a random floating-point number between the specified interval [a <= x < b ]" +E2Helper.Descriptions["randint"] = "Returns a random integer from 1 to the specified value [1 <= x <= a ]" +E2Helper.Descriptions["randint"] = "Returns a random integer in the specified interval [a <= x <= b ]" +E2Helper.Descriptions["pi"] = "Returns the constant PI" +E2Helper.Descriptions["toRad"] = "Converts Degree angles to Radian angles" +E2Helper.Descriptions["toDeg"] = "Converts Radian angles to Degree angles" +E2Helper.Descriptions["sin"] = "Returns the sine of N degrees" +E2Helper.Descriptions["cos"] = "Returns the cosine of N degrees" +E2Helper.Descriptions["tan"] = "Returns the tangent of N degrees" +E2Helper.Descriptions["asin"] = "Returns the inverse sine of the argument, in degrees" +E2Helper.Descriptions["acos"] = "Returns the inverse cosine of the argument, in degrees" +E2Helper.Descriptions["atan"] = "Returns the inverse tangent of the argument, in degrees" +E2Helper.Descriptions["sinh"] = "Returns the hyperbolic sine of N" +E2Helper.Descriptions["cosh"] = "Returns the hyperbolic cosine of N" +E2Helper.Descriptions["tanh"] = "Returns the hyperbolic tangent of N" +E2Helper.Descriptions["sinr"] = "Returns the sine of N radians" +E2Helper.Descriptions["cosr"] = "Returns the cosine of N radians" +E2Helper.Descriptions["tanr"] = "Returns the tangent of N radians" +E2Helper.Descriptions["asinr"] = "Returns the inverse sine of the argument, in radians" +E2Helper.Descriptions["acosr"] = "Returns the inverse cosine of the argument, in radians" +E2Helper.Descriptions["atanr"] = "Returns the inverse tangent of the argument, in radians" +E2Helper.Descriptions["index"] = "Returns Nth letter of the string, formatted as a string" +E2Helper.Descriptions["length"] = "Returns the length of the string" +E2Helper.Descriptions["upper"] = "All characters are made uppercase" +E2Helper.Descriptions["lower"] = "All characters are made lowercase" +E2Helper.Descriptions["sub"] = "Returns a substring, starting at the first number argument and ending at the second" +E2Helper.Descriptions["left"] = "Returns N amount of characters starting from the leftmost character" +E2Helper.Descriptions["right"] = "Returns N amount of characters starting from the rightmost character" +E2Helper.Descriptions["find"] = "Returns the 1st occurrence of the string S, returns 0 if not found" +E2Helper.Descriptions["find"] = "Returns the 1st occurrence of the string S starting at N and going to the end of the string, returns 0 if not found" +E2Helper.Descriptions["explode"] = "Splits the string into an array, along the boundaries formed by the string S. See also String.Explode" +E2Helper.Descriptions["repeat"] = "Repeats the input string N times" +E2Helper.Descriptions["trim"] = "Trims away spaces at the beginning and end of a string" +E2Helper.Descriptions["trimLeft"] = "Trims away opening spaces on the string" +E2Helper.Descriptions["trimRight"] = "Trims away spaces at the end of a string" +E2Helper.Descriptions["replace"] = "Finds and replaces every occurrence of the first argument with the second argument" +E2Helper.Descriptions["reverse"] = "Returns a reversed version of S" +E2Helper.Descriptions["toNumber"] = "Parses a number from a string" +E2Helper.Descriptions["toString"] = "Formats a number as a string. (Numbers may be concatenated into a string without using this function)" +E2Helper.Descriptions["toChar"] = "Returns a one-character string from its ASCII code" +E2Helper.Descriptions["toByte"] = "Returns the ASCII code of the 1st character in the string" +E2Helper.Descriptions["toByte"] = "Returns the ASCII code of the Nth character in the string" +E2Helper.Descriptions["format"] = "Formats a values exactly like Lua's string.format. Any number and type of parameter can be passed through the \"...\". Prints errors to the chat area." +E2Helper.Descriptions["match"] = "runs string.match(S, S2) and returns the sub-captures as an array." +E2Helper.Descriptions["match"] = "runs string.match(S, S2, N) and returns the sub-captures as an array." +E2Helper.Descriptions["matchFirst"] = "runs string.match(S, S2) and returns the first match or an empty string if the match failed." +E2Helper.Descriptions["matchFirst"] = "runs string.match(S, S2, N) and returns the first match or an empty string if the match failed." +E2Helper.Descriptions["entity"] = "Gets the entity associated with the id" +E2Helper.Descriptions["owner"] = "Gets the owner of the expression ( same as entity():owner() )" +E2Helper.Descriptions["id"] = "Gets the numeric id of an entity" +E2Helper.Descriptions["noentity"] = "Returns an invalid entity" +E2Helper.Descriptions["type"] = "Gets the class of an entity" +E2Helper.Descriptions["model"] = "Gets the model of an entity" +E2Helper.Descriptions["owner"] = "Gets the owner of an entity" +E2Helper.Descriptions["name"] = "Gets the name of a player" +E2Helper.Descriptions["steamID"] = "Gets the steam ID of the player" +E2Helper.Descriptions["pos"] = "Gets the position of the entity" +E2Helper.Descriptions["eye"] = "Gets a players view direction else entity forward direction" +E2Helper.Descriptions["eyeTrace"] = "Equivalent to rangerOffset(16384, E:shootPos(), E:eye()), but faster (causing less lag)" +E2Helper.Descriptions["shootPos"] = "Returns a players shoot position" +E2Helper.Descriptions["aimEntity"] = "Returns the entity that the entity is aiming at" +E2Helper.Descriptions["aimBone"] = "Returns the bone the player is currently aiming at" +E2Helper.Descriptions["aimPos"] = "Returns the point that the entity is looking at" +E2Helper.Descriptions["aimNormal"] = "Returns a normalized directional vector perpendicular to the surface pointed at" +E2Helper.Descriptions["frags"] = "Returns the number of kills the player has made" +E2Helper.Descriptions["deaths"] = "Returns the number of times the player died" +E2Helper.Descriptions["team"] = "Returns the team number a player is on" +E2Helper.Descriptions["teamName"] = "Returns the name of the team associated with the team number" +E2Helper.Descriptions["forward"] = "Gets the forward direction of the entity" +E2Helper.Descriptions["right"] = "Gets the right direction of the entity" +E2Helper.Descriptions["up"] = "Gets the up direction of the entity" +E2Helper.Descriptions["vel"] = "Gets the velocity of the entity" +E2Helper.Descriptions["velL"] = "Gets the local velocity of the entity" +E2Helper.Descriptions["boxCenter"] = "Gets the center of the entity's bounding box, as a local position vector" +E2Helper.Descriptions["boxMax"] = "Gets the maximum local XYZ of the entity's bounding box (the \"highest\" corner), as a local position vector" +E2Helper.Descriptions["boxMin"] = "Gets the minimum local XYZ of the entity's bounding box (the \"lowest\" corner), as a local position vector" +E2Helper.Descriptions["boxSize"] = "Gets the dimensions of the entity's bounding box as a vector (length, width, height)" +E2Helper.Descriptions["toWorld"] = "Transforms from a vector local to E to a world vector." +E2Helper.Descriptions["toLocal"] = "Transforms from a world vector to a vector local to E." +E2Helper.Descriptions["toWorld"] = "Transforms from an angle local to E to a world angle." +E2Helper.Descriptions["toLocal"] = "Transforms from a world angle to an angle local to E." +E2Helper.Descriptions["angVel"] = "Gets the angular velocity of the entity" +E2Helper.Descriptions["angVelVector"] = "Returns rotation axis, velocity and direction given as the vector's direction, magnitude and sense" +E2Helper.Descriptions["angles"] = "Gets the pitch, yaw and roll of the entity" +E2Helper.Descriptions["radius"] = "Gets the size of the object (not precisely, but useful)" +E2Helper.Descriptions["height"] = "Gets the height of a player or npc" +E2Helper.Descriptions["bearing"] = "Gets the bearing from the entity to the vector" +E2Helper.Descriptions["elevation"] = "Gets the elevation from the entity to the vector" +E2Helper.Descriptions["heading"] = "Gets the elevation and bearing from the entity to the vector" +E2Helper.Descriptions["health"] = "Gets the health of the entity" +E2Helper.Descriptions["armor"] = "Gets the armor of the player" +E2Helper.Descriptions["mass"] = "Gets the mass of the entity" +E2Helper.Descriptions["timeConnected"] = "Returns a players time connected to a server" +E2Helper.Descriptions["massCenter"] = "Gets the Center of Mass of the entity" +E2Helper.Descriptions["massCenterL"] = "Gets the center of mass as a local vector" +E2Helper.Descriptions["inertia"] = "Gets the principal components of the entity's inertia tensor in the form ( Ixx, Iyy, Izz )" +E2Helper.Descriptions["isPlayer"] = "Is the entity a player?" +E2Helper.Descriptions["isOnFire"] = "Is the entity on fire?" +E2Helper.Descriptions["isWeapon"] = "Is the entity a weapon?" +E2Helper.Descriptions["isNPC"] = "Is the entity a NPC?" +E2Helper.Descriptions["isFrozen"] = "Is the entity frozen?" +E2Helper.Descriptions["isVehicle"] = "Is the entity a vehicle?" +E2Helper.Descriptions["inVehicle"] = "Is the player in a vehicle?" +E2Helper.Descriptions["isWorld"] = "Is the entity the world?" +E2Helper.Descriptions["isOnGround"] = "Is the player/NPC resting on something?" +E2Helper.Descriptions["isUnderWater"] = "Is the entity under water?" +E2Helper.Descriptions["isPlayerHolding"] = "Is the entity being held by a player?" +E2Helper.Descriptions["isAlive"] = "Is the player or NPC alive?" +E2Helper.Descriptions["isCrouch"] = "Is the player crouching?" +E2Helper.Descriptions["inNoclip"] = "Is the player in noclip mode?" +E2Helper.Descriptions["keyAttack1"] = "Is the player pressing their primary fire key?" +E2Helper.Descriptions["keyAttack2"] = "Is the player pressing their secondary fire key?" +E2Helper.Descriptions["keyUse"] = "Is the player pressing their use key?" +E2Helper.Descriptions["printDriver"] = "Posts S to the chat of the driver of vehicle E. Returns 1 if the text was printed, 0 if not." +E2Helper.Descriptions["driver"] = "Returns the driver of the vehicle if there is one, nil otherwise" +E2Helper.Descriptions["passenger"] = "Returns the passenger of the vehicle if there is one, in single seat pods this will return the driver." +E2Helper.Descriptions["weapon"] = "Returns the weapon that player E is currently holding" +E2Helper.Descriptions["clip1"] = "Returns the amount of ammo in the primary clip of weapon E, -1 if there is no primary clip" +E2Helper.Descriptions["clip2"] = "Returns the amount of ammo in the secondary clip of weapon E, -1 if there is no secondary clip 1)" +E2Helper.Descriptions["primaryAmmoType"] = "Returns the type of primary ammo of weapon E as a number in a string" +E2Helper.Descriptions["secondaryAmmoType"] = "Returns the type of secondary ammo of weapon E as number in a string" +E2Helper.Descriptions["ammoCount"] = "Returns the amount of stored ammo of type S on player E, excluding current clip" +E2Helper.Descriptions["cross"] = "Gets the 2D vector cross product/wedge product" +E2Helper.Descriptions["toAngle"] = "Returns the 2D angle of the vector (given in degrees, -180 to 180)" +E2Helper.Descriptions["vec"] = "Makes a 3D vector" +E2Helper.Descriptions["vec"] = "Same as vec(0,0,0)" +E2Helper.Descriptions["vec"] = "Converts a 2D vector into a 3D vector (the z component is set to 0)" +E2Helper.Descriptions["vec"] = "Converts a 2D vector into a 3D vector (the z component is set to the second argument)" +E2Helper.Descriptions["vec"] = "Converts a 4D vector into a 3D vector (the w component is dropped)" +E2Helper.Descriptions["vec"] = "Changes an angle variable into a vector variable" +E2Helper.Descriptions["randvec"] = "Returns a uniformly distributed, random, normalized direction vector." +E2Helper.Descriptions["randvec"] = "Returns a random vector with its components between N1 and N2" +E2Helper.Descriptions["randvec"] = "Returns a random vector between V1 and V2" +E2Helper.Descriptions["cross"] = "Gets the 3D vector cross product" +E2Helper.Descriptions["shiftL"] = "Shifts the vector's components left: shiftL( x,y,z ) = ( y,z,x )" +E2Helper.Descriptions["shiftR"] = "Shifts the vector's components right: shiftR( x,y,z ) = ( z,x,y )" +E2Helper.Descriptions["rotate"] = "Gets the rotated vector" +E2Helper.Descriptions["rotate"] = "Gets the rotated vector" +E2Helper.Descriptions["toAngle"] = "Gets the angles of the vector" +E2Helper.Descriptions["dehomogenized"] = "Converts a 3D homogeneous vector (x,y,z,w) into a 3D cartesian vector" +E2Helper.Descriptions["isInWorld"] = "Returns 1 if the position vector is within the world, 0 if not" +E2Helper.Descriptions["ceil"] = "Rounds XYZ up to the nearest integer" +E2Helper.Descriptions["ceil"] = "Rounds XYZ up to argument 2's decimal precision" +E2Helper.Descriptions["floor"] = "Rounds XYZ down to the nearest integer" +E2Helper.Descriptions["floor"] = "Rounds XYZ down to argument 2's decimal precision" +E2Helper.Descriptions["round"] = "Rounds XYZ to the nearest integer" +E2Helper.Descriptions["round"] = "Rounds XYZ to argument 2's decimal precision" +E2Helper.Descriptions["mod"] = "Returns the remainder after XYZ have been divided by argument 2" +E2Helper.Descriptions["mod"] = "Returns the remainder after the components of vector 1 have been divided by the components of vector 2" +E2Helper.Descriptions["clamp"] = "Clamps vector 1's XYZ between the XYZ of vector 2(min) and vector 3(max)" +E2Helper.Descriptions["clamp"] = "Returns a vector in the same direction as vector 1, with length clamped between argument 2(min) and argument 3(max)" +E2Helper.Descriptions["min"] = "Returns the vector with the smallest length" +E2Helper.Descriptions["max"] = "Returns the vector with the greatest length" +E2Helper.Descriptions["minVec"] = "Returns a vector combining the lowest value components of V1 and V2" +E2Helper.Descriptions["maxVec"] = "Returns the vector combining the highest value components of V1 and V2" +E2Helper.Descriptions["mix"] = "Combines vector 1's XYZ with vector 2's XYZ by a proportion given by argument 3 (between 0 and 1)" +E2Helper.Descriptions["positive"] = "Returns a vector containing the positive value of each vector component, equivalent to abs(N)" +E2Helper.Descriptions["inrange"] = "Returns 1 if each component of V is between (or is equal to) the components of Vmin and Vmax" +E2Helper.Descriptions["length"] = "Gets the length of the vector" +E2Helper.Descriptions["length2"] = "Gets the squared length of the vector" +E2Helper.Descriptions["distance"] = "Gets the distance between vectors" +E2Helper.Descriptions["distance2"] = "Gets the squared distance between vectors" +E2Helper.Descriptions["normalized"] = "Gets the normalized vector" +E2Helper.Descriptions["dot"] = "Gets the vector dot (scalar) product" +E2Helper.Descriptions["x"] = "Gets the x component of the vector" +E2Helper.Descriptions["y"] = "Gets the y component of the vector" +E2Helper.Descriptions["z"] = "Gets the z component of the vector" +E2Helper.Descriptions["w"] = "Gets the w component of the vector" +E2Helper.Descriptions["setX"] = "Returns a copy of the vector with X replaced (use as Vec = Vec:setX(...))" +E2Helper.Descriptions["setY"] = "Returns a copy of the vector with Y replaced (use as Vec = Vec:setY(...))" +E2Helper.Descriptions["setZ"] = "Returns a copy of the vector with Z replaced (use as Vec = Vec:setZ(...))" +E2Helper.Descriptions["setW"] = "Returns a copy of the vector with W replaced (use as Vec = Vec:setW(...))" +E2Helper.Descriptions["toString"] = "Gets the vector nicely formatted as a string \"[X,Y,Z]\"" +E2Helper.Descriptions["identity"] = "Creates a 3x3 identity matrix" +E2Helper.Descriptions["matrix"] = "Creates a 3x3 zero matrix" +E2Helper.Descriptions["matrix"] = "Creates a matrix with 9 values in the following order (i.j): (1,1), (1,2), (1,3), (2,1) etc." +E2Helper.Descriptions["matrix"] = "Creates a matrix with vectors by columns" +E2Helper.Descriptions["matrix"] = "Converts a 2x2 matrix into a 3x3 matrix - all (i,3) and (3,j) are filled with 0's" +E2Helper.Descriptions["matrix"] = "Converts a 4x4 matrix into a 3x3 matrix - all (i,4) and (4,j) are omitted" +E2Helper.Descriptions["swapRows"] = "Swaps the two rows specified" +E2Helper.Descriptions["swapColumns"] = "Swaps the two columns specified" +E2Helper.Descriptions["setRow"] = "Sets the values of a row. The first argument given specifies the row(j), the following arguments are the values 1j, 2j, 3j" +E2Helper.Descriptions["setRow"] = "Sets the values of a row. The first argument given specifies the row, the vector contains the values to set" +E2Helper.Descriptions["setColumn"] = "Sets the values of a column. The first argument given specifies the column, the vector contains the values to set" +E2Helper.Descriptions["setDiagonal"] = "Sets the elements of the leading diagonal" +E2Helper.Descriptions["setDiagonal"] = "Sets the elements of the leading diagonal from the components of a vector" +E2Helper.Descriptions["matrix"] = "Creates a reference frame matrix from an entity's local direction vectors by columns in the order ( x, y, z )" +E2Helper.Descriptions["matrix"] = "Returns a 3x3 reference frame matrix as described by the angle A. Multiplying by this matrix will be the same as rotating by the given angle." +E2Helper.Descriptions["x"] = "Returns the local x direction vector from a 3x3 coordinate reference frame matrix ( same as M:column(1) )" +E2Helper.Descriptions["y"] = "Returns the local y direction vector from a 3x3 coordinate reference frame matrix ( same as M:column(2) )" +E2Helper.Descriptions["z"] = "Returns the local z direction vector from a 3x3 coordinate reference frame matrix ( same as M:column(3) )" +E2Helper.Descriptions["x"] = "Returns the local x direction vector from a 4x4 coordinate reference frame matrix ( same as M:column(1) )" +E2Helper.Descriptions["y"] = "Returns the local y direction vector from a 4x4 coordinate reference frame matrix ( same as M:column(2) )" +E2Helper.Descriptions["z"] = "Returns the local z direction vector from a 4x4 coordinate reference frame matrix ( same as M:column(3) )" +E2Helper.Descriptions["pos"] = "Returns the position vector from a 4x4 coordinate reference frame matrix ( same as M:column(4) )" +E2Helper.Descriptions["row"] = "Returns the row as a vector" +E2Helper.Descriptions["column"] = "Returns the column as a vector" +E2Helper.Descriptions["element"] = "Returns the element with indices (i,j)" +E2Helper.Descriptions["setElement"] = "Sets an element's value. The first two arguments specify the indices (i,j), the third argument is the value to set it to" +E2Helper.Descriptions["swapElements"] = "Swaps two elements, specified by indices ( i1, j1, i2, j2 )" +E2Helper.Descriptions["diagonal"] = "Returns a vector comprising the elements along the leading diagonal" +E2Helper.Descriptions["trace"] = "Returns the trace of a matrix" +E2Helper.Descriptions["det"] = "Returns the determinant of a matrix (Does not work for 4x4 matrices)" +E2Helper.Descriptions["transpose"] = "Returns the transpose of a matrix" +E2Helper.Descriptions["ang"] = "Makes an angle" +E2Helper.Descriptions["ang"] = "Same as ang(0,0,0)" +E2Helper.Descriptions["ang"] = "Changes a vector variable into an angle variable" +E2Helper.Descriptions["ceil"] = "Rounds PYR up to the nearest integer" +E2Helper.Descriptions["ceil"] = "Rounds PYR up to argument 2's decimal precision" +E2Helper.Descriptions["floor"] = "Rounds PYR down to the nearest integer" +E2Helper.Descriptions["floor"] = "Rounds PYR down to argument 2's decimal precision" +E2Helper.Descriptions["round"] = "Rounds PYR to the nearest integer" +E2Helper.Descriptions["round"] = "Rounds PYR to argument 2's decimal precision" +E2Helper.Descriptions["mod"] = "Returns the remainder after PYR have been divided by argument 2" +E2Helper.Descriptions["mod"] = "Returns the remainder after the components of angle 1 have been divided by the components of angle 2" +E2Helper.Descriptions["clamp"] = "Clamps angle 1's PYR between the PYR of angle 2(min) and angle 3(max)" +E2Helper.Descriptions["clamp"] = "Clamps angle 1's PYR between argument 2(min) and argument 3(max)" +E2Helper.Descriptions["mix"] = "Combines angle 1's PYR with angle 2's PYR by a proportion given by argument 3 (between 0 and 1)" +E2Helper.Descriptions["shiftL"] = "Shifts the angle's components left: shiftL( p,y,r ) = ( y,r,p )" +E2Helper.Descriptions["shiftR"] = "Shifts the angle's components right: shiftR( p,y,r ) = ( r,p,y )" +E2Helper.Descriptions["angnorm"] = "Gets the normalized angle of an angle" +E2Helper.Descriptions["angnorm"] = "Gets the normalized angle of a number" +E2Helper.Descriptions["pitch"] = "Gets the pitch of the angle" +E2Helper.Descriptions["yaw"] = "Gets the yaw of the angle" +E2Helper.Descriptions["roll"] = "Gets the roll of the angle" +E2Helper.Descriptions["setPitch"] = "Returns a copy of the angle with Pitch replaced (use as Ang = Ang:setPitch(...))" +E2Helper.Descriptions["setYaw"] = "Returns a copy of the angle with Yaw replaced (use as Ang = Ang:setYaw(...))" +E2Helper.Descriptions["setRoll"] = "Returns a copy of the angle with Roll replaced (use as Ang = Ang:setRoll(...))" +E2Helper.Descriptions["toString"] = "Gets the angle nicely formatted as a string \"[P,Y,R]\"" +E2Helper.Descriptions["forward"] = "Gets the forward vector of the angle." +E2Helper.Descriptions["right"] = "Gets the right vector of the angle." +E2Helper.Descriptions["up"] = "Gets the up vector of the angle." +E2Helper.Descriptions["table"] = "Creates an empty table" +E2Helper.Descriptions["clone"] = "Creates an independent copy of a table" +E2Helper.Descriptions["count"] = "Returns the number of used indexes in the table" +E2Helper.Descriptions[""] = "Deprecated. Use T[S,] instead." +E2Helper.Descriptions["array"] = "Creates an empty array" +E2Helper.Descriptions["clone"] = "Creates an independant copy of an array" +E2Helper.Descriptions["count"] = "Returns the number of used indexes in the array" +E2Helper.Descriptions["sum"] = "Adds all numbers in the array together and returns result" +E2Helper.Descriptions["concat"] = "Combines all strings and returns result" +E2Helper.Descriptions["concat"] = "Combines all strings with specified string in between and returns result" +E2Helper.Descriptions["average"] = "Gives the average of all numbers in array" +E2Helper.Descriptions["min"] = "Returns the smallest number in array" +E2Helper.Descriptions["minIndex"] = "Returns the index of the smallest number in array" +E2Helper.Descriptions["max"] = "Returns the largest number in array" +E2Helper.Descriptions["maxIndex"] = "Returns the index of the largest number in array" +E2Helper.Descriptions["bone"] = "Returns E's Nth bone" +E2Helper.Descriptions["bones"] = "Returns an array containing all of E's bones. This array's first element has the index 0!" +E2Helper.Descriptions["boneCount"] = "Returns E's number of bones" +E2Helper.Descriptions["nobone"] = "Returns an invalid bone" +E2Helper.Descriptions["aimBone"] = "Returns the bone the player is currently aiming at" +E2Helper.Descriptions["entity"] = "Returns the entity B belongs to" +E2Helper.Descriptions["index"] = "Returns B's index in the entity it belongs to. Returns -1 if the bone is invalid or an error occured" +E2Helper.Descriptions["pos"] = "Returns B's position" +E2Helper.Descriptions["forward"] = "Returns a vector describing B's forward direction" +E2Helper.Descriptions["right"] = "Returns a vector describing B's right direction" +E2Helper.Descriptions["up"] = "Returns a vector describing B's up direction" +E2Helper.Descriptions["vel"] = "Returns B's velocity" +E2Helper.Descriptions["velL"] = "Returns B's velocity in local coordinates" +E2Helper.Descriptions["toWorld"] = "Transforms V from local coordinates (as seen from B) to world coordinates" +E2Helper.Descriptions["toLocal"] = "Transforms V from world coordinates to local coordinates (as seen from B)" +E2Helper.Descriptions["angVel"] = "Returns B's angular velocity" +E2Helper.Descriptions["angles"] = "Returns B's pitch, yaw and roll angles" +E2Helper.Descriptions["bearing"] = "Returns the bearing (yaw) from B to V" +E2Helper.Descriptions["elevation"] = "Returns the elevation (pitch) from B to V" +E2Helper.Descriptions["mass"] = "Returns B's mass" +E2Helper.Descriptions["massCenter"] = "Returns B's Center of Mass" +E2Helper.Descriptions["massCenterL"] = "Returns B's Center of Mass in local coordinates" +E2Helper.Descriptions["inertia"] = "Gets the principal components of B's inertia tensor in the form vec(Ixx, Iyy, Izz)" +E2Helper.Descriptions["isFrozen"] = "Returns 1 if B is frozen, 0 otherwise" +E2Helper.Descriptions["isHiSpeed"] = "Returns true if the linked component is high-speed capable." +E2Helper.Descriptions["entity"] = "Returns the entity of the linked component." +E2Helper.Descriptions["hasInput"] = "Returns true if the linked component has an input of the specified name." +E2Helper.Descriptions["hasOutput"] = "Returns true if the linked component has an output of the specified name." +E2Helper.Descriptions["number"] = "Deprecated. Use XWL[S,number] instead." +E2Helper.Descriptions["vector"] = "Deprecated. Use XWL[S,vector] instead." +E2Helper.Descriptions["string"] = "Deprecated. Use XWL[S,string] instead." +E2Helper.Descriptions["xyz"] = "Retrieves the X/Y/Z as the corresponding values in the vector." +E2Helper.Descriptions["entity"] = "Deprecated. Use XWL[S,entity] instead." +E2Helper.Descriptions["writeCell"] = "Deprecated. Use XWL[N]=X instead." +E2Helper.Descriptions["readCell"] = "Deprecated. Use XWL[N] instead." +E2Helper.Descriptions["XWL[N]"] = " Returns contents of the specified memory cell." +E2Helper.Descriptions["writeString"] = "Writes a null-terminated string to the given address. Returns the next free address or 0 on failure." +E2Helper.Descriptions["readString"] = "Reads a null-terminated string from the given address. Returns an empty string on failure." +E2Helper.Descriptions["inputs"] = "Returns an array of all the inputs that XWL has without their types. Returns an empty array if it has none" +E2Helper.Descriptions["outputs"] = "Returns an array of all the outputs that XWL has without their types. Returns an empty array if it has none" +E2Helper.Descriptions["inputType"] = "Returns the type of input that S is in lowercase. ( \"NORMAL\" is changed to \"number\" )" +E2Helper.Descriptions["outputType"] = "Returns the type of output that S is in lowercase. ( \"NORMAL\" is changed to \"number\" )" +E2Helper.Descriptions["comp"] = "Returns complex zero" +E2Helper.Descriptions["comp"] = "Converts a real number to complex (returns complex number with real part N and imaginary part 0)" +E2Helper.Descriptions["comp"] = "Returns N+N2*i" +E2Helper.Descriptions["i"] = "Returns the imaginary unit i" +E2Helper.Descriptions["i"] = "Returns N*i" +E2Helper.Descriptions["abs"] = "Returns the absolute value of C" +E2Helper.Descriptions["arg"] = "Returns the argument of C" +E2Helper.Descriptions["conj"] = "Returns the conjugate of C" +E2Helper.Descriptions["real"] = "Returns the real part of C" +E2Helper.Descriptions["imag"] = "Returns the imaginary part of C" +E2Helper.Descriptions["exp"] = "Raises Euler's constant e to the power of C" +E2Helper.Descriptions["log"] = "Calculates the natural logarithm of C" +E2Helper.Descriptions["log"] = "Calculates the logarithm of C2 to a complex base C" +E2Helper.Descriptions["log"] = "Calculates the logarithm of C to a real base N" +E2Helper.Descriptions["log2"] = "Calculates the logarithm of C to base 2" +E2Helper.Descriptions["log10"] = "Calculates the logarithm of C to base 10" +E2Helper.Descriptions["sqrt"] = "Calculates the square root of C" +E2Helper.Descriptions["csqrt"] = "Calculates the complex square root of the real number N" +E2Helper.Descriptions["sin"] = "Calculates the sine of C" +E2Helper.Descriptions["cos"] = "Calculates the cosine of C" +E2Helper.Descriptions["sinh"] = "Calculates the hyperbolic sine of C" +E2Helper.Descriptions["cosh"] = "Calculates the hyperbolic cosine of C" +E2Helper.Descriptions["toString"] = "Formats C as a string." +E2Helper.Descriptions["quat"] = "Creates a zero quaternion" +E2Helper.Descriptions["quat"] = "Creates a quaternion with real part equal to N" +E2Helper.Descriptions["quat"] = "Creates a quaternion with real and \"i\" parts equal to C" +E2Helper.Descriptions["quat"] = "Converts a vector to a quaternion (returns V.x*i + V.y*j + V.z*k)" +E2Helper.Descriptions["quat"] = "Returns N+N2i+N3j+N4k" +E2Helper.Descriptions["quat"] = "Converts A to a quaternion" +E2Helper.Descriptions["quat"] = "Converts angle of E to a quaternion" +E2Helper.Descriptions["qi"] = "Returns quaternion i" +E2Helper.Descriptions["qi"] = "Returns quaternion N*i" +E2Helper.Descriptions["qj"] = "Returns j" +E2Helper.Descriptions["qj"] = "Returns N*j" +E2Helper.Descriptions["qk"] = "Returns k" +E2Helper.Descriptions["qk"] = "Returns N*k" +E2Helper.Descriptions["abs"] = "Returns absolute value of Q" +E2Helper.Descriptions["conj"] = "Returns the conjugate of Q" +E2Helper.Descriptions["inv"] = "Returns the inverse of Q" +E2Helper.Descriptions["real"] = "Returns the real component of the quaternion" +E2Helper.Descriptions["i"] = "Returns the i component of the quaternion" +E2Helper.Descriptions["j"] = "Returns the j component of the quaternion" +E2Helper.Descriptions["k"] = "Returns the k component of the quaternion" +E2Helper.Descriptions["xaxis"] = "Rotates X axis using Q" +E2Helper.Descriptions["yaxis"] = "Rotates Y axis using Q" +E2Helper.Descriptions["zaxis"] = "Rotates Z axis using Q" +E2Helper.Descriptions["qRotation"] = "Returns quaternion for rotation about axis V by angle N" +E2Helper.Descriptions["vec"] = "Converts Q to a vector by dropping the real component" +E2Helper.Descriptions["matrix"] = "Converts Q to a transformation matrix" +E2Helper.Descriptions["rotationAngle"] = "Returns the angle of rotation in degrees (by coder0xff)" +E2Helper.Descriptions["rotationAxis"] = "Returns the axis of rotation (by coder0xff)" +E2Helper.Descriptions["rotationVector"] = "Returns the rotation vector - rotation axis where magnitude is the angle of rotation in degress (by coder0xff)" +E2Helper.Descriptions["toString"] = "Formats Q as a string." +E2Helper.Descriptions["first"] = "Returns 1 if the expression was spawned or reset" +E2Helper.Descriptions["duped"] = "Returns 1 if the expression was duplicated" +E2Helper.Descriptions["inputClk"] = "Returns 1 if the expression was triggered by an input" +E2Helper.Descriptions["ops"] = "Returns how many ops are used every execution on average" +E2Helper.Descriptions["opcounter"] = "Returns how many ops have been used so far in this execution plus the amount of hard quota used" +E2Helper.Descriptions["minquota"] = "The ops left before soft quota is used up" +E2Helper.Descriptions["maxquota"] = "The ops left before hard quota is exceeded and the expression shuts down" +E2Helper.Descriptions["entity"] = "Gets the entity of the expression" +E2Helper.Descriptions["concmd"] = "Takes a string and executes it in console. Returns 1 if it succeeded and 0 if it failed.The client must enable this in the console with \"wire_expression2_concmd 1\"." +E2Helper.Descriptions["tickClk"] = "Returns 1 if the current execution was caused by \"runOnTick\"" +E2Helper.Descriptions["curtime"] = "Returns the current time since server-start in seconds" +E2Helper.Descriptions["clk"] = "Returns 1 if the current execution was caused by the interval" +E2Helper.Descriptions["clk"] = "Returns 1 if the current execution was caused by the inserted name" +E2Helper.Descriptions["toUnit"] = "Converts default garrysmod units to specified units" +E2Helper.Descriptions["fromUnit"] = "Converts specified units to default garrysmod units" +E2Helper.Descriptions["convertUnit"] = "Converts between two units" +E2Helper.Descriptions["map"] = "Returns the current map name" +E2Helper.Descriptions["hostname"] = "Returns the Name of the server" +E2Helper.Descriptions["isLan"] = "Returns 1 if lan mode is enabled" +E2Helper.Descriptions["gamemode"] = "Returns the name of the current gamemode" +E2Helper.Descriptions["gravity"] = "Returns gravity" +E2Helper.Descriptions["ping"] = "Returns the latency (Player:ping() for individual player)" +E2Helper.Descriptions["isSinglePlayer"] = "Returns 1 if singleplayer, 0 if multiplayer" +E2Helper.Descriptions["isDedicated"] = "Returns 1 if server is dedicated, 0 if listen" +E2Helper.Descriptions["numPlayers"] = "Returns the number of players currently in the server" +E2Helper.Descriptions["maxPlayers"] = "Returns the max number of players allowed in the server" +E2Helper.Descriptions["playerDamage"] = "Returns 1 if player vs player damage is enabled on the server" +E2Helper.Descriptions["convar"] = "Give a console command such as \"name\" and it returns the set value" +E2Helper.Descriptions["convarnum"] = "Give a console command such as \"sbox_godmode\" and it returns the set value" +E2Helper.Descriptions["time"] = "Returns numerical time/date info from the server. Possible arguments: \"year\", \"month\", \"day\", \"hour\", \"min\", \"sec\", \"wday\" (weekday, Sunday is 1), \"yday\" (day of the year), and \"isdst\" (daylight saving flag 0/1)" +E2Helper.Descriptions["getConstraints"] = "Returns an array with all entities directly or indirectly constrained to E, except E itself." +E2Helper.Descriptions["hasConstraints"] = "Returns the number of the constraints E has" +E2Helper.Descriptions["hasConstraints"] = "Returns the number of the constraints E has with the given constraint type (see the types list below)" +E2Helper.Descriptions["isConstrained"] = "Returns 1 if E has constraints, 0 if not" +E2Helper.Descriptions["isWeldedTo"] = "Returns the first entity E was welded to" +E2Helper.Descriptions["isWeldedTo"] = "Returns the Nth entity E was welded to" +E2Helper.Descriptions["isConstrainedTo"] = "Returns the first entity E was constrained to" +E2Helper.Descriptions["isConstrainedTo"] = "Returns the Nth entity E was constrained to" +E2Helper.Descriptions["isConstrainedTo"] = "Returns the first entity E was constrained to with the given constraint type (see the types list below)" +E2Helper.Descriptions["isConstrainedTo"] = "Returns the Nth entity E was constrained to with the given constraint type (see the types list below)" +E2Helper.Descriptions["parent"] = "Returns the entity E is parented to." +E2Helper.Descriptions["parentBone"] = "Returns the bone E is parented to." +E2Helper.Descriptions["chatClk"] = "Returns 1 if the chip is being executed because of a chat event. Returns 0 otherwise." +E2Helper.Descriptions["chatClk"] = "Returns 1 if the chip is being executed because of a chat event by player E. Returns 0 otherwise." +E2Helper.Descriptions["lastSpoke"] = "Returns the last player to speak." +E2Helper.Descriptions["lastSaid"] = "Returns the last message in the chat log." +E2Helper.Descriptions["lastSaidWhen"] = "Returns the time the last message was sent." +E2Helper.Descriptions["lastSaidTeam"] = "Returns 1 if the last message was sent in the team chat, 0 otherwise." +E2Helper.Descriptions["lastSaid"] = "Returns what the player E last said." +E2Helper.Descriptions["lastSaidWhen"] = "Returns when the given player last said something." +E2Helper.Descriptions["lastSaidTeam"] = "Returns 1 if the last message was sent in the team chat, 0 otherwise." +E2Helper.Descriptions["getColor"] = "Returns the color of an entity as a vector (R,G,B)" +E2Helper.Descriptions["getAlpha"] = "Returns the alpha of an entity" +E2Helper.Descriptions["getMaterial"] = "Returns the material of an entity" +E2Helper.Descriptions["getSkin"] = "Gets E's current skin number." +E2Helper.Descriptions["getSkinCount"] = "Gets E's number of skins." +E2Helper.Descriptions["hsv2rgb"] = "Converts V from the HSV color space to the RGB color space" +E2Helper.Descriptions["rgb2hsv"] = "Converts V from the RGB color space to the HSV color space" +E2Helper.Descriptions["rgb2digi"] = "Converts an RGB vector V to a number in digital screen format. N Specifies a mode, either 0, 2 or 3, corresponding to Digital Screen color modes." +E2Helper.Descriptions["rgb2digi"] = "Converts the RGB color (N,N2,N3) to a number in digital screen format. N4 Specifies a mode, either 0, 2 or 3, corresponding to Digital Screen color modes." +E2Helper.Descriptions["findUpdateRate"] = "Returns the minimum delay between entity find events on a chip" +E2Helper.Descriptions["findPlayerUpdateRate"] = "Returns the minimum delay between entity find events per player" +E2Helper.Descriptions["findCanQuery"] = "Returns 1 if find functions can be used, 0 otherwise." +E2Helper.Descriptions["findInSphere"] = "Finds entities in a sphere around V with a radius of N, returns the number found after filtering" +E2Helper.Descriptions["findInCone"] = "Like findInSphere but with a [Spherical cone], arguments are for position, direction, length, and degrees (works now)" +E2Helper.Descriptions["findInBox"] = "Like findInSphere but with a globally aligned box, the arguments are the diagonal corners of the box" +E2Helper.Descriptions["findByName"] = "Find all entities with the given name" +E2Helper.Descriptions["findByModel"] = "Find all entities with the given model" +E2Helper.Descriptions["findByClass"] = "Find all entities with the given class" +E2Helper.Descriptions["findPlayerByName"] = "Returns the player with the given name, this is an exception to the rule" +E2Helper.Descriptions["findResult"] = "Returns the indexed entity from the previous find event (valid parameters are 1 to the number of entities found)" +E2Helper.Descriptions["findClosest"] = "Returns the closest entity to the given point from the previous find event" +E2Helper.Descriptions["findToArray"] = "Formats the query as an array, R:entity(Index) to get a entity, R:string to get a description including the name and entity id." +E2Helper.Descriptions["find"] = "Equivalent to findResult(1)" +E2Helper.Descriptions["findSortByDistance"] = "Sorts the entities from the last find event, index 1 is the closest to point V, returns the number of entities in the list" +E2Helper.Descriptions["findClipToClass"] = "Filters the list of entities by removing all entities that are NOT of this class" +E2Helper.Descriptions["findClipFromClass"] = "Filters the list of entities by removing all entities that are of this class" +E2Helper.Descriptions["findClipToModel"] = "Filters the list of entities by removing all entities that do NOT have this model" +E2Helper.Descriptions["findClipFromModel"] = "Filters the list of entities by removing all entities that do have this model" +E2Helper.Descriptions["findClipToName"] = "Filters the list of entities by removing all entities that do NOT have this name" +E2Helper.Descriptions["findClipFromName"] = "Filters the list of entities by removing all entities that do have this name" +E2Helper.Descriptions["findClipToSphere"] = "Filters the list of entities by removing all entities NOT within the specified sphere (center, radius)" +E2Helper.Descriptions["findClipFromSphere"] = "Filters the list of entities by removing all entities within the specified sphere (center, radius)" +E2Helper.Descriptions["gGetGroup"] = "Returns the name of the current group for the chip" +E2Helper.Descriptions["ranger"] = "You input max range, it returns ranger data" +E2Helper.Descriptions["ranger"] = "Same as above with added inputs for X and Y skew" +E2Helper.Descriptions["rangerAngle"] = "You input the distance, x-angle and y-angle (both in degrees) it returns ranger data" +E2Helper.Descriptions["rangerOffset"] = "You input two vector points, it returns ranger data" +E2Helper.Descriptions["rangerOffset"] = "You input the range, a position vector, and a direction vector and it returns ranger data" +E2Helper.Descriptions["distance"] = "Outputs the distance from the rangerdata input, else depends on rangerDefault" +E2Helper.Descriptions["position"] = "Outputs the position of the input ranger data trace IF it hit anything, else returns (0,0,0)" +E2Helper.Descriptions["entity"] = "Returns the entity of the input ranger data trace IF it hit an entity, else returns nil" +E2Helper.Descriptions["hit"] = "Returns 1 if the input ranger data hit anything and 0 if it didn't" +E2Helper.Descriptions["hitNormal"] = "Outputs a normalized vector perpendicular to the surface the ranger is pointed at." +E2Helper.Descriptions["npcDisp"] = " Returns the NPC's relationship to entity E." +E2Helper.Descriptions["signalGetGroup"] = "Gets the E-2's current signal group" +E2Helper.Descriptions["signalClk"] = "Returns 1 if the chip was executed because of any signal, regardless of name, group or scope. Returns 0 otherwise." +E2Helper.Descriptions["signalClk"] = "Returns 1 if the chip was executed because the signal S was sent, regardless of group or scope. Returns 0 otherwise." +E2Helper.Descriptions["signalClk"] = "Returns 1 if the chip was executed because the signal S was sent to the scope N, regardless of group. Returns 0 otherwise." +E2Helper.Descriptions["signalClk"] = "Returns 1 if the chip was executed because the signal S2 was sent in the group S, regardless of scope. Returns 0 otherwise." +E2Helper.Descriptions["signalClk"] = "Returns 1 if the chip was executed because the signal S2 was sent in the group S to the scope N. Returns 0 otherwise." +E2Helper.Descriptions["signalName"] = "Returns the name of the received signal." +E2Helper.Descriptions["signalGroup"] = "Returns the group name of the received signal." +E2Helper.Descriptions["signalSender"] = "Returns the entity of the chip that sent the signal." +E2Helper.Descriptions["signalSenderId"] = "Returns the entity ID of the chip that sent the signal. Useful if the entity doesn't exist anymore." +E2Helper.Descriptions["glonEncode"] = "Encodes R into a string, using GLON." +E2Helper.Descriptions["glonEncode"] = "Encodes T into a string, using GLON." +E2Helper.Descriptions["glonDecode"] = "Decodes S into an array, using GLON." +E2Helper.Descriptions["glonDecodeTable"] = "Decodes S into a table, using GLON." +E2Helper.Descriptions["holoEntity"] = "Returns the entity corresponding to the hologram given by the specified index." +E2Helper.Descriptions["fileLoaded"] = "Returns whether or not the file has been loaded onto the server." +E2Helper.Descriptions["fileRead"] = "Returns the string data from a given file (has to be loaded onto server)." +E2Helper.Descriptions["fileClk"] = "Returns whether the execution was run because a file finished uploading and was that file of a specific file name." +E2Helper.Descriptions["fileClk"] = "Returns whether the execution was run because a file finished uploading." +end diff --git a/lua/wire/client/e2helper.lua b/lua/wire/client/e2helper.lua new file mode 100644 index 0000000000..a224289089 --- /dev/null +++ b/lua/wire/client/e2helper.lua @@ -0,0 +1,315 @@ +/******************************************************************************\ + Expression 2 Helper for Expression 2 + -HP- (and tomylobo, though he breaks a lot ^^) +\******************************************************************************/ + +-- TODO: fix the desciptions (aka talk to pyro making him fix his php script) + +E2Helper = {} +local E2Helper = E2Helper -- faster access +E2Helper.Descriptions = {} + +include("e2descriptions.lua") + +local lastmax = 0 +local cookie_maxresults, cookie_tooltip, cookie_w, cookie_h +local function cookie_update() + local current_maxresults = E2Helper.MaxEntry:GetValue() + if current_maxresults>lastmax then + lastmax = current_maxresults + E2Helper.Update() + return -- return, since E2Helper.Update() already called cookie_update again. + end + + if current_maxresults ~= cookie_maxresults then + cookie.Set("e2helper_maxresults", current_maxresults) + cookie_maxresults = current_maxresults + end + + local current_tooltip = E2Helper.Tooltip:GetChecked(true) and 1 or 0 + if current_tooltip ~= cookie_tooltip then + cookie.Set("e2helper_tooltip", current_tooltip) + cookie_tooltip = current_tooltip + end + + local current_w, current_h = E2Helper.Frame:GetSize() + if current_w ~= cookie_w then + cookie.Set("e2helper_w", current_w) + cookie_w = current_w + end + + if current_h ~= cookie_h then + cookie.Set("e2helper_h", current_h) + cookie_h = current_h + end +end + +-- returns a function that executes , delayed by seconds +local function delayed(t,func) + return function() + timer.Remove("e2helper_delayed") + timer.Create("e2helper_delayed", t, 1, func) + end +end + +function E2Helper.Create(reset) + local x, y, w, h + + E2Helper.Frame = vgui.Create("DFrame") + E2Helper.Frame:SetSize(280, 425) + E2Helper.Frame:Center() + E2Helper.Frame:SetSizable(true) + E2Helper.Frame:SetDeleteOnClose(false) + E2Helper.Frame:SetVisible(false) + E2Helper.Frame:SetTitle("E2Helper") + E2Helper.Frame._PerformLayout = E2Helper.Frame.PerformLayout + function E2Helper.Frame:PerformLayout(...) + local w,h = E2Helper.Frame:GetSize() + if w < 240 then w = 240 end + if h < 300 then h = 300 end + E2Helper.Frame:SetSize(w,h) + + self:_PerformLayout(...) + E2Helper.Resize() + end + + E2Helper.DescriptionEntry = vgui.Create("DTextEntry", E2Helper.Frame) + E2Helper.DescriptionEntry:SetPos(5,330) + E2Helper.DescriptionEntry:SetSize(270,45) + E2Helper.DescriptionEntry:SetEditable(false) + E2Helper.DescriptionEntry:SetMultiline(true) + + E2Helper.ResultFrame = vgui.Create("DListView", E2Helper.Frame) + E2Helper.ResultFrame:SetPos(5, 60) + E2Helper.ResultFrame:SetSize(270, 240) + E2Helper.ResultFrame:SetMultiSelect(false) + E2Helper.ResultFrame:AddColumn("Function"):SetWidth(126) + E2Helper.ResultFrame:AddColumn("Takes"):SetWidth(60) + E2Helper.ResultFrame:AddColumn("Returns"):SetWidth(60) + E2Helper.ResultFrame:AddColumn("Cost"):SetWidth(30) + + function E2Helper.ResultFrame:OnClickLine(line) + self:ClearSelection() + self:SelectItem(line) + E2Helper.FuncEntry:SetText(E2Helper.GetFunctionSyntax(line:GetValue(1), line:GetValue(2), line:GetValue(3))) + local desc = E2Helper.Descriptions[line:GetValue(1)] + if desc then + E2Helper.DescriptionEntry:SetText(desc) + E2Helper.DescriptionEntry:SetTextColor(Color(0, 0, 0)) + else + E2Helper.DescriptionEntry:SetText("No description found :(") + E2Helper.DescriptionEntry:SetTextColor(Color(128, 128, 128)) + end + end + + E2Helper.Image = vgui.Create("DImage", E2Helper.Frame) + E2Helper.Image:SetPos(5, 75) + E2Helper.Image:SetImage("expression 2/cog.vtf") + E2Helper.Image:SetImageColor(Color(255,0,0,8)) + E2Helper.Image:SetSize(225,225) + + E2Helper.ResultLabel = vgui.Create("DLabel", E2Helper.Frame) + E2Helper.ResultLabel:SetPos(5, 405) + E2Helper.ResultLabel:SetText("") + E2Helper.ResultLabel:SizeToContents() + + E2Helper.CredLabel = vgui.Create("DLabel", E2Helper.Frame) + E2Helper.CredLabel:SetPos(238, 405) + E2Helper.CredLabel:SetText("By -HP-") + E2Helper.CredLabel:SizeToContents() + E2Helper.CredLabel.Resize = { true, true, false, false } + + E2Helper.NameEntry = vgui.Create("DTextEntry", E2Helper.Frame) + E2Helper.NameEntry:SetPos(5, 32) + E2Helper.NameEntry:SetWide(129) + + E2Helper.ParamEntry = vgui.Create("DTextEntry", E2Helper.Frame) + E2Helper.ParamEntry:SetPos(136, 32) + E2Helper.ParamEntry:SetWide(68) + + E2Helper.ReturnEntry = vgui.Create("DTextEntry", E2Helper.Frame) + E2Helper.ReturnEntry:SetPos(206, 32) + E2Helper.ReturnEntry:SetWide(68) + + E2Helper.Tooltip = vgui.Create("DCheckBoxLabel", E2Helper.Frame) + E2Helper.Tooltip:SetPos(5, 384) + E2Helper.Tooltip:SetText("Tooltip") + E2Helper.Tooltip:SetValue(reset and 0 or cookie.GetNumber("e2helper_tooltip", 0)) + E2Helper.Tooltip:SizeToContents() + + E2Helper.FuncEntry = vgui.Create("DTextEntry", E2Helper.Frame) + E2Helper.FuncEntry:SetText("") + E2Helper.FuncEntry:SetWidth(270) + E2Helper.FuncEntry:SetPos(5, 305) + + E2Helper.MaxEntry = vgui.Create("DNumberWang", E2Helper.Frame) + E2Helper.MaxEntry:SetPos(235, 380) + E2Helper.MaxEntry:SetWide(40) + E2Helper.MaxEntry:SetTooltip("E2 is being loaded, please wait...") + timer.Create("E2Helper.SetMaxEntry", 1, 0, function() + if e2_function_data_received then + E2Helper.MaxEntry:SetMax(table.Count(wire_expression2_funcs)) + timer.Remove("E2Helper.SetMaxEntry") + E2Helper.MaxEntry:SetTooltip(false) + end + end) + E2Helper.MaxEntry:SetValue(reset and 50 or cookie.GetNumber("e2helper_maxresults", 50)) + E2Helper.MaxEntry:SetDecimals(0) + + E2Helper.MaxLabel = vgui.Create("DLabel", E2Helper.Frame) + E2Helper.MaxLabel:SetPos(170, 384) + E2Helper.MaxLabel:SetText("Max results:") + E2Helper.MaxLabel:SizeToContents() + + + E2Helper.NameEntry.OnTextChanged = delayed(0.1, E2Helper.Update) + E2Helper.ParamEntry.OnTextChanged = delayed(0.1, E2Helper.Update) + E2Helper.ReturnEntry.OnTextChanged = delayed(0.1, E2Helper.Update) + E2Helper.Tooltip.OnChange = E2Helper.Update + E2Helper.MaxEntry.OnValueChanged = delayed(1, cookie_update) + + local x, y, w, h + E2Helper.Originals = {} + for k, v in pairs(E2Helper) do + if type(v) == "Panel" then + x, y = v:GetPos() + w, h = v:GetSize() + E2Helper.Originals[k] = { x, y, w, h } + end + end + + if not reset then + E2Helper.Frame:SetSize(cookie.GetNumber("e2helper_w", 280), cookie.GetNumber("e2helper_h", 425)) + else + cookie_update() + end + E2Helper.Resize() +end + +function E2Helper.GetFunctionSyntax(func, args, rets) + local signature = func.."("..args..")" + local ret = E2Lib.generate_signature(signature, rets, wire_expression2_funcs[signature].argnames) + if rets ~= "" then ret = ret:sub(1, 1):upper()..ret:sub(2) end + return ret +end + +function E2Helper.Update() + + cookie_update() + + E2Helper.ResultFrame:Clear() + + local search_name, search_args, search_rets = E2Helper.NameEntry:GetValue():lower(), E2Helper.ParamEntry:GetValue():lower(), E2Helper.ReturnEntry:GetValue():lower() + local count = 0 + local maxcount = E2Helper.MaxEntry:GetValue() + local tooltip = E2Helper.Tooltip:GetChecked(true) + + for _,v in pairs(wire_expression2_funcs) do + local argnames, signature,rets,func,cost = v.argnames, unpack(v) + local name, args = string.match(signature, "^([^(]+)%(([^)]*)%)$") + + if signature:sub(1, 3) ~= "op:" and + name:lower():find(search_name,1,true) and + args:lower():find(search_args,1,true) and + rets:lower():find(search_rets,1,true) then + local line = E2Helper.ResultFrame:AddLine(name, args, rets, cost or 20) + if tooltip then line:SetTooltip(E2Helper.GetFunctionSyntax(name, args, rets)) end + count = count + 1 + if count >= maxcount then break end + end + end + + E2Helper.ResultFrame:SortByColumn(1) + E2Helper.ResultLabel:SetText(count.." results") + E2Helper.ResultLabel:SizeToContents() +end + +function E2Helper.Show(searchtext) + if not E2Helper.Frame then E2Helper.Create(false) end + E2Helper.Frame:MakePopup() + E2Helper.Frame:SetVisible(true) + if searchtext and searchtext ~= E2Helper.NameEntry:GetValue() then + E2Helper.NameEntry:SetValue(searchtext) + E2Helper.Update() + E2Helper.FuncEntry:SetValue("") + E2Helper.DescriptionEntry:SetValue("") + else + E2Helper.NameEntry:RequestFocus() + end +end + +local delayed_cookie_update = delayed(1, cookie_update) + +local lastw, lasth +function E2Helper.Resize() + local w, h = E2Helper.Frame:GetSize() + if w == lastw and h == lasth then return end + + local orig = E2Helper.Originals + local changew, changeh = w-orig.Frame[3], h-orig.Frame[4] + + -- Epically messy code: + E2Helper.CredLabel:SetPos(orig.CredLabel[1]+changew, orig.CredLabel[2]+changeh) + E2Helper.MaxLabel:SetPos(orig.MaxLabel[1]+changew, orig.MaxLabel[2]+changeh) + E2Helper.MaxEntry:SetPos(orig.MaxEntry[1]+changew, orig.MaxEntry[2]+changeh) + E2Helper.ResultLabel:SetPos(orig.ResultLabel[1], orig.ResultLabel[2]+changeh) + E2Helper.Tooltip:SetPos(orig.Tooltip[1], orig.Tooltip[2]+changeh) + E2Helper.FuncEntry:SetPos(orig.FuncEntry[1], orig.FuncEntry[2]+changeh) + E2Helper.FuncEntry:SetSize(orig.FuncEntry[3]+changew, orig.FuncEntry[4]) + E2Helper.DescriptionEntry:SetPos(orig.DescriptionEntry[1], orig.DescriptionEntry[2]+changeh) + E2Helper.DescriptionEntry:SetSize(orig.DescriptionEntry[3]+changew, orig.DescriptionEntry[4]) + E2Helper.ResultFrame:SetSize(orig.ResultFrame[3]+changew, orig.ResultFrame[4]+changeh) + + E2Helper.NameEntry:SetSize(orig.NameEntry[3]+changew*0.5, orig.NameEntry[4]) + E2Helper.ParamEntry:SetPos(orig.ParamEntry[1]+changew*0.5, orig.ParamEntry[2]) + E2Helper.ParamEntry:SetSize(orig.ParamEntry[3]+changew*0.25, orig.ParamEntry[4]) + E2Helper.ReturnEntry:SetPos(orig.ReturnEntry[1]+changew*0.75, orig.ReturnEntry[2]) + E2Helper.ReturnEntry:SetSize(orig.ReturnEntry[3]+changew*0.25, orig.ReturnEntry[4]) + + -- Keep the (funky) image overlay centered on the listview + local w1, h1 = E2Helper.ResultFrame:GetSize() + local x1, y1 = E2Helper.ResultFrame:GetPos() + + -- add borders of mysterious dimensions + x1 = x1+15 + w1 = w1-30 + y1 = y1+30 + h1 = h1-45 + + -- fix aspect ratio + if w1 > h1 then + x1 = x1+(w1-h1)/2 + w1 = h1 + else + y1 = y1+(h1-w1)/2 + h1 = w1 + end + + -- apply position ans size + E2Helper.Image:SetPos(x1, y1) + E2Helper.Image:SetSize(w1, h1) + + delayed_cookie_update() + + lastw, lasth = w, h +end + +concommand.Add("e2helper", function() E2Helper.Show() end) +concommand.Add("e2helper_reset", function() + if E2Helper.Frame then + E2Helper.Frame:SetDeleteOnClose(true) + E2Helper.Frame:Close() + end + E2Helper.Create(true) + cookie_update() +end) + +local PrevCtrlQ +hook.Add("Think", "E2Helper_KeyListener", function() + if not E2Helper.Frame then return end + local CtrlQ = input.IsKeyDown(KEY_Q) and (input.IsKeyDown(KEY_LCONTROL) or input.IsKeyDown(KEY_RCONTROL)) + if CtrlQ and not PrevCtrlQ and E2Helper.Frame:IsActive() then + E2Helper.Frame:SetVisible(false) + end + PrevCtrlQ = CtrlQ +end) diff --git a/lua/wire/client/toolscreen.lua b/lua/wire/client/toolscreen.lua new file mode 100644 index 0000000000..22f617bd42 --- /dev/null +++ b/lua/wire/client/toolscreen.lua @@ -0,0 +1,36 @@ +/******************************************************************************\ + Tool Screen rendering hook for Garry's Mod + Andreas "Syranide" Svensson, me@syranide.com +\******************************************************************************/ + +hook.Add("InitPostEntity", "RenderToolScreenInitialize", function() + local SWEP + for _,weapon in pairs(weapons.GetList()) do + if weapon.Classname == "gmod_tool" then + SWEP = weapon + break + end + end + if not SWEP then return end + + local ToolGunMaterial = Material("models/weapons/v_toolgun/screen") + local NewRT = GetRenderTarget("GModToolgunScreen", 256, 256) + + local _ViewModelDrawn = SWEP.ViewModelDrawn + function SWEP:ViewModelDrawn() + local tool = self.Tool[gmod_toolmode:GetString()] + if not tool then return _ViewModelDrawn(self) end + if not tool.RenderToolScreen then return _ViewModelDrawn(self) end + + local ToolGunRT = render.GetRenderTarget() + + ToolGunMaterial:SetMaterialTexture("$basetexture", NewRT) + + render.SetRenderTarget(NewRT) + render.SetViewPort(0, 0, 256, 256) + + tool:RenderToolScreen() + + render.SetRenderTarget(ToolGunRT) + end +end) diff --git a/lua/wire/client/wire_expression2_browser.lua b/lua/wire/client/wire_expression2_browser.lua new file mode 100644 index 0000000000..cf71b1aa1e --- /dev/null +++ b/lua/wire/client/wire_expression2_browser.lua @@ -0,0 +1,169 @@ +// made by Shandolum - Shandolum@gmail.com + +local PANEL = {} + +local invalid_filename_chars = { + ["*"] = "", + ["?"] = "", + [">"] = "", + ["<"] = "", + ["|"] = "", + ["\\"] = "", + ['"'] = "", + [" "] = "_", +} + +local function GetFileName(name) + local name = string.Replace( name, ".txt", "" ) + return string.Replace( name, "/", "" ) +end + +local function setTree(dir, parent) + if(dir==nil) then return end + parent:Clear(true) + parent.ChildNodes = nil + local pFolders = file.FindDir(dir .. "/*") + for k, v in pairs( pFolders ) do + local pFolder = parent:AddNode( v ) + pFolder.FileDir = dir .. "/" .. v + pFolder.IsFile = false + pFolder.Name = v + setTree( pFolder.FileDir, pFolder ) + end + local function InternalDoRightClick(self) + self:GetRoot():SetSelectedItem( self ) + if ( self:DoRightClick() ) then return end + if ( self:GetRoot():DoRightClick( self ) ) then return end + end + local pFiles = file.Find( dir .. "/*.txt" ) + for k, v in pairs( pFiles ) do + local file = parent:AddNode( GetFileName(v) ) + file.FileDir = dir .. "/" .. v + file.IsFile = true + file.Name = GetFileName(v) + file.Icon:SetImage( "vgui/spawnmenu/file" ) + file.InternalDoRightClick = InternalDoRightClick + end +end + +function PANEL:Init() + self.Folders = {} + self.Update = vgui.Create( "DButton" , self ) + self.Update:SetSize( self:GetWide(), 20 ) + self.Update:SetPos( 0,self:GetTall()-20 ) + self.Update:SetText( "Update" ) + self.Update.DoClick = function( button ) + self:UpdateFolders() + end + + self.panelmenu = {} + self.filemenu = {} + self.foldermenu = {} + + self:AddRightClick( self.filemenu, "New File" , function() + Derma_StringRequest( "New File in \"" .. string.GetPathFromFilename(self.File.FileDir) .. "\"", "Create new file", "", + function( strTextOut ) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + file.Write( string.GetPathFromFilename(self.File.FileDir) .. "/" .. strTextOut .. ".txt", "" ) + self:UpdateFolders() + end ) + end) + self:AddRightClick( self.filemenu, "Rename to.." , function() + Derma_StringRequest( "Rename File \"" .. self.File.Name .. "\"", "Rename file " .. self.File.Name, self.File.Name, + function( strTextOut ) + // Renaming starts in the garrysmod folder now, in comparison to other commands that start in the data folder. + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + file.Rename( "data/" .. self.File.FileDir, "data/" .. string.GetPathFromFilename(self.File.FileDir) .. "/" .. strTextOut .. ".txt" ) + self:UpdateFolders() + end ) + end ) + self:AddRightClick( self.filemenu, "Delete" , function() + if(file.Exists(self.File.FileDir)) then file.Delete(self.File.FileDir) end + self:UpdateFolders() + end ) + self:AddRightClick( self.filemenu, "Copy to.." , function() + Derma_StringRequest( "Copy File \"" .. self.File.Name .. "\"", "Copy File to..." , self.File.Name, + function( strTextOut ) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + file.Write(string.GetPathFromFilename(self.File.FileDir) .. "/" .. strTextOut .. ".txt", file.Read(self.File.FileDir) ) + self:UpdateFolders() + end ) + end ) + self:AddRightClick( self.foldermenu, "New File.." , function() + Derma_StringRequest( "New File in \"" .. self.File.FileDir .. "\"", "Create new file", "", + function( strTextOut ) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + file.Write( self.File.FileDir .. "/" .. strTextOut .. ".txt", "" ) + self:UpdateFolders() + end ) + end ) + self:AddRightClick( self.panelmenu, "New File.." , function() + Derma_StringRequest( "New File in \"" .. self.File.FileDir .. "\"", "Create new file", "", + function( strTextOut ) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + file.Write( self.File.FileDir .. "/" .. strTextOut .. ".txt", "" ) + self:UpdateFolders() + end ) + end ) + + function self:OnFileClick( Dir ) + //Override this. + end + function self:OnFolderClick( Dir ) + //Override this. + end +end + +function PANEL:UpdateFolders() + self.Folders = vgui.Create( "DTree" , self) + self.Folders:SetPadding( 5 ) + self.Folders:SetPos(0,0) + self.Folders:SetSize(self:GetWide(),self:GetTall()-20) + setTree( self.startfolder , self.Folders ) + self.Folders.DoClick = function( tree, node ) + self.File = node + node:SetExpanded( !node.m_bExpanded ) + if(node.IsFile) then self:OnFileClick() else self:OnFolderClick() end + return true + end + self.Folders.DoRightClick = function( tree, node) + self.File = node + if(node.IsFile) then self:OpenMenu(self.filemenu) else self:OpenMenu(self.foldermenu) end + return true + end + self.DoRightClick = function(self) + self.File.FileDir = self.startfolder + self:OpenMenu(self.panelmenu) + return true + end +end + +function PANEL:PerformLayout() + local w,h = self:GetSize() + self.Update:SetPos( 0, h-20 ) + self.Update:SetSize( w, 20 ) + self.Folders:SetSize( w, h-20 ) +end + +function PANEL:OpenMenu(menu) + if(!menu) then return end + if(table.Count(menu)<1) then return end + self.Menu = vgui.Create("DMenu", self.Folders) + for k, v in pairs(menu) do + self.Menu:AddOption(k,v) + end + self.Menu:Open() +end + +function PANEL:AddRightClick(menu, name, option) + if(!menu) then menu = {} end + if(menu[name]) then return end + menu[name] = option +end + +function PANEL:Setup(folder) + self.startfolder = folder + self:UpdateFolders() +end + +vgui.Register( "wire_expression2_browser" , PANEL , "DPanel" ) diff --git a/lua/wire/client/wire_expression2_editor.lua b/lua/wire/client/wire_expression2_editor.lua new file mode 100644 index 0000000000..7329c784e9 --- /dev/null +++ b/lua/wire/client/wire_expression2_editor.lua @@ -0,0 +1,764 @@ +local Editor = {} + +local invalid_filename_chars = { + ["*"] = "", + ["?"] = "", + [">"] = "", + ["<"] = "", + ["|"] = "", + ["\\"] = "", + ['"'] = "", + [" "] = "_", +} + +// overwritten commands +function Editor:Init() + self.Title = "" + self.subTitle = "" + self.LastClick = 0 + self.GuiClick = 0 + self.SimpleGUI = false + self.Location = "" + + // colors + self.colors = {} + self.colors.col_FL = Color( 65, 105, 225, 255 ) //Royal Blue + self.colors.col_FR = Color( 25, 25, 112, 255 ) //Midnight Blue + self.colors.tmp_FL = Color( 65, 105, 225, 255 ) + self.colors.tmp_FR = Color( 25, 25, 112, 255 ) + self.colors.tmp_Dark = 255 + + self.C = {} + self.Components = {} + + surface.CreateFont( "default", 11, 300, false, false, "E2SmallFont" ) + self.logo = surface.GetTextureID("vgui/e2logo") + + self:InitComponents() + + local width, height = math.min(surface.ScreenWidth()-200, 780), math.min(surface.ScreenHeight()-200, 580) + self:SetPos((surface.ScreenWidth() - width) / 2, (surface.ScreenHeight() - height) / 2) + self:SetSize(width, height) + + // This turns off the engine drawing + self:SetPaintBackgroundEnabled(false) + self:SetPaintBorderEnabled(false) + + self:SetV(false) + + self:InitShutdownHook() +end + +function Editor:Paint() + local w,h = self:GetSize() + if(self.SimpleGUI) then + draw.RoundedBox(4, 0, 0, w, h, self.colors.col_FL) + surface.SetDrawColor( 0, 0, 0, 150 ) + surface.DrawRect( 0, 22, w, 1 ) + else + local dif = {(self.colors.col_FR.r-self.colors.col_FL.r)/w, (self.colors.col_FR.g-self.colors.col_FL.g)/w, (self.colors.col_FR.b-self.colors.col_FL.b)/w } + draw.RoundedBox(4, 0, 0, 10, h, self.colors.col_FL) + draw.RoundedBox(4, w-15, 0, 15, h, self.colors.col_FR) + + for i = 5 , w-9, 5 do + surface.SetDrawColor(math.floor(self.colors.col_FL.r + dif[1]*i), math.floor(self.colors.col_FL.g + dif[2]*i), math.floor(self.colors.col_FL.b + dif[3]*i), self.colors.col_FL.a) + surface.DrawRect( i, 0, 5, h ) + end + end + draw.RoundedBox(4, 7, 27, w - 14, h - 34, Color(0, 0, 0, 192)) + surface.SetDrawColor( 0, 0, 0, 150 ) + surface.DrawRect( 0, 22, w, 1 ) + surface.SetDrawColor( 255, 255, 255, 255 ) + + draw.RoundedBox(4, 7, 27, w - 14, h - 34, Color(0, 0, 0, 192)) + return true +end + +function Editor:PaintOver() + local w,h = self:GetSize() + + draw.RoundedBox( 4, 0, 0, 118, 21, self.colors.col_FL ) + surface.SetFont("DefaultBold") + surface.SetTextColor( 255, 255, 255, 255 ) + surface.SetTextPos( 10, 6 ) + surface.DrawText(self.Title .. self.subTitle) + /*if(self.E2) then + surface.SetTexture(self.logo) + surface.SetDrawColor( 255, 255, 255, 128 ) + surface.DrawTexturedRect( w-148, h-158, 128, 128) + end*/ + surface.SetDrawColor( 255, 255, 255, 255 ) + surface.SetTextPos(0,0) + surface.SetFont("Default") + return true +end + +function Editor:PerformLayout() + local w,h = self:GetSize() + + for i=1, #self.Components do + local c = self.Components[i] + local c_x,c_y,c_w,c_h = c.x,c.y,c.w,c.h + if(c.x<0) then c_x = w+c.x end + if(c.y<0) then c_y = h+c.y end + if(c.w<0) then c_w = w+c.w-c_x end + if(c.h<0) then c_h = h+c.h-c_y end + c.panel:SetPos(c_x,c_y) + c.panel:SetSize(c_w,c_h) + end +end + +function Editor:OnMousePressed(mousecode) + if(mousecode != 107) then return end // do nothing if mouseclick is other than left-click + if(!self.pressed) then + self.pressed = true + self.p_x, self.p_y = self:GetPos() + self.p_w, self.p_h = self:GetSize() + self.p_mx = gui.MouseX() + self.p_my = gui.MouseY() + self.p_mode = self:getMode() + if(self.p_mode == "drag") then + if(self.GuiClick>CurTime()-1) then + self:fullscreen() + self.pressed = false + self.GuiClick = 0 + else + self.GuiClick = CurTime() + end + end + end +end + +function Editor:OnMouseReleased(mousecode) + if(mousecode != 107) then return end // do nothing if mouseclick is other than left-click + self.pressed = false +end + +function Editor:Think() + if(self.fs) then return end + if(self.pressed) then + if(!input.IsMouseDown()) then // needs this if you let go of the mouse outside the panel + self.pressed = false + end + local movedX = gui.MouseX()-self.p_mx + local movedY = gui.MouseY()-self.p_my + if(self.p_mode == "drag") then + local x = self.p_x + movedX + local y = self.p_y + movedY + if(x<10 and x>-10) then x = 0 end + if(y<10 and y>-10) then y = 0 end + if(x+self.p_wsurface.ScreenWidth()-10) then x = surface.ScreenWidth()-self.p_w end + if(y+self.p_hsurface.ScreenHeight()-10) then y = surface.ScreenHeight()-self.p_h end + self:SetPos(x,y) + end + if(self.p_mode == "sizeBR") then + local w = self.p_w + movedX + local h = self.p_h + movedY + if(self.p_x+wsurface.ScreenWidth()-10) then w = surface.ScreenWidth()-self.p_x end + if(self.p_y+hsurface.ScreenHeight()-10) then h = surface.ScreenHeight()-self.p_y end + if(w<400) then w = 400 end + if(h<400) then h = 400 end + self:SetSize(w,h) + end + if(self.p_mode == "sizeR") then + local w = self.p_w + movedX + if(w<400) then w = 400 end + self:SetWide(w) + end + if(self.p_mode == "sizeB") then + local h = self.p_h + movedY + if(h<400) then h = 400 end + self:SetTall(h) + end + end + if(!self.pressed) then + local cursor = "arrow" + local mode = self:getMode() + if(mode == "sizeBR") then cursor = "sizenwse" end + if(mode == "sizeR") then cursor = "sizewe" end + if(mode == "sizeB") then cursor = "sizens" end + if(cursor != self.cursor) then + self.cursor = cursor + self:SetCursor(self.cursor) + end + end + + local x, y = self:GetPos() + local w, h = self:GetSize() + + if w < 400 then w = 400 end + if h < 400 then h = 400 end + if x < 0 then x = 0 end + if y < 0 then y = 0 end + if x + w > surface.ScreenWidth() then x = surface.ScreenWidth() - w end + if y + h > surface.ScreenHeight() then y = surface.ScreenHeight() - h end + + self:SetPos(x, y) + self:SetSize(w, h) +end + +// special functions + +function Editor:fullscreen() + if(self.fs) then + self:SetPos(self.preX,self.preY) + self:SetSize(self.preW,self.preH) + self.fs = false + else + self.preX,self.preY = self:GetPos() + self.preW,self.preH = self:GetSize() + self:SetPos(0, 0) + self:SetSize(surface.ScreenWidth(), surface.ScreenHeight()) + self.fs = true + end +end + +function Editor:getMode() + local x, y = self:GetPos() + local w, h = self:GetSize() + local ix = gui.MouseX() - x + local iy = gui.MouseY() - y + + if(ix<0 or ix>w or iy<0 or iy>h) then return end // if the mouse is outside the box + if(iy<22) then + return "drag" + end + if(iy>h-10) then + if(ix>w-20) then return "sizeBR" end + return "sizeB" + end + if(ix>w-10) then + if(iy>h-20) then return "sizeBR" end + return "sizeR" + end +end + +function Editor:addComponent(panel,x,y,w,h) + local t = #self.Components+1 + self.Components[t] = {} + self.Components[t].panel = panel + self.Components[t].x = x + self.Components[t].y = y + self.Components[t].w = w + self.Components[t].h = h + return self.Components[t] +end + +// initialization commands + +function Editor:InitComponents() + self.Components = {} + self.C = {} + + // addComponent( panel, x, y, w, h ) + // if x, y, w, h is minus, it will stay relative to right or buttom border + self.C['Close'] = self:addComponent(vgui.Create( "DSysButton", self ) , -22, 4, 18, 18) // Close button + self.C['Inf'] = self:addComponent(vgui.Create( "DSysButton", self ) , -42, 4, 18, 18) // Info button + self.C['Sav'] = self:addComponent(vgui.Create( "Button", self ) , 191, 30, 20, 20) // Save button + self.C['Dir'] = self:addComponent(vgui.Create( "Label", self ) , 220, 30, -70, 20) // Directory line + self.C['SaE'] = self:addComponent(vgui.Create( "Button", self ) , -70, 30, -10, 20) // Save & Exit button + self.C['SavAs'] = self:addComponent(vgui.Create( "Button", self ) , -135, 30, -80, 20) // Save As button + self.C['Browser'] = self:addComponent(vgui.Create( "wire_expression2_browser", self ) , 10, 30, 157, -10) // Expression browser + self.C['Editor'] = self:addComponent(vgui.Create( "Expression2Editor", self ) , 170, 53, -10, -33) // Expression editor + self.C['Val'] = self:addComponent(vgui.Create( "Label", self ) , 170, -30, -10, 20) // Validation line + self.C['Btoggle'] = self:addComponent(vgui.Create( "Button", self ) , 170, 30, 20, 20) // Toggle Browser being shown + self.C['ConBut'] = self:addComponent(vgui.Create( "Button", self ) , -62, 4, 18, 18) // Control panel open/close + self.C['Control'] = self:addComponent(vgui.Create( "Panel", self ) ,-210, 52, 200, 150) // Control Panel + self.C['Credit'] = self:addComponent(vgui.Create( "TextEntry", self ) ,-160, 52, 150, 60) // Credit box + /*self.C['CtC'] = self:addComponent(vgui.Create( "Button", self ) ,-105, 30, -71, 20) // Copy to Clipboard button + + self.C['CtC'].panel:SetText("") + self.C['CtC'].panel.Font = "E2SmallFont" + self.C['CtC'].panel.Paint = function(button) + local w,h = button:GetSize() + draw.RoundedBox(1, 0, 0, w, h, self.colors.col_FL) + if ( button.Hovered ) then draw.RoundedBox(0, 1, 1, w - 2, h - 2, Color(0,0,0,192)) end + surface.SetFont(button.Font) + surface.SetTextPos( 3, 4 ) + surface.SetTextColor( 255, 255, 255, 255 ) + surface.DrawText(" Copy") + end + self.C['CtC'].panel.DoClick = function( button ) + SetClipboardText(string.gsub(self.C['Editor'].panel:GetValue(),"\n", "\r\n")) + end*/ + + // extra component options + self.C['Close'].panel:SetType( "close" ) + self.C['Close'].panel:SetDrawBorder( false ) + self.C['Close'].panel:SetDrawBackground( false ) + self.C['Close'].panel.DoClick = function ( button ) self:Close() end + self.C['Credit'].panel:SetText("\t\tCREDITS\n\n\tEditor by: \tSyranide and Shandolum") + self.C['Credit'].panel:SetMultiline(true) + self.C['Credit'].panel:SetVisible(false) + self.C['Inf'].panel:SetType( "question" ) + self.C['Inf'].panel:SetDrawBorder( false ) + self.C['Inf'].panel:SetDrawBackground( false ) + self.C['Inf'].panel.OnCursorEntered = function() self.C['Credit'].panel:SetVisible(true) end + self.C['Inf'].panel.OnCursorExited = function() self.C['Credit'].panel:SetVisible(false) end + self.C['Sav'].panel:SetText("") + self.C['Sav'].panel.Icon = surface.GetTextureID( "vgui/spawnmenu/save" ) + self.C['Sav'].panel.Paint = function(button) + local w,h = button:GetSize() + draw.RoundedBox(1, 0, 0, w, h, self.colors.col_FL) + if ( button.Hovered ) then draw.RoundedBox(0, 1, 1, w - 2, h - 2, Color(0,0,0,192)) end + surface.SetTexture(button.Icon) + surface.SetDrawColor( 255, 255, 255, 255 ) + surface.DrawTexturedRect( 2, 2, w-4, h-4) + end + self.C['Sav'].panel.DoClick = function( button ) self:SaveFile( self.chosenfile ) end + + self.C['SaE'].panel:SetText("") + self.C['SaE'].panel.Font = "E2SmallFont" + self.C['SaE'].panel.Paint = function(button) + local w,h = button:GetSize() + draw.RoundedBox(1, 0, 0, w, h, self.colors.col_FL) + if ( button.Hovered ) then draw.RoundedBox(0, 1, 1, w - 2, h - 2, Color(0,0,0,192)) end + surface.SetFont(button.Font) + surface.SetTextPos( 3, 4 ) + surface.SetTextColor( 255, 255, 255, 255 ) + if(self.chip) then surface.DrawText("Upload & Exit") + else surface.DrawText(" Save & Exit") end + end + self.C['SaE'].panel.DoClick = function( button ) self:SaveFile( self.chosenfile, true ) end + + self.C['SavAs'].panel:SetText("") + self.C['SavAs'].panel.Font = "E2SmallFont" + self.C['SavAs'].panel.Paint = function(button) + local w,h = button:GetSize() + draw.RoundedBox(1, 0, 0, w, h, self.colors.col_FL) + if ( button.Hovered ) then draw.RoundedBox(0, 1, 1, w - 2, h - 2, Color(0,0,0,192)) end + surface.SetFont(button.Font) + surface.SetTextPos( 3, 4 ) + surface.SetTextColor( 255, 255, 255, 255 ) + surface.DrawText(" Save As") + end + self.C['SavAs'].panel.DoClick = function( button ) self:SaveFile( self.chosenfile, false, true ) end + + self.C['Browser'].panel.OnFileClick = function(panel) + if(panel.sDir and panel.sDir == panel.File.FileDir and CurTime()-LastClick < 1) then + self:LoadFile(panel.sDir) + else + panel.sDir = panel.File.FileDir + LastClick = CurTime() + end + end + self.C['Browser'].panel:AddRightClick( self.C['Browser'].panel.filemenu, "Save To" , function() + self:SaveFile( self.C['Browser'].panel.File.FileDir) + end ) + self.C['Editor'].panel.OnTextChanged = function(panel) + timer.Create("e2autosave", 5, 1, function() + self:AutoSave() + end) + end + self.C['Editor'].panel.OnShortcut = function(_, code) + if code == KEY_S then + self:SaveFile(self.chosenfile) + self:Validate() + elseif code == KEY_SPACE then + self:Validate(true) + end + end + self.C['Editor'].panel:RequestFocus() + self.C['Val'].panel:SetText( " Click to validate..." ) + self.C['Val'].panel.OnMousePressed = function(panel) self:Validate(true) end + self.C['Btoggle'].panel:SetText("<") + self.C['Btoggle'].panel.Paint = function(button) + local w,h = button:GetSize() + draw.RoundedBox(1, 0, 0, w, h, self.colors.col_FL) + if ( button.Hovered ) then draw.RoundedBox(0, 1, 1, w - 2, h - 2, Color(0,0,0,192)) end + end + self.C['Btoggle'].panel.DoClick = function(button) + if(button.hide) then + button.hide = false + button:SetText("<") + else + button.hide = true + button:SetText(">") + end + button.toggle = true + end + self.C['Btoggle'].panel.anispeed = 10 + self.C['Btoggle'].panel.Think = function(button) + if(!button.toggle) then return end + if(button.hide and self.C['Btoggle'].x > 10) then + self.C['Btoggle'].x = self.C['Btoggle'].x-button.anispeed + self.C['Sav'].x = self.C['Sav'].x-button.anispeed + self.C['Dir'].x = self.C['Dir'].x-button.anispeed + self.C['Editor'].x = self.C['Editor'].x-button.anispeed + self.C['Val'].x = self.C['Val'].x-button.anispeed + self.C['Browser'].w = self.C['Browser'].w-button.anispeed + elseif(!button.hide and self.C['Btoggle'].x < 170) then + self.C['Btoggle'].x = self.C['Btoggle'].x+button.anispeed + self.C['Sav'].x = self.C['Sav'].x+button.anispeed + self.C['Dir'].x = self.C['Dir'].x+button.anispeed + self.C['Editor'].x = self.C['Editor'].x+button.anispeed + self.C['Val'].x = self.C['Val'].x+button.anispeed + self.C['Browser'].w = self.C['Browser'].w+button.anispeed + end + + if(self.C['Browser'].panel:IsVisible() and self.C['Browser'].w <= 0) then self.C['Browser'].panel:SetVisible(false) + elseif(!self.C['Browser'].panel:IsVisible() and self.C['Browser'].w > 0) then self.C['Browser'].panel:SetVisible(true) end + self:InvalidateLayout() + if(button.hide) then + if(self.C['Btoggle'].x > 10 or self.C['Sav'].x > 30 or self.C['Dir'].x > 50 or self.C['Val'].x < 170 or self.C['Browser'].w > 0) then return end + button.toggle = false + else + if(self.C['Btoggle'].x < 170 or self.C['Sav'].x < 190 or self.C['Dir'].x < 210 or self.C['Val'].x < 170 or self.C['Browser'].w < 150) then return end + button.toggle = false + end + + end + self.C['ConBut'].panel.Icon = surface.GetTextureID( "gui/silkicons/wrench" ) + self.C['ConBut'].panel:SetText("") + self.C['ConBut'].panel.Paint = function(button) + local w,h = button:GetSize() + surface.SetTexture(button.Icon) + surface.SetDrawColor( 255, 255, 255, 255 ) + surface.DrawTexturedRect( 2, 2, w-4, h-4) + end + self.C['ConBut'].panel.DoClick = function() self.C['Control'].panel:SetVisible(!self.C['Control'].panel:IsVisible()) end + self:InitControlPanel(self.C['Control'].panel) //making it seperate for better overview + self.C['Control'].panel:SetVisible(false) + self:Validate() +end + +function Editor:AutoSave() + local buffer = self:GetCode() + if self.savebuffer == buffer then return end + self.savebuffer = buffer + file.Write(self.Location .. "/_autosave_.txt", buffer) +end + +function Editor:InitControlPanel(frame) + + local ColorPanel = vgui.Create( "Panel" , frame) + ColorPanel:SetPos(0,0) + ColorPanel:SetSize(200,130) + ColorPanel.Paint = function(panel) + local w,h = panel:GetSize() + surface.SetDrawColor( 0, 0, 0, 150 ) + surface.DrawRect(0, 0, w, h) + end + + local SimpleColors = vgui.Create( "Label", ColorPanel) + SimpleColors:SetPos(10,10) + SimpleColors:SetSize(180,20) + SimpleColors:SetText("Simple Colors = off") + SimpleColors.OnMousePressed = function(check) + if(self.SimpleGUI) then + self.SimpleGUI = false + check:SetText("Simple Colors = off") + else + self.SimpleGUI = true + check:SetText("Simple Colors = on") + end + self:InvalidateLayout() + end + local FLColor = vgui.Create( "DColorCircle" , ColorPanel) + FLColor:SetPos(30,35) + FLColor:SetSize(64,64) + FLColor.SetFrameColor = function(panel) + self.colors.tmp_FL = panel:GetRGB() + self:CalculateColor() + end + FLColor.TranslateValues = function(panel, x, y ) return self:TranslateValues(panel, x, y ) end + local FRColor = vgui.Create( "DColorCircle" , ColorPanel) + FRColor:SetPos(120,35) + FRColor:SetSize(64,64) + FRColor.SetFrameColor = function(panel) + self.colors.tmp_FR = panel:GetRGB() + self:CalculateColor() + end + FRColor.TranslateValues = function(panel, x, y ) return self:TranslateValues(panel, x, y ) end + local DarknessColor = vgui.Create( "DSlider" , ColorPanel) + DarknessColor:SetPos(10,100) + DarknessColor:SetSize(180,30) + DarknessColor.TranslateValues = function(panel, x, y ) + self.colors.tmp_Dark = 255-math.floor(x*255) + self:CalculateColor() + return x, 0.5 + end + DarknessColor:SetSlideX(0) +end + +function Editor:CalculateColor() + self.colors.col_FL.r = math.floor(self.colors.tmp_FL.r*self.colors.tmp_Dark/255) + self.colors.col_FL.g = math.floor(self.colors.tmp_FL.g*self.colors.tmp_Dark/255) + self.colors.col_FL.b = math.floor(self.colors.tmp_FL.b*self.colors.tmp_Dark/255) + + self.colors.col_FR.r = math.floor(self.colors.tmp_FR.r*self.colors.tmp_Dark/255) + self.colors.col_FR.g = math.floor(self.colors.tmp_FR.g*self.colors.tmp_Dark/255) + self.colors.col_FR.b = math.floor(self.colors.tmp_FR.b*self.colors.tmp_Dark/255) + + self:InvalidateLayout() +end + +// used with color-circles +function Editor:TranslateValues(panel, x, y ) + x = x - 0.5 + y = y - 0.5 + local angle = math.atan2( x, y ) + local length = math.sqrt( x*x + y*y ) + length = math.Clamp( length, 0, 0.5 ) + x = 0.5 + math.sin( angle ) * length + y = 0.5 + math.cos( angle ) * length + panel:SetHue( math.Rad2Deg( angle ) + 270 ) + panel:SetSaturation( length * 2 ) + panel:SetRGB( HSVToColor( panel:GetHue(), panel:GetSaturation(), 1 ) ) + panel:SetFrameColor() + return x, y +end +// options + +-- code1 contains the code that is not to be marked +local code1 = "@name \n@inputs \n@outputs \n@persist \n@trigger \n\n" +-- code2 contains the code that is to be marked, so it can simply be overwritten or deleted. +local code2 = [[ +# IMPORTANT: Cannot get past "Loading Extensions..."? Remove the "PCMod" addon. +# IMPORTANT: Strange errors when validating? Update the "Stargate Extras" addon. +# IMPORTANT: Not playing on your own server? Tell owner to remove those addons. +# +# Performance monitoring and restriction have been implemented. +# While-loops and for-loops now exist! (break and continue too) +# Example: while(Val < Max) { Val++, if (Val == 10) { break } } +# Example: for(I=Start, End) { ... }, for(I=Start, End, Step) { ... } +# +# Documentation and examples are available at: +# http://wiki.garrysmod.com/?title=Wire_Expression2 +# The community is available at http://www.wiremod.com +]] +local defaultcode = code1 .. code2 + +function Editor:NewScript() + self:AutoSave() + self:ChosenFile() + + -- add both code1 and code2 to the editor + self:SetCode(defaultcode) + local ed = self.C['Editor'].panel + -- mark only code2 + ed.Start = ed:MovePosition({ 1, 1 }, code1:len()) + ed.Caret = ed:MovePosition({ 1, 1 }, defaultcode:len()) +end + +local id = 0 +function Editor:InitShutdownHook() + id = id + 1 + + -- save code when shutting down + hook.Add("ShutDown", "wire_expression2_ShutDown"..id, function() + --if wire_expression2_editor == nil then return end + local buffer = self:GetCode() + if buffer == defaultcode then return end + file.Write(self.Location .. "/_shutdown_.txt", buffer) + end) +end + +local chipmap = { + E2 = "wire_expression2_validate", + CPU = "wire_cpu_validate", + GPU = "wire_cpu_validate", +} + +function Editor:Validate(gotoerror) + local validator = chipmap[self.EditorType] + if not validator then return end + validator = _G[validator] + if not validator then return end + + self:ExtractName() + local errors = validator(self:GetCode()) + if not errors then + self.C['Val'].panel:SetBGColor(0, 128, 0, 180) + self.C['Val'].panel:SetFGColor(255, 255, 255, 128) + self.C['Val'].panel:SetText( " Validation successful" ) + return true + end + if gotoerror then + local row, col = errors:match("at line ([0-9]+), char ([0-9]+)$") + if not row then + row, col = errors:match("at line ([0-9]+)$"), 1 + end + if row then self.C['Editor'].panel:SetCaret({ tonumber(row), tonumber(col) }) end + end + self.C['Val'].panel:SetBGColor(128, 0, 0, 180) + self.C['Val'].panel:SetFGColor(255, 255, 255, 128) + self.C['Val'].panel:SetText( " " .. errors ) + return false +end + +function Editor:SubTitle(sub) + if(!sub) then self.subTitle = "" + else self.subTitle = " - " .. sub end +end + +function Editor:SetV(bool) + if(bool) then + self:MakePopup() + self:SetVisible(true) + self:InvalidateLayout(true) + self:SetKeyBoardInputEnabled(true) + self:Validate() + else + self:SetVisible(false) + self:SetKeyBoardInputEnabled() + end + if CanRunConsoleCommand() then RunConsoleCommand("wire_expression2_event", bool and "editor_open" or "editor_close") end +end + +function Editor:ChosenFile(Line) + self.chosenfile = Line + if(Line) then + self:SubTitle("Editing: " .. Line) + self.C['Dir'].panel:SetText(Line) + else + self:SubTitle() + self.C['Dir'].panel:SetText("") + end +end + +function Editor:ExtractName() + if(!self.E2) then return end + local code = self:GetCode() + local lines = string.Explode("\n", code) + e2savefilenfn = "filename" + for _,line in ipairs(lines) do + if string.sub(line, 1, 6) == "@name " then + if string.Trim(string.sub(line, 7)) == "" then + Expression2SetName(nil) + e2savefilenfn = "filename" + return + else + Expression2SetName(string.Trim(string.sub(line, 7))) + e2savefilenfn = string.Replace(string.Trim(string.sub(line, 7)), "/", "") + e2savefilenfn = string.gsub(e2savefilenfn, ".", invalid_filename_chars) + return + end + end + end +end + +function Editor:SetCode(code) + self.C['Editor'].panel:SetText(code) + self.savebuffer = self:GetCode() + self:Validate() + self:ExtractName() +end + +function Editor:GetCode() + return self.C['Editor'].panel:GetValue() +end + +function Editor:Open(Line,code) + if(self:IsVisible() and !Line and !code) then self:Close() end + self:SetV(true) + if(code) then + if self:GetCode() == code then return end + self:ChosenFile() + self:SetCode(code) + if(Line) then self:SubTitle("Editing: " .. Line) end + return + end + if(Line) then self:LoadFile(Line) return end +end + +function Editor:SaveFile(Line, close, SaveAs) + self:ExtractName() + if(close and self.chip) then + if(!self:Validate(true)) then return end + wire_expression2_upload() + self:Close() + return + end + if(!Line or SaveAs or Line == self.Location .. "/" .. ".txt") then + Derma_StringRequest( "Save to New File", "", e2savefilenfn, + function( strTextOut ) + strTextOut = string.gsub(strTextOut, ".", invalid_filename_chars) + self:SaveFile( self.Location .. "/" .. strTextOut .. ".txt", close ) + end ) + return + end + + file.Write(Line, self:GetCode()) + + local panel = self.C['Val'].panel + timer.Simple(0,panel.SetText, panel, " Saved as "..Line) + surface.PlaySound("ambient/water/drip3.wav") + + if(!self.chip) then self:ChosenFile(Line) end + if(close) then + GAMEMODE:AddNotify( "Expression saved as "..Line..".", NOTIFY_GENERIC, 7 ) + self:Close() + end +end + +function Editor:LoadFile( Line ) + if(!Line or file.IsDir( Line )) then return end + local str = file.Read(Line) + if str == nil then + Error("ERROR LOADING FILE!") + else + self:AutoSave() + if(!self.chip) then self:ChosenFile(Line) end + self:SetCode(str) + end +end + +function Editor:Close() + timer.Stop("e2autosave") + self:AutoSave() + + self:ExtractName() + self:SetV(false) + self.chip = false +end + +function Editor:Setup(nTitle, nLocation, nEditorType) + self.Title = nTitle + self.Location = nLocation + self.EditorType = nEditorType + self.C['Browser'].panel:Setup(nLocation) + if(!nEditorType) then + self.C['Editor'].panel.SyntaxColorLine = function(self, row) return {{self.Rows[row], { Color(255, 255, 255, 255), false}}} end + + -- Remove validation line + self.C['Editor'].h = -10 + self.C['Val'].panel:SetVisible(false) + elseif nEditorType == "CPU" or nEditorType == "GPU" then + -- Select syntax highlighter + self.C['Editor'].panel.SyntaxColorLine = self.C['Editor'].panel.CPUGPUSyntaxColorLine + + -- insert default code + local code = "// "..nEditorType.." Syntax Higlighting \n// By -HP-" + self:SetCode(code) + + -- mark all code + local ed = self.C['Editor'].panel + ed.Start = ed:MovePosition({ 1, 1 }, 0) + ed.Caret = ed:MovePosition({ 1, 1 }, code:len()) + elseif nEditorType == "E2" then + -- Add "E2Helper" button + local E2Help = self:addComponent(vgui.Create("Button", self), -200, 30, -145, 20) + E2Help.panel:SetText("") + E2Help.panel.Font = "E2SmallFont" + E2Help.panel.Paint = function(button) + local w,h = button:GetSize() + draw.RoundedBox(1, 0, 0, w, h, self.colors.col_FL) + if ( button.Hovered ) then draw.RoundedBox(0, 1, 1, w - 2, h - 2, Color(0,0,0,192)) end + surface.SetFont(button.Font) + surface.SetTextPos( 3, 4 ) + surface.SetTextColor( 255, 255, 255, 255 ) + surface.DrawText(" E2Helper") + end + E2Help.panel.DoClick = function() E2Helper.Show() end + self.C.E2Help = E2Help + + -- Flag as E2 + self.E2 = true + self:NewScript() + end + self:InvalidateLayout() +end + + +vgui.Register( "Expression2EditorFrame" , Editor , "DFrame" ) diff --git a/lua/wire/opcodes.lua b/lua/wire/opcodes.lua new file mode 100644 index 0000000000..5d5d1d4cd2 --- /dev/null +++ b/lua/wire/opcodes.lua @@ -0,0 +1,231 @@ +local opcodes = {} +opcodes["jne"] = 1 //JNE X : IP = X, IF CMPR ~= 0 +opcodes["jnz"] = 1 //JNZ X : IP = X, IF CMPR ~= 0 +opcodes["jmp"] = 2 //JMP X : IP = X +opcodes["jg"] = 3 //JG X : IP = X, IF CMPR > 0 +opcodes["jnle"] = 3 //JNLE X : IP = X, IF !(CMPR <= 0) +opcodes["jge"] = 4 //JGE X : IP = X, IF CMPR >= 0 +opcodes["jnl"] = 4 //JNL X : IP = X, IF !(CMPR < 0) +opcodes["jl"] = 5 //JL X : IP = X, IF CMPR < 0 +opcodes["jnge"] = 5 //JNGE X : IP = X, IF !(CMPR >= 0) +opcodes["jle"] = 6 //JLE X : IP = X, IF CMPR <= 0 +opcodes["jng"] = 6 //JNG X : IP = X, IF !(CMPR > 0) +opcodes["je"] = 7 //JE X : IP = X, IF CMPR = 0 +opcodes["jz"] = 7 //JZ X : IP = X, IF CMPR = 0 +opcodes["cpuid"] = 8 //CPUID X : EAX -> CPUID[X] +opcodes["push"] = 9 //PUSH X : WRITE(ESP+SS,X); ESP = ESP - 1 +opcodes["add"] = 10 //ADD X,Y : X = X + Y +opcodes["sub"] = 11 //SUB X,Y : X = X - Y +opcodes["mul"] = 12 //MUL X,Y : X = X * Y +opcodes["div"] = 13 //DIV X,Y : X = X / Y +opcodes["mov"] = 14 //MOV X,Y : X = y +opcodes["cmp"] = 15 //CMP X,Y : CMPR = X - Y +opcodes["rd"] = 16 //RD X,Y : X = MEMORY[Y] +opcodes["wd"] = 17 //WD X,Y : MEMORY[X] = Y +opcodes["min"] = 18 //MIN X,Y : MIN(X,Y) +opcodes["max"] = 19 //MAX X,Y : MAX(X,Y) +opcodes["inc"] = 20 //INC X : X = X + 1 +opcodes["dec"] = 21 //DEC X : X = X - 1 +opcodes["neg"] = 22 //NEG X : X = -X +opcodes["rand"] = 23 //RAND X : X = Random(0..1) +opcodes["loop"] = 24 //LOOP X : IF ECX ~= 0 THEN JUMP X 2.00 +opcodes["loopa"] = 25 //LOOPA X : IF EAX ~= 0 THEN JUMP X 2.00 +opcodes["loopb"] = 26 //LOOPB X : IF EBX ~= 0 THEN JUMP X 2.00 +opcodes["loopd"] = 27 //LOOPD X : IF EDX ~= 0 THEN JUMP X 2.00 +opcodes["spg"] = 28 //SPG X : PAGE(X) = READ ONLY 2.00 +opcodes["cpg"] = 29 //CPG X : PAGE(X) = READ AND WRITE 2.00 +opcodes["pop"] = 30 //POP X : X = READ(ESP+SS); ESP = ESP + 1 +opcodes["call"] = 31 //CALL X : IP -> STACK; IP = X +opcodes["bnot"] = 32 //BNOT X : X = BINARY NOT X +opcodes["fint"] = 33 //FINT X : X = FLOOR(X) +opcodes["frnd"] = 34 //FRND X : X = ROUND(X) +opcodes["ffrac"] = 35 //FFRAC X : X = X - FLOOR(X) +opcodes["finv"] = 36 //FINV X : X = 1 / X +opcodes["halt"] = 37 //HALT X : HALT UNTIL PORT[X] +opcodes["fshl"] = 38 //FSHL X : X = X * 2 2.00 +opcodes["fshr"] = 39 //FSHR X : X = X / 2 2.00 +opcodes["ret"] = 40 //RET : IP <- STACK +opcodes["iret"] = 41 //IRET : IP <- STACK 2.00 +opcodes["sti"] = 42 //STI : IF = TRUE 2.00 +opcodes["cli"] = 43 //CLI : IF = FALSE 2.00 +opcodes["stp"] = 44 //STP : PF = TRUE 2.00 +opcodes["clp"] = 45 //CLP : PF = FALSE 2.00 +opcodes["retf"] = 47 //RETF : IP,CS <- STACK 2.00 +opcodes["stef"] = 48 //STEF : EF = TRUE 4.00 +opcodes["clef"] = 49 //CLEF : EF = FALSE 4.00 +opcodes["and"] = 50 //FAND X,Y : X = X AND Y +opcodes["or"] = 51 //FOR X,Y : X = X OR Y +opcodes["xor"] = 52 //FXOR X,Y : X = X XOR Y +opcodes["fsin"] = 53 //FSIN X,Y : X = SIN Y +opcodes["fcos"] = 54 //FCOS X,Y : X = COS Y +opcodes["ftan"] = 55 //FTAN X,Y : X = TAN Y +opcodes["fasin"] = 56 //FASIN X,Y : X = ASIN Y +opcodes["facos"] = 57 //FACOS X,Y : X = ACOS Y +opcodes["fatan"] = 58 //FATAN X,Y : X = ATAN Y +opcodes["mod"] = 59 //MOD X,Y : X = X MOD Y 2.00 +opcodes["bit"] = 60 //BIT X,Y : CMPR = BIT(X,Y) 2.00 +opcodes["sbit"] = 61 //SBIT X,Y : BIT(X,Y) = 1 2.00 +opcodes["cbit"] = 62 //CBIT X,Y : BIT(X,Y) = 0 2.00 +opcodes["tbit"] = 63 //TBIT X,Y : BIT(X,Y) = ~BIT(X,Y) 2.00 +opcodes["band"] = 64 //BAND X,Y : X = X BAND Y 2.00 +opcodes["bor"] = 65 //BOR X,Y : X = X BOR Y 2.00 +opcodes["bxor"] = 66 //BXOR X,Y : X = X BXOR Y 2.00 +opcodes["bshl"] = 67 //BSHL X,Y : X = X BSHL Y 2.00 +opcodes["bshr"] = 68 //BSHR X,Y : X = X BSHR Y 2.00 +opcodes["jmpf"] = 69 //JMPF X,Y : CS = Y; IP = X 2.00 +opcodes["nmiint"] = 70 //NMIINT X : NMIINTERRUPT(X); 4.00 +opcodes["cne"] = 71 //CNE X : CALL(X), IF CMPR ~= 0 2.00 +opcodes["cnz"] = 71 //CNZ X : CALL(X), IF CMPR ~= 0 2.00 +opcodes["cg"] = 73 //CG X : CALL(X), IF CMPR > 0 2.00 +opcodes["cnle"] = 73 //CNLE X : CALL(X), IF !(CMPR <= 0) 2.00 +opcodes["cge"] = 74 //CGE X : CALL(X), IF CMPR >= 0 2.00 +opcodes["cnl"] = 74 //CNL X : CALL(X), IF !(CMPR < 0) 2.00 +opcodes["cl"] = 75 //CL X : CALL(X), IF CMPR < 0 2.00 +opcodes["cnge"] = 75 //CNGE X : CALL(X), IF !(CMPR >= 0) 2.00 +opcodes["cle"] = 76 //CLE X : CALL(X), IF CMPR <= 0 2.00 +opcodes["cng"] = 76 //CNG X : CALL(X), IF !(CMPR > 0) 2.00 +opcodes["ce"] = 77 //CE X : CALL(X), IF CMPR = 0 2.00 +opcodes["cz"] = 77 //CZ X : CALL(X), IF CMPR = 0 2.00 +opcodes["mcopy"] = 78 //MCOPY X : X BYTES(ESI) -> EDI 2.00 +opcodes["mxchg"] = 79 //MXCHG X : X BYTES(ESI) <> EDI 2.00 +opcodes["fpwr"] = 80 //FPWR X,Y : X = X ^ Y 2.00 +opcodes["xchg"] = 81 //XCHG X,Y : X,Y = Y,X 2.00 +opcodes["flog"] = 82 //FLOG X,Y : X = LOG(Y) 2.00 +opcodes["flog10"] = 83 //FLOG10 X,Y : X = LOG10(Y) 2.00 +opcodes["in"] = 84 //IN X,Y : X = PORT[Y] 2.00 +opcodes["out"] = 85 //OUT X,Y : PORT[X] = Y 2.00 +opcodes["fabs"] = 86 //FABS X,Y : X = ABS(Y) 2.00 +opcodes["fsgn"] = 87 //FSGN X,Y : X = SIGN(Y) 2.00 +opcodes["fexp"] = 88 //FEXP X,Y : X = EXP(Y) 2.00 +opcodes["callf"] = 89 //CALLF X,Y : CS = Y; CALL(X) 2.00 +opcodes["fpi"] = 90 //FPI X : X = PI 2.00 +opcodes["fe"] = 91 //FE X : X = E 2.00 +opcodes["int"] = 92 //INT X : INTERRUPT(X) 2.00 +opcodes["tpg"] = 93 //TPG X : CMPR = 1 IF PAGE READS, ELSE 0 2.00 +opcodes["fceil"] = 94 //FCEIL X : X = CEIL(X) 2.00 +opcodes["erpg"] = 95 //ERPG X : ERASE ROM PAGE(X) 2.00 +opcodes["wrpg"] = 96 //WRPG X : WRITE ROM PAGE(X) 2.00 +opcodes["rdpg"] = 97 //RDPG X : READ ROM PAGE(X) 2.00 +opcodes["timer"] = 98 //TIMER X : X = TIMER 2.00 +opcodes["lidtr"] = 99 //LIDTR X : IDTR = X 2.00 +opcodes["jner"] = 101 //JNER X : IP = IP+X, IF CMPR ~= 0 +opcodes["jnzr"] = 101 //JNZR X : IP = IP+X, IF CMPR ~= 0 +opcodes["jmpr"] = 102 //JMPR X : IP = IP+X +opcodes["jgr"] = 103 //JGR X : IP = IP+X, IF CMPR > 0 +opcodes["jnler"] = 103 //JNLER X : IP = IP+X, IF !(CMPR <= 0) +opcodes["jger"] = 104 //JGER X : IP = IP+X, IF CMPR >= 0 +opcodes["jnlr"] = 104 //JNLR X : IP = IP+X, IF !(CMPR < 0) +opcodes["jlr"] = 105 //JLR X : IP = IP+X, IF CMPR < 0 +opcodes["jnger"] = 105 //JNGER X : IP = IP+X, IF !(CMPR >= 0) +opcodes["jler"] = 106 //JLER X : IP = IP+X, IF CMPR <= 0 +opcodes["jngr"] = 106 //JNGR X : IP = IP+X, IF !(CMPR > 0) +opcodes["jer"] = 107 //JER X : IP = IP+X, IF CMPR = 0 +opcodes["jzr"] = 107 //JZR X : IP = IP+X, IF CMPR = 0 +opcodes["lneg"] = 108 //LNEG X : X = LOGic NEGATE(X) 3.00 +opcodes["nmiret"] = 110 //NMIRET : NMIRESTORE; 2.00 +opcodes["idle"] = 111 //IDLE : FORCE_CPU_IDLE; 4.00 +opcodes["nop"] = 112 //NOP : 5.00 +opcodes["cpuget"] = 120 //CPUGET X,Y : X = CPU[Y] 5.00 +opcodes["cpuset"] = 121 //CPUSET X,Y : CPU[X] = Y 5.00 +opcodes["spp"] = 122 //SPP X,Y : PAGE[X].Y = 1 5.00 [BLOCK] +opcodes["cpp"] = 123 //CPP X,Y : PAGE[X].Y = 0 5.00 [BLOCK] +opcodes["srl"] = 124 //SRL X,Y : PAGE[X].RunLevel = Y 5.00 [BLOCK] +opcodes["grl"] = 125 //GRL X,Y : X = PAGE[Y].RunLevel 5.00 +opcodes["lea"] = 126 //LEA X,Y : X = ADDRESS(Y) 5.00 +opcodes["block"] = 127 //BLOCK X,Y : SETUP_DATA_BLOCK([X..X+Y-1]) 6.00 +opcodes["cmpand"] = 128 //CMPAND X,Y : CMPR = CMPR AND (X - Y) 6.00 +opcodes["cmpor"] = 129 //CMPOR X,Y : CMPR = CMPR OR (X - Y) 6.00 +opcodes["mshift"] = 130 //MSHIFT X : SHIFT DATA (look in lua) 7.00 +opcodes["smap"] = 131 //SMAP X,Y : PAGE[X].MappedTo = Y 8.00 [BLOCK] +opcodes["gmap"] = 132 //GMAP X,Y : X = PAGE[Y].MappedTo 8.00 + +local gpuopcodes = {} +gpuopcodes["drect_test"] = 200 //DRECT_TEST : Draw retarded stuff +gpuopcodes["dexit"] = 201 //DEXIT : End current frame execution +gpuopcodes["dclr"] = 202 //DCLR : Clear screen color to black +gpuopcodes["dclrtex"] = 203 //DCLRTEX : Clear background with texture +gpuopcodes["dvxflush"] = 204 //DVXFLUSH : Flush current vertex buffer to screen +gpuopcodes["dvxclear"] = 205 //DVXCLEAR : Clear vertex buffer +gpuopcodes["derrorexit"] = 206 //DERROREXIT : Exit error handler +gpuopcodes["dsetbuf_spr"] = 207 //DSETBUF_SPR : Set frame buffer to sprite buffer +gpuopcodes["dsetbuf_fbo"] = 208 //DSETBUF_FBO : Set frame buffer to view buffer +gpuopcodes["dbindbuf_spr"] = 209 //DBINDBUF_SPR : Bind sprite buffer as texture +gpuopcodes["dvxpipe"] = 210 //DVXPIPE X : Vertex pipe = X [INT] +gpuopcodes["dcvxpipe"] = 211 //DCVXPIPE X : Coordinate vertex pipe = X [INT] +gpuopcodes["denable"] = 212 //DENABLE X : Enable parameter X [INT] +gpuopcodes["ddisable"] = 213 //DDISABLE X : Disable parameter X [INT] +gpuopcodes["dclrscr"] = 214 //DCLRSCR X : Clear screen with color X [COLOR] +gpuopcodes["dcolor"] = 215 //DCOLOR X : Set current color to X [COLOR] +gpuopcodes["dbindtexture"] = 216 //DBINDTEXTURE X : Bind texture [STRING] +gpuopcodes["dsetfont"] = 217 //DSETFONT X : Set current font to X [FONTID] +gpuopcodes["dsetsize"] = 218 //DSETSIZE X : Set font size to X [INT] +gpuopcodes["dmove"] = 219 //DMOVE X : Set offset position to X [2F] +gpuopcodes["dvxdata_2f"] = 220 //DVXDATA_2F X,Y : Draw solid 2d polygon (OFFSET,NUMVALUES) [2F,INT] +gpuopcodes["dvxpoly"] = 220 // +gpuopcodes["dvxdata_2f_tex"] = 221 //DVXDATA_2F_TEX X,Y : Draw textured 2d polygon (OFFSET,NUMVALUES) [2F+UV,INT] +gpuopcodes["dvxtexpoly"] = 221 // +gpuopcodes["dvxdata_3f"] = 222 //DVXDATA_3F X,Y : Draw solid 3d polygon (OFFSET,NUMVALUES) [3F,INT] +gpuopcodes["dvxdata_3f_tex"] = 223 //DVXDATA_3F_TEX X,Y : Draw textured 3d polygon (OFFSET,NUMVALUES) [3F+UV,INT] +gpuopcodes["dvxdata_wf"] = 224 //DVXDATA_WF X,Y : Draw wireframe 3d polygon (OFFSET,NUMVALUES) [3F,INT] +gpuopcodes["drect"] = 225 //DRECT X,Y : Draw rectangle (XY1,XY2) [2F,2F] +gpuopcodes["dcircle"] = 226 //DCIRCLE X,Y : Draw circle (XY,R) [2F,F] +gpuopcodes["dline"] = 227 //DLINE X,Y : Draw line (XY1,XY2) [2F,2F] +gpuopcodes["drectwh"] = 228 //DRECTWH X,Y : Draw rectangle (XY,WH) [2F,2F] +gpuopcodes["dtrectwh"] = 229 //DTRECTWH X,Y : Draw textured rectangle (XY1,XY2) [2F,2F] +gpuopcodes["dtransform2f"] = 230 //DTRANSFORM2F X,Y : Transform Y, save to X [2F,2F] +gpuopcodes["dtransform3f"] = 231 //DTRANSFORM3F X,Y : Transform Y, save to X [3F,3F] +gpuopcodes["dscrsize"] = 232 //DSCRSIZE X,Y : Set screen size [F,F] +gpuopcodes["drotatescale"] = 233 //DROTATESCALE X,Y : Rotate and scale +gpuopcodes["dorectwh"] = 234 //DORECTWH X,Y : Draw outlined rectangle (XY1,XY2) [2F,2F] +gpuopcodes["docircle"] = 235 //DOCIRCLE X,Y : Draw outlined circle (XY1,XY2) [2F,2F] +gpuopcodes["dwrite"] = 240 //DWRITE X,Y : Write Y to coordinates X [2F,STRING] +gpuopcodes["dwritei"] = 241 //DWRITEI X,Y : Write INT Y to coordinates X [2F,I] +gpuopcodes["dwritef"] = 242 //DWRITEF X,Y : Write 1F Y to coordinates X [2F,F] +gpuopcodes["dentrypoint"] = 243 //DENTRYPOINT X,Y : Set entry point X to address Y [INT,INT] +gpuopcodes["dsetlight"] = 244 //DSETLIGHT X,Y : Set light X to Y (Y points to [pos,color]) [INT,3F+COLOR] +gpuopcodes["dgetlight"] = 245 //DGETLIGHT X,Y : Get light Y to X (X points to [pos,color]) [INT,3F+COLOR] +gpuopcodes["dwritefmt"] = 246 //DWRITEFMT X,Y : Write formatted string Y to coordinates X [2F,STRING+PARAMS] +gpuopcodes["dwritefix"] = 247 //DWRITEFIX X,Y : Write fixed value Y to coordinates X [2F,F] +gpuopcodes["dhaschanged"] = 258 //DHASCHANGED X,Y : CMPR = HasChanged(Memory[X...Y]) [INT,INT] +gpuopcodes["dloopxy"] = 259 //DLOOPXY X,Y : IF DX>0 {IP=X;IF CX>0{CX--}ELSE{DX--;CX=Y}} [INT,INT] +gpuopcodes["mload"] = 271 //MLOAD X : Load matrix X into view matrix [MATRIX] +gpuopcodes["mread"] = 272 //MREAD X : Write view matrix into matrix X [MATRIX] +gpuopcodes["dt"] = 274 //DT X : X -> Frame DeltaTime [F] +gpuopcodes["dstrprecache"] = 275 //DSTRPRECACHE X : Read and cache string [STRING] +gpuopcodes["dshade"] = 276 //DSHADE X : COLOR = COLOR * X [F] +gpuopcodes["dsetwidth"] = 277 //DSETWIDTH X : LINEWIDTH = X [F] +gpuopcodes["ddframe"] = 280 //DDFRAME X : Draw bordered frame [BORDER_STRUCT] +gpuopcodes["ddbar"] = 281 //DDBAR X : Draw progress bar [BAR_STRUCT] +gpuopcodes["ddgauge"] = 282 //DDGAUGE X : Draw gauge needle [GAUGE_STRUCT] +gpuopcodes["dspritesize"] = 290 //DSPRITESIZE X,Y : Set sprite size in X,Y [INT,INT] +gpuopcodes["dtosprite"] = 291 //DTOSPRITE X,Y : Copy region Y to sprite X [INT,4F] +gpuopcodes["dfromsprite"] = 292 //DFROMSPRITE X,Y : Copy sprite Y to region X [4F,INT] +gpuopcodes["dsprite"] = 293 //DSPRITE X,Y : Draw sprite Y to position X [2F,INT] +gpuopcodes["dmuldt"] = 294 //DMULDT X,Y : X = Y * dT [2F,2F] +gpuopcodes["drotate"] = 300 //DROTATE X : Rotate(X) [4F] +gpuopcodes["dtranslate"] = 301 //DTRANSLATE X : Translate(X) [4F] +gpuopcodes["dscale"] = 302 //DSCALE X : Scale(X) [4F] + +local registers = {} +registers["eax"] = 0 +registers["ebx"] = 1 +registers["ecx"] = 2 +registers["edx"] = 3 +registers["esi"] = 4 +registers["edi"] = 5 +registers["esp"] = 6 +registers["ebp"] = 7 +registers["cs"] = 8 +registers["ss"] = 9 +registers["ds"] = 10 +registers["es"] = 11 +registers["gs"] = 12 +registers["fs"] = 13 +registers["ks"] = 14 +registers["ls"] = 15 + +WireLib.CPU = { + opcodes = opcodes, + gpuopcodes = gpuopcodes, + registers = registers, +} diff --git a/lua/wire/server/ModelPlug.lua b/lua/wire/server/ModelPlug.lua new file mode 100644 index 0000000000..4f349ee104 --- /dev/null +++ b/lua/wire/server/ModelPlug.lua @@ -0,0 +1,44 @@ +-- $Rev: 1303 $ +-- $LastChangedDate: 2009-07-08 19:10:33 -0700 (Wed, 08 Jul 2009) $ +-- $LastChangedBy: tad2020 $ + +ModelPlugInfo = {} + +--uncomment line 15 and line 26-34 to enable sending model packs to clients + +function ModelPlug_Register(category) + if (not ModelPlugInfo[category]) then + local catinfo = {} + + local packs = file.Find("WireModelPacks/*.txt") + for _,filename in pairs(packs) do + --resource.AddFile("data/WireModelPacks/" .. filename) + + local packtbl = util.KeyValuesToTable(file.Read("WireModelPacks/" .. filename) or {}) + + for name,entry in pairs(packtbl) do + local categorytable = string.Explode(",", entry.categories or "none") or { "none" } + + for _,cat in pairs(categorytable) do + if (cat == category) then + catinfo[name] = entry.model or "" + + --[[if (entry.model) then + resource.AddFile(entry.model) + end + + if (entry.files) then + for _,extrafilename in pairs(entry.files) do + resource.AddFile(extrafilename) + end + end]] + + break + end + end + end + end + + ModelPlugInfo[category] = catinfo + end +end diff --git a/lua/wire/server/WireLib.lua b/lua/wire/server/WireLib.lua new file mode 100644 index 0000000000..619a6703d9 --- /dev/null +++ b/lua/wire/server/WireLib.lua @@ -0,0 +1,1076 @@ +-- $Rev: 1753 $ +-- $LastChangedDate: 2009-09-29 18:34:43 -0700 (Tue, 29 Sep 2009) $ +-- $LastChangedBy: TomyLobo $ + +-- Compatibility Global +WireAddon = 1 + + +function WireLib.PortComparator(a,b) + return a.Idx < b.Idx +end + + +local Inputs = {} +local Outputs = {} +local CurLink = {} +local PathQueue = {} + +hook.Add("Think", "WireLib_Think", function() + for idx, output in pairs(Outputs) do + output.TriggerLimit = 4 + end +end) + +-- helper function that pcalls an input +function WireLib.TriggerInput(ent, name, value, ...) + ent.Inputs[name].Value = value + if not ent.TriggerInput then return end + local ok, ret = pcall(ent.TriggerInput, ent, name, value, ...) + if not ok then + local message = string.format("Wire error (%s): %s", tostring(ent), ret) + ErrorNoHalt(message .. "\n") + local ply = E2Lib and E2Lib.getOwner and E2Lib.getOwner(ent) + if ValidEntity(ply) then WireLib.ClientError(message, ply) end + end +end + +function Wire_CreateInputs(ent, names) + local ent_Inputs = {} + for n,v in pairs(names) do + local input = { + Entity = ent, + Name = v, + Value = 0, + Type = "NORMAL", + Material = "tripmine_laser", + Color = Color(255, 255, 255, 255), + Width = 1, + Num = n, + } + + local idx = 1 + while (Inputs[idx]) do + idx = idx+1 + end + input.Idx = idx + + ent_Inputs[v] = input + Inputs[idx] = input + end + + Wire_SetPathNames(ent, names) + + return ent_Inputs +end + + +function Wire_CreateOutputs(ent, names, desc) + local ent_Outputs = {} + for n,v in pairs(names) do + local output = { + Entity = ent, + Name = v, + Value = 0, + Type = "NORMAL", + Connected = {}, + TriggerLimit = 8, + Num = n, + } + + if (desc) and (desc[n]) then + output.Desc = desc[n] + end + + local idx = 1 + while (Outputs[idx]) do + idx = idx+1 + end + output.Idx = idx + + ent_Outputs[v] = output + Outputs[idx] = output + end + + return ent_Outputs +end + + +function Wire_AdjustInputs(ent, names) + local ent_Inputs = ent.Inputs + for n,v in pairs(names) do + if (ent_Inputs[v]) then + ent_Inputs[v].Keep = true + ent_Inputs[v].Num = n + else + local input = { + Entity = ent, + Name = v, + Value = 0, + Type = "NORMAL", + Material = "tripmine_laser", + Color = Color(255, 255, 255, 255), + Width = 1, + Keep = true, + Num = n, + } + + local idx = 1 + while (Inputs[idx]) do + idx = idx+1 + end + input.Idx = idx + + ent_Inputs[v] = input + Inputs[idx] = input + end + end + + for k,v in pairs(ent_Inputs) do + if (v.Keep) then + v.Keep = nil + else + Wire_Link_Clear(ent, k) + + ent_Inputs[k] = nil + end + end + + Wire_SetPathNames(ent, names) +end + + +function Wire_AdjustOutputs(ent, names, desc) + local ent_Outputs = ent.Outputs + for n,v in pairs(names) do + if (ent_Outputs[v]) then + ent_Outputs[v].Keep = true + ent_Outputs[v].Num = n + if (desc) and (desc[n]) then + ent_Outputs[v].Desc = desc[n] + end + else + local output = { + Keep = true, + Name = v, + Value = 0, + Type = "NORMAL", + Connected = {}, + TriggerLimit = 8, + Num = n, + } + + if (desc) and (desc[n]) then + output.Desc = desc[n] + end + + local idx = 1 + while (Outputs[idx]) do + idx = idx+1 + end + output.Idx = idx + + ent_Outputs[v] = output + Outputs[idx] = output + end + end + + for k,v in pairs(ent_Outputs) do + if (v.Keep) then + v.Keep = nil + else + -- fix by Syranide: unlinks wires of removed outputs + for i,v in ipairs(ent_Outputs[k].Connected) do + if (v.Entity:IsValid()) then + Wire_Link_Clear(v.Entity, v.Name) + end + end + ent_Outputs[k] = nil + end + end +end + + +-- and array of data types +WireLib.DT = { + NORMAL = { + Zero = 0 + }, -- Numbers + VECTOR = { + Zero = Vector(0, 0, 0) + }, + ANGLE = { + Zero = Angle(0, 0, 0) + }, + COLOR = { + Zero = Color(0, 0, 0) + }, + ENTITY = { + Zero = NULL + }, + STRING = { + Zero = "" + }, + TABLE = { + Zero = {} + }, + BIDIRTABLE = { + Zero = {}, + BiDir = true + }, + ANY = { + Zero = 0 + }, + HOVERDATAPORT = { + Zero = 0 + }, + ARRAY = { + Zero = {} + }, + BIDIRARRAY = { + Zero = {}, + BiDir = true + }, +} + +function WireLib.CreateSpecialInputs(ent, names, types, desc) + types = types or {} + desc = desc or {} + local ent_Inputs = {} + for n,v in pairs(names) do + local input = { + Entity = ent, + Name = v, + Desc = desc[n], + Type = types[n] or "NORMAL", + Value = WireLib.DT[ (types[n] or "NORMAL") ].Zero, + Material = "tripmine_laser", + Color = Color(255, 255, 255, 255), + Width = 1, + Num = n, + } + + local idx = 1 + while (Inputs[idx]) do + idx = idx+1 + end + input.Idx = idx + + ent_Inputs[v] = input + Inputs[idx] = input + end + + WireLib.SetPathNames(ent, names) + + return ent_Inputs +end + + +function WireLib.CreateSpecialOutputs(ent, names, types, desc) + types = types or {} + desc = desc or {} + local ent_Outputs = {} + for n,v in pairs(names) do + local output = { + Entity = ent, + Name = v, + Desc = desc[n], + Type = types[n] or "NORMAL", + Value = WireLib.DT[ (types[n] or "NORMAL") ].Zero, + Connected = {}, + TriggerLimit = 8, + Num = n, + } + + local idx = 1 + while (Outputs[idx]) do + idx = idx+1 + end + output.Idx = idx + + ent_Outputs[v] = output + Outputs[idx] = output + end + + return ent_Outputs +end + + + +function WireLib.AdjustSpecialInputs(ent, names, types, desc) + types = types or {} + desc = desc or {} + local ent_Inputs = ent.Inputs + for n,v in pairs(names) do + if (ent_Inputs[v]) then + local newtype = types[n] or "NORMAL" + if newtype ~= ent_Inputs[v].Type then + timer.Simple(0, Wire_Link_Clear, ent, v) -- TODO: Think of a non-triggering way to clear a link. But delayed triggering will do for now. + ent_Inputs[v].Value = WireLib.DT[newtype].Zero + end + ent_Inputs[v].Keep = true + ent_Inputs[v].Num = n + ent_Inputs[v].Desc = desc[n] + ent_Inputs[v].Type = newtype + else + local input = { + Entity = ent, + Name = v, + Desc = desc[n], + Type = types[n] or "NORMAL", + Value = WireLib.DT[ (types[n] or "NORMAL") ].Zero, + Material = "tripmine_laser", + Color = Color(255, 255, 255, 255), + Width = 1, + Keep = true, + Num = n, + } + + local idx = 1 + while (Inputs[idx]) do + idx = idx+1 + end + input.Idx = idx + + ent_Inputs[v] = input + Inputs[idx] = input + end + end + + for k,v in pairs(ent_Inputs) do + if (v.Keep) then + v.Keep = nil + else + Wire_Link_Clear(ent, k) + + ent_Inputs[k] = nil + end + end + + WireLib.SetPathNames(ent, names) + + return ent_Inputs +end + + +function WireLib.AdjustSpecialOutputs(ent, names, types, desc) + types = types or {} + desc = desc or {} + local ent_Outputs = ent.Outputs + for n,v in pairs(names) do + if (ent_Outputs[v]) then + local newtype = types[n] or "NORMAL" + if newtype ~= ent_Outputs[v].Type then + for i,inp in ipairs(ent_Outputs[v].Connected) do + if (inp.Entity:IsValid()) then + Wire_Link_Clear(inp.Entity, inp.Name) + end + end + end + ent_Outputs[v].Keep = true + ent_Outputs[v].Num = n + ent_Outputs[v].Desc = desc[n] + ent_Outputs[v].Type = newtype + else + local output = { + Keep = true, + Name = v, + Desc = desc[n], + Type = types[n] or "NORMAL", + Value = WireLib.DT[ (types[n] or "NORMAL") ].Zero, + Connected = {}, + TriggerLimit = 8, + Num = n, + } + + local idx = 1 + while (Outputs[idx]) do + idx = idx+1 + end + output.Idx = idx + + ent_Outputs[v] = output + Outputs[idx] = output + end + end + + for k,v in pairs(ent_Outputs) do + if (v.Keep) then + v.Keep = nil + else + -- fix by Syranide: unlinks wires of removed outputs + for i,v in ipairs(ent_Outputs[k].Connected) do + if (v.Entity:IsValid()) then + Wire_Link_Clear(v.Entity, v.Name) + end + end + ent_Outputs[k] = nil + end + end + + return ent_Outputs +end + + +function WireLib.RetypeInputs(ent, iname, itype, desc) + local ent_Inputs = ent.Inputs + if (!ent_Inputs[iname]) or (!itype) then return end + ent_Inputs[iname].Desc = desc + ent_Inputs[iname].Type = itype + ent_Inputs[iname].Value = WireLib.DT[itype].Zero +end + + +function WireLib.RetypeOutputs(ent, oname, otype, desc) + local ent_Outputs = ent.Outputs + if (!ent_Outputs[oname]) or (!otype) then return end + ent_Outputs[oname].Desc = desc + ent_Outputs[oname].Type = otype + ent_Outputs[oname].Value = WireLib.DT[otype].Zero +end + + +-- force_outputs is only needed for existing components to allow them to be updated +function Wire_Restored(ent, force_outputs) + local ent_Inputs = ent.Inputs + if (ent_Inputs) then + for name,input in pairs(ent_Inputs) do + if (not input.Material) then -- Must be an old save + input.Name = name + + if (input.Ropes) then + for _,rope in pairs(input.Ropes) do + rope:Remove() + end + input.Ropes = nil + end + end + + input.Entity = ent + input.Type = input.Type or "NORMAL" + input.Material = input.Material or "cable/blue_elec" + input.Color = input.Color or Color(255, 255, 255, 255) + input.Width = input.Width or 2 + input.StartPos = input.StartPos or Vector(0, 0, 0) + if (input.Src) and (not input.Path) then + input.Path = { { Entity = input.Src, Pos = Vector(0, 0, 0) } } + end + + local idx = 1 + while (Inputs[idx]) do + idx = idx+1 + end + input.Idx = idx + + Inputs[idx] = input + end + end + + local ent_Outputs = ent.Outputs + if (ent_Outputs) then + for _,output in pairs(ent_Outputs) do + output.Entity = ent + output.Type = output.Type or "NORMAL" + + local idx = 1 + while (Outputs[idx]) do + idx = idx+1 + end + output.Idx = idx + + Outputs[idx] = output + end + elseif (force_outputs) then + ent.Outputs = Wire_CreateOutputs(ent, force_outputs) + end +end + + +function Wire_Remove(ent) + local ent_Inputs = ent.Inputs + if (ent_Inputs) then + for _,input in pairs(ent_Inputs) do + if (input.Src) and (input.Src:IsValid()) then + local output = input.Src.Outputs[input.SrcId] + if (output) then + for k,v in ipairs(output.Connected) do + if (v.Entity == dst) and (v.Name == dstid) then + table.remove(output.Connected, k) + break + end + end + end + end + + Inputs[input.Idx] = nil + end + end + + local ent_Outputs = ent.Outputs + if (ent_Outputs) then + for _,output in pairs(ent_Outputs) do + for _,v in ipairs(output.Connected) do + if (v.Entity:IsValid()) then + local input = v.Entity.Inputs[v.Name] + local zero = WireLib.DT[input.Type].Zero + + WireLib.TriggerInput(v.Entity, v.Name, zero) + -- disable for beamlib + Wire_Link_Clear(v.Entity, v.Name) + end + end + + Outputs[output.Idx] = nil + end + end +end + + +local function Wire_Link(dst, dstid, src, srcid, path) + if (not dst) or (not dst.Inputs) or (not dst.Inputs[dstid]) then + Msg("Wire_link: Invalid destination!\n") + return + end + if (not src) or (not src.Outputs) or (not src.Outputs[srcid]) then + Msg("Wire_link: Invalid source!\n") + return + end + + local input = dst.Inputs[dstid] + local output = src.Outputs[srcid] + + if (input.Src) and (input.Src:IsValid()) then + if (input.Src.Outputs) then + local oldOutput = input.Src.Outputs[input.SrcId] + if (oldOutput) then + for k,v in ipairs(oldOutput.Connected) do + if (v.Entity == dst) and (v.Name == dstid) then + table.remove(oldOutput.Connected, k) + end + end + end + end + end + + input.Src = src + input.SrcId = srcid + input.Path = path + + table.insert(output.Connected, { Entity = dst, Name = dstid }) + + if dst.OnInputWireLink then + -- ENT:OnInputWireLink(iName, iType, oEnt, oName, oType) + dst:OnInputWireLink(dstid, input.Type, src, srcid, output.Type) + end + + if src.OnOutputWireLink then + -- ENT:OnOutputWireLink(oName, oType, iEnt, iName, iType) + src:OnOutputWireLink(srcid, output.Type, dst, dstid, input.Type) + end + + WireLib.TriggerInput(dst, dstid, output.Value) +end + +function Wire_TriggerOutput(ent, oname, value, iter) + if (not ent) or (not ent:IsValid()) or (not ent.Outputs) or (not ent.Outputs[oname]) then return end + + local output = ent.Outputs[oname] + if (output) and (value ~= output.Value or output.Type == "ARRAY" or output.Type == "TABLE") then + if (output.TriggerLimit <= 0) then return end + output.TriggerLimit = output.TriggerLimit - 1 + + output.Value = value + + if (iter) then + for _,dst in ipairs(output.Connected) do + if (dst.Entity:IsValid()) then + iter:Add(dst.Entity, dst.Name, value) + end + end + return + end + + iter = Wire_CreateOutputIterator() + + for _,dst in ipairs(output.Connected) do + if (dst.Entity:IsValid()) then + WireLib.TriggerInput(dst.Entity, dst.Name, value, iter) + end + end + + iter:Process() + end +end + +local function Wire_Unlink(ent, iname) + local input = ent.Inputs[iname] + if (input) then + if (input.Src) and (input.Src:IsValid()) then + local output = input.Src.Outputs[input.SrcId] + if (output) then + for k,v in ipairs(output.Connected) do + if (v.Entity == ent) and (v.Name == iname) then + table.remove(output.Connected, k) + end + end + -- untested + if input.Src.OnOutputWireLink then + -- ENT:OnOutputWireLink(oName, oType, iEnt, iName, iType) + input.Src:OnOutputWireLink(input.SrcId, input.Src.Outputs[input.SrcId].Type, ent, iname, input.Type) + end + end + -- untested + if ent.OnInputWireUnlink then + -- ENT:OnInputWireUnlink(iName, iType, oEnt, oName, oType) + ent:OnInputWireUnlink(iname, input.Type, input.Src, input.SrcId, input.Src.Outputs[input.SrcId].Type) + end + end + + WireLib.TriggerInput(ent, iname, WireLib.DT[input.Type].Zero) + + input.Src = nil + input.SrcId = nil + end +end + +function Wire_Link_Start(idx, ent, pos, iname, material, color, width) + if (not ent) or (not ent:IsValid()) or (not ent.Inputs) or (not ent.Inputs[iname]) then return end + + local input = ent.Inputs[iname] + + CurLink[idx] = { + Dst = ent, + DstId = iname, + Path = {}, + OldPath = input.Path, + } + + CurLink[idx].OldPath = CurLink[idx].OldPath or {} + CurLink[idx].OldPath[0] = {} + CurLink[idx].OldPath[0].pos = input.StartPos + CurLink[idx].OldPath[0].material = input.Material + CurLink[idx].OldPath[0].color = input.Color + CurLink[idx].OldPath[0].width = input.Width + + local net_name = "wp_" .. iname + ent:SetNetworkedBeamInt(net_name, 0) + ent:SetNetworkedBeamVector(net_name .. "_start", pos) + ent:SetNetworkedBeamString(net_name .. "_mat", material) + ent:SetNetworkedBeamVector(net_name .. "_col", Vector(color.r, color.g, color.b)) + ent:SetNetworkedBeamFloat(net_name .. "_width", width) + + --RDbeamlib.StartWireBeam( ent, iname, pos, material, color, width ) + + input.StartPos = pos + input.Material = material + input.Color = color + input.Width = width + + return true +end + + +function Wire_Link_Node(idx, ent, pos) + if (not CurLink[idx]) or (not CurLink[idx].Dst) then return end + if (!ent:IsValid()) then return end -- its the world, give up + + local net_name = "wp_" .. CurLink[idx].DstId + local node_idx = CurLink[idx].Dst:GetNetworkedBeamInt(net_name)+1 + CurLink[idx].Dst:SetNetworkedBeamEntity(net_name .. "_" .. node_idx .. "_ent", ent) + CurLink[idx].Dst:SetNetworkedBeamVector(net_name .. "_" .. node_idx .. "_pos", pos) + CurLink[idx].Dst:SetNetworkedBeamInt(net_name, node_idx) + + --RDbeamlib.AddWireBeamNode( CurLink[idx].Dst, CurLink[idx].DstId, ent, pos ) + + table.insert(CurLink[idx].Path, { Entity = ent, Pos = pos }) +end + + +function Wire_Link_End(idx, ent, pos, oname, pl) + if (not CurLink[idx]) or (not CurLink[idx].Dst) or (not ent.Outputs) then return end + + if (CurLink[idx].Dst:GetClass() == "gmod_wire_sensor") and (ent:GetClass() != "gmod_wire_target_finder") then + Msg("Wire_link: Beacon Sensor can only be wired to a Target Finder!\n") + if pl then + WireLib.AddNotify(pl, "Beacon Sensor can only be wired to a Target Finder!", NOTIFY_GENERIC, 7) + end + Wire_Link_Cancel(idx) + return + end + + local input = CurLink[idx].Dst.Inputs[CurLink[idx].DstId] + local output = ent.Outputs[oname] or {} + --Msg("input type= " .. input.Type .. " output type= " .. (output.Type or "NIL") .. "\n") -- I bet that was getting anoying (TAD2020) + output.Type = output.Type or "NORMAL" + if (input.Type != output.Type) and (input.Type != "ANY") and (output.Type != "ANY") then + local txt = "Data Type Mismatch! Input takes "..input.Type.." and Output gives "..output.Type + Msg(txt.."\n") + if pl then + WireLib.AddNotify(pl, txt, NOTIFY_GENERIC, 7) + end + Wire_Link_Cancel(idx) + return + end + + local net_name = "wp_" .. CurLink[idx].DstId + local node_idx = CurLink[idx].Dst:GetNetworkedBeamInt(net_name)+1 + CurLink[idx].Dst:SetNetworkedBeamEntity(net_name .. "_" .. node_idx .. "_ent", ent) + CurLink[idx].Dst:SetNetworkedBeamVector(net_name .. "_" .. node_idx .. "_pos", pos) + CurLink[idx].Dst:SetNetworkedBeamInt(net_name, node_idx) + + --RDbeamlib.AddWireBeamNode( CurLink[idx].Dst, CurLink[idx].DstId, ent, pos ) + + table.insert(CurLink[idx].Path, { Entity = ent, Pos = pos }) + + Wire_Link(CurLink[idx].Dst, CurLink[idx].DstId, ent, oname, CurLink[idx].Path) + + if (WireLib.DT[input.Type].BiDir) then + Wire_Link(ent, oname, CurLink[idx].Dst, CurLink[idx].DstId, {}) + end + + CurLink[idx] = nil +end + + +function Wire_Link_Cancel(idx) + if (not CurLink[idx]) or (not CurLink[idx].Dst) then return end + + --local orig = CurLink[idx].OldPath[0] + --RDbeamlib.StartWireBeam( CurLink[idx].Dst, CurLink[idx].DstId, orig.pos, orig.material, orig.color, orig.width ) + + local path_len = 0 + if (CurLink[idx].OldPath) then path_len = table.getn(CurLink[idx].OldPath) end + + local net_name = "wp_" .. CurLink[idx].DstId + for i=1,path_len do + CurLink[idx].Dst:SetNetworkedBeamEntity(net_name .. "_" .. i, CurLink[idx].OldPath[i].Entity) + CurLink[idx].Dst:SetNetworkedBeamVector(net_name .. "_" .. i, CurLink[idx].OldPath[i].Pos) + --RDbeamlib.AddWireBeamNode( CurLink[idx].Dst, CurLink[idx].DstId, CurLink[idx].OldPath[i].Entity, CurLink[idx].OldPath[i].Pos ) + end + CurLink[idx].Dst:SetNetworkedBeamInt(net_name, path_len) + + CurLink[idx] = nil +end + + +function Wire_Link_Clear(ent, iname) + local net_name = "wp_" .. iname + ent:SetNetworkedBeamInt(net_name, 0) + --RDbeamlib.ClearWireBeam( ent, iname ) + + Wire_Unlink(ent, iname) +end + +function Wire_SetPathNames(ent, names) + for k,v in pairs(names) do + ent:SetNetworkedBeamString("wpn_" .. k, v) + end + ent:SetNetworkedBeamInt("wpn_count", table.getn(names)) +end + + +function Wire_CreateOutputIterator() + local iter = { + Queue = {} + } + + function iter:Add(ent, iname, value) + table.insert(self.Queue, { Entity = ent, IName = iname, Value = value }) + end + + function iter:Process() + if (self.Processing) then return end + self.Processing = true + + while (table.getn(self.Queue) > 0) do + local next = self.Queue[1] + table.remove(self.Queue, 1) + + WireLib.TriggerInput(next.Entity, next.IName, next.Value, self) + end + + self.Processing = nil + end + + return iter +end + + +function Wire_AfterPasteMods(ply, Ent, DupeInfo) + -- this does nothing for now, we need the blank function to get the duplicator to copy the WireDupeInfo into the pasted ent +end +duplicator.RegisterEntityModifier( "WireDupeInfo", Wire_AfterPasteMods ) + + +-- used for welding wired stuff, if trace is world, the ent is not welded and is frozen instead +function WireLib.Weld(ent, traceEntity, tracePhysicsBone, DOR, collision, AllowWorldWeld) + if (!ent or !traceEntity or traceEntity:IsNPC() or traceEntity:IsPlayer()) then return end + local phys = ent:GetPhysicsObject() + if ( traceEntity:IsValid() ) or ( traceEntity:IsWorld() and AllowWorldWeld ) then + local const = constraint.Weld( ent, traceEntity, 0, tracePhysicsBone, 0, (not collision), DOR ) + -- Don't disable collision if it's not attached to anything + if (!collision) then + if phys:IsValid() then phys:EnableCollisions( false ) end + ent.nocollide = true + end + return const + else + if phys:IsValid() then ent:GetPhysicsObject():EnableMotion( false ) end + return nil + end +end + + +function WireLib.BuildDupeInfo( Ent ) + if (not Ent.Inputs) then return end + + local info = { Wires = {} } + for k,input in pairs(Ent.Inputs) do + if (input.Src) and (input.Src:IsValid()) then + info.Wires[k] = { + StartPos = input.StartPos, + Material = input.Material, + Color = input.Color, + Width = input.Width, + Src = input.Src:EntIndex(), + SrcId = input.SrcId, + SrcPos = Vector(0, 0, 0), + } + + if (input.Path) then + info.Wires[k].Path = {} + + for _,v in ipairs(input.Path) do + if (v.Entity) and (v.Entity:IsValid()) then + table.insert(info.Wires[k].Path, { Entity = v.Entity:EntIndex(), Pos = v.Pos }) + end + end + + local n = table.getn(info.Wires[k].Path) + if (n > 0) and (info.Wires[k].Path[n].Entity == info.Wires[k].Src) then + info.Wires[k].SrcPos = info.Wires[k].Path[n].Pos + table.remove(info.Wires[k].Path, n) + end + end + end + end + + return info +end + +function WireLib.ApplyDupeInfo( ply, ent, info, GetEntByID ) + if (info.Wires) then + for k,input in pairs(info.Wires) do + + Wire_Link_Start(ply:UniqueID(), ent, input.StartPos, k, input.Material, input.Color, input.Width) + + if (input.Path) then + for _,v in ipairs(input.Path) do + + local ent2 = GetEntByID(v.Entity) + if (!ent2) or (!ent2:IsValid()) then + local EntityList = GetEntByID("EntityList") + if (!EntityList) or (!EntityList[v.Entity]) then + ent2 = ents.GetByIndex(v.Entity) + end + end + if (ent2) or (ent2:IsValid()) then + Wire_Link_Node(ply:UniqueID(), ent2, v.Pos) + else + Msg("ApplyDupeInfo: Error, Could not find the entity for wire path\n") + end + end + end + + local ent2 = GetEntByID(input.Src) + if (!ent2) or (!ent2:IsValid()) then + local EntityList = GetEntByID("EntityList") + if (!EntityList) or (!EntityList[input.Src]) then + ent2 = ents.GetByIndex(input.Src) + end + end + if (ent2) or (ent2:IsValid()) then + Wire_Link_End(ply:UniqueID(), ent2, input.SrcPos, input.SrcId) + else + Msg("ApplyDupeInfo: Error, Could not find the output entity\n") + end + end + end +end + + + +WireLib.CreateInputs = Wire_CreateInputs +WireLib.CreateOutputs = Wire_CreateOutputs +WireLib.AdjustInputs = Wire_AdjustInputs +WireLib.AdjustOutputs = Wire_AdjustOutputs +WireLib.Restored = Wire_Restored +WireLib.Remove = Wire_Remove +WireLib.TriggerOutput = Wire_TriggerOutput +WireLib.Link_Start = Wire_Link_Start +WireLib.Link_Node = Wire_Link_Node +WireLib.Link_End = Wire_Link_End +WireLib.Link_Cancel = Wire_Link_Cancel +WireLib.Link_Clear = Wire_Link_Clear +WireLib.SetPathNames = Wire_SetPathNames +WireLib.CreateOutputIterator = Wire_CreateOutputIterator +WireLib.AfterPasteMods = Wire_AfterPasteMods +Wire_BuildDupeInfo = WireLib.BuildDupeInfo +Wire_ApplyDupeInfo = WireLib.ApplyDupeInfo + +--backwards logic: set enable to false to show show values on gates instead +Wire_EnableGateInputValues = true +local function WireEnableInputValues(pl, cmd, args) + if ( args[1] ) and ( ( pl:IsAdmin() ) or ( pl:IsSuperAdmin( )() ) ) then + if args[1] == "1" or args[1] == 1 then + Wire_EnableGateInputValues = true + elseif args[1] == "0" or args[1] == 0 then + Wire_EnableGateInputValues = false + else + pl:PrintMessage(HUD_PRINTCONSOLE, "Only takes 0 or 1") + end + end + pl:PrintMessage(HUD_PRINTCONSOLE, "\nWire_EnableGateInputValues = "..tostring(Wire_EnableGateInputValues).."\n") +end +concommand.Add( "Wire_EnableGateInputValues", WireEnableInputValues ) + +Wire_FastOverlayTextUpdate = false +local function WireFastOverlayTextUpdate(pl, cmd, args) + if ( args[1] ) and ( ( pl:IsAdmin() ) or ( pl:IsSuperAdmin( )() ) ) then + if args[1] == "1" or args[1] == 1 then + Wire_FastOverlayTextUpdate = true + elseif args[1] == "0" or args[1] == 0 then + Wire_FastOverlayTextUpdate = false + else + pl:PrintMessage(HUD_PRINTCONSOLE, "Only takes 0 or 1") + end + end + pl:PrintMessage(HUD_PRINTCONSOLE, "\nWire_FastOverlayTextUpdate = "..tostring(Wire_FastOverlayTextUpdate).."\n") +end +concommand.Add( "Wire_FastOverlayTextUpdate", WireFastOverlayTextUpdate ) + +Wire_SlowerOverlayTextUpdate = false +local function WireSlowerOverlayTextUpdate(pl, cmd, args) + if ( args[1] ) and ( ( pl:IsAdmin() ) or ( pl:IsSuperAdmin( )() ) ) then + if args[1] == "1" or args[1] == 1 then + Wire_SlowerOverlayTextUpdate = true + elseif args[1] == "0" or args[1] == 0 then + Wire_SlowerOverlayTextUpdate = false + else + pl:PrintMessage(HUD_PRINTCONSOLE, "Only takes 0 or 1") + end + end + pl:PrintMessage(HUD_PRINTCONSOLE, "\nWire_SlowerOverlayTextUpdate = "..tostring(Wire_SlowerOverlayTextUpdate).."\n") +end +concommand.Add( "Wire_SlowerOverlayTextUpdate", WireSlowerOverlayTextUpdate ) + +Wire_DisableOverlayTextUpdate = false +local function WireDisableOverlayTextUpdate(pl, cmd, args) + if ( args[1] ) and ( ( pl:IsAdmin() ) or ( pl:IsSuperAdmin( )() ) ) then + if args[1] == "1" or args[1] == 1 then + Wire_DisableOverlayTextUpdate = true + elseif args[1] == "0" or args[1] == 0 then + Wire_DisableOverlayTextUpdate = false + else + pl:PrintMessage(HUD_PRINTCONSOLE, "Only takes 0 or 1") + end + end + pl:PrintMessage(HUD_PRINTCONSOLE, "\nWire_DisableOverlayTextUpdate = "..tostring(Wire_DisableOverlayTextUpdate).."\n") +end +concommand.Add( "Wire_DisableOverlayTextUpdate", WireDisableOverlayTextUpdate ) + +Wire_ForceDelayOverlayTextUpdate = false +local function WireForceDelayOverlayTextUpdate(pl, cmd, args) + if ( args[1] ) and ( ( pl:IsAdmin() ) or ( pl:IsSuperAdmin( )() ) ) then + if args[1] == "1" or args[1] == 1 then + Wire_ForceDelayOverlayTextUpdate = true + elseif args[1] == "0" or args[1] == 0 then + Wire_ForceDelayOverlayTextUpdate = false + else + pl:PrintMessage(HUD_PRINTCONSOLE, "Only takes 0 or 1") + end + end + pl:PrintMessage(HUD_PRINTCONSOLE, "\nWire_ForceDelayOverlayTextUpdate = "..tostring(Wire_ForceDelayOverlayTextUpdate).."\n") +end +concommand.Add( "Wire_ForceDelayOverlayTextUpdate", WireForceDelayOverlayTextUpdate ) + + +--[[Wire_UseOldGateOutputLables = false +local function WireUseOldGateOutputLables(pl, cmd, args) + if ( args[1] ) and ( ( pl:IsAdmin() ) or ( pl:IsSuperAdmin( )() ) ) then + if args[1] == "1" or args[1] == 1 then + Wire_UseOldGateOutputLables = true + elseif args[1] == "0" or args[1] == 0 then + Wire_UseOldGateOutputLables = false + else + pl:PrintMessage(HUD_PRINTCONSOLE, "Only takes 0 or 1") + end + end + pl:PrintMessage(HUD_PRINTCONSOLE, "\nWire_UseOldGateOutputLables = "..tostring(Wire_UseOldGateOutputLables).."\n") +end +concommand.Add( "Wire_UseOldGateOutputLables", WireUseOldGateOutputLables )]] + + + +-- add wiresvn tag + +-- add wiresvn_rev tag (doesn't work like it should) +--RunConsoleCommand("sv_tags", (GetConVarString("sv_tags") or "")..",wiresvn"..WireVersion) + +-- this still doesn't quiet work like it looks like it should, must be some issues with setting sv_tags (long tags, similar tags might be ignored/removed while duplicates might get though) + +local tags = string.Explode(",", GetConVarString("sv_tags") or "") +-- remove old tags +for i,tag in ipairs(tags) do + if tag:find("wiresvn") then table.remove(tags,i) end +end + +-- insert new ones +table.insert(tags, "wiresvn") +if SVNver then + table.insert(tags, "wiresvn" .. SVNver) +end + +-- sort and update tags +table.sort(tags) +RunConsoleCommand("sv_tags", table.concat(tags, ",")) + +-- prevent applyForce+Anti-noclip-based killing contraptions +hook.Add("InitPostEntity", "antiantinoclip", function() + local ENT = scripted_ents.GetList().rt_antinoclip_handler + if not ENT then return end + ENT = ENT.t + + local rt_antinoclip_handler_StartTouch = ENT.StartTouch + function ENT:StartTouch(...) + if self.speed >= 20 then return end + + local phys = self.Ent:GetPhysicsObject() + if phys:IsValid() and phys:GetAngleVelocity():Length() > 20 then return end + + rt_antinoclip_handler_StartTouch(self, ...) + end + + --local rt_antinoclip_handler_Think = ENT.Think + function ENT:Think() + + local t = CurTime() + local dt = t-self.lastt + self.lastt = t + + local phys = self.Ent:GetPhysicsObject() + local pos + if phys:IsValid() then + pos = phys:LocalToWorld(phys:GetMassCenter()) + else + pos = self.Ent:GetPos() + end + self.speed = pos:Distance(self.oldpos)/dt + self.oldpos = pos + --rt_antinoclip_handler_Think(self, ...) + end + + ENT.speed = 20 + ENT.lastt = 0 + ENT.oldpos = Vector(0,0,0) +end) diff --git a/lua/wire/server/radiolib.lua b/lua/wire/server/radiolib.lua new file mode 100644 index 0000000000..f414aee57f --- /dev/null +++ b/lua/wire/server/radiolib.lua @@ -0,0 +1,161 @@ +//First there was phenes +//Then there was High6 +//Then Black Phoenix came and rewrote everything, what a bastard + +local Radio_Entities = {} + +function Radio_Register(ent) + table.insert(Radio_Entities, ent) +end +/* +function Radio_Update() //FIXME + for ks,vs in ipairs(Radio_Entities) do + if (not IsEntity(vs.Entity)) then //Invalid radio + table.remove(Radio_Entities, ks) + else + if (vs.Transmitting == true) then //Radio has something to transmit + for kr,vr in ipairs(Radio_Entities) do + if (not IsEntity(vr.Entity)) then + table.remove(Radio_Entities, vr) + elseif (ks ~= kr) then + vs.TransmitTo(vr) + end + end + end + end + end +end +hook.Add("Think", "RadioLib_Think", Radio_Update)*/ + +function Radio_Unregister(ent) + for k,v in ipairs(Radio_Entities) do + if (v == ent) then + table.remove(Radio_Entities, k) + elseif (IsEntity(v.Entity)) then + //Zero out all channels that this radio used + for i=0,31 do + if (v.RecievedData[i].Owner == ent) then + v.RecievedData[i].Owner = nil + v.RecievedData[i].Data = 0 + v:NotifyDataRecieved(i) + end + end + v:ShowOutput() + end + end +end + +function Radio_SendData(ent, subch, data) + ent.SentData[subch] = data + + for k,v in ipairs(Radio_Entities) do + if (not IsEntity(v.Entity)) then //Invalid radio + Radio_Unregister(v) + elseif (ent:EntIndex() != v.Entity:EntIndex()) then //Not sender + if ((ent.Secure) && (v.Secure)) then + if ((ent.pl:SteamID() == v.pl:SteamID()) && (ent.Channel == v.Channel)) then + v.RecievedData[subch].Owner = ent + v.RecievedData[subch].Data = data + v:NotifyDataRecieved(subch) + end + else + if (ent.Channel == v.Channel) then + v.RecievedData[subch].Owner = ent + v.RecievedData[subch].Data = data + v:NotifyDataRecieved(subch) + end + end + v:ShowOutput() + end + end +end + +function Radio_RecieveData(ent) + for i=0,31 do + ent.RecievedData[i].Owner = nil + ent.RecievedData[i].Data = 0 + ent:NotifyDataRecieved(i) + end + + for k,v in ipairs(Radio_Entities) do + if (not IsEntity(v.Entity)) then //Invalid radio + Radio_Unregister(v) + elseif (ent:EntIndex() != v.Entity:EntIndex()) then //Not sender + if ((ent.Secure) && (v.Secure)) then + if ((ent.pl:SteamID() == v.pl:SteamID()) && (ent.Channel == v.Channel)) then + for i=0,31 do + ent.RecievedData[i].Owner = v + ent.RecievedData[i].Data = v.SentData[i] + ent:NotifyDataRecieved(i) + end + end + else + if (ent.Channel == v.Channel) then + for i=0,31 do + ent.RecievedData[i].Owner = v + ent.RecievedData[i].Data = v.SentData[i] + ent:NotifyDataRecieved(i) + end + end + end + end + end + ent:ShowOutput() +end + +function Radio_ChangeChannel(ent) + //Request all other radios send data to this radio + Radio_RecieveData(ent) + + for k,v in ipairs(Radio_Entities) do + if (not IsEntity(v.Entity)) then //Invalid radio + Radio_Unregister(v) + elseif (ent:EntIndex() != v.Entity:EntIndex()) then //Not sender + //1. Kill all transmissions for this radio + //for i=0,31 do + // if (v.RecievedData[i].Owner == ent) then + // v.RecievedData[i].Owner = nil + // v.RecievedData[i].Data = 0 + // v:NotifyDataRecieved(i) + // end + //end + Radio_RecieveData(v) + + //2. Retransmit under new channel + if ((ent.Secure) && (v.Secure)) then + if ((ent.pl:SteamID() == v.pl:SteamID()) && (ent.Channel == v.Channel)) then + for i=0,31 do + if (ent.SentData[i] ~= 0) then //dont send zeroes + v.RecievedData[i].Owner = ent + v.RecievedData[i].Data = ent.SentData[i] + v:NotifyDataRecieved(i) + end + end + end + else + if (ent.Channel == v.Channel) then + for i=0,31 do + if (ent.SentData[i] ~= 0) then //dont send zeroes + v.RecievedData[i].Owner = ent + v.RecievedData[i].Data = ent.SentData[i] + v:NotifyDataRecieved(i) + end + end + end + end + + v:ShowOutput() + end + end +end + +local radio_twowaycounter = 0 + +function Radio_GetTwoWayID() + radio_twowaycounter = radio_twowaycounter + 1 + return radio_twowaycounter +end + +-- phenex: End radio mod. +//Modified by High6 (To support 4 values) +//Rebuilt by high6 to allow defined amount of values/secure lines diff --git a/lua/wire/stools/detection.lua b/lua/wire/stools/detection.lua new file mode 100644 index 0000000000..ba5f64c94d --- /dev/null +++ b/lua/wire/stools/detection.lua @@ -0,0 +1,31 @@ +AddCSLuaFile( "detection.lua" ) +WireToolSetup.setCategory( "Detection" ) + +do -- wire_speedometer + WireToolSetup.open( "speedometer", "Speedometer", "gmod_wire_speedometer", WireToolMakeSpeedometer ) + + if CLIENT then + language.Add( "Tool_wire_speedometer_name", "Speedometer Tool (Wire)" ) + language.Add( "Tool_wire_speedometer_desc", "Spawns a speedometer for use with the wire system." ) + language.Add( "Tool_wire_speedometer_0", "Primary: Create/Update Speedometer" ) + language.Add( "Tool_wire_speedometer_xyz_mode", "Split Outputs to X,Y,Z" ) + language.Add( "Tool_wire_speedometer_angvel", "Add Angular Velocity Outputs" ) + language.Add( "sboxlimit_wire_speedometers", "You've hit speedometers limit!" ) + end + WireToolSetup.BaseLang("Speedometers") + + if SERVER then + CreateConVar('sbox_maxwire_speedometers', 10) + end + + TOOL.Model = "models/jaanus/wiretool/wiretool_speed.mdl" + TOOL.ClientConVar = { + xyz_mode = 0, + angvel = 0 + } + + function TOOL.BuildCPanel(panel) + panel:CheckBox("#Tool_wire_speedometer_xyz_mode", "wire_speedometer_xyz_mode") + panel:CheckBox("#Tool_wire_speedometer_angvel", "wire_speedometer_AngVel") + end +end -- wire_speedometer diff --git a/lua/wire/stools/display.lua b/lua/wire/stools/display.lua new file mode 100644 index 0000000000..05824f3463 --- /dev/null +++ b/lua/wire/stools/display.lua @@ -0,0 +1,1104 @@ +-- $Rev: 1734 $ +-- $LastChangedDate: 2009-09-26 16:31:08 -0700 (Sat, 26 Sep 2009) $ +-- $LastChangedBy: tad2020 $ + +AddCSLuaFile( "display.lua" ) +WireToolSetup.setCategory( "Display" ) + +local function CreateFlatGetAngle( self, trace ) + local Ang = trace.HitNormal:Angle() + if self:GetClientNumber("createflat") == 0 then + Ang.pitch = Ang.pitch + 90 + end + return Ang +end + + +do -- wire_indicator + WireToolSetup.open( "indicator", "Indicator", "gmod_wire_indicator", nil, "Indicators" ) + + if CLIENT then + language.Add( "Tool_wire_indicator_name", "Indicator Tool (Wire)" ) + language.Add( "Tool_wire_indicator_desc", "Spawns a indicator for use with the wire system." ) + language.Add( "Tool_wire_indicator_0", "Primary: Create/Update Indicator" ) + language.Add( "ToolWireIndicator_Model", "Model:" ) + language.Add( "ToolWireIndicator_a_value", "A Value:" ) + language.Add( "ToolWireIndicator_a_colour", "A Colour:" ) + language.Add( "ToolWireIndicator_b_value", "B Value:" ) + language.Add( "ToolWireIndicator_b_colour", "B Colour:" ) + language.Add( "ToolWireIndicator_Material", "Material:" ) + language.Add( "ToolWireIndicator_90", "Rotate segment 90" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_indicators", "You've hit indicators limit!" ) + + if SERVER then + ModelPlug_Register("indicator") + + function TOOL:GetConVars() + return self:GetClientNumber("a"), + math.Clamp(self:GetClientNumber("ar"),0,255), + math.Clamp(self:GetClientNumber("ag"),0,255), + math.Clamp(self:GetClientNumber("ab"),0,255), + math.Clamp(self:GetClientNumber("aa"),0,255), + self:GetClientNumber("b"), + math.Clamp(self:GetClientNumber("br"),0,255), + math.Clamp(self:GetClientNumber("bg"),0,255), + math.Clamp(self:GetClientNumber("bb"),0,255), + math.Clamp(self:GetClientNumber("ba"),0,255), + self:GetClientInfo( "material" ), + self:GetClientNumber( "noclip" ) == 1 + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireIndicator( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.ClientConVar = { + model = "models/jaanus/wiretool/wiretool_siren.mdl", + a = 0, + ar = 255, + ag = 0, + ab = 0, + aa = 255, + b = 1, + br = 0, + bg = 255, + bb = 0, + ba = 255, + material = "models/debug/debugwhite", + noclip = 0, + rotate90 = 0, + weld = 1, + } + + --function TOOL:GetGhostAngle( Ang ) + function TOOL:GetAngle( trace ) + local Ang = trace.HitNormal:Angle() + local Model = self:GetModel() + --these models get mounted differently + if Model == "models/props_borealis/bluebarrel001.mdl" || Model == "models/props_junk/PopCan01a.mdl" then + return Ang + Angle(-90, 0, 0) + elseif Model == "models/props_trainstation/trainstation_clock001.mdl" or Model == "models/segment.mdl" or Model == "models/segment2.mdl" then + return Ang + Angle(0, 0, (self:GetClientNumber("rotate90") * 90)) + end + Ang.pitch = Ang.pitch + 90 + return Ang + end + + function TOOL:GetGhostMin( min ) + local Model = self:GetModel() + --these models are different + if Model == "models/props_trainstation/trainstation_clock001.mdl" or Model == "models/segment.mdl" or Model == "models/segment2.mdl" then + return min.x + end + return min.z + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_indicator") + panel:NumSlider("#ToolWireIndicator_a_value", "wire_indicator_a", -10, 10, 1) + + panel:AddControl("Color", { + Label = "#ToolWireIndicator_a_colour", + Red = "wire_indicator_ar", + Green = "wire_indicator_ag", + Blue = "wire_indicator_ab", + Alpha = "wire_indicator_aa", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:NumSlider("#ToolWireIndicator_b_value", "wire_indicator_b", -10, 10, 1) + + panel:AddControl("Color", { + Label = "#ToolWireIndicator_b_colour", + Red = "wire_indicator_br", + Green = "wire_indicator_bg", + Blue = "wire_indicator_bb", + Alpha = "wire_indicator_ba", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + ModelPlug_AddToCPanel(panel, "indicator", "wire_indicator", "#ToolWireIndicator_Model", nil, "#ToolWireIndicator_Model") + + panel:AddControl("ComboBox", { + Label = "#ToolWireIndicator_Material", + Options = { + ["Matte"] = { wire_indicator_material = "models/debug/debugwhite" }, + ["Shiny"] = { wire_indicator_material = "models/shiny" }, + ["Metal"] = { wire_indicator_material = "models/props_c17/metalladder003" } + } + }) + + panel:CheckBox("#ToolWireIndicator_90", "wire_indicator_rotate90") + panel:CheckBox("#WireGatesTool_noclip", "wire_indicator_noclip") + panel:CheckBox("Weld", "wire_indicator_weld") + end +end -- wire_indicator + + + +do -- wire_7seg + WireToolSetup.open( "7seg", "7 Segment Display", "gmod_wire_indicator" ) + + TOOL.GhostAngle = Angle(90, 0, 0) + TOOL.GhostMin = "x" + + if CLIENT then + language.Add( "Tool_wire_7seg_name", "7-Segment Display Tool" ) + language.Add( "Tool_wire_7seg_desc", "Spawns 7 indicators for numeric display with the wire system." ) + language.Add( "Tool_wire_7seg_0", "Primary: Create display/Update Indicator" ) + language.Add( "ToolWire7Seg_a_colour", "Off Colour:" ) + language.Add( "ToolWire7Seg_b_colour", "On Colour:" ) + language.Add( "ToolWire7SegTool_worldweld", "Allow weld to world" ) + language.Add( "undone_wire7seg", "Undone 7-Segment Display" ) + end + + -- define MaxLimitName cause this tool just uses gmod_wire_indicators + TOOL.MaxLimitName = "wire_indicators" + + if SERVER then + function TOOL:GetConVars() + return 0, + math.Clamp(self:GetClientNumber("ar"),0,255), + math.Clamp(self:GetClientNumber("ag"),0,255), + math.Clamp(self:GetClientNumber("ab"),0,255), + math.Clamp(self:GetClientNumber("aa"),0,255), + 1, + math.Clamp(self:GetClientNumber("br"),0,255), + math.Clamp(self:GetClientNumber("bg"),0,255), + math.Clamp(self:GetClientNumber("bb"),0,255), + math.Clamp(self:GetClientNumber("ba"),0,255) + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWire7Seg( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.ClientConVar = { + model = "models/segment.mdl", + ar = 70, --default: dark grey off, full red on + ag = 70, + ab = 70, + aa = 255, + br = 255, + bg = 0, + bb = 0, + ba = 255, + worldweld = 1, + } + + function TOOL:PostMake_SetPos() end + + function TOOL:LeftClick_PostMake( wire_indicators, ply, trace ) + if not wire_indicators then return end + local worldweld = self:GetClientNumber("worldweld") == 1 + undo.Create("Wire7Seg") + for x=1, 7 do + --make welds + local const = WireLib.Weld(wire_indicators[x], trace.Entity, trace.PhysicsBone, true, false, worldweld) + undo.AddEntity( wire_indicators[x] ) + undo.AddEntity( const ) + ply:AddCleanup( "wire_indicators", wire_indicators[x] ) + ply:AddCleanup( "wire_indicators", const) + end + undo.SetPlayer( ply ) + undo.Finish() + return true + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_7seg") + + panel:AddControl("Color", { + Label = "#ToolWire7Seg_a_colour", + Red = "wire_7seg_ar", + Green = "wire_7seg_ag", + Blue = "wire_7seg_ab", + Alpha = "wire_7seg_aa", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:AddControl("Color", { + Label = "#ToolWire7Seg_b_colour", + Red = "wire_7seg_br", + Green = "wire_7seg_bg", + Blue = "wire_7seg_bb", + Alpha = "wire_7seg_ba", + ShowAlpha = "1", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:AddControl("ComboBox", { + Label = "#ToolWireIndicator_Model", + Options = { + ["Medium 7-seg bar"] = { wire_7seg_model = "models/segment2.mdl" }, + ["Small 7-seg bar"] = { wire_7seg_model = "models/segment.mdl" }, + } + }) + + panel:CheckBox("#ToolWire7SegTool_worldweld", "wire_7seg_worldweld") + end +end -- wire_7seg + + + +do -- wire_consolescreen + WireToolSetup.open( "consolescreen", "Console Screen", "gmod_wire_consolescreen", nil, "Screens" ) + + if CLIENT then + language.Add( "Tool_wire_consolescreen_name", "Console Screen Tool (Wire)" ) + language.Add( "Tool_wire_consolescreen_desc", "Spawns a console screen" ) + language.Add( "Tool_wire_consolescreen_0", "Primary: Create/Update screen" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_consolescreens", "You've hit console screens limit!" ) + + if SERVER then + function TOOL:GetConVars() end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireconsoleScreen( ply, trace.HitPos, Ang, model ) + end + end + + TOOL.GetAngle = CreateFlatGetAngle + TOOL.NoLeftOnClass = true -- no update ent function needed + TOOL.ClientConVar = { + model = "models/props_lab/monitor01b.mdl", + createflat = 0, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + Options = { + ["#Small tv (4:3)"] = { wire_consolescreen_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv (16:10)"] = { wire_consolescreen_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#Plasma tv (4:3)"] = { wire_consolescreen_model = "models/blacknecro/tv_plasma_4_3.mdl" }, + ["#LCD Monitor (4:3)"] = { wire_consolescreen_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big (1:1)"] = { wire_consolescreen_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small (1:1)"] = { wire_consolescreen_model = "models/kobilica/wiremonitorsmall.mdl" }, + ["#Billboard"] = { wire_consolescreen_model = "models/props/cs_assault/Billboard.mdl" }, + ["#LCD Screen (1:1)"] = { wire_consolescreen_model = "models/blacknecro/ledboard60.mdl" }, + } + }) + panel:CheckBox("#Create Flat to Surface", "wire_consolescreen_createflat") + panel:CheckBox("Weld", "wire_consolescreen_weld") + end +end -- wire_consolescreen + + + +do -- wire_digitalscreen + WireToolSetup.open( "digitalscreen", "Digital Screen", "gmod_wire_digitalscreen", nil, "Digital Screens" ) + + if CLIENT then + language.Add( "Tool_wire_digitalscreen_name", "Digital Screen Tool (Wire)" ) + language.Add( "Tool_wire_digitalscreen_desc", "Spawns a digital screen, which can be used to draw pixel by pixel. Resoultion is 32x32!" ) + language.Add( "Tool_wire_digitalscreen_0", "Primary: Create/Update screen" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_digitalscreens", "You've hit digital screens limit!" ) + + if SERVER then + function TOOL:GetConVars() + return self:GetClientInfo("width"), self:GetClientInfo("height") + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireDigitalScreen( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.NoLeftOnClass = true -- no update ent function needed + TOOL.GetAngle = CreateFlatGetAngle + TOOL.ClientConVar = { + model = "models/props_lab/monitor01b.mdl", + width = 32, + height = 32, + createflat = 0, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + Options = { + ["#Small tv"] = { wire_digitalscreen_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv"] = { wire_digitalscreen_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#LCD monitor"] = { wire_digitalscreen_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big"] = { wire_digitalscreen_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small"] = { wire_digitalscreen_model = "models/kobilica/wiremonitorsmall.mdl" }, + } + }) + panel:NumSlider("Width", "wire_digitalscreen_width", 1, 512, 0) + panel:NumSlider("Height", "wire_digitalscreen_height", 1, 512, 0) + panel:CheckBox("#Create Flat to Surface", "wire_digitalscreen_createflat") + panel:CheckBox("Weld", "wire_digitalscreen_weld") + end +end -- wire_digitalscreen + + + +do -- wire_lamp + WireToolSetup.open( "lamp", "Lamp", "gmod_wire_lamp", nil, "Lamps" ) + + if CLIENT then + language.Add( "Tool_wire_lamp_name", "Wire Lamps" ) + language.Add( "Tool_wire_lamp_desc", "Spawns a lamp for use with the wire system." ) + language.Add( "Tool_wire_lamp_0", "Primary: Create hanging lamp Secondary: Create unattached lamp" ) + language.Add( "WireLampTool_RopeLength", "Rope Length:") + language.Add( "WireLampTool_Color", "Color:" ) + language.Add( "WireLampTool_Const", "Constraint:" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 10, "wire_lamps", "You've hit lamps limit!" ) + + if SERVER then + function TOOL:GetConVars() + return math.Clamp( self:GetClientNumber( "r" ), 0, 255 ), + math.Clamp( self:GetClientNumber( "g" ), 0, 255 ), + math.Clamp( self:GetClientNumber( "b" ), 0, 255 ), + self:GetClientInfo( "texture" ) + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + local r, g, b, Texture = self:GetConVars() + return MakeWireLamp( ply, r, g, b, Texture, { Pos = trace.HitPos, Angle = Ang } ) + end + + function TOOL:LeftClick_PostMake( ent, ply, trace ) + if ent == true then return true end + if ent == nil or ent == false or not ent:IsValid() then return false end + + local const = self:GetClientInfo( "const" ) + + if const == "weld" then + local const = WireLib.Weld( ent, trace.Entity, trace.PhysicsBone, true ) + undo.Create( self.WireClass ) + undo.AddEntity( ent ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + elseif const == "rope" then + + local length = self:GetClientNumber( "ropelength" ) + local material = self:GetClientInfo( "ropematerial" ) + + local LPos1 = Vector( 0, 0, 5 ) + local LPos2 = trace.Entity:WorldToLocal( trace.HitPos ) + + if trace.Entity:IsValid() then + local phys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + if phys:IsValid() then + LPos2 = phys:WorldToLocal( trace.HitPos ) + end + end + + local constraint, rope = constraint.Rope( ent, trace.Entity, 0, trace.PhysicsBone, LPos1, LPos2, 0, length, 0, 1.5, material, nil ) + + undo.Create( self.WireClass ) + undo.AddEntity( ent ) + undo.AddEntity( rope ) + undo.AddEntity( constraint ) + undo.SetPlayer( ply ) + undo.Finish() + + else --none + ent:GetPhysicsObject():EnableMotion(false) -- freeze + + undo.Create( self.WireClass ) + undo.AddEntity( ent ) + undo.SetPlayer( ply ) + undo.Finish() + end + + ply:AddCleanup( self.WireClass, ent ) + + return true + end + end + + function TOOL:GetAngle( trace ) + return trace.HitNormal:Angle() - Angle( 90, 0, 0 ) + end + + function TOOL:SetPos( ent, trace ) + ent:SetPos(trace.HitPos + trace.HitNormal * 10) + end + + --TOOL.GhostAngle = Angle(180, 0, 0) + TOOL.Model = "models/props_wasteland/prison_lamp001c.mdl" + TOOL.ClientConVar = { + ropelength = 64, + ropematerial = "cable/rope", + r = 255, + g = 255, + b = 255, + const = "rope", + texture = "effects/flashlight001", + } + + -- TODO: redo this function + function TOOL:RightClick( trace ) -- Spawn a lamp without constraints (just frozen) + if not trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local ply = self:GetOwner() + local noconstraint = true + + local ent = self:LeftClick_Make( trace, ply, noconstraint ) + if ent == true then return true end + if ent == nil or ent == false or not ent:IsValid() then return false end + + undo.Create( self.WireClass ) + undo.AddEntity( ent ) + undo.AddEntity( const ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( self.WireClass, ent ) + + return true + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_lamp") + + panel:NumSlider("#WireLampTool_RopeLength", "wire_lamp_ropelength", 4, 400, 0) + + panel:AddControl("Color", { + Label = "#WireLampTool_Color", + Red = "wire_lamp_r", + Green = "wire_lamp_g", + Blue = "wire_lamp_b", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + + panel:AddControl("ComboBox", { + Label = "#WireLampTool_Const", + Options = { + ["Rope"] = { wire_lamp_const = "rope" }, + ["Weld"] = { wire_lamp_const = "weld" }, + ["None"] = { wire_lamp_const = "none" }, + } + }) + + local MatSelect = panel:MatSelect( "wire_lamp_texture", nil, true, 0.33, 0.33 ) + for k, v in pairs( list.Get( "LampTextures" ) ) do + MatSelect:AddMaterial( v.Name or k, k ) + end + end +end -- wire_lamp + + + +do -- wire_light + WireToolSetup.open( "light", "Light", "gmod_wire_light", nil, "Lights" ) + + if CLIENT then + language.Add( "Tool_wire_light_name", "Light Tool (Wire)" ) + language.Add( "Tool_wire_light_desc", "Spawns a Light for use with the wire system." ) + language.Add( "Tool_wire_light_0", "Primary: Create Light" ) + language.Add( "WireLightTool_directional", "Directional Component" ) + language.Add( "WireLightTool_radiant", "Radiant Component" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 8, "wire_lights", "You've hit lights limit!" ) + + if SERVER then + function TOOL:GetConVars() + return self:GetClientNumber("directional") ~= 0, + self:GetClientNumber("radiant") ~= 0 + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireLight( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" + TOOL.ClientConVar = { + directional = 0, + radiant = 0, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + panel:CheckBox("#WireLightTool_directional", "wire_light_directional") + panel:CheckBox("#WireLightTool_radiant", "wire_light_radiant") + panel:CheckBox("Weld", "wire_light_weld") + end +end -- wire_light + + + +do -- wire_oscilloscope + WireToolSetup.open( "oscilloscope", "Oscilloscope", "gmod_wire_oscilloscope", nil, "Oscilloscopes" ) + + if CLIENT then + language.Add( "Tool_wire_oscilloscope_name", "Oscilloscope Tool (Wire)" ) + language.Add( "Tool_wire_oscilloscope_desc", "Spawns a oscilloscope what display line graphs." ) + language.Add( "Tool_wire_oscilloscope_0", "Primary: Create/Update oscilloscope" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_oscilloscopes", "You've hit oscilloscopes limit!" ) + + if SERVER then + function TOOL:GetConVars() end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireOscilloscope( ply, trace.HitPos, Ang, model ) + end + end + + TOOL.NoLeftOnClass = true -- no update ent function needed + TOOL.GetAngle = CreateFlatGetAngle + TOOL.ClientConVar = { + model = "models/props_lab/monitor01b.mdl", + createflat = 0, + weld = 1, + } + + + function TOOL.BuildCPanel(panel) + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + Options = { + ["#Small tv"] = { wire_oscilloscope_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv"] = { wire_oscilloscope_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#LCD monitor"] = { wire_oscilloscope_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big"] = { wire_oscilloscope_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small"] = { wire_oscilloscope_model = "models/kobilica/wiremonitorsmall.mdl" }, + } + }) + panel:CheckBox("#Create Flat to Surface", "wire_oscilloscope_createflat") + panel:CheckBox("Weld", "wire_oscilloscope_weld") + end +end -- wire_oscilloscope + + + +do -- wire_panel + WireToolSetup.open( "panel", "Control Panel", "gmod_wire_panel", nil, "Control Panels" ) + + if CLIENT then + language.Add( "Tool_wire_panel_name", "Control Panel Tool (Wire)" ) + language.Add( "Tool_wire_panel_desc", "Spawns a panel what display values." ) + language.Add( "Tool_wire_panel_0", "Primary: Create/Update panel" ) + language.Add( "Tool_wire_panel_createflat", "Create flat to surface" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_panels", "You've hit panels limit!" ) + + if SERVER then + function TOOL:GetConVars() end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWirePanel( ply, trace.HitPos, Ang, model ) + end + end + + TOOL.GetAngle = CreateFlatGetAngle + TOOL.NoLeftOnClass = true -- no update ent function needed + TOOL.ClientConVar = { + model = "models/props_lab/monitor01b.mdl", + createflat = 1, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + Options = { + ["#Small tv"] = { wire_panel_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv"] = { wire_panel_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#LCD monitor"] = { wire_panel_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big"] = { wire_panel_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small"] = { wire_panel_model = "models/kobilica/wiremonitorsmall.mdl" }, + } + }) + WireDermaExts.ModelSelect(panel, "wire_panel_model", list.Get( "WireScreenModels" ), 2) + panel:CheckBox("#Tool_wire_panel_createflat", "wire_panel_createflat") + panel:CheckBox("Weld", "wire_panel_weld") + end +end -- wire_panel + + + +do -- wire_pixel + WireToolSetup.open( "pixel", "Pixel", "gmod_wire_pixel", nil, "Pixels" ) + + if CLIENT then + language.Add( "Tool_wire_pixel_name", "Pixel Tool (Wire)" ) + language.Add( "Tool_wire_pixel_desc", "Spawns a Pixel for use with the wire system." ) + language.Add( "Tool_wire_pixel_0", "Primary: Create Pixel" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_pixels", "You've hit pixels limit!" ) + + if SERVER then + ModelPlug_Register("pixel") + + function TOOL:GetConVars() + return self:GetClientNumber( "noclip" ) == 1 + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWirePixel( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.NoLeftOnClass = true -- no update ent function needed + TOOL.ClientConVar = { + model = "models/jaanus/wiretool/wiretool_siren.mdl", + noclip = 0, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + WireDermaExts.ModelSelect(panel, "wire_pixel_model", list.Get("Wire_pixel_Models"), 3, true) + panel:CheckBox("#WireGatesTool_noclip", "wire_pixel_noclip") + panel:CheckBox("Weld", "wire_pixel_weld") + end +end -- wire_pixel + + + +do -- wire_screen + WireToolSetup.open( "screen", "Screen", "gmod_wire_screen", nil, "Screens" ) + + if CLIENT then + language.Add( "Tool_wire_screen_name", "Screen Tool (Wire)" ) + language.Add( "Tool_wire_screen_desc", "Spawns a screen that display values." ) + language.Add( "Tool_wire_screen_0", "Primary: Create/Update screen" ) + language.Add("Tool_wire_screen_singlevalue", "Only one value") + language.Add("Tool_wire_screen_singlebigfont", "Use bigger font for single-value screen") + language.Add("Tool_wire_screen_texta", "Text A:") + language.Add("Tool_wire_screen_textb", "Text B:") + language.Add("Tool_wire_screen_leftalign", "Left alignment") + language.Add("Tool_wire_screen_floor", "Floor screen value") + language.Add("Tool_wire_screen_createflat", "Create flat to surface") + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_screens", "You've hit screens limit!" ) + + if SERVER then + ModelPlug_Register("pixel") + + function TOOL:GetConVars() + return self:GetClientNumber("singlevalue") == 1, + self:GetClientNumber("singlebigfont") == 1, + self:GetClientInfo("texta"), + self:GetClientInfo("textb"), + self:GetClientNumber("leftalign") == 1, + self:GetClientNumber("floor") == 1 + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireScreen( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.GetAngle = CreateFlatGetAngle + TOOL.ClientConVar = { + model = "models/props_lab/monitor01b.mdl", + singlevalue = 0, + singlebigfont = 1, + texta = "Value A", + textb = "Value B", + createflat = 1, + leftalign = 0, + floor = 0, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_screen") + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_Model", + Options = { + ["#Small tv"] = { wire_screen_model = "models/props_lab/monitor01b.mdl" }, + ["#Plasma tv"] = { wire_screen_model = "models/props/cs_office/TV_plasma.mdl" }, + ["#LCD monitor"] = { wire_screen_model = "models/props/cs_office/computer_monitor.mdl" }, + ["#Monitor Big"] = { wire_screen_model = "models/kobilica/wiremonitorbig.mdl" }, + ["#Monitor Small"] = { wire_screen_model = "models/kobilica/wiremonitorsmall.mdl" }, + } + }) + WireDermaExts.ModelSelect(panel, "wire_screen_model", list.Get( "WireScreenModels" ), 2) + panel:CheckBox("#Tool_wire_screen_singlevalue", "wire_screen_singlevalue") + panel:CheckBox("#Tool_wire_screen_singlebigfont", "wire_screen_singlebigfont") + panel:CheckBox("#Tool_wire_screen_leftalign", "wire_screen_leftalign") + panel:CheckBox("#Tool_wire_screen_floor", "wire_screen_floor") + panel:TextEntry("#Tool_wire_screen_texta", "wire_screen_texta") + panel:TextEntry("#Tool_wire_screen_textb", "wire_screen_textb") + panel:CheckBox("#Tool_wire_screen_createflat", "wire_screen_createflat") + panel:CheckBox("Weld", "wire_screen_weld") + end +end -- wire_screen + + + +do -- wire_soundemitter + WireToolSetup.open( "soundemitter", "Sound Emitter", "gmod_wire_soundemitter", nil, "Sound Emitters" ) + + if CLIENT then + language.Add( "Tool_wire_soundemitter_name", "Sound Emitter Tool (Wire)" ) + language.Add( "Tool_wire_soundemitter_desc", "Spawns a sound emitter for use with the wire system." ) + language.Add( "Tool_wire_soundemitter_0", "Primary: Create/Update Sound Emitter" ) + language.Add( "WireEmitterTool_sound", "Sound:" ) + language.Add( "WireEmitterTool_collision", "Collision" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 10, "wire_emitters", "You've hit sound emitters limit!" ) + + if SERVER then + ModelPlug_Register("speaker") + + function TOOL:GetConVars() + return Sound( self:GetClientInfo( "sound" ) ) + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireEmitter( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.ClientConVar = { + model = "models/cheeze/wires/speaker.mdl", + sound = "synth/square.wav", + collision = 0, + weld = 1, + } + + function TOOL.BuildCPanel(panel) + panel:AddControl("ComboBox", { + Label = "#WireEmitterTool_sound", + MenuButton = "1", -- Don't remove that "MenuButton = 1" again please. Without that, we can't save our sounds, we added manually @aVoN + Folder = "wire_soundemitter", + Options = list.Get( "WireSounds" ), + }) + panel:TextEntry("#WireEmitterTool_sound", "wire_soundemitter_sound") + panel:CheckBox("#WireEmitterTool_collision", "wire_soundemitter_collision" ) + ModelPlug_AddToCPanel(panel, "speaker", "wire_soundemitter", nil, nil, true) + panel:CheckBox("Weld", "wire_soundemitter_weld") + end +end -- wire_soundemitter + + + +do -- wire_textscreen + --Wire text screen by greenarrow + --http://gmodreviews.googlepages.com/ + --http://forums.facepunchstudios.com/greenarrow + WireToolSetup.open( "textscreen", "Text Screen", "gmod_wire_textscreen", nil, "Text Screens" ) + + TOOL.Model = "models/kobilica/wiremonitorbig.mdl" + + if CLIENT then + language.Add( "Tool_wire_textscreen_name", "Text Screen Tool (Wire)" ) + language.Add( "Tool_wire_textscreen_desc", "Spawns a screen that display text." ) + language.Add( "Tool_wire_textscreen_0", "Primary: Create/Update text screen Secondary: Copy textscreenlines" ) + for i=1,12 do + language.Add("Tool_wire_textscreen_text"..i, "Text "..i..":") + end + language.Add("Tool_wire_textscreen_tsize", "Text size:") + language.Add("Tool_wire_textscreen_tjust", "Text justification:") + language.Add("Tool_wire_textscreen_colour", "Text colour:") + language.Add("Tool_wire_textscreen_ninputs", "Number of inputs:") + language.Add("Tool_wire_textscreen_createflat", "Create flat to surface") + language.Add("Tool_wire_textscreen_defaulton", "Force show text (make wires optional)") + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_textscreens", "You've hit sound text screens limit!" ) + + if SERVER then + ModelPlug_Register("speaker") + + function TOOL:GetConVars() + local TextList = {} + for i = 1, 12 do + TextList[i] = self:GetClientInfo("text"..i) + end + return TextList, + (16 - tonumber(self:GetClientInfo("tsize"))), + self:GetClientInfo("tjust"), + math.min(self:GetClientNumber("tred"), 255), + math.min(self:GetClientNumber("tgreen"), 255), + math.min(self:GetClientNumber("tblue"), 255), + self:GetClientNumber("ninputs"), + self:GetClientNumber("defaulton") + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireTextScreen( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + + TOOL.GetAngle = CreateFlatGetAngle + TOOL.ClientConVar = { + tsize = 10, + tjust = 1, + tred = 255, + tblue = 255, + tgreen = 255, + ninputs = 3, + defaulton = 1, + createflat = 1, + weld = 1, + } + for i = 1, 12 do + TOOL.ClientConVar["text"..i] = "" + end + + function TOOL:RightClick( trace ) + if not trace.HitPos then return false end + if trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + local ply = self:GetOwner() + + if trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_textscreen" then + for i=1, 12 do + if(trace.Entity.TextList[i]!="") then + ply:ConCommand("wire_textscreen_text"..i.." "..trace.Entity.TextList[i]) + else + ply:ConCommand("wire_textscreen_text"..i.." \"\"") + end + end + return true + end + + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_textscreen") + panel:NumSlider("#Tool_wire_textscreen_tsize", "wire_textscreen_tsize", 1, 15, 0) + panel:NumSlider("#Tool_wire_textscreen_tjust", "wire_textscreen_tjust", 0, 2, 0) + panel:NumSlider("#Tool_wire_textscreen_ninputs", "wire_textscreen_ninputs", 1, 10, 0) + panel:AddControl("Color", { + Label = "#Tool_wire_textscreen_colour", + Red = "wire_textscreen_tred", + Green = "wire_textscreen_tgreen", + Blue = "wire_textscreen_tblue", + ShowAlpha = "0", + ShowHSV = "1", + ShowRGB = "1", + Multiplier = "255" + }) + panel:CheckBox("#Tool_wire_textscreen_createflat", "wire_textscreen_createflat") + panel:CheckBox("#Tool_wire_textscreen_defaulton", "wire_textscreen_defaulton") + for i = 1, 12 do + panel:TextEntry("#Tool_wire_textscreen_text"..i, "wire_textscreen_text"..i) + end + panel:CheckBox("Weld", "wire_textscreen_weld") + end +end -- wire_textscreen + + + +do -- Holography-- + WireToolSetup.setCategory( "Render" ) + + local function HoloRightClick( self, trace ) + if CLIENT then return true end + + local ent = trace.Entity + if not trace.HitNonWorld or not ent:IsValid() then return false end + + local class = ent:GetClass() + + if self:GetStage()==0 then + if class == "gmod_wire_holoemitter" then + self.Linked = trace.Entity + self:SetStage(1) + return true + elseif class == "gmod_wire_hologrid" then + self.Linked = trace.Entity + self:SetStage(2) + return true + end + elseif self:GetStage()==1 then + if class ~= "gmod_wire_hologrid" then return false end + self.Linked:LinkToGrid(ent); + self:SetStage(0) + return true + elseif self:GetStage()==2 then + if class ~= "gmod_wire_holoemitter" then + self.Linked:TriggerInput("Reference", ent) + self.Linked:TriggerInput("UseGPS", 0) + self:SetStage(0) + return true + end + ent:LinkToGrid(self.Linked); + self:SetStage(0) + return true + end + + return false + end + + local function HoloReload( self, trace ) + self.Linked = nil + self:SetStage(0) + + local ent = trace.Entity + if not trace.HitNonWorld or not ent:IsValid() then return false end + if trace.Entity:GetClass() == "gmod_wire_holoemitter" then + ent:LinkToGrid( nil ); + return true + elseif trace.Entity:GetClass() == "gmod_wire_hologrid" then + self.Linked:TriggerInput("Reference", nil) + end + end + + local stage0 = "Secondary: Link HoloGrid with HoloEmitter or reference entity, Reload: Unlink HoloEmitter or HoloGrid" + local stage1 = "Select the HoloGrid to link to." + local stage2 = "Select the Holo Emitter or reference entity to link to." + + do -- wire_holoemitter + WireToolSetup.open( "holoemitter", "HoloEmitter", "gmod_wire_holoemitter", nil, "HoloEmitters" ) + + if CLIENT then + language.Add( "Tool_wire_holoemitter_name", "Holographic Emitter Tool (Wire)" ) + language.Add( "Tool_wire_holoemitter_desc", "The emitter required for holographic projections" ) + language.Add( "Tool_wire_holoemitter_0", "Primary: Create emitter, "..stage0 ) + language.Add( "Tool_wire_holoemitter_1", stage1 ) + language.Add( "Tool_wire_holoemitter_2", stage2 ) + language.Add( "Tool_wire_holoemitter_showbeams", "Show Point->Point beams" ) + language.Add( "Tool_wire_holoemitter_groundbeams", "Show Emitter->Point beams" ) + language.Add( "Tool_wire_holoemitter_size", "Point size" ) + language.Add( "Tool_wire_holoemitter_minimum_fade_rate", "CLIENT: Minimum Fade Rate - Applied to all holoemitters" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_holoemitters", "You've hit sound holoemitters limit!" ) + + if SERVER then + function TOOL:GetConVars() + return self:GetClientNumber( "r" ), + self:GetClientNumber( "g" ), + self:GetClientNumber( "b" ), + self:GetClientNumber( "a" ), + util.tobool( self:GetClientNumber( "showbeams" ) ), + util.tobool( self:GetClientNumber( "groundbeams" ) ), + self:GetClientNumber( "size" ) + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireHoloemitter( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.RightClick = HoloRightClick + TOOL.Reload = HoloReload + TOOL.Model = "models/jaanus/wiretool/wiretool_range.mdl" + TOOL.NoGhostOn = { "gmod_wire_hologrid" } + TOOL.ClientConVar = { + r = 255, + g = 255, + b = 255, + a = 255, + showbeams = 1, + groundbeams = 1, + size = 4, + weld = 1, + } + + function TOOL.BuildCPanel( panel ) + WireToolHelpers.MakePresetControl(panel, "wire_holoemitter") + panel:CheckBox("#Tool_wire_holoemitter_showbeams", "wire_holoemitter_showbeams") + panel:CheckBox("#Tool_wire_holoemitter_groundbeams", "wire_holoemitter_groundbeams") + panel:NumSlider("#Tool_wire_holoemitter_size","wire_holoemitter_size", 1, 32, 1) + + panel:AddControl( "Color", { + Label = "Color", + Red = "wire_holoemitter_r", + Green = "wire_holoemitter_g", + Blue = "wire_holoemitter_b", + Alpha = "wire_holoemitter_a", + ShowAlpha = 1, + ShowHSV = 1, + ShowRGB = 1, + Multiplier = 255, + }) + + if not SinglePlayer() then + panel:NumSlider("#Tool_wire_holoemitter_minimum_fade_rate", "cl_wire_holoemitter_minfaderate", 0.1, 100, 1) + end + panel:CheckBox("Weld", "wire_holoemitter_weld") + end + end -- wire_holoemitter + + do -- wire_hologrid + WireToolSetup.open( "hologrid", "HoloGrid", "gmod_wire_hologrid", nil, "HoloGrids" ) + + if CLIENT then + language.Add( "Tool_wire_hologrid_name", "Holographic Grid Tool (Wire)" ) + language.Add( "Tool_wire_hologrid_desc", "The grid to aid in holographic projections" ) + language.Add( "Tool_wire_hologrid_0", "Primary: Create grid, "..stage0 ) + language.Add( "Tool_wire_hologrid_1", stage1 ) + language.Add( "Tool_wire_hologrid_2", stage2 ) + language.Add( "Tool_wire_hologrid_usegps", "Use GPS coordinates" ) + end + WireToolSetup.BaseLang() + + WireToolSetup.SetupMax( 20, "wire_hologrids", "You've hit sound hologrids limit!" ) + + if SERVER then + function TOOL:GetConVars() + return util.tobool(self:GetClientNumber( "usegps" )) + end + + function TOOL:MakeEnt( ply, model, Ang, trace ) + return MakeWireHologrid( ply, trace.HitPos, Ang, model, self:GetConVars() ) + end + end + + TOOL.RightClick = HoloRightClick + TOOL.Reload = HoloReload + TOOL.Model = "models/jaanus/wiretool/wiretool_siren.mdl" + TOOL.NoGhostOn = { "sbox_maxwire_holoemitters" } + TOOL.NoLeftOnClass = true + TOOL.ClientConVar ={ + usegps = 0, + weld = 1, + } + + function TOOL.BuildCPanel( panel ) + panel:CheckBox("#Tool_wire_hologrid_usegps", "wire_hologrid_usegps") + panel:CheckBox("Weld", "wire_hologrid_weld") + end + end -- wire_hologrid +end -- holography diff --git a/lua/wire/stools/gates.lua b/lua/wire/stools/gates.lua new file mode 100644 index 0000000000..e548477541 --- /dev/null +++ b/lua/wire/stools/gates.lua @@ -0,0 +1,197 @@ +AddCSLuaFile( "gates.lua" ) + + +if CLIENT then + language.Add( "Tool_wire_gate_arithmetic_name", "Arithmetic Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_arithmetic_desc", "Spawns an arithmetic gate for use with the wire system." ) + language.Add( "Tool_wire_gate_arithmetic_0", "Primary: Create/Update Arithmetic Gate" ) + + language.Add( "Tool_wire_gate_rd_name", "Ranger Data Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_rd_desc", "Spawns a ranger data gate for use with the wire system." ) + language.Add( "Tool_wire_gate_rd_0", "Primary: Create/Update Ranger Data Gate" ) + + language.Add( "Tool_wire_gate_vector_name", "Vector Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_vector_desc", "Spawns a vector gate for use with the wire system." ) + language.Add( "Tool_wire_gate_vector_0", "Primary: Create/Update Vector Gate" ) + + language.Add( "Tool_wire_gate_angle_name", "Angle Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_angle_desc", "Spawns an angle gate for use with the wire system." ) + language.Add( "Tool_wire_gate_angle_0", "Primary: Create/Update Angle Gate" ) + + language.Add( "Tool_wire_gate_string_name", "String Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_string_desc", "Spawns a string gate for use with the wire system." ) + language.Add( "Tool_wire_gate_string_0", "Primary: Create/Update String Gate" ) + + language.Add( "Tool_wire_gate_entity_name", "Entity Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_entity_desc", "Spawns an entity gate for use with the wire system." ) + language.Add( "Tool_wire_gate_entity_0", "Primary: Create/Update Entity Gate" ) + + language.Add( "Tool_wire_gate_comparison_name", "Comparison Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_comparison_desc", "Spawns a comparison gate for use with the wire system." ) + language.Add( "Tool_wire_gate_comparison_0", "Primary: Create/Update Comparison Gate" ) + + language.Add( "Tool_wire_gate_duplexer_name", "Duplexer Chip Tool (Wire)" ) + language.Add( "Tool_wire_gate_duplexer_desc", "Spawns a duplexer chip for use with the wire system." ) + language.Add( "Tool_wire_gate_duplexer_0", "Primary: Create/Update Duplexer Chip" ) + + language.Add( "Tool_wire_gate_logic_name", "Logic Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_logic_desc", "Spawns a logic gate for use with the wire system." ) + language.Add( "Tool_wire_gate_logic_0", "Primary: Create/Update Logic Gate" ) + + language.Add( "Tool_wire_gate_memory_name", "Memory Chip Tool (Wire)" ) + language.Add( "Tool_wire_gate_memory_desc", "Spawns a memory chip for use with the wire system." ) + language.Add( "Tool_wire_gate_memory_0", "Primary: Create/Update Memory Chip" ) + + language.Add( "Tool_wire_gate_selection_name", "Selection Chip Tool (Wire)" ) + language.Add( "Tool_wire_gate_selection_desc", "Spawns a selection chip for use with the wire system." ) + language.Add( "Tool_wire_gate_selection_0", "Primary: Create/Update Selection Chip" ) + + language.Add( "Tool_wire_gate_time_name", "Time Chip Tool (Wire)" ) + language.Add( "Tool_wire_gate_time_desc", "Spawns a time chip for use with the wire system." ) + language.Add( "Tool_wire_gate_time_0", "Primary: Create/Update Time Chip" ) + + language.Add( "Tool_wire_gate_trig_name", "Trig Gate Tool (Wire)" ) + language.Add( "Tool_wire_gate_trig_desc", "Spawns a trig gate for use with the wire system." ) + language.Add( "Tool_wire_gate_trig_0", "Primary: Create/Update Trig Gate" ) + + language.Add( "Tool_wire_gates_name", "Gate Tool (Wire)" ) + language.Add( "Tool_wire_gates_desc", "Spawns a gate for use with the wire system." ) + language.Add( "Tool_wire_gates_0", "Primary: Create/Update Gate" ) + + language.Add( "WireGatesTool_action", "Gate action" ) + language.Add( "WireGatesTool_noclip", "NoCollide" ) + language.Add( "WireGatesTool_weld", "Weld" ) + language.Add( "sboxlimit_wire_gates", "You've hit your gates limit!" ) + language.Add( "undone_gmod_wire_gate", "Undone wire gate" ) + language.Add( "Cleanup_gmod_wire_gate", "Wire Gates" ) + language.Add( "Cleaned_gmod_wire_gate", "Cleaned up wire gates" ) +end + +if SERVER then + CreateConVar('sbox_maxwire_gates', 30) + CreateConVar('sbox_maxwire_gate_comparisons', 30) + CreateConVar('sbox_maxwire_gate_duplexer', 16) + CreateConVar('sbox_maxwire_gate_logics', 30) + CreateConVar('sbox_maxwire_gate_memorys', 30) + CreateConVar('sbox_maxwire_gate_selections', 30) + CreateConVar('sbox_maxwire_gate_times', 30) + CreateConVar('sbox_maxwire_gate_trigs', 30) + ModelPlug_Register("gate") +end + +cleanup.Register("wire_gates") + +local base_tool = { + Category = "Wire - Control", + WireClass = "gmod_wire_gate", + LeftClick_Make = WireToolMakeGate, + ClientConVar = { + noclip = 0, + model = "models/jaanus/wiretool/wiretool_gate.mdl", + weld = 1, + }, +} + +local function openTOOL() + TOOL = WireToolObj:Create() + table.Merge(TOOL, base_tool) +end + +local function GateGetModel(self) + return self:GetOwner():GetInfo( "wire_gates_model" ) +end + +local function buildTOOL( s_name, s_def ) + openTOOL() + local s_mode = "wire_gate_"..string.lower(s_name) + TOOL.Mode = s_mode + TOOL.Name = "Gate - "..s_name + TOOL.ClientConVar.action = s_def + TOOL.GetModel = GateGetModel + if CLIENT then + TOOL.BuildCPanel = function(panel) + panel:CheckBox("#WireGatesTool_noclip", s_mode.."_noclip") + panel:CheckBox("#WireGatesTool_weld", "wire_gates_weld") + local Actions = { + Label = "#WireGatesTool_action", + MenuButton = "0", + Height = 180, + Options = {} + } + for k,v in pairs(GateActions) do + if(v.group == s_name) then + Actions.Options[v.name or "No Name"] = {} + Actions.Options[v.name or "No Name"][s_mode.."_action"] = k + end + end + panel:AddControl("ListBox", Actions) + WireDermaExts.ModelSelect(panel, "wire_gates_model", list.Get("Wire_gate_Models"), 3, true) + end + end + WireToolSetup.close() +end + + +buildTOOL( "Arithmetic", "+" ) + +buildTOOL( "Comparison", "<" ) + +buildTOOL( "Ranger", "trace" ) + +buildTOOL( "Vector", "addition" ) + +buildTOOL( "Angle", "addition" ) + +buildTOOL( "String", "index" ) + +buildTOOL( "Entity", "owner" ) + +buildTOOL( "Array", "table_8duplexer" ) + +buildTOOL( "Logic", "and" ) + +buildTOOL( "Memory", "latch" ) + +buildTOOL( "Selection", "min" ) + +buildTOOL( "Time", "timer" ) + +buildTOOL( "Trig", "sin" ) + + +openTOOL() +TOOL.Mode = "wire_gates" +TOOL.Category = "Wire - Tools" +TOOL.Name = "Gate" +TOOL.ClientConVar.action = "+" + +function TOOL.BuildCPanel(panel) + WireDermaExts.ModelSelect(panel, "wire_gates_model", list.Get("Wire_gate_Models"), 3, true) + + panel:CheckBox("#WireGatesTool_noclip", "wire_gates_noclip") + panel:CheckBox("#WireGatesTool_weld", "wire_gates_weld") + + local tree = vgui.Create( "DTree" ) + tree:SetTall( 400 ) + panel:AddPanel( tree ) + + for gatetype, gatefuncs in pairs(WireGatesSorted) do + local node = tree:AddNode( gatetype.." Gates" ) + table.SortByMember( gatefuncs, "name", true ) + for k,v in pairs(gatefuncs) do + local cnode = node:AddNode( v.name or "No Name" ) + cnode.myname = v.name + cnode.myaction = k + function cnode:DoClick() + RunConsoleCommand( "wire_gates_action", self.myaction ) + end + cnode.Icon:SetImage( "gui/silkicons/newspaper" ) + end + node.ChildNodes:SortByMember( "myname", false ) + end + +end + +WireToolSetup.close() + +base_tool = nil diff --git a/lua/wire/stools/io.lua b/lua/wire/stools/io.lua new file mode 100644 index 0000000000..cb586f9e21 --- /dev/null +++ b/lua/wire/stools/io.lua @@ -0,0 +1,346 @@ +AddCSLuaFile( "io.lua" ) +WireToolSetup.setCategory( "I/O" ) + +do -- wire_adv_input + WireToolSetup.open( "adv_input", "Adv. Input", "gmod_wire_adv_input", WireToolMakeAdvInput ) + + if CLIENT then + language.Add( "Tool_wire_adv_input_name", "Adv. Input Tool (Wire)" ) + language.Add( "Tool_wire_adv_input_desc", "Spawns a adv. input for use with the wire system." ) + language.Add( "Tool_wire_adv_input_0", "Primary: Create/Update Adv. Input" ) + language.Add( "WireAdvInputTool_keymore", "Increase:" ) + language.Add( "WireAdvInputTool_keyless", "Decrease:" ) + language.Add( "WireAdvInputTool_toggle", "Toggle" ) + language.Add( "WireAdvInputTool_value_min", "Minimum:" ) + language.Add( "WireAdvInputTool_value_max", "Maximum:" ) + language.Add( "WireAdvInputTool_value_start", "Start at:" ) + language.Add( "WireAdvInputTool_speed", "Change per second:" ) + language.Add( "sboxlimit_wire_adv_inputs", "You've hit wired adv input limit!" ) + end + WireToolSetup.BaseLang("Adv. Inputs") + + if SERVER then + CreateConVar('sbox_maxwire_adv_inputs',20) + ModelPlug_Register("Numpad") + end + + TOOL.ClientConVar = { + keymore = "3", + keyless = "1", + toggle = "0", + value_min = "0", + value_max = "10", + value_start = "5", + speed = "1", + model = "models/beer/wiremod/numpad.mdl", + modelsize = "" + } + TOOL.ModelInfo = {"","",""} + + function TOOL:Think() + if self.ModelInfo[1]!= self:GetClientInfo( "model" ) || self.ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + self.ModelInfo[1] = self:GetClientInfo( "model" ) + self.ModelInfo[2] = self:GetClientInfo( "modelsize" ) + self.ModelInfo[3] = self.ModelInfo[1] + if (self.ModelInfo[1] && self.ModelInfo[2] && self.ModelInfo[2]!="") then + local test = string.sub(self.ModelInfo[1], 1, -5) .. self.ModelInfo[2] .. string.sub(self.ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + self.ModelInfo[3] = test + end + end + self:MakeGhostEntity( self.ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( self.ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhost( self.GhostEntity ) + end + + function TOOL.BuildCPanel( CPanel ) + CPanel:AddControl("Label", {Text = "Model Size (if available)"}) + CPanel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_adv_input_modelsize = "" }, + ["mini"] = { wire_adv_input_modelsize = "_mini" }, + ["nano"] = { wire_adv_input_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(CPanel, "Numpad", "wire_adv_input", "#ToolWireIndicator_Model") + CPanel:AddControl( "Numpad", {Label = "#WireAdvInputTool_keymore", Command = "wire_adv_input_keymore"}) + CPanel:AddControl( "Numpad", {Label = "#WireAdvInputTool_keyless", Command = "wire_adv_input_keyless"}) + CPanel:CheckBox("#WireAdvInputTool_toggle", "wire_adv_input_toggle") + CPanel:NumSlider("#WireAdvInputTool_value_min", "wire_adv_input_value_min", -50, 50, 0) + CPanel:NumSlider("#WireAdvInputTool_value_max", "wire_adv_input_value_max", -50, 50, 0) + CPanel:NumSlider("#WireAdvInputTool_value_start", "wire_adv_input_value_start", -50, 50, 0) + CPanel:NumSlider("#WireAdvInputTool_speed", "wire_adv_input_speed", 0.1, 50, 1) + end +end -- wire_adv_input + + + +do -- wire_adv_pod + WireToolSetup.open( "adv_pod", "Advanced Pod Controller", "gmod_wire_adv_pod", WireToolMakeAdvPod ) + + if CLIENT then + language.Add("Tool_wire_adv_pod_name", "Advanced Pod Controller Tool (Wire)") + language.Add("Tool_wire_adv_pod_desc", "Spawn/link a Wire Advanced Pod controller.") + language.Add("Tool_wire_adv_pod_0", "Primary: Create Advanced Pod controller. Secondary: Link Advanced controller.") + language.Add("Tool_wire_adv_pod_1", "Now select the pod to link to.") + end + WireToolSetup.BaseLang("Adv. Pod Controllers") + + if SERVER then + ModelPlug_Register("podctrlr") + end + + TOOL.NoLeftOnClass = true + TOOL.ClientConVar = { + model = "models/jaanus/wiretool/wiretool_siren.mdl" + } + + function TOOL:RightClick(trace) + if self:GetStage() == 0 and trace.Entity:GetClass() == "gmod_wire_adv_pod" then + self.PodCont = trace.Entity + self:SetStage(1) + return true + elseif self:GetStage() == 1 and trace.Entity.GetPassenger then + local owner = self:GetOwner() + if self.PodCont:Link(trace.Entity,false) then + owner:PrintMessage(HUD_PRINTTALK,"Adv. Pod linked!") + else + owner:PrintMessage(HUD_PRINTTALK,"Link failed!") + end + self:SetStage(0) + self.PodCont = nil + return true + else + return false + end + end + + function TOOL:Reload(trace) + self:SetStage(0) + self.PodCont = nil + end + + function TOOL.BuildCPanel(panel) + ModelPlug_AddToCPanel(panel, "podctrlr", "wire_adv_pod", nil, nil, nil, 1) + end +end -- wire_adv_pod + + + +do --wire_button + WireToolSetup.open( "button", "Button", "gmod_wire_button", WireToolMakeButton ) + + if CLIENT then + language.Add( "Tool_wire_button_name", "Button Tool (Wire)" ) + language.Add( "Tool_wire_button_desc", "Spawns a button for use with the wire system." ) + language.Add( "Tool_wire_button_0", "Primary: Create/Update Button" ) + language.Add( "WireButtonTool_toggle", "Toggle" ) + language.Add( "WireButtonTool_entityout", "Output Entity" ) + language.Add( "WireButtonTool_value_on", "Value On:" ) + language.Add( "WireButtonTool_value_off", "Value Off:" ) + language.Add( "sboxlimit_wire_buttons", "You've hit wired buttons limit!" ) + end + WireToolSetup.BaseLang("Buttons") + + if SERVER then + CreateConVar('sbox_maxwire_buttons', 20) + ModelPlug_Register("button") + end + + TOOL.ClientConVar = { + model = "models/props_c17/clock01.mdl", + model_category = "button", + toggle = "0", + value_off = "0", + value_on = "1", + description = "", + entityout = "0" + } + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_button") + + ModelPlug_AddToCPanel_Multi( + panel, + { button = "Normal", + button_small = "Small" + }, + "wire_button", + "#Button_Model", nil, "#Button_Model", 6 + ) + panel:CheckBox("#WireButtonTool_toggle", "wire_button_toggle") + panel:CheckBox("#WireButtonTool_entityout", "wire_button_entityout") + panel:NumSlider("#WireButtonTool_value_on", "wire_button_value_on", -10, 10, 1) + panel:NumSlider("#WireButtonTool_value_off", "wire_button_value_off", -10, 10, 1) + end +end --wire_button + + + +do -- wire_dual_input + WireToolSetup.open( "dual_input", "Dual Input", "gmod_wire_dual_input", WireToolMakeDualInput ) + + if CLIENT then + language.Add( "Tool_wire_dual_input_name", "Dual Input Tool (Wire)" ) + language.Add( "Tool_wire_dual_input_desc", "Spawns a daul input for use with the wire system." ) + language.Add( "Tool_wire_dual_input_0", "Primary: Create/Update Input" ) + language.Add( "WireDualInputTool_keygroup", "Key 1:" ) + language.Add( "WireDualInputTool_keygroup2", "Key 2:" ) + language.Add( "WireDualInputTool_toggle", "Toggle" ) + language.Add( "WireDualInputTool_value_on", "Value 1 On:" ) + language.Add( "WireDualInputTool_value_on2", "Value 2 On:" ) + language.Add( "WireDualInputTool_value_off", "Value Off:" ) + language.Add( "sboxlimit_wire_dual_inputs", "You've hit inputs limit!" ) + language.Add( "undone_gmod_wire_dual_input", "Undone Wire Dual Input" ) + language.Add( "Cleanup_gmod_wire_dual_input", "Wire Dual Inputs" ) + language.Add( "Cleaned_gmod_wire_dual_input", "Cleaned Up Wire Dual Inputs" ) + end + WireToolSetup.BaseLang("Dual Inputs") + + if SERVER then + CreateConVar('sbox_maxwire_dual_inputs', 20) + ModelPlug_Register("Numpad") + end + + TOOL.ClientConVar = { + keygroup = 7, + keygroup2 = 4, + toggle = 0, + value_off = 0, + value_on = 1, + value_on2 = -1, + model = "models/beer/wiremod/numpad.mdl", + modelsize = "" + } + TOOL.ModelInfo = {"","",""} + + function TOOL:Think() + if self.ModelInfo[1]!= self:GetClientInfo( "model" ) || self.ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + self.ModelInfo[1] = self:GetClientInfo( "model" ) + self.ModelInfo[2] = self:GetClientInfo( "modelsize" ) + self.ModelInfo[3] = self.ModelInfo[1] + if (self.ModelInfo[1] && self.ModelInfo[2] && self.ModelInfo[2]!="") then + local test = string.sub(self.ModelInfo[1], 1, -5) .. self.ModelInfo[2] .. string.sub(self.ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + self.ModelInfo[3] = test + end + end + self:MakeGhostEntity( self.ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( self.ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhost( self.GhostEntity ) + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_dual_input") + + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_dual_input_modelsize = "" }, + ["mini"] = { wire_dual_input_modelsize = "_mini" }, + ["nano"] = { wire_dual_input_modelsize = "_nano" } + } + }) + + ModelPlug_AddToCPanel(panel, "Numpad", "wire_dual_input", "#ToolWireIndicator_Model") + + panel:AddControl("Numpad", { + Label = "#WireDualInputTool_keygroup", + Command = "wire_dual_input_keygroup" + }) + + panel:AddControl("Numpad", { + Label = "#WireDualInputTool_keygroup2", + Command = "wire_dual_input_keygroup2" + }) + + panel:CheckBox("#WireDualInputTool_toggle", "wire_dual_input_toggle") + panel:NumSlider("#WireDualInputTool_value_on", "wire_dual_input_value_on", -10, 10, 1) + panel:NumSlider("#WireDualInputTool_value_off", "wire_dual_input_value_off", -10, 10, 1) + panel:NumSlider("#WireDualInputTool_value_on2", "wire_dual_input_value_on2", -10, 10, 1) + end +end -- wire_dual_input + + + +do -- wire_input + WireToolSetup.open( "input", "Numpad Input", "gmod_wire_input", WireToolMakeInput ) + + if CLIENT then + language.Add( "Tool_wire_input_name", "Input Tool (Wire)" ) + language.Add( "Tool_wire_input_desc", "Spawns a input for use with the wire system." ) + language.Add( "Tool_wire_input_0", "Primary: Create/Update Input" ) + language.Add( "WireInputTool_keygroup", "Key:" ) + language.Add( "WireInputTool_toggle", "Toggle" ) + language.Add( "WireInputTool_value_on", "Value On:" ) + language.Add( "WireInputTool_value_off", "Value Off:" ) + language.Add( "sboxlimit_wire_inputs", "You've hit inputs limit!" ) + end + WireToolSetup.BaseLang("Inputs") + + if SERVER then + CreateConVar('sbox_maxwire_inputs', 20) + ModelPlug_Register("Numpad") + end + + TOOL.ClientConVar = { + keygroup = 7, + toggle = 0, + value_off = 0, + value_on = 1, + model = "models/beer/wiremod/numpad.mdl", + modelsize = "" + } + TOOL.ModelInfo = {"","",""} + + function TOOL:Think() + if self.ModelInfo[1]!= self:GetClientInfo( "model" ) || self.ModelInfo[2]!= self:GetClientInfo( "modelsize" ) then + self.ModelInfo[1] = self:GetClientInfo( "model" ) + self.ModelInfo[2] = self:GetClientInfo( "modelsize" ) + self.ModelInfo[3] = self.ModelInfo[1] + if (self.ModelInfo[1] && self.ModelInfo[2] && self.ModelInfo[2]!="") then + local test = string.sub(self.ModelInfo[1], 1, -5) .. self.ModelInfo[2] .. string.sub(self.ModelInfo[1], -4) + if (util.IsValidModel(test) && util.IsValidProp(test)) then + self.ModelInfo[3] = test + end + end + self:MakeGhostEntity( self.ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + if !self.GhostEntity || !self.GhostEntity:IsValid() || !self.GhostEntity:GetModel() then + self:MakeGhostEntity( self.ModelInfo[3], Vector(0,0,0), Angle(0,0,0) ) + end + self:UpdateGhost( self.GhostEntity ) + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_input") + panel:AddControl("Label", {Text = "Model Size (if available)"}) + panel:AddControl("ComboBox", { + Label = "Model Size", + MenuButton = 0, + Options = { + ["normal"] = { wire_input_modelsize = "" }, + ["mini"] = { wire_input_modelsize = "_mini" }, + ["nano"] = { wire_input_modelsize = "_nano" } + } + }) + ModelPlug_AddToCPanel(panel, "Numpad", "wire_input", "#ToolWireIndicator_Model") + panel:AddControl("Numpad", { + Label = "#WireInputTool_keygroup", + Command = "wire_input_keygroup" + }) + panel:CheckBox("#WireInputTool_toggle", "wire_input_toggle") + panel:NumSlider("#WireInputTool_value_on", "wire_input_value_on", -10, 10, 1) + panel:NumSlider("#WireInputTool_value_off", "wire_input_value_off", -10, 10, 1) + end +end -- wire_input diff --git a/lua/wire/stools/physics.lua b/lua/wire/stools/physics.lua new file mode 100644 index 0000000000..54f936d5e5 --- /dev/null +++ b/lua/wire/stools/physics.lua @@ -0,0 +1,933 @@ +AddCSLuaFile( "physics.lua" ) +WireToolSetup.setCategory( "Physics" ) + +do --wire_weight + WireToolSetup.open( "weight", "Weight", "gmod_wire_weight", WireToolMakeWeight ) + + if CLIENT then + language.Add( "Tool_wire_weight_name", "Weight Tool (Wire)" ) + language.Add( "Tool_wire_weight_desc", "Spawns a weight." ) + language.Add( "Tool_wire_weight_0", "Primary: Create/Update weight" ) + language.Add( "WireDataWeightTool_weight", "Weight:" ) + language.Add( "sboxlimit_wire_weights", "You've hit weights limit!" ) + end + WireToolSetup.BaseLang("Weights") + + if SERVER then + CreateConVar('sbox_maxwire_weights', 20) + ModelPlug_Register("weight") + end + + TOOL.ClientConVar = {model = "models/props_interiors/pot01a.mdl"} + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakeModelSel(panel, "wire_weight") + end +end --wire_weight + + +do --wire_simple_explosive + WireToolSetup.open( "simple_explosive", "Explosives (Simple)", "gmod_wire_simple_explosive", WireToolMakeExplosivesSimple ) + TOOL.ConfigName = nil -- eeeeeewwwwwwwwwwwwww D: + + if CLIENT then + language.Add( "Tool_wire_simple_explosive_name", "Simple Wired Explosives Tool" ) + language.Add( "Tool_wire_simple_explosive_desc", "Creates a simple explosives for wire system." ) + language.Add( "Tool_wire_simple_explosive_0", "Left click to place the bomb. Right click update." ) + language.Add( "WireSimpleExplosiveTool_Model", "Model:" ) + language.Add( "WireSimpleExplosiveTool_modelman", "Manual model selection:" ) + language.Add( "WireSimpleExplosiveTool_usemodelman", "Use manual model selection:" ) + language.Add( "WireSimpleExplosiveTool_tirgger", "Trigger value:" ) + language.Add( "WireSimpleExplosiveTool_damage", "Damage:" ) + language.Add( "WireSimpleExplosiveTool_remove", "Remove on explosion" ) + language.Add( "WireSimpleExplosiveTool_doblastdamage", "Do blast damage" ) + language.Add( "WireSimpleExplosiveTool_radius", "Blast radius:" ) + language.Add( "WireSimpleExplosiveTool_freeze", "Freeze" ) + language.Add( "WireSimpleExplosiveTool_weld", "Weld" ) + language.Add( "WireSimpleExplosiveTool_noparentremove", "Don't remove on parent remove" ) + language.Add( "WireSimpleExplosiveTool_nocollide", "No collide all but world" ) + language.Add( "WireSimpleExplosiveTool_weight", "Weight:" ) + language.Add( "sboxlimit_wire_simple_explosive", "You've hit wired explosives limit!" ) + end + WireToolSetup.BaseLang("SimpleExplosives") + + if SERVER then + CreateConVar('sbox_maxwire_simple_explosive', 30) + end + + TOOL.ClientConVar = { + model = "models/props_c17/oildrum001_explosive.mdl", + modelman = "", + tirgger = 1, -- Current tirgger + damage = 200, -- Damage to inflict + doblastdamage = 1, + radius = 300, + removeafter = 0, + freeze = 0, + weld = 1, + weight = 400, + nocollide = 0, + noparentremove = 0, + } + + function TOOL:GetSelModel( showerr ) + local model = self:GetClientInfo( "model" ) + + if (model == "usemanmodel") then + local _modelman = self:GetClientInfo( "modelman" ) + if (_modelman && string.len(_modelman) > 0) then + model = _modelman + else + local message = "You need to define a model." + if (showerr) then + self:GetOwner():PrintMessage(3, message) + self:GetOwner():PrintMessage(2, message) + end + return false + end + elseif (model == "usereloadmodel") then + if (self.reloadmodel && string.len(self.reloadmodel) > 0) then + model = self.reloadmodel + else + local message = "You need to select a model model." + if (showerr) then + self:GetOwner():PrintMessage(3, message) + self:GetOwner():PrintMessage(2, message) + end + return false + end + end + + if (not util.IsValidModel(model)) then + --something fucked up, notify user of that + local message = "This is not a valid model."..model + if (showerr) then + self:GetOwner():PrintMessage(3, message) + self:GetOwner():PrintMessage(2, message) + end + return false + end + if (not util.IsValidProp(model)) then return false end + + return model + end + + function TOOL:RightClick( trace ) + local ply = self:GetOwner() + --shot an explosive, update it instead + if ( trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_wire_simple_explosive" && trace.Entity:GetTable().pl == ply ) then + local _trigger = self:GetClientNumber( "tirgger" ) + local _damage = math.Clamp( self:GetClientNumber( "damage" ), 0, 1500 ) + local _removeafter = self:GetClientNumber( "removeafter" ) == 1 + local _doblastdamage = self:GetClientNumber( "doblastdamage" ) == 1 + local _radius = math.Clamp( self:GetClientNumber( "radius" ), 0, 10000 ) + local _weld = self:GetClientNumber( "weld" ) == 1 + local _nocollide = self:GetClientNumber( "nocollide" ) == 1 + local _weight = math.Max(self:GetClientNumber( "weight" ), 1) + + trace.Entity:Setup( _damage, _delaytime, _removeafter, _doblastdamage, _radius, _nocollide ) + + local ttable = { + nocollide = _nocollide, + key = _trigger, + damage = _damage, + removeafter = _removeafter, + doblastdamage = _doblastdamage, + radius = _radius + } + table.Merge( trace.Entity:GetTable(), ttable ) + + trace.Entity:GetPhysicsObject():SetMass(_weight) + duplicator.StoreEntityModifier( trace.Entity, "MassMod", {Mass = _weight} ) + + return true + end + + end + + function TOOL:Reload( trace ) + --get the model of what was shot and set our reloadmodel to that + --model info getting code mostly copied from OverloadUT's What Is That? STool + if !trace.Entity then return false end + local ent = trace.Entity + local ply = self:GetOwner() + local class = ent:GetClass() + if class == "worldspawn" then + return false + else + local model = ent:GetModel() + local message = "Model selected: "..model + self.reloadmodel = model + ply:PrintMessage(3, message) + ply:PrintMessage(2, message) + end + return true + end +end --wire_simple_explosive + + +do --wire_wheel + WireToolSetup.open( "wheel", "Wheel", "gmod_wire_wheel", WireToolMakeWheel ) + + if CLIENT then + language.Add( "Tool_wire_wheel_name", "Wheel Tool (wire)" ) + language.Add( "Tool_wire_wheel_desc", "Attaches a wheel to something." ) + language.Add( "Tool_wire_wheel_0", "Click on a prop to attach a wheel." ) + + language.Add( "WireWheelTool_group", "Input value to go forward:" ) + language.Add( "WireWheelTool_group_reverse", "Input value to go in reverse:" ) + language.Add( "WireWheelTool_group_stop", "Input value for no acceleration:" ) + language.Add( "WireWheelTool_group_desc", "All these values need to be different." ) + + language.Add( "undone_WireWheel", "Undone Wire Wheel" ) + language.Add( "Cleanup_wire_wheels", "Wired Wheels" ) + language.Add( "Cleaned_wire_wheels", "Cleaned up all Wired Wheels" ) + language.Add( "SBoxLimit_wire_wheels", "You've reached the wired wheels limit!" ) + end + WireToolSetup.BaseLang("Wheels") + + if SERVER then + CreateConVar('sbox_maxwire_wheels', 30) + end + + TOOL.ClientConVar = { + torque = 3000, + friction = 1, + nocollide = 1, + forcelimit = 0, + fwd = 1, -- Forward + bck = -1, -- Back + stop = 0, -- Stop + } + + --[[--------------------------------------------------------- + Apply new values to the wheel + ---------------------------------------------------------]] + function TOOL:RightClick( trace ) + if trace.Entity and trace.Entity:GetClass() ~= "gmod_wire_wheel" then return false end + + if CLIENT then return true end + + local wheelEnt = trace.Entity + + -- Only change your own wheels.. + if wheelEnt:GetTable():GetPlayer():IsValid() and + wheelEnt:GetTable():GetPlayer() != self:GetOwner() then + return false + end + + -- Get client's CVars + local torque = self:GetClientNumber( "torque" ) + local toggle = self:GetClientNumber( "toggle" ) != 0 + local fwd = self:GetClientNumber( "fwd" ) + local bck = self:GetClientNumber( "bck" ) + local stop = self:GetClientNumber( "stop" ) + + wheelEnt:SetTorque( torque ) + wheelEnt:SetFwd( fwd ) + wheelEnt:SetBck( bck ) + wheelEnt:SetStop( stop ) + wheelEnt:UpdateOverlayText() + + return true + end + + function TOOL:UpdateGhostWireWheel( ent, player ) + if not (ent and ent:IsValid()) then return end + + local tr = utilx.GetPlayerTrace( player, player:GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if not trace.Hit then return end + + if trace.Entity:IsPlayer() then + ent:SetNoDraw( true ) + return + end + + local Ang = trace.HitNormal:Angle() + self.wheelAngle + local CurPos = ent:GetPos() + local NearestPoint = ent:NearestPoint( CurPos - (trace.HitNormal * 512) ) + local WheelOffset = CurPos - NearestPoint + + local min = ent:OBBMins() + ent:SetPos( trace.HitPos + trace.HitNormal + WheelOffset ) + ent:SetAngles( Ang ) + ent:SetNoDraw( false ) + end + + --[[--------------------------------------------------------- + Maintains the ghost wheel + ---------------------------------------------------------]] + function TOOL:Think() + + if (!self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != self:GetOwner():GetInfo( "wheel_model" )) then + self.wheelAngle = Angle( tonumber(self:GetOwner():GetInfo( "wheel_rx" )), tonumber(self:GetOwner():GetInfo( "wheel_ry" )), tonumber(self:GetOwner():GetInfo( "wheel_rz" )) ) + self:MakeGhostEntity( self:GetOwner():GetInfo( "wheel_model" ), Vector(0,0,0), Angle(0,0,0) ) + end + + self:UpdateGhostWireWheel( self.GhostEntity, self:GetOwner() ) + + end + + + function TOOL.BuildCPanel( panel ) + -- HEADER + panel:AddControl( "Header", { Text = "#Tool_wire_wheel_name", Description = "#Tool_wire_wheel_desc" } ) + + local Options = { Default = { wire_wheel_torque = "3000", + wire_wheel_friction = "0", + wire_wheel_nocollide = "1", + wire_wheel_forcelimit = "0", + wire_wheel_fwd = "1", + wire_wheel_bck = "-1", + wire_wheel_stop = "0", } } + + local CVars = { "wire_wheel_torque", "wire_wheel_friction", "wire_wheel_nocollide", "wire_wheel_forcelimit", "wire_wheel_fwd", "wire_wheel_bck", "wire_wheel_stop" } + + panel:AddControl( "ComboBox", { Label = "#Presets", + MenuButton = 1, + Folder = "wire_wheel", + Options = Options, + CVars = CVars } ) + + panel:AddControl( "Slider", { Label = "#WireWheelTool_group", + Description = "#WireWheelTool_group_desc", + Type = "Float", + Min = -10, + Max = 10, + Command = "wire_wheel_fwd" } ) + + panel:AddControl( "Slider", { Label = "#WireWheelTool_group_stop", + Description = "#WireWheelTool_group_desc", + Type = "Float", + Min = -10, + Max = 10, + Command = "wire_wheel_stop" } ) + + panel:AddControl( "Slider", { Label = "#WireWheelTool_group_reverse", + Description = "#WireWheelTool_group_desc", + Type = "Float", + Min = -10, + Max = 10, + Command = "wire_wheel_bck" } ) + + panel:AddControl( "PropSelect", { Label = "#WheelTool_model", + ConVar = "wheel_model", + Category = "Wheels", + Models = list.Get( "WheelModels" ) } ) + + panel:AddControl( "Slider", { Label = "#WheelTool_torque", + Description = "#WheelTool_torque_desc", + Type = "Float", + Min = 10, + Max = 10000, + Command = "wire_wheel_torque" } ) + + + panel:AddControl( "Slider", { Label = "#WheelTool_forcelimit", + Description = "#WheelTool_forcelimit_desc", + Type = "Float", + Min = 0, + Max = 50000, + Command = "wire_wheel_forcelimit" } ) + + panel:AddControl( "Slider", { Label = "#WheelTool_friction", + Description = "#WheelTool_friction_desc", + Type = "Float", + Min = 0, + Max = 100, + Command = "wire_wheel_friction" } ) + + panel:AddControl( "CheckBox", { Label = "#WheelTool_nocollide", + Description = "#WheelTool_nocollide_desc", + Command = "wire_wheel_nocollide" } ) + + --[[ This is the stuff that broke the wheels. I hope TAD2020 can fix it some time... + WireToolHelpers.MakePresetControl(panel, "wire_wheel") + panel:NumSlider("#WireWheelTool_group", "wire_wheel_fwd", -10, 10, 1) + panel:NumSlider("#WireWheelTool_group_stop", "wire_wheel_stop", -10, 10, 1) + panel:NumSlider("#WireWheelTool_group_reverse", "wire_wheel_bck", -10, 10, 1) + WireDermaExts.ModelSelect(panel, "wheel_model", list.Get( "WheelModels" ), 3, true) + panel:NumSlider("#WheelTool_torque", "wire_wheel_torque", 10, 10000, 0) + panel:NumSlider("#WheelTool_forcelimit", "wire_wheel_forcelimit", 0, 50000, 0) + panel:NumSlider("#WheelTool_friction", "wire_wheel_friction", 0, 100, 1) + panel:CheckBox("#WheelTool_nocollide", "wire_wheel_nocollide") + ]] + end + +end --wire_wheel + + +do --wire_turret + WireToolSetup.open( "turret", "Turret", "gmod_wire_turret", nil ) + + -- Precache these sounds.. + Sound( "ambient.electrical_zap_3" ) + Sound( "NPC_FloorTurret.Shoot" ) + + -- Add Default Language translation (saves adding it to the txt files) + if CLIENT then + language.Add( "Tool_wire_turret_name", "Turret" ) + language.Add( "Tool_wire_turret_desc", "Throws bullets at things" ) + language.Add( "Tool_wire_turret_0", "Click somewhere to spawn an turret. Click on an existing turret to change it." ) + + language.Add( "Tool_wire_turret_spread", "Bullet Spread" ) + language.Add( "Tool_wire_turret_numbullets", "Bullets per Shot" ) + language.Add( "Tool_wire_turret_force", "Bullet Force" ) + language.Add( "Tool_wire_turret_sound", "Shoot Sound" ) + language.Add( "Tool_wire_turret_tracernum", "Tracer Every x Bullets:" ) + + language.Add( "SBoxLimit_wire_turrets", "You've reached the Turret limit!" ) + end + WireToolSetup.BaseLang("Turrets") + + TOOL.ClientConVar = { + delay = 0.05, + force = 1, + sound = 0, + damage = 10, + spread = 0, + numbullets = 1, + automatic = 1, + tracer = "Tracer", + tracernum = 1, + model = "models/weapons/w_smg1.mdl" + } + + TOOL.GhostAngle = Angle(-90,0,0) + TOOL.GetGhostMin = function() return -2 end + + if SERVER then + CreateConVar('sbox_maxwire_turrets', 30) + end + + function TOOL:LeftClick( trace, worldweld ) + if trace.Entity and trace.Entity:IsPlayer() then return false end + if CLIENT then return true end + + worldweld = worldweld or false + + local ply = self:GetOwner() + + local delay = self:GetClientNumber( "delay" ) + local force = self:GetClientNumber( "force" ) + local sound = self:GetClientInfo( "sound" ) + local tracer = self:GetClientInfo( "tracer" ) + local damage = self:GetClientNumber( "damage" ) + local spread = self:GetClientNumber( "spread" ) + local numbullets = self:GetClientNumber( "numbullets" ) + local tracernum = self:GetClientNumber( "tracernum" ) + local model = self:GetClientInfo( "model" ) + + -- We shot an existing turret - just change its values + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_turret" and trace.Entity.pl == ply then + trace.Entity:SetDamage( damage ) + trace.Entity:SetDelay( delay ) + trace.Entity:SetNumBullets( numbullets ) + trace.Entity:SetSpread( spread ) + trace.Entity:SetForce( force ) + trace.Entity:SetSound( sound ) + trace.Entity:SetTracer( tracer ) + trace.Entity:SetTracerNum( tracernum ) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_turrets" ) then return false end + + trace.HitPos = trace.HitPos + trace.HitNormal * 2 + + local Ang=nil + + local turret = MakeWireTurret( ply, trace.HitPos, Ang, model, delay, damage, force, sound, numbullets, spread, tracer, tracernum ) + + turret:SetAngles( trace.HitNormal:Angle() ) + + local weld = WireLib.Weld(turret, trace.Entity, trace.PhysicsBone, true, false, worldweld) + + undo.Create("WireTurret") + undo.AddEntity( turret ) + undo.AddEntity( weld ) + undo.SetPlayer( ply ) + undo.Finish() + + return true + end + + function TOOL:RightClick( trace ) + return self:LeftClick( trace, true ) + end + + + function TOOL.BuildCPanel( CPanel ) + WireToolHelpers.MakePresetControl(CPanel, "wire_turret") + + -- Shot sounds + local weaponSounds = {Label = "#Tool_wire_turret_sound", MenuButton = 0, Options={}, CVars = {}} + weaponSounds["Options"]["#No Weapon"] = { wire_turret_sound = "" } + weaponSounds["Options"]["#Pistol"] = { wire_turret_sound = "Weapon_Pistol.Single" } + weaponSounds["Options"]["#SMG"] = { wire_turret_sound = "Weapon_SMG1.Single" } + weaponSounds["Options"]["#AR2"] = { wire_turret_sound = "Weapon_AR2.Single" } + weaponSounds["Options"]["#Shotgun"] = { wire_turret_sound = "Weapon_Shotgun.Single" } + weaponSounds["Options"]["#Floor Turret"] = { wire_turret_sound = "NPC_FloorTurret.Shoot" } + weaponSounds["Options"]["#Airboat Heavy"] = { wire_turret_sound = "Airboat.FireGunHeavy" } + weaponSounds["Options"]["#Zap"] = { wire_turret_sound = "ambient.electrical_zap_3" } + + + CPanel:AddControl("ComboBox", weaponSounds ) + + -- Tracer + local TracerType = {Label = "#Tracer", MenuButton = 0, Options={}, CVars = {}} + TracerType["Options"]["#Default"] = { wire_turret_tracer = "Tracer" } + TracerType["Options"]["#AR2 Tracer"] = { wire_turret_tracer = "AR2Tracer" } + TracerType["Options"]["#Airboat Tracer"] = { wire_turret_tracer = "AirboatGunHeavyTracer" } + TracerType["Options"]["#Laser"] = { wire_turret_tracer = "LaserTracer" } + + --Turret Models + local TurretModels = { + ["models/weapons/w_smg1.mdl"] = {}, + ["models/weapons/w_smg_mp5.mdl"] = {}, + ["models/weapons/w_smg_mac10.mdl"] = {}, + ["models/weapons/w_rif_m4a1.mdl"] = {}, + ["models/weapons/w_357.mdl"] = {}, + ["models/weapons/w_shot_m3super90.mdl"] = {} + } + + CPanel:AddControl( "PropSelect", { Label = "#Select Model", + ConVar = "wire_turret_model", + Category = "Wire Turrets", + Models = TurretModels } ) + + CPanel:AddControl("ComboBox", TracerType ) + + -- Various controls that you should play with! + if SinglePlayer() then + CPanel:NumSlider("#Tool_wire_turret_numbullets", "wire_turret_numbullets", 1, 10, 0) + end + CPanel:NumSlider("#Damage", "wire_turret_damage", 0, 100, 0) + CPanel:NumSlider("#Tool_wire_turret_spread", "wire_turret_spread", 0, 1.0, 2) + CPanel:NumSlider("#Tool_wire_turret_force", "wire_turret_force", 0, 500, 1) + + -- The delay between shots. + if SinglePlayer() then + CPanel:NumSlider("#Delay", "wire_turret_delay", 0.01, 1.0, 2) + CPanel:NumSlider("#Tool_wire_turret_tracernum", "wire_turret_tracernum", 0, 15, 0) + else + CPanel:NumSlider("#Delay", "wire_turret_delay", 0.05, 1.0, 2) + end + + end + +end --wire_turret + + +do --wire_forcer + WireToolSetup.open( "forcer", "Forcer", "gmod_wire_forcer", WireToolMakeForcer ) + + if CLIENT then + language.Add( "Tool_wire_forcer_name", "Forcer Tool (Wire)" ) + language.Add( "Tool_wire_forcer_desc", "Spawns a forcer prop for use with the wire system." ) + language.Add( "Tool_wire_forcer_0", "Primary: Create/Update Forcer" ) + language.Add( "sboxlimit_wire_forcers", "You've hit forcers limit!" ) + end + WireToolSetup.BaseLang("Forcers") + + if SERVER then + CreateConVar('sbox_maxwire_forcers', 20) + end + + TOOL.ClientConVar = { + multiplier = 1, + length = 100, + beam = 1, + reaction = 0, + model = "models/jaanus/wiretool/wiretool_siren.mdl" + } + + local forcermodels = { + ["models/jaanus/wiretool/wiretool_grabber_forcer.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {} + } + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_forcer") + WireDermaExts.ModelSelect(panel, "wire_forcer_Model", forcermodels, 1, true) + panel:NumSlider("Force multiplier", "wire_forcer_multiplier", 1, 10000, 0) + panel:NumSlider("Force distance", "wire_forcer_length", 1, 10000, 0) + panel:CheckBox("Show beam", "wire_forcer_beam") + panel:CheckBox("Apply reaction force", "wire_forcer_reaction") + end +end --wire_forcer + + +do --wire_detonator + WireToolSetup.open( "detonator", "Detonator", "gmod_wire_detonator", WireToolMakeDetonator ) + + if CLIENT then + language.Add( "Tool_wire_detonator_name", "Detonator Tool (Wire)" ) + language.Add( "Tool_wire_detonator_desc", "Spawns a Detonator for use with the wire system." ) + language.Add( "Tool_wire_detonator_0", "Primary: Create/Update Detonator" ) + language.Add( "sboxlimit_wire_detonators", "You've hit Detonators limit!" ) + end + WireToolSetup.BaseLang("Detonators") + + if SERVER then + CreateConVar('sbox_maxwire_detonators', 20) + ModelPlug_Register("detonator") + end + + TOOL.ClientConVar = { + damage = 1, + model = "models/props_combine/breenclock.mdl" + } + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_detonator") + panel:NumSlider("#Damage", "wire_detonator_damage", 1, 200, 0) + ModelPlug_AddToCPanel(panel, "detonator", "wire_detonator", nil, nil, true, 1) + end +end --wire_detonator + + +do --wire_grabber + WireToolSetup.open( "grabber", "Grabber", "gmod_wire_grabber", WireToolMakeGrabber ) + + if CLIENT then + language.Add( "Tool_wire_grabber_name", "Grabber Tool (Wire)" ) + language.Add( "Tool_wire_grabber_desc", "Spawns a constant grabber prop for use with the wire system." ) + language.Add( "Tool_wire_grabber_0", "Primary: Create/Update Grabber Secondary: link the grabber to its extra prop that is attached for stabilty" ) + language.Add( "WireGrabberTool_Range", "Max Range:" ) + language.Add( "WireGrabberTool_Gravity", "Disable Gravity" ) + language.Add( "sboxlimit_wire_grabbers", "You've hit grabbers limit!" ) + end + WireToolSetup.BaseLang("Grabbers") + + if SERVER then + CreateConVar('sbox_maxwire_grabbers', 20) + CreateConVar('sbox_wire_grabbers_onlyOwnersProps', 1) + end + + TOOL.ClientConVar = { + model = "models/jaanus/wiretool/wiretool_range.mdl", + Range = 100, + Gravity = 1, + } + + local grabbermodels = { + ["models/jaanus/wiretool/wiretool_grabber_forcer.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_range.mdl"] = {} + } + + function TOOL:GetGhostMin( min ) + if self:GetClientInfo("model") == "models/jaanus/wiretool/wiretool_grabber_forcer.mdl" then + return min.z + 20 + end + return min.z + end + + function TOOL:RightClick( trace ) + if not trace.HitPos then return false end + if CLIENT then return true end + if not trace.Entity or not trace.Entity:IsValid() then return false end + if self.Oldent then + self.Oldent.ExtraProp = trace.Entity + self.Oldent = nil + return true + else + if trace.Entity:GetClass() == "gmod_wire_grabber" then + self.Oldent = trace.Entity + return true + end + end + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_grabber") + WireDermaExts.ModelSelect(panel, "wire_grabber_Model", grabbermodels, 1, true) + panel:CheckBox("#WireGrabberTool_Gravity", "wire_grabber_Gravity") + panel:NumSlider("#WireGrabberTool_Range", "wire_grabber_Range", 1, 10000, 0) + end +end --wire_grabber + + +do --wire_hoverball + WireToolSetup.open( "hoverball", "Hoverball", "gmod_wire_hoverball", WireToolMakeHoverball ) + + if CLIENT then + language.Add( "Tool_wire_hoverball_name", "Wired Hoverball Tool" ) + language.Add( "Tool_wire_hoverball_desc", "Spawns a hoverball for use with the wire system." ) + language.Add( "Tool_wire_hoverball_0", "Primary: Create/Update Hoverball" ) + language.Add( "WireHoverballTool_starton", "Create with hover mode on" ) + language.Add( "sboxlimit_wire_hoverballs", "You've hit wired hover balls limit!" ) + end + WireToolSetup.BaseLang("Hoverballs") + + if SERVER then + CreateConVar('sbox_maxwire_hoverballs', 30) + end + + TOOL.ClientConVar = { + speed = 1, + resistance = 0, + strength = 1, + starton = 1, + } + + TOOL.Model = "models/dav0r/hoverball.mdl" + + function TOOL:GetGhostMin( min, trace ) + if trace.Entity:IsWorld() then + return -8 + end + return 0 + end + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_hoverball") + panel:NumSlider("#Movement Speed", "wire_hoverball_speed", 1, 10, 0) + panel:NumSlider("#Air Resistance", "wire_hoverball_resistance", 1, 10, 0) + panel:NumSlider("#Strength", "wire_hoverball_strength", .1, 10, 2) + panel:CheckBox("#WireHoverballTool_starton", "wire_hoverball_starton") + end +end --wire_hoverball + + +do --wire_igniter + WireToolSetup.open( "igniter", "Igniter", "gmod_wire_igniter", WireToolMakeIgniter ) + + if CLIENT then + language.Add( "Tool_wire_igniter_name", "Igniter Tool (Wire)" ) + language.Add( "Tool_wire_igniter_desc", "Spawns a constant igniter prop for use with the wire system." ) + language.Add( "Tool_wire_igniter_0", "Primary: Create/Update Igniter" ) + language.Add( "WireIgniterTool_trgply", "Allow Player Igniting" ) + language.Add( "WireIgniterTool_Range", "Max Range:" ) + language.Add( "sboxlimit_wire_igniters", "You've hit igniters limit!" ) + end + WireToolSetup.BaseLang("Igniters") + + if SERVER then + CreateConVar('sbox_maxwire_igniters', 20) + CreateConVar('sbox_wire_igniters_maxlen', 30) + CreateConVar('sbox_wire_igniters_allowtrgply',1) + end + + TOOL.ClientConVar = { + trgply = 0, + Range = 2048, + model = "models/jaanus/wiretool/wiretool_siren.mdl", + } + + local ignitermodels = { + ["models/jaanus/wiretool/wiretool_beamcaster.mdl"] = {}, + ["models/jaanus/wiretool/wiretool_siren.mdl"] = {} + } + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_igniter") + WireDermaExts.ModelSelect(panel, "wire_igniter_Model", ignitermodels, 1, true) + panel:CheckBox("#WireIgniterTool_trgply", "wire_igniter_trgply") + panel:NumSlider("#WireIgniterTool_Range", "wire_igniter_Range", 1, 10000, 0) + end +end --wire_igniter + + +do --wire_trail + WireToolSetup.open( "trail", "Trail", "gmod_wire_trail", WireToolMakeTrail ) + + if CLIENT then + language.Add( "Tool_wire_trail_name", "Trail Tool (Wire)" ) + language.Add( "Tool_wire_trail_desc", "Spawns a wired trail." ) + language.Add( "Tool_wire_trail_0", "Primary: Create/Update trail" ) + language.Add( "WireTrailTool_trail", "Trail:" ) + language.Add( "WireTrailTool_mat", "Material:" ) + language.Add( "sboxlimit_wire_trails", "You've hit trails limit!" ) + end + WireToolSetup.BaseLang("Trails") + + if SERVER then + CreateConVar('sbox_maxwire_trails', 20) + end + + TOOL.ClientConVar = { + material = "" + } + + TOOL.Model = "models/jaanus/wiretool/wiretool_range.mdl" + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_trail") + panel:AddControl( "MatSelect", { Height = "2", Label = "#WireTrailTool_mat", ConVar = "wire_trail_material", Options = list.Get( "trail_materials" ), ItemWidth = 64, ItemHeight = 64 } ) + end +end --wire_trail + + +do --wire_thruster + WireToolSetup.open( "thruster", "Thruster", "gmod_wire_thruster", WireToolMakeThruster ) + + if CLIENT then + language.Add( "Tool_wire_thruster_name", "Thruster Tool (Wire)" ) + language.Add( "Tool_wire_thruster_desc", "Spawns a thruster for use with the wire system." ) + language.Add( "Tool_wire_thruster_0", "Primary: Create/Update Thruster" ) + language.Add( "WireThrusterTool_Model", "Model:" ) + language.Add( "WireThrusterTool_OWEffects", "Over water effects:" ) + language.Add( "WireThrusterTool_UWEffects", "Under water effects:" ) + language.Add( "WireThrusterTool_force", "Force multiplier:" ) + language.Add( "WireThrusterTool_force_min", "Force minimum:" ) + language.Add( "WireThrusterTool_force_max", "Force maximum:" ) + language.Add( "WireThrusterTool_bidir", "Bi-directional" ) + language.Add( "WireThrusterTool_collision", "Collision" ) + language.Add( "WireThrusterTool_sound", "Enable sound" ) + language.Add( "WireThrusterTool_owater", "Works out of water" ) + language.Add( "WireThrusterTool_uwater", "Works under water" ) + language.Add( "sboxlimit_wire_thrusters", "You've hit thrusters limit!" ) + language.Add( "undone_wirethruster", "Undone Wire Thruster" ) + end + WireToolSetup.BaseLang("Thrusters") + + if SERVER then + CreateConVar('sbox_maxwire_thrusters', 10) + end + + TOOL.ClientConVar = { + force = 1500, + force_min = 0, + force_max = 10000, + model = "models/props_c17/lampShade001a.mdl", + bidir = 1, + collision = 0, + sound = 0, + oweffect = "fire", + uweffect = "same", + owater = 1, + uwater = 1, + } + + function TOOL.BuildCPanel(panel) + WireToolHelpers.MakePresetControl(panel, "wire_thruster") + + WireDermaExts.ModelSelect(panel, "wire_thruster_model", list.Get( "ThrusterModels" ), 4, true) + + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_OWEffects", + MenuButton = "0", + + Options = { + ["#No_Effects"] = { wire_thruster_oweffect = "none" }, + ["#Flames"] = { wire_thruster_oweffect = "fire" }, + ["#Plasma"] = { wire_thruster_oweffect = "plasma" }, + ["#Smoke"] = { wire_thruster_oweffect = "smoke" }, + ["#Smoke Random"] = { wire_thruster_oweffect = "smoke_random" }, + ["#Smoke Do it Youself"] = { wire_thruster_oweffect = "smoke_diy" }, + ["#Rings"] = { wire_thruster_oweffect = "rings" }, + ["#Rings Growing"] = { wire_thruster_oweffect = "rings_grow" }, + ["#Rings Shrinking"] = { wire_thruster_oweffect = "rings_shrink" }, + ["#Bubbles"] = { wire_thruster_oweffect = "bubble" }, + ["#Magic"] = { wire_thruster_oweffect = "magic" }, + ["#Magic Random"] = { wire_thruster_oweffect = "magic_color" }, + ["#Magic Do It Yourself"] = { wire_thruster_oweffect = "magic_diy" }, + ["#Colors"] = { wire_thruster_oweffect = "color" }, + ["#Colors Random"] = { wire_thruster_oweffect = "color_random" }, + ["#Colors Do It Yourself"] = { wire_thruster_oweffect = "color_diy" }, + ["#Blood"] = { wire_thruster_oweffect = "blood" }, + ["#Money"] = { wire_thruster_oweffect = "money" }, + ["#Sperms"] = { wire_thruster_oweffect = "sperm" }, + ["#Feathers"] = { wire_thruster_oweffect = "feather" }, + ["#Candy Cane"] = { wire_thruster_oweffect = "candy_cane" }, + ["#Goldstar"] = { wire_thruster_oweffect = "goldstar" }, + ["#Water Small"] = { wire_thruster_oweffect = "water_small" }, + ["#Water Medium"] = { wire_thruster_oweffect = "water_medium" }, + ["#Water Big"] = { wire_thruster_oweffect = "water_big" }, + ["#Water Huge"] = { wire_thruster_oweffect = "water_huge" }, + ["#Striderblood Small"] = { wire_thruster_oweffect = "striderblood_small" }, + ["#Striderblood Medium"] = { wire_thruster_oweffect = "striderblood_medium" }, + ["#Striderblood Big"] = { wire_thruster_oweffect = "striderblood_big" }, + ["#Striderblood Huge"] = { wire_thruster_oweffect = "striderblood_huge" }, + ["#More Sparks"] = { wire_thruster_oweffect = "more_sparks" }, + ["#Spark Fountain"] = { wire_thruster_oweffect = "spark_fountain" }, + ["#Jetflame"] = { wire_thruster_oweffect = "jetflame" }, + ["#Jetflame Advanced"] = { wire_thruster_oweffect = "jetflame_advanced" }, + ["#Jetflame Blue"] = { wire_thruster_oweffect = "jetflame_blue" }, + ["#Jetflame Red"] = { wire_thruster_oweffect = "jetflame_red" }, + ["#Jetflame Purple"] = { wire_thruster_oweffect = "jetflame_purple" }, + ["#Comic Balls"] = { wire_thruster_oweffect = "balls" }, + ["#Comic Balls Random"] = { wire_thruster_oweffect = "balls_random" }, + ["#Comic Balls Fire Colors"] = { wire_thruster_oweffect = "balls_firecolors" }, + ["#Souls"] = { wire_thruster_oweffect = "souls" }, + ["#Debugger 10 Seconds"] = { wire_thruster_oweffect = "debug_10" }, + ["#Debugger 30 Seconds"] = { wire_thruster_oweffect = "debug_30" }, + ["#Debugger 60 Seconds"] = { wire_thruster_oweffect = "debug_60" }, + ["#Fire and Smoke"] = { wire_thruster_oweffect = "fire_smoke" }, + ["#Fire and Smoke Huge"] = { wire_thruster_oweffect = "fire_smoke_big" }, + ["#5 Growing Rings"] = { wire_thruster_oweffect = "rings_grow_rings" }, + ["#Color and Magic"] = { wire_thruster_oweffect = "color_magic" }, + } + }) + + panel:AddControl("ComboBox", { + Label = "#WireThrusterTool_UWEffects", + MenuButton = "0", + + Options = { + ["#No_Effects"] = { wire_thruster_uweffect = "none" }, + ["#Same as over water"] = { wire_thruster_uweffect = "same" }, + ["#Flames"] = { wire_thruster_uweffect = "fire" }, + ["#Plasma"] = { wire_thruster_uweffect = "plasma" }, + ["#Smoke"] = { wire_thruster_uweffect = "smoke" }, + ["#Smoke Random"] = { wire_thruster_uweffect = "smoke_random" }, + ["#Smoke Do it Youself"] = { wire_thruster_uweffect = "smoke_diy" }, + ["#Rings"] = { wire_thruster_uweffect = "rings" }, + ["#Rings Growing"] = { wire_thruster_uweffect = "rings_grow" }, + ["#Rings Shrinking"] = { wire_thruster_uweffect = "rings_shrink" }, + ["#Bubbles"] = { wire_thruster_uweffect = "bubble" }, + ["#Magic"] = { wire_thruster_uweffect = "magic" }, + ["#Magic Random"] = { wire_thruster_uweffect = "magic_color" }, + ["#Magic Do It Yourself"] = { wire_thruster_uweffect = "magic_diy" }, + ["#Colors"] = { wire_thruster_uweffect = "color" }, + ["#Colors Random"] = { wire_thruster_uweffect = "color_random" }, + ["#Colors Do It Yourself"] = { wire_thruster_uweffect = "color_diy" }, + ["#Blood"] = { wire_thruster_uweffect = "blood" }, + ["#Money"] = { wire_thruster_uweffect = "money" }, + ["#Sperms"] = { wire_thruster_uweffect = "sperm" }, + ["#Feathers"] = { wire_thruster_uweffect = "feather" }, + ["#Candy Cane"] = { wire_thruster_uweffect = "candy_cane" }, + ["#Goldstar"] = { wire_thruster_uweffect = "goldstar" }, + ["#Water Small"] = { wire_thruster_uweffect = "water_small" }, + ["#Water Medium"] = { wire_thruster_uweffect = "water_medium" }, + ["#Water Big"] = { wire_thruster_uweffect = "water_big" }, + ["#Water Huge"] = { wire_thruster_uweffect = "water_huge" }, + ["#Striderblood Small"] = { wire_thruster_uweffect = "striderblood_small" }, + ["#Striderblood Medium"] = { wire_thruster_uweffect = "striderblood_medium" }, + ["#Striderblood Big"] = { wire_thruster_uweffect = "striderblood_big" }, + ["#Striderblood Huge"] = { wire_thruster_uweffect = "striderblood_huge" }, + ["#More Sparks"] = { wire_thruster_uweffect = "more_sparks" }, + ["#Spark Fountain"] = { wire_thruster_uweffect = "spark_fountain" }, + ["#Jetflame"] = { wire_thruster_uweffect = "jetflame" }, + ["#Jetflame Advanced"] = { wire_thruster_uweffect = "jetflame_advanced" }, + ["#Jetflame Blue"] = { wire_thruster_uweffect = "jetflame_blue" }, + ["#Jetflame Red"] = { wire_thruster_uweffect = "jetflame_red" }, + ["#Jetflame Purple"] = { wire_thruster_uweffect = "jetflame_purple" }, + ["#Comic Balls"] = { wire_thruster_uweffect = "balls" }, + ["#Comic Balls Random"] = { wire_thruster_uweffect = "balls_random" }, + ["#Comic Balls Fire Colors"] = { wire_thruster_uweffect = "balls_firecolors" }, + ["#Souls"] = { wire_thruster_uweffect = "souls" }, + ["#Debugger 10 Seconds"] = { wire_thruster_uweffect = "debug_10" }, + ["#Debugger 30 Seconds"] = { wire_thruster_uweffect = "debug_30" }, + ["#Debugger 60 Seconds"] = { wire_thruster_uweffect = "debug_60" }, + ["#Fire and Smoke"] = { wire_thruster_uweffect = "fire_smoke" }, + ["#Fire and Smoke Huge"] = { wire_thruster_uweffect = "fire_smoke_big" }, + ["#5 Growing Rings"] = { wire_thruster_uweffect = "rings_grow_rings" }, + ["#Color and Magic"] = { wire_thruster_uweffect = "color_magic" }, + } + }) + + panel:NumSlider("#WireThrusterTool_force", "wire_thruster_force", 1, 10000, 0) + panel:NumSlider("#WireThrusterTool_force_min", "wire_thruster_force_min", -10000, 10000, 0) + panel:NumSlider("#WireThrusterTool_force_max", "wire_thruster_force_max", -10000, 10000, 0) + panel:CheckBox("#WireThrusterTool_bidir", "wire_thruster_bidir") + panel:CheckBox("#WireThrusterTool_collision", "wire_thruster_collision") + panel:CheckBox("#WireThrusterTool_sound", "wire_thruster_sound") + panel:CheckBox("#WireThrusterTool_owater", "wire_thruster_owater") + panel:CheckBox("#WireThrusterTool_uwater", "wire_thruster_uwater") + end + --from model pack 1 + list.Set( "ThrusterModels", "models/jaanus/thruster_flat.mdl", {} ) +end --wire_thruster diff --git a/lua/wire/stools/sv_detection.lua b/lua/wire/stools/sv_detection.lua new file mode 100644 index 0000000000..688bdffe86 --- /dev/null +++ b/lua/wire/stools/sv_detection.lua @@ -0,0 +1,23 @@ + +function WireToolMakeSpeedometer( self, trace, ply ) + + local xyz_mode = util.tobool(self:GetClientNumber("xyz_mode")) + local AngVel = util.tobool(self:GetClientNumber("angvel")) + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_speedometer" and trace.Entity.pl == ply then + trace.Entity:Setup(xyz_mode, AngVel) + return true + end + + if not self:GetSWEP():CheckLimit("wire_speedometers") then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_speedometer = MakeWireSpeedometer(ply, trace.HitPos, Ang, self.Model, xyz_mode, AngVel) + + local min = wire_speedometer:OBBMins() + wire_speedometer:SetPos(trace.HitPos - trace.HitNormal * min.z) + + return wire_speedometer +end diff --git a/lua/wire/stools/sv_display.lua b/lua/wire/stools/sv_display.lua new file mode 100644 index 0000000000..c14f419a47 --- /dev/null +++ b/lua/wire/stools/sv_display.lua @@ -0,0 +1 @@ +-- all moved to display.lua diff --git a/lua/wire/stools/sv_io.lua b/lua/wire/stools/sv_io.lua new file mode 100644 index 0000000000..cc83d782ef --- /dev/null +++ b/lua/wire/stools/sv_io.lua @@ -0,0 +1,133 @@ + +function WireToolMakeAdvInput( self, trace, ply ) + + local _keymore = self:GetClientNumber( "keymore" ) + local _keyless = self:GetClientNumber( "keyless" ) + local _toggle = self:GetClientNumber( "toggle" ) + local _value_min = self:GetClientNumber( "value_min" ) + local _value_max = self:GetClientNumber( "value_max" ) + local _value_start = self:GetClientNumber( "value_start" ) + local _speed = self:GetClientNumber( "speed" ) + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_adv_input" and trace.Entity.pl == ply then + trace.Entity:Setup( _keymore, _keyless, _toggle, _value_min, _value_max, _value_start, _speed ) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_adv_inputs" ) then return false end + + if not util.IsValidModel(self.ModelInfo[3]) or not util.IsValidProp(self.ModelInfo[3]) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_adv_input = MakeWireAdvInput( ply, trace.HitPos, Ang, self.ModelInfo[3], _keymore, _keyless, _toggle, _value_min, _value_max, _value_start, _speed ) + + local min = wire_adv_input:OBBMins() + wire_adv_input:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_adv_input +end + + +function WireToolMakeAdvPod( self, trace, ply ) + + if not self:GetSWEP():CheckLimit("wire_pods") then return false end + + local model = self:GetModel() + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_pod = MakeWireAdvPod(ply, trace.HitPos, Ang, model) + + wire_pod:SetPos(trace.HitPos - trace.HitNormal * wire_pod:OBBMins().z) + + return wire_pod +end + + +function WireToolMakeButton( self, trace, ply ) + + local _model = self:GetModel() + local _toggle = (self:GetClientNumber( "toggle" ) ~= 0) + local _value_off = self:GetClientNumber( "value_off" ) + local _value_on = self:GetClientNumber( "value_on" ) + local _description = self:GetClientInfo( "description" ) + local _entityout = (self:GetClientNumber( "entityout" ) ~= 0) + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_button" and trace.Entity.pl == ply then + trace.Entity:Setup(_toggle, _value_off, _value_on, _entityout) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_buttons" ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_button = MakeWireButton( ply, trace.HitPos, Ang, _model, _toggle, _value_off, _value_on, _description, _entityout ) + + local min = wire_button:OBBMins() + wire_button:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_button +end + + +function WireToolMakeDualInput( self, trace, ply ) + + local _keygroup = self:GetClientNumber( "keygroup" ) + local _keygroup2 = self:GetClientNumber( "keygroup2" ) + local _toggle = self:GetClientNumber( "toggle" ) + local _value_off = self:GetClientNumber( "value_off" ) + local _value_on = self:GetClientNumber( "value_on" ) + local _value_on2 = self:GetClientNumber( "value_on2" ) + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_dual_input" and trace.Entity.pl == ply then + trace.Entity:Setup( _keygroup, _keygroup2, _toggle, _value_off, _value_on, _value_on2 ) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_dual_inputs" ) then return false end + + if not util.IsValidModel(self.ModelInfo[3]) or not util.IsValidProp(self.ModelInfo[3]) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_dual_input = MakeWireDualInput( ply, trace.HitPos, Ang, self.ModelInfo[3], _keygroup, _keygroup2, _toggle, _value_off, _value_on, _value_on2 ) + + local min = wire_dual_input:OBBMins() + wire_dual_input:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_dual_input +end + + +function WireToolMakeInput( self, trace, ply ) + + local keygroup = self:GetClientNumber( "keygroup" ) + local toggle = self:GetClientNumber( "toggle" ) + local value_off = self:GetClientNumber( "value_off" ) + local value_on = self:GetClientNumber( "value_on" ) + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_input" and trace.Entity.pl == ply then + trace.Entity:Setup( keygroup, toggle, value_off, value_on ) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_inputs" ) then return false end + + if not util.IsValidModel(self.ModelInfo[3]) or not util.IsValidProp(self.ModelInfo[3]) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_input = MakeWireInput( ply, trace.HitPos, Ang, self.ModelInfo[3], keygroup, toggle, value_off, value_on ) + + local min = wire_input:OBBMins() + wire_input:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_input +end + diff --git a/lua/wire/stools/sv_physics.lua b/lua/wire/stools/sv_physics.lua new file mode 100644 index 0000000000..ef2ab94782 --- /dev/null +++ b/lua/wire/stools/sv_physics.lua @@ -0,0 +1,386 @@ + +function WireToolMakeWeight( self, trace, ply ) + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_weight" and trace.Entity.pl == ply then + return true + end + + local model = self:GetModel() + + if not self:GetSWEP():CheckLimit( "wire_weights" ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_weight = MakeWireWeight( ply, trace.HitPos, Ang, model ) + + local min = wire_weight:OBBMins() + wire_weight:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_weight +end + + +function WireToolMakeExplosivesSimple( self, trace, ply ) + if not self:GetSWEP():CheckLimit( "wire_simple_explosive" ) then return false end + + local _trigger = self:GetClientNumber( "tirgger" ) + local _damage = math.Clamp( self:GetClientNumber( "damage" ), 0, 1500 ) + local _removeafter = self:GetClientNumber( "removeafter" ) == 1 + local _doblastdamage = self:GetClientNumber( "doblastdamage" ) == 1 + local _radius = math.Clamp( self:GetClientNumber( "radius" ), 0, 10000 ) + local _freeze = self:GetClientNumber( "freeze" ) == 1 + local _weld = self:GetClientNumber( "weld" ) == 1 + local _noparentremove = self:GetClientNumber( "noparentremove" ) == 1 + local _nocollide = self:GetClientNumber( "nocollide" ) == 1 + local _weight = math.Max(self:GetClientNumber( "weight" ), 1) + + --get & check selected model + _model = self:GetSelModel( true ) + if not _model then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local explosive = MakeWireSimpleExplosive( ply, trace.HitPos, Ang, _model, _trigger, _damage, _removeafter, _doblastdamage, _radius, _nocollide ) + + local min = explosive:OBBMins() + explosive:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + if _freeze then + explosive:GetPhysicsObject():Sleep() --will freeze the explosive till something touches it + end + + explosive.Entity:GetPhysicsObject():SetMass(_weight) + -- Make sure the weight is duplicated as well (TheApathetic) + duplicator.StoreEntityModifier( explosive, "MassMod", {Mass = _weight} ) + + undo.Create("WireSimpleExplosive") + undo.AddEntity( explosive ) + + -- Don't weld to world + if trace.Entity:IsValid() and _weld then + if _noparentremove then + local const, nocollide = constraint.Weld( explosive, trace.Entity, 0, trace.PhysicsBone, 0, collision == 0, false ) + undo.AddEntity( const ) + else + local const, nocollide = constraint.Weld( explosive, trace.Entity, 0, trace.PhysicsBone, 0, collision == 0, true ) + undo.AddEntity( const ) + end + end + + undo.SetPlayer( ply ) + undo.Finish() + + return true +end + + +function WireToolMakeWheel( self, trace, ply ) + if trace.Entity and trace.Entity:IsPlayer() then return false end + if SERVER and not util.IsValidPhysicsObject( trace.Entity, trace.PhysicsBone ) then return false end + if CLIENT then return true end + + if not self:GetSWEP():CheckLimit( "wire_wheels" ) then return false end + + local targetPhys = trace.Entity:GetPhysicsObjectNum( trace.PhysicsBone ) + + -- Get client's CVars + local torque = self:GetClientNumber( "torque" ) + local friction = self:GetClientNumber( "friction" ) + local nocollide = self:GetClientNumber( "nocollide" ) + local limit = self:GetClientNumber( "forcelimit" ) + local model = ply:GetInfo("wheel_model") + + local fwd = self:GetClientNumber( "fwd" ) + local bck = self:GetClientNumber( "bck" ) + local stop = self:GetClientNumber( "stop" ) + + if not util.IsValidModel( model ) or not util.IsValidProp( model ) then return false end + + if fwd == stop or bck == stop or fwd == bck then return false end + + -- Create the wheel + local wheelEnt = MakeWireWheel( ply, trace.HitPos, Angle(0,0,0), model, nil, nil, nil, fwd, bck, stop, torque ) + + -- Make sure we have our wheel angle + self.wheelAngle = Angle( tonumber(ply:GetInfo( "wheel_rx" )), tonumber(ply:GetInfo( "wheel_ry" )), tonumber(ply:GetInfo( "wheel_rz" )) ) + + local TargetAngle = trace.HitNormal:Angle() + self.wheelAngle + wheelEnt:SetAngles( TargetAngle ) + + local CurPos = wheelEnt:GetPos() + local NearestPoint = wheelEnt:NearestPoint( CurPos - (trace.HitNormal * 512) ) + local wheelOffset = CurPos - NearestPoint + + wheelEnt:SetPos( trace.HitPos + wheelOffset + trace.HitNormal ) + + -- Wake up the physics object so that the entity updates + wheelEnt:GetPhysicsObject():Wake() + + local TargetPos = wheelEnt:GetPos() + + -- Set the hinge Axis perpendicular to the trace hit surface + local LPos1 = wheelEnt:GetPhysicsObject():WorldToLocal( TargetPos + trace.HitNormal ) + local LPos2 = targetPhys:WorldToLocal( trace.HitPos ) + + local constraint, axis = constraint.Motor( wheelEnt, trace.Entity, 0, trace.PhysicsBone, LPos1, LPos2, friction, torque, 0, nocollide, false, ply, limit ) + + undo.Create("WireWheel") + undo.AddEntity( axis ) + undo.AddEntity( constraint ) + undo.AddEntity( wheelEnt ) + undo.SetPlayer( ply ) + undo.Finish() + + ply:AddCleanup( "wire_wheels", axis ) + ply:AddCleanup( "wire_wheels", constraint ) + ply:AddCleanup( "wire_wheels", wheelEnt ) + + --BUGFIX:WIREMOD-11:Deleting prop did not deleting wheels + wheelEnt:SetWheelBase(trace.Entity) + + wheelEnt:SetMotor( constraint ) + wheelEnt:SetDirection( constraint.direction ) + wheelEnt:SetAxis( trace.HitNormal ) + wheelEnt:SetToggle( toggle ) + wheelEnt:DoDirectionEffect() + wheelEnt:SetBaseTorque( torque ) + + return true +end + + +function WireToolMakeForcer( self, trace, ply ) + + local showbeam = self:GetClientNumber( "beam" ) == 1 + local reaction = self:GetClientNumber( "reaction" ) == 1 + local multiplier = self:GetClientNumber( "multiplier" ) + local length = self:GetClientNumber( "length" ) + local model = self:GetModel() + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_forcer" and trace.Entity.pl == ply then + trace.Entity:Setup(multiplier, length, showbeam, reaction) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_forcers" ) then return false end + if not util.IsValidModel( model ) or not util.IsValidProp( model ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_forcer = MakeWireForcer( ply, trace.HitPos, Ang, model, multiplier, length, showbeam, reaction ) + + local min = wire_forcer:OBBMins() + if model == "models/jaanus/wiretool/wiretool_grabber_forcer.mdl" then + wire_forcer:SetPos( trace.HitPos - trace.HitNormal * (min.z + 20) ) + else + wire_forcer:SetPos( trace.HitPos - trace.HitNormal * min.z ) + end + + return wire_forcer +end + + +function WireToolMakeDetonator( self, trace, ply ) + + local damage = self:GetClientNumber("damage") + local model = self:GetModel() + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_detonator" and trace.Entity.pl == ply then + trace.Entity:Setup(damage) + trace.Entity.damage = damage + return true + end + + if not self:GetSWEP():CheckLimit( "wire_detonators" ) then return false end + if not util.IsValidModel( model ) or not util.IsValidProp( model ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_detonator = MakeWireDetonator(ply, trace.HitPos, Ang, model, damage) + wire_detonatortarget = trace.Entity + + local min = wire_detonator:OBBMins() + wire_detonator:SetPos(trace.HitPos - trace.HitNormal * min.z) + + return wire_detonator +end + + +function WireToolMakeGrabber( self, trace, ply ) + + local range = self:GetClientNumber("Range") + local gravity = self:GetClientNumber("Gravity") ~= 0 + local model = self:GetModel() + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_grabber" and trace.Entity.pl == ply then + trace.Entity:Setup(range, gravity) + trace.Entity.Gange = range + trace.Entity.Gravity = gravity + return true + end + + if not self:GetSWEP():CheckLimit( "wire_grabbers" ) then return false end + if not util.IsValidModel( model ) or not util.IsValidProp( model ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_grabber = MakeWireGrabber( ply, trace.HitPos, Ang, model, range, gravity ) + + local min = wire_grabber:OBBMins() + if model == "models/jaanus/wiretool/wiretool_grabber_forcer.mdl" then + wire_grabber:SetPos( trace.HitPos - trace.HitNormal * (min.z + 20) ) + else + wire_grabber:SetPos( trace.HitPos - trace.HitNormal * min.z ) + end + + return wire_grabber +end + + +function WireToolMakeHoverball( self, trace, ply ) + + local speed = self:GetClientNumber( "speed" ) + local resistance = self:GetClientNumber( "resistance" ) + local strength = self:GetClientNumber( "strength" ) + local starton = self:GetClientNumber( "starton" ) == 1 + + resistance = math.Clamp( resistance, 0, 20 ) + strength = math.Clamp( strength, 0.1, 20 ) + + -- We shot an existing hoverball - just change its values + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_hoverball" and trace.Entity.pl == ply then + + trace.Entity:SetSpeed( speed ) + trace.Entity:SetAirResistance( resistance ) + trace.Entity:SetStrength( strength ) + + trace.Entity.speed = speed + trace.Entity.strength = strength + trace.Entity.resistance = resistance + + if not starton then trace.Entity:DisableHover() else trace.Entity:EnableHover() end + + return true + end + + if not self:GetSWEP():CheckLimit( "wire_hoverballs" ) then return false end + + -- If we hit the world then offset the spawn position + if trace.Entity:IsWorld() then + trace.HitPos = trace.HitPos + trace.HitNormal * 8 + end + + local wire_ball = MakeWireHoverBall( ply, trace.HitPos, Angle(0,0,0), self.Model, speed, resistance, strength ) + + if not starton then wire_ball:DisableHover() end + + return wire_ball +end + + +function WireToolMakeIgniter( self, trace, ply ) + + local trgply = self:GetClientNumber( "trgply" ) ~= 0 + local Range = self:GetClientNumber("Range") + local model = self:GetModel() + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_igniter" and trace.Entity.pl == ply then + trace.Entity:Setup(trgply, Range) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_igniters" ) then return false end + if not util.IsValidModel( model ) or not util.IsValidProp( model ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_igniter = MakeWireIgniter( ply, trace.HitPos, Ang, model, trgply, Range ) + + local min = wire_igniter:OBBMins() + wire_igniter:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_igniter +end + + +function WireToolMakeTrail( self, trace, ply ) + + local mat = self:GetClientInfo("material") + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_trail" and trace.Entity.pl == ply then + trace.Entity.mat = mat + trace.Entity:Setup(mat) + return true + end + + if not self:GetSWEP():CheckLimit( "wire_trails" ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_trail = MakeWireTrail( ply, trace.HitPos, Ang, self.Model, mat ) + + local min = wire_trail:OBBMins() + wire_trail:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_trail +end + + +function WireToolMakeThruster( self, trace, ply ) + + local force = self:GetClientNumber( "force" ) + local force_min = self:GetClientNumber( "force_min" ) + local force_max = self:GetClientNumber( "force_max" ) + local model = self:GetModel() + local bidir = self:GetClientNumber( "bidir" ) ~= 0 + local nocollide = self:GetClientNumber( "collision" ) == 0 + local sound = self:GetClientNumber( "sound" ) ~= 0 + local oweffect = self:GetClientInfo( "oweffect" ) + local uweffect = self:GetClientInfo( "uweffect" ) + local owater = self:GetClientNumber( "owater" ) ~= 0 + local uwater = self:GetClientNumber( "uwater" ) ~= 0 + + if not trace.Entity:IsValid() then nocollide = false end + + -- If we shot a wire_thruster change its force + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_thruster" and trace.Entity.pl == ply then + trace.Entity:SetForce( force ) + trace.Entity:SetEffect( effect ) + trace.Entity:Setup(force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound) + + trace.Entity.force = force + trace.Entity.force_min = force_min + trace.Entity.force_max = force_max + trace.Entity.bidir = bidir + trace.Entity.sound = sound + trace.Entity.oweffect = oweffect + trace.Entity.uweffect = uweffect + trace.Entity.owater = owater + trace.Entity.uwater = uwater + trace.Entity.nocollide = nocollide + + if nocollide == true then trace.Entity:GetPhysicsObject():EnableCollisions( false ) end + + return true + end + + if not self:GetSWEP():CheckLimit( "wire_thrusters" ) then return false end + if not util.IsValidModel( model ) or not util.IsValidProp( model ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_thruster = MakeWireThruster( ply, trace.HitPos, Ang, model, force, force_min, force_max, oweffect, uweffect, owater, uwater, bidir, sound, nocollide ) + + local min = wire_thruster:OBBMins() + wire_thruster:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_thruster +end diff --git a/lua/wire/stools/sv_wirestools.lua b/lua/wire/stools/sv_wirestools.lua new file mode 100644 index 0000000000..7e6cbf7054 --- /dev/null +++ b/lua/wire/stools/sv_wirestools.lua @@ -0,0 +1,42 @@ +--[[ + This is stool code, + These are used by the Wired tools' LeftClick to make/update ents, + the part after trace check and before welding/undo/cleanup creation. +]]-- + + +function WireToolMakeGate( self, trace, ply ) + local action = self:GetClientInfo( "action" ) + local noclip = self:GetClientNumber( "noclip" ) == 1 + local model = self:GetModel() + + if GateActions[action] == nil then return false end + + if trace.Entity:IsValid() and trace.Entity:GetClass() == "gmod_wire_gate" and trace.Entity.pl == ply then + trace.Entity:Setup( GateActions[action], noclip ) + trace.Entity:GetTable().action = action + return true + end + + if not util.IsValidModel(model) and not util.IsValidProp(model) then return false end + + if ( GateActions[action].group == "Arithmetic" and not self:GetSWEP():CheckLimit( "wire_gates" ) ) or + ( GateActions[action].group == "Comparison" and not self:GetSWEP():CheckLimit( "wire_gate_comparisons" ) ) or + ( GateActions[action].group == "Logic" and not self:GetSWEP():CheckLimit( "wire_gate_logics" ) ) or + ( GateActions[action].group == "Memory" and not self:GetSWEP():CheckLimit( "wire_gate_memorys" ) ) or + ( GateActions[action].group == "Selection" and not self:GetSWEP():CheckLimit( "wire_gate_selections" ) ) or + ( GateActions[action].group == "Time" and not self:GetSWEP():CheckLimit( "wire_gate_times" ) ) or + ( GateActions[action].group == "Trig" and not self:GetSWEP():CheckLimit( "wire_gate_trigs" ) ) or + ( GateActions[action].group == "Table" and not self:GetSWEP():CheckLimit( "wire_gate_duplexer" ) ) then return false end + + local Ang = trace.HitNormal:Angle() + Ang.pitch = Ang.pitch + 90 + + local wire_gate = MakeWireGate( ply, trace.HitPos, Ang, model, action, noclip ) + + local min = wire_gate:OBBMins() + wire_gate:SetPos( trace.HitPos - trace.HitNormal * min.z ) + + return wire_gate +end + diff --git a/lua/wire/stools/tool_loader.lua b/lua/wire/stools/tool_loader.lua new file mode 100644 index 0000000000..7fa47ef403 --- /dev/null +++ b/lua/wire/stools/tool_loader.lua @@ -0,0 +1,319 @@ +-- $Rev: 1731 $ +-- $LastChangedDate: 2009-09-24 15:06:08 -0700 (Thu, 24 Sep 2009) $ +-- $LastChangedBy: tad2020 $ + +MsgN("Loading Wire Tools") + +AddCSLuaFile( "tool_loader.lua" ) + + +local function LoadTools() + -- load server side code for tools + if SERVER then + include( "sv_wirestools.lua" ) + include( "sv_detection.lua" ) + include( "sv_display.lua" ) + include( "sv_io.lua" ) + include( "sv_physics.lua" ) + end + + -- load tools + include("gates.lua") + include("detection.lua") + include("display.lua") + include("io.lua") + include("physics.lua") + + -- close last TOOL + if TOOL then WireToolSetup.close() end +end + + +-- prevent showing the ghost when poiting at any class in the TOOL.NoGhostOn table +local function NoGhostOn(self, trace) + return self.NoGhostOn and table.HasValue( self.NoGhostOn, trace.Entity:GetClass()) +end + + +WireToolObj = {} +setmetatable( WireToolObj, ToolObj ) + + +WireToolObj.Tab = "Wire" + + +-- optional LeftClick tool function for basic tools that just place/weld a device [default] +function WireToolObj:LeftClick( trace ) + if not trace.HitPos or trace.Entity:IsPlayer() or trace.Entity:IsNPC() then return false end + if self.NoLeftOnClass and trace.HitNonWorld and (trace.Entity:GetClass() == self.WireClass or NoGhostOn(self, trace)) or (SERVER and trace.Entity:GetClass() == self.WireClass and trace.Entity.pl ~= self:GetOwner()) then return false end + + if CLIENT then return true end + + local ply = self:GetOwner() + + local ent = self:LeftClick_Make( trace, ply ) -- WireToolObj.LeftClick_Make will be called if another function was not defined + + return self:LeftClick_PostMake( ent, ply, trace ) +end + +if SERVER then + -- + function WireToolObj:LeftClick_Make( trace, ply ) + -- hit our own class, update + if self:CheckHitOwnClass(trace) then + self:LeftClick_Update(trace) + return true + end + + local model = self:GetModel() + if self:CheckMaxLimit() or self:CheckValidModel(model) then return false end + + local Ang = self:GetAngle( trace ) + + local ent = self:MakeEnt( ply, model, Ang, trace ) + + self:PostMake_SetPos( ent, trace ) + + return ent + end + + -- + -- to prevent update, set TOOL.NoLeftOnClass = true + function WireToolObj:LeftClick_Update( trace ) + trace.Entity:Setup(self:GetConVars()) + end + + -- + -- this function needs to return true if the tool beam should be "fired" + function WireToolObj:LeftClick_PostMake( ent, ply, trace ) + if ent == true then return true end + if ent == nil or ent == false or not ent:IsValid() then return false end + + local const + if not self.ClientConVar.weld or self:GetClientNumber( "weld" ) == 1 then + const = WireLib.Weld( ent, trace.Entity, trace.PhysicsBone, true ) + end + + undo.Create( self.WireClass ) + undo.AddEntity( ent ) + undo.AddEntity( const ) + undo.SetPlayer( self:GetOwner() ) + undo.Finish() + + ply:AddCleanup( self.WireClass, ent ) + + return true + end +end + +-- basic UpdateGhost function that should cover most of wire's ghost updating needs [default] +function WireToolObj:UpdateGhost( ent ) + if not ent or not ent:IsValid() then return end + + local tr = utilx.GetPlayerTrace( self:GetOwner(), self:GetOwner():GetCursorAimVector() ) + local trace = util.TraceLine( tr ) + if not trace.Hit then return end + + -- don't draw the ghost if we hit nothing, a player, an npc, the type of device this tool makes, or any class this tool says not to + if not trace.Hit or trace.Entity:IsPlayer() or trace.Entity:IsNPC() or trace.Entity:GetClass() == self.WireClass or NoGhostOn(self, trace) then + ent:SetNoDraw( true ) + return + end + + -- modify the ghosts angle + local Ang = self:GetAngle( trace ) + ent:SetAngles( Ang ) + + self:SetPos( ent, trace ) + + --show the ghost + ent:SetNoDraw( false ) +end + + +-- option tool Think function for updating the pos of the ghost and making one when needed [default] +function WireToolObj:Think() + local model = self:GetModel() + if not self.GhostEntity or not self.GhostEntity:IsValid() or self.GhostEntity:GetModel() ~= model then + if self.GetGhostAngle then -- the tool as a function for getting the proper angle for the ghost + self:MakeGhostEntity( model, Vector(0,0,0), self:GetGhostAngle(Angle(0,0,0)) ) + else -- the tool gives a fixed angle to add else use a zero'd angle + self:MakeGhostEntity( model, Vector(0,0,0), self.GhostAngle or Angle(0,0,0) ) + end + end + self:UpdateGhost( self.GhostEntity ) +end + + +if SERVER then + -- + function WireToolObj:CheckHitOwnClass( trace ) + return trace.Entity:IsValid() and trace.Entity:GetClass() == self.WireClass and trace.Entity.pl == self:GetOwner() + end + + -- + function WireToolObj:CheckMaxLimit() + return not self:GetSWEP():CheckLimit(self.MaxLimitName) + end + + -- Allow ragdolls to be used? + function WireToolObj:CheckValidModel( model ) + return not util.IsValidModel(model) or not util.IsValidProp(model) + end + + -- + function WireToolObj:GetModel() + if self.Model then + return self.Model + elseif not self:CheckValidModel(self:GetClientInfo( "model" )) then --use a valid model or the server crashes :< + return self:GetClientInfo( "model" ) + else + return "models/props_c17/oildrum001.mdl" --use some other random, valid prop instead if they fuck up + end + end +else + -- + function WireToolObj:GetModel() + return self.Model or self:GetClientInfo( "model" ) --meh, they are crash themselves if they want + end +end + +-- +function WireToolObj:GetAngle( trace ) + local Ang = trace.HitNormal:Angle() + if self.GetGhostAngle then -- the tool as a function for getting the proper angle for the ghost + Ang = self:GetGhostAngle( Ang ) + elseif self.GhostAngle then -- the tool gives a fixed angle to add + Ang = Ang + self.GhostAngle + end + Ang.pitch = Ang.pitch + 90 + return Ang +end + +-- +function WireToolObj:SetPos( ent, trace ) + -- move the ghost to aline properly to where the device will be made + local min = ent:OBBMins() + if self.GetGhostMin then -- tool has a function for getting the min + ent:SetPos( trace.HitPos - trace.HitNormal * self:GetGhostMin( min, trace ) ) + elseif self.GhostMin then -- tool gives the axis for the OBBmin to use + ent:SetPos( trace.HitPos - trace.HitNormal * min[self.GhostMin] ) + else -- default to the z OBBmin + ent:SetPos( trace.HitPos - trace.HitNormal * min.z ) + end +end +if SERVER then WireToolObj.PostMake_SetPos = WireToolObj.SetPos end + + + + +-- function used by TOOL.BuildCPanel +WireToolHelpers = {} + +if CLIENT then + -- gets the TOOL since TOOL.BuildCPanel isn't passed this var. wts >_< + local function GetTOOL(mode) + for _,wep in ipairs(LocalPlayer():GetWeapons()) do + if wep:GetClass() == "gmod_tool" then + wep:GetToolObject(mode) + end + end + end + + -- makes the preset control for use cause we're lazy + function WireToolHelpers.MakePresetControl(panel, mode, folder) + if not mode or not panel then return end + local TOOL = GetTOOL(mode) + if not TOOL then return end + local ctrl = vgui.Create( "ControlPresets", panel ) + ctrl:SetPreset(folder or mode) + if TOOL.ClientConVar then + local options = {} + for k, v in pairs(TOOL.ClientConVar) do + if k ~= "id" then + k = mode.."_"..k + options[k] = v + ctrl:AddConVar(k) + end + end + ctrl:AddOption("#Default", options) + end + panel:AddPanel( ctrl ) + end + + -- adds the neato model select control + function WireToolHelpers.MakeModelSel(panel, mode) + local TOOL = GetTOOL(mode) + if not TOOL then return end + ModelPlug_AddToCPanel(panel, TOOL.short_name, TOOL.Mode, "#ToolWireIndicator_Model") + end +end + + + +WireToolSetup = {} + +-- sets the ToolCategory for every wire tool made fallowing its call +function WireToolSetup.setCategory( s_cat ) + WireToolSetup.cat = "Wire - "..s_cat +end + +-- makes a new TOOL +-- s_mode: Tool_mode, same as the old tool lua file name, minus the "wire_" part +-- s_name: Proper name for the tool +-- s_class: For tools that make a device. Should begin with "gmod_wire_". Can be nil if not using WireToolObj.LeftClick or WireToolSetup.BaseLang +-- f_toolmakeent: Server side function for making the tools device. Can be nil if not using WireToolObj.LeftClick +function WireToolSetup.open( s_mode, s_name, s_class, f_toolmakeent, s_pluralname ) + -- close the previous TOOL if not done so already + if TOOL then WireToolSetup.close() end + + -- make new TOOL object + TOOL = WireToolObj:Create() + + -- default vars, + TOOL.Mode = "wire_"..s_mode + TOOL.short_name = s_mode + TOOL.Category = WireToolSetup.cat + TOOL.Name = s_name + TOOL.PluralName = s_pluralname + TOOL.WireClass = s_class + if f_toolmakeent then + TOOL.LeftClick_Make = f_toolmakeent + end + local info = debug.getinfo(2, "S") + if info then + TOOL.SourceFile = info.short_src + end +end + +-- closes and saves the open TOOL obj +function WireToolSetup.close() + TOOL:CreateConVars() + SWEP.Tool[TOOL.Mode] = TOOL + TOOL = nil +end + + +-- optional function to add the basic language for basic tools +function WireToolSetup.BaseLang( pluralname ) + if CLIENT then + language.Add( "undone_"..TOOL.WireClass, "Undone Wire "..TOOL.Name ) + language.Add( "Cleanup_"..TOOL.WireClass, "Wire "..(TOOL.PluralName or pluralname) ) + language.Add( "Cleaned_"..TOOL.WireClass, "Cleaned Up Wire "..(TOOL.PluralName or pluralname) ) + end + cleanup.Register(TOOL.WireClass) +end + + +-- +function WireToolSetup.SetupMax( i_limit, s_maxlimitname , s_warning ) + TOOL.MaxLimitName = s_maxlimitname + if CLIENT then + language.Add("SBoxLimit_"..TOOL.MaxLimitName, s_warning) + AddWireAdminMaxDevice(TOOL.PluralName, TOOL.MaxLimitName) + else + CreateConVar("sbox_max"..TOOL.MaxLimitName, i_limit) + end +end + +LoadTools() diff --git a/materials/Holograms/HQ/white.vmt b/materials/Holograms/HQ/white.vmt new file mode 100644 index 0000000000..6b1b9f4aa8 --- /dev/null +++ b/materials/Holograms/HQ/white.vmt @@ -0,0 +1,4 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/HQ/white.vtf" +} diff --git a/materials/Holograms/HQ/white.vtf b/materials/Holograms/HQ/white.vtf new file mode 100644 index 0000000000..1f80f88f88 Binary files /dev/null and b/materials/Holograms/HQ/white.vtf differ diff --git a/materials/Holograms/Plastic.vmt b/materials/Holograms/Plastic.vmt new file mode 100644 index 0000000000..05e338f0b0 --- /dev/null +++ b/materials/Holograms/Plastic.vmt @@ -0,0 +1,9 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/Plastic.vtf" + "$surfaceprop" "tile" + "$bumpmap" "Holograms/Plastic" + "$ssbump" "1" + "$halflambert" "1" + "$model" "1" +} diff --git a/materials/Holograms/Plastic.vtf b/materials/Holograms/Plastic.vtf new file mode 100644 index 0000000000..962fa1a174 Binary files /dev/null and b/materials/Holograms/Plastic.vtf differ diff --git a/materials/Holograms/Plastic2.vmt b/materials/Holograms/Plastic2.vmt new file mode 100644 index 0000000000..09353dc031 --- /dev/null +++ b/materials/Holograms/Plastic2.vmt @@ -0,0 +1,13 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/Plastic.vtf" + "$surfaceprop" "tile" + "$bumpmap" "Holograms/Plastic" + "$ssbump" "1" + "$halflambert" "1" + "$model" "1" + "$phong" "1" + "$phongexponent" "30" + "$phongboost" "6" + "$phongfresnelranges" "[0 .5 1]" +} diff --git a/materials/Holograms/Plastic3.ifl b/materials/Holograms/Plastic3.ifl new file mode 100644 index 0000000000..62b66115b8 --- /dev/null +++ b/materials/Holograms/Plastic3.ifl @@ -0,0 +1,2 @@ +Plastic3.vtf +Plastic5.vtf diff --git a/materials/Holograms/Plastic3.vmt b/materials/Holograms/Plastic3.vmt new file mode 100644 index 0000000000..787239349a --- /dev/null +++ b/materials/Holograms/Plastic3.vmt @@ -0,0 +1,9 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/Plastic3.vtf" + "$surfaceprop" "tile" + "$bumpmap" "Holograms/Plastic3" + "$ssbump" "1" + "$halflambert" 1 + "$model" "1" +} \ No newline at end of file diff --git a/materials/Holograms/Plastic3.vtf b/materials/Holograms/Plastic3.vtf new file mode 100644 index 0000000000..e952c7af16 Binary files /dev/null and b/materials/Holograms/Plastic3.vtf differ diff --git a/materials/Holograms/Plastic3_normal.vtf b/materials/Holograms/Plastic3_normal.vtf new file mode 100644 index 0000000000..bc1559ff31 Binary files /dev/null and b/materials/Holograms/Plastic3_normal.vtf differ diff --git a/materials/Holograms/Plastic4.vmt b/materials/Holograms/Plastic4.vmt new file mode 100644 index 0000000000..d9a0d8e9e1 --- /dev/null +++ b/materials/Holograms/Plastic4.vmt @@ -0,0 +1,13 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/Plastic3.vtf" + "$surfaceprop" "tile" + "$bumpmap" "Holograms/Plastic3" + "$ssbump" "1" + "$halflambert" 1 + "$model" "1" + "$phong" "1" + "$phongexponent" "30" + "$phongboost" "6" + "$phongfresnelranges" "[0.5 0.8 1]" +} \ No newline at end of file diff --git a/materials/Holograms/Plastic5.vmt b/materials/Holograms/Plastic5.vmt new file mode 100644 index 0000000000..927af0704e --- /dev/null +++ b/materials/Holograms/Plastic5.vmt @@ -0,0 +1,8 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/Plastic5.vtf" + "$surfaceprop" "tile" + "$bumpmap" "Holograms/Plastic" + "$model" "1" + "$halflambert" "1" +} \ No newline at end of file diff --git a/materials/Holograms/Plastic5.vtf b/materials/Holograms/Plastic5.vtf new file mode 100644 index 0000000000..c4fbbb6fa9 Binary files /dev/null and b/materials/Holograms/Plastic5.vtf differ diff --git a/materials/Holograms/Plastic6.vmt b/materials/Holograms/Plastic6.vmt new file mode 100644 index 0000000000..d3d5d758ea --- /dev/null +++ b/materials/Holograms/Plastic6.vmt @@ -0,0 +1,12 @@ +"VertexLitGeneric" +{ + "$basetexture" "Holograms/Plastic5.vtf" + "$surfaceprop" "tile" + "$bumpmap" "Holograms/Plastic" + "$halflambert" 1 + "$model" "1" + "$phong" "1" + "$phongexponent" "60" + "$phongboost" "2" + "$phongfresnelranges" "[0.5 0.8 1]" +} \ No newline at end of file diff --git a/materials/Holograms/Plastic_normal.vtf b/materials/Holograms/Plastic_normal.vtf new file mode 100644 index 0000000000..7f6c3f383d Binary files /dev/null and b/materials/Holograms/Plastic_normal.vtf differ diff --git a/materials/gui/silkicons/newspaper.vmt b/materials/gui/silkicons/newspaper.vmt new file mode 100644 index 0000000000..519e652d22 --- /dev/null +++ b/materials/gui/silkicons/newspaper.vmt @@ -0,0 +1,8 @@ +"UnlitGeneric" +{ + "$basetexture" "gui/silkicons/newspaper" + "$ignorez" 1 + "$vertexcolor" 1 + "$vertexalpha" 1 + "$nolod" 1 +} \ No newline at end of file diff --git a/materials/gui/silkicons/newspaper.vtf b/materials/gui/silkicons/newspaper.vtf new file mode 100644 index 0000000000..7b6d4fa8e9 Binary files /dev/null and b/materials/gui/silkicons/newspaper.vtf differ diff --git a/models/Holograms/cone.dx80.vtx b/models/Holograms/cone.dx80.vtx new file mode 100644 index 0000000000..7d12a13cb7 Binary files /dev/null and b/models/Holograms/cone.dx80.vtx differ diff --git a/models/Holograms/cone.dx90.vtx b/models/Holograms/cone.dx90.vtx new file mode 100644 index 0000000000..a39b6bd84a Binary files /dev/null and b/models/Holograms/cone.dx90.vtx differ diff --git a/models/Holograms/cone.mdl b/models/Holograms/cone.mdl new file mode 100644 index 0000000000..ae77ee72e2 Binary files /dev/null and b/models/Holograms/cone.mdl differ diff --git a/models/Holograms/cone.phy b/models/Holograms/cone.phy new file mode 100644 index 0000000000..bbb7034e69 Binary files /dev/null and b/models/Holograms/cone.phy differ diff --git a/models/Holograms/cone.sw.vtx b/models/Holograms/cone.sw.vtx new file mode 100644 index 0000000000..ce29e56aee Binary files /dev/null and b/models/Holograms/cone.sw.vtx differ diff --git a/models/Holograms/cone.vvd b/models/Holograms/cone.vvd new file mode 100644 index 0000000000..63b882f517 Binary files /dev/null and b/models/Holograms/cone.vvd differ diff --git a/models/Holograms/cube.dx80.vtx b/models/Holograms/cube.dx80.vtx new file mode 100644 index 0000000000..aabee9dd9c Binary files /dev/null and b/models/Holograms/cube.dx80.vtx differ diff --git a/models/Holograms/cube.dx90.vtx b/models/Holograms/cube.dx90.vtx new file mode 100644 index 0000000000..e5c28194bd Binary files /dev/null and b/models/Holograms/cube.dx90.vtx differ diff --git a/models/Holograms/cube.mdl b/models/Holograms/cube.mdl new file mode 100644 index 0000000000..4a59e32182 Binary files /dev/null and b/models/Holograms/cube.mdl differ diff --git a/models/Holograms/cube.phy b/models/Holograms/cube.phy new file mode 100644 index 0000000000..cd09e6c5b4 Binary files /dev/null and b/models/Holograms/cube.phy differ diff --git a/models/Holograms/cube.sw.vtx b/models/Holograms/cube.sw.vtx new file mode 100644 index 0000000000..f743965dcd Binary files /dev/null and b/models/Holograms/cube.sw.vtx differ diff --git a/models/Holograms/cube.vvd b/models/Holograms/cube.vvd new file mode 100644 index 0000000000..1a225b0c2d Binary files /dev/null and b/models/Holograms/cube.vvd differ diff --git a/models/Holograms/cylinder.dx80.vtx b/models/Holograms/cylinder.dx80.vtx new file mode 100644 index 0000000000..64bdc59f82 Binary files /dev/null and b/models/Holograms/cylinder.dx80.vtx differ diff --git a/models/Holograms/cylinder.dx90.vtx b/models/Holograms/cylinder.dx90.vtx new file mode 100644 index 0000000000..5f61bbde5f Binary files /dev/null and b/models/Holograms/cylinder.dx90.vtx differ diff --git a/models/Holograms/cylinder.mdl b/models/Holograms/cylinder.mdl new file mode 100644 index 0000000000..086eee89e4 Binary files /dev/null and b/models/Holograms/cylinder.mdl differ diff --git a/models/Holograms/cylinder.phy b/models/Holograms/cylinder.phy new file mode 100644 index 0000000000..9c97198388 Binary files /dev/null and b/models/Holograms/cylinder.phy differ diff --git a/models/Holograms/cylinder.sw.vtx b/models/Holograms/cylinder.sw.vtx new file mode 100644 index 0000000000..8999829c5a Binary files /dev/null and b/models/Holograms/cylinder.sw.vtx differ diff --git a/models/Holograms/cylinder.vvd b/models/Holograms/cylinder.vvd new file mode 100644 index 0000000000..2f39b762e8 Binary files /dev/null and b/models/Holograms/cylinder.vvd differ diff --git a/models/Holograms/dome.dx80.vtx b/models/Holograms/dome.dx80.vtx new file mode 100644 index 0000000000..19a176c926 Binary files /dev/null and b/models/Holograms/dome.dx80.vtx differ diff --git a/models/Holograms/dome.dx90.vtx b/models/Holograms/dome.dx90.vtx new file mode 100644 index 0000000000..52a4c612f7 Binary files /dev/null and b/models/Holograms/dome.dx90.vtx differ diff --git a/models/Holograms/dome.mdl b/models/Holograms/dome.mdl new file mode 100644 index 0000000000..794ce02616 Binary files /dev/null and b/models/Holograms/dome.mdl differ diff --git a/models/Holograms/dome.phy b/models/Holograms/dome.phy new file mode 100644 index 0000000000..6b000308ae Binary files /dev/null and b/models/Holograms/dome.phy differ diff --git a/models/Holograms/dome.sw.vtx b/models/Holograms/dome.sw.vtx new file mode 100644 index 0000000000..4c177eb839 Binary files /dev/null and b/models/Holograms/dome.sw.vtx differ diff --git a/models/Holograms/dome.vvd b/models/Holograms/dome.vvd new file mode 100644 index 0000000000..9817b1d0a6 Binary files /dev/null and b/models/Holograms/dome.vvd differ diff --git a/models/Holograms/dome2.dx80.vtx b/models/Holograms/dome2.dx80.vtx new file mode 100644 index 0000000000..b2242e5807 Binary files /dev/null and b/models/Holograms/dome2.dx80.vtx differ diff --git a/models/Holograms/dome2.dx90.vtx b/models/Holograms/dome2.dx90.vtx new file mode 100644 index 0000000000..123c279bf3 Binary files /dev/null and b/models/Holograms/dome2.dx90.vtx differ diff --git a/models/Holograms/dome2.mdl b/models/Holograms/dome2.mdl new file mode 100644 index 0000000000..1e9ba64d61 Binary files /dev/null and b/models/Holograms/dome2.mdl differ diff --git a/models/Holograms/dome2.phy b/models/Holograms/dome2.phy new file mode 100644 index 0000000000..e79f52bc62 Binary files /dev/null and b/models/Holograms/dome2.phy differ diff --git a/models/Holograms/dome2.sw.vtx b/models/Holograms/dome2.sw.vtx new file mode 100644 index 0000000000..af9b7a036b Binary files /dev/null and b/models/Holograms/dome2.sw.vtx differ diff --git a/models/Holograms/dome2.vvd b/models/Holograms/dome2.vvd new file mode 100644 index 0000000000..02e46d46bc Binary files /dev/null and b/models/Holograms/dome2.vvd differ diff --git a/models/Holograms/hqcone.dx80.vtx b/models/Holograms/hqcone.dx80.vtx new file mode 100644 index 0000000000..83b28f1a43 Binary files /dev/null and b/models/Holograms/hqcone.dx80.vtx differ diff --git a/models/Holograms/hqcone.dx90.vtx b/models/Holograms/hqcone.dx90.vtx new file mode 100644 index 0000000000..88cbfbf454 Binary files /dev/null and b/models/Holograms/hqcone.dx90.vtx differ diff --git a/models/Holograms/hqcone.mdl b/models/Holograms/hqcone.mdl new file mode 100644 index 0000000000..3ea02f1510 Binary files /dev/null and b/models/Holograms/hqcone.mdl differ diff --git a/models/Holograms/hqcone.phy b/models/Holograms/hqcone.phy new file mode 100644 index 0000000000..8e095be64f Binary files /dev/null and b/models/Holograms/hqcone.phy differ diff --git a/models/Holograms/hqcone.sw.vtx b/models/Holograms/hqcone.sw.vtx new file mode 100644 index 0000000000..fb27fc2af6 Binary files /dev/null and b/models/Holograms/hqcone.sw.vtx differ diff --git a/models/Holograms/hqcone.vvd b/models/Holograms/hqcone.vvd new file mode 100644 index 0000000000..2c8b4f7d15 Binary files /dev/null and b/models/Holograms/hqcone.vvd differ diff --git a/models/Holograms/hqcylinder.dx80.vtx b/models/Holograms/hqcylinder.dx80.vtx new file mode 100644 index 0000000000..0ed9fd5918 Binary files /dev/null and b/models/Holograms/hqcylinder.dx80.vtx differ diff --git a/models/Holograms/hqcylinder.dx90.vtx b/models/Holograms/hqcylinder.dx90.vtx new file mode 100644 index 0000000000..843bc1a241 Binary files /dev/null and b/models/Holograms/hqcylinder.dx90.vtx differ diff --git a/models/Holograms/hqcylinder.mdl b/models/Holograms/hqcylinder.mdl new file mode 100644 index 0000000000..b004df52a8 Binary files /dev/null and b/models/Holograms/hqcylinder.mdl differ diff --git a/models/Holograms/hqcylinder.phy b/models/Holograms/hqcylinder.phy new file mode 100644 index 0000000000..f2d1785505 Binary files /dev/null and b/models/Holograms/hqcylinder.phy differ diff --git a/models/Holograms/hqcylinder.sw.vtx b/models/Holograms/hqcylinder.sw.vtx new file mode 100644 index 0000000000..e033afd4bc Binary files /dev/null and b/models/Holograms/hqcylinder.sw.vtx differ diff --git a/models/Holograms/hqcylinder.vvd b/models/Holograms/hqcylinder.vvd new file mode 100644 index 0000000000..8526ca6a2d Binary files /dev/null and b/models/Holograms/hqcylinder.vvd differ diff --git a/models/Holograms/hqcylinder2.dx80.vtx b/models/Holograms/hqcylinder2.dx80.vtx new file mode 100644 index 0000000000..e6c7f15ab4 Binary files /dev/null and b/models/Holograms/hqcylinder2.dx80.vtx differ diff --git a/models/Holograms/hqcylinder2.dx90.vtx b/models/Holograms/hqcylinder2.dx90.vtx new file mode 100644 index 0000000000..a1c00d97a0 Binary files /dev/null and b/models/Holograms/hqcylinder2.dx90.vtx differ diff --git a/models/Holograms/hqcylinder2.mdl b/models/Holograms/hqcylinder2.mdl new file mode 100644 index 0000000000..be3b577e3a Binary files /dev/null and b/models/Holograms/hqcylinder2.mdl differ diff --git a/models/Holograms/hqcylinder2.phy b/models/Holograms/hqcylinder2.phy new file mode 100644 index 0000000000..aa39560582 Binary files /dev/null and b/models/Holograms/hqcylinder2.phy differ diff --git a/models/Holograms/hqcylinder2.sw.vtx b/models/Holograms/hqcylinder2.sw.vtx new file mode 100644 index 0000000000..4d454a3dba Binary files /dev/null and b/models/Holograms/hqcylinder2.sw.vtx differ diff --git a/models/Holograms/hqcylinder2.vvd b/models/Holograms/hqcylinder2.vvd new file mode 100644 index 0000000000..c15b79d940 Binary files /dev/null and b/models/Holograms/hqcylinder2.vvd differ diff --git a/models/Holograms/hqicosphere.dx80.vtx b/models/Holograms/hqicosphere.dx80.vtx new file mode 100644 index 0000000000..21cb031fd8 Binary files /dev/null and b/models/Holograms/hqicosphere.dx80.vtx differ diff --git a/models/Holograms/hqicosphere.dx90.vtx b/models/Holograms/hqicosphere.dx90.vtx new file mode 100644 index 0000000000..6d32dfd027 Binary files /dev/null and b/models/Holograms/hqicosphere.dx90.vtx differ diff --git a/models/Holograms/hqicosphere.mdl b/models/Holograms/hqicosphere.mdl new file mode 100644 index 0000000000..0e0ee5b53f Binary files /dev/null and b/models/Holograms/hqicosphere.mdl differ diff --git a/models/Holograms/hqicosphere.phy b/models/Holograms/hqicosphere.phy new file mode 100644 index 0000000000..cadfc6ca14 Binary files /dev/null and b/models/Holograms/hqicosphere.phy differ diff --git a/models/Holograms/hqicosphere.sw.vtx b/models/Holograms/hqicosphere.sw.vtx new file mode 100644 index 0000000000..125b51dfe4 Binary files /dev/null and b/models/Holograms/hqicosphere.sw.vtx differ diff --git a/models/Holograms/hqicosphere.vvd b/models/Holograms/hqicosphere.vvd new file mode 100644 index 0000000000..d2bef47efe Binary files /dev/null and b/models/Holograms/hqicosphere.vvd differ diff --git a/models/Holograms/hqicosphere2.dx80.vtx b/models/Holograms/hqicosphere2.dx80.vtx new file mode 100644 index 0000000000..119e5172fd Binary files /dev/null and b/models/Holograms/hqicosphere2.dx80.vtx differ diff --git a/models/Holograms/hqicosphere2.dx90.vtx b/models/Holograms/hqicosphere2.dx90.vtx new file mode 100644 index 0000000000..f40c0d8eab Binary files /dev/null and b/models/Holograms/hqicosphere2.dx90.vtx differ diff --git a/models/Holograms/hqicosphere2.mdl b/models/Holograms/hqicosphere2.mdl new file mode 100644 index 0000000000..1e9c2a534b Binary files /dev/null and b/models/Holograms/hqicosphere2.mdl differ diff --git a/models/Holograms/hqicosphere2.phy b/models/Holograms/hqicosphere2.phy new file mode 100644 index 0000000000..28c4fd1582 Binary files /dev/null and b/models/Holograms/hqicosphere2.phy differ diff --git a/models/Holograms/hqicosphere2.sw.vtx b/models/Holograms/hqicosphere2.sw.vtx new file mode 100644 index 0000000000..01a47f39df Binary files /dev/null and b/models/Holograms/hqicosphere2.sw.vtx differ diff --git a/models/Holograms/hqicosphere2.vvd b/models/Holograms/hqicosphere2.vvd new file mode 100644 index 0000000000..525608f96c Binary files /dev/null and b/models/Holograms/hqicosphere2.vvd differ diff --git a/models/Holograms/hqsphere.dx80.vtx b/models/Holograms/hqsphere.dx80.vtx new file mode 100644 index 0000000000..8c81eeff0a Binary files /dev/null and b/models/Holograms/hqsphere.dx80.vtx differ diff --git a/models/Holograms/hqsphere.dx90.vtx b/models/Holograms/hqsphere.dx90.vtx new file mode 100644 index 0000000000..e98fdf69d2 Binary files /dev/null and b/models/Holograms/hqsphere.dx90.vtx differ diff --git a/models/Holograms/hqsphere.mdl b/models/Holograms/hqsphere.mdl new file mode 100644 index 0000000000..1b6443049c Binary files /dev/null and b/models/Holograms/hqsphere.mdl differ diff --git a/models/Holograms/hqsphere.phy b/models/Holograms/hqsphere.phy new file mode 100644 index 0000000000..bbac809b09 Binary files /dev/null and b/models/Holograms/hqsphere.phy differ diff --git a/models/Holograms/hqsphere.sw.vtx b/models/Holograms/hqsphere.sw.vtx new file mode 100644 index 0000000000..361084e191 Binary files /dev/null and b/models/Holograms/hqsphere.sw.vtx differ diff --git a/models/Holograms/hqsphere.vvd b/models/Holograms/hqsphere.vvd new file mode 100644 index 0000000000..2e1fd06dfe Binary files /dev/null and b/models/Holograms/hqsphere.vvd differ diff --git a/models/Holograms/hqsphere2.dx80.vtx b/models/Holograms/hqsphere2.dx80.vtx new file mode 100644 index 0000000000..9989064097 Binary files /dev/null and b/models/Holograms/hqsphere2.dx80.vtx differ diff --git a/models/Holograms/hqsphere2.dx90.vtx b/models/Holograms/hqsphere2.dx90.vtx new file mode 100644 index 0000000000..fb76cdd816 Binary files /dev/null and b/models/Holograms/hqsphere2.dx90.vtx differ diff --git a/models/Holograms/hqsphere2.mdl b/models/Holograms/hqsphere2.mdl new file mode 100644 index 0000000000..93f0f77efc Binary files /dev/null and b/models/Holograms/hqsphere2.mdl differ diff --git a/models/Holograms/hqsphere2.phy b/models/Holograms/hqsphere2.phy new file mode 100644 index 0000000000..3219563b6f Binary files /dev/null and b/models/Holograms/hqsphere2.phy differ diff --git a/models/Holograms/hqsphere2.sw.vtx b/models/Holograms/hqsphere2.sw.vtx new file mode 100644 index 0000000000..ef2b43138c Binary files /dev/null and b/models/Holograms/hqsphere2.sw.vtx differ diff --git a/models/Holograms/hqsphere2.vvd b/models/Holograms/hqsphere2.vvd new file mode 100644 index 0000000000..c807a0d640 Binary files /dev/null and b/models/Holograms/hqsphere2.vvd differ diff --git a/models/Holograms/hqtorus.dx80.vtx b/models/Holograms/hqtorus.dx80.vtx new file mode 100644 index 0000000000..779cc964ed Binary files /dev/null and b/models/Holograms/hqtorus.dx80.vtx differ diff --git a/models/Holograms/hqtorus.dx90.vtx b/models/Holograms/hqtorus.dx90.vtx new file mode 100644 index 0000000000..04ef9c968d Binary files /dev/null and b/models/Holograms/hqtorus.dx90.vtx differ diff --git a/models/Holograms/hqtorus.mdl b/models/Holograms/hqtorus.mdl new file mode 100644 index 0000000000..67f1b96889 Binary files /dev/null and b/models/Holograms/hqtorus.mdl differ diff --git a/models/Holograms/hqtorus.phy b/models/Holograms/hqtorus.phy new file mode 100644 index 0000000000..5f9d7146f3 Binary files /dev/null and b/models/Holograms/hqtorus.phy differ diff --git a/models/Holograms/hqtorus.sw.vtx b/models/Holograms/hqtorus.sw.vtx new file mode 100644 index 0000000000..077a2b08ef Binary files /dev/null and b/models/Holograms/hqtorus.sw.vtx differ diff --git a/models/Holograms/hqtorus.vvd b/models/Holograms/hqtorus.vvd new file mode 100644 index 0000000000..6bbbc6bf9d Binary files /dev/null and b/models/Holograms/hqtorus.vvd differ diff --git a/models/Holograms/hqtorus2.dx80.vtx b/models/Holograms/hqtorus2.dx80.vtx new file mode 100644 index 0000000000..1b15801a60 Binary files /dev/null and b/models/Holograms/hqtorus2.dx80.vtx differ diff --git a/models/Holograms/hqtorus2.dx90.vtx b/models/Holograms/hqtorus2.dx90.vtx new file mode 100644 index 0000000000..b911ed76e4 Binary files /dev/null and b/models/Holograms/hqtorus2.dx90.vtx differ diff --git a/models/Holograms/hqtorus2.mdl b/models/Holograms/hqtorus2.mdl new file mode 100644 index 0000000000..3e5c6f80a7 Binary files /dev/null and b/models/Holograms/hqtorus2.mdl differ diff --git a/models/Holograms/hqtorus2.phy b/models/Holograms/hqtorus2.phy new file mode 100644 index 0000000000..fb1ab979ff Binary files /dev/null and b/models/Holograms/hqtorus2.phy differ diff --git a/models/Holograms/hqtorus2.sw.vtx b/models/Holograms/hqtorus2.sw.vtx new file mode 100644 index 0000000000..db22ff6fd4 Binary files /dev/null and b/models/Holograms/hqtorus2.sw.vtx differ diff --git a/models/Holograms/hqtorus2.vvd b/models/Holograms/hqtorus2.vvd new file mode 100644 index 0000000000..145ab94819 Binary files /dev/null and b/models/Holograms/hqtorus2.vvd differ diff --git a/models/Holograms/icosphere.dx80.vtx b/models/Holograms/icosphere.dx80.vtx new file mode 100644 index 0000000000..a5c443d6b7 Binary files /dev/null and b/models/Holograms/icosphere.dx80.vtx differ diff --git a/models/Holograms/icosphere.dx90.vtx b/models/Holograms/icosphere.dx90.vtx new file mode 100644 index 0000000000..72be1a0ed7 Binary files /dev/null and b/models/Holograms/icosphere.dx90.vtx differ diff --git a/models/Holograms/icosphere.mdl b/models/Holograms/icosphere.mdl new file mode 100644 index 0000000000..a915d99668 Binary files /dev/null and b/models/Holograms/icosphere.mdl differ diff --git a/models/Holograms/icosphere.phy b/models/Holograms/icosphere.phy new file mode 100644 index 0000000000..cb16e15bb6 Binary files /dev/null and b/models/Holograms/icosphere.phy differ diff --git a/models/Holograms/icosphere.sw.vtx b/models/Holograms/icosphere.sw.vtx new file mode 100644 index 0000000000..6bfb082e53 Binary files /dev/null and b/models/Holograms/icosphere.sw.vtx differ diff --git a/models/Holograms/icosphere.vvd b/models/Holograms/icosphere.vvd new file mode 100644 index 0000000000..921ebc7ef9 Binary files /dev/null and b/models/Holograms/icosphere.vvd differ diff --git a/models/Holograms/icosphere2.dx80.vtx b/models/Holograms/icosphere2.dx80.vtx new file mode 100644 index 0000000000..0025889ca2 Binary files /dev/null and b/models/Holograms/icosphere2.dx80.vtx differ diff --git a/models/Holograms/icosphere2.dx90.vtx b/models/Holograms/icosphere2.dx90.vtx new file mode 100644 index 0000000000..9aa0a1afd7 Binary files /dev/null and b/models/Holograms/icosphere2.dx90.vtx differ diff --git a/models/Holograms/icosphere2.mdl b/models/Holograms/icosphere2.mdl new file mode 100644 index 0000000000..12111f3a91 Binary files /dev/null and b/models/Holograms/icosphere2.mdl differ diff --git a/models/Holograms/icosphere2.phy b/models/Holograms/icosphere2.phy new file mode 100644 index 0000000000..0e3f4ecfc5 Binary files /dev/null and b/models/Holograms/icosphere2.phy differ diff --git a/models/Holograms/icosphere2.sw.vtx b/models/Holograms/icosphere2.sw.vtx new file mode 100644 index 0000000000..f042ac8d65 Binary files /dev/null and b/models/Holograms/icosphere2.sw.vtx differ diff --git a/models/Holograms/icosphere2.vvd b/models/Holograms/icosphere2.vvd new file mode 100644 index 0000000000..1ae70ac3cb Binary files /dev/null and b/models/Holograms/icosphere2.vvd differ diff --git a/models/Holograms/icosphere3.dx80.vtx b/models/Holograms/icosphere3.dx80.vtx new file mode 100644 index 0000000000..892386d739 Binary files /dev/null and b/models/Holograms/icosphere3.dx80.vtx differ diff --git a/models/Holograms/icosphere3.dx90.vtx b/models/Holograms/icosphere3.dx90.vtx new file mode 100644 index 0000000000..bd2237bfdf Binary files /dev/null and b/models/Holograms/icosphere3.dx90.vtx differ diff --git a/models/Holograms/icosphere3.mdl b/models/Holograms/icosphere3.mdl new file mode 100644 index 0000000000..5e2e767eba Binary files /dev/null and b/models/Holograms/icosphere3.mdl differ diff --git a/models/Holograms/icosphere3.phy b/models/Holograms/icosphere3.phy new file mode 100644 index 0000000000..067330492f Binary files /dev/null and b/models/Holograms/icosphere3.phy differ diff --git a/models/Holograms/icosphere3.sw.vtx b/models/Holograms/icosphere3.sw.vtx new file mode 100644 index 0000000000..a6e96be809 Binary files /dev/null and b/models/Holograms/icosphere3.sw.vtx differ diff --git a/models/Holograms/icosphere3.vvd b/models/Holograms/icosphere3.vvd new file mode 100644 index 0000000000..884d9e1b42 Binary files /dev/null and b/models/Holograms/icosphere3.vvd differ diff --git a/models/Holograms/plane.dx80.vtx b/models/Holograms/plane.dx80.vtx new file mode 100644 index 0000000000..4592b16a79 Binary files /dev/null and b/models/Holograms/plane.dx80.vtx differ diff --git a/models/Holograms/plane.dx90.vtx b/models/Holograms/plane.dx90.vtx new file mode 100644 index 0000000000..000186b193 Binary files /dev/null and b/models/Holograms/plane.dx90.vtx differ diff --git a/models/Holograms/plane.mdl b/models/Holograms/plane.mdl new file mode 100644 index 0000000000..1abf1578aa Binary files /dev/null and b/models/Holograms/plane.mdl differ diff --git a/models/Holograms/plane.phy b/models/Holograms/plane.phy new file mode 100644 index 0000000000..365a5e525b Binary files /dev/null and b/models/Holograms/plane.phy differ diff --git a/models/Holograms/plane.sw.vtx b/models/Holograms/plane.sw.vtx new file mode 100644 index 0000000000..51934d87a7 Binary files /dev/null and b/models/Holograms/plane.sw.vtx differ diff --git a/models/Holograms/plane.vvd b/models/Holograms/plane.vvd new file mode 100644 index 0000000000..9fb3ee2140 Binary files /dev/null and b/models/Holograms/plane.vvd differ diff --git a/models/Holograms/prism.dx80.vtx b/models/Holograms/prism.dx80.vtx new file mode 100644 index 0000000000..360344c438 Binary files /dev/null and b/models/Holograms/prism.dx80.vtx differ diff --git a/models/Holograms/prism.dx90.vtx b/models/Holograms/prism.dx90.vtx new file mode 100644 index 0000000000..9f93313ac1 Binary files /dev/null and b/models/Holograms/prism.dx90.vtx differ diff --git a/models/Holograms/prism.mdl b/models/Holograms/prism.mdl new file mode 100644 index 0000000000..d1d98959d1 Binary files /dev/null and b/models/Holograms/prism.mdl differ diff --git a/models/Holograms/prism.phy b/models/Holograms/prism.phy new file mode 100644 index 0000000000..611ed232e2 Binary files /dev/null and b/models/Holograms/prism.phy differ diff --git a/models/Holograms/prism.sw.vtx b/models/Holograms/prism.sw.vtx new file mode 100644 index 0000000000..f63c4406e6 Binary files /dev/null and b/models/Holograms/prism.sw.vtx differ diff --git a/models/Holograms/prism.vvd b/models/Holograms/prism.vvd new file mode 100644 index 0000000000..b16d8fc573 Binary files /dev/null and b/models/Holograms/prism.vvd differ diff --git a/models/Holograms/pyramid.dx80.vtx b/models/Holograms/pyramid.dx80.vtx new file mode 100644 index 0000000000..427abcd31f Binary files /dev/null and b/models/Holograms/pyramid.dx80.vtx differ diff --git a/models/Holograms/pyramid.dx90.vtx b/models/Holograms/pyramid.dx90.vtx new file mode 100644 index 0000000000..023e7f2571 Binary files /dev/null and b/models/Holograms/pyramid.dx90.vtx differ diff --git a/models/Holograms/pyramid.mdl b/models/Holograms/pyramid.mdl new file mode 100644 index 0000000000..bced00ef90 Binary files /dev/null and b/models/Holograms/pyramid.mdl differ diff --git a/models/Holograms/pyramid.phy b/models/Holograms/pyramid.phy new file mode 100644 index 0000000000..20aca96f86 Binary files /dev/null and b/models/Holograms/pyramid.phy differ diff --git a/models/Holograms/pyramid.sw.vtx b/models/Holograms/pyramid.sw.vtx new file mode 100644 index 0000000000..aa51a933a1 Binary files /dev/null and b/models/Holograms/pyramid.sw.vtx differ diff --git a/models/Holograms/pyramid.vvd b/models/Holograms/pyramid.vvd new file mode 100644 index 0000000000..59b34d7dbd Binary files /dev/null and b/models/Holograms/pyramid.vvd differ diff --git a/models/Holograms/sphere.dx80.vtx b/models/Holograms/sphere.dx80.vtx new file mode 100644 index 0000000000..170f7f4b09 Binary files /dev/null and b/models/Holograms/sphere.dx80.vtx differ diff --git a/models/Holograms/sphere.dx90.vtx b/models/Holograms/sphere.dx90.vtx new file mode 100644 index 0000000000..2664c381f1 Binary files /dev/null and b/models/Holograms/sphere.dx90.vtx differ diff --git a/models/Holograms/sphere.mdl b/models/Holograms/sphere.mdl new file mode 100644 index 0000000000..ab892ce96f Binary files /dev/null and b/models/Holograms/sphere.mdl differ diff --git a/models/Holograms/sphere.phy b/models/Holograms/sphere.phy new file mode 100644 index 0000000000..c094ce42a6 Binary files /dev/null and b/models/Holograms/sphere.phy differ diff --git a/models/Holograms/sphere.sw.vtx b/models/Holograms/sphere.sw.vtx new file mode 100644 index 0000000000..b35e7014c1 Binary files /dev/null and b/models/Holograms/sphere.sw.vtx differ diff --git a/models/Holograms/sphere.vvd b/models/Holograms/sphere.vvd new file mode 100644 index 0000000000..13d36d7d93 Binary files /dev/null and b/models/Holograms/sphere.vvd differ diff --git a/models/Holograms/sphere2.dx80.vtx b/models/Holograms/sphere2.dx80.vtx new file mode 100644 index 0000000000..e6722866a9 Binary files /dev/null and b/models/Holograms/sphere2.dx80.vtx differ diff --git a/models/Holograms/sphere2.dx90.vtx b/models/Holograms/sphere2.dx90.vtx new file mode 100644 index 0000000000..b7065c419e Binary files /dev/null and b/models/Holograms/sphere2.dx90.vtx differ diff --git a/models/Holograms/sphere2.mdl b/models/Holograms/sphere2.mdl new file mode 100644 index 0000000000..b63f012365 Binary files /dev/null and b/models/Holograms/sphere2.mdl differ diff --git a/models/Holograms/sphere2.phy b/models/Holograms/sphere2.phy new file mode 100644 index 0000000000..e75916e899 Binary files /dev/null and b/models/Holograms/sphere2.phy differ diff --git a/models/Holograms/sphere2.sw.vtx b/models/Holograms/sphere2.sw.vtx new file mode 100644 index 0000000000..01365e8b99 Binary files /dev/null and b/models/Holograms/sphere2.sw.vtx differ diff --git a/models/Holograms/sphere2.vvd b/models/Holograms/sphere2.vvd new file mode 100644 index 0000000000..c6ee9764ec Binary files /dev/null and b/models/Holograms/sphere2.vvd differ diff --git a/models/Holograms/sphere3.dx80.vtx b/models/Holograms/sphere3.dx80.vtx new file mode 100644 index 0000000000..c44f25c3c7 Binary files /dev/null and b/models/Holograms/sphere3.dx80.vtx differ diff --git a/models/Holograms/sphere3.dx90.vtx b/models/Holograms/sphere3.dx90.vtx new file mode 100644 index 0000000000..1999cd0174 Binary files /dev/null and b/models/Holograms/sphere3.dx90.vtx differ diff --git a/models/Holograms/sphere3.mdl b/models/Holograms/sphere3.mdl new file mode 100644 index 0000000000..893decb127 Binary files /dev/null and b/models/Holograms/sphere3.mdl differ diff --git a/models/Holograms/sphere3.phy b/models/Holograms/sphere3.phy new file mode 100644 index 0000000000..2265e47f47 Binary files /dev/null and b/models/Holograms/sphere3.phy differ diff --git a/models/Holograms/sphere3.sw.vtx b/models/Holograms/sphere3.sw.vtx new file mode 100644 index 0000000000..737527cd51 Binary files /dev/null and b/models/Holograms/sphere3.sw.vtx differ diff --git a/models/Holograms/sphere3.vvd b/models/Holograms/sphere3.vvd new file mode 100644 index 0000000000..ee9aa8ef16 Binary files /dev/null and b/models/Holograms/sphere3.vvd differ diff --git a/models/Holograms/tetra.dx80.vtx b/models/Holograms/tetra.dx80.vtx new file mode 100644 index 0000000000..f1ca058da6 Binary files /dev/null and b/models/Holograms/tetra.dx80.vtx differ diff --git a/models/Holograms/tetra.dx90.vtx b/models/Holograms/tetra.dx90.vtx new file mode 100644 index 0000000000..0dd05eb241 Binary files /dev/null and b/models/Holograms/tetra.dx90.vtx differ diff --git a/models/Holograms/tetra.mdl b/models/Holograms/tetra.mdl new file mode 100644 index 0000000000..968b2419bd Binary files /dev/null and b/models/Holograms/tetra.mdl differ diff --git a/models/Holograms/tetra.phy b/models/Holograms/tetra.phy new file mode 100644 index 0000000000..d863724434 Binary files /dev/null and b/models/Holograms/tetra.phy differ diff --git a/models/Holograms/tetra.sw.vtx b/models/Holograms/tetra.sw.vtx new file mode 100644 index 0000000000..2f8f6ab638 Binary files /dev/null and b/models/Holograms/tetra.sw.vtx differ diff --git a/models/Holograms/tetra.vvd b/models/Holograms/tetra.vvd new file mode 100644 index 0000000000..a55b9cba39 Binary files /dev/null and b/models/Holograms/tetra.vvd differ diff --git a/models/Holograms/torus.dx80.vtx b/models/Holograms/torus.dx80.vtx new file mode 100644 index 0000000000..fb9936a5d6 Binary files /dev/null and b/models/Holograms/torus.dx80.vtx differ diff --git a/models/Holograms/torus.dx90.vtx b/models/Holograms/torus.dx90.vtx new file mode 100644 index 0000000000..e67423e50a Binary files /dev/null and b/models/Holograms/torus.dx90.vtx differ diff --git a/models/Holograms/torus.mdl b/models/Holograms/torus.mdl new file mode 100644 index 0000000000..11d95fd0cf Binary files /dev/null and b/models/Holograms/torus.mdl differ diff --git a/models/Holograms/torus.phy b/models/Holograms/torus.phy new file mode 100644 index 0000000000..aeec5cc22f Binary files /dev/null and b/models/Holograms/torus.phy differ diff --git a/models/Holograms/torus.sw.vtx b/models/Holograms/torus.sw.vtx new file mode 100644 index 0000000000..223d2e9bca Binary files /dev/null and b/models/Holograms/torus.sw.vtx differ diff --git a/models/Holograms/torus.vvd b/models/Holograms/torus.vvd new file mode 100644 index 0000000000..d02a3196e8 Binary files /dev/null and b/models/Holograms/torus.vvd differ diff --git a/models/Holograms/torus2.dx80.vtx b/models/Holograms/torus2.dx80.vtx new file mode 100644 index 0000000000..938437a246 Binary files /dev/null and b/models/Holograms/torus2.dx80.vtx differ diff --git a/models/Holograms/torus2.dx90.vtx b/models/Holograms/torus2.dx90.vtx new file mode 100644 index 0000000000..8a9337da92 Binary files /dev/null and b/models/Holograms/torus2.dx90.vtx differ diff --git a/models/Holograms/torus2.mdl b/models/Holograms/torus2.mdl new file mode 100644 index 0000000000..4bc66c32f3 Binary files /dev/null and b/models/Holograms/torus2.mdl differ diff --git a/models/Holograms/torus2.phy b/models/Holograms/torus2.phy new file mode 100644 index 0000000000..9a9659a087 Binary files /dev/null and b/models/Holograms/torus2.phy differ diff --git a/models/Holograms/torus2.sw.vtx b/models/Holograms/torus2.sw.vtx new file mode 100644 index 0000000000..74e8c8c407 Binary files /dev/null and b/models/Holograms/torus2.sw.vtx differ diff --git a/models/Holograms/torus2.vvd b/models/Holograms/torus2.vvd new file mode 100644 index 0000000000..36245efd08 Binary files /dev/null and b/models/Holograms/torus2.vvd differ diff --git a/models/Holograms/torus3.dx80.vtx b/models/Holograms/torus3.dx80.vtx new file mode 100644 index 0000000000..1ac60697cf Binary files /dev/null and b/models/Holograms/torus3.dx80.vtx differ diff --git a/models/Holograms/torus3.dx90.vtx b/models/Holograms/torus3.dx90.vtx new file mode 100644 index 0000000000..6c15b7632c Binary files /dev/null and b/models/Holograms/torus3.dx90.vtx differ diff --git a/models/Holograms/torus3.mdl b/models/Holograms/torus3.mdl new file mode 100644 index 0000000000..6ffe3aa26a Binary files /dev/null and b/models/Holograms/torus3.mdl differ diff --git a/models/Holograms/torus3.phy b/models/Holograms/torus3.phy new file mode 100644 index 0000000000..0f9fda6814 Binary files /dev/null and b/models/Holograms/torus3.phy differ diff --git a/models/Holograms/torus3.sw.vtx b/models/Holograms/torus3.sw.vtx new file mode 100644 index 0000000000..b9f8d22412 Binary files /dev/null and b/models/Holograms/torus3.sw.vtx differ diff --git a/models/Holograms/torus3.vvd b/models/Holograms/torus3.vvd new file mode 100644 index 0000000000..4ce7880d4f Binary files /dev/null and b/models/Holograms/torus3.vvd differ diff --git a/models/holograms/Holograms/cone.dx80.vtx b/models/holograms/Holograms/cone.dx80.vtx new file mode 100644 index 0000000000..7d12a13cb7 Binary files /dev/null and b/models/holograms/Holograms/cone.dx80.vtx differ diff --git a/models/holograms/Holograms/cone.dx90.vtx b/models/holograms/Holograms/cone.dx90.vtx new file mode 100644 index 0000000000..a39b6bd84a Binary files /dev/null and b/models/holograms/Holograms/cone.dx90.vtx differ diff --git a/models/holograms/Holograms/cone.mdl b/models/holograms/Holograms/cone.mdl new file mode 100644 index 0000000000..ae77ee72e2 Binary files /dev/null and b/models/holograms/Holograms/cone.mdl differ diff --git a/models/holograms/Holograms/cone.phy b/models/holograms/Holograms/cone.phy new file mode 100644 index 0000000000..bbb7034e69 Binary files /dev/null and b/models/holograms/Holograms/cone.phy differ diff --git a/models/holograms/Holograms/cone.sw.vtx b/models/holograms/Holograms/cone.sw.vtx new file mode 100644 index 0000000000..ce29e56aee Binary files /dev/null and b/models/holograms/Holograms/cone.sw.vtx differ diff --git a/models/holograms/Holograms/cone.vvd b/models/holograms/Holograms/cone.vvd new file mode 100644 index 0000000000..63b882f517 Binary files /dev/null and b/models/holograms/Holograms/cone.vvd differ diff --git a/models/holograms/Holograms/cube.dx80.vtx b/models/holograms/Holograms/cube.dx80.vtx new file mode 100644 index 0000000000..aabee9dd9c Binary files /dev/null and b/models/holograms/Holograms/cube.dx80.vtx differ diff --git a/models/holograms/Holograms/cube.dx90.vtx b/models/holograms/Holograms/cube.dx90.vtx new file mode 100644 index 0000000000..e5c28194bd Binary files /dev/null and b/models/holograms/Holograms/cube.dx90.vtx differ diff --git a/models/holograms/Holograms/cube.mdl b/models/holograms/Holograms/cube.mdl new file mode 100644 index 0000000000..4a59e32182 Binary files /dev/null and b/models/holograms/Holograms/cube.mdl differ diff --git a/models/holograms/Holograms/cube.phy b/models/holograms/Holograms/cube.phy new file mode 100644 index 0000000000..cd09e6c5b4 Binary files /dev/null and b/models/holograms/Holograms/cube.phy differ diff --git a/models/holograms/Holograms/cube.sw.vtx b/models/holograms/Holograms/cube.sw.vtx new file mode 100644 index 0000000000..f743965dcd Binary files /dev/null and b/models/holograms/Holograms/cube.sw.vtx differ diff --git a/models/holograms/Holograms/cube.vvd b/models/holograms/Holograms/cube.vvd new file mode 100644 index 0000000000..1a225b0c2d Binary files /dev/null and b/models/holograms/Holograms/cube.vvd differ diff --git a/models/holograms/Holograms/cylinder.dx80.vtx b/models/holograms/Holograms/cylinder.dx80.vtx new file mode 100644 index 0000000000..64bdc59f82 Binary files /dev/null and b/models/holograms/Holograms/cylinder.dx80.vtx differ diff --git a/models/holograms/Holograms/cylinder.dx90.vtx b/models/holograms/Holograms/cylinder.dx90.vtx new file mode 100644 index 0000000000..5f61bbde5f Binary files /dev/null and b/models/holograms/Holograms/cylinder.dx90.vtx differ diff --git a/models/holograms/Holograms/cylinder.mdl b/models/holograms/Holograms/cylinder.mdl new file mode 100644 index 0000000000..086eee89e4 Binary files /dev/null and b/models/holograms/Holograms/cylinder.mdl differ diff --git a/models/holograms/Holograms/cylinder.phy b/models/holograms/Holograms/cylinder.phy new file mode 100644 index 0000000000..9c97198388 Binary files /dev/null and b/models/holograms/Holograms/cylinder.phy differ diff --git a/models/holograms/Holograms/cylinder.sw.vtx b/models/holograms/Holograms/cylinder.sw.vtx new file mode 100644 index 0000000000..8999829c5a Binary files /dev/null and b/models/holograms/Holograms/cylinder.sw.vtx differ diff --git a/models/holograms/Holograms/cylinder.vvd b/models/holograms/Holograms/cylinder.vvd new file mode 100644 index 0000000000..2f39b762e8 Binary files /dev/null and b/models/holograms/Holograms/cylinder.vvd differ diff --git a/models/holograms/Holograms/dome.dx80.vtx b/models/holograms/Holograms/dome.dx80.vtx new file mode 100644 index 0000000000..19a176c926 Binary files /dev/null and b/models/holograms/Holograms/dome.dx80.vtx differ diff --git a/models/holograms/Holograms/dome.dx90.vtx b/models/holograms/Holograms/dome.dx90.vtx new file mode 100644 index 0000000000..52a4c612f7 Binary files /dev/null and b/models/holograms/Holograms/dome.dx90.vtx differ diff --git a/models/holograms/Holograms/dome.mdl b/models/holograms/Holograms/dome.mdl new file mode 100644 index 0000000000..794ce02616 Binary files /dev/null and b/models/holograms/Holograms/dome.mdl differ diff --git a/models/holograms/Holograms/dome.phy b/models/holograms/Holograms/dome.phy new file mode 100644 index 0000000000..6b000308ae Binary files /dev/null and b/models/holograms/Holograms/dome.phy differ diff --git a/models/holograms/Holograms/dome.sw.vtx b/models/holograms/Holograms/dome.sw.vtx new file mode 100644 index 0000000000..4c177eb839 Binary files /dev/null and b/models/holograms/Holograms/dome.sw.vtx differ diff --git a/models/holograms/Holograms/dome.vvd b/models/holograms/Holograms/dome.vvd new file mode 100644 index 0000000000..9817b1d0a6 Binary files /dev/null and b/models/holograms/Holograms/dome.vvd differ diff --git a/models/holograms/Holograms/dome2.dx80.vtx b/models/holograms/Holograms/dome2.dx80.vtx new file mode 100644 index 0000000000..b2242e5807 Binary files /dev/null and b/models/holograms/Holograms/dome2.dx80.vtx differ diff --git a/models/holograms/Holograms/dome2.dx90.vtx b/models/holograms/Holograms/dome2.dx90.vtx new file mode 100644 index 0000000000..123c279bf3 Binary files /dev/null and b/models/holograms/Holograms/dome2.dx90.vtx differ diff --git a/models/holograms/Holograms/dome2.mdl b/models/holograms/Holograms/dome2.mdl new file mode 100644 index 0000000000..1e9ba64d61 Binary files /dev/null and b/models/holograms/Holograms/dome2.mdl differ diff --git a/models/holograms/Holograms/dome2.phy b/models/holograms/Holograms/dome2.phy new file mode 100644 index 0000000000..e79f52bc62 Binary files /dev/null and b/models/holograms/Holograms/dome2.phy differ diff --git a/models/holograms/Holograms/dome2.sw.vtx b/models/holograms/Holograms/dome2.sw.vtx new file mode 100644 index 0000000000..af9b7a036b Binary files /dev/null and b/models/holograms/Holograms/dome2.sw.vtx differ diff --git a/models/holograms/Holograms/dome2.vvd b/models/holograms/Holograms/dome2.vvd new file mode 100644 index 0000000000..02e46d46bc Binary files /dev/null and b/models/holograms/Holograms/dome2.vvd differ diff --git a/models/holograms/Holograms/hqcone.dx80.vtx b/models/holograms/Holograms/hqcone.dx80.vtx new file mode 100644 index 0000000000..83b28f1a43 Binary files /dev/null and b/models/holograms/Holograms/hqcone.dx80.vtx differ diff --git a/models/holograms/Holograms/hqcone.dx90.vtx b/models/holograms/Holograms/hqcone.dx90.vtx new file mode 100644 index 0000000000..88cbfbf454 Binary files /dev/null and b/models/holograms/Holograms/hqcone.dx90.vtx differ diff --git a/models/holograms/Holograms/hqcone.mdl b/models/holograms/Holograms/hqcone.mdl new file mode 100644 index 0000000000..3ea02f1510 Binary files /dev/null and b/models/holograms/Holograms/hqcone.mdl differ diff --git a/models/holograms/Holograms/hqcone.phy b/models/holograms/Holograms/hqcone.phy new file mode 100644 index 0000000000..8e095be64f Binary files /dev/null and b/models/holograms/Holograms/hqcone.phy differ diff --git a/models/holograms/Holograms/hqcone.sw.vtx b/models/holograms/Holograms/hqcone.sw.vtx new file mode 100644 index 0000000000..fb27fc2af6 Binary files /dev/null and b/models/holograms/Holograms/hqcone.sw.vtx differ diff --git a/models/holograms/Holograms/hqcone.vvd b/models/holograms/Holograms/hqcone.vvd new file mode 100644 index 0000000000..2c8b4f7d15 Binary files /dev/null and b/models/holograms/Holograms/hqcone.vvd differ diff --git a/models/holograms/Holograms/hqcylinder.dx80.vtx b/models/holograms/Holograms/hqcylinder.dx80.vtx new file mode 100644 index 0000000000..0ed9fd5918 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder.dx80.vtx differ diff --git a/models/holograms/Holograms/hqcylinder.dx90.vtx b/models/holograms/Holograms/hqcylinder.dx90.vtx new file mode 100644 index 0000000000..843bc1a241 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder.dx90.vtx differ diff --git a/models/holograms/Holograms/hqcylinder.mdl b/models/holograms/Holograms/hqcylinder.mdl new file mode 100644 index 0000000000..b004df52a8 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder.mdl differ diff --git a/models/holograms/Holograms/hqcylinder.phy b/models/holograms/Holograms/hqcylinder.phy new file mode 100644 index 0000000000..f2d1785505 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder.phy differ diff --git a/models/holograms/Holograms/hqcylinder.sw.vtx b/models/holograms/Holograms/hqcylinder.sw.vtx new file mode 100644 index 0000000000..e033afd4bc Binary files /dev/null and b/models/holograms/Holograms/hqcylinder.sw.vtx differ diff --git a/models/holograms/Holograms/hqcylinder.vvd b/models/holograms/Holograms/hqcylinder.vvd new file mode 100644 index 0000000000..8526ca6a2d Binary files /dev/null and b/models/holograms/Holograms/hqcylinder.vvd differ diff --git a/models/holograms/Holograms/hqcylinder2.dx80.vtx b/models/holograms/Holograms/hqcylinder2.dx80.vtx new file mode 100644 index 0000000000..e6c7f15ab4 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder2.dx80.vtx differ diff --git a/models/holograms/Holograms/hqcylinder2.dx90.vtx b/models/holograms/Holograms/hqcylinder2.dx90.vtx new file mode 100644 index 0000000000..a1c00d97a0 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder2.dx90.vtx differ diff --git a/models/holograms/Holograms/hqcylinder2.mdl b/models/holograms/Holograms/hqcylinder2.mdl new file mode 100644 index 0000000000..be3b577e3a Binary files /dev/null and b/models/holograms/Holograms/hqcylinder2.mdl differ diff --git a/models/holograms/Holograms/hqcylinder2.phy b/models/holograms/Holograms/hqcylinder2.phy new file mode 100644 index 0000000000..aa39560582 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder2.phy differ diff --git a/models/holograms/Holograms/hqcylinder2.sw.vtx b/models/holograms/Holograms/hqcylinder2.sw.vtx new file mode 100644 index 0000000000..4d454a3dba Binary files /dev/null and b/models/holograms/Holograms/hqcylinder2.sw.vtx differ diff --git a/models/holograms/Holograms/hqcylinder2.vvd b/models/holograms/Holograms/hqcylinder2.vvd new file mode 100644 index 0000000000..c15b79d940 Binary files /dev/null and b/models/holograms/Holograms/hqcylinder2.vvd differ diff --git a/models/holograms/Holograms/hqicosphere.dx80.vtx b/models/holograms/Holograms/hqicosphere.dx80.vtx new file mode 100644 index 0000000000..21cb031fd8 Binary files /dev/null and b/models/holograms/Holograms/hqicosphere.dx80.vtx differ diff --git a/models/holograms/Holograms/hqicosphere.dx90.vtx b/models/holograms/Holograms/hqicosphere.dx90.vtx new file mode 100644 index 0000000000..6d32dfd027 Binary files /dev/null and b/models/holograms/Holograms/hqicosphere.dx90.vtx differ diff --git a/models/holograms/Holograms/hqicosphere.mdl b/models/holograms/Holograms/hqicosphere.mdl new file mode 100644 index 0000000000..0e0ee5b53f Binary files /dev/null and b/models/holograms/Holograms/hqicosphere.mdl differ diff --git a/models/holograms/Holograms/hqicosphere.phy b/models/holograms/Holograms/hqicosphere.phy new file mode 100644 index 0000000000..cadfc6ca14 Binary files /dev/null and b/models/holograms/Holograms/hqicosphere.phy differ diff --git a/models/holograms/Holograms/hqicosphere.sw.vtx b/models/holograms/Holograms/hqicosphere.sw.vtx new file mode 100644 index 0000000000..125b51dfe4 Binary files /dev/null and b/models/holograms/Holograms/hqicosphere.sw.vtx differ diff --git a/models/holograms/Holograms/hqicosphere.vvd b/models/holograms/Holograms/hqicosphere.vvd new file mode 100644 index 0000000000..d2bef47efe Binary files /dev/null and b/models/holograms/Holograms/hqicosphere.vvd differ diff --git a/models/holograms/Holograms/hqicosphere2.dx80.vtx b/models/holograms/Holograms/hqicosphere2.dx80.vtx new file mode 100644 index 0000000000..119e5172fd Binary files /dev/null and b/models/holograms/Holograms/hqicosphere2.dx80.vtx differ diff --git a/models/holograms/Holograms/hqicosphere2.dx90.vtx b/models/holograms/Holograms/hqicosphere2.dx90.vtx new file mode 100644 index 0000000000..f40c0d8eab Binary files /dev/null and b/models/holograms/Holograms/hqicosphere2.dx90.vtx differ diff --git a/models/holograms/Holograms/hqicosphere2.mdl b/models/holograms/Holograms/hqicosphere2.mdl new file mode 100644 index 0000000000..1e9c2a534b Binary files /dev/null and b/models/holograms/Holograms/hqicosphere2.mdl differ diff --git a/models/holograms/Holograms/hqicosphere2.phy b/models/holograms/Holograms/hqicosphere2.phy new file mode 100644 index 0000000000..28c4fd1582 Binary files /dev/null and b/models/holograms/Holograms/hqicosphere2.phy differ diff --git a/models/holograms/Holograms/hqicosphere2.sw.vtx b/models/holograms/Holograms/hqicosphere2.sw.vtx new file mode 100644 index 0000000000..01a47f39df Binary files /dev/null and b/models/holograms/Holograms/hqicosphere2.sw.vtx differ diff --git a/models/holograms/Holograms/hqicosphere2.vvd b/models/holograms/Holograms/hqicosphere2.vvd new file mode 100644 index 0000000000..525608f96c Binary files /dev/null and b/models/holograms/Holograms/hqicosphere2.vvd differ diff --git a/models/holograms/Holograms/hqsphere.dx80.vtx b/models/holograms/Holograms/hqsphere.dx80.vtx new file mode 100644 index 0000000000..8c81eeff0a Binary files /dev/null and b/models/holograms/Holograms/hqsphere.dx80.vtx differ diff --git a/models/holograms/Holograms/hqsphere.dx90.vtx b/models/holograms/Holograms/hqsphere.dx90.vtx new file mode 100644 index 0000000000..e98fdf69d2 Binary files /dev/null and b/models/holograms/Holograms/hqsphere.dx90.vtx differ diff --git a/models/holograms/Holograms/hqsphere.mdl b/models/holograms/Holograms/hqsphere.mdl new file mode 100644 index 0000000000..1b6443049c Binary files /dev/null and b/models/holograms/Holograms/hqsphere.mdl differ diff --git a/models/holograms/Holograms/hqsphere.phy b/models/holograms/Holograms/hqsphere.phy new file mode 100644 index 0000000000..bbac809b09 Binary files /dev/null and b/models/holograms/Holograms/hqsphere.phy differ diff --git a/models/holograms/Holograms/hqsphere.sw.vtx b/models/holograms/Holograms/hqsphere.sw.vtx new file mode 100644 index 0000000000..361084e191 Binary files /dev/null and b/models/holograms/Holograms/hqsphere.sw.vtx differ diff --git a/models/holograms/Holograms/hqsphere.vvd b/models/holograms/Holograms/hqsphere.vvd new file mode 100644 index 0000000000..2e1fd06dfe Binary files /dev/null and b/models/holograms/Holograms/hqsphere.vvd differ diff --git a/models/holograms/Holograms/hqsphere2.dx80.vtx b/models/holograms/Holograms/hqsphere2.dx80.vtx new file mode 100644 index 0000000000..9989064097 Binary files /dev/null and b/models/holograms/Holograms/hqsphere2.dx80.vtx differ diff --git a/models/holograms/Holograms/hqsphere2.dx90.vtx b/models/holograms/Holograms/hqsphere2.dx90.vtx new file mode 100644 index 0000000000..fb76cdd816 Binary files /dev/null and b/models/holograms/Holograms/hqsphere2.dx90.vtx differ diff --git a/models/holograms/Holograms/hqsphere2.mdl b/models/holograms/Holograms/hqsphere2.mdl new file mode 100644 index 0000000000..93f0f77efc Binary files /dev/null and b/models/holograms/Holograms/hqsphere2.mdl differ diff --git a/models/holograms/Holograms/hqsphere2.phy b/models/holograms/Holograms/hqsphere2.phy new file mode 100644 index 0000000000..3219563b6f Binary files /dev/null and b/models/holograms/Holograms/hqsphere2.phy differ diff --git a/models/holograms/Holograms/hqsphere2.sw.vtx b/models/holograms/Holograms/hqsphere2.sw.vtx new file mode 100644 index 0000000000..ef2b43138c Binary files /dev/null and b/models/holograms/Holograms/hqsphere2.sw.vtx differ diff --git a/models/holograms/Holograms/hqsphere2.vvd b/models/holograms/Holograms/hqsphere2.vvd new file mode 100644 index 0000000000..c807a0d640 Binary files /dev/null and b/models/holograms/Holograms/hqsphere2.vvd differ diff --git a/models/holograms/Holograms/hqtorus.dx80.vtx b/models/holograms/Holograms/hqtorus.dx80.vtx new file mode 100644 index 0000000000..779cc964ed Binary files /dev/null and b/models/holograms/Holograms/hqtorus.dx80.vtx differ diff --git a/models/holograms/Holograms/hqtorus.dx90.vtx b/models/holograms/Holograms/hqtorus.dx90.vtx new file mode 100644 index 0000000000..04ef9c968d Binary files /dev/null and b/models/holograms/Holograms/hqtorus.dx90.vtx differ diff --git a/models/holograms/Holograms/hqtorus.mdl b/models/holograms/Holograms/hqtorus.mdl new file mode 100644 index 0000000000..67f1b96889 Binary files /dev/null and b/models/holograms/Holograms/hqtorus.mdl differ diff --git a/models/holograms/Holograms/hqtorus.phy b/models/holograms/Holograms/hqtorus.phy new file mode 100644 index 0000000000..5f9d7146f3 Binary files /dev/null and b/models/holograms/Holograms/hqtorus.phy differ diff --git a/models/holograms/Holograms/hqtorus.sw.vtx b/models/holograms/Holograms/hqtorus.sw.vtx new file mode 100644 index 0000000000..077a2b08ef Binary files /dev/null and b/models/holograms/Holograms/hqtorus.sw.vtx differ diff --git a/models/holograms/Holograms/hqtorus.vvd b/models/holograms/Holograms/hqtorus.vvd new file mode 100644 index 0000000000..6bbbc6bf9d Binary files /dev/null and b/models/holograms/Holograms/hqtorus.vvd differ diff --git a/models/holograms/Holograms/hqtorus2.dx80.vtx b/models/holograms/Holograms/hqtorus2.dx80.vtx new file mode 100644 index 0000000000..1b15801a60 Binary files /dev/null and b/models/holograms/Holograms/hqtorus2.dx80.vtx differ diff --git a/models/holograms/Holograms/hqtorus2.dx90.vtx b/models/holograms/Holograms/hqtorus2.dx90.vtx new file mode 100644 index 0000000000..b911ed76e4 Binary files /dev/null and b/models/holograms/Holograms/hqtorus2.dx90.vtx differ diff --git a/models/holograms/Holograms/hqtorus2.mdl b/models/holograms/Holograms/hqtorus2.mdl new file mode 100644 index 0000000000..3e5c6f80a7 Binary files /dev/null and b/models/holograms/Holograms/hqtorus2.mdl differ diff --git a/models/holograms/Holograms/hqtorus2.phy b/models/holograms/Holograms/hqtorus2.phy new file mode 100644 index 0000000000..fb1ab979ff Binary files /dev/null and b/models/holograms/Holograms/hqtorus2.phy differ diff --git a/models/holograms/Holograms/hqtorus2.sw.vtx b/models/holograms/Holograms/hqtorus2.sw.vtx new file mode 100644 index 0000000000..db22ff6fd4 Binary files /dev/null and b/models/holograms/Holograms/hqtorus2.sw.vtx differ diff --git a/models/holograms/Holograms/hqtorus2.vvd b/models/holograms/Holograms/hqtorus2.vvd new file mode 100644 index 0000000000..145ab94819 Binary files /dev/null and b/models/holograms/Holograms/hqtorus2.vvd differ diff --git a/models/holograms/Holograms/icosphere.dx80.vtx b/models/holograms/Holograms/icosphere.dx80.vtx new file mode 100644 index 0000000000..a5c443d6b7 Binary files /dev/null and b/models/holograms/Holograms/icosphere.dx80.vtx differ diff --git a/models/holograms/Holograms/icosphere.dx90.vtx b/models/holograms/Holograms/icosphere.dx90.vtx new file mode 100644 index 0000000000..72be1a0ed7 Binary files /dev/null and b/models/holograms/Holograms/icosphere.dx90.vtx differ diff --git a/models/holograms/Holograms/icosphere.mdl b/models/holograms/Holograms/icosphere.mdl new file mode 100644 index 0000000000..a915d99668 Binary files /dev/null and b/models/holograms/Holograms/icosphere.mdl differ diff --git a/models/holograms/Holograms/icosphere.phy b/models/holograms/Holograms/icosphere.phy new file mode 100644 index 0000000000..cb16e15bb6 Binary files /dev/null and b/models/holograms/Holograms/icosphere.phy differ diff --git a/models/holograms/Holograms/icosphere.sw.vtx b/models/holograms/Holograms/icosphere.sw.vtx new file mode 100644 index 0000000000..6bfb082e53 Binary files /dev/null and b/models/holograms/Holograms/icosphere.sw.vtx differ diff --git a/models/holograms/Holograms/icosphere.vvd b/models/holograms/Holograms/icosphere.vvd new file mode 100644 index 0000000000..921ebc7ef9 Binary files /dev/null and b/models/holograms/Holograms/icosphere.vvd differ diff --git a/models/holograms/Holograms/icosphere2.dx80.vtx b/models/holograms/Holograms/icosphere2.dx80.vtx new file mode 100644 index 0000000000..0025889ca2 Binary files /dev/null and b/models/holograms/Holograms/icosphere2.dx80.vtx differ diff --git a/models/holograms/Holograms/icosphere2.dx90.vtx b/models/holograms/Holograms/icosphere2.dx90.vtx new file mode 100644 index 0000000000..9aa0a1afd7 Binary files /dev/null and b/models/holograms/Holograms/icosphere2.dx90.vtx differ diff --git a/models/holograms/Holograms/icosphere2.mdl b/models/holograms/Holograms/icosphere2.mdl new file mode 100644 index 0000000000..12111f3a91 Binary files /dev/null and b/models/holograms/Holograms/icosphere2.mdl differ diff --git a/models/holograms/Holograms/icosphere2.phy b/models/holograms/Holograms/icosphere2.phy new file mode 100644 index 0000000000..0e3f4ecfc5 Binary files /dev/null and b/models/holograms/Holograms/icosphere2.phy differ diff --git a/models/holograms/Holograms/icosphere2.sw.vtx b/models/holograms/Holograms/icosphere2.sw.vtx new file mode 100644 index 0000000000..f042ac8d65 Binary files /dev/null and b/models/holograms/Holograms/icosphere2.sw.vtx differ diff --git a/models/holograms/Holograms/icosphere2.vvd b/models/holograms/Holograms/icosphere2.vvd new file mode 100644 index 0000000000..1ae70ac3cb Binary files /dev/null and b/models/holograms/Holograms/icosphere2.vvd differ diff --git a/models/holograms/Holograms/icosphere3.dx80.vtx b/models/holograms/Holograms/icosphere3.dx80.vtx new file mode 100644 index 0000000000..892386d739 Binary files /dev/null and b/models/holograms/Holograms/icosphere3.dx80.vtx differ diff --git a/models/holograms/Holograms/icosphere3.dx90.vtx b/models/holograms/Holograms/icosphere3.dx90.vtx new file mode 100644 index 0000000000..bd2237bfdf Binary files /dev/null and b/models/holograms/Holograms/icosphere3.dx90.vtx differ diff --git a/models/holograms/Holograms/icosphere3.mdl b/models/holograms/Holograms/icosphere3.mdl new file mode 100644 index 0000000000..5e2e767eba Binary files /dev/null and b/models/holograms/Holograms/icosphere3.mdl differ diff --git a/models/holograms/Holograms/icosphere3.phy b/models/holograms/Holograms/icosphere3.phy new file mode 100644 index 0000000000..067330492f Binary files /dev/null and b/models/holograms/Holograms/icosphere3.phy differ diff --git a/models/holograms/Holograms/icosphere3.sw.vtx b/models/holograms/Holograms/icosphere3.sw.vtx new file mode 100644 index 0000000000..a6e96be809 Binary files /dev/null and b/models/holograms/Holograms/icosphere3.sw.vtx differ diff --git a/models/holograms/Holograms/icosphere3.vvd b/models/holograms/Holograms/icosphere3.vvd new file mode 100644 index 0000000000..884d9e1b42 Binary files /dev/null and b/models/holograms/Holograms/icosphere3.vvd differ diff --git a/models/holograms/Holograms/plane.dx80.vtx b/models/holograms/Holograms/plane.dx80.vtx new file mode 100644 index 0000000000..4592b16a79 Binary files /dev/null and b/models/holograms/Holograms/plane.dx80.vtx differ diff --git a/models/holograms/Holograms/plane.dx90.vtx b/models/holograms/Holograms/plane.dx90.vtx new file mode 100644 index 0000000000..000186b193 Binary files /dev/null and b/models/holograms/Holograms/plane.dx90.vtx differ diff --git a/models/holograms/Holograms/plane.mdl b/models/holograms/Holograms/plane.mdl new file mode 100644 index 0000000000..1abf1578aa Binary files /dev/null and b/models/holograms/Holograms/plane.mdl differ diff --git a/models/holograms/Holograms/plane.phy b/models/holograms/Holograms/plane.phy new file mode 100644 index 0000000000..365a5e525b Binary files /dev/null and b/models/holograms/Holograms/plane.phy differ diff --git a/models/holograms/Holograms/plane.sw.vtx b/models/holograms/Holograms/plane.sw.vtx new file mode 100644 index 0000000000..51934d87a7 Binary files /dev/null and b/models/holograms/Holograms/plane.sw.vtx differ diff --git a/models/holograms/Holograms/plane.vvd b/models/holograms/Holograms/plane.vvd new file mode 100644 index 0000000000..9fb3ee2140 Binary files /dev/null and b/models/holograms/Holograms/plane.vvd differ diff --git a/models/holograms/Holograms/prism.dx80.vtx b/models/holograms/Holograms/prism.dx80.vtx new file mode 100644 index 0000000000..360344c438 Binary files /dev/null and b/models/holograms/Holograms/prism.dx80.vtx differ diff --git a/models/holograms/Holograms/prism.dx90.vtx b/models/holograms/Holograms/prism.dx90.vtx new file mode 100644 index 0000000000..9f93313ac1 Binary files /dev/null and b/models/holograms/Holograms/prism.dx90.vtx differ diff --git a/models/holograms/Holograms/prism.mdl b/models/holograms/Holograms/prism.mdl new file mode 100644 index 0000000000..d1d98959d1 Binary files /dev/null and b/models/holograms/Holograms/prism.mdl differ diff --git a/models/holograms/Holograms/prism.phy b/models/holograms/Holograms/prism.phy new file mode 100644 index 0000000000..611ed232e2 Binary files /dev/null and b/models/holograms/Holograms/prism.phy differ diff --git a/models/holograms/Holograms/prism.sw.vtx b/models/holograms/Holograms/prism.sw.vtx new file mode 100644 index 0000000000..f63c4406e6 Binary files /dev/null and b/models/holograms/Holograms/prism.sw.vtx differ diff --git a/models/holograms/Holograms/prism.vvd b/models/holograms/Holograms/prism.vvd new file mode 100644 index 0000000000..b16d8fc573 Binary files /dev/null and b/models/holograms/Holograms/prism.vvd differ diff --git a/models/holograms/Holograms/pyramid.dx80.vtx b/models/holograms/Holograms/pyramid.dx80.vtx new file mode 100644 index 0000000000..427abcd31f Binary files /dev/null and b/models/holograms/Holograms/pyramid.dx80.vtx differ diff --git a/models/holograms/Holograms/pyramid.dx90.vtx b/models/holograms/Holograms/pyramid.dx90.vtx new file mode 100644 index 0000000000..023e7f2571 Binary files /dev/null and b/models/holograms/Holograms/pyramid.dx90.vtx differ diff --git a/models/holograms/Holograms/pyramid.mdl b/models/holograms/Holograms/pyramid.mdl new file mode 100644 index 0000000000..bced00ef90 Binary files /dev/null and b/models/holograms/Holograms/pyramid.mdl differ diff --git a/models/holograms/Holograms/pyramid.phy b/models/holograms/Holograms/pyramid.phy new file mode 100644 index 0000000000..20aca96f86 Binary files /dev/null and b/models/holograms/Holograms/pyramid.phy differ diff --git a/models/holograms/Holograms/pyramid.sw.vtx b/models/holograms/Holograms/pyramid.sw.vtx new file mode 100644 index 0000000000..aa51a933a1 Binary files /dev/null and b/models/holograms/Holograms/pyramid.sw.vtx differ diff --git a/models/holograms/Holograms/pyramid.vvd b/models/holograms/Holograms/pyramid.vvd new file mode 100644 index 0000000000..59b34d7dbd Binary files /dev/null and b/models/holograms/Holograms/pyramid.vvd differ diff --git a/models/holograms/Holograms/sphere.dx80.vtx b/models/holograms/Holograms/sphere.dx80.vtx new file mode 100644 index 0000000000..170f7f4b09 Binary files /dev/null and b/models/holograms/Holograms/sphere.dx80.vtx differ diff --git a/models/holograms/Holograms/sphere.dx90.vtx b/models/holograms/Holograms/sphere.dx90.vtx new file mode 100644 index 0000000000..2664c381f1 Binary files /dev/null and b/models/holograms/Holograms/sphere.dx90.vtx differ diff --git a/models/holograms/Holograms/sphere.mdl b/models/holograms/Holograms/sphere.mdl new file mode 100644 index 0000000000..ab892ce96f Binary files /dev/null and b/models/holograms/Holograms/sphere.mdl differ diff --git a/models/holograms/Holograms/sphere.phy b/models/holograms/Holograms/sphere.phy new file mode 100644 index 0000000000..c094ce42a6 Binary files /dev/null and b/models/holograms/Holograms/sphere.phy differ diff --git a/models/holograms/Holograms/sphere.sw.vtx b/models/holograms/Holograms/sphere.sw.vtx new file mode 100644 index 0000000000..b35e7014c1 Binary files /dev/null and b/models/holograms/Holograms/sphere.sw.vtx differ diff --git a/models/holograms/Holograms/sphere.vvd b/models/holograms/Holograms/sphere.vvd new file mode 100644 index 0000000000..13d36d7d93 Binary files /dev/null and b/models/holograms/Holograms/sphere.vvd differ diff --git a/models/holograms/Holograms/sphere2.dx80.vtx b/models/holograms/Holograms/sphere2.dx80.vtx new file mode 100644 index 0000000000..e6722866a9 Binary files /dev/null and b/models/holograms/Holograms/sphere2.dx80.vtx differ diff --git a/models/holograms/Holograms/sphere2.dx90.vtx b/models/holograms/Holograms/sphere2.dx90.vtx new file mode 100644 index 0000000000..b7065c419e Binary files /dev/null and b/models/holograms/Holograms/sphere2.dx90.vtx differ diff --git a/models/holograms/Holograms/sphere2.mdl b/models/holograms/Holograms/sphere2.mdl new file mode 100644 index 0000000000..b63f012365 Binary files /dev/null and b/models/holograms/Holograms/sphere2.mdl differ diff --git a/models/holograms/Holograms/sphere2.phy b/models/holograms/Holograms/sphere2.phy new file mode 100644 index 0000000000..e75916e899 Binary files /dev/null and b/models/holograms/Holograms/sphere2.phy differ diff --git a/models/holograms/Holograms/sphere2.sw.vtx b/models/holograms/Holograms/sphere2.sw.vtx new file mode 100644 index 0000000000..01365e8b99 Binary files /dev/null and b/models/holograms/Holograms/sphere2.sw.vtx differ diff --git a/models/holograms/Holograms/sphere2.vvd b/models/holograms/Holograms/sphere2.vvd new file mode 100644 index 0000000000..c6ee9764ec Binary files /dev/null and b/models/holograms/Holograms/sphere2.vvd differ diff --git a/models/holograms/Holograms/sphere3.dx80.vtx b/models/holograms/Holograms/sphere3.dx80.vtx new file mode 100644 index 0000000000..c44f25c3c7 Binary files /dev/null and b/models/holograms/Holograms/sphere3.dx80.vtx differ diff --git a/models/holograms/Holograms/sphere3.dx90.vtx b/models/holograms/Holograms/sphere3.dx90.vtx new file mode 100644 index 0000000000..1999cd0174 Binary files /dev/null and b/models/holograms/Holograms/sphere3.dx90.vtx differ diff --git a/models/holograms/Holograms/sphere3.mdl b/models/holograms/Holograms/sphere3.mdl new file mode 100644 index 0000000000..893decb127 Binary files /dev/null and b/models/holograms/Holograms/sphere3.mdl differ diff --git a/models/holograms/Holograms/sphere3.phy b/models/holograms/Holograms/sphere3.phy new file mode 100644 index 0000000000..2265e47f47 Binary files /dev/null and b/models/holograms/Holograms/sphere3.phy differ diff --git a/models/holograms/Holograms/sphere3.sw.vtx b/models/holograms/Holograms/sphere3.sw.vtx new file mode 100644 index 0000000000..737527cd51 Binary files /dev/null and b/models/holograms/Holograms/sphere3.sw.vtx differ diff --git a/models/holograms/Holograms/sphere3.vvd b/models/holograms/Holograms/sphere3.vvd new file mode 100644 index 0000000000..ee9aa8ef16 Binary files /dev/null and b/models/holograms/Holograms/sphere3.vvd differ diff --git a/models/holograms/Holograms/tetra.dx80.vtx b/models/holograms/Holograms/tetra.dx80.vtx new file mode 100644 index 0000000000..f1ca058da6 Binary files /dev/null and b/models/holograms/Holograms/tetra.dx80.vtx differ diff --git a/models/holograms/Holograms/tetra.dx90.vtx b/models/holograms/Holograms/tetra.dx90.vtx new file mode 100644 index 0000000000..0dd05eb241 Binary files /dev/null and b/models/holograms/Holograms/tetra.dx90.vtx differ diff --git a/models/holograms/Holograms/tetra.mdl b/models/holograms/Holograms/tetra.mdl new file mode 100644 index 0000000000..968b2419bd Binary files /dev/null and b/models/holograms/Holograms/tetra.mdl differ diff --git a/models/holograms/Holograms/tetra.phy b/models/holograms/Holograms/tetra.phy new file mode 100644 index 0000000000..d863724434 Binary files /dev/null and b/models/holograms/Holograms/tetra.phy differ diff --git a/models/holograms/Holograms/tetra.sw.vtx b/models/holograms/Holograms/tetra.sw.vtx new file mode 100644 index 0000000000..2f8f6ab638 Binary files /dev/null and b/models/holograms/Holograms/tetra.sw.vtx differ diff --git a/models/holograms/Holograms/tetra.vvd b/models/holograms/Holograms/tetra.vvd new file mode 100644 index 0000000000..a55b9cba39 Binary files /dev/null and b/models/holograms/Holograms/tetra.vvd differ diff --git a/models/holograms/Holograms/torus.dx80.vtx b/models/holograms/Holograms/torus.dx80.vtx new file mode 100644 index 0000000000..fb9936a5d6 Binary files /dev/null and b/models/holograms/Holograms/torus.dx80.vtx differ diff --git a/models/holograms/Holograms/torus.dx90.vtx b/models/holograms/Holograms/torus.dx90.vtx new file mode 100644 index 0000000000..e67423e50a Binary files /dev/null and b/models/holograms/Holograms/torus.dx90.vtx differ diff --git a/models/holograms/Holograms/torus.mdl b/models/holograms/Holograms/torus.mdl new file mode 100644 index 0000000000..11d95fd0cf Binary files /dev/null and b/models/holograms/Holograms/torus.mdl differ diff --git a/models/holograms/Holograms/torus.phy b/models/holograms/Holograms/torus.phy new file mode 100644 index 0000000000..aeec5cc22f Binary files /dev/null and b/models/holograms/Holograms/torus.phy differ diff --git a/models/holograms/Holograms/torus.sw.vtx b/models/holograms/Holograms/torus.sw.vtx new file mode 100644 index 0000000000..223d2e9bca Binary files /dev/null and b/models/holograms/Holograms/torus.sw.vtx differ diff --git a/models/holograms/Holograms/torus.vvd b/models/holograms/Holograms/torus.vvd new file mode 100644 index 0000000000..d02a3196e8 Binary files /dev/null and b/models/holograms/Holograms/torus.vvd differ diff --git a/models/holograms/Holograms/torus2.dx80.vtx b/models/holograms/Holograms/torus2.dx80.vtx new file mode 100644 index 0000000000..938437a246 Binary files /dev/null and b/models/holograms/Holograms/torus2.dx80.vtx differ diff --git a/models/holograms/Holograms/torus2.dx90.vtx b/models/holograms/Holograms/torus2.dx90.vtx new file mode 100644 index 0000000000..8a9337da92 Binary files /dev/null and b/models/holograms/Holograms/torus2.dx90.vtx differ diff --git a/models/holograms/Holograms/torus2.mdl b/models/holograms/Holograms/torus2.mdl new file mode 100644 index 0000000000..4bc66c32f3 Binary files /dev/null and b/models/holograms/Holograms/torus2.mdl differ diff --git a/models/holograms/Holograms/torus2.phy b/models/holograms/Holograms/torus2.phy new file mode 100644 index 0000000000..9a9659a087 Binary files /dev/null and b/models/holograms/Holograms/torus2.phy differ diff --git a/models/holograms/Holograms/torus2.sw.vtx b/models/holograms/Holograms/torus2.sw.vtx new file mode 100644 index 0000000000..74e8c8c407 Binary files /dev/null and b/models/holograms/Holograms/torus2.sw.vtx differ diff --git a/models/holograms/Holograms/torus2.vvd b/models/holograms/Holograms/torus2.vvd new file mode 100644 index 0000000000..36245efd08 Binary files /dev/null and b/models/holograms/Holograms/torus2.vvd differ diff --git a/models/holograms/Holograms/torus3.dx80.vtx b/models/holograms/Holograms/torus3.dx80.vtx new file mode 100644 index 0000000000..1ac60697cf Binary files /dev/null and b/models/holograms/Holograms/torus3.dx80.vtx differ diff --git a/models/holograms/Holograms/torus3.dx90.vtx b/models/holograms/Holograms/torus3.dx90.vtx new file mode 100644 index 0000000000..6c15b7632c Binary files /dev/null and b/models/holograms/Holograms/torus3.dx90.vtx differ diff --git a/models/holograms/Holograms/torus3.mdl b/models/holograms/Holograms/torus3.mdl new file mode 100644 index 0000000000..6ffe3aa26a Binary files /dev/null and b/models/holograms/Holograms/torus3.mdl differ diff --git a/models/holograms/Holograms/torus3.phy b/models/holograms/Holograms/torus3.phy new file mode 100644 index 0000000000..0f9fda6814 Binary files /dev/null and b/models/holograms/Holograms/torus3.phy differ diff --git a/models/holograms/Holograms/torus3.sw.vtx b/models/holograms/Holograms/torus3.sw.vtx new file mode 100644 index 0000000000..b9f8d22412 Binary files /dev/null and b/models/holograms/Holograms/torus3.sw.vtx differ diff --git a/models/holograms/Holograms/torus3.vvd b/models/holograms/Holograms/torus3.vvd new file mode 100644 index 0000000000..4ce7880d4f Binary files /dev/null and b/models/holograms/Holograms/torus3.vvd differ diff --git a/settings/controls/wire_explosive.txt b/settings/controls/wire_explosive.txt new file mode 100644 index 0000000000..5175af909a --- /dev/null +++ b/settings/controls/wire_explosive.txt @@ -0,0 +1,399 @@ +"0" +{ + "Text" "#Tool_wire_explosive_name" + "Description" "#Tool_wire_explosive_desc" + + "ComboBox" + { + "Label" "#Presets" + "MenuButton" "1" + "Folder" "wire_explosive" + + "Options" + { + "Default" + { + "wire_explosive_model" "models/props_c17/oildrum001_explosive.mdl" + "wire_explosive_tirgger" "1" + "wire_explosive_damage" "200" + "wire_explosive_delaytime" "0" + "wire_explosive_removeafter" "0" + "wire_explosive_doblastdamage" "1" + "wire_explosive_affectother" "0" + "wire_explosive_notaffected" "0" + "wire_explosive_freeze" "0" + "wire_explosive_weld" "1" + "wire_explosive_maxhealth" "100" + "wire_explosive_bulletproof" "0" + "wire_explosive_explosionproof" "0" + "wire_explosive_explodeatzero" "1" + "wire_explosive_resetatexplode" "1" + "wire_explosive_fireeffect" "1" + "wire_explosive_nocollide" "0" + "wire_explosive_weight" "400" + "wire_explosive_noparentremove" "0" + } + } + + "CVars" + { + "0" "wire_explosive_model" + "0" "wire_explosive_tirgger" + "0" "wire_explosive_damage" + "0" "wire_explosive_delaytime" + "0" "wire_explosive_removeafter" + "0" "wire_explosive_doblastdamage" + "0" "wire_explosive_affectother" + "0" "wire_explosive_notaffected" + "0" "wire_explosive_freeze" + "0" "wire_explosive_weld" + "0" "wire_explosive_maxhealth" + "0" "wire_explosive_bulletproof" + "0" "wire_explosive_explosionproof" + "0" "wire_explosive_explodeatzero" + "0" "wire_explosive_resetatexplode" + "0" "wire_explosive_fireeffect" + "0" "wire_explosive_nocollide" + "0" "wire_explosive_weight" + "0" "wire_explosive_noparentremove" + } + } + + "ComboBox" + { + "Label" "#WireExplosiveTool_Model" + "MenuButton" "0" + + "Options" + { + "Use manual model selection" + { + "wire_explosive_model" "usemanmodel" + } + "Use reload copied model" + { + "wire_explosive_model" "usereloadmodel" + } + "Dynamite" + { + "wire_explosive_model" "models/dav0r/tnt/tnt.mdl" + } + "Heli Bomb" + { + "wire_explosive_model" "models/Combine_Helicopter/helicopter_bomb01.mdl" + } + "Flat Bomb" + { + "wire_explosive_model" "models/jaanus/thruster_flat.mdl" + } + "Oil Drum" + { + "wire_explosive_model" "models/props_c17/oildrum001.mdl" + } + "Explosive Oil Drum" + { + "wire_explosive_model" "models/props_c17/oildrum001_explosive.mdl" + } + "PHX Cannon Ball" + { + "wire_explosive_model" "models/props_phx/cannonball.mdl" + } + "PHX Facepunch Barrel" + { + "wire_explosive_model" "models/props_phx/facepunch_barrel.mdl" + } + "PHX Oil Drum" + { + "wire_explosive_model" "models/props_phx/oildrum001.mdl" + } + "PHX Explosive Oil Drum" + { + "wire_explosive_model" "models/props_phx/oildrum001_explosive.mdl" + } + "PHX AARM" + { + "wire_explosive_model" "models/props_phx/amraam.mdl" + } + "PHX MK-82" + { + "wire_explosive_model" "models/props_phx/mk-82.mdl" + } + "PHX Rocket" + { + "wire_explosive_model" "models/props_phx/rocket1.mdl" + } + "PHX Torpedo" + { + "wire_explosive_model" "models/props_phx/torpedo.mdl" + } + "PHX WW2 Bomb" + { + "wire_explosive_model" "models/props_phx/ww2bomb.mdl" + } + "Paint Bucket" + { + "wire_explosive_model" "models/props_junk/plasticbucket001a.mdl" + } + "Small Propane Canister" + { + "wire_explosive_model" "models/props_junk/PropaneCanister001a.mdl" + } + "Medium Propane Tank" + { + "wire_explosive_model" "models/props_junk/propane_tank001a.mdl" + } + "Cola Can" + { + "wire_explosive_model" "models/props_junk/PopCan01a.mdl" + } + "Vitamin Jar" + { + "wire_explosive_model" "models/props_lab/jar01a.mdl" + } + "Fat Can" + { + "wire_explosive_model" "models/props_c17/canister_propane01a.mdl" + } + "Black Canister" + { + "wire_explosive_model" "models/props_c17/canister01a.mdl" + } + "Red Canister" + { + "wire_explosive_model" "models/props_c17/canister02a.mdl" + } + "Gas Pump" + { + "wire_explosive_model" "models/props_wasteland/gaspump001a.mdl" + } + "cardboard_box001a" + { + "wire_explosive_model" "models/props_junk/cardboard_box001a.mdl" + } + "cardboard_box001b" + { + "wire_explosive_model" "models/props_junk/cardboard_box001b.mdl" + } + "cardboard_box002a" + { + "wire_explosive_model" "models/props_junk/cardboard_box002a.mdl" + } + "cardboard_box002b" + { + "wire_explosive_model" "models/props_junk/cardboard_box002b.mdl" + } + "cardboard_box003a" + { + "wire_explosive_model" "models/props_junk/cardboard_box003a.mdl" + } + "cardboard_box003b" + { + "wire_explosive_model" "models/props_junk/cardboard_box003b.mdl" + } + "cardboard_box004a" + { + "wire_explosive_model" "models/props_junk/cardboard_box004a.mdl" + } + "Cinder Block" + { + "wire_explosive_model" "models/props_junk/CinderBlock01a.mdl" + } + "Gas can" + { + "wire_explosive_model" "models/props_junk/gascan001a.mdl" + } + "Traffic Cone" + { + "wire_explosive_model" "models/props_junk/TrafficCone001a.mdl" + } + "Metal gas can" + { + "wire_explosive_model" "models/props_junk/metalgascan.mdl" + } + "Metal paint can" + { + "wire_explosive_model" "models/props_junk/metal_paintcan001a.mdl" + } + "Wood crate 1" + { + "wire_explosive_model" "models/props_junk/wood_crate001a.mdl" + } + "Wood crate 2" + { + "wire_explosive_model" "models/props_junk/wood_crate002a.mdl" + } + "Wood Pallet 1" + { + "wire_explosive_model" "models/props_junk/wood_pallet001a.mdl" + } + } + + } + + "Slider" + { + "Label" "#WireExplosiveTool_tirgger" + "Type" "Integer" + "Min" "1" + "Max" "10" + "Command" "wire_explosive_tirgger" + } + + "Slider" + { + "Label" "#WireExplosiveTool_damage" + "Description" "#ExplosiveTool_damage_desc" + "Type" "Integer" + "Min" "1" + "Max" "1500" + "Command" "wire_explosive_damage" + } + + "Slider" + { + "Label" "#WireExplosiveTool_radius" + "Description" "#ExplosiveTool_delay" + "Type" "Integer" + "Min" "1" + "Max" "400" + "Command" "wire_explosive_radius" + } + + "Slider" + { + "Label" "#WireExplosiveTool_delay" + "Description" "#ExplosiveTool_delay" + "Type" "Integer" + "Min" "0" + "Max" "30" + "Command" "wire_explosive_delaytime" + } + + "Slider" + { + "Label" "#WireExplosiveTool_delayreload" + "Description" "#ExplosiveTool_delayreload" + "Type" "Integer" + "Min" "0" + "Max" "30" + "Command" "wire_explosive_delayreloadtime" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_remove" + "Command" "wire_explosive_removeafter" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_doblastdamage" + "Command" "wire_explosive_doblastdamage" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_affectother" + "Command" "wire_explosive_affectother" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_notaffected" + "Command" "wire_explosive_notaffected" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_freeze" + "Command" "wire_explosive_freeze" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_weld" + "Command" "wire_explosive_weld" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_noparentremove" + "Command" "wire_explosive_noparentremove" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_nocollide" + "Command" "wire_explosive_nocollide" + } + + "Slider" + { + "Label" "#WireExplosiveTool_maxhealth" + "Description" "#ExplosiveTool_maxhealth" + "Type" "Integer" + "Min" "0" + "Max" "300" + "Command" "wire_explosive_maxhealth" + } + + "Slider" + { + "Label" "#WireExplosiveTool_weight" + "Description" "#ExplosiveTool_weight" + "Type" "Integer" + "Min" "1" + "Max" "1000" + "Command" "wire_explosive_weight" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_bulletproof" + "Command" "wire_explosive_bulletproof" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_explosionproof" + "Command" "wire_explosive_explosionproof" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_explodeatzero" + "Command" "wire_explosive_explodeatzero" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_resetatexplode" + "Command" "wire_explosive_resetatexplode" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_fireeffect" + "Command" "wire_explosive_fireeffect" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_coloreffect" + "Command" "wire_explosive_coloreffect" + } + + "CheckBox" + { + "Label" "#WireExplosiveTool_invisibleatzero" + "Command" "wire_explosive_invisibleatzero" + } + + "TextBox" + { + "Label" "#WireExplosiveTool_modelman" + "Command" "wire_explosive_modelman" + "MaxLength" "50" + } + +} \ No newline at end of file diff --git a/settings/controls/wire_simple_explosive.txt b/settings/controls/wire_simple_explosive.txt new file mode 100644 index 0000000000..27da27eb0b --- /dev/null +++ b/settings/controls/wire_simple_explosive.txt @@ -0,0 +1,301 @@ +"0" +{ + "Text" "#Tool_wire_simple_explosive_name" + "Description" "#Tool_wire_simple_explosive_desc" + + "ComboBox" + { + "Label" "#Presets" + "MenuButton" "1" + "Folder" "wire_simple_explosive" + + "Options" + { + "Default" + { + "wire_simple_explosive_model" "models/props_c17/oildrum001_explosive.mdl" + "wire_simple_explosive_tirgger" "1" + "wire_simple_explosive_damage" "200" + "wire_simple_explosive_radius" "200" + "wire_simple_explosive_removeafter" "1" + "wire_simple_explosive_doblastdamage" "1" + "wire_simple_explosive_freeze" "0" + "wire_simple_explosive_weld" "1" + "wire_simple_explosive_nocollide" "0" + "wire_simple_explosive_weight" "400" + "wire_simple_explosive_noparentremove" "0" + "wire_simple_explosive_modelman" "" + } + } + + "CVars" + { + "0" "wire_simple_explosive_model" + "0" "wire_simple_explosive_tirgger" + "0" "wire_simple_explosive_damage" + "0" "wire_simple_explosive_radius" + "0" "wire_simple_explosive_removeafter" + "0" "wire_simple_explosive_doblastdamage" + "0" "wire_simple_explosive_freeze" + "0" "wire_simple_explosive_weld" + "0" "wire_simple_explosive_nocollide" + "0" "wire_simple_explosive_weight" + "0" "wire_simple_explosive_noparentremove" + "0" "wire_simple_explosive_modelman" + } + } + + "ComboBox" + { + "Label" "#WireSimpleExplosiveTool_Model" + "MenuButton" "0" + + "Options" + { + "Use manual model selection" + { + "wire_simple_explosive_model" "usemanmodel" + } + "Use relaod copied model" + { + "wire_simple_explosive_model" "usereloadmodel" + } + "Dynamite" + { + "wire_simple_explosive_model" "models/dav0r/tnt/tnt.mdl" + } + "Heli Bomb" + { + "wire_simple_explosive_model" "models/Combine_Helicopter/helicopter_bomb01.mdl" + } + "Flat Bomb" + { + "wire_simple_explosive_model" "models/jaanus/thruster_flat.mdl" + } + "Oil Drum" + { + "wire_simple_explosive_model" "models/props_c17/oildrum001.mdl" + } + "Explosive Oil Drum" + { + "wire_simple_explosive_model" "models/props_c17/oildrum001_explosive.mdl" + } + "PHX Cannon Ball" + { + "wire_simple_explosive_model" "models/props_phx/cannonball.mdl" + } + "PHX Facepunch Barrel" + { + "wire_simple_explosive_model" "models/props_phx/facepunch_barrel.mdl" + } + "PHX Oil Drum" + { + "wire_simple_explosive_model" "models/props_phx/oildrum001.mdl" + } + "PHX Explosive Oil Drum" + { + "wire_simple_explosive_model" "models/props_phx/oildrum001_explosive.mdl" + } + "PHX AARM" + { + "wire_simple_explosive_model" "models/props_phx/amraam.mdl" + } + "PHX MK-82" + { + "wire_simple_explosive_model" "models/props_phx/mk-82.mdl" + } + "PHX Rocket" + { + "wire_simple_explosive_model" "models/props_phx/rocket1.mdl" + } + "PHX Torpedo" + { + "wire_simple_explosive_model" "models/props_phx/torpedo.mdl" + } + "PHX WW2 Bomb" + { + "wire_simple_explosive_model" "models/props_phx/ww2bomb.mdl" + } + "Paint Bucket" + { + "wire_simple_explosive_model" "models/props_junk/plasticbucket001a.mdl" + } + "Small Propane Canister" + { + "wire_simple_explosive_model" "models/props_junk/PropaneCanister001a.mdl" + } + "Medium Propane Tank" + { + "wire_simple_explosive_model" "models/props_junk/propane_tank001a.mdl" + } + "Cola Can" + { + "wire_simple_explosive_model" "models/props_junk/PopCan01a.mdl" + } + "Vitamin Jar" + { + "wire_simple_explosive_model" "models/props_lab/jar01a.mdl" + } + "Fat Can" + { + "wire_simple_explosive_model" "models/props_c17/canister_propane01a.mdl" + } + "Black Canister" + { + "wire_simple_explosive_model" "models/props_c17/canister01a.mdl" + } + "Red Canister" + { + "wire_simple_explosive_model" "models/props_c17/canister02a.mdl" + } + "Gas Pump" + { + "wire_simple_explosive_model" "models/props_wasteland/gaspump001a.mdl" + } + "cardboard_box001a" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box001a.mdl" + } + "cardboard_box001b" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box001b.mdl" + } + "cardboard_box002a" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box002a.mdl" + } + "cardboard_box002b" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box002b.mdl" + } + "cardboard_box003a" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box003a.mdl" + } + "cardboard_box003b" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box003b.mdl" + } + "cardboard_box004a" + { + "wire_simple_explosive_model" "models/props_junk/cardboard_box004a.mdl" + } + "Cinder Block" + { + "wire_simple_explosive_model" "models/props_junk/CinderBlock01a.mdl" + } + "Gas can" + { + "wire_simple_explosive_model" "models/props_junk/gascan001a.mdl" + } + "Traffic Cone" + { + "wire_simple_explosive_model" "models/props_junk/TrafficCone001a.mdl" + } + "Metal gas can" + { + "wire_simple_explosive_model" "models/props_junk/metalgascan.mdl" + } + "Metal paint can" + { + "wire_simple_explosive_model" "models/props_junk/metal_paintcan001a.mdl" + } + "Wood crate 1" + { + "wire_simple_explosive_model" "models/props_junk/wood_crate001a.mdl" + } + "Wood crate 2" + { + "wire_simple_explosive_model" "models/props_junk/wood_crate002a.mdl" + } + "Wood Pallet 1" + { + "wire_simple_explosive_model" "models/props_junk/wood_pallet001a.mdl" + } + } + + } + + "Slider" + { + "Label" "#WireSimpleExplosiveTool_tirgger" + "Type" "Integer" + "Min" "1" + "Max" "10" + "Command" "wire_simple_explosive_tirgger" + } + + "Slider" + { + "Label" "#WireSimpleExplosiveTool_damage" + "Description" "#SimpleExplosiveTool_damage_desc" + "Type" "Integer" + "Min" "1" + "Max" "1500" + "Command" "wire_simple_explosive_damage" + } + + "Slider" + { + "Label" "#WireSimpleExplosiveTool_radius" + "Description" "#SimpleExplosiveTool_delay" + "Type" "Integer" + "Min" "1" + "Max" "400" + "Command" "wire_simple_explosive_radius" + } + + "CheckBox" + { + "Label" "#WireSimpleExplosiveTool_remove" + "Command" "wire_simple_explosive_removeafter" + } + + "CheckBox" + { + "Label" "#WireSimpleExplosiveTool_doblastdamage" + "Command" "wire_simple_explosive_doblastdamage" + } + + "CheckBox" + { + "Label" "#WireSimpleExplosiveTool_freeze" + "Command" "wire_simple_explosive_freeze" + } + + "CheckBox" + { + "Label" "#WireSimpleExplosiveTool_weld" + "Command" "wire_simple_explosive_weld" + } + + "CheckBox" + { + "Label" "#WireSimpleExplosiveTool_noparentremove" + "Command" "wire_simple_explosive_noparentremove" + } + + "CheckBox" + { + "Label" "#WireSimpleExplosiveTool_nocollide" + "Command" "wire_simple_explosive_nocollide" + } + + "Slider" + { + "Label" "#WireSimpleExplosiveTool_weight" + "Description" "#SimpleExplosiveTool_weight" + "Type" "Integer" + "Min" "1" + "Max" "1000" + "Command" "wire_simple_explosive_weight" + } + + "TextBox" + { + "Label" "#WireSimpleExplosiveTool_modelman" + "Command" "wire_simple_explosive_modelman" + "MaxLength" "50" + } + +} \ No newline at end of file diff --git a/settings/controls/wire_thruster.txt b/settings/controls/wire_thruster.txt new file mode 100644 index 0000000000..43fb23e036 --- /dev/null +++ b/settings/controls/wire_thruster.txt @@ -0,0 +1,679 @@ +"0" +{ + "Text" "#Tool_wire_thruster_name" + "Description" "#Tool_wire_thruster_desc" + + "ComboBox" + { + "Label" "#Presets" + "MenuButton" "1" + "Folder" "wire_thruster" + + "Options" + { + "Default" + { + "wire_thruster_force" "20" + "wire_thruster_model" "models/props_junk/plasticbucket001a.mdl" + "wire_thruster_effect" "fire" + } + } + + "CVars" + { + "0" "wire_thruster_model" + "1" "wire_thruster_force" + "2" "wire_thruster_effect" + } + } + + "ComboBox" + { + "Label" "#WireThrusterTool_Model" + "MenuButton" "0" + + "Options" + { + "#Thruster" + { + "wire_thruster_model" "models/dav0r/thruster.mdl" + } + "#Paint_Bucket" + { + "wire_thruster_model" "models/props_junk/plasticbucket001a.mdl" + } + "#Small_Propane_Canister" + { + "wire_thruster_model" "models/props_junk/PropaneCanister001a.mdl" + } + "#Medium_Propane_Tank" + { + "wire_thruster_model" "models/props_junk/propane_tank001a.mdl" + } + "#Cola_Can" + { + "wire_thruster_model" "models/props_junk/PopCan01a.mdl" + } + "#Bucket" + { + "wire_thruster_model" "models/props_junk/MetalBucket01a.mdl" + } + "#Vitamin_Jar" + { + "wire_thruster_model" "models/props_lab/jar01a.mdl" + } + "#Lamp_Shade" + { + "wire_thruster_model" "models/props_c17/lampShade001a.mdl" + } + "#Fat_Can" + { + "wire_thruster_model" "models/props_c17/canister_propane01a.mdl" + } + "#Black_Canister" + { + "wire_thruster_model" "models/props_c17/canister01a.mdl" + } + "#Red_Canister" + { + "wire_thruster_model" "models/props_c17/canister02a.mdl" + } + } + } + + "ComboBox" + { + "Label" "#WireThrusterTool_OWEffects" + "MenuButton" "0" + + "Options" + { + "#No_Effects" + { + "wire_thruster_oweffect" "none" + } + + "#Flames" + { + "wire_thruster_oweffect" "fire" + } + "#Heatwave" + { + "wire_thruster_oweffect" "heatwave" + } + + "#Plasma" + { + "wire_thruster_oweffect" "plasma" + } + + "#Plasma Rings" + { + "wire_thruster_oweffect" "plasma_rings" + } + + "#Smoke" + { + "wire_thruster_oweffect" "smoke" + } + + "#Smoke Random" + { + "wire_thruster_oweffect" "smoke_random" + } + + "#Smoke Do it Youself" + { + "wire_thruster_oweffect" "smoke_diy" + } + + "#Smoke Fire Colors" + { + "wire_thruster_oweffect" "smoke_diy" + } + + "#Rings" + { + "wire_thruster_oweffect" "rings" + } + "#Rings Growing" + { + "wire_thruster_oweffect" "rings_grow" + } + "#Rings Shrinking" + { + "wire_thruster_oweffect" "rings_shrink" + } + + "#Bubbles" + { + "wire_thruster_oweffect" "bubble" + } + + "#Magic" + { + "wire_thruster_oweffect" "magic" + } + + "#Magic Random" + { + "wire_thruster_oweffect" "magic_color" + } + + "#Magic Do It Yourself" + { + "wire_thruster_oweffect" "magic_diy" + } + "#Magic Fire Colors" + { + "wire_thruster_oweffect" "magic_diy" + } + + "#Colors" + { + "wire_thruster_oweffect" "color" + } + "#Colors Random" + { + "wire_thruster_oweffect" "color_random" + } + "#Colors Do It Yourself" + { + "wire_thruster_oweffect" "color_diy" + } + + "#Blood" + { + "wire_thruster_oweffect" "blood" + } + + "#Money" + { + "wire_thruster_oweffect" "money" + } + + "#Sperms" + { + "wire_thruster_oweffect" "sperm" + } + + "#Feathers" + { + "wire_thruster_oweffect" "feather" + } + + "#Candy Cane" + { + "wire_thruster_oweffect" "candy_cane" + } + + "#Goldstar" + { + "wire_thruster_oweffect" "goldstar" + } + "#Water Small" + { + "wire_thruster_oweffect" "water_small" + } + + "#Water Medium" + { + "wire_thruster_oweffect" "water_medium" + } + + "#Water Big" + { + "wire_thruster_oweffect" "water_big" + } + + "#Water Huge" + { + "wire_thruster_oweffect" "water_huge" + } + + "#Striderblood Small" + { + "wire_thruster_oweffect" "striderblood_small" + } + + "#Striderblood Medium" + { + "wire_thruster_oweffect" "striderblood_medium" + } + "#Striderblood Big" + { + "wire_thruster_oweffect" "striderblood_big" + } + "#Striderblood Huge" + { + "wire_thruster_oweffect" "striderblood_huge" + } + + "#Some Sparks" + { + "wire_thruster_oweffect" "some_sparks" + } + + "#More Sparks" + { + "wire_thruster_oweffect" "more_sparks" + } + + "#Spark Fountain" + { + "wire_thruster_oweffect" "spark_fountain" + } + + "#Jetflame" + { + "wire_thruster_oweffect" "jetflame" + } + + "#Jetflame Advanced" + { + "wire_thruster_oweffect" "jetflame_advanced" + } + + "#Jetflame Blue" + { + "wire_thruster_oweffect" "jetflame_blue" + } + + "#Jetflame Red" + { + "wire_thruster_oweffect" "jetflame_red" + } + + "#Jetflame Purple" + { + "wire_thruster_oweffect" "jetflame_purple" + } + + "#Comic Balls" + { + "wire_thruster_oweffect" "balls" + } + + "#Comic Balls Random" + { + "wire_thruster_oweffect" "balls_random" + } + + "#Comic Balls Fire Colors" + { + "wire_thruster_oweffect" "balls_firecolors" + } + + "#Souls" + { + "wire_thruster_oweffect" "souls" + } + "#Debugger 10 Seconds" + { + "wire_thruster_oweffect" "debug_10" + } + "#Debugger 30 Seconds" + { + "wire_thruster_oweffect" "debug_30" + } + "#Debugger 60 Seconds" + { + "wire_thruster_oweffect" "debug_60" + } + + "#Combinations (this is no effect!)" + { + "wire_thruster_oweffect" "" + } + + "#Fire and Smoke" + { + "wire_thruster_oweffect" "fire_smoke" + } + + "#Fire and Smoke Huge" + { + "wire_thruster_oweffect" "fire_smoke_big" + } + + "#5 Growing Rings" + { + "wire_thruster_oweffect" "rings_grow_rings" + } + + "#Color and Magic" + { + "wire_thruster_oweffect" "color_magic" + } + } + } + + "ComboBox" + { + "Label" "#WireThrusterTool_UWEffects" + "MenuButton" "0" + + "Options" + { + "#No_Effects" + { + "wire_thruster_uweffect" "none" + } + + "#Same as over water" + { + "wire_thruster_uweffect" "same" + } + + "#Flames" + { + "wire_thruster_uweffect" "fire" + } + "#Heatwave" + { + "wire_thruster_uweffect" "heatwave" + } + + "#Plasma" + { + "wire_thruster_uweffect" "plasma" + } + + "#Plasma Rings" + { + "wire_thruster_uweffect" "plasma_rings" + } + + "#Smoke" + { + "wire_thruster_uweffect" "smoke" + } + + "#Smoke Random" + { + "wire_thruster_uweffect" "smoke_random" + } + + "#Smoke Do it Youself" + { + "wire_thruster_uweffect" "smoke_diy" + } + + "#Smoke Fire Colors" + { + "wire_thruster_uweffect" "smoke_diy" + } + + "#Rings" + { + "wire_thruster_uweffect" "rings" + } + "#Rings Growing" + { + "wire_thruster_uweffect" "rings_grow" + } + "#Rings Shrinking" + { + "wire_thruster_uweffect" "rings_shrink" + } + + "#Bubbles" + { + "wire_thruster_uweffect" "bubble" + } + + "#Magic" + { + "wire_thruster_uweffect" "magic" + } + + "#Magic Random" + { + "wire_thruster_uweffect" "magic_color" + } + + "#Magic Do It Yourself" + { + "wire_thruster_uweffect" "magic_diy" + } + "#Magic Fire Colors" + { + "wire_thruster_uweffect" "magic_diy" + } + + "#Colors" + { + "wire_thruster_uweffect" "color" + } + "#Colors Random" + { + "wire_thruster_uweffect" "color_random" + } + "#Colors Do It Yourself" + { + "wire_thruster_uweffect" "color_diy" + } + + "#Blood" + { + "wire_thruster_uweffect" "blood" + } + + "#Money" + { + "wire_thruster_uweffect" "money" + } + + "#Sperms" + { + "wire_thruster_uweffect" "sperm" + } + + "#Feathers" + { + "wire_thruster_uweffect" "feather" + } + + "#Candy Cane" + { + "wire_thruster_uweffect" "candy_cane" + } + + "#Goldstar" + { + "wire_thruster_uweffect" "goldstar" + } + "#Water Small" + { + "wire_thruster_uweffect" "water_small" + } + + "#Water Medium" + { + "wire_thruster_uweffect" "water_medium" + } + + "#Water Big" + { + "wire_thruster_uweffect" "water_big" + } + + "#Water Huge" + { + "wire_thruster_uweffect" "water_huge" + } + + "#Striderblood Small" + { + "wire_thruster_uweffect" "striderblood_small" + } + + "#Striderblood Medium" + { + "wire_thruster_uweffect" "striderblood_medium" + } + "#Striderblood Big" + { + "wire_thruster_uweffect" "striderblood_big" + } + "#Striderblood Huge" + { + "wire_thruster_uweffect" "striderblood_huge" + } + + "#Some Sparks" + { + "wire_thruster_uweffect" "some_sparks" + } + + "#More Sparks" + { + "wire_thruster_uweffect" "more_sparks" + } + + "#Spark Fountain" + { + "wire_thruster_uweffect" "spark_fountain" + } + + "#Jetflame" + { + "wire_thruster_uweffect" "jetflame" + } + + "#Jetflame Advanced" + { + "wire_thruster_uweffect" "jetflame_advanced" + } + + "#Jetflame Blue" + { + "wire_thruster_uweffect" "jetflame_blue" + } + + "#Jetflame Red" + { + "wire_thruster_uweffect" "jetflame_red" + } + + "#Jetflame Purple" + { + "wire_thruster_uweffect" "jetflame_purple" + } + + "#Comic Balls" + { + "wire_thruster_uweffect" "balls" + } + + "#Comic Balls Random" + { + "wire_thruster_uweffect" "balls_random" + } + + "#Comic Balls Fire Colors" + { + "wire_thruster_uweffect" "balls_firecolors" + } + + "#Souls" + { + "wire_thruster_uweffect" "souls" + } + "#Debugger 10 Seconds" + { + "wire_thruster_uweffect" "debug_10" + } + "#Debugger 30 Seconds" + { + "wire_thruster_uweffect" "debug_30" + } + "#Debugger 60 Seconds" + { + "wire_thruster_uweffect" "debug_60" + } + + "#Combinations (this is no effect!)" + { + "wire_thruster_uweffect" "" + } + + "#Fire and Smoke" + { + "wire_thruster_uweffect" "fire_smoke" + } + + "#Fire and Smoke Huge" + { + "wire_thruster_uweffect" "fire_smoke_big" + } + + "#5 Growing Rings" + { + "wire_thruster_uweffect" "rings_grow_rings" + } + + "#Color and Magic" + { + "wire_thruster_uweffect" "color_magic" + } + } + } + + "Slider" + { + "Label" "#WireThrusterTool_force" + "Type" "Float" + "Min" "1" + "Max" "10000" + "Command" "wire_thruster_force" + } + + "Slider" + { + "Label" "#WireThrusterTool_force_min" + "Type" "Float" + "Min" "0" + "Max" "10000" + "Command" "wire_thruster_force_min" + } + + "Slider" + { + "Label" "#WireThrusterTool_force_max" + "Type" "Float" + "Min" "0" + "Max" "10000" + "Command" "wire_thruster_force_max" + } + + "CheckBox" + { + "Label" "#WireThrusterTool_bidir" + "Command" "wire_thruster_bidir" + } + + "CheckBox" + { + "Label" "#WireThrusterTool_collision" + "Command" "wire_thruster_collision" + } + + "CheckBox" + { + "Label" "#WireThrusterTool_sound" + "Command" "wire_thruster_sound" + } + + "CheckBox" + { + "Label" "#WireThrusterTool_owater" + "Command" "wire_thruster_owater" + } + + "CheckBox" + { + "Label" "#WireThrusterTool_uwater" + "Command" "wire_thruster_uwater" + } +} diff --git a/settings/controls/wire_wheel.txt b/settings/controls/wire_wheel.txt new file mode 100644 index 0000000000..fa68083b3f --- /dev/null +++ b/settings/controls/wire_wheel.txt @@ -0,0 +1,262 @@ +"0" +{ + "Text" "#Tool_wire_wheel_name" + "Description" "#Tool_wire_wheel_desc" + + "ComboBox" + { + "Label" "#Presets" + "MenuButton" "1" + "Folder" "wire_wheel" + + "Options" + { + "Default" + { + "wire_wheel_torque" "3000" + "wire_wheel_friction" "0" + "wire_wheel_nocollide" "1" + "wire_wheel_forcelimit" "0" + "wire_wheel_fwd" "1" + "wire_wheel_bck" "-1" + "wire_wheel_stop" "0" + } + } + + "CVars" + { + "0" "wire_wheel_torque" + "0" "wire_wheel_friction" + "0" "wire_wheel_nocollide" + "0" "wire_wheel_forcelimit" + "0" "wire_wheel_fwd" + "0" "wire_wheel_bck" + "0" "wire_wheel_stop" + } + } + + "Slider" + { + "Label" "#WireWheelTool_group" + "Description" "#WireWheelTool_group_desc" + "Type" "Float" + "Min" "-10" + "Max" "10" + "Command" "wire_wheel_fwd" + } + + "Slider" + { + "Label" "#WireWheelTool_group_stop" + "Description" "#WireWheelTool_group_desc" + "Type" "Float" + "Min" "-10" + "Max" "10" + "Command" "wire_wheel_stop" + } + + "Slider" + { + "Label" "#WireWheelTool_group_reverse" + "Description" "#WireWheelTool_group_desc" + "Type" "Float" + "Min" "-10" + "Max" "10" + "Command" "wire_wheel_bck" + } + + "ComboBox" + { + "Label" "#WheelTool_model" + "MenuButton" "0" + + "Options" + { + "#Saw_Blade" + { + "wire_wheel_model" "models/props_junk/sawblade001a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Car_wheel" + { + "wire_wheel_model" "models/props_vehicles/carparts_wheel01a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "90" + } + + "#APC_Wheel" + { + + "wire_wheel_model" "models/props_vehicles/apc_tire001.mdl" + "wire_wheel_rx" "0" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + + "#Tractor_Wheel" + { + + "wire_wheel_model" "models/props_vehicles/tire001a_tractor.mdl" + "wire_wheel_rx" "0" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Truck_Tire" + { + "wire_wheel_model" "models/props_vehicles/tire001b_truck.mdl" + "wire_wheel_rx" "0" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Car_Tire" + { + "wire_wheel_model" "models/props_vehicles/tire001c_car.mdl" + "wire_wheel_rx" "0" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#File_Cabinet" + { + "wire_wheel_model" "models/props_wasteland/controlroom_filecabinet002a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "File Cabinet on side" + { + "wire_wheel_model" "models/props_wasteland/controlroom_filecabinet002a.mdl" + "wire_wheel_rx" "0" + "wire_wheel_ry" "90" + "wire_wheel_rz" "0" + } + + "#Blue_Barrel" + { + + "wire_wheel_model" "models/props_borealis/bluebarrel001.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Oil_Drum" + { + + "wire_wheel_model" "models/props_c17/oildrum001.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Carousel" + { + + "wire_wheel_model" "models/props_c17/playground_carousel01.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Office_Chair" + { + "wire_wheel_model" "models/props_c17/chair_office01a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "#Propeller_Blade" + { + "wire_wheel_model" "models\props_c17\TrapPropeller_Blade.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "0" + } + + "Big wheel 1" + { + "wire_wheel_model" "models/props_wasteland/wheel01.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "90" + } + + "Big wheel 1a" + { + "wire_wheel_model" "models/props_wasteland/wheel01a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "90" + } + + "Big wheel 2" + { + "wire_wheel_model" "models/props_wasteland/wheel02a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "90" + } + + "Big wheel 3a" + { + "wire_wheel_model" "models/props_wasteland/wheel03a.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "90" + } + + "Big wheel 3b" + { + "wire_wheel_model" "models/props_wasteland/wheel03b.mdl" + "wire_wheel_rx" "90" + "wire_wheel_ry" "0" + "wire_wheel_rz" "90" + } + } + + } + + "Slider" + { + "Label" "#WheelTool_torque" + "Description" "#WheelTool_torque_desc" + "Type" "Float" + "Min" "10" + "Max" "10000" + "Command" "wire_wheel_torque" + } + "Slider" + { + "Label" "#WheelTool_forcelimit" + "Description" "#WheelTool_forcelimit_desc" + "Type" "Float" + "Min" "0" + "Max" "50000" + "Command" "wire_wheel_forcelimit" + } + "Slider" + { + "Label" "#WheelTool_friction" + "Description" "#WheelTool_friction_desc" + "Type" "Float" + "Min" "0" + "Max" "100" + "Command" "wire_wheel_friction" + } + "CheckBox" + { + "Label" "#WheelTool_nocollide" + "Description" "#WheelTool_nocollide_desc" + "Command" "wire_wheel_nocollide" + } + +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_1.txt b/settings/render_targets/WireGPU_RT_1.txt new file mode 100644 index 0000000000..95456701e3 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_1.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_1" +{ + "Name" "WireGPU_RT_1" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_10.txt b/settings/render_targets/WireGPU_RT_10.txt new file mode 100644 index 0000000000..9efea3fa9a --- /dev/null +++ b/settings/render_targets/WireGPU_RT_10.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_10" +{ + "Name" "WireGPU_RT_10" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_11.txt b/settings/render_targets/WireGPU_RT_11.txt new file mode 100644 index 0000000000..b024e59823 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_11.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_11" +{ + "Name" "WireGPU_RT_11" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_12.txt b/settings/render_targets/WireGPU_RT_12.txt new file mode 100644 index 0000000000..6eb740f198 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_12.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_12" +{ + "Name" "WireGPU_RT_12" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_13.txt b/settings/render_targets/WireGPU_RT_13.txt new file mode 100644 index 0000000000..0a35de55fc --- /dev/null +++ b/settings/render_targets/WireGPU_RT_13.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_13" +{ + "Name" "WireGPU_RT_13" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_14.txt b/settings/render_targets/WireGPU_RT_14.txt new file mode 100644 index 0000000000..4728fb0fa3 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_14.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_14" +{ + "Name" "WireGPU_RT_14" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_15.txt b/settings/render_targets/WireGPU_RT_15.txt new file mode 100644 index 0000000000..a3253ad7d6 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_15.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_15" +{ + "Name" "WireGPU_RT_15" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_16.txt b/settings/render_targets/WireGPU_RT_16.txt new file mode 100644 index 0000000000..fedf831983 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_16.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_16" +{ + "Name" "WireGPU_RT_16" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_2.txt b/settings/render_targets/WireGPU_RT_2.txt new file mode 100644 index 0000000000..e182a51889 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_2.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_2" +{ + "Name" "WireGPU_RT_2" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_3.txt b/settings/render_targets/WireGPU_RT_3.txt new file mode 100644 index 0000000000..dec95408d8 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_3.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_3" +{ + "Name" "WireGPU_RT_3" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_4.txt b/settings/render_targets/WireGPU_RT_4.txt new file mode 100644 index 0000000000..e4d422fe71 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_4.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_4" +{ + "Name" "WireGPU_RT_4" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_5.txt b/settings/render_targets/WireGPU_RT_5.txt new file mode 100644 index 0000000000..ed4356350b --- /dev/null +++ b/settings/render_targets/WireGPU_RT_5.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_5" +{ + "Name" "WireGPU_RT_5" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_6.txt b/settings/render_targets/WireGPU_RT_6.txt new file mode 100644 index 0000000000..67ec838143 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_6.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_6" +{ + "Name" "WireGPU_RT_6" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_7.txt b/settings/render_targets/WireGPU_RT_7.txt new file mode 100644 index 0000000000..f494c1d512 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_7.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_7" +{ + "Name" "WireGPU_RT_7" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_8.txt b/settings/render_targets/WireGPU_RT_8.txt new file mode 100644 index 0000000000..b69d2a8c6c --- /dev/null +++ b/settings/render_targets/WireGPU_RT_8.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_8" +{ + "Name" "WireGPU_RT_8" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/settings/render_targets/WireGPU_RT_9.txt b/settings/render_targets/WireGPU_RT_9.txt new file mode 100644 index 0000000000..b22e81f0e1 --- /dev/null +++ b/settings/render_targets/WireGPU_RT_9.txt @@ -0,0 +1,6 @@ +"WireGPU_RT_9" +{ + "Name" "WireGPU_RT_9" + "Width" "512" + "Height" "512" +} \ No newline at end of file diff --git a/sound/synth/12_5_pwm_1760.wav b/sound/synth/12_5_pwm_1760.wav new file mode 100644 index 0000000000..00323401f2 Binary files /dev/null and b/sound/synth/12_5_pwm_1760.wav differ diff --git a/sound/synth/12_5_pwm_440.wav b/sound/synth/12_5_pwm_440.wav new file mode 100644 index 0000000000..9065028177 Binary files /dev/null and b/sound/synth/12_5_pwm_440.wav differ diff --git a/sound/synth/12_5_pwm_880.wav b/sound/synth/12_5_pwm_880.wav new file mode 100644 index 0000000000..2cbc135bed Binary files /dev/null and b/sound/synth/12_5_pwm_880.wav differ diff --git a/sound/synth/25_pwm_1760.wav b/sound/synth/25_pwm_1760.wav new file mode 100644 index 0000000000..a86285c525 Binary files /dev/null and b/sound/synth/25_pwm_1760.wav differ diff --git a/sound/synth/25_pwm_440.wav b/sound/synth/25_pwm_440.wav new file mode 100644 index 0000000000..5dd2437616 Binary files /dev/null and b/sound/synth/25_pwm_440.wav differ diff --git a/sound/synth/25_pwm_880.wav b/sound/synth/25_pwm_880.wav new file mode 100644 index 0000000000..c12ad03a8c Binary files /dev/null and b/sound/synth/25_pwm_880.wav differ diff --git a/sound/synth/75_pwm_1760.wav b/sound/synth/75_pwm_1760.wav new file mode 100644 index 0000000000..8321f5520f Binary files /dev/null and b/sound/synth/75_pwm_1760.wav differ diff --git a/sound/synth/75_pwm_440.wav b/sound/synth/75_pwm_440.wav new file mode 100644 index 0000000000..488bec7587 Binary files /dev/null and b/sound/synth/75_pwm_440.wav differ diff --git a/sound/synth/75_pwm_880.wav b/sound/synth/75_pwm_880.wav new file mode 100644 index 0000000000..81fa7747a8 Binary files /dev/null and b/sound/synth/75_pwm_880.wav differ diff --git a/sound/synth/brown_noise.wav b/sound/synth/brown_noise.wav new file mode 100644 index 0000000000..94d3aee8a0 Binary files /dev/null and b/sound/synth/brown_noise.wav differ diff --git a/sound/synth/pink_noise.wav b/sound/synth/pink_noise.wav new file mode 100644 index 0000000000..f263a755e3 Binary files /dev/null and b/sound/synth/pink_noise.wav differ diff --git a/sound/synth/saw.wav b/sound/synth/saw.wav new file mode 100644 index 0000000000..3b6635a76f Binary files /dev/null and b/sound/synth/saw.wav differ diff --git a/sound/synth/saw_1760.wav b/sound/synth/saw_1760.wav new file mode 100644 index 0000000000..f8197a78de Binary files /dev/null and b/sound/synth/saw_1760.wav differ diff --git a/sound/synth/saw_440.wav b/sound/synth/saw_440.wav new file mode 100644 index 0000000000..64cbe10525 Binary files /dev/null and b/sound/synth/saw_440.wav differ diff --git a/sound/synth/saw_880.wav b/sound/synth/saw_880.wav new file mode 100644 index 0000000000..df7aaab2f3 Binary files /dev/null and b/sound/synth/saw_880.wav differ diff --git a/sound/synth/saw_inverted_1760.wav b/sound/synth/saw_inverted_1760.wav new file mode 100644 index 0000000000..106de69d28 Binary files /dev/null and b/sound/synth/saw_inverted_1760.wav differ diff --git a/sound/synth/saw_inverted_440.wav b/sound/synth/saw_inverted_440.wav new file mode 100644 index 0000000000..2914879a10 Binary files /dev/null and b/sound/synth/saw_inverted_440.wav differ diff --git a/sound/synth/saw_inverted_880.wav b/sound/synth/saw_inverted_880.wav new file mode 100644 index 0000000000..80b251bb0a Binary files /dev/null and b/sound/synth/saw_inverted_880.wav differ diff --git a/sound/synth/sine.wav b/sound/synth/sine.wav new file mode 100644 index 0000000000..f5328d7ea5 Binary files /dev/null and b/sound/synth/sine.wav differ diff --git a/sound/synth/sine_1760.wav b/sound/synth/sine_1760.wav new file mode 100644 index 0000000000..719c45dcca Binary files /dev/null and b/sound/synth/sine_1760.wav differ diff --git a/sound/synth/sine_440.wav b/sound/synth/sine_440.wav new file mode 100644 index 0000000000..139ecd4a18 Binary files /dev/null and b/sound/synth/sine_440.wav differ diff --git a/sound/synth/sine_880.wav b/sound/synth/sine_880.wav new file mode 100644 index 0000000000..cb206872b9 Binary files /dev/null and b/sound/synth/sine_880.wav differ diff --git a/sound/synth/square.wav b/sound/synth/square.wav new file mode 100644 index 0000000000..78f616cc2a Binary files /dev/null and b/sound/synth/square.wav differ diff --git a/sound/synth/square_1760.wav b/sound/synth/square_1760.wav new file mode 100644 index 0000000000..b9f7780159 Binary files /dev/null and b/sound/synth/square_1760.wav differ diff --git a/sound/synth/square_440.wav b/sound/synth/square_440.wav new file mode 100644 index 0000000000..03ab9fd728 Binary files /dev/null and b/sound/synth/square_440.wav differ diff --git a/sound/synth/square_880.wav b/sound/synth/square_880.wav new file mode 100644 index 0000000000..1cd2f64bbc Binary files /dev/null and b/sound/synth/square_880.wav differ diff --git a/sound/synth/tri.wav b/sound/synth/tri.wav new file mode 100644 index 0000000000..7998f8220d Binary files /dev/null and b/sound/synth/tri.wav differ diff --git a/sound/synth/triangle_1760.wav b/sound/synth/triangle_1760.wav new file mode 100644 index 0000000000..58a97b7dcb Binary files /dev/null and b/sound/synth/triangle_1760.wav differ diff --git a/sound/synth/triangle_440.wav b/sound/synth/triangle_440.wav new file mode 100644 index 0000000000..482d0b1b65 Binary files /dev/null and b/sound/synth/triangle_440.wav differ diff --git a/sound/synth/triangle_880.wav b/sound/synth/triangle_880.wav new file mode 100644 index 0000000000..f1e22efe9e Binary files /dev/null and b/sound/synth/triangle_880.wav differ diff --git a/sound/synth/white_noise.wav b/sound/synth/white_noise.wav new file mode 100644 index 0000000000..cd6d54a2f3 Binary files /dev/null and b/sound/synth/white_noise.wav differ