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: ,