diff --git a/.github/workflows/workshop.yml b/.github/workflows/workshop.yml new file mode 100644 index 0000000..5f8fbb1 --- /dev/null +++ b/.github/workflows/workshop.yml @@ -0,0 +1,41 @@ +name: Workshop + +on: + push: + branches: [ master ] + workflow_dispatch: + +jobs: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + linter: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: GLua Linter + uses: TASSIA710/action-glua-lint@v1.1.4 + with: + lint_maxScopeDepth: 99 + lint_deprecated: false + lint_emptyBlocks: false + lint_unusedParameters: false + lint_unusedLoopVars: false + lint_shadowing: false + lint_inconsistentVariableStyle: false + + # Creates a GMA and publishes it to the Steam Workshop + workshop-publish: + needs: linter + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Publish to Steam Workshop + uses: Earu/GSW-action@V2.1 + with: + account-name: ${{secrets.STEAM_NAME}} + account-password: ${{secrets.STEAM_PASSWORD}} + workshop-id: '2664712820' + addon-path: ${{env.GITHUB_WORKSPACE}} \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a935ef2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,489 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + The Crawler - Garry's Mod highly adaptable vehicle. + Copyright (C) 2021 Earu + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA diff --git a/addon.json b/addon.json new file mode 100644 index 0000000..06d54ad --- /dev/null +++ b/addon.json @@ -0,0 +1,14 @@ +{ + "title" : "THE Crawler", + "type" : "Vehicle", + "tags" : [ "fun", "roleplay" ], + "ignore" : + [ + "*.psd", + "*.vcproj", + "*.svn*", + ".git/*", + "*.md", + "*.yml" + ] +} \ No newline at end of file diff --git a/lua/entities/crawler.lua b/lua/entities/crawler.lua deleted file mode 100644 index b8d7f38..0000000 --- a/lua/entities/crawler.lua +++ /dev/null @@ -1,453 +0,0 @@ -AddCSLuaFile() - -ENT.Type = "anim" -ENT.Base = "base_anim" -ENT.Author = "Earu" -ENT.Spawnable = true -ENT.AdminOnly = false -ENT.PrintName = "Crawler" -ENT.ClassName = "crawler" -ENT.Category = "Half-Life 2" -ENT.Model = "models/sprops/rectangles/size_4/rect_36x72x3.mdl" -ENT.Information = "Highly adaptable monowheel vehicle" -ENT.Name = "Crawler" -ENT.Class = "crawler" -ENT.AdminSpawnable = false -ENT.Forward = false -ENT.Backward = false -ENT.Left = false -ENT.Right = false -ENT.LastComputedRollTime = 0 -ENT.CurrentRollVariation = 0 -ENT.WheelLoopStopTime = 0 - -for _, f in pairs(file.Find("sound/crawler/*", "GAME")) do - util.PrecacheSound("sound/crawler/" .. f) -end - -list.Set("Vehicles", "crawler", ENT) - -if SERVER then - local CVAR_FASTDL = CreateConVar("crawler_fastdl", "0", FCVAR_ARCHIVE, "Should clients download content for crawlers on join or not") - - local function add_resource_dir(dir) - for _,f in pairs(file.Find(dir .. "/*","GAME")) do - local path = dir .. "/" .. f - resource.AddFile(path) - end - end - - if CVAR_FASTDL:GetBool() then - add_resource_dir("sound/crawler") - end - - function ENT:Initialize() - self:SetModel("models/sprops/rectangles/size_4/rect_36x72x3.mdl") - self:PhysicsInit(SOLID_VPHYSICS) - self:SetMoveType(MOVETYPE_VPHYSICS) - self:SetSolid(SOLID_VPHYSICS) - self:SetUseType(SIMPLE_USE) - self:SetPos(self:GetPos() + Vector(0, 0, 40)) - - local phys = self:GetPhysicsObject() - if phys:IsValid() then - phys:SetMass(24) - phys:AddGameFlag(FVPHYSICS_NO_PLAYER_PICKUP) - phys:SetMaterial("gmod_silent") - phys:Wake() - end - - self.Seat = ents.Create("prop_vehicle_prisoner_pod") - self.Seat:SetModel("models/nova/jalopy_seat.mdl") - self.Seat:SetPos(self:GetPos() + Vector(0, 0, 5) + self:GetForward() * -5) - self.Seat:SetAngles(self:GetAngles() - Angle(0, 90, 0)) - self.Seat:SetParent(self) - self.Seat:Spawn() - - self.Wheel = ents.Create("prop_physics") - self.Wheel:SetModel("models/hunter/tubes/tube2x2x025.mdl") - self.Wheel:SetMaterial("models/props_combine/portalball001_sheet") - self.Wheel:SetPos(self:GetPos() + Vector(0, 0, 20)) - self.Wheel:SetAngles(self:GetAngles() + Angle(90, 90, 0)) - self.Wheel:SetUseType(SIMPLE_USE) - self.Wheel:Spawn() - - local old_bounds = self.Wheel:OBBMaxs() - self.Wheel:PhysicsInitSphere(50) - self.Wheel:SetCollisionBounds(-old_bounds, old_bounds) - self.Wheel:SetCollisionGroup(COLLISION_GROUP_PASSABLE_DOOR) - - self.Wheel:SetRenderMode(RENDERMODE_TRANSALPHA) - self.Wheel:SetColor(Color(0, 255, 255, 100)) - self:DeleteOnRemove(self.Wheel) - - local phys_wheel = self.Wheel:GetPhysicsObject() - if IsValid(phys_wheel) then - phys_wheel:SetMass(1) - phys_wheel:AddGameFlag(FVPHYSICS_NO_PLAYER_PICKUP) - phys_wheel:SetMaterial("gmod_silent") - phys_wheel:Wake() - end - - -- this is necessary because if its done too early it crashes the game - -- reason being that the spawnmenu, and some other entity spawning code set the pos - -- of the entity after spawning it which causes the constraint to literreally shit itself - timer.Simple(0.1, function() - if not IsValid(self) then return end - if not IsValid(self.Wheel) then return end - - self.Wheel:SetPos(self:GetPos() + Vector(0, 0, 20)) - self.Wheel:SetAngles(self:GetAngles() + Angle(90, 90, 0)) - constraint.Axis(self.Wheel, self, 0, 0, Vector(0, 0, 1), Vector(0, 0, 20), 0, 0, 0, 1) - end) - - self.Filter = { self, self.Seat, self.Wheel } - - local function key_handler(ply, key, pressed) - if ply ~= self:GetDriver() then return end - - if key == IN_FORWARD then - self.Forward = pressed - end - - if key == IN_BACK then - self.Backward = pressed - end - - if key == IN_MOVELEFT then - self.Left = pressed - end - - if key == IN_MOVERIGHT then - self.Right = pressed - end - end - - hook.Add("KeyPress", self, function(_, ply, key) key_handler(ply, key, true) end) - hook.Add("KeyRelease", self, function(_, ply, key) key_handler(ply, key, false) end) - - hook.Add("PlayerEnteredVehicle", self, function(_, ply, veh) - if veh ~= self.Seat then return end - table.insert(self.Filter, ply) - end) - - hook.Add("PlayerLeaveVehicle", self, function(_, ply, veh) - if veh ~= self.Seat then return end - table.remove(self.Filter, 4) - key_handler(ply, IN_FORWARD, false) - key_handler(ply, IN_BACK, false) - key_handler(ply, IN_MOVELEFT, false) - key_handler(ply, IN_MOVERIGHT, false) - end) - - hook.Add("PlayerUse", self, function(_, ply, ent) - if (ent == self.Wheel or ent == self) and IsValid(self.Seat) then - ply:EnterVehicle(self.Seat) - end - end) - - hook.Add("EntityEmitSound", self, function(_, data) - if data.Entity == self.Wheel then return false end - end) - end - - local MAX_ROLL_VARIATION = 30 - local ROLL_VARIATION = MAX_ROLL_VARIATION * MAX_ROLL_VARIATION - function ENT:ComputeTurningRoll() - local now = CurTime() - local delta = now - self.LastComputedRollTime - - if not self:InWater() then - if (self.Right and self.Left) or (not self.Right and not self.Left) then - if self.CurrentRollVariation > 0 then - self.CurrentRollVariation = math.max(self.CurrentRollVariation - (ROLL_VARIATION * delta), 0) - elseif self.CurrentRollVariation < 0 then - self.CurrentRollVariation = math.min(self.CurrentRollVariation + (ROLL_VARIATION * delta), 0) - else - self.CurrentRollVariation = 0 - end - else - if self.Right then - self.CurrentRollVariation = math.min(self.CurrentRollVariation + (ROLL_VARIATION * delta), MAX_ROLL_VARIATION) - end - - if self.Left then - self.CurrentRollVariation = math.max(self.CurrentRollVariation - (ROLL_VARIATION * delta), -MAX_ROLL_VARIATION) - end - end - else - self.CurrentRollVariation = 0 - end - - self.LastComputedRollTime = now - return self.CurrentRollVariation - end - - function ENT:ComputeAngularVelocity(phys, target_ang) - local ang_vel = Vector(0, 0, 0) - local cur_ang = phys:GetAngles() - local cur_forward = cur_ang:Forward() - local cur_right = cur_ang:Right() - local target_right = target_ang:Right() - local target_up = target_ang:Up() - local pitch_vel = math.asin(cur_forward:Dot(target_up)) * 180 / math.pi - local yaw_vel = math.asin(cur_forward:Dot(target_right)) * 180 / math.pi - local roll_vel = math.asin(cur_right:Dot(target_up)) * 180 / math.pi - ang_vel.y = ang_vel.y + pitch_vel - ang_vel.z = ang_vel.z + yaw_vel - ang_vel.x = ang_vel.x + roll_vel + self:ComputeTurningRoll() - - return ang_vel - end - - local trace_line = util.TraceLine - -- Technically we could only do two traces, but adding more fallbacks allows the bike to be more adaptable - local TRACE_LENGTH = 200 - local PLATE_FORWARD_LENGTH = 36 - local PLATE_SIDE_LENGTH = 20 - function ENT:ExecuteTraces() - local tr_front, tr_back, tr_right, tr_left - local vector_down = -self:GetUp() * TRACE_LENGTH - local vector_forward = self:GetForward() * TRACE_LENGTH - local struct = { filter = self.Filter } - - do -- FRONT - local tr_front_pos = self:GetPos() + self:GetForward() * PLATE_FORWARD_LENGTH - - -- TRACE FORWARD TO FURTHER FORWARD - struct.start = tr_front_pos - struct.endpos = tr_front_pos + vector_down + vector_forward - tr_front = trace_line(struct) - - -- TRACE FORWARD TO BELOW - if not tr_front.Hit then - struct.endpos = tr_front_pos + vector_down - tr_front = trace_line(struct) - end - - -- TRACE FORWARD TO BACKWARD - if not tr_front.Hit then - struct.endpos = tr_front_pos + vector_down + -vector_forward - tr_front = trace_line(struct) - end - end - - do -- BACK - local tr_back_pos = self:GetPos() + -self:GetForward() * PLATE_FORWARD_LENGTH - - -- TRACE BACKWARD TO FURTHER BACKWARD - struct.start = tr_back_pos - struct.endpos = tr_back_pos + vector_down + -vector_forward - tr_back = trace_line(struct) - - -- TRACE BACKWARD TO BELOW - if not tr_back.Hit then - struct.endpos = tr_back_pos + vector_down - tr_back = trace_line(struct) - end - - -- TRACE BACKWARD TO FORWARD - if not tr_back.Hit then - struct.endpos = tr_back_pos + vector_down + vector_forward - tr_back = trace_line(struct) - end - end - - do -- RIGHT - local tr_right_pos = self:GetPos() + self:GetRight() * PLATE_SIDE_LENGTH - struct.start = tr_right_pos - struct.endpos = tr_right_pos + vector_down - tr_right = trace_line(struct) - end - - do -- LEFT - local tr_left_pos = self:GetPos() + -self:GetRight() * PLATE_SIDE_LENGTH - struct.start = tr_left_pos - struct.endpos = tr_left_pos + vector_down - tr_left = trace_line(struct) - end - - return tr_front, tr_back, tr_right, tr_left - end - - local DAMP_FACTOR = 1.00001 - local VECTOR_UP = Vector(0, 0, 1) - local ANGLE_VEL_MULT = 100 - local VEL_MULT = 500 - local DOWNWARD_FORCE = -600 - local MIN_VEL_FOR_SOUND = 10 - function ENT:Think() - if not IsValid(self.Wheel) then return end - - local phys = self:GetPhysicsObject() - local phys_wheel = self.Wheel:GetPhysicsObject() - if not IsValid(phys) or not IsValid(phys_wheel) then return end - - -- enable back gravity for jumping, falling etc - if not self:IsOnSurface() then - phys:EnableGravity(true) - phys_wheel:EnableGravity(true) - return - end - - do -- base related stuff - phys:EnableGravity(false) - local target_ang = Angle(0, 0, 0) - - if self:InWater() then - local cur_ang = self:GetAngles() - target_ang = Angle(0, cur_ang.yaw, cur_ang.roll) - else - local tr_front, tr_back, tr_right, tr_left = self:ExecuteTraces() - if tr_front.Hit and tr_back.Hit then - -- Rotation matrix, will work with ceilings, sky etc but not with walls -> gimbal lock - local diff_ang = (tr_back.HitPos - tr_front.HitPos):Angle() - if tr_right.Hit and tr_left.Hit then - local side_diff_ang = (tr_left.HitPos - tr_right.HitPos):Angle() - diff_ang.roll = side_diff_ang.pitch - end - - local m = Matrix() - m:Rotate(diff_ang) - m:SetUp(self:GetUp()) - - if self:OnWall() then - m:SetRight(self.Wheel:GetUp()) - end - - target_ang = m:GetAngles() - end - end - - local ang_vel = self:ComputeAngularVelocity(phys, target_ang) - ang_vel:Mul(20) - ang_vel:Sub(phys:GetAngleVelocity()) - phys:AddAngleVelocity(ang_vel) - end - - do -- wheel related stuff - phys_wheel:EnableGravity(false) - phys_wheel:AddVelocity(self:GetUp() * DOWNWARD_FORCE) -- stick to surface below - - if self.Forward then - phys_wheel:AddAngleVelocity(VECTOR_UP * ANGLE_VEL_MULT) - phys_wheel:AddVelocity(self:GetForward() * VEL_MULT) - - -- this should push harder on walls when going up or down - if self:OnWall() then - phys_wheel:AddVelocity(self:GetForward() * VEL_MULT / 2) - end - end - - if self.Backward then - phys_wheel:AddAngleVelocity(VECTOR_UP * -ANGLE_VEL_MULT) - phys_wheel:AddVelocity(self:GetForward() * -VEL_MULT) - end - - if self.Right then - phys_wheel:ApplyForceOffset(-self.Wheel:GetUp() * VEL_MULT * 1.5, self:GetPos() + self:GetForward() * PLATE_FORWARD_LENGTH) - end - - if self.Left then - phys_wheel:ApplyForceOffset(self.Wheel:GetUp() * VEL_MULT * 1.5, self:GetPos() + self:GetForward() * PLATE_FORWARD_LENGTH) - end - - local going_left = self.Left and 1 or 0 - local going_right = self.Right and 1 or 0 - local going_forward = self.Forward and 1 or 0 - local going_backward = self.Backward and 1 or 0 - if (going_left - going_right) == 0 then - local cur_ang_vel = phys_wheel:GetAngleVelocity() - phys_wheel:AddAngleVelocity(-cur_ang_vel / 2 * DAMP_FACTOR) - end - - if (going_forward - going_backward) == 0 then - local cur_vel = phys_wheel:GetVelocity() - phys_wheel:AddVelocity(-cur_vel / 2 * DAMP_FACTOR) - end - - local final_ang_vel = phys_wheel:GetAngleVelocity() - if math.abs(final_ang_vel.z) > MIN_VEL_FOR_SOUND then - if not self.WheelLoop then - self.WheelLoop = CreateSound(self,"crawler/wheel_loop.wav") - self.WheelLoop:Play() - end - - self.WheelLoopStopTime = nil - self.WheelLoop:ChangePitch(20 + math.min(math.abs(final_ang_vel.z) / 200, 200)) - end - - if self.WheelLoop and math.abs(final_ang_vel.z) < MIN_VEL_FOR_SOUND then - if not self.WheelLoopStopTime then - self.WheelLoopStopTime = CurTime() + 0.5 - elseif self.WheelLoopStopTime >= CurTime() then - self.WheelLoop:Stop() - self.WheelLoop = nil - end - end - end - - self:NextThink(CurTime()) - - return true - end - - function ENT:OnRemove() - if self.WheelLoop then - self.WheelLoop:Stop() - self.WheelLoop = nil - end - end - - function ENT:GetDriver() - return self.Seat:GetDriver() - end - - function ENT:InWater() - if self:WaterLevel() > 0 then return true end - if IsValid(self.Wheel) and self.Wheel:WaterLevel() > 0 then return true end - if IsValid(self.Seat) and self.Seat:WaterLevel() > 0 then return true end - - return false - end - - local MIN_WALL_PITCH = 50 - function ENT:OnWall() - return math.abs(self:GetAngles().pitch) > MIN_WALL_PITCH - end - - function ENT:IsOnSurface() - if self:InWater() then return true end - - local tr = util.TraceLine({ - start = self:GetPos(), - endpos = self:GetPos() + self:GetUp() * -TRACE_LENGTH, - filter = self.Filter, - }) - - return tr.Hit == true - end - - local impact_sounds = { - "physics/metal/metal_solid_impact_soft1.wav", - "physics/metal/metal_solid_impact_soft2.wav", - "physics/metal/metal_solid_impact_soft3.wav", - "physics/metal/metal_solid_impact_hard1.wav", - "physics/metal/metal_solid_impact_hard2.wav", - "physics/metal/metal_solid_impact_hard3.wav", - "physics/metal/metal_solid_impact_hard4.wav", - "physics/metal/metal_solid_impact_hard5.wav", - } - function ENT:PhysicsCollide(data, collider) - if not IsValid(data.HitEntity) then return end - if data.HitEntity == self or data.HitEntity == self.Seat then - local vel = data.OurOldVelocity:Length() - local impact_sound = vel <= 1000 and impact_sounds[math.random(1, 3)] or impact_sounds[math.random(4, #impact_sounds)] - self:EmitSound(impact_sound) - end - end -end - -if CLIENT then - language.Add("crawler", "Crawler") -end \ No newline at end of file diff --git a/lua/entities/prop_vehicle_crawler/cl_init.lua b/lua/entities/prop_vehicle_crawler/cl_init.lua new file mode 100644 index 0000000..780a47d --- /dev/null +++ b/lua/entities/prop_vehicle_crawler/cl_init.lua @@ -0,0 +1,209 @@ +include("shared.lua") +local tag = "prop_vehicle_crawler" +local Ride_Height = 33 + +language.Add(tag, "Crawler") +local wheel_mat = Material("models/crawler/energy_wheel") + +local max_bar_height = 256 - 100 +local max_velocity = 3000 + +local gizo_cam_pos = Vector(-460, -460, 650) +local gizmo_cam_ang = Angle(45, 45, 0) +local boost_text_col = Color(255, 0, 0) +local WHEEL_OFFSET = Vector(0, 0, 10) + +hook.Add("CalcVehicleView", tag, function(veh, ply, view) + local crawler = veh:GetParent() + + if not IsValid(crawler) then return end + if crawler:GetClass() ~= tag then return end + if not veh:GetThirdPersonMode() then return end + + local tr = util.TraceHull( { + start = view.origin, + endpos = view.origin - view.angles:Forward() * (50 + veh:GetCameraDistance() * 50), + filter = {crawler, veh, ply}, + mins = Vector(-4, -4, -4), + maxs = Vector(4, 4, 4), + } ) + + view.drawviewer = true + view.origin = tr.HitPos + return view +end) + +function ENT:SetupWheel() + self.Wheel = ClientsideModel("models/crawler/energy_wheel.mdl", RENDERGROUP_BOTH) + self.Wheel:SetPos(self:LocalToWorld(WHEEL_OFFSET)) + self.Wheel:SetAngles(self:LocalToWorldAngles(Angle(0, 0, 0))) + self.Wheel:SetColor(self.EnergyColor) + self.Wheel.RenderGroup = RENDERGROUP_BOTH + self.Wheel:SetParent(self) + self.Wheel:Spawn() +end + +function ENT:Initialize() + self.vel_increment = 0 + self.steering_wheel_angle = 0 + + self:SetupWheel() + + self.DashboardTexture = self.DashboardTexture or GetRenderTargetEx( + "Crawler_Dashboard_" .. self:EntIndex(), + 512, + 256, + RT_SIZE_LITERAL, + MATERIAL_RT_DEPTH_NONE, + 16, + CREATERENDERTARGETFLAGS_HDR, + IMAGE_FORMAT_DEFAULT + ) + self.DashboardMaterial = self.DashboardMaterial or CreateMaterial( + "Crawler_Dashboard_Material_" .. self:EntIndex(), + "VertexLitGeneric", + { + ["$basetexture"] = self.DashboardTexture:GetName(), + ["$model"] = 1, + ["$nodecal"] = 1, + ["$selfillum"] = 1, + ["$selfillummask"] = "dev/reflectivity_30b" + } + ) + + local clr = self:GetEnergyColor() + self.EnergyColor = Color(clr.x, clr.y, clr.z) + self.GizmoModel = self.GizmoModel or ClientsideModel("models/crawler/gizmo.mdl") + self.GizmoModel:SetNoDraw(true) + +end + +local Ride_Height_Visual = Ride_Height + 11 +function ENT:Think() + if IsValid(self.Wheel) then + --Wheel spin + self.vel_local = self:WorldToLocal(self:GetPos() + self:GetVelocity() * 180 * FrameTime()) + self.vel_increment = self.vel_increment + (self.vel_local[1] / 295.30970943744) --circumference of the wheel + self.Wheel:SetAngles(self:LocalToWorldAngles(Angle(self.vel_increment, 0, 0))) + + --Wheel suspension + local Terrain_Distance = util.TraceHull({ + start = self:LocalToWorld(WHEEL_OFFSET), + endpos = self:LocalToWorld(WHEEL_OFFSET - Vector(0, 0, Ride_Height_Visual)), + filter = self, + mins = Vector(-2, -2, -2), + maxs = Vector(2, 2, 2), + mask = MASK_SOLID, + collisiongroup = COLLISION_GROUP_WEAPON + }) + self.Wheel:SetPos(self:LocalToWorld(WHEEL_OFFSET + Vector(0, 0, Ride_Height_Visual- Terrain_Distance.Fraction * Ride_Height_Visual))) + else + self:SetupWheel() + end + + --Steering Wheel + local AD = self:GetSteering() + self.steering_wheel_angle = self.steering_wheel_angle + math.Clamp((AD * 20 - self.steering_wheel_angle) * 0.035, -2, 2) * 150 * FrameTime() + self:ManipulateBoneAngles(1, Angle(self.steering_wheel_angle, 0, 0), false) + if IsValid(self:GetDriver()) then + self:GetDriver():SetPoseParameter("vehicle_steer", -self.steering_wheel_angle / 25) + end +end + +function ENT:DrawDashboard() + if not IsValid(self) then return end + + render.PushRenderTarget(self.DashboardTexture) + cam.Start2D() + surface.SetDrawColor(self.EnergyColor.r, self.EnergyColor.g, self.EnergyColor.b, 10) + surface.DrawRect(0, 0, 512, 256) + + surface.SetMaterial(wheel_mat) + surface.SetDrawColor(255, 255, 255) + surface.DrawTexturedRectUV(0, 0, 512, 256, 0, 0.02, 1.0, 0.5) + + surface.SetDrawColor(0, 0, 0, 200) + surface.DrawRect(10, 20, 256 - 10, 256 - 40, 1) + surface.SetDrawColor(255, 255, 255, 255) + surface.DrawOutlinedRect(10, 20, 256 - 10, 256 - 40, 3) + + surface.SetDrawColor(0, 0, 0, 200) + surface.DrawRect(256 + 10, 20, 256 - 20, 256 - 40, 1) + surface.SetDrawColor(255, 255, 255, 255) + surface.DrawOutlinedRect(256 + 10, 20, 256 - 20, 256 - 40, 3) + + self:DrawVelocityBars() + self:DrawGizmo() + cam.End2D() + render.PopRenderTarget() +end + +function ENT:DrawVelocityBars() + local vel = self:GetVelocity() + + local vel_x = math.abs(vel.x) + local vel_y = math.abs(vel.y) + local vel_z = math.abs(vel.z) + + local x_height = (math.Clamp(vel_x, 0, max_velocity) / max_velocity) * -max_bar_height + surface.SetDrawColor(HSVToColor(0, math.Clamp(vel_x, 0, max_velocity) / max_velocity, 1)) + surface.DrawRect(30, 256 - 30, 40, x_height) + + local y_height = (math.Clamp(vel_y, 0, max_velocity) / max_velocity) * -max_bar_height + surface.SetDrawColor(HSVToColor(0, math.Clamp(vel_y, 0, max_velocity) / max_velocity, 1)) + surface.DrawRect(115, 256 - 30, 40, y_height) + + local z_height = (math.Clamp(vel_z, 0, max_velocity) / max_velocity) * -max_bar_height + surface.SetDrawColor(HSVToColor(0, math.Clamp(vel_z, 0, max_velocity) / max_velocity, 1)) + surface.DrawRect(200, 256 - 30, 40, z_height) + + draw.DrawText(math.floor(vel_x), "DermaLarge", 50, 30, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) + draw.DrawText(math.floor(vel_y), "DermaLarge", 136, 30, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) + draw.DrawText(math.floor(vel_z), "DermaLarge", 220, 30, color_white, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) +end + +function ENT:DrawGizmo() + local driver = self:GetNWEntity("Driver") + if IsValid(driver) and driver:KeyDown(IN_SPEED) then + boost_text_col.a = RealTime() * 5 % 1 > 0.5 and 255 or 0 + draw.DrawText("BOOST", "DermaLarge", 256 + 128, 30, boost_text_col, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) + end + + if not IsValid(self.GizmoModel) then return end + + self.GizmoModel:SetAngles(-self:LocalToWorldAngles(Angle(0, 45, 0))) + + cam.Start3D(gizo_cam_pos, gizmo_cam_ang, 3.5, 256, 30, 256, 256 - 60) + render.SuppressEngineLighting(true) + self.GizmoModel:DrawModel() + render.SuppressEngineLighting(false) + cam.End3D() +end + +local sprite_mat = Material("sprites/glow04_noz") +function ENT:DrawTranslucent() + self:DrawDashboard() + + render.MaterialOverrideByIndex(1, self.DashboardMaterial) + self:DrawModel() + render.MaterialOverrideByIndex() + + local size = (self:GetVelocity():Length()) * 2 / 100 + + render.SetMaterial(sprite_mat) + for i = 1, 4 do + local attachment = self:GetAttachment(i) + if not attachment then continue end + render.DrawSprite(attachment.Pos, size, size, self.EnergyColor) + end +end + +function ENT:OnRemove() + if IsValid(self.GizmoModel) then + self.GizmoModel:Remove() + end + + if IsValid(self.Wheel) then + self.Wheel:Remove() + end +end \ No newline at end of file diff --git a/lua/entities/prop_vehicle_crawler/init.lua b/lua/entities/prop_vehicle_crawler/init.lua new file mode 100644 index 0000000..2f09d03 --- /dev/null +++ b/lua/entities/prop_vehicle_crawler/init.lua @@ -0,0 +1,304 @@ +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") +include("shared.lua") + +local CVAR_FASTDL = CreateConVar("prop_vehicle_crawler_fastdl", "1", FCVAR_ARCHIVE, "Should clients download content for crawlers on join or not") +local Ride_Height = 33 +local tag = "prop_vehicle_crawler" + +--FastDL +local function add_resource_dir(dir) + for _, f in pairs(file.Find(dir .. "/*","GAME")) do + local path = dir .. "/" .. f + resource.AddSingleFile(path) + end +end + +if CVAR_FASTDL:GetBool() then + resource.AddSingleFile("materials/entities/prop_vehicle_crawler.png") + + add_resource_dir("sound/crawler") + add_resource_dir("materials/models/crawler") + add_resource_dir("models/crawler") +end + +function ENT:SetupTrails() + self.Trails = self.Trails or {} + for i = 1, 4 do + if IsValid(self.Trails[i]) then SafeRemoveEntity(self.Trails[i]) end + + self.Trails[i] = util.SpriteTrail(self, i, self.EnergyColor, true, 32, 0, 0.1, 0.015625, "trails/laser.vmt") + if CPPI then + self.Trails[i]:CPPISetOwner(self:GetCreator()) + self.Trails[i]:SetOwner(self:GetCreator()) + end + end +end + +function ENT:UpdateTrailColors() + for _, v in ipairs(self.Trails) do + if not IsValid(v) then self.SetupTrails() break end + + v:Input("Color", nil, nil, string.format("%i %i %i", self.EnergyColor.r, self.EnergyColor.g, self.EnergyColor.b)) + end +end + +function ENT:SetupSounds() + self.Sounds = self.Sounds or {} + self.Sounds.EngineLoop = CreateSound(self, "crawler/engine_loop.wav") + self.Sounds.WheelLoop = CreateSound(self, "crawler/wheel_loop.wav") +end + +function ENT:Initialize() + self:SetModel("models/crawler/monowheel.mdl") + self:PhysicsInit(SOLID_VPHYSICS) + self:SetMoveType(MOVETYPE_VPHYSICS) + self:SetSolid(SOLID_VPHYSICS) + self:SetUseType(SIMPLE_USE) + self:SetPos(self:GetPos() + Vector(0, 0, 47)) + self:SetRenderMode( RENDERMODE_TRANSALPHA ) + self:AddFlags( FL_OBJECT ) + + self.phys = self:GetPhysicsObject() + if not IsValid(self.phys) then return end + self.phys:SetMass(1000) + self.phys:SetMaterial("metalvehicle") + self.phys:Wake() + + if IsValid(self.Seat) then SafeRemoveEntity(self.Seat) end + self.Seat = ents.Create("prop_vehicle_prisoner_pod") + if not IsValid(self.Seat) then return end + + self.Seat:SetModel("models/nova/airboat_seat.mdl") + self.Seat:SetMoveType( MOVETYPE_NONE ) + + self.Seat:SetPos(self:LocalToWorld(Vector(-29, 0, -9))) + self.Seat:SetAngles(self:LocalToWorldAngles(Angle(0, -90, 0))) + self.Seat:SetUseType(SIMPLE_USE) + self.Seat:Spawn() + self.Seat:Activate() + self.Seat:SetNoDraw(true) + self.Seat:SetParent(self) + self.Seat:SetVehicleClass("phx_seat3") + self.Seat:SetKeyValue("vehiclescript","scripts/vehicles/prisoner_pod.txt") + self.Seat:SetKeyValue("limitview", 0) + self.Seat.Crawler = self + + self:SetNW2Entity("Seat", self.Seat) + self.Seat:GetPhysicsObject():EnableDrag( false ) + self.Seat:GetPhysicsObject():EnableMotion( false ) + self.Gravity = -physenv.GetGravity() * engine.TickInterval() + self.Tick_Adjust = 66 * engine.TickInterval() + + self:DeleteOnRemove(self.Seat) + + local owner = self:GetCreator() + if IsValid(owner) then + + if CPPI then + self.Seat:CPPISetOwner(owner) + end + + self.Seat:SetOwner(owner) + self.Seat:SetCreator(owner) + end + + self:SetupTrails() + self:SetupSounds() + + self.Forward = 0 + self.Backward = 0 + self.Left = 0 + self.Right = 0 + self.Turbo = 0 + self.WS = 0 + self.AD = 0 + self._Derivative = 0 + self._Cross = Vector(0, 0, 0) + self.Cross = Vector(0, 0, 0) + + self.Filter = { self, self.Seat } +end + +function ENT:ControlHandler(ply, key, pressed) + local process_input = pressed and 1 or 0 + + if key == IN_FORWARD then self.Forward = process_input end + if key == IN_BACK then self.Backward = process_input end + if key == IN_MOVELEFT then self.Left = process_input end + if key == IN_MOVERIGHT then self.Right = process_input end + if key == IN_SPEED then self.Turbo = process_input * 1.5 end + + self.WS = self.Forward - self.Backward + self.AD = self.Left - self.Right + self:SetSteering(self.AD) +end + +local function handle_keys(ply, key, pressed) + local vhy = ply:GetVehicle() + if not IsValid(vhy) then return end + if not IsValid(vhy.Crawler) then return end + + vhy.Crawler:ControlHandler(ply, key, pressed) +end + +hook.Add("KeyPress", tag, function(ply, key) handle_keys(ply, key, true) end) +hook.Add("KeyRelease", tag, function(ply, key) handle_keys(ply, key, false) end) + +hook.Add("PlayerEnteredVehicle", "prop_vehicle_crawler", function(ply, veh) + local crawler = veh.Crawler + if not IsValid(crawler) then return end + + crawler:SetDriver(ply) + + crawler.Sounds.EngineLoop:PlayEx(0, 100) + crawler.Sounds.WheelLoop:PlayEx(0, 100) +end) + +hook.Add("PlayerLeaveVehicle", "prop_vehicle_crawler", function(ply, veh) + local crawler = veh.Crawler + if not IsValid(crawler) then return end + + crawler:SetDriver(NULL) + + crawler.Forward = 0 + crawler.Backward = 0 + crawler.Left = 0 + crawler.Right = 0 + crawler.Turbo = 0 + crawler.WS = 0 + crawler.AD = 0 + + crawler.Sounds.EngineLoop:ChangeVolume(0, 0.5) + crawler.Sounds.WheelLoop:ChangeVolume(0, 0.5) + timer.Simple(0.5, function() + if not IsValid(crawler) then return end + + crawler.Sounds.EngineLoop:Stop() + crawler.Sounds.WheelLoop:Stop() + end) + +end) + +function ENT:Use(ply, activator) + if ply ~= activator then return end + if not IsValid(self.Seat) then return end + + ply:EnterVehicle(self.Seat) +end + +local MIN_VEL_FOR_SOUND = 500 +function ENT:HandleSounds(velfwd) + self.Sounds.WheelLoop:ChangeVolume(math.min(velfwd / MIN_VEL_FOR_SOUND, 1), 0.1) + self.Sounds.WheelLoop:ChangePitch(20 + math.min(velfwd * 0.005, 200)) + + self.Sounds.EngineLoop:ChangeVolume(math.ease.InExpo(math.Clamp((velfwd - 500) * 0.005 , 0, 1)), 0.1) + self.Sounds.EngineLoop:ChangePitch(100 + math.min(velfwd / 15, 130)) +end + +local PD_Settings = { P = 1, D = 3 } +function ENT:CalculatePD(PD, err, minmax) + self._Derivative = (err - self._Derivative) * PD.D + + local result = math.Clamp(err * PD.P + self._Derivative, -minmax, minmax) + self._Derivative = err + + return result * self.Tick_Adjust +end + +local Trace_Offsets = { + Vector(50, 20, 0), + Vector(50, -20, 0), + Vector(-60, -20, 0), + Vector(-60, 20, 0), + Vector(0, 0, 0) +} + +local deg2rad = math.pi / 180 +local function rotate_around_axis(this, axis, degrees) --thank you wiremod + local ca, sa = math.cos(degrees*deg2rad), math.sin(degrees*deg2rad) + local x,y,z = axis[1], axis[2], axis[3] + local length = (x*x+y*y+z*z)^0.5 + x,y,z = x/length, y/length, z/length + + return Vector((ca + (x^2)*(1-ca)) * this[1] + (x*y*(1-ca) - z*sa) * this[2] + (x*z*(1-ca) + y*sa) * this[3], + (y*x*(1-ca) + z*sa) * this[1] + (ca + (y^2)*(1-ca)) * this[2] + (y*z*(1-ca) - x*sa) * this[3], + (z*x*(1-ca) - y*sa) * this[1] + (z*y*(1-ca) + x*sa) * this[2] + (ca + (z^2)*(1-ca)) * this[3]) +end + +local Ride_Height_Functional = Ride_Height + 30 +function ENT:PhysicsUpdate(phys) + self.vel_local = phys:WorldToLocalVector(self:GetVelocity()) --composite not needed as its not e2 + local velfwd = math.abs(self.vel_local.x) + + self:HandleSounds(velfwd) + + local mass = phys:GetMass() + local Distance_Average = 0 + local Up_Average = Vector(0, 0, 0) + + --5 Traces, 4 in the corners, 1 in the middle + local tr_corner = {} + local Any_Trace_Hit = false + for i, v in ipairs(Trace_Offsets) do + tr_corner[i] = util.TraceHull({ + start = self:LocalToWorld(v), + endpos = self:LocalToWorld(v + self.vel_local * 0.2 - Vector(0, 0, Ride_Height_Functional)), + filter = self.Filter, + mins = Vector(-3, -3, -3), + maxs = Vector(3, 3, 3), + mask = MASK_SOLID, + collisiongroup = COLLISION_GROUP_WEAPON + }) + if tr_corner[i].HitSky then + Distance_Average = Distance_Average + 1 + tr_corner[i].HitPos = self:LocalToWorld(v + self.vel_local * 0.2 - Vector(0, 0, Ride_Height_Functional)) + continue + end + Any_Trace_Hit = Any_Trace_Hit or (tr_corner[i].Hit and not tr_corner[i].HitSky) + Distance_Average = Distance_Average + tr_corner[i].Fraction + end + + if not Any_Trace_Hit then return end + + --Get 4 cross products + local middle_pos = tr_corner[5].HitPos + local normal1 = -(tr_corner[1].HitPos - middle_pos):GetNormalized():Cross((tr_corner[2].HitPos - middle_pos):GetNormalized()) + local normal2 = -(tr_corner[2].HitPos - middle_pos):GetNormalized():Cross((tr_corner[3].HitPos - middle_pos):GetNormalized()) + local normal3 = -(tr_corner[3].HitPos - middle_pos):GetNormalized():Cross((tr_corner[4].HitPos - middle_pos):GetNormalized()) + local normal4 = -(tr_corner[4].HitPos - middle_pos):GetNormalized():Cross((tr_corner[1].HitPos - middle_pos):GetNormalized()) + + --Average it out into 1 + Up_Average = ((normal1 + normal2 + normal3 + normal4) * 0.25):GetNormalized() + + --Calculate lean based on angular velocity + Up_Average = rotate_around_axis(Up_Average, self:GetForward(), math.Clamp(-phys:GetAngleVelocity()[3] * 0.1, -20, 20)) + + --Movement Force + Distance_Average = (Distance_Average / #Trace_Offsets) * Ride_Height_Functional + local Up = Up_Average * self:CalculatePD(PD_Settings, Ride_Height - Distance_Average, mass) + local MoveForce = self:GetForward() * 20 * self.WS * (1 + self.Turbo) * self.Tick_Adjust + self.Force = (self.Gravity + Up + MoveForce - phys:GetVelocity() * 0.02) * mass + + phys:ApplyForceCenter(self.Force) + + --Angle Force + self._Cross = self.Cross + self.Cross = Up_Average:Cross(self:GetUp()) * 500 + self._Cross = self.Cross - self._Cross + + local AngVel = self:LocalToWorld(phys:GetAngleVelocity() * 0.8 - Vector(0, 0, self.AD * 170)) - self:GetPos() + self.AngForce = (self.Cross + self._Cross * 5 + AngVel) * mass / 28.5 * self.Tick_Adjust + phys:ApplyTorqueCenter(-self.AngForce) + + return true +end + +function ENT:OnRemove() + for k, v in pairs(self.Sounds) do + if not v then continue end + + v:Stop() + v = nil + end +end \ No newline at end of file diff --git a/lua/entities/prop_vehicle_crawler/shared.lua b/lua/entities/prop_vehicle_crawler/shared.lua new file mode 100644 index 0000000..c67082c --- /dev/null +++ b/lua/entities/prop_vehicle_crawler/shared.lua @@ -0,0 +1,87 @@ +ENT.Type = "anim" +ENT.Base = "base_anim" + +ENT.PrintName = "Crawler" +ENT.Name = "Crawler" +ENT.Author = "Earu" +ENT.Information = "Highly adaptable monowheel vehicle" +ENT.Category = "Earu" + +ENT.Class = "prop_vehicle_crawler" +ENT.Spawnable = false +ENT.AdminOnly = false +ENT.RenderGroup = RENDERGROUP_BOTH +ENT.Model = "models/crawler/monowheel.mdl" +ENT.EnergyColor = ENT.EnergyColor or Color(0, 255, 255) + +list.Set("Vehicles", "prop_vehicle_crawler", ENT) +tag = "prop_vehicle_crawler" + +function ENT:SetupDataTables() + self:NetworkVar("Float", 0, "Steering") + self:NetworkVar("Vector", 0, "EnergyColor") + self:NetworkVar("Entity", 0 , "Driver") + + self:NetworkVarNotify("EnergyColor", function(_, _, _, rgb) + self.EnergyColor.r = rgb.x + self.EnergyColor.g = rgb.y + self.EnergyColor.b = rgb.z + + if CLIENT then + if not IsValid(self.Wheel) then self:SetupWheel() return end + + self.Wheel:SetColor(self.EnergyColor) + end + if SERVER then + timer.Simple(0, function() + self:UpdateTrailColors() + end) + end + end) +end + +properties.Add("energy_color", { + MenuLabel = "Energy Color", + Order = 2e9, + MenuIcon = "icon16/color_wheel.png", + Filter = function( self, ent, ply ) + if not IsValid(ent) then return false end + if ent:IsPlayer() then return false end + + if ent:GetClass() ~= tag then return false end + if not gamemode.Call("CanProperty", ply, "energy_color", ent) then return false end + + return true + end, + Action = function(self, ent) + local frame = vgui.Create("DFrame") + local rgb = ent.EnergyColor + frame:SetSize(250, 200) + frame:Center() + frame:MakePopup() + frame:SetTitle(tostring(ent)) + frame.OnClose = function() + if not IsValid(ent) then return end + + ent.EnergyColor = rgb + self:MsgStart() + net.WriteEntity(ent) + net.WriteVector(Vector(rgb.r, rgb.g, rgb.b)) + self:MsgEnd() + end + + local color_combo = vgui.Create("DColorCombo", frame) + color_combo:Dock(FILL) + color_combo:SetColor(ent.EnergyColor) + function color_combo:OnValueChanged(rgb) + ent:SetEnergyColor(Vector(rgb.r, rgb.g, rgb.b)) + end + end, + Receive = function(self, length, ply) + local ent = net.ReadEntity() + local rgb = net.ReadVector() + + if not properties.CanBeTargeted(ent, ply) then return end + ent:SetEnergyColor(rgb) + end +}) \ No newline at end of file diff --git a/materials/entities/prop_vehicle_crawler.png b/materials/entities/prop_vehicle_crawler.png new file mode 100644 index 0000000..72efaac Binary files /dev/null and b/materials/entities/prop_vehicle_crawler.png differ diff --git a/materials/models/crawler/body.vmt b/materials/models/crawler/body.vmt new file mode 100644 index 0000000..a6868b5 --- /dev/null +++ b/materials/models/crawler/body.vmt @@ -0,0 +1,11 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/body" + "$surfaceprop" "metal" + + "$envmap" "env_cubemap" + "$halflambert" 1 + "$envmapcontrast" 1 + "$envmapsaturation" 0.1 + "$envmaptint" "[ 0.5 0.5 0.5 ]" +} \ No newline at end of file diff --git a/materials/models/crawler/body.vtf b/materials/models/crawler/body.vtf new file mode 100644 index 0000000..b327fc5 Binary files /dev/null and b/materials/models/crawler/body.vtf differ diff --git a/materials/models/crawler/body_colorable.vmt b/materials/models/crawler/body_colorable.vmt new file mode 100644 index 0000000..8d12a26 --- /dev/null +++ b/materials/models/crawler/body_colorable.vmt @@ -0,0 +1,14 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/body_colorable" + "$surfaceprop" "metal" + + "$envmap" "env_cubemap" + "$halflambert" 1 + "$envmapcontrast" 1 + "$envmapsaturation" 0.1 + "$envmaptint" "[ 0.5 0.5 0.5 ]" + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" +} \ No newline at end of file diff --git a/materials/models/crawler/body_colorable.vtf b/materials/models/crawler/body_colorable.vtf new file mode 100644 index 0000000..d0243da Binary files /dev/null and b/materials/models/crawler/body_colorable.vtf differ diff --git a/materials/models/crawler/body_exp.vtf b/materials/models/crawler/body_exp.vtf new file mode 100644 index 0000000..e7edce8 Binary files /dev/null and b/materials/models/crawler/body_exp.vtf differ diff --git a/materials/models/crawler/body_normal.vtf b/materials/models/crawler/body_normal.vtf new file mode 100644 index 0000000..d49fc01 Binary files /dev/null and b/materials/models/crawler/body_normal.vtf differ diff --git a/materials/models/crawler/chair.vmt b/materials/models/crawler/chair.vmt new file mode 100644 index 0000000..3ad9947 --- /dev/null +++ b/materials/models/crawler/chair.vmt @@ -0,0 +1,13 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/chair" + "$surfaceprop" "metal" + "$bumpmap" "models/crawler/chair_normal" + "$envmap" "env_cubemap" + "$envmapcontrast" 1 + "$halflambert" 1 + "$envmaptint" "[ .1 .1 .1 ]" + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" +} \ No newline at end of file diff --git a/materials/models/crawler/chair.vtf b/materials/models/crawler/chair.vtf new file mode 100644 index 0000000..3ca3c1b Binary files /dev/null and b/materials/models/crawler/chair.vtf differ diff --git a/materials/models/crawler/chair_normal.vtf b/materials/models/crawler/chair_normal.vtf new file mode 100644 index 0000000..61279fd Binary files /dev/null and b/materials/models/crawler/chair_normal.vtf differ diff --git a/materials/models/crawler/energy_wheel.vmt b/materials/models/crawler/energy_wheel.vmt new file mode 100644 index 0000000..4c70880 --- /dev/null +++ b/materials/models/crawler/energy_wheel.vmt @@ -0,0 +1,20 @@ +"UnlitGeneric" +{ + "$basetexture" "models/crawler/energy_wheel" + //$alphatest 1 + "$translucent" "1" + "$nocull" 1 + "$nodecal" "1" + + "$surfaceprop" "glass" + + "Proxies" + { + "AnimatedTexture" + { + "animatedtexturevar" "$basetexture" + "animatedtextureframenumvar" "$frame" + "animatedtextureframerate" 20 + } + } +} diff --git a/materials/models/crawler/energy_wheel.vtf b/materials/models/crawler/energy_wheel.vtf new file mode 100644 index 0000000..fc7ab0b Binary files /dev/null and b/materials/models/crawler/energy_wheel.vtf differ diff --git a/materials/models/crawler/gizmo.vmt b/materials/models/crawler/gizmo.vmt new file mode 100644 index 0000000..31e3e02 --- /dev/null +++ b/materials/models/crawler/gizmo.vmt @@ -0,0 +1,4 @@ +"UnLitGeneric" +{ + "$basetexture" "models/crawler/gizmo" +} \ No newline at end of file diff --git a/materials/models/crawler/gizmo.vtf b/materials/models/crawler/gizmo.vtf new file mode 100644 index 0000000..a583212 Binary files /dev/null and b/materials/models/crawler/gizmo.vtf differ diff --git a/materials/models/crawler/glass.vmt b/materials/models/crawler/glass.vmt new file mode 100644 index 0000000..44fb726 --- /dev/null +++ b/materials/models/crawler/glass.vmt @@ -0,0 +1,14 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/body" + "$additive" "1" + "$translucent" "1" + "$halflambert" "1" + + "$envmap" "env_cubemap" + "$envmapcontrast" 1 + "$envmaptint" "[ 0.5 0.5 0.5 ]" + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" +} \ No newline at end of file diff --git a/materials/models/crawler/metals.vmt b/materials/models/crawler/metals.vmt new file mode 100644 index 0000000..ce8e840 --- /dev/null +++ b/materials/models/crawler/metals.vmt @@ -0,0 +1,28 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/body_colorable" + "$surfaceprop" "metal" + "$model" 1 + "$HalfLambert" 1 + + "$bumpmap" "models/crawler/body_normal" + "$envmap" "env_cubemap" + + "$normalmapalphaenvmapmask" 1 + "$envmapcontrast" 0.5 + "$envmapsaturation" 0 + "$envmaptint" "[ 0.6 0.6 0.6 ]" + + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" + + "$phong" "1" + "$phongexponenttexture" "models/crawler/body_exp" + "$phongboost" "3" + "$phongfresnelranges" "[.7 .1 1]" + + "$rimlight" "1" + "$rimlightboost" "5" + "$rimlightexponent" "3" +} \ No newline at end of file diff --git a/materials/models/crawler/power_cell.vmt b/materials/models/crawler/power_cell.vmt new file mode 100644 index 0000000..aa6b407 --- /dev/null +++ b/materials/models/crawler/power_cell.vmt @@ -0,0 +1,20 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/power_cell" + "$bumpmap" "models/crawler/power_cell_normal" + "$surfaceprop" "metal" + "$halflambert" "1" + "$model" "1" + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" + + "$rimlight" "1" + "$rimlightexponent" "5" + "$rimlightboost" "0.2" + + "$phong" "1" + "$phongexponent" "60" + "$phongboost" "1" + "$phongfresnelranges" "[0.4 0.4 1]" +} \ No newline at end of file diff --git a/materials/models/crawler/power_cell.vtf b/materials/models/crawler/power_cell.vtf new file mode 100644 index 0000000..287ddb6 Binary files /dev/null and b/materials/models/crawler/power_cell.vtf differ diff --git a/materials/models/crawler/power_cell_illum.vmt b/materials/models/crawler/power_cell_illum.vmt new file mode 100644 index 0000000..fd3a0dd --- /dev/null +++ b/materials/models/crawler/power_cell_illum.vmt @@ -0,0 +1,5 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/power_cell_illum" + "$selfillum" 1 +} \ No newline at end of file diff --git a/materials/models/crawler/power_cell_illum.vtf b/materials/models/crawler/power_cell_illum.vtf new file mode 100644 index 0000000..0aeca00 Binary files /dev/null and b/materials/models/crawler/power_cell_illum.vtf differ diff --git a/materials/models/crawler/power_cell_illum_colorable.vmt b/materials/models/crawler/power_cell_illum_colorable.vmt new file mode 100644 index 0000000..55b07bd --- /dev/null +++ b/materials/models/crawler/power_cell_illum_colorable.vmt @@ -0,0 +1,5 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/power_cell_illum_colorable" + "$selfillum" 1 +} \ No newline at end of file diff --git a/materials/models/crawler/power_cell_illum_colorable.vtf b/materials/models/crawler/power_cell_illum_colorable.vtf new file mode 100644 index 0000000..8e0d403 Binary files /dev/null and b/materials/models/crawler/power_cell_illum_colorable.vtf differ diff --git a/materials/models/crawler/power_cell_normal.vtf b/materials/models/crawler/power_cell_normal.vtf new file mode 100644 index 0000000..f206826 Binary files /dev/null and b/materials/models/crawler/power_cell_normal.vtf differ diff --git a/materials/models/crawler/reactor.vmt b/materials/models/crawler/reactor.vmt new file mode 100644 index 0000000..60ff173 --- /dev/null +++ b/materials/models/crawler/reactor.vmt @@ -0,0 +1,21 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/reactor" + "$surfaceprop" "metal" + "$bumpmap" "models/crawler/reactor_normal" + + "$envmap" "env_cubemap" + "$envmapcontrast" 0.2 + "$envmapsaturation" 0 + "$envmaptint" "[1 1 1]" + "$model" 1 + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" + + "$phong" "1" + "$halflambert" "1" + "$phongexponent" "60" + "$phongboost" "2" + "$phongfresnelranges" "[.5 .8 1]" +} \ No newline at end of file diff --git a/materials/models/crawler/reactor.vtf b/materials/models/crawler/reactor.vtf new file mode 100644 index 0000000..818d9f5 Binary files /dev/null and b/materials/models/crawler/reactor.vtf differ diff --git a/materials/models/crawler/reactor_normal.vtf b/materials/models/crawler/reactor_normal.vtf new file mode 100644 index 0000000..da2a892 Binary files /dev/null and b/materials/models/crawler/reactor_normal.vtf differ diff --git a/materials/models/crawler/screen.vmt b/materials/models/crawler/screen.vmt new file mode 100644 index 0000000..8023dba --- /dev/null +++ b/materials/models/crawler/screen.vmt @@ -0,0 +1,12 @@ +"VertexLitGeneric" +{ + "$basetexture" "models/crawler/screen" + + "$detail" "models/crawler/screen" + "$detailscale" "1" + "$detailblendfactor" "1" + "$detailblendmode" "5" + + "$blendtintbybasealpha" "1" + "$blendtintcoloroverbase" "0" +} \ No newline at end of file diff --git a/materials/models/crawler/screen.vtf b/materials/models/crawler/screen.vtf new file mode 100644 index 0000000..345e171 Binary files /dev/null and b/materials/models/crawler/screen.vtf differ diff --git a/models/crawler/energy_wheel.dx80.vtx b/models/crawler/energy_wheel.dx80.vtx new file mode 100644 index 0000000..ea30f76 Binary files /dev/null and b/models/crawler/energy_wheel.dx80.vtx differ diff --git a/models/crawler/energy_wheel.dx90.vtx b/models/crawler/energy_wheel.dx90.vtx new file mode 100644 index 0000000..46238f2 Binary files /dev/null and b/models/crawler/energy_wheel.dx90.vtx differ diff --git a/models/crawler/energy_wheel.mdl b/models/crawler/energy_wheel.mdl new file mode 100644 index 0000000..93fdc4b Binary files /dev/null and b/models/crawler/energy_wheel.mdl differ diff --git a/models/crawler/energy_wheel.phy b/models/crawler/energy_wheel.phy new file mode 100644 index 0000000..e71f888 Binary files /dev/null and b/models/crawler/energy_wheel.phy differ diff --git a/models/crawler/energy_wheel.sw.vtx b/models/crawler/energy_wheel.sw.vtx new file mode 100644 index 0000000..58a706a Binary files /dev/null and b/models/crawler/energy_wheel.sw.vtx differ diff --git a/models/crawler/energy_wheel.vvd b/models/crawler/energy_wheel.vvd new file mode 100644 index 0000000..731955f Binary files /dev/null and b/models/crawler/energy_wheel.vvd differ diff --git a/models/crawler/gizmo.dx80.vtx b/models/crawler/gizmo.dx80.vtx new file mode 100644 index 0000000..4dc8446 Binary files /dev/null and b/models/crawler/gizmo.dx80.vtx differ diff --git a/models/crawler/gizmo.dx90.vtx b/models/crawler/gizmo.dx90.vtx new file mode 100644 index 0000000..1fa2436 Binary files /dev/null and b/models/crawler/gizmo.dx90.vtx differ diff --git a/models/crawler/gizmo.mdl b/models/crawler/gizmo.mdl new file mode 100644 index 0000000..959a8f7 Binary files /dev/null and b/models/crawler/gizmo.mdl differ diff --git a/models/crawler/gizmo.sw.vtx b/models/crawler/gizmo.sw.vtx new file mode 100644 index 0000000..cf95823 Binary files /dev/null and b/models/crawler/gizmo.sw.vtx differ diff --git a/models/crawler/gizmo.vvd b/models/crawler/gizmo.vvd new file mode 100644 index 0000000..1f77ff1 Binary files /dev/null and b/models/crawler/gizmo.vvd differ diff --git a/models/crawler/monowheel.dx80.vtx b/models/crawler/monowheel.dx80.vtx new file mode 100644 index 0000000..803a387 Binary files /dev/null and b/models/crawler/monowheel.dx80.vtx differ diff --git a/models/crawler/monowheel.dx90.vtx b/models/crawler/monowheel.dx90.vtx new file mode 100644 index 0000000..baf9795 Binary files /dev/null and b/models/crawler/monowheel.dx90.vtx differ diff --git a/models/crawler/monowheel.mdl b/models/crawler/monowheel.mdl new file mode 100644 index 0000000..a9bbcd9 Binary files /dev/null and b/models/crawler/monowheel.mdl differ diff --git a/models/crawler/monowheel.phy b/models/crawler/monowheel.phy new file mode 100644 index 0000000..14788a3 Binary files /dev/null and b/models/crawler/monowheel.phy differ diff --git a/models/crawler/monowheel.sw.vtx b/models/crawler/monowheel.sw.vtx new file mode 100644 index 0000000..bc33f8f Binary files /dev/null and b/models/crawler/monowheel.sw.vtx differ diff --git a/models/crawler/monowheel.vvd b/models/crawler/monowheel.vvd new file mode 100644 index 0000000..38723af Binary files /dev/null and b/models/crawler/monowheel.vvd differ diff --git a/sound/crawler/engine_loop.wav b/sound/crawler/engine_loop.wav new file mode 100644 index 0000000..2f99bbd Binary files /dev/null and b/sound/crawler/engine_loop.wav differ