diff --git a/ParoxysmII/COPYING b/ParoxysmII/COPYING new file mode 100644 index 0000000..c4792dd --- /dev/null +++ b/ParoxysmII/COPYING @@ -0,0 +1,515 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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. +^L + 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 +^L + 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. + + + + Copyright (C) + + 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper +mail. + +You should also get your employer (if you work as a programmer) or +your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James +Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ParoxysmII/README b/ParoxysmII/README new file mode 100644 index 0000000..37f0686 --- /dev/null +++ b/ParoxysmII/README @@ -0,0 +1,269 @@ +Paroxysm II - Release Notes +Copyright (C) 2002 Jeff Teunissen +Copyright (C) 2002 Contributors to the QuakeForge Project + +This program 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 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 Lesser +General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place, Suite 330 + Boston, MA 02111-1307 USA + +Paroxysm II is based on "Paroxysm", created and donated to the public +domain by Frank "p0x" Condello . + +Table of Contents +~~~~~~~~~~~~~~~~~ + +1 Special Thanks +2 About Paroxysm II +3 Requirements +4 Installation +5 New Commands +6 Weapons +7 Armor +8 Configuring Deathmatch Options +9 Troubleshooting +10 Credits + + +1 Special Thanks +~~~~~~~~~~~~~~~~~~~~~~ + +The QuakeForge Project would like to thank Frank "p0x" Condello, first for +writing, and then donating Paroxysm to the public domain. You've created +a great game, and we hope that Paroxysm II lives up to the high standard +you've set. + +We would also like to thank Id Software, Inc. for writing and releasing +Quake under the GNU General Public License. The Quake engine is a most +excellent gift for the Free Software community, and has created an +entire subculture dedicated to playing all the wonderful Quake +modifications. Thank you! + + +2 About Paroxysm II +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Paroxysm II (Pox2) is a Deathmatch game for the QuakeForge engine. While +most (if not all) of Pox2 will work with non-QuakeForge Quake engines, +it will take some work, and we will not go out of our way to support +running Pox2 without QuakeForge servers and clients. + + +3 Requirements +~~~~~~~~~~~~~~~~~~~~ + +Pox2 requires a QuakeForge engine built with compression support. The +files in the package are gzip-compressed for small size and speed of +loading. + +An OpenGL-supporting client is HIGHLY recommended. The game works in +software mode, but it's nowhere near as cool. :) + + +4 Installation +~~~~~~~~~~~~~~~~~~~~ + +Copy the "paroxysm" directory in the distribution to the directory in +which your shared QuakeForge data files are located. On a Unix-based +system, these will usually be in "/usr/local/share/games/quakeforge"; on +a Windows system, this will usually be C:\QUAKE or something similar. +Create a subdirectory called "paroxysm" in your unshared data files (on +a Unix system, typically ~/.quakeforge/ -- on a Windows system, it's +usually the same as the shared directory), and copy the autoexec.cfg +there. You should edit this file to create a standard configuration for +yourself. + +NOTE: Make sure the "secondtrigger" command is bound to a key or button +that you have fast, ready access to; you won't be very formidable in a +deathmatch if you can't use the special features of the weapons! :) + + +5 New Commands +~~~~~~~~~~~~~~~~~~~~ + +These commands can by typed into the console, or bound to a key. + +The default bindings can be changed in the autoexec.cfg file (note: this +will change in the future -- there will be a "bindkeys" command that +will rebind the default keys for you, allowing you to use autoexec.cfg +for your own stuff. + +Command Description +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +secondtrigger - Activate the current weapon's secondary trigger. + - Default key binding: ALT + - This command is used a LOT. The Paroxysm II weapons + are most powerful when you learn how to use both of + their functions. +idtarget - Display the names of players that pass through your + line-of-sight. (toggles) + - Default key binding: I +rules - Display the state of all Deathmatch modifiers. + + +6 Weapons +~~~~~~~~~~~~~~~ + +Pox2's weapons are substantially different from Quake's. Paroxysm II +contains a number of rule changes that you may not be used to: + +There is no weapon auto switching when you run out of ammo. You will +hear some sort of click or buzz indicating you need more ammo. (Although +you can run out of ammo, you still can't cycle to a weapon if you have +no ammo for it.) + +NOTE: This rule can be overridden by using the Weapon Autoswitch Option. + +If you touch a weapon, it becomes your active weapon (no auto best +weapon). Picking up a backpack that contains a weapon will NOT change your current weapon. + +NOTE: This rule can be overriden by using the Weapon Autoswitch Option. + +You can not pick up a weapon if you already have it. The chief reason +for this is that it prevents things like respawn camping of certain +powerful weapons to dominate a map. + +Here is a table of the weapons, what they are and what they do. + +Weapon (#) Description +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +T-Shot (2) A rotating triple-barreled shotgun. The secondary + trigger primes all three barrels for a powerful + triple-blast. + (you spawn with this weapon) + +Combo Gun (3) Double-barreled shotgun. The secondary trigger fires a + short-range impact grenade. + +Plasma Gun (4) Fires short, fast-moving bursts of charged plasma. The + secondary trigger overloads the weapon, firing a large + plasma burst with high radial damage...but it takes time + to recharge when you do. + +Nail Gun (5) The gift that keeps on giving, the Nail Gun has a + secondary trigger that launches a Shrapnel Bomb. A + second press on the second trigger detonates your + deployed shrapnel bomb. + +Grenade Primary trigger fires "bouncy" grenades. Secondary +Launcher (6) trigger plants phase mines. Phase mines fuse with + inanimate objects and arm themselves on contact. They + are triggered by touch, damage, or movement. Phase mines + explode after 60 seconds, and can be placed inside + powerups, dead bodies, attached to walls or ceilings, + etc. In a future version, phase mines may become + invisible, but do less damage. + +Annihilator (7) The Annihilator is the weapon your mother warned you + about (or should have). It's a semi-automatic, + shoulder-mounted, double-barreled high-impact grenade + launcher. It fires two grenades per shot, and must be + reloaded after four shots. Because its main function can + be so devastating, the secondary trigger performs a + reload instead of some other offensive capability. + + +7 Armor +~~~~~~~~~~~~~ + +Like Paroxysm before it, Paroxysm II deals with armor in a different way +than Quake does. You don't pick up armor, you stand in a regeneration +station. There are three types of regeneration stations; blue, yellow, +and red. This color coding shows how high the station can recharge your +armor; blue can give you 50 armor points, yellow 150, red 250. Your +powered armor only has the capacity to hold 150 power units without +losing some over time...so if your armor meter shows red, you will lose +armor points over time until it reaches 150. + +The color of the armor indicator only shows how much armor you have -- +unlike Quake, there are not multiple armor types, they all protect +equally. + + +8 Deathmatch Options +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Server administrators can change how Paroxysm II plays by setting the +"deathmatch" Cvar in the server console. Deathmatch modifiers are +"bitfields" -- to combine two modifiers, add their values. + +NOTE: Normal deathmatch has a flag of 1. + +The "fraglimit" and "timelimit" Cvars still apply, but may not work the +same in various game modes. + +Mode (value) Description +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Predator Mode (2) All players are cloaked (eyes only) + +Dark Mode (4) Lighting is dimmed, and players are equipped + with flashlights. + +Last Man Standing (8) Players start with a set number of frags, and + lose a frag when they die. When only one player + remains, the game is over. + * fraglimit sets the initial frags for players + +Free For All (16) Players spawn with full health (200), armor, all + weapons, and 5 seconds of invulnerability. + Shield generators, quad damage, cloak, and + MegaShields are the only power-ups. + Backpacks contain health and any ammo left when + its owner died. + +Gib Mode (32) Players always gib when killed, and never drop + backpacks. + +Weapon Autoswitch (128) Similar to the original Quake weapon pickup + rules; Switches to another weapon when you run + out of ammo, selects your best weapon when you + pick up weapons or ammo. + + (NOTE: You still can't pick up a weapon you + already have) + + +9 Troubleshooting +~~~~~~~~~~~~~~~~~~~~~~~ + +Problem: My flashlight doesn't work in dark mode when using OpenGL! +Fix: Hit "L" to toggle the gl_dlight_lightmap Cvar. This may slow your +gameplay down a bit, but it is necessary for dark mode to work in GL. + +Problem: There's not enough ammo on (insert map here). +Explanation: Although Paroxysm II is compatible with existing DeathMatch +maps, some maps may not be ideal. Due to weapon and rule changes, many +(mostly older) maps will not have enough ammo for a solid game. +Unfortunately, none of the original Quake Deathmatch maps are +ideal Paroxysm II maps. For this reason, we are compiling a list of maps +that are known to work well with Paroxysm II. If you know of a map that +works well with Paroxysm II, please let us know via email, at: +"Quake-User" + +10 Credits +~~~~~~~~~~~~~~~ + +All models, and all skins (except for the Plasma Gun) were created by +Frank Condello. The Plasma Gun was skinned by Forest "LordHavoc" Hale +with help from Jeff "Deek" Teunissen. + +The QuakeC code was written by Frank Condello. It is currently being +maintained by Jeff Teunissen. + + + Ok, that's all of it. Really, no foolin'. + + + GO PLAY! diff --git a/ParoxysmII/TODO b/ParoxysmII/TODO new file mode 100644 index 0000000..d98cc5d --- /dev/null +++ b/ParoxysmII/TODO @@ -0,0 +1,14 @@ +o = todo +X = done +? = maybe but not likely +M = more testing +I = in progress +W = waiting on other work + +I Single source tree, using qfcc with cpp support +o Add code to exec a script on map change (e.g. if maps/q1_q3dm17.bsp is + loaded, exec q1_q3dm17.bsp.cfg) +o commit to a CVS tree somewhere +o Better team support (classes?) +W Client-side game code (waiting for QF support) +W secondtrigger as a +command (CSGC) diff --git a/ParoxysmII/bin/makedist b/ParoxysmII/bin/makedist new file mode 100755 index 0000000..1f05138 --- /dev/null +++ b/ParoxysmII/bin/makedist @@ -0,0 +1,10 @@ +#! /bin/sh +mkdir paroxysm +mkdir paroxysm/source +cp -a COPYING README defaults qwprogs.dat.gz qwprogs.sym.gz pak0.pak paroxysm +cp -a source/*.{r,rh,qw,nq} source/progs.src paroxysm/source +tar -cho paroxysm | gzip -9 > paroxysm.tar.gz +tar -cho paroxysm | bzip2 -9 > paroxysm.tar.bz2 +rm -f paroxysm.zip +zip -9r paroxysm.zip paroxysm +rm -rf paroxysm diff --git a/ParoxysmII/bin/makepack b/ParoxysmII/bin/makepack new file mode 100755 index 0000000..b6bf646 --- /dev/null +++ b/ParoxysmII/bin/makepack @@ -0,0 +1,22 @@ +#! /bin/sh +gamename=paroxysm +LOCALRUNDIR=$HOME/.quake/paroxysm + +gzip -9rv gfx maps progs sound +pushd source +make +cp qwprogs.dat qwprogs.sym .. +popd +for i in gfx.wad quake.rc qwprogs.dat qwprogs.sym; do + cat $i | gzip -9v > $i.gz +done + +find gfx/ > file.list +find maps/ >> file.list +find progs/ >> file.list +find sound/ >> file.list +sort file.list -o file.list +echo gfx.wad.gz quake.rc.gz >> file.list + +pak -cv -fpak0.pak `cat file.list` +cp pak0.pak qwprogs.*.gz $LOCALRUNDIR diff --git a/ParoxysmII/deeks.cfg b/ParoxysmII/deeks.cfg new file mode 100644 index 0000000..049655d --- /dev/null +++ b/ParoxysmII/deeks.cfg @@ -0,0 +1,60 @@ +// An example server.cfg + +gamedir "paroxysm" // Game directory MUST be 'paroxysm' +sv_gamedir "paroxysm" +sv_timestamps 1 +sv_minqfversion "0.3.0" // Minimum QuakeForge version to play +sv_maxrate 2500 +sys_dead_sleep 0 // don't go completely dead when sleeping + +hostname "QuakeForge Paroxysm #1" + +timelimit 20 +samelevel 0 +fraglimit 0 + +// 1 deathmatch +// + 2 predator mode +// + 4 dark mode +// + 8 Last Man Standing +// + 16 Free For All +// + 32 Gib Mode (no packs) +// + 128 weapon autoswitch + +deathmatch 133 // See the documentation for adding deathmatch modifiers + +teamplay 0 + +// 0 is KEEP, 1 is DROP +serverinfo dr 0 // Drop cloaking device +serverinfo dq 0 // Drop quad + +// limits +maxclients 5 +maxspectators 2 + +// serverinfo and setmaster ip# should follow +serverinfo Admin "[QF]-Deek " +serverinfo URL "http://www.quakeforge.net/" +serverinfo Download "http://www.d2dc.net/~deek/paroxysm.tar.gz" + +// Masters to report to +//setmaster 192.246.40.37:27004 192.246.40.37:27006 +setmaster gozer.quakeforge.net + +// Remote Connection password +// what, you think I'd give this to you? + +// Map rotation +//localinfo q1_q2edge q1_q3dm13 +//localinfo q1_q3dm13 q1_q3dm17 +//localinfo q1_q3dm17 q1_q3tourney2 +//localinfo q1_q3tourney2 q1_q2edge + +localinfo q1_q3dm13 q1_q3dm17 +localinfo q1_q3dm17 q1_q3tourney2 +localinfo q1_q3tourney2 q1_q3dm13 + +// startup map +//map q1_q2edge +map q1_q3dm13 diff --git a/ParoxysmII/defaults/autoexec.cfg b/ParoxysmII/defaults/autoexec.cfg new file mode 100644 index 0000000..4a2959d --- /dev/null +++ b/ParoxysmII/defaults/autoexec.cfg @@ -0,0 +1,30 @@ +// This is the default binding for the secondtrigger +bind alt secondtrigger + +// Default binding for Target ID +bind i idtarget + +// Default binding for gl_flashblend toggle +//NOTE: this item is not needed for software rendered Quake +bind g gllight + +// These are the default bindings for Adding Bots +bind b addbot +bind n teambot + +//This is the default binding for the ChaseCam +bind c chasecam + +// These are the default bindings for the KasCam (Automatic Camera for Observing Only) +bind k autocam +bind ] camnext + +//These are optional bindings for the Chasecam (Manual 3rd person Camera) +//To make them active, remove the comment tags '//' + +//bind UPARROW camforward +//bind LEFTARROW camleft +//bind RIGHTARROW camright +//bind DOWNARROW cambackward +//bind HOME camup +//bind END camdown diff --git a/ParoxysmII/defaults/autoexec.dos b/ParoxysmII/defaults/autoexec.dos new file mode 100644 index 0000000..2e3fda7 --- /dev/null +++ b/ParoxysmII/defaults/autoexec.dos @@ -0,0 +1,30 @@ +// This is the default binding for the secondtrigger +bind COMMAND secondtrigger + +// Default binding for Target ID +bind i idtarget + +// Default binding for gl_flashblend toggle +//NOTE: this item is not needed for software rendered Quake +bind g gllight + +// These are the default bindings for Adding Bots +bind b addbot +bind n teambot + +//This is the default binding for the ChaseCam +bind c chasecam + +// These are the default bindings for the KasCam (Automatic Camera for Observing Only) +bind k autocam +bind ] camnext + +//These are optional bindings for the Chasecam (Manual 3rd person Camera) +//To make them active, remove the comment tags '//' + +//bind UPARROW camforward +//bind LEFTARROW camleft +//bind RIGHTARROW camright +//bind DOWNARROW cambackward +//bind HOME camup +//bind END camdown \ No newline at end of file diff --git a/ParoxysmII/defaults/server.cfg b/ParoxysmII/defaults/server.cfg new file mode 100644 index 0000000..bf9c329 --- /dev/null +++ b/ParoxysmII/defaults/server.cfg @@ -0,0 +1,40 @@ +// An example server.cfg + +gamedir "paroxysm" // Game directory MUST be 'paroxysm' +sv_gamedir "paroxysm" + +hostname "QuakeForge Paroxysm server" + +timelimit 0 +fraglimit 0 +deathmatch 129 // See the documentation for adding Deathmatch Modifiers +teamplay 1 + + +// 0 is OFF, 1 is ON +serverinfo dr 0 // drop ring +serverinfo dq 0 // drop quad + +// limits +maxclients 8 +maxspectators 4 + +// serverinfo and setmaster ip# should follow +serverinfo Admin "Nobody" +serverinfo Email "postmaster@localhost" +serverinfo URL "http://www.quakeforge.net/" + +// Masters to report to +setmaster 192.246.40.37:27004 192.246.40.37:27006 + +// maps to play +localinfo dm1 dm2 +localinfo dm2 dm3 +localinfo dm3 dm4 +localinfo dm4 dm5 +localinfo dm5 dm6 +localinfo dm6 e1m8 +localinfo e1m8 dm1 + +// startup map +map e1m8 diff --git a/ParoxysmII/gfx.wad b/ParoxysmII/gfx.wad new file mode 100644 index 0000000..e823249 Binary files /dev/null and b/ParoxysmII/gfx.wad differ diff --git a/ParoxysmII/gfx/complete.lmp b/ParoxysmII/gfx/complete.lmp new file mode 100644 index 0000000..fcded3b Binary files /dev/null and b/ParoxysmII/gfx/complete.lmp differ diff --git a/ParoxysmII/gfx/conback.lmp b/ParoxysmII/gfx/conback.lmp new file mode 100644 index 0000000..83fc437 Binary files /dev/null and b/ParoxysmII/gfx/conback.lmp differ diff --git a/ParoxysmII/gfx/dim_drct.lmp b/ParoxysmII/gfx/dim_drct.lmp new file mode 100644 index 0000000..d06d76e Binary files /dev/null and b/ParoxysmII/gfx/dim_drct.lmp differ diff --git a/ParoxysmII/gfx/dim_ipx.lmp b/ParoxysmII/gfx/dim_ipx.lmp new file mode 100644 index 0000000..0c83437 Binary files /dev/null and b/ParoxysmII/gfx/dim_ipx.lmp differ diff --git a/ParoxysmII/gfx/dim_modm.lmp b/ParoxysmII/gfx/dim_modm.lmp new file mode 100644 index 0000000..453c083 Binary files /dev/null and b/ParoxysmII/gfx/dim_modm.lmp differ diff --git a/ParoxysmII/gfx/dim_mult.lmp b/ParoxysmII/gfx/dim_mult.lmp new file mode 100644 index 0000000..c12de54 Binary files /dev/null and b/ParoxysmII/gfx/dim_mult.lmp differ diff --git a/ParoxysmII/gfx/dim_tcp.lmp b/ParoxysmII/gfx/dim_tcp.lmp new file mode 100644 index 0000000..e6781e3 Binary files /dev/null and b/ParoxysmII/gfx/dim_tcp.lmp differ diff --git a/ParoxysmII/gfx/finale.lmp b/ParoxysmII/gfx/finale.lmp new file mode 100644 index 0000000..bcdc83e Binary files /dev/null and b/ParoxysmII/gfx/finale.lmp differ diff --git a/ParoxysmII/gfx/inter.lmp b/ParoxysmII/gfx/inter.lmp new file mode 100644 index 0000000..f373514 Binary files /dev/null and b/ParoxysmII/gfx/inter.lmp differ diff --git a/ParoxysmII/gfx/loading.lmp b/ParoxysmII/gfx/loading.lmp new file mode 100644 index 0000000..c29d58a Binary files /dev/null and b/ParoxysmII/gfx/loading.lmp differ diff --git a/ParoxysmII/gfx/mainmenu.lmp b/ParoxysmII/gfx/mainmenu.lmp new file mode 100644 index 0000000..eb24f3e Binary files /dev/null and b/ParoxysmII/gfx/mainmenu.lmp differ diff --git a/ParoxysmII/gfx/menudot1.lmp b/ParoxysmII/gfx/menudot1.lmp new file mode 100644 index 0000000..eb17909 Binary files /dev/null and b/ParoxysmII/gfx/menudot1.lmp differ diff --git a/ParoxysmII/gfx/menudot2.lmp b/ParoxysmII/gfx/menudot2.lmp new file mode 100644 index 0000000..eb17909 Binary files /dev/null and b/ParoxysmII/gfx/menudot2.lmp differ diff --git a/ParoxysmII/gfx/menudot3.lmp b/ParoxysmII/gfx/menudot3.lmp new file mode 100644 index 0000000..32b8eda Binary files /dev/null and b/ParoxysmII/gfx/menudot3.lmp differ diff --git a/ParoxysmII/gfx/menudot4.lmp b/ParoxysmII/gfx/menudot4.lmp new file mode 100644 index 0000000..eb17909 Binary files /dev/null and b/ParoxysmII/gfx/menudot4.lmp differ diff --git a/ParoxysmII/gfx/menudot5.lmp b/ParoxysmII/gfx/menudot5.lmp new file mode 100644 index 0000000..eb17909 Binary files /dev/null and b/ParoxysmII/gfx/menudot5.lmp differ diff --git a/ParoxysmII/gfx/menudot6.lmp b/ParoxysmII/gfx/menudot6.lmp new file mode 100644 index 0000000..32b8eda Binary files /dev/null and b/ParoxysmII/gfx/menudot6.lmp differ diff --git a/ParoxysmII/gfx/mp_menu.lmp b/ParoxysmII/gfx/mp_menu.lmp new file mode 100644 index 0000000..b384684 Binary files /dev/null and b/ParoxysmII/gfx/mp_menu.lmp differ diff --git a/ParoxysmII/gfx/netmen1.lmp b/ParoxysmII/gfx/netmen1.lmp new file mode 100644 index 0000000..30de0b9 Binary files /dev/null and b/ParoxysmII/gfx/netmen1.lmp differ diff --git a/ParoxysmII/gfx/netmen2.lmp b/ParoxysmII/gfx/netmen2.lmp new file mode 100644 index 0000000..589a17a Binary files /dev/null and b/ParoxysmII/gfx/netmen2.lmp differ diff --git a/ParoxysmII/gfx/netmen3.lmp b/ParoxysmII/gfx/netmen3.lmp new file mode 100644 index 0000000..f44bc0c Binary files /dev/null and b/ParoxysmII/gfx/netmen3.lmp differ diff --git a/ParoxysmII/gfx/netmen4.lmp b/ParoxysmII/gfx/netmen4.lmp new file mode 100644 index 0000000..b289f93 Binary files /dev/null and b/ParoxysmII/gfx/netmen4.lmp differ diff --git a/ParoxysmII/gfx/netmen5.lmp b/ParoxysmII/gfx/netmen5.lmp new file mode 100644 index 0000000..a2e70b4 Binary files /dev/null and b/ParoxysmII/gfx/netmen5.lmp differ diff --git a/ParoxysmII/gfx/p_load.lmp b/ParoxysmII/gfx/p_load.lmp new file mode 100644 index 0000000..6df74cb Binary files /dev/null and b/ParoxysmII/gfx/p_load.lmp differ diff --git a/ParoxysmII/gfx/p_multi.lmp b/ParoxysmII/gfx/p_multi.lmp new file mode 100644 index 0000000..3b8d64c Binary files /dev/null and b/ParoxysmII/gfx/p_multi.lmp differ diff --git a/ParoxysmII/gfx/p_option.lmp b/ParoxysmII/gfx/p_option.lmp new file mode 100644 index 0000000..ee149b6 Binary files /dev/null and b/ParoxysmII/gfx/p_option.lmp differ diff --git a/ParoxysmII/gfx/p_save.lmp b/ParoxysmII/gfx/p_save.lmp new file mode 100644 index 0000000..5b58183 Binary files /dev/null and b/ParoxysmII/gfx/p_save.lmp differ diff --git a/ParoxysmII/gfx/pause.lmp b/ParoxysmII/gfx/pause.lmp new file mode 100644 index 0000000..ce149f3 Binary files /dev/null and b/ParoxysmII/gfx/pause.lmp differ diff --git a/ParoxysmII/gfx/qplaque.lmp b/ParoxysmII/gfx/qplaque.lmp new file mode 100644 index 0000000..762cd64 Binary files /dev/null and b/ParoxysmII/gfx/qplaque.lmp differ diff --git a/ParoxysmII/gfx/ranking.lmp b/ParoxysmII/gfx/ranking.lmp new file mode 100644 index 0000000..3b51b74 Binary files /dev/null and b/ParoxysmII/gfx/ranking.lmp differ diff --git a/ParoxysmII/gfx/sp_menu.lmp b/ParoxysmII/gfx/sp_menu.lmp new file mode 100644 index 0000000..0951c3f Binary files /dev/null and b/ParoxysmII/gfx/sp_menu.lmp differ diff --git a/ParoxysmII/gfx/ttl_cstm.lmp b/ParoxysmII/gfx/ttl_cstm.lmp new file mode 100644 index 0000000..b1822d8 Binary files /dev/null and b/ParoxysmII/gfx/ttl_cstm.lmp differ diff --git a/ParoxysmII/gfx/ttl_main.lmp b/ParoxysmII/gfx/ttl_main.lmp new file mode 100644 index 0000000..e4b2b24 Binary files /dev/null and b/ParoxysmII/gfx/ttl_main.lmp differ diff --git a/ParoxysmII/gfx/ttl_sgl.lmp b/ParoxysmII/gfx/ttl_sgl.lmp new file mode 100644 index 0000000..d5491b6 Binary files /dev/null and b/ParoxysmII/gfx/ttl_sgl.lmp differ diff --git a/ParoxysmII/gfx/vidmodes.lmp b/ParoxysmII/gfx/vidmodes.lmp new file mode 100644 index 0000000..cba0a6e Binary files /dev/null and b/ParoxysmII/gfx/vidmodes.lmp differ diff --git a/ParoxysmII/maps/bspmdls/b_batt0.bsp b/ParoxysmII/maps/bspmdls/b_batt0.bsp new file mode 100644 index 0000000..c148659 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_batt0.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_batt1.bsp b/ParoxysmII/maps/bspmdls/b_batt1.bsp new file mode 100644 index 0000000..c148659 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_batt1.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_bh10.bsp b/ParoxysmII/maps/bspmdls/b_bh10.bsp new file mode 100644 index 0000000..d239f31 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_bh10.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_bh100.bsp b/ParoxysmII/maps/bspmdls/b_bh100.bsp new file mode 100644 index 0000000..6168634 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_bh100.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_bh25.bsp b/ParoxysmII/maps/bspmdls/b_bh25.bsp new file mode 100644 index 0000000..71f8e5d Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_bh25.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_nail0.bsp b/ParoxysmII/maps/bspmdls/b_nail0.bsp new file mode 100644 index 0000000..647b721 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_nail0.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_nail1.bsp b/ParoxysmII/maps/bspmdls/b_nail1.bsp new file mode 100644 index 0000000..647b721 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_nail1.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_rock0.bsp b/ParoxysmII/maps/bspmdls/b_rock0.bsp new file mode 100644 index 0000000..f36a46e Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_rock0.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_rock1.bsp b/ParoxysmII/maps/bspmdls/b_rock1.bsp new file mode 100644 index 0000000..f36a46e Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_rock1.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_shell0.bsp b/ParoxysmII/maps/bspmdls/b_shell0.bsp new file mode 100644 index 0000000..69e62d0 Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_shell0.bsp differ diff --git a/ParoxysmII/maps/bspmdls/b_shell1.bsp b/ParoxysmII/maps/bspmdls/b_shell1.bsp new file mode 100644 index 0000000..e42542b Binary files /dev/null and b/ParoxysmII/maps/bspmdls/b_shell1.bsp differ diff --git a/ParoxysmII/progs/bsaw_p.mdl b/ParoxysmII/progs/bsaw_p.mdl new file mode 100644 index 0000000..bf66955 Binary files /dev/null and b/ParoxysmII/progs/bsaw_p.mdl differ diff --git a/ParoxysmII/progs/cloak.mdl b/ParoxysmII/progs/cloak.mdl new file mode 100644 index 0000000..ff20c38 Binary files /dev/null and b/ParoxysmII/progs/cloak.mdl differ diff --git a/ParoxysmII/progs/combo_p.mdl b/ParoxysmII/progs/combo_p.mdl new file mode 100644 index 0000000..723416d Binary files /dev/null and b/ParoxysmII/progs/combo_p.mdl differ diff --git a/ParoxysmII/progs/d_bsaw.mdl b/ParoxysmII/progs/d_bsaw.mdl new file mode 100644 index 0000000..bfea677 Binary files /dev/null and b/ParoxysmII/progs/d_bsaw.mdl differ diff --git a/ParoxysmII/progs/d_combo.mdl b/ParoxysmII/progs/d_combo.mdl new file mode 100644 index 0000000..968260c Binary files /dev/null and b/ParoxysmII/progs/d_combo.mdl differ diff --git a/ParoxysmII/progs/d_gren.mdl b/ParoxysmII/progs/d_gren.mdl new file mode 100644 index 0000000..c03f84c Binary files /dev/null and b/ParoxysmII/progs/d_gren.mdl differ diff --git a/ParoxysmII/progs/d_nail.mdl b/ParoxysmII/progs/d_nail.mdl new file mode 100644 index 0000000..d87d67f Binary files /dev/null and b/ParoxysmII/progs/d_nail.mdl differ diff --git a/ParoxysmII/progs/d_plasma.mdl b/ParoxysmII/progs/d_plasma.mdl new file mode 100644 index 0000000..86f2a65 Binary files /dev/null and b/ParoxysmII/progs/d_plasma.mdl differ diff --git a/ParoxysmII/progs/d_rhino.mdl b/ParoxysmII/progs/d_rhino.mdl new file mode 100644 index 0000000..f6bcf2e Binary files /dev/null and b/ParoxysmII/progs/d_rhino.mdl differ diff --git a/ParoxysmII/progs/d_tshot.mdl b/ParoxysmII/progs/d_tshot.mdl new file mode 100644 index 0000000..7ff6aa2 Binary files /dev/null and b/ParoxysmII/progs/d_tshot.mdl differ diff --git a/ParoxysmII/progs/death_p.mdl b/ParoxysmII/progs/death_p.mdl new file mode 100644 index 0000000..5bd6e64 Binary files /dev/null and b/ParoxysmII/progs/death_p.mdl differ diff --git a/ParoxysmII/progs/drip.spr b/ParoxysmII/progs/drip.spr new file mode 100644 index 0000000..443654a Binary files /dev/null and b/ParoxysmII/progs/drip.spr differ diff --git a/ParoxysmII/progs/g_combo.mdl b/ParoxysmII/progs/g_combo.mdl new file mode 100644 index 0000000..298e11a Binary files /dev/null and b/ParoxysmII/progs/g_combo.mdl differ diff --git a/ParoxysmII/progs/g_gren.mdl b/ParoxysmII/progs/g_gren.mdl new file mode 100644 index 0000000..58192a3 Binary files /dev/null and b/ParoxysmII/progs/g_gren.mdl differ diff --git a/ParoxysmII/progs/g_nailg.mdl b/ParoxysmII/progs/g_nailg.mdl new file mode 100644 index 0000000..a11a3c3 Binary files /dev/null and b/ParoxysmII/progs/g_nailg.mdl differ diff --git a/ParoxysmII/progs/g_plasma.mdl b/ParoxysmII/progs/g_plasma.mdl new file mode 100644 index 0000000..716eb8b Binary files /dev/null and b/ParoxysmII/progs/g_plasma.mdl differ diff --git a/ParoxysmII/progs/g_rhino.mdl b/ParoxysmII/progs/g_rhino.mdl new file mode 100644 index 0000000..5e09a92 Binary files /dev/null and b/ParoxysmII/progs/g_rhino.mdl differ diff --git a/ParoxysmII/progs/g_tshot.mdl b/ParoxysmII/progs/g_tshot.mdl new file mode 100644 index 0000000..eed8194 Binary files /dev/null and b/ParoxysmII/progs/g_tshot.mdl differ diff --git a/ParoxysmII/progs/glassrub.mdl b/ParoxysmII/progs/glassrub.mdl new file mode 100644 index 0000000..036e0c6 Binary files /dev/null and b/ParoxysmII/progs/glassrub.mdl differ diff --git a/ParoxysmII/progs/gren_p.mdl b/ParoxysmII/progs/gren_p.mdl new file mode 100644 index 0000000..5a7e7a2 Binary files /dev/null and b/ParoxysmII/progs/gren_p.mdl differ diff --git a/ParoxysmII/progs/grenade.mdl b/ParoxysmII/progs/grenade.mdl new file mode 100644 index 0000000..0e4e074 Binary files /dev/null and b/ParoxysmII/progs/grenade.mdl differ diff --git a/ParoxysmII/progs/h_player.mdl b/ParoxysmII/progs/h_player.mdl new file mode 100644 index 0000000..0f6c92c Binary files /dev/null and b/ParoxysmII/progs/h_player.mdl differ diff --git a/ParoxysmII/progs/laser.mdl b/ParoxysmII/progs/laser.mdl new file mode 100644 index 0000000..16aa641 Binary files /dev/null and b/ParoxysmII/progs/laser.mdl differ diff --git a/ParoxysmII/progs/mwrub1.mdl b/ParoxysmII/progs/mwrub1.mdl new file mode 100644 index 0000000..5ae6843 Binary files /dev/null and b/ParoxysmII/progs/mwrub1.mdl differ diff --git a/ParoxysmII/progs/mwrub2.mdl b/ParoxysmII/progs/mwrub2.mdl new file mode 100644 index 0000000..7f3e1c0 Binary files /dev/null and b/ParoxysmII/progs/mwrub2.mdl differ diff --git a/ParoxysmII/progs/mwrub3.mdl b/ParoxysmII/progs/mwrub3.mdl new file mode 100644 index 0000000..e9fa990 Binary files /dev/null and b/ParoxysmII/progs/mwrub3.mdl differ diff --git a/ParoxysmII/progs/nail_p.mdl b/ParoxysmII/progs/nail_p.mdl new file mode 100644 index 0000000..22548d5 Binary files /dev/null and b/ParoxysmII/progs/nail_p.mdl differ diff --git a/ParoxysmII/progs/null.mdl b/ParoxysmII/progs/null.mdl new file mode 100644 index 0000000..b9396a4 Binary files /dev/null and b/ParoxysmII/progs/null.mdl differ diff --git a/ParoxysmII/progs/null.spr b/ParoxysmII/progs/null.spr new file mode 100644 index 0000000..3a55aeb Binary files /dev/null and b/ParoxysmII/progs/null.spr differ diff --git a/ParoxysmII/progs/plasma.mdl b/ParoxysmII/progs/plasma.mdl new file mode 100644 index 0000000..17bad7a Binary files /dev/null and b/ParoxysmII/progs/plasma.mdl differ diff --git a/ParoxysmII/progs/plasma_p.mdl b/ParoxysmII/progs/plasma_p.mdl new file mode 100644 index 0000000..6dfe8c2 Binary files /dev/null and b/ParoxysmII/progs/plasma_p.mdl differ diff --git a/ParoxysmII/progs/poxmegs.mdl b/ParoxysmII/progs/poxmegs.mdl new file mode 100644 index 0000000..8a6871a Binary files /dev/null and b/ParoxysmII/progs/poxmegs.mdl differ diff --git a/ParoxysmII/progs/poxquad.mdl b/ParoxysmII/progs/poxquad.mdl new file mode 100644 index 0000000..0ca854b Binary files /dev/null and b/ParoxysmII/progs/poxquad.mdl differ diff --git a/ParoxysmII/progs/poxquad2.mdl b/ParoxysmII/progs/poxquad2.mdl new file mode 100644 index 0000000..b351d33 Binary files /dev/null and b/ParoxysmII/progs/poxquad2.mdl differ diff --git a/ParoxysmII/progs/regen.mdl b/ParoxysmII/progs/regen.mdl new file mode 100644 index 0000000..8884330 Binary files /dev/null and b/ParoxysmII/progs/regen.mdl differ diff --git a/ParoxysmII/progs/rhino_p.mdl b/ParoxysmII/progs/rhino_p.mdl new file mode 100644 index 0000000..c309382 Binary files /dev/null and b/ParoxysmII/progs/rhino_p.mdl differ diff --git a/ParoxysmII/progs/rubble1.mdl b/ParoxysmII/progs/rubble1.mdl new file mode 100644 index 0000000..ed5f2fc Binary files /dev/null and b/ParoxysmII/progs/rubble1.mdl differ diff --git a/ParoxysmII/progs/rubble2.mdl b/ParoxysmII/progs/rubble2.mdl new file mode 100644 index 0000000..ee69eb7 Binary files /dev/null and b/ParoxysmII/progs/rubble2.mdl differ diff --git a/ParoxysmII/progs/rubble3.mdl b/ParoxysmII/progs/rubble3.mdl new file mode 100644 index 0000000..22d7aeb Binary files /dev/null and b/ParoxysmII/progs/rubble3.mdl differ diff --git a/ParoxysmII/progs/spark.spr b/ParoxysmII/progs/spark.spr new file mode 100644 index 0000000..bcf0128 Binary files /dev/null and b/ParoxysmII/progs/spark.spr differ diff --git a/ParoxysmII/progs/spike.mdl b/ParoxysmII/progs/spike.mdl new file mode 100644 index 0000000..f13bc40 Binary files /dev/null and b/ParoxysmII/progs/spike.mdl differ diff --git a/ParoxysmII/progs/splash.spr b/ParoxysmII/progs/splash.spr new file mode 100644 index 0000000..d17cc98 Binary files /dev/null and b/ParoxysmII/progs/splash.spr differ diff --git a/ParoxysmII/progs/stream.mdl b/ParoxysmII/progs/stream.mdl new file mode 100644 index 0000000..ca81c9c Binary files /dev/null and b/ParoxysmII/progs/stream.mdl differ diff --git a/ParoxysmII/progs/tshot_p.mdl b/ParoxysmII/progs/tshot_p.mdl new file mode 100644 index 0000000..6c222d7 Binary files /dev/null and b/ParoxysmII/progs/tshot_p.mdl differ diff --git a/ParoxysmII/progs/v_bsaw.mdl b/ParoxysmII/progs/v_bsaw.mdl new file mode 100644 index 0000000..eb4d3c2 Binary files /dev/null and b/ParoxysmII/progs/v_bsaw.mdl differ diff --git a/ParoxysmII/progs/v_combo.mdl b/ParoxysmII/progs/v_combo.mdl new file mode 100644 index 0000000..d709800 Binary files /dev/null and b/ParoxysmII/progs/v_combo.mdl differ diff --git a/ParoxysmII/progs/v_gren.mdl b/ParoxysmII/progs/v_gren.mdl new file mode 100644 index 0000000..af937e4 Binary files /dev/null and b/ParoxysmII/progs/v_gren.mdl differ diff --git a/ParoxysmII/progs/v_nailg.mdl b/ParoxysmII/progs/v_nailg.mdl new file mode 100644 index 0000000..d896694 Binary files /dev/null and b/ParoxysmII/progs/v_nailg.mdl differ diff --git a/ParoxysmII/progs/v_nailgl.mdl b/ParoxysmII/progs/v_nailgl.mdl new file mode 100644 index 0000000..a481cb9 Binary files /dev/null and b/ParoxysmII/progs/v_nailgl.mdl differ diff --git a/ParoxysmII/progs/v_plasma.mdl b/ParoxysmII/progs/v_plasma.mdl new file mode 100644 index 0000000..e4fdc78 Binary files /dev/null and b/ParoxysmII/progs/v_plasma.mdl differ diff --git a/ParoxysmII/progs/v_rhino.mdl b/ParoxysmII/progs/v_rhino.mdl new file mode 100644 index 0000000..e7ce969 Binary files /dev/null and b/ParoxysmII/progs/v_rhino.mdl differ diff --git a/ParoxysmII/progs/v_tshot.mdl b/ParoxysmII/progs/v_tshot.mdl new file mode 100644 index 0000000..7c32889 Binary files /dev/null and b/ParoxysmII/progs/v_tshot.mdl differ diff --git a/ParoxysmII/quake.rc b/ParoxysmII/quake.rc new file mode 100644 index 0000000..500ba79 --- /dev/null +++ b/ParoxysmII/quake.rc @@ -0,0 +1,57 @@ +// load the base configuration +exec default.cfg + +// load the last saved configuration +exec config.cfg + +// run a user script file if present +exec autoexec.cfg + +// stuff command line statements +stuffcmds + +v_idlescale 0.54 +v_ipitch_cycle 3.5 +v_ipitch_level 0.4 +v_iroll_level 0.1 +v_iyaw_level 0 + +v_kickpitch 0.8 +v_kickroll 0.8 + +sv_friction 3.12 + +scr_conspeed 900 + +cl_bobcycle 0.8 +cl_bobup 0 +cl_bob 0.015 +sv_aim 2 + +alias addbot "impulse 251" +alias teambot "impulse 252" + +alias kaskam "impulse 250" + +alias secondtrigger "impulse 15" + +alias autocam "kaskam;wait;+lookup;wait;+lookdown" + +alias rules "impulse 253" + +alias tele_zoom "fov 35;wait;fov 40;wait;fov 50;wait;fov 60;wait;fov 70;wait;fov 90;" + +alias chasecam "impulse 254" +alias camup "impulse 150" +alias camright "impulse 151" +alias camforward "impulse 152" +alias camdown "impulse 153" +alias camleft "impulse 154" +alias cambackward "impulse 155" + +alias idtarget "impulse 16" +alias gllight "impulse 17" + +menu_main + +//startdemos diff --git a/ParoxysmII/sound/ambience/brik_hit.wav b/ParoxysmII/sound/ambience/brik_hit.wav new file mode 100644 index 0000000..0704093 Binary files /dev/null and b/ParoxysmII/sound/ambience/brik_hit.wav differ diff --git a/ParoxysmII/sound/ambience/brikhit2.wav b/ParoxysmII/sound/ambience/brikhit2.wav new file mode 100644 index 0000000..337fd3f Binary files /dev/null and b/ParoxysmII/sound/ambience/brikhit2.wav differ diff --git a/ParoxysmII/sound/ambience/distex.wav b/ParoxysmII/sound/ambience/distex.wav new file mode 100644 index 0000000..c410f41 Binary files /dev/null and b/ParoxysmII/sound/ambience/distex.wav differ diff --git a/ParoxysmII/sound/ambience/glassbrk.wav b/ParoxysmII/sound/ambience/glassbrk.wav new file mode 100644 index 0000000..9f9fbd8 Binary files /dev/null and b/ParoxysmII/sound/ambience/glassbrk.wav differ diff --git a/ParoxysmII/sound/ambience/hdrone.wav b/ParoxysmII/sound/ambience/hdrone.wav new file mode 100644 index 0000000..42999b1 Binary files /dev/null and b/ParoxysmII/sound/ambience/hdrone.wav differ diff --git a/ParoxysmII/sound/ambience/hdrone1.wav b/ParoxysmII/sound/ambience/hdrone1.wav new file mode 100644 index 0000000..08c15b5 Binary files /dev/null and b/ParoxysmII/sound/ambience/hdrone1.wav differ diff --git a/ParoxysmII/sound/ambience/met1.wav b/ParoxysmII/sound/ambience/met1.wav new file mode 100644 index 0000000..972bca3 Binary files /dev/null and b/ParoxysmII/sound/ambience/met1.wav differ diff --git a/ParoxysmII/sound/ambience/met2.wav b/ParoxysmII/sound/ambience/met2.wav new file mode 100644 index 0000000..512ec3b Binary files /dev/null and b/ParoxysmII/sound/ambience/met2.wav differ diff --git a/ParoxysmII/sound/ambience/metbrk.wav b/ParoxysmII/sound/ambience/metbrk.wav new file mode 100644 index 0000000..d4cdb89 Binary files /dev/null and b/ParoxysmII/sound/ambience/metbrk.wav differ diff --git a/ParoxysmII/sound/ambience/methit1.wav b/ParoxysmII/sound/ambience/methit1.wav new file mode 100644 index 0000000..5b16d99 Binary files /dev/null and b/ParoxysmII/sound/ambience/methit1.wav differ diff --git a/ParoxysmII/sound/ambience/methit2.wav b/ParoxysmII/sound/ambience/methit2.wav new file mode 100644 index 0000000..e5e76cd Binary files /dev/null and b/ParoxysmII/sound/ambience/methit2.wav differ diff --git a/ParoxysmII/sound/ambience/regen1.wav b/ParoxysmII/sound/ambience/regen1.wav new file mode 100644 index 0000000..4552655 Binary files /dev/null and b/ParoxysmII/sound/ambience/regen1.wav differ diff --git a/ParoxysmII/sound/ambience/sqeak2.wav b/ParoxysmII/sound/ambience/sqeak2.wav new file mode 100644 index 0000000..815b956 Binary files /dev/null and b/ParoxysmII/sound/ambience/sqeak2.wav differ diff --git a/ParoxysmII/sound/ambience/squeak1.wav b/ParoxysmII/sound/ambience/squeak1.wav new file mode 100644 index 0000000..8d79b05 Binary files /dev/null and b/ParoxysmII/sound/ambience/squeak1.wav differ diff --git a/ParoxysmII/sound/ambience/wall01.wav b/ParoxysmII/sound/ambience/wall01.wav new file mode 100644 index 0000000..b0f3901 Binary files /dev/null and b/ParoxysmII/sound/ambience/wall01.wav differ diff --git a/ParoxysmII/sound/ambience/woodbrk.wav b/ParoxysmII/sound/ambience/woodbrk.wav new file mode 100644 index 0000000..ab42c88 Binary files /dev/null and b/ParoxysmII/sound/ambience/woodbrk.wav differ diff --git a/ParoxysmII/sound/ambience/woodhit1.wav b/ParoxysmII/sound/ambience/woodhit1.wav new file mode 100644 index 0000000..0f2271f Binary files /dev/null and b/ParoxysmII/sound/ambience/woodhit1.wav differ diff --git a/ParoxysmII/sound/ambience/woodhit2.wav b/ParoxysmII/sound/ambience/woodhit2.wav new file mode 100644 index 0000000..56d0dbb Binary files /dev/null and b/ParoxysmII/sound/ambience/woodhit2.wav differ diff --git a/ParoxysmII/sound/ambience/wpump1.wav b/ParoxysmII/sound/ambience/wpump1.wav new file mode 100644 index 0000000..2f8ddfd Binary files /dev/null and b/ParoxysmII/sound/ambience/wpump1.wav differ diff --git a/ParoxysmII/sound/demon/dland2.wav b/ParoxysmII/sound/demon/dland2.wav new file mode 100644 index 0000000..63a6fb1 Binary files /dev/null and b/ParoxysmII/sound/demon/dland2.wav differ diff --git a/ParoxysmII/sound/doors/ddoor1.wav b/ParoxysmII/sound/doors/ddoor1.wav new file mode 100644 index 0000000..3ac6a26 Binary files /dev/null and b/ParoxysmII/sound/doors/ddoor1.wav differ diff --git a/ParoxysmII/sound/doors/ddoor2.wav b/ParoxysmII/sound/doors/ddoor2.wav new file mode 100644 index 0000000..4aad884 Binary files /dev/null and b/ParoxysmII/sound/doors/ddoor2.wav differ diff --git a/ParoxysmII/sound/doors/hydro1.wav b/ParoxysmII/sound/doors/hydro1.wav new file mode 100644 index 0000000..a98b1a7 Binary files /dev/null and b/ParoxysmII/sound/doors/hydro1.wav differ diff --git a/ParoxysmII/sound/doors/hydro2.wav b/ParoxysmII/sound/doors/hydro2.wav new file mode 100644 index 0000000..0ad45ec Binary files /dev/null and b/ParoxysmII/sound/doors/hydro2.wav differ diff --git a/ParoxysmII/sound/doors/stndr1.wav b/ParoxysmII/sound/doors/stndr1.wav new file mode 100644 index 0000000..3024c32 Binary files /dev/null and b/ParoxysmII/sound/doors/stndr1.wav differ diff --git a/ParoxysmII/sound/doors/stndr2.wav b/ParoxysmII/sound/doors/stndr2.wav new file mode 100644 index 0000000..8b3729d Binary files /dev/null and b/ParoxysmII/sound/doors/stndr2.wav differ diff --git a/ParoxysmII/sound/items/health1.wav b/ParoxysmII/sound/items/health1.wav new file mode 100644 index 0000000..c237d6c Binary files /dev/null and b/ParoxysmII/sound/items/health1.wav differ diff --git a/ParoxysmII/sound/items/sheild1.wav b/ParoxysmII/sound/items/sheild1.wav new file mode 100644 index 0000000..9e7e397 Binary files /dev/null and b/ParoxysmII/sound/items/sheild1.wav differ diff --git a/ParoxysmII/sound/items/sheild2.wav b/ParoxysmII/sound/items/sheild2.wav new file mode 100644 index 0000000..41ff0b9 Binary files /dev/null and b/ParoxysmII/sound/items/sheild2.wav differ diff --git a/ParoxysmII/sound/items/sheild3.wav b/ParoxysmII/sound/items/sheild3.wav new file mode 100644 index 0000000..597a889 Binary files /dev/null and b/ParoxysmII/sound/items/sheild3.wav differ diff --git a/ParoxysmII/sound/items/shield1.wav b/ParoxysmII/sound/items/shield1.wav new file mode 100644 index 0000000..9e7e397 Binary files /dev/null and b/ParoxysmII/sound/items/shield1.wav differ diff --git a/ParoxysmII/sound/items/shield2.wav b/ParoxysmII/sound/items/shield2.wav new file mode 100644 index 0000000..41ff0b9 Binary files /dev/null and b/ParoxysmII/sound/items/shield2.wav differ diff --git a/ParoxysmII/sound/items/shield3.wav b/ParoxysmII/sound/items/shield3.wav new file mode 100644 index 0000000..597a889 Binary files /dev/null and b/ParoxysmII/sound/items/shield3.wav differ diff --git a/ParoxysmII/sound/misc/drip1.wav b/ParoxysmII/sound/misc/drip1.wav new file mode 100644 index 0000000..f317b84 Binary files /dev/null and b/ParoxysmII/sound/misc/drip1.wav differ diff --git a/ParoxysmII/sound/misc/drip2.wav b/ParoxysmII/sound/misc/drip2.wav new file mode 100644 index 0000000..d1aecb6 Binary files /dev/null and b/ParoxysmII/sound/misc/drip2.wav differ diff --git a/ParoxysmII/sound/misc/drip3.wav b/ParoxysmII/sound/misc/drip3.wav new file mode 100644 index 0000000..71ba246 Binary files /dev/null and b/ParoxysmII/sound/misc/drip3.wav differ diff --git a/ParoxysmII/sound/misc/foot1.wav b/ParoxysmII/sound/misc/foot1.wav new file mode 100644 index 0000000..29b1e61 Binary files /dev/null and b/ParoxysmII/sound/misc/foot1.wav differ diff --git a/ParoxysmII/sound/misc/foot2.wav b/ParoxysmII/sound/misc/foot2.wav new file mode 100644 index 0000000..73728d5 Binary files /dev/null and b/ParoxysmII/sound/misc/foot2.wav differ diff --git a/ParoxysmII/sound/misc/foot3.wav b/ParoxysmII/sound/misc/foot3.wav new file mode 100644 index 0000000..1499c73 Binary files /dev/null and b/ParoxysmII/sound/misc/foot3.wav differ diff --git a/ParoxysmII/sound/misc/foot4.wav b/ParoxysmII/sound/misc/foot4.wav new file mode 100644 index 0000000..6419a05 Binary files /dev/null and b/ParoxysmII/sound/misc/foot4.wav differ diff --git a/ParoxysmII/sound/misc/h2ohit1.wav b/ParoxysmII/sound/misc/h2ohit1.wav new file mode 100644 index 0000000..63a6fb1 Binary files /dev/null and b/ParoxysmII/sound/misc/h2ohit1.wav differ diff --git a/ParoxysmII/sound/misc/inh2ob.wav b/ParoxysmII/sound/misc/inh2ob.wav new file mode 100644 index 0000000..becd894 Binary files /dev/null and b/ParoxysmII/sound/misc/inh2ob.wav differ diff --git a/ParoxysmII/sound/misc/menu1.wav b/ParoxysmII/sound/misc/menu1.wav new file mode 100644 index 0000000..cae35b0 Binary files /dev/null and b/ParoxysmII/sound/misc/menu1.wav differ diff --git a/ParoxysmII/sound/misc/menu2.wav b/ParoxysmII/sound/misc/menu2.wav new file mode 100644 index 0000000..a5a657f Binary files /dev/null and b/ParoxysmII/sound/misc/menu2.wav differ diff --git a/ParoxysmII/sound/misc/menu3.wav b/ParoxysmII/sound/misc/menu3.wav new file mode 100644 index 0000000..58196f8 Binary files /dev/null and b/ParoxysmII/sound/misc/menu3.wav differ diff --git a/ParoxysmII/sound/misc/outwater.wav b/ParoxysmII/sound/misc/outwater.wav new file mode 100644 index 0000000..09e8c11 Binary files /dev/null and b/ParoxysmII/sound/misc/outwater.wav differ diff --git a/ParoxysmII/sound/misc/owater2.wav b/ParoxysmII/sound/misc/owater2.wav new file mode 100644 index 0000000..b01c9d5 Binary files /dev/null and b/ParoxysmII/sound/misc/owater2.wav differ diff --git a/ParoxysmII/sound/misc/quake.wav b/ParoxysmII/sound/misc/quake.wav new file mode 100644 index 0000000..953df92 Binary files /dev/null and b/ParoxysmII/sound/misc/quake.wav differ diff --git a/ParoxysmII/sound/misc/quakeend.wav b/ParoxysmII/sound/misc/quakeend.wav new file mode 100644 index 0000000..6fdd531 Binary files /dev/null and b/ParoxysmII/sound/misc/quakeend.wav differ diff --git a/ParoxysmII/sound/misc/r_tele1.wav b/ParoxysmII/sound/misc/r_tele1.wav new file mode 100644 index 0000000..86578a5 Binary files /dev/null and b/ParoxysmII/sound/misc/r_tele1.wav differ diff --git a/ParoxysmII/sound/misc/r_tele2.wav b/ParoxysmII/sound/misc/r_tele2.wav new file mode 100644 index 0000000..6f6cfd4 Binary files /dev/null and b/ParoxysmII/sound/misc/r_tele2.wav differ diff --git a/ParoxysmII/sound/misc/rain.wav b/ParoxysmII/sound/misc/rain.wav new file mode 100644 index 0000000..2f446f5 Binary files /dev/null and b/ParoxysmII/sound/misc/rain.wav differ diff --git a/ParoxysmII/sound/misc/spark1.wav b/ParoxysmII/sound/misc/spark1.wav new file mode 100644 index 0000000..870f3ad Binary files /dev/null and b/ParoxysmII/sound/misc/spark1.wav differ diff --git a/ParoxysmII/sound/misc/spark2.wav b/ParoxysmII/sound/misc/spark2.wav new file mode 100644 index 0000000..4098367 Binary files /dev/null and b/ParoxysmII/sound/misc/spark2.wav differ diff --git a/ParoxysmII/sound/misc/spark3.wav b/ParoxysmII/sound/misc/spark3.wav new file mode 100644 index 0000000..f575529 Binary files /dev/null and b/ParoxysmII/sound/misc/spark3.wav differ diff --git a/ParoxysmII/sound/misc/spark4.wav b/ParoxysmII/sound/misc/spark4.wav new file mode 100644 index 0000000..0ce4d0c Binary files /dev/null and b/ParoxysmII/sound/misc/spark4.wav differ diff --git a/ParoxysmII/sound/misc/talk.wav b/ParoxysmII/sound/misc/talk.wav new file mode 100644 index 0000000..082abc0 Binary files /dev/null and b/ParoxysmII/sound/misc/talk.wav differ diff --git a/ParoxysmII/sound/misc/uwater.wav b/ParoxysmII/sound/misc/uwater.wav new file mode 100644 index 0000000..9eb5f5c Binary files /dev/null and b/ParoxysmII/sound/misc/uwater.wav differ diff --git a/ParoxysmII/sound/nar/n_elim.wav b/ParoxysmII/sound/nar/n_elim.wav new file mode 100644 index 0000000..0981edc Binary files /dev/null and b/ParoxysmII/sound/nar/n_elim.wav differ diff --git a/ParoxysmII/sound/weapons/armed.wav b/ParoxysmII/sound/weapons/armed.wav new file mode 100644 index 0000000..fd0141a Binary files /dev/null and b/ParoxysmII/sound/weapons/armed.wav differ diff --git a/ParoxysmII/sound/weapons/bounce.wav b/ParoxysmII/sound/weapons/bounce.wav new file mode 100644 index 0000000..18bac20 Binary files /dev/null and b/ParoxysmII/sound/weapons/bounce.wav differ diff --git a/ParoxysmII/sound/weapons/bounce2.wav b/ParoxysmII/sound/weapons/bounce2.wav new file mode 100644 index 0000000..e677bd1 Binary files /dev/null and b/ParoxysmII/sound/weapons/bounce2.wav differ diff --git a/ParoxysmII/sound/weapons/error.wav b/ParoxysmII/sound/weapons/error.wav new file mode 100644 index 0000000..8eed2f0 Binary files /dev/null and b/ParoxysmII/sound/weapons/error.wav differ diff --git a/ParoxysmII/sound/weapons/gren.wav b/ParoxysmII/sound/weapons/gren.wav new file mode 100644 index 0000000..1b6e3be Binary files /dev/null and b/ParoxysmII/sound/weapons/gren.wav differ diff --git a/ParoxysmII/sound/weapons/gren2.wav b/ParoxysmII/sound/weapons/gren2.wav new file mode 100644 index 0000000..b63c84c Binary files /dev/null and b/ParoxysmII/sound/weapons/gren2.wav differ diff --git a/ParoxysmII/sound/weapons/grenade.wav b/ParoxysmII/sound/weapons/grenade.wav new file mode 100644 index 0000000..b1d2c2a Binary files /dev/null and b/ParoxysmII/sound/weapons/grenade.wav differ diff --git a/ParoxysmII/sound/weapons/hog.wav b/ParoxysmII/sound/weapons/hog.wav new file mode 100644 index 0000000..89aab51 Binary files /dev/null and b/ParoxysmII/sound/weapons/hog.wav differ diff --git a/ParoxysmII/sound/weapons/lock4.wav b/ParoxysmII/sound/weapons/lock4.wav new file mode 100644 index 0000000..6c01669 Binary files /dev/null and b/ParoxysmII/sound/weapons/lock4.wav differ diff --git a/ParoxysmII/sound/weapons/mfire1.wav b/ParoxysmII/sound/weapons/mfire1.wav new file mode 100644 index 0000000..b9180bd Binary files /dev/null and b/ParoxysmII/sound/weapons/mfire1.wav differ diff --git a/ParoxysmII/sound/weapons/mfire2.wav b/ParoxysmII/sound/weapons/mfire2.wav new file mode 100644 index 0000000..fc78f2c Binary files /dev/null and b/ParoxysmII/sound/weapons/mfire2.wav differ diff --git a/ParoxysmII/sound/weapons/minedet.wav b/ParoxysmII/sound/weapons/minedet.wav new file mode 100644 index 0000000..87d5211 Binary files /dev/null and b/ParoxysmII/sound/weapons/minedet.wav differ diff --git a/ParoxysmII/sound/weapons/mplasex.wav b/ParoxysmII/sound/weapons/mplasex.wav new file mode 100644 index 0000000..b94195b Binary files /dev/null and b/ParoxysmII/sound/weapons/mplasex.wav differ diff --git a/ParoxysmII/sound/weapons/mplasma.wav b/ParoxysmII/sound/weapons/mplasma.wav new file mode 100644 index 0000000..e278a22 Binary files /dev/null and b/ParoxysmII/sound/weapons/mplasma.wav differ diff --git a/ParoxysmII/sound/weapons/nailgun.wav b/ParoxysmII/sound/weapons/nailgun.wav new file mode 100644 index 0000000..0afd877 Binary files /dev/null and b/ParoxysmII/sound/weapons/nailgun.wav differ diff --git a/ParoxysmII/sound/weapons/pkup.wav b/ParoxysmII/sound/weapons/pkup.wav new file mode 100644 index 0000000..6c01669 Binary files /dev/null and b/ParoxysmII/sound/weapons/pkup.wav differ diff --git a/ParoxysmII/sound/weapons/plasma.wav b/ParoxysmII/sound/weapons/plasma.wav new file mode 100644 index 0000000..b33523c Binary files /dev/null and b/ParoxysmII/sound/weapons/plasma.wav differ diff --git a/ParoxysmII/sound/weapons/r_exp3.wav b/ParoxysmII/sound/weapons/r_exp3.wav new file mode 100644 index 0000000..7fe9691 Binary files /dev/null and b/ParoxysmII/sound/weapons/r_exp3.wav differ diff --git a/ParoxysmII/sound/weapons/rhino.wav b/ParoxysmII/sound/weapons/rhino.wav new file mode 100644 index 0000000..9f57efb Binary files /dev/null and b/ParoxysmII/sound/weapons/rhino.wav differ diff --git a/ParoxysmII/sound/weapons/rhinore.wav b/ParoxysmII/sound/weapons/rhinore.wav new file mode 100644 index 0000000..01cea7d Binary files /dev/null and b/ParoxysmII/sound/weapons/rhinore.wav differ diff --git a/ParoxysmII/sound/weapons/sawatck.wav b/ParoxysmII/sound/weapons/sawatck.wav new file mode 100644 index 0000000..beba5a0 Binary files /dev/null and b/ParoxysmII/sound/weapons/sawatck.wav differ diff --git a/ParoxysmII/sound/weapons/sawguts.wav b/ParoxysmII/sound/weapons/sawguts.wav new file mode 100644 index 0000000..461b3d9 Binary files /dev/null and b/ParoxysmII/sound/weapons/sawguts.wav differ diff --git a/ParoxysmII/sound/weapons/sawhit.wav b/ParoxysmII/sound/weapons/sawhit.wav new file mode 100644 index 0000000..4c70314 Binary files /dev/null and b/ParoxysmII/sound/weapons/sawhit.wav differ diff --git a/ParoxysmII/sound/weapons/sawidle.wav b/ParoxysmII/sound/weapons/sawidle.wav new file mode 100644 index 0000000..3a23e08 Binary files /dev/null and b/ParoxysmII/sound/weapons/sawidle.wav differ diff --git a/ParoxysmII/sound/weapons/sawoff.wav b/ParoxysmII/sound/weapons/sawoff.wav new file mode 100644 index 0000000..0179bb5 Binary files /dev/null and b/ParoxysmII/sound/weapons/sawoff.wav differ diff --git a/ParoxysmII/sound/weapons/sawon.wav b/ParoxysmII/sound/weapons/sawon.wav new file mode 100644 index 0000000..a386031 Binary files /dev/null and b/ParoxysmII/sound/weapons/sawon.wav differ diff --git a/ParoxysmII/sound/weapons/shotgn2.wav b/ParoxysmII/sound/weapons/shotgn2.wav new file mode 100644 index 0000000..43836d5 Binary files /dev/null and b/ParoxysmII/sound/weapons/shotgn2.wav differ diff --git a/ParoxysmII/sound/weapons/shrapdet.wav b/ParoxysmII/sound/weapons/shrapdet.wav new file mode 100644 index 0000000..07bc65f Binary files /dev/null and b/ParoxysmII/sound/weapons/shrapdet.wav differ diff --git a/ParoxysmII/sound/weapons/tink2.wav b/ParoxysmII/sound/weapons/tink2.wav new file mode 100644 index 0000000..3ad80ed Binary files /dev/null and b/ParoxysmII/sound/weapons/tink2.wav differ diff --git a/ParoxysmII/sound/weapons/ts3fire.wav b/ParoxysmII/sound/weapons/ts3fire.wav new file mode 100644 index 0000000..6215a49 Binary files /dev/null and b/ParoxysmII/sound/weapons/ts3fire.wav differ diff --git a/ParoxysmII/sound/weapons/tsfire.wav b/ParoxysmII/sound/weapons/tsfire.wav new file mode 100644 index 0000000..be7b5ee Binary files /dev/null and b/ParoxysmII/sound/weapons/tsfire.wav differ diff --git a/ParoxysmII/sound/weapons/tsload.wav b/ParoxysmII/sound/weapons/tsload.wav new file mode 100644 index 0000000..9abe58b Binary files /dev/null and b/ParoxysmII/sound/weapons/tsload.wav differ diff --git a/ParoxysmII/source/Makefile b/ParoxysmII/source/Makefile new file mode 100644 index 0000000..c83f1b7 --- /dev/null +++ b/ParoxysmII/source/Makefile @@ -0,0 +1,23 @@ +SRC= buttons.r builtins.r client.r combat.r defs.r doors.r dynlight.r \ + flash.r items.r misc.r observe.r plats.r player.r poxdefs.r sectrig.r \ + server.r shields.r specfx.r spectate.r subs.r targid.r triggers.r \ + wall.r weapons.r world.r + +HEADERS = client.rh paroxysm.rh config.rh + +RFLAGS += -Wall -Werror -g + +INSTALLDIR = $(HOME)/.quake/paroxysm/ + +all: qwprogs.dat + +qwprogs.dat: $(SRC) $(HEADERS) progs.src Makefile + qfcc $(RFLAGS) + +clean: + -rm -f *.dat *.sym progdefs.h + +install: qwprogs.dat + -cp qwprogs.* $(INSTALLDIR) + +.PHONY: clean install diff --git a/ParoxysmII/source/builtins.r b/ParoxysmII/source/builtins.r new file mode 100644 index 0000000..7263d03 --- /dev/null +++ b/ParoxysmII/source/builtins.r @@ -0,0 +1,236 @@ +/* + builtins.r + + Master builtins list + + Copyright (C) 2002 Jeff Teunissen + + This program 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include "config.rh" + +/**************************************************************************** + * MATH FUNCTIONS * + ****************************************************************************/ + +/* + ceil + + Returns v, rounded up to the next higher integer +*/ +float (float v) ceil = #38; + +/* + fabs + + Returns the absolute value of v +*/ +float (float v) fabs = #43; + +/* + floor + + Returns v, rounded down to the next lower integer +*/ +float (float v) floor = #37; + +/* + makevectors + + Sets v_forward, v_up, v_right global vectors from the vector ang +*/ +void (vector ang) makevectors = #1; + +/* + normalize + + Transform vector v into a unit vector (a vector with a length of 1) +*/ +vector (vector v) normalize = #9; + +/* + random + + Generate a random number such that 0 <= num <= 1 +*/ +float () random = #7; + +/* + rint + + Returns v, rounded to the closest integer. x.5 rounds up. +*/ +float (float v) rint = #36; + +/* + vectoangles + + Returns a vector 'pitch yaw 0' corresponding to vector v. +*/ +vector (vector v) vectoangles = #51; + +/* + vectoyaw + + Returns the yaw angle ("bearing"), in degrees, associated with vector v. +*/ +float (vector v) vectoyaw = #13; +/* + vlen + + Return the length of vector v +*/ +float (vector v) vlen = #12; + +/**************************************************************************** + * ENTITY FUNCTIONS * + ****************************************************************************/ + +/* + makestatic + + Make entity e static (part of the world). + Static entities do not interact with the game. +*/ +void (entity e) makestatic = #69; + +/* + remove + + Remove entity e. +*/ +void (entity e) remove = #15; + +/* + setmodel + + Sets the model name for entity e to string m. + Set the entity's move type and solid type before calling. +*/ +void (entity e, string m) setmodel = #3; + +/* + setorigin + + Sets origin for entity e to vector o. +*/ +void (entity e, vector o) setorigin = #2; + +/* + setsize + + Set the size of entity e to a cube with the bounds ( x1 y1 z1 ) ( x2 y2 z2 ) +*/ +void (entity e, vector min, vector max) setsize = #4; + +/* + spawn + + Creates a new entity and returns it. +*/ +entity () spawn = #14; + +// ------------------------------------------------------------------------- + +/**************************************************************************** + * DEBUGGING FUNCTIONS * + ****************************************************************************/ + +/* + abort (in QuakeC, this was break) + + Tell the engine to abort (stop) code processing. +*/ +void () abort = #6; + +/* + coredump + + Tell the engine to print all edicts (entities) +*/ +void () coredump = #28; + +/* + traceon + + Enable instruction trace in the interpreter +*/ +void () traceon = #29; + +/* + traceoff + + Disable instruction trace in the interpreter +*/ +void () traceoff = #30; + +/* + eprint + + Print all information on an entity to the server console +*/ +void (entity e) eprint = #31; + +/**************************************************************************** + * CLIENT/SERVER FUNCTIONS * + ****************************************************************************/ + +/* + cvar + + Get the value of one of the server's configuration variables. + Doesn't really supply anything useful for string or vector Cvars. +*/ +float (string s) cvar = #45; + +/* + cvar_set + + Set the value of one of the server's configuration variables. +*/ +void (string var, string val) cvar_set = #72; + +/* + infokey + + Get an info key for an entity. Using an entity of zero (world) reads the + serverinfo/localinfo. +*/ +#ifdef QUAKEWORLD +string (entity e, string key) infokey = #80; + +/* + setinfokey (QuakeForge-only) + + Set an info key for an entity. Using an entity of zero (world) sets a key + in the server's localinfo. Does nothing if the entity is not a player. +*/ +# ifndef __VERSION6__ +void (entity e, string key, string value) setinfokey = #102; +# endif +#endif + +#if 0 + PR_AddBuiltin (pr, "vectoyaw", PF_vectoyaw, 13); // float (vector v) vectoyaw + PR_AddBuiltin (pr, "find", PF_Find, 18); // entity (entity start, .(...) fld, ... match) find + PR_AddBuiltin (pr, "dprint", PF_dprint, 25); // void (string s) dprint +#endif diff --git a/ParoxysmII/source/buttons.r b/ParoxysmII/source/buttons.r new file mode 100644 index 0000000..aef61fa --- /dev/null +++ b/ParoxysmII/source/buttons.r @@ -0,0 +1,139 @@ +#include "config.rh" + +#include "paroxysm.rh" + +// button and multiple button + +void() button_wait; +void() button_return; + +void() button_wait = +{ + self.state = STATE_TOP; + self.nextthink = self.ltime + self.wait; + self.think = button_return; + activator = self.enemy; + SUB_UseTargets(); + self.frame = 1; // use alternate textures +}; + +void() button_done = +{ + self.state = STATE_BOTTOM; +}; + +void() button_return = +{ + self.state = STATE_DOWN; + SUB_CalcMove (self.pos1, self.speed, button_done); + self.frame = 0; // use normal textures + if (self.health) + self.takedamage = DAMAGE_YES; // can be shot again +}; + + +void() button_blocked = // do nothing, just don't come all the way back out +{ +}; + + +void() button_fire = +{ + if (self.state == STATE_UP || self.state == STATE_TOP) + return; + + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + + self.state = STATE_UP; + SUB_CalcMove (self.pos2, self.speed, button_wait); +}; + + +void() button_use = +{ + self.enemy = activator; + button_fire (); +}; + +void() button_touch = +{ + if (other.classname != "player") + return; + self.enemy = other; + button_fire (); +}; + +void() button_killed = +{ + self.enemy = damage_attacker; + self.health = self.max_health; + self.takedamage = DAMAGE_NO; // wil be reset upon return + button_fire (); +}; + + +/*QUAKED func_button (0 .5 .8) ? +When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again. + +"angle" determines the opening direction +"target" all entities with a matching targetname will be used +"speed" override the default 40 speed +"wait" override the default 1 second wait (-1 = never return) +"lip" override the default 4 pixel lip remaining at end of move +"health" if set, the button must be killed instead of touched +"sounds" +0) steam metal +1) wooden clunk +2) metallic click +3) in-out +*/ +void() func_button = +{ + if (self.sounds == 0) { + precache_sound ("buttons/airbut1.wav"); + self.noise = "buttons/airbut1.wav"; + } + if (self.sounds == 1) { + precache_sound ("buttons/switch21.wav"); + self.noise = "buttons/switch21.wav"; + } + if (self.sounds == 2) { + precache_sound ("buttons/switch02.wav"); + self.noise = "buttons/switch02.wav"; + } + if (self.sounds == 3) { + precache_sound ("buttons/switch04.wav"); + self.noise = "buttons/switch04.wav"; + } + + SetMovedir (); + + self.movetype = MOVETYPE_PUSH; + self.solid = SOLID_BSP; + setmodel (self, self.model); + + self.blocked = button_blocked; + self.use = button_use; + + if (self.health) { + self.max_health = self.health; + self.th_die = button_killed; + self.takedamage = DAMAGE_YES; + + self.nobleed = TRUE; // + POX + } else + self.touch = button_touch; + + if (!self.speed) + self.speed = 40; + if (!self.wait) + self.wait = 1; + if (!self.lip) + self.lip = 4; + + self.state = STATE_BOTTOM; + + self.pos1 = self.origin; + self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); +}; + diff --git a/ParoxysmII/source/client.r b/ParoxysmII/source/client.r new file mode 100644 index 0000000..7c6786d --- /dev/null +++ b/ParoxysmII/source/client.r @@ -0,0 +1,1676 @@ +#include "config.rh" + +#include "client.rh" +#include "paroxysm.rh" + +// prototypes +void () W_WeaponFrame; +void () W_SetCurrentAmmo; +void () player_pain; +void () player_stand1; +void (vector org) spawn_tfog; +void (vector org, entity death_owner) spawn_tdeath; + +float modelindex_eyes, modelindex_player; + +// POX - more prototypes +.float armor_rot; //POX - Holds the armor rot time out + +/* +============================================================================= + LEVEL CHANGING / INTERMISSION +============================================================================= +*/ + +string nextmap; + +//float intermission_running; // POX - moved to poxdefs.qc +float intermission_exittime; + + +/*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16) +This is the camera point for the intermission. +Use mangle instead of angle, so you can set pitch or roll as well as yaw. 'pitch roll yaw' +*/ +void() info_intermission = +{ + self.angles = self.mangle; // so C can get at it +}; + +//POX v1.2 +.float configed; + +void() SetChangeParms = +{ + //POX v1.1 - fresh start for everyone on changelevel + + //POX v1.2 - parms are used to store the Taget ID toggle, and to prevent running autoexec.cfg more than once per session + parm1 = self.target_id_toggle; + parm2 = TRUE; +}; + +void() SetNewParms = +{ + // Quake Complains if this function isn't defined +}; + +void() DecodeLevelParms = +{ + localcmd ("serverinfo playerfly 1"); + //POX v1.2 - parms are used to store the Taget ID toggle, and to prevent running autoexec.cfg more than once per session + if(!self.target_id_toggle && !self.target_id_temp) + self.target_id_toggle = parm1; + + self.configed = parm2; + + //POX v1.2 - run autoexec.cfg ONCE when first joining server only! + if (!self.configed) { + self.configed = TRUE; + stuffcmd(self, "exec autoexec.cfg\n"); + } +}; +/* +============ +FindIntermission +Returns the entity to view from +============ +*/ +entity() FindIntermission = +{ + local entity spot; + local float cyc; +// look for info_intermission first + spot = find (world, classname, "info_intermission"); + if (spot) + { // pick a random one + cyc = random() * 4; + while (cyc > 1) { + spot = find (spot, classname, "info_intermission"); + if (!spot) + spot = find (spot, classname, "info_intermission"); + cyc = cyc - 1; + } + return spot; + } +// then look for the start position + spot = find (world, classname, "info_player_start"); + if (spot) + return spot; + + objerror ("FindIntermission: no spot"); +}; + +void() GotoNextMap = +{ + local string newmap; + + //ZOID: 12-13-96, samelevel is overloaded, only 1 works for same level + if (cvar ("samelevel") == 1) // if samelevel is set, stay on same level + changelevel (mapname); + else { + // configurable map lists, see if the current map exists as a + // serverinfo/localinfo var + newmap = infokey(world, mapname); + if (newmap != "") + changelevel (newmap); + else + changelevel (nextmap); + } +}; + +/* +============ +IntermissionThink +When the player presses attack or jump, change to the next level +============ +*/ +void() IntermissionThink = +{ + if (time < intermission_exittime) + return; + + if (!self.button0 && !self.button1 && !self.button2) + return; + + GotoNextMap (); +}; + +/* +============ +execute_changelevel + +The global "nextmap" has been set previously. +Take the players to the intermission spot +============ +*/ +void() execute_changelevel = +{ + local entity pos; + intermission_running = 1; + + // enforce a wait time before allowing changelevel + intermission_exittime = time + 5; + pos = FindIntermission (); + + // play intermission music + WriteByte (MSG_ALL, SVC_CDTRACK); + WriteByte (MSG_ALL, 3); + WriteByte (MSG_ALL, SVC_INTERMISSION); + WriteCoord (MSG_ALL, pos.origin_x); + WriteCoord (MSG_ALL, pos.origin_y); + WriteCoord (MSG_ALL, pos.origin_z); + WriteAngle (MSG_ALL, pos.mangle_x); + WriteAngle (MSG_ALL, pos.mangle_y); + WriteAngle (MSG_ALL, pos.mangle_z); + + other = find (world, classname, "player"); + while (other != world) { + other.takedamage = DAMAGE_NO; + other.solid = SOLID_NOT; + other.movetype = MOVETYPE_NONE; + other.modelindex = 0; + other = find (other, classname, "player"); + } +}; + +void() changelevel_touch = +{ + if (other.classname != "player") + return; +// if "noexit" is set, blow up the player trying to leave +//ZOID, 12-13-96, noexit isn't supported in QW. Overload samelevel +// if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start"))) + if ((cvar("samelevel") == 2) || ((cvar("samelevel") == 3) && (mapname != "start"))) + { + T_Damage (other, self, self, 50000); + return; + } + BPRINT (PRINT_HIGH, other.netname); + BPRINT (PRINT_HIGH," exited the level\n"); + + nextmap = self.map; + SUB_UseTargets (); + self.touch = SUB_Null; +// we can't move people right now, because touch functions are called +// in the middle of C movement code, so set a think time to do it + self.think = execute_changelevel; + self.nextthink = time + 0.1; +}; + +/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION +When the player touches this, he gets sent to the map listed in the "map" variable. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats. +*/ +void() trigger_changelevel = +{ + if (!self.map) + objerror ("chagnelevel trigger doesn't have map"); + + InitTrigger (); + self.touch = changelevel_touch; +}; +/* +============================================================================= + PLAYER GAME EDGE FUNCTIONS +============================================================================= +*/ +void() set_suicide_frame; +// called by ClientKill and DeadThink + +void() respawn = +{ + // make a copy of the dead body for appearances sake + CopyToBodyQue (self); + // set default spawn parms + //SetNewParms (); //POX v1.12 + // respawn + PutClientInServer (); +}; + +/* +============ +ClientKill +Player entered the suicide command +============ +*/ +void() NextLevel; //POX v1.12 + +void() ClientKill = +{ + //POX v1.12 - don't let LMS observers suicide! + if (self.classname == "LMSobserver") { + sprint (self, PRINT_HIGH, "Observers can't suicide!\n"); + return; + } + + BPRINT (PRINT_MEDIUM, self.netname); + BPRINT (PRINT_MEDIUM, " suicides\n"); + set_suicide_frame (); + self.modelindex = modelindex_player; + LOGFRAG (self, self); + self.frags -= 2; // extra penalty + + //POX v1.12 - forgot about stink'n suicides + if ((deathmatch & DM_LMS) && (self.LMS_registered)) { + if (self.frags <= 0) { + lms_plrcount = lms_plrcount - 1; + + self.frags = 0; + self.LMS_registered = 0; + self.LMS_observer = 2; + + BPRINT(PRINT_HIGH, self.netname); + BPRINT(PRINT_HIGH, " is eliminated!\n"); + + sound (self, CHAN_BODY, "nar/n_elim.wav", 1, ATTN_NONE); + + if (lms_plrcount <= 1) //1 player left so end the game + NextLevel (); + } + } + + respawn (); +}; + +float(vector v) CheckSpawnPoint = +{ + return FALSE; +}; + +/* +============ +SelectSpawnPoint +Returns the entity to spawn at +============ +*/ +entity() SelectSpawnPoint = +{ + local entity spot, thing; + local entity spots; + local float numspots, totalspots; + local float pcount; + + numspots = 0; + totalspots = 0; + + // testinfo_player_start is only found in regioned levels + + if ((spot = find (world, classname, "testplayerstart"))) + return spot; + +// choose a info_player_deathmatch point +// ok, find all spots that don't have players nearby + spots = world; + + spot = find (world, classname, "info_player_deathmatch"); + while (spot) { + totalspots++; + + thing = findradius (spot.origin, 84); + pcount = 0; + while (thing) { + if (thing.classname == "player") + pcount++; + thing = thing.chain; + } + + if (!pcount) { + spot.goalentity = spots; + spots = spot; + numspots = numspots + 1; + } + + // Get the next spot in the chain + spot = find (spot, classname, "info_player_deathmatch"); + } + totalspots--; + + if (!numspots) { + // ack, they are all full, just pick one at random +// BPRINT (PRINT_HIGH, "Ack! All spots are full. Selecting random spawn spot\n"); + totalspots = rint (random () * totalspots); + spot = find (world, classname, "info_player_deathmatch"); + do { + spot = find (world, classname, "info_player_deathmatch"); + } while (totalspots-- > 0); + return spot; + } + +// We now have the number of spots available on the map in numspots + + // Generate a random number between 1 and numspots + numspots--; + + numspots = rint((random() * numspots ) ); + spot = spots; + while (numspots > 0) { + spot = spot.goalentity; + numspots--; + } + return spot; +}; + +//void() DecodeLevelParms; +void() PlayerDie; +/* +=========== +ValidateUser +============ +*/ +float(entity e) ValidateUser = +{ +/* + local string s; + local string userclan; + local float rank, rankmin, rankmax; +// +// if the server has set "clan1" and "clan2", then it +// is a clan match that will allow only those two clans in +// + s = serverinfo("clan1"); + if (s) + { + userclan = masterinfo(e,"clan"); + if (s == userclan) + return true; + s = serverinfo("clan2"); + if (s == userclan) + return true; + return false; + } +// +// if the server has set "rankmin" and/or "rankmax" then +// the users rank must be between those two values +// + s = masterinfo (e, "rank"); + rank = stof (s); + s = serverinfo("rankmin"); + if (s) + { + rankmin = stof (s); + if (rank < rankmin) + return false; + } + s = serverinfo("rankmax"); + if (s) + { + rankmax = stof (s); + if (rankmax < rank) + return false; + } + return true; +*/ +}; +/* +=========== +PutClientInServer +called each time a player enters a new level +============ +*/ +void() PutClientInServer = +{ + local entity spot; + + // remove items + self.items &= ~(IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD); + + if (deathmatch & DM_LMS && self.LMS_observer) { + SpawnObserver (); + return; + } + + self.classname = "player"; + self.health = 100; + self.takedamage = DAMAGE_AIM; + self.solid = SOLID_SLIDEBOX; + self.movetype = MOVETYPE_WALK; + self.show_hostile = 0; + self.max_health = 100; + self.flags = FL_CLIENT; + self.air_finished = time + 12; + self.dmg = 2; // initial water damage + self.super_damage_finished = 0; + self.radsuit_finished = 0; + self.invisible_finished = 0; + self.invincible_finished = 0; + self.effects = 0; + self.invincible_time = 0; + + // Give player some stuff + self.items = IT_AXE | IT_TSHOT; + self.ammo_shells = 25; + self.ammo_nails = 0; + self.ammo_rockets = 0; + self.ammo_cells = 0; + self.weapon = IT_TSHOT; + + // Give player 50 points of blue armor + self.items &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); + self.items |= IT_ARMOR1; + self.armorvalue = 50; + self.armortype = 0.9; + + DecodeLevelParms (); // exec autoconfig if needed, restore Target ID value + + W_SetCurrentAmmo (); + self.attack_finished = time; + self.th_pain = player_pain; + self.th_die = PlayerDie; + + self.deadflag = DEAD_NO; + + // paustime is set by teleporters to keep the player from moving a while + self.pausetime = 0; + + // reset reload_rocket after death + self.reload_rocket = 0; + + // reset T-Shot after death + self.prime_tshot = FALSE; + + spot = SelectSpawnPoint (); + self.origin = spot.origin + '0 0 1'; + self.angles = spot.angles; + self.fixangle = TRUE; // turn this way immediately + + // oh, this is a hack! + setmodel (self, "progs/eyes.mdl"); + modelindex_eyes = self.modelindex; + setmodel (self, "progs/player.mdl"); + modelindex_player = self.modelindex; + setsize (self, VEC_HULL_MIN, VEC_HULL_MAX); + + self.view_ofs = '0 0 22'; + +// Mod - Xian (May.20.97) +// Bug where player would have velocity from their last kill + self.velocity = '0 0 0'; + player_stand1 (); + + makevectors (self.angles); + spawn_tfog (self.origin + v_forward * 20); + spawn_tdeath (self.origin, self); + + // Set Rocket Jump Modifiers + if (stof (infokey (world, "rj"))) { + rj = stof (infokey (world, "rj")); + } else { + rj = 1; + } + +/* + New deathmatch modes +*/ + // Last Man Standing + if ((deathmatch & DM_LMS) && !self.LMS_registered) { + if (!fraglimit_LMS) + self.frags = 5; + else + self.frags = fraglimit_LMS; + + self.LMS_registered = TRUE; + lms_plrcount++; + } + + // Dark Mode - more stuff in Player_PostThink - doesn't work here (?) + if (deathmatch & DM_DARK) + flash_on (self); + + // just in case a player dies in a colour_light field +// stuffcmd (self, "v_cshift 1 1 1 0\n"); + + // Free For All mode + if (deathmatch & DM_FFA) { + self.ammo_nails = 200; + self.ammo_shells = 200; + self.ammo_rockets = 100; + self.ammo_cells = 200; + self.items |= IT_PLASMAGUN; + self.items |= IT_SUPER_NAILGUN; + self.items |= IT_COMBOGUN; + self.items |= IT_ROCKET_LAUNCHER; + self.items |= IT_GRENADE_LAUNCHER; + + self.items &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); + self.items |= IT_ARMOR3; + + self.armorvalue = 250; + self.armortype = 0.9; + self.health = 200; + self.items |= IT_INVULNERABILITY; + self.invincible_time = 1; + self.invincible_finished = time + 3; + + self.weapon = IT_ROCKET_LAUNCHER; + self.currentammo = self.ammo_rockets; + self.weaponmodel = "progs/v_rhino.mdl"; + self.weaponframe = 0; + self.items |= IT_ROCKETS; + + self.max_health = 200; + sound (self, CHAN_AUTO, "items/protect.wav", 1, ATTN_NORM); + stuffcmd (self, "bf\n"); + } + + // + POX - Predator mode + if (deathmatch & DM_PREDATOR) { + + sound (self, CHAN_AUTO, "items/inv1.wav", 1, ATTN_NORM); + stuffcmd (self, "bf\n"); + + self.items |= IT_INVISIBILITY; + self.invisible_time = 1; + self.invisible_finished = time + 9999999999; + } +}; + +/* +============================================================================= + QUAKED FUNCTIONS +============================================================================= +*/ +/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24) +The normal starting point for a level. +*/ +void() info_player_start = +{ +}; +/*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24) +Only used on start map for the return point from an episode. +*/ +void() info_player_start2 = +{ +}; +/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24) +potential spawning position for deathmatch games +*/ +void() info_player_deathmatch = +{ +}; +/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24) +potential spawning position for coop games +*/ +void() info_player_coop = +{ +}; +/* +=============================================================================== +RULES +=============================================================================== +*/ + +/* + NextLevel () + + go to the next level for deathmatch +*/ +void() NextLevel = +{ + local entity o; + + // + POX + if (deathmatch & DM_LMS) + lms_plrcount = 0; + // - POX + + if (nextmap != "") + return; // already done + + if (mapname == "start") { + if (!cvar ("registered")) { + mapname = "e1m1"; + } else if (!(serverflags & 1)) { + mapname = "e1m1"; + serverflags |= 1; + } else if (!(serverflags & 2)) { + mapname = "e2m1"; + serverflags |= 2; + } else if (!(serverflags & 4)) { + mapname = "e3m1"; + serverflags |= 4; + } else if (!(serverflags & 8)) { + mapname = "e4m1"; + serverflags -= 7; + } + + o = spawn (); + o.map = mapname; + } else { // find a trigger changelevel + o = find (world, classname, "trigger_changelevel"); + if (!o || mapname == "start") { // go back to same map if no trigger_changelevel + o = spawn (); + o.map = mapname; + } + } + nextmap = o.map; + + if (o.nextthink < time) { + o.think = execute_changelevel; + o.nextthink = time + 0.1; + } +}; + +/* +============ +CheckRules +Exit deathmatch games upon conditions +============ +*/ +void() CheckRules = +{ + // Last Man Standing + if ((deathmatch & DM_LMS) && lms_gameover) { + NextLevel (); + return; + } + + if (timelimit && time >= timelimit) + NextLevel (); + + if (fraglimit && self.frags >= fraglimit) + NextLevel (); +}; + +//============================================================================ +void() PlayerDeathThink = +{ + local float forward; + + if ((self.flags & FL_ONGROUND)) { + forward = vlen (self.velocity); + forward -= 20; + if (forward <= 0) + self.velocity = '0 0 0'; + else + self.velocity = forward * normalize (self.velocity); + } + + // wait for all buttons released + if (self.deadflag == DEAD_DEAD) { + if (self.button2 || self.button1 || self.button0) + return; + + self.deadflag = DEAD_RESPAWNABLE; + return; + } + + // don't let players lay around as dead guys during a Last Man Standing game + if (deathmatch & DM_LMS) { + if (!(self.button2 || self.button1 || self.button0)) + return; + + stuffcmd (self, "wait;wait;wait;wait;wait;wait\n"); + } + + // wait for any button down + if (!(self.button2 || self.button1 || self.button0)) + return; + + self.button0 = self.button1 = self.button2 = 0; + respawn (); +}; + + +void() PlayerJump = +{ + if (self.flags & FL_WATERJUMP) + return; + +// + POX - Removed (new water sounds handled in WaterMove) +#if 0 + if (self.waterlevel >= 2) { // play swimming sound + if (self.swim_flag < time) { + self.swim_flag = time + 1; + if (random() < 0.5) + sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM); + } + return; + } +#endif + +// + POX - velocities changed in fluids + if (self.waterlevel >= 2) { + switch (self.watertype) { + case CONTENT_WATER: + self.velocity_z = 100; + break; + case CONTENT_SLIME: + self.velocity_z = 80; + break; + default: + self.velocity_z = 50; + } + return; + } +// - POX + + if (!(self.flags & FL_ONGROUND)) + return; + + if (!(self.flags & FL_JUMPRELEASED) ) + return; // don't pogo stick + + self.flags -= FL_JUMPRELEASED; + self.button2 = 0; + + // player jumping sound + sound (self, CHAN_VOICE, "player/plyrjmp8.wav", 1, ATTN_NORM); +}; + +/* +=========== +WaterMove +============ +*/ +.float dmgtime; +void() WaterMove = +{ +// dprint (ftos(self.waterlevel)); + if (self.movetype == MOVETYPE_NOCLIP) + return; + if (self.health < 0) + return; + if (self.waterlevel != 3) { + if (self.air_finished < time) + sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM); + else if (self.air_finished < time + 9) + sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM); + self.air_finished = time + 12; + self.dmg = 2; + } else if (self.air_finished < time) { // drown! + if (self.pain_finished < time) { + self.dmg += 2; + + if (self.dmg > 15) + self.dmg = 10; + + T_Damage (self, world, world, self.dmg); + self.pain_finished = time + 1; + } + } + + if (!self.waterlevel) { + if (self.flags & FL_INWATER) { // play leave water sound + sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM); + self.flags = self.flags - FL_INWATER; + + //POX v1.2 - fixed rare cases of underwater sound not cancelling out + if (self.outwsound == 1) { + sound (self, CHAN_BODY, "misc/owater2.wav", 0.5, ATTN_NORM); + self.outwsound = 0; + self.inwsound = 1; + self.uwmuffle = time; + } + } + return; + } + + switch (self.watertype) { + case CONTENT_LAVA: // do damage + if (self.dmgtime < time) { + if (self.radsuit_finished > time) + self.dmgtime = time + 1; + else + self.dmgtime = time + 0.2; + + T_Damage (self, world, world, 10*self.waterlevel); + } + break; + case CONTENT_SLIME: // do damage + if (self.dmgtime < time && self.radsuit_finished < time) { + self.dmgtime = time + 1; + T_Damage (self, world, world, 4*self.waterlevel); + } + break; + default: + break; + } + + if (!(self.flags & FL_INWATER)) { // player enter water sound + switch (self.watertype) { + case CONTENT_LAVA: + sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM); + case CONTENT_SLIME: + sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM); + case CONTENT_WATER: + sound (self, CHAN_VOICE, "player/inh2o.wav", 1, ATTN_NORM); + default: + self.flags |= FL_INWATER; + self.dmgtime = 0; + } + } + +// + POX - New water movement sounds +if (self.waterlevel >= 3) + { + self.onwsound = time; + self.outwsound = 1; + + if (self.inwsound == 1) + { + sound (self, CHAN_VOICE, "misc/inh2ob.wav", 1, ATTN_NORM); + self.inwsound = 0; + } + + if (self.uwmuffle < time) + { + sound (self, CHAN_BODY, "misc/uwater.wav", 1, ATTN_STATIC); + self.uwmuffle = time + 3.58; + } + } + + if (self.waterlevel == 2) + { + if (self.outwsound == 1) + { + sound (self, CHAN_BODY, "misc/owater2.wav", 1, ATTN_NORM); + self.outwsound = 0; + self.inwsound = 1; + //self.onwsound = time + 1.9; + } + + //POXnote + //CheckWaterJump (); Not used in QW? + + self.uwmuffle = time; + + /* POX - now done in footstep routine + if (self.onwsound < time) + { + if (random() < 0.5) + sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM); + else + sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM); + + self.onwsound = time + random()*2; + } + */ + } +// - POX +}; +void() CheckWaterJump = +{ + local vector start, end; +// check for a jump-out-of-water + makevectors (self.angles); + start = self.origin; + start_z = start_z + 8; + v_forward_z = 0; + normalize(v_forward); + end = start + v_forward*24; + traceline (start, end, TRUE, self); + if (trace_fraction < 1) + { // solid at waist + start_z = start_z + self.maxs_z - 8; + end = start + v_forward*24; + self.movedir = trace_plane_normal * -50; + traceline (start, end, TRUE, self); + if (trace_fraction == 1) + { // open at eye level + self.flags = self.flags | FL_WATERJUMP; + self.velocity_z = 225; + self.flags = self.flags - (self.flags & FL_JUMPRELEASED); + self.teleport_time = time + 2; // safety net + return; + } + } +}; +/* +================ +PlayerPreThink +Called every frame before physics are run +================ +*/ +void() PlayerPreThink = +{ + if (intermission_running) + { + IntermissionThink (); // otherwise a button could be missed between + return; // the think tics + } + if (self.view_ofs == '0 0 0') + return; // intermission or finale + makevectors (self.v_angle); // is this still used + self.deathtype = ""; + + CheckRules (); + + // + POX - for LMS observer - POX 1.2 moved above WaterMove(); + if (deathmatch & DM_LMS && self.LMS_observer) + { + if (self.deadflag >= DEAD_DEAD) + { + PlayerDeathThink (); + return; + } + return; + } + // - POX + WaterMove (); +/* + if (self.waterlevel == 2) + CheckWaterJump (); +*/ + if (self.deadflag >= DEAD_DEAD) + { + PlayerDeathThink (); + return; + } + + if (self.deadflag == DEAD_DYING) + return; // dying, so do nothing + + if (self.button2) + { + PlayerJump (); + } + else + self.flags = self.flags | FL_JUMPRELEASED; +// teleporters can force a non-moving pause time + if (time < self.pausetime) + self.velocity = '0 0 0'; + + // POX + if (deathmatch & DM_AUTOSWITCH) + { + + if(time > self.attack_finished && self.currentammo == 0 && self.weapon != IT_AXE) + { + self.weapon = W_BestWeapon (); + W_SetCurrentAmmo (); + } + } +}; + +/* +================ +CheckPowerups +Check for turning off powerups +================ +*/ +void() CheckPowerups = +{ + if (self.health <= 0) + return; +// + POX - Rot armour down to 150 (max 250) + if (self.armorvalue > 150 && self.armor_rot < time) { + self.armorvalue = self.armorvalue - 1; + self.armor_rot = time + 1; + + // change armour to Yellow + if (self.armorvalue == 150) { + self.items &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); + self.items |= IT_ARMOR2; + } + } +// - POX +// invisibility + if (self.invisible_finished) + { +// sound and screen flash when items starts to run out + if (self.invisible_sound < time) + { + sound (self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE); + self.invisible_sound = time + ((random() * 3) + 1); + } + if (self.invisible_finished < time + 3) + { + if (self.invisible_time == 1) + { + sprint (self, PRINT_HIGH, "Cloak is failing...\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM); + self.invisible_time = time + 1; + } + + if (self.invisible_time < time) + { + self.invisible_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + if (self.invisible_finished < time) + { // just stopped + self.items -= IT_INVISIBILITY; + self.invisible_finished = 0; + self.invisible_time = 0; + } + + // use the eyes + self.frame = 0; + self.modelindex = modelindex_eyes; + } + else + self.modelindex = modelindex_player; // don't use eyes +// invincibility + if (self.invincible_finished) + { +// sound and screen flash when items starts to run out + if (self.invincible_finished < time + 3) + { + if (self.invincible_time == 1) + { + sprint (self, PRINT_HIGH, "MegaShields are almost burned out...\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM); + self.invincible_time = time + 1; + } + + if (self.invincible_time < time) + { + self.invincible_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + + if (self.invincible_finished < time) + { // just stopped + self.items -= IT_INVULNERABILITY; + self.invincible_time = 0; + self.invincible_finished = 0; + } + // + POX - ignore light effects in Dark Mode + if (self.invincible_finished > time && !(deathmatch & DM_DARK)) + { + self.effects = self.effects | EF_DIMLIGHT; + self.effects = self.effects | EF_RED; + } + else + { + self.effects = self.effects - (self.effects & EF_DIMLIGHT); + self.effects = self.effects - (self.effects & EF_RED); + } + } +// super damage + if (self.super_damage_finished) + { +// sound and screen flash when items starts to run out + if (self.super_damage_finished < time + 3) + { + if (self.super_time == 1) + { + //if (deathmatch == 4) + // sprint (self, PRINT_HIGH, "OctaPower is wearing off\n"); + //else + sprint (self, PRINT_HIGH, "Quad Damage is wearing off\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM); + self.super_time = time + 1; + } + + if (self.super_time < time) + { + self.super_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + if (self.super_damage_finished < time) + { // just stopped + self.items &= ~IT_QUAD; + /*if (deathmatch == 4) + { + self.ammo_cells = 255; + self.armorvalue = 1; + self.armortype = 0.8; + self.health = 100; + }*/ + self.super_damage_finished = 0; + self.super_time = 0; + } + + if (self.super_damage_finished > time) { + self.effects |= EF_DIMLIGHT; + self.effects |= EF_BLUE; + } else { + self.effects &= ~EF_DIMLIGHT; + self.effects &= ~EF_BLUE; + } + } + + // suit + if (self.radsuit_finished) { + self.air_finished = time + 12; // don't drown + + if (self.radsuit_finished < time + 3) { // sound and flash when running out + if (self.rad_time == 1) { + sprint (self, PRINT_HIGH, "Air supply in Biosuit expiring\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM); + self.rad_time = time + 1; + } + + if (self.rad_time < time) { + self.rad_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + + if (self.radsuit_finished < time) { // just stopped + self.items &= ~IT_SUIT; + self.rad_time = 0; + self.radsuit_finished = 0; + } + } +}; + +//POX v1.2 - PlayerRegen - for better handling of regen staion touch +.float armregen; +.float regen_finished; +void () PlayerRegen = +{ + local float type, bit; + local string snd; + + if (self.armorvalue >= self.armregen) { + self.regen_finished = time; + return; // already have max armour that station can give + } + + self.armorvalue += 3; + + // Cap armour + if (self.armorvalue > self.armregen) + self.armorvalue = self.armregen; + if (self.armorvalue > 150) { // Equivalent to Red (level 3) Armour + type = 0.8; + snd = "items/shield3.wav"; + bit = IT_ARMOR3; + } else if (self.armorvalue > 50) { // Equivlent to Yellow (level 2) Armour + type = 0.8; + snd = "items/shield2.wav"; + bit = IT_ARMOR2; + } else if (self.armorvalue > 1) { // Equivlent to Blue (level 1) Armour + type = 0.8; + snd = "items/shield1.wav"; + bit = IT_ARMOR1; + } else { //you aint got squat + type = 0; + snd = "misc/null.wav"; + bit = 0; + } + + // set armour type + self.armortype = type; + self.items &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); + self.items |= bit; + sound (self, CHAN_AUTO, snd, 1, ATTN_NORM); + + //POX - 1.01b - Don't allow armour to rot while recharging + self.armor_rot = time + 5; +}; + +/* +================ +PlayerPostThink +Called every frame after physics are run +================ +*/ +void () PlayerPostThink = +{ + //POX v1.2 - clear overrun v_cshifts! + if (time < 1.5) + stuffcmd (self, "bf\n"); + + //POX v1.2 - cshift auto reset for colour_light + if (self.cshift_finished < time) { + if (!self.cshift_off) { + stuffcmd (self, "v_cshift 0 0 0 0\n"); + self.cshift_off = TRUE; + } + } + + if (self.view_ofs == '0 0 0') + return; // intermission or finale + + if (self.deadflag) + return; + + //+POX TESTING + //local string howmanyplayers; + //howmanyplayers = ftos(lms_plrcount); + //sprint (self, PRINT_HIGH, howmanyplayers); + //-POX TESTING + + // + POX - for LMS observer + if (deathmatch & DM_LMS && self.LMS_observer) { + ObserverThink (); + return; + } + // - POX + + // check to see if player landed and play landing sound + if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND)) { + if (self.jump_flag < -650) { + local float d; + + self.deathtype = "falling"; + + d = (self.jump_flag + 625) * -0.1; // scale damage by fall height + T_Damage (self, world, world, d); + sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM); + } else { + sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM); + } + } + self.jump_flag = self.velocity_z; + + //POX v1.2 - better regen touch handling + if (self.regen_finished > time) + PlayerRegen (); + + CheckPowerups (); + W_WeaponFrame (); +}; +/* +=========== +ClientConnect +called when a player connects to a server +============ +*/ +void() ClientConnect = +{ +// + POX - I hard coded some environmental changes to prevent tampering.... +// NOTE: autoexec.cfg is called at DecodeLevelParms (QW ignores it when called from quake.rc - which is also ignored) +// POX v1.12 added 'fov 90' (mostly for lms_observer additions) + stuffcmd (self, "alias rules impulse 253;alias secondtrigger impulse 15;alias idtarget impulse 16;alias gllight impulse 17;wait;fov 90;v_idlescale 0.54;v_ipitch_cycle 3.5;v_ipitch_level 0.4;v_iroll_level 0.1;v_iyaw_level 0;v_kickpitch 0.8;v_kickroll 0.8;scr_conspeed 900;cl_bobcycle 0.8;cl_bobup 0;cl_bob 0.015\n"); +// - POX + + // + POX LMS late joiners get booted to spectate + if (!(deathmatch & DM_LMS)) { + BPRINT (PRINT_HIGH, self.netname); + BPRINT (PRINT_HIGH, " entered the game\n"); + centerprint (self, "Paroxysm II v1.2.0\n"); + } else if (time < 40) { // Allow entrants for up to 40 secs after changelevel + BPRINT (PRINT_HIGH, self.netname); + BPRINT (PRINT_HIGH, " entered the game\n"); + + if (!lms_plrcount) + centerprint(self, "Paroxysm II v1.2.0\nLast Man Standing Rules Apply.\n\nWaiting for players..."); + else + centerprint(self, "Paroxysm II v1.2.0\nLast Man Standing Rules Apply."); + } else { //After 40 secs, If there are two or more players, go into observer mode + if (!lms_plrcount) { //First player arrived, let him wait around + BPRINT (PRINT_HIGH, self.netname); + BPRINT (PRINT_HIGH, " entered the game\n"); + centerprint(self, "Paroxysm II v1.2.0\nLast Man Standing Rules Apply.\n\nWaiting for players..."); + } else if (lms_plrcount == 1) { // second player arrived, so go to the next map (for a fair start) + BPRINT (PRINT_HIGH, self.netname); + BPRINT (PRINT_HIGH, " entered the game\n"); + NextLevel (); + } else { // LMS Game allready started so boot to observe + BPRINT (PRINT_HIGH, self.netname); + BPRINT (PRINT_HIGH, " entered the game late!"); + //LMS reconnect as spectator + self.LMS_observer = 1; + } + + } + + // a client connecting during an intermission can cause problems + if (intermission_running) + GotoNextMap (); +}; +/* +=========== +ClientDisconnect +called when a player disconnects from a server +============ +*/ +void() ClientDisconnect = +{ + // let everyone else know + BPRINT (PRINT_HIGH, self.netname); + BPRINT (PRINT_HIGH, " left the game with "); + BPRINT (PRINT_HIGH, ftos (self.frags)); + BPRINT (PRINT_HIGH, " frags\n"); + sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE); +// + POX + if ((deathmatch & DM_LMS) && (self.LMS_registered)) + { + lms_plrcount = lms_plrcount - 1; + + if (lms_plrcount <= 1) //One or no players left so end the game + NextLevel (); + } +// - POX + + set_suicide_frame (); +}; + +/* + ClientObituary (entity, entity) + + called when a player dies +*/ +void(entity targ, entity attacker) ClientObituary = +{ + local float rnum; + local string deathstring = "", deathstring2; +#ifdef QUAKEWORLD + local string attackerteam; + local string targteam; +#else + local entity nar; +#endif + +#ifdef QUAKEWORLD + attackerteam = infokey (attacker, "team"); + targteam = infokey (targ, "team"); +#endif + rnum = random (); + + if (targ.classname == "player") { + if (deathmatch & DM_LMS) { // Last Man Standing frag stuff... + targ.frags = targ.frags - 1; + //LOGFRAG (attacker, targ); // we don't log frags until the end + + if (targ.frags <= 0) { + BPRINT (PRINT_HIGH, targ.netname); + BPRINT (PRINT_HIGH, " is eliminated!\n"); + lms_plrcount = lms_plrcount - 1; + + sound (targ, CHAN_BODY, "nar/n_elim.wav", 1, ATTN_NONE); + + if (lms_plrcount <= 1) { // end of game + lms_gameover = TRUE; // Check this in CheckRules (so frag logging is still done) + return; + } + + // Put 'em into LMS observer mode at next spawn + targ.frags = 0; + targ.LMS_registered = 0; + targ.LMS_observer = 2; + } + } + + if (attacker.classname == "teledeath") { + LOGFRAG (attacker.owner, targ); + + if (!(deathmatch & DM_LMS)) // don't add frags in Last Man Standing + attacker.owner.frags = attacker.owner.frags + 1; + + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, " was telefragged by "); + BPRINT (PRINT_MEDIUM, attacker.owner.netname); + BPRINT (PRINT_MEDIUM, "\n"); + return; + } + + if (attacker.classname == "teledeath2") { + LOGFRAG (targ, targ); + + if (!(deathmatch & DM_LMS)) + targ.frags = targ.frags - 1; + + BPRINT (PRINT_MEDIUM, "MegaShields deflect "); + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, "'s telefrag\n"); + return; + } + + // double MegaShield telefrag + if (attacker.classname == "teledeath3") { + LOGFRAG (targ, targ); + + if (!(deathmatch & DM_LMS)) // do regular obituary taunts in LMS + targ.frags = targ.frags - 1; + + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, " was telefragged by "); + BPRINT (PRINT_MEDIUM, attacker.owner.netname); + BPRINT (PRINT_MEDIUM, "'s MegaShield's power\n"); + + return; + } + + if (targ.deathtype == "squish") { + if (attacker.classname == "player") { + if (teamplay) { + if ((targteam != "") && (targteam == attackerteam) && (targ != attacker)) { + LOGFRAG (attacker, attacker); + + if (!(deathmatch & DM_LMS)) + attacker.frags = attacker.frags - 1; + + BPRINT (PRINT_MEDIUM, attacker.netname); + BPRINT (PRINT_MEDIUM, " squished a teammate\n"); + + return; + } + } + + if (attacker != targ) { + LOGFRAG (attacker, targ); + + if (!(deathmatch & DM_LMS)) + attacker.frags = attacker.frags + 1; + + BPRINT (PRINT_MEDIUM, attacker.netname); + BPRINT (PRINT_MEDIUM, " squishes "); + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, "\n"); + + return; + } + } else { + LOGFRAG (targ, targ); + + if (!(deathmatch & DM_LMS)) + targ.frags = targ.frags - 1; + + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, " was squished\n"); + + return; + } + } + + if (attacker.classname == "player") { + if (targ == attacker) { // killed self (dumbass!) + LOGFRAG (attacker, attacker); + + if (!(deathmatch & DM_LMS)) + attacker.frags = attacker.frags - 1; + + deathstring2 = "\n"; + + if (targ.deathtype == "grenade") { + if (rnum < 0.5) + deathstring = " tries to put the pin back in"; + else + deathstring = " throws the pin"; + } else if (targ.deathtype == "impactgrenade") { + deathstring = " eats his own impact grenade"; + } else if (targ.deathtype == "megaplasma") { + deathstring = " plays with the plasma"; + } else if (targ.deathtype == "mine") { + if (rnum < 0.67) + deathstring = " forgot where his phase mine was"; + else + deathstring = " found his phase mine"; + } else if (targ.deathtype == "nail") { + deathstring = " nails himself to the wall"; + } else if (targ.deathtype == "rocket") { + if (rnum < 0.5) + deathstring = " finds his rocket tasty"; + else + deathstring = " plays \"Doctor Strangelove\""; + } else if (targ.deathtype == "shrapnel") { + if (rnum < 0.9) + deathstring = " finds out what a shrapnel bomb does"; + else + deathstring = " give us up the bomb"; + } else if (targ.deathtype == "supernail") { + deathstring = " decides to use himself for a pin cushion"; + } else if (targ.weapon == IT_PLASMAGUN && targ.waterlevel > 1) { + deathstring = " discharges into the "; + if (targ.watertype == CONTENT_SLIME) + deathstring2 = "slime\n"; + else if (targ.watertype == CONTENT_LAVA) + deathstring2 = "lava\n"; + else + deathstring2 = "water\n"; + } else { + deathstring = " becomes bored with life"; + } + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, deathstring); + BPRINT (PRINT_MEDIUM, deathstring2); + + return; + } else if ((teamplay == 2) && (targteam == attackerteam) && (attackerteam != "")) { + LOGFRAG (attacker, attacker); + + if (!(deathmatch & DM_LMS)) + attacker.frags = attacker.frags - 1; + + if (rnum < 0.25) + deathstring = " mows down a teammate\n"; + else if (rnum < 0.50) + deathstring = " checks his glasses\n"; + else if (rnum < 0.75) + deathstring = " gets a frag for the other team\n"; + else + deathstring = " loses another friend\n"; + + BPRINT (PRINT_MEDIUM, attacker.netname); + BPRINT (PRINT_MEDIUM, deathstring); + + return; + } else { + LOGFRAG (attacker, targ); + + if (!(deathmatch & DM_LMS)) + attacker.frags = attacker.frags + 1; + + deathstring2 = "\n"; + + if (targ.deathtype == "nail") { + deathstring = " was nailed by "; + } else if (targ.deathtype == "supernail") { + deathstring = " was punctured by "; + } else if (targ.deathtype == "grenade") { + deathstring = " eats "; + deathstring2 = "'s pineapple\n"; + if (targ.health < -40) { + deathstring = " was gibbed by "; + deathstring2 = "'s grenade\n"; + } + } else if (targ.deathtype == "rocket") { + if (attacker.super_damage_finished > 0 && targ.health < -40) { + if (rnum < 0.3) { + deathstring = " was brutalized by "; + } else if (rnum < 0.6) { + deathstring = " was smeared by "; + } else { + BPRINT (PRINT_MEDIUM, attacker.netname); + BPRINT (PRINT_MEDIUM, " rips "); + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, " a new one\n"); + return; + } + deathstring2 = "'s quad rocket\n"; + } else { + if (targ.health < -40) + deathstring = " was gibbed by "; + else + deathstring = " rides "; + deathstring2 = "'s rocket\n"; + } + } else if (targ.deathtype == "megaplasma") { + if (targ.health < -40) + deathstring = " was discombobulated by "; + else + deathstring = " bites "; + deathstring2 = "'s plasma burst\n"; + } else if (targ.deathtype == "impactgrenade") { + if (targ.health < -40) + deathstring = " was gibbed by "; + else + deathstring = " swallows "; + deathstring2 = "'s impact grenade\n"; + } else if (targ.deathtype == "mine") { + if (targ.health < -40) + deathstring = " is chum thanks to "; + else + deathstring = " stepped on "; + deathstring2 = "'s phase mine\n"; + } else if (targ.deathtype == "shrapnel") { + if (targ.health < -40) + deathstring = " gets a face full of "; + else + deathstring = " got too close to "; + deathstring2 = "'s shrapnel bomb\n"; +#ifdef QUAKEWORLD + } else if (attacker.weapon == IT_AXE) { +#else + } else if (attacker.weapon == IT_BONESAW) { +#endif + deathstring = " was butchered by "; + deathstring2 = "\n"; + } else if (attacker.weapon == IT_TSHOT) { + deathstring = " chewed on "; + deathstring2 = "'s boomstick\n"; + } else if (attacker.weapon == IT_COMBOGUN) { + deathstring = " ate 2 loads of "; + deathstring2 = "'s buckshot\n"; + } else if (attacker.weapon == IT_PLASMAGUN) { + deathstring = " got burned by "; + deathstring2 = "'s plasma\n"; + } + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, deathstring); + BPRINT (PRINT_MEDIUM, attacker.netname); + BPRINT (PRINT_MEDIUM, deathstring2); + } + return; + } else { // traps, world stuff + LOGFRAG (targ, targ); + + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + targ.frags = targ.frags - 1; // killed self + + if (attacker.classname == "explo_box" || attacker.classname == "explo_bsp") { + deathstring = " blew up\n"; + } else if (targ.deathtype == "falling") { + deathstring = " fell to his death\n"; + } else if (targ.deathtype == "nail" || targ.deathtype == "supernail") { + deathstring = " was spiked\n"; + } else if (targ.deathtype == "laser") { + deathstring = " was zapped\n"; + } else if (attacker.classname == "fireball") { + deathstring = " ate a lavaball\n"; + } else if (attacker.classname == "trigger_changelevel") { + deathstring = " tried to leave\n"; + } else if (targ.watertype == CONTENT_SLIME) { + if (rnum < 0.5) + deathstring = " gulped down a load of slime\n"; + else + deathstring = " can't exist on slime alone\n"; + } else if (targ.watertype == CONTENT_LAVA) { + if (targ.health < -15) { + deathstring = " burst into flames\n"; + } else if (rnum < 0.5) { + deathstring = " turned into hot slag\n"; + } else { + deathstring = " visits the Volcano God\n"; + } + } else if (targ.watertype == CONTENT_WATER) { + if (rnum < 0.5) + deathstring = " sleeps with the fishes\n"; + else + deathstring = " sucks it down\n"; + } else { + if (rnum < 0.5) + deathstring = " bought the farm\n"; + else + deathstring = " died\n"; + } + + BPRINT (PRINT_MEDIUM, targ.netname); + BPRINT (PRINT_MEDIUM, deathstring); + return; + } + } +}; diff --git a/ParoxysmII/source/client.rh b/ParoxysmII/source/client.rh new file mode 100644 index 0000000..8ca0f71 --- /dev/null +++ b/ParoxysmII/source/client.rh @@ -0,0 +1,62 @@ +/* + client.qh + + Client functions + + Copyright (C) 2001 Jeff Teunissen + + This program 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +entity () FindIntermission; +entity() SelectSpawnPoint; +float (entity e) ValidateUser; +float (vector v) CheckSpawnPoint; +void () DecodeLevelParms; +void () GotoNextMap; +void () IntermissionThink; +void () SetChangeParms; +void () SetNewParms; +void () changelevel_touch; +void () execute_changelevel; +void () info_intermission; +void () trigger_changelevel; +void () CheckPowerups; +void () CheckRules; +void () CheckWaterJump; +void () ClientConnect; +void () ClientDisconnect; +void () ClientKill; +void () NextLevel; +void () PlayerDeathThink; +void () PlayerJump; +void () PlayerPostThink; +void () PlayerPreThink; +void () PlayerRegen; +void () PutClientInServer; +void () WaterMove; +void () info_player_coop; +void () info_player_deathmatch; +void () info_player_start; +void () info_player_start2; +void () respawn; +void () set_suicide_frame; +void (entity targ, entity attacker) ClientObituary; diff --git a/ParoxysmII/source/combat.r b/ParoxysmII/source/combat.r new file mode 100644 index 0000000..5977adf --- /dev/null +++ b/ParoxysmII/source/combat.r @@ -0,0 +1,311 @@ +#include "config.rh" + +#include "paroxysm.rh" + +void () T_MissileTouch; +void () info_player_start; +void (entity targ, entity attacker) ClientObituary; +void (entity inflictor, entity attacker, float damage, entity ignore, string dtype) T_RadiusDamage; + +/*SERVER +void() monster_death_use; +*/ + +//============================================================================ + +/* + CanDamage + + Returns true if the inflictor can directly damage the target. Used for + explosions and melee attacks. +*/ +float (entity targ, entity inflictor) CanDamage = +{ + // bmodels need special checking because their origin is 0,0,0 + if (targ.movetype == MOVETYPE_PUSH) { + traceline (inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self); + + if (trace_fraction == 1) + return TRUE; + + if (trace_ent == targ) + return TRUE; + + return FALSE; + } + + traceline (inflictor.origin, targ.origin, TRUE, self); + + if (trace_fraction == 1) + return TRUE; + + traceline (inflictor.origin, targ.origin + '15 15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + + traceline (inflictor.origin, targ.origin + '-15 -15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + + traceline (inflictor.origin, targ.origin + '-15 15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + + traceline (inflictor.origin, targ.origin + '15 -15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + + return FALSE; +}; + +/* +============ +Killed +============ +*/ +void (entity targ, entity attacker) Killed = +{ + local entity oself = self; + + self = targ; + + if (self.health < -99) + self.health = -99; // don't let sbar get funky + + if (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE) { // doors, triggers, etc + self.th_die (); + self = oself; + return; + } + + self.enemy = attacker; + + // bump the monster counter + if (self.flags & FL_MONSTER) { + killed_monsters++; + WriteByte (MSG_ALL, SVC_KILLEDMONSTER); + } + ClientObituary (self, attacker); + + self.takedamage = DAMAGE_NO; + self.touch = NIL; + self.effects = 0; + +/*SERVER + monster_death_use(); +*/ + self.th_die (); + + self = oself; +}; + + +/* +============ +T_Damage +The damage is coming from inflictor, but get mad at attacker +This should be the only function that ever reduces health. +============ +*/ +void (entity targ, entity inflictor, entity attacker, float damage) T_Damage = +{ + local vector dir; + local entity oldself; + local float save; + local float take; + local string attackerteam, targteam; + + if (!targ.takedamage) + return; + + // used by buttons and triggers to set activator for target firing + damage_attacker = attacker; + + // check for quad damage powerup on the attacker + if (attacker.super_damage_finished > time && inflictor.classname != "door") + damage = damage * 4; + +//POX - this was moved from below the armour save routines to above so armour isn't lost + + // check for godmode or invincibility + if (targ.flags & FL_GODMODE) + return; + + if (targ.invincible_finished >= time && self.invincible_sound < time) { + sound (targ, CHAN_ITEM, "items/protect3.wav", 1, ATTN_NORM); + self.invincible_sound = time + 2; + return; + } + + // save damage based on the target's armor level + save = ceil (targ.armortype * damage); + if (save >= targ.armorvalue) { + save = targ.armorvalue; + targ.armortype = 0; // lost all armor + targ.items &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); + } + + targ.armorvalue -= save; + if (targ.armorvalue > 0) { + targ.armortype = 0.8; // all armor has same value + targ.items &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); + } + + // Change the color using only the value -- no different armor types + if (targ.armorvalue > 150) { // red + targ.items |= IT_ARMOR3; + } else if (targ.armorvalue > 50) { // yellow + targ.items |= IT_ARMOR2; + } else if (targ.armorvalue > 1) { // blue + targ.items |= IT_ARMOR2; + } + + take = ceil (damage - save); + +/* + Add to the damage total for clients, which will be sent as a single + message at the end of the frame +*/ + // FIXME: remove after combining shotgun blasts? + if (targ.flags & FL_CLIENT) { + targ.dmg_take += take; + targ.dmg_save += save; + targ.dmg_inflictor = inflictor; + } + damage_inflictor = inflictor; + + // figure momentum add + if ((inflictor != world) && (targ.movetype == MOVETYPE_WALK)) { + dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5; + dir = normalize (dir); + + if ((damage < 60) // player-player damage (not self-inflicted) + && (attacker.classname == "player") + && (targ.classname == "player") + && (attacker != targ)) + targ.velocity += dir * damage * 11; + else + targ.velocity += dir * damage * 8; + + // Rocket Jump modifiers + if ((rj > 1) && ((attacker.classname == "player") && (targ.classname == "player")) && (attacker == targ)) + targ.velocity = targ.velocity + dir * damage * rj; + } + + // team play damage avoidance + //ZOID 12-13-96: self.team doesn't work in QW. Use keys + attackerteam = infokey (attacker, "team"); + targteam = infokey (targ, "team"); + + if (((teamplay == 1) || (teamplay == 3)) + && (attacker.classname == "player") + && ((attackerteam) && (targteam == attackerteam) && (targ != attacker)) + && inflictor.classname != "door") + return; + + // do the damage + targ.health -= take; + if (targ.health <= 0) { + Killed (targ, attacker); + return; + } + + // react to the damage + oldself = self; + self = targ; + +#if 0 + if ((self.flags & FL_MONSTER) && attacker != world) { // get mad unless of the same class (except for soldiers) + if (self != attacker && attacker != self.enemy) { + if ((self.classname != attacker.classname) + || (self.classname == "monster_army" )) { + if (self.enemy.classname == "player") + self.oldenemy = self.enemy; + + self.enemy = attacker; + FoundTarget (); + } + } + } +#endif + + if (self.th_pain) + self.th_pain (); + + self = oldself; +}; + +/* +============ +T_RadiusDamage +============ +*/ +void (entity inflictor, + entity attacker, + float damage, + entity ignore, + string dtype) T_RadiusDamage = +{ + local float points; + local entity head; + local vector org; + + if ((head = findradius (inflictor.origin, damage + 40))) { + do { + if (head != ignore && head.takedamage) { + org = head.origin + (head.mins + head.maxs)*0.5; + points = 0.5 * vlen (inflictor.origin - org); + + if (points < 0) + points = 0; + + points = damage - points; + + if (head == attacker) + points *= 0.5; + + if (points > 0) { + if (CanDamage (head, inflictor)) { + head.deathtype = dtype; + T_Damage (head, inflictor, attacker, points); + } + } + } + head = head.chain; + } while (head); + } +}; + +/* +============ +T_BeamDamage +============ +*/ +void (entity attacker, float damage) T_BeamDamage = +{ + local float points; + local entity head; + + if ((head = findradius (attacker.origin, damage + 40))) { + do { + if (head.takedamage) { + points = 0.5 * vlen (attacker.origin - head.origin); + + if (points < 0) + points = 0; + + points = damage - points; + + if (head == attacker) + points *= 0.5; + + if (points > 0) { + if (CanDamage (head, attacker)) { + T_Damage (head, attacker, attacker, points); + } + } + } + head = head.chain; + } while (head); + } +}; diff --git a/ParoxysmII/source/config.nq b/ParoxysmII/source/config.nq new file mode 100644 index 0000000..51177e3 --- /dev/null +++ b/ParoxysmII/source/config.nq @@ -0,0 +1,37 @@ +/* + config.nq + + NetQuake-compatible config for Paroxysm II + + Copyright (C) 2001 Jeff Teunissen + + This program 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __config_nq_ +#define __config_nq_ + +#define NETQUAKE 1 + +#define BPRINT(a,b) bprint ((b)) +#define LOGFRAG(a,b) + +#endif // __config_nq_ diff --git a/ParoxysmII/source/config.qw b/ParoxysmII/source/config.qw new file mode 100644 index 0000000..900963c --- /dev/null +++ b/ParoxysmII/source/config.qw @@ -0,0 +1,37 @@ +/* + config.qw + + QuakeWorld-compatible config for Paroxysm II + + Copyright (C) 2001 Jeff Teunissen + + This program 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#ifndef __config_qw_ +#define __config_qw_ + +#define QUAKEWORLD 1 + +#define BPRINT(a,b) bprint ((a), (b)) +#define LOGFRAG(a,b) logfrag ((a), (b)) + +#endif // __config_qw_ diff --git a/ParoxysmII/source/defs.r b/ParoxysmII/source/defs.r new file mode 100644 index 0000000..55d3ac1 --- /dev/null +++ b/ParoxysmII/source/defs.r @@ -0,0 +1,616 @@ + +/* +============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + +============================================================================== +*/ + +// +// system globals +// +entity self; +entity other; +entity world; +float time; +float frametime; + +entity newmis; // if this is set, the entity that just + // run created a new missile that should + // be simulated immediately + +float force_retouch; // force all entities to touch triggers + // next frame. this is needed because + // non-moving things don't normally scan + // for triggers, and when a trigger is + // created (like a teleport trigger), it + // needs to catch everything. + // decremented each frame, so set to 2 + // to guarantee everything is touched +string mapname; + +float serverflags; // propagated from level to level, used to + // keep track of completed episodes + +float total_secrets; +float total_monsters; + +float found_secrets; // number of secrets found +float killed_monsters; // number of monsters killed + +// spawnparms are used to encode information about clients across server +// level changes +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; + +// +// global variables set by built in functions +// +vector v_forward, v_up, v_right; // set by makevectors() + +// set by traceline / tracebox +float trace_allsolid; +float trace_startsolid; +float trace_fraction; +vector trace_endpos; +vector trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; + +entity msg_entity; // destination of single entity writes + +// +// required prog functions +// +void() main; // only for testing + +void() StartFrame; + +void() PlayerPreThink; +void() PlayerPostThink; + +void() ClientKill; +void() ClientConnect; +void() PutClientInServer; // call after setting the parm1... parms +void() ClientDisconnect; + +void() SetNewParms; // called when a client first connects to + // a server. sets parms so they can be + // saved off for restarts + +void() SetChangeParms; // call to set parms for self so they can + // be saved for a level transition + + +//================================================ +void end_sys_globals; // flag for structure dumping +//================================================ + +/* +============================================================================== + + SOURCE FOR ENTVARS_T C STRUCTURE + +============================================================================== +*/ + +// +// system fields (*** = do not set in prog code, maintained by C code) +// +.float modelindex; // *** model index in the precached list +.vector absmin, absmax; // *** origin + mins / maxs + +.float ltime; // local time for entity +.float lastruntime; // *** to allow entities to run out of sequence + +.float movetype; +.float solid; + +.vector origin; // *** +.vector oldorigin; // *** +.vector velocity; +.vector angles; +.vector avelocity; + +.string classname; // spawn function +.string model; +.float frame; +.float skin; +.float effects; + +.vector mins, maxs; // bounding box extents reletive to origin +.vector size; // maxs - mins + +.void() touch; +.void() use; +.void() think; +.void() blocked; // for doors or plats, called when can't push other + +.float nextthink; +.entity groundentity; + + + +// stats +.float health; +.float frags; +.float weapon; // one of the IT_TSHOT, etc flags +.string weaponmodel; +.float weaponframe; +.float currentammo; +.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells; + +.float items; // bit flags + +.float takedamage; +.entity chain; +.float deadflag; + +.vector view_ofs; // add to origin to get eye point + + +.float button0; // fire +.float button1; // use +.float button2; // jump + +.float impulse; // weapon changes + +.float fixangle; +.vector v_angle; // view / targeting angle for players + +.string netname; + +.entity enemy; + +.float flags; + +.float colormap; +.float team; + +.float max_health; // players maximum health is stored here + +.float teleport_time; // don't back up + +.float armortype; // save this fraction of incoming damage +.float armorvalue; + +.float waterlevel; // 0 = not in, 1 = feet, 2 = wast, 3 = eyes +.float watertype; // a contents value + +.float ideal_yaw; +.float yaw_speed; + +.entity aiment; + +.entity goalentity; // a movetarget or an enemy + +.float spawnflags; + +.string target; +.string targetname; + +// damage is accumulated through a frame. and sent as one single +// message, so the super shotgun doesn't generate huge messages +.float dmg_take; +.float dmg_save; +.entity dmg_inflictor; + +.entity owner; // who launched a missile +.vector movedir; // mostly for doors, but also used for waterjump + +.string message; // trigger messages + +.float sounds; // either a cd track number or sound number + +.string noise, noise1, noise2, noise3; // contains names of wavs to play + +//================================================ +void end_sys_fields; // flag for structure dumping +//================================================ + +/* +============================================================================== + + VARS NOT REFERENCED BY C CODE + +============================================================================== +*/ + +// edict.solid values +float SOLID_NOT = 0; // no interaction with other objects +float SOLID_TRIGGER = 1; // touch on edge, but not blocking +float SOLID_BBOX = 2; // touch on edge, block +float SOLID_SLIDEBOX = 3; // touch on edge, but not an onground +float SOLID_BSP = 4; // bsp clip, touch on edge, block + +// range values +float RANGE_MELEE = 0; +float RANGE_NEAR = 1; +float RANGE_MID = 2; +float RANGE_FAR = 3; + +// deadflag values + +float DEAD_NO = 0; +float DEAD_DYING = 1; +float DEAD_DEAD = 2; +float DEAD_RESPAWNABLE = 3; + +// takedamage values + +float DAMAGE_NO = 0; +float DAMAGE_YES = 1; +float DAMAGE_AIM = 2; + +float STATE_TOP = 0; +float STATE_BOTTOM = 1; +float STATE_UP = 2; +float STATE_DOWN = 3; + +vector VEC_ORIGIN = '0 0 0'; +vector VEC_HULL_MIN = '-16 -16 -24'; +vector VEC_HULL_MAX = '16 16 32'; +vector VEC_HULL2_MIN = '-32 -32 -24'; +vector VEC_HULL2_MAX = '32 32 64'; + +// protocol bytes +float SVC_TEMPENTITY = 23; +float SVC_KILLEDMONSTER = 27; +float SVC_FOUNDSECRET = 28; +float SVC_INTERMISSION = 30; +float SVC_FINALE = 31; +float SVC_CDTRACK = 32; +float SVC_SELLSCREEN = 33; +float SVC_SMALLKICK = 34; +float SVC_BIGKICK = 35; +float SVC_MUZZLEFLASH = 39; + +float TE_SPIKE = 0; +float TE_SUPERSPIKE = 1; +float TE_GUNSHOT = 2; +float TE_EXPLOSION = 3; +float TE_TAREXPLOSION = 4; +float TE_LIGHTNING1 = 5; +float TE_LIGHTNING2 = 6; +float TE_WIZSPIKE = 7; +float TE_KNIGHTSPIKE = 8; +float TE_LIGHTNING3 = 9; +float TE_LAVASPLASH = 10; +float TE_TELEPORT = 11; +float TE_BLOOD = 12; +float TE_LIGHTNINGBLOOD = 13; + +// sound channels +// channel 0 never willingly overrides +// other channels (1-7) allways override a playing sound on that channel +float CHAN_AUTO = 0; +float CHAN_WEAPON = 1; +float CHAN_VOICE = 2; +float CHAN_ITEM = 3; +float CHAN_BODY = 4; +float CHAN_NO_PHS_ADD = 8; // ie: CHAN_BODY | CHAN_NO_PHS_ADD + +float ATTN_NONE = 0; // Attenuation +float ATTN_NORM = 1; +float ATTN_IDLE = 2; +float ATTN_STATIC = 3; + +// update types + +float UPDATE_GENERAL = 0; +float UPDATE_STATIC = 1; +float UPDATE_BINARY = 2; +float UPDATE_TEMP = 3; + +// entity effects + +//float EF_BRIGHTFIELD = 1; +//float EF_MUZZLEFLASH = 2; +float EF_BRIGHTLIGHT = 4; +float EF_DIMLIGHT = 8; +float EF_FLAG1 = 16; +float EF_FLAG2 = 32; + +// GLQuakeWorld Stuff +float EF_BLUE = 64; // Blue Globe effect for Quad +float EF_RED = 128; // Red Globe effect for Pentagram + +// messages +float MSG_BROADCAST = 0; // unreliable to all +float MSG_ONE = 1; // reliable to one (msg_entity) +float MSG_ALL = 2; // reliable to all +float MSG_INIT = 3; // write to the init string +float MSG_MULTICAST = 4; // for multicast() call + +// message levels +float PRINT_LOW = 0; // pickup messages +float PRINT_MEDIUM = 1; // death messages +float PRINT_HIGH = 2; // critical messages +float PRINT_CHAT = 3; // also goes to chat console + +// multicast sets +float MULTICAST_ALL = 0; // every client +float MULTICAST_PHS = 1; // within hearing +float MULTICAST_PVS = 2; // within sight +float MULTICAST_ALL_R = 3; // every client, reliable +float MULTICAST_PHS_R = 4; // within hearing, reliable +float MULTICAST_PVS_R = 5; // within sight, reliable + +//================================================ + +// +// globals +// +float movedist; + +string string_null; // null string, nothing should be held here +float empty_float; + +entity activator; // the entity that activated a trigger or brush + +entity damage_attacker; // set by T_Damage +entity damage_inflictor; +float framecount; + +// +// cvars checked each frame +// +float teamplay; +float timelimit; +float fraglimit; +float deathmatch; +float rj; + +//================================================ + +// +// world fields (FIXME: make globals) +// +.string wad; +.string map; +.float worldtype; // 0=medieval 1=metal 2=base + +//================================================ + +.string killtarget; + +// +// quakeed fields +// +.float light_lev; // not used by game, but parsed by light util +.float style; + +// +// monster ai +// +.void() th_stand; +.void() th_walk; +.void() th_run; +.void() th_missile; +.void() th_melee; +.void() th_pain; +.void() th_die; + +.entity oldenemy; // mad at this player before taking damage + +.float speed; + +.float lefty; + +.float search_time; +.float attack_state; + +float AS_STRAIGHT = 1; +float AS_SLIDING = 2; +float AS_MELEE = 3; +float AS_MISSILE = 4; + +// +// player only fields +// +.float voided; +.float walkframe; + +// Zoid Additions +.float maxspeed; // Used to set Maxspeed on a player +.float gravity; // Gravity Multiplier (0 to 1.0) + +.float attack_finished; +.float pain_finished; + +.float invincible_finished; +.float invisible_finished; +.float super_damage_finished; +.float radsuit_finished; + +.float invincible_time, invincible_sound; +.float invisible_time, invisible_sound; +.float super_time, super_sound; +.float rad_time; +.float fly_sound; + +.float axhitme; + +.float show_hostile; // set to time+0.2 whenever a client fires a + // weapon or takes damage. Used to alert + // monsters that otherwise would let the player go +.float jump_flag; // player jump flag +// + POX - removed +//.float swim_flag; // player swimming sound flag +// - POX +.float air_finished; // when time > air_finished, start drowning +.float bubble_count; // keeps track of the number of bubbles +.string deathtype; // keeps track of how the player died + +// +// object stuff +// +.string mdl; +.vector mangle; // angle at start + +.vector oldorigin; // only used by secret door + +.float t_length, t_width; + +// +// doors, etc +// +.vector dest, dest1, dest2; +.float wait; // time from firing to restarting +.float delay; // time from activation to firing +.entity trigger_field; // door's trigger entity +.string noise4; + +// +// monsters +// +.float pausetime; +.entity movetarget; + +// +// doors +// +.float aflag; +.float dmg; // damage done by door when hit + +// +// misc +// +.float cnt; // misc flag + +// +// subs +// +.void() think1; +.vector finaldest, finalangle; + +// +// triggers +// +.float count; // for counting triggers + + +// +// plats / doors / buttons +// +.float lip; +.float state; +.vector pos1, pos2; // top and bottom positions +.float height; + +// +// sounds +// +.float waitmin, waitmax; +.float distance; +.float volume; + + +//=========================================================================== + +// +// builtin functions +// + +void(string e) error = #10; +void(string e) objerror = #11; + +// sets trace_* globals +// nomonsters can be: +// An entity will also be ignored for testing if forent == test, +// forent->owner == test, or test->owner == forent +// a forent of world is ignored +void(vector v1, vector v2, float nomonsters, entity forent) traceline = #16; + +entity() checkclient = #17; // returns a client to look for +entity(entity start, .string fld, string match) find = #18; +void(entity client, string s)stuffcmd = #21; +entity(vector org, float rad) findradius = #22; +void(float level, string s) bprint = #23; +void(entity client, float level, string s) sprint = #24; +void(string s) dprint = #25; +string(float f) ftos = #26; +string(vector v) vtos = #27; +float(float yaw, float dist) walkmove = #32; // returns TRUE or FALSE +// #33 was removed +float() droptofloor= #34; // TRUE if landed on floor +void(float style, string value) lightstyle = #35; +// #39 was removed +float(entity e) checkbottom = #40; // true if self is on ground +float(vector v) pointcontents = #41; // returns a CONTENT_* +// #42 was removed +vector(entity e, float speed) aim = #44; // returns the shooting vector +void(string s) localcmd = #46; // put string into local que +entity(entity e) nextent = #47; // for looping through all ents +// #48 was removed +void() ChangeYaw = #49; // turn towards self.ideal_yaw + // at self.yaw_speed +// #50 was removed + +// +// direct client message generation +// +void(float to, float f) WriteByte = #52; +void(float to, float f) WriteChar = #53; +void(float to, float f) WriteShort = #54; +void(float to, float f) WriteLong = #55; +void(float to, float f) WriteCoord = #56; +void(float to, float f) WriteAngle = #57; +void(float to, string s) WriteString = #58; +void(float to, entity s) WriteEntity = #59; + +void(float step) movetogoal = #67; + +void(string s) changelevel = #70; + +//#71 was removed + +void(entity client, string s) centerprint = #73; // sprint, but in middle + +void(entity e, float chan, string samp, float vol, float atten) sound = #8; +void(vector pos, string samp, float vol, float atten) ambientsound = #74; + +string(string s) precache_sound = #19; +string(string s) precache_model = #20; +string(string s) precache_file = #68; // no effect except for -copy +string(string s) precache_model2 = #75; // registered version only +string(string s) precache_sound2 = #76; // registered version only +string(string s) precache_file2 = #77; // registered version only + +void(entity e) setspawnparms = #78; // set parm1... to the + // values at level start + // for coop respawn + +void(entity killer, entity killee) logfrag = #79; // add to stats + +float(string s) stof = #81; // convert string to float +void(vector where, float set) multicast = #82; // sends the temp message to a set + // of clients, possibly in PVS or PHS + +//============================================================================ + +// +// subs.qc +// +void(vector tdest, float tspeed, void() func) SUB_CalcMove; +void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt; +void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove; +void() SUB_CalcMoveDone; +void() SUB_CalcAngleMoveDone; +void() SUB_Null; +void() SUB_UseTargets; +void() SUB_Remove; + +// +// combat.qc +// +void(entity targ, entity inflictor, entity attacker, float damage) T_Damage; + + +float (entity e, float healamount, float ignore) T_Heal; // health function + +float(entity targ, entity inflictor) CanDamage; + + diff --git a/ParoxysmII/source/doors.r b/ParoxysmII/source/doors.r new file mode 100644 index 0000000..5c3a0bb --- /dev/null +++ b/ParoxysmII/source/doors.r @@ -0,0 +1,680 @@ +#include "config.rh" + +#include "paroxysm.rh" + +float DOOR_START_OPEN = 1; +float DOOR_DONT_LINK = 4; +float DOOR_GOLD_KEY = 8; +float DOOR_SILVER_KEY = 16; +float DOOR_TOGGLE = 32; +/* +Doors are similar to buttons, but can spawn a fat trigger field around them +to open without a touch, and they link together to form simultanious +double/quad doors. + +Door.owner is the master door. If there is only one door, it points to itself. +If multiple doors, all will point to a single one. +Door.enemy chains from the master door through all doors linked in the chain. +*/ +/* +============================================================================= +THINK FUNCTIONS +============================================================================= +*/ +void() door_go_down; +void() door_go_up; +void() door_blocked = +{ + other.deathtype = "squish"; + T_Damage (other, self, self.goalentity, self.dmg); +// if a door has a negative wait, it would never come back if blocked, +// so let it just squash the object to death real fast + if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + door_go_up (); + else + door_go_down (); + } +}; +void() door_hit_top = +{ + sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_TOP; + if (self.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + self.think = door_go_down; + self.nextthink = self.ltime + self.wait; +}; +void() door_hit_bottom = +{ + sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_BOTTOM; +}; +void() door_go_down = +{ + sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + + self.state = STATE_DOWN; + SUB_CalcMove (self.pos1, self.speed, door_hit_bottom); +}; +void() door_go_up = +{ + if (self.state == STATE_UP) + return; // allready going up + if (self.state == STATE_TOP) + { // reset top wait time + self.nextthink = self.ltime + self.wait; + return; + } + + sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + self.state = STATE_UP; + SUB_CalcMove (self.pos2, self.speed, door_hit_top); + SUB_UseTargets(); +}; +/* +============================================================================= +ACTIVATION FUNCTIONS +============================================================================= +*/ +void() door_fire = +{ + local entity oself; + local entity starte; + if (self.owner != self) + objerror ("door_fire: self.owner != self"); +// play use key sound + if (self.items) + sound (self, CHAN_VOICE, self.noise4, 1, ATTN_NORM); + self.message = string_null; // no more message + oself = self; + if (self.spawnflags & DOOR_TOGGLE) + { + if (self.state == STATE_UP || self.state == STATE_TOP) + { + starte = self; + do + { + door_go_down (); + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; + return; + } + } + +// trigger all paired doors + starte = self; + + do + { + self.goalentity = activator; // Who fired us + door_go_up (); + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; +}; +void() door_use = +{ + local entity oself; + self.message = ""; // door message are for touch only + self.owner.message = ""; + self.enemy.message = ""; + oself = self; + self = self.owner; + door_fire (); + self = oself; +}; +void() door_trigger_touch = +{ + if (other.health <= 0) + return; + if (time < self.attack_finished) + return; + self.attack_finished = time + 1; + activator = other; + self = self.owner; + door_use (); +}; +void() door_killed = +{ + local entity oself; + + oself = self; + self = self.owner; + self.health = self.max_health; + self.takedamage = DAMAGE_NO; // wil be reset upon return + door_use (); + self = oself; +}; +/* +================ +door_touch +Prints messages and opens key doors +================ +*/ +void() door_touch = +{ + if (other.classname != "player") + return; + if (self.owner.attack_finished > time) + return; + self.owner.attack_finished = time + 2; + if (self.owner.message != "") + { + self.target_id_finished = time + 4;//POX don't let TargetID override centerprints + centerprint (other, self.owner.message); + sound (other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); + } + +// key door stuff + if (!self.items) + return; +// FIXME: blink key on player's status bar + if ( (self.items & other.items) != self.items ) + { + self.target_id_finished = time + 4;//POX don't let TargetID override centerprints + + if (self.owner.items == IT_KEY1) + { + if (world.worldtype == 2) + { + centerprint (other, "You need the silver keycard"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 1) + { + centerprint (other, "You need the silver runekey"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 0) + { + centerprint (other, "You need the silver key"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + } + else + { + if (world.worldtype == 2) + { + centerprint (other, "You need the gold keycard"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 1) + { + centerprint (other, "You need the gold runekey"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 0) + { + centerprint (other, "You need the gold key"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + } + return; + } + other.items = other.items - self.items; + self.touch = SUB_Null; + if (self.enemy) + self.enemy.touch = SUB_Null; // get paired door + door_use (); +}; +/* +============================================================================= +SPAWNING FUNCTIONS +============================================================================= +*/ +entity(vector fmins, vector fmaxs) spawn_field = +{ + local entity trigger; + local vector t1, t2; + trigger = spawn(); + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.owner = self; + trigger.touch = door_trigger_touch; + t1 = fmins; + t2 = fmaxs; + setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); + return (trigger); +}; +float (entity e1, entity e2) EntitiesTouching = +{ + if (e1.mins_x > e2.maxs_x) + return FALSE; + if (e1.mins_y > e2.maxs_y) + return FALSE; + if (e1.mins_z > e2.maxs_z) + return FALSE; + if (e1.maxs_x < e2.mins_x) + return FALSE; + if (e1.maxs_y < e2.mins_y) + return FALSE; + if (e1.maxs_z < e2.mins_z) + return FALSE; + return TRUE; +}; +/* +============= +LinkDoors +============= +*/ +void() LinkDoors = +{ + local entity t, starte; + local vector cmins, cmaxs; + if (self.enemy) + return; // already linked by another door + if (self.spawnflags & 4) + { + self.owner = self.enemy = self; + return; // don't want to link this door + } + cmins = self.mins; + cmaxs = self.maxs; + + starte = self; + t = self; + + do + { + self.owner = starte; // master door + if (self.health) + starte.health = self.health; + if (self.targetname) + starte.targetname = self.targetname; + if (self.message != "") + starte.message = self.message; + t = find (t, classname, self.classname); + if (!t) + { + self.enemy = starte; // make the chain a loop + // shootable, fired, or key doors just needed the owner/enemy links, + // they don't spawn a field + + self = self.owner; + if (self.health) + return; + if (self.targetname) + return; + if (self.items) + return; + self.owner.trigger_field = spawn_field(cmins, cmaxs); + return; + } + if (EntitiesTouching(self,t)) + { + if (t.enemy) + objerror ("cross connected doors"); + + self.enemy = t; + self = t; + if (t.mins_x < cmins_x) + cmins_x = t.mins_x; + if (t.mins_y < cmins_y) + cmins_y = t.mins_y; + if (t.mins_z < cmins_z) + cmins_z = t.mins_z; + if (t.maxs_x > cmaxs_x) + cmaxs_x = t.maxs_x; + if (t.maxs_y > cmaxs_y) + cmaxs_y = t.maxs_y; + if (t.maxs_z > cmaxs_z) + cmaxs_z = t.maxs_z; + } + } while (1 ); +}; +/*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE +if two doors touch, they are assumed to be connected and operate as a unit. +TOGGLE causes the door to wait in both the start and end states for a trigger event. +START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors). +Key doors are allways wait -1. +"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet +"angle" determines the opening direction +"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. +"health" if set, door must be shot open +"speed" movement speed (100 default) +"wait" wait before returning (3 default, -1 = never return) +"lip" lip remaining at end of move (8 default) +"dmg" damage to inflict when blocked (2 default) +"sounds" +0) no sound +1) stone +2) base +3) stone chain +4) screechy metal +*/ +void() func_door = +{ + if (world.worldtype == 0) + { + precache_sound ("doors/medtry.wav"); + precache_sound ("doors/meduse.wav"); + self.noise3 = "doors/medtry.wav"; + self.noise4 = "doors/meduse.wav"; + } + else if (world.worldtype == 1) + { + precache_sound ("doors/runetry.wav"); + precache_sound ("doors/runeuse.wav"); + self.noise3 = "doors/runetry.wav"; + self.noise4 = "doors/runeuse.wav"; + } + else if (world.worldtype == 2) + { + precache_sound ("doors/basetry.wav"); + precache_sound ("doors/baseuse.wav"); + self.noise3 = "doors/basetry.wav"; + self.noise4 = "doors/baseuse.wav"; + } + else + { + dprint ("no worldtype set!\n"); + } + if (self.sounds == 0) + { + precache_sound ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = "misc/null.wav"; + self.noise2 = "misc/null.wav"; + } + if (self.sounds == 1) + { + precache_sound ("doors/drclos4.wav"); + precache_sound ("doors/doormv1.wav"); + self.noise1 = "doors/drclos4.wav"; + self.noise2 = "doors/doormv1.wav"; + } + if (self.sounds == 2) + { + precache_sound ("doors/hydro1.wav"); + precache_sound ("doors/hydro2.wav"); + self.noise2 = "doors/hydro1.wav"; + self.noise1 = "doors/hydro2.wav"; + } + if (self.sounds == 3) + { + precache_sound ("doors/stndr1.wav"); + precache_sound ("doors/stndr2.wav"); + self.noise2 = "doors/stndr1.wav"; + self.noise1 = "doors/stndr2.wav"; + } + if (self.sounds == 4) + { + precache_sound ("doors/ddoor1.wav"); + precache_sound ("doors/ddoor2.wav"); + self.noise1 = "doors/ddoor2.wav"; + self.noise2 = "doors/ddoor1.wav"; + } + SetMovedir (); + self.max_health = self.health; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + setorigin (self, self.origin); + setmodel (self, self.model); + self.classname = "door"; + self.blocked = door_blocked; + self.use = door_use; + + if (self.spawnflags & DOOR_SILVER_KEY) + self.items = IT_KEY1; + if (self.spawnflags & DOOR_GOLD_KEY) + self.items = IT_KEY2; + + if (!self.speed) + self.speed = 100; + if (!self.wait) + self.wait = 3; + if (!self.lip) + self.lip = 8; + if (!self.dmg) + self.dmg = 2; + self.pos1 = self.origin; + self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (self.spawnflags & DOOR_START_OPEN) + { + setorigin (self, self.pos2); + self.pos2 = self.pos1; + self.pos1 = self.origin; + } + self.state = STATE_BOTTOM; + if (self.health) + { + self.takedamage = DAMAGE_YES; + self.th_die = door_killed; + } + + if (self.items) + self.wait = -1; + + self.touch = door_touch; +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + self.think = LinkDoors; + self.nextthink = self.ltime + 0.1; +}; +/* +============================================================================= +SECRET DOORS +============================================================================= +*/ +void() fd_secret_move1; +void() fd_secret_move2; +void() fd_secret_move3; +void() fd_secret_move4; +void() fd_secret_move5; +void() fd_secret_move6; +void() fd_secret_done; +float SECRET_OPEN_ONCE = 1; // stays open +float SECRET_1ST_LEFT = 2; // 1st move is left of arrow +float SECRET_1ST_DOWN = 4; // 1st move is down from arrow +float SECRET_NO_SHOOT = 8; // only opened by trigger +float SECRET_YES_SHOOT = 16; // shootable even if targeted +void () fd_secret_use = +{ + local float temp; + + self.health = 10000; + // exit if still moving around... + if (self.origin != self.oldorigin) + return; + + self.message = string_null; // no more message + SUB_UseTargets(); // fire all targets / killtargets + + if (!(self.spawnflags & SECRET_NO_SHOOT)) + { + self.th_pain = SUB_Null; + self.takedamage = DAMAGE_NO; + } + self.velocity = '0 0 0'; + // Make a sound, wait a little... + + sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.nextthink = self.ltime + 0.1; + temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1 + makevectors(self.mangle); + + if (!self.t_width) + { + if (self.spawnflags & SECRET_1ST_DOWN) + self. t_width = fabs(v_up * self.size); + else + self. t_width = fabs(v_right * self.size); + } + + if (!self.t_length) + self. t_length = fabs(v_forward * self.size); + if (self.spawnflags & SECRET_1ST_DOWN) + self.dest1 = self.origin - v_up * self.t_width; + else + self.dest1 = self.origin + v_right * (self.t_width * temp); + + self.dest2 = self.dest1 + v_forward * self.t_length; + SUB_CalcMove(self.dest1, self.speed, fd_secret_move1); + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); +}; +// Wait after first movement... +void () fd_secret_move1 = +{ + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move2; + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); +}; +// Start moving sideways w/sound... +void () fd_secret_move2 = +{ + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + SUB_CalcMove(self.dest2, self.speed, fd_secret_move3); +}; +// Wait here until time to go back... +void () fd_secret_move3 = +{ + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + if (!(self.spawnflags & SECRET_OPEN_ONCE)) + { + self.nextthink = self.ltime + self.wait; + self.think = fd_secret_move4; + } +}; +// Move backward... +void () fd_secret_move4 = +{ + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + SUB_CalcMove(self.dest1, self.speed, fd_secret_move5); +}; +// Wait 1 second... +void () fd_secret_move5 = +{ + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move6; + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); +}; +void () fd_secret_move6 = +{ + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done); +}; +void () fd_secret_done = +{ + if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + self.th_pain = fd_secret_use; + self.th_die = fd_secret_use; + } + sound(self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise3, 1, ATTN_NORM); +}; +void () secret_blocked = +{ + if (time < self.attack_finished) + return; + self.attack_finished = time + 0.5; + other.deathtype = "squish"; + T_Damage (other, self, self, self.dmg); +}; +/* +================ +secret_touch +Prints messages +================ +*/ +void() secret_touch = +{ + if (other.classname != "player") + return; + if (self.attack_finished > time) + return; + self.attack_finished = time + 2; + + if (self.message) + { + self.target_id_finished = time + 4;//POX don't let TargetID override centerprints + + centerprint (other, self.message); + sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM); + } +}; +/*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot +Basic secret door. Slides back, then to the side. Angle determines direction. +wait = # of seconds before coming back +1st_left = 1st move is left of arrow +1st_down = 1st move is down from arrow +always_shoot = even if targeted, keep shootable +t_width = override WIDTH to move back (or height if going down) +t_length = override LENGTH to move sideways +"dmg" damage to inflict when blocked (2 default) +If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. +"sounds" +1) medieval +2) metal +3) base +*/ +void () func_door_secret = +{ + if (self.sounds == 0) + self.sounds = 3; + if (self.sounds == 1) + { + precache_sound ("doors/latch2.wav"); + precache_sound ("doors/winch2.wav"); + precache_sound ("doors/drclos4.wav"); + self.noise1 = "doors/latch2.wav"; + self.noise2 = "doors/winch2.wav"; + self.noise3 = "doors/drclos4.wav"; + } + if (self.sounds == 2) + { + precache_sound ("doors/airdoor1.wav"); + precache_sound ("doors/airdoor2.wav"); + self.noise2 = "doors/airdoor1.wav"; + self.noise1 = "doors/airdoor2.wav"; + self.noise3 = "doors/airdoor2.wav"; + } + if (self.sounds == 3) + { + precache_sound ("doors/basesec1.wav"); + precache_sound ("doors/basesec2.wav"); + self.noise2 = "doors/basesec1.wav"; + self.noise1 = "doors/basesec2.wav"; + self.noise3 = "doors/basesec2.wav"; + } + if (!self.dmg) + self.dmg = 2; + + // Magic formula... + self.mangle = self.angles; + self.angles = '0 0 0'; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.classname = "door"; + setmodel (self, self.model); + setorigin (self, self.origin); + + self.touch = secret_touch; + self.blocked = secret_blocked; + self.speed = 50; + self.use = fd_secret_use; + if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + self.th_pain = fd_secret_use; + + // + POX + self.nobleed = TRUE; + // - POX + } + self.oldorigin = self.origin; + if (!self.wait) + self.wait = 5; // 5 seconds before closing +}; diff --git a/ParoxysmII/source/dynlight.r b/ParoxysmII/source/dynlight.r new file mode 100644 index 0000000..f007e7d --- /dev/null +++ b/ParoxysmII/source/dynlight.r @@ -0,0 +1,105 @@ +#include "config.rh" + +#include "paroxysm.rh" + +/* +Frank Condello 09.28.98 - originally for the M.A.H.E.M. MacQuake Mod. +Now used in paroxysm +EMAIL: pox@planetquake.com +WEB: http://www.planetquake.com/paroxysm/ +========================================================================= +This is the code for Moving Light Entities (Dynamic Lights) +It's basically identical to the the func_train code with out +stuff like damage and blocked code used for solid objects. +The light follows Targets and waits just like a train. +A new variable (dynlight_style) defines the type of light emitted. +Quake has only three dynamic light styles: +1 - Dimlight +2 - Brightlight +3 - Brightfield (Brightlight with 'shimmering' particles) +Dynamic Light can also be triggered, They currenty only start turned off. +*/ +.float dynlight_style; +void() dynlight_next; +void() dynlight_find; +void() start_dynlight = +{ +//POX v1.1 - changed this for QW support +//No EF_BRIGHTFIELD in QW - defaults to EF_DIMLIGHT + if (self.dynlight_style == 2) + self.effects = self.effects | EF_BRIGHTLIGHT; + + else if (self.dynlight_style == 4) + self.effects = self.effects | EF_BLUE; + + else if (self.dynlight_style == 5) + self.effects = self.effects | EF_RED; + + else + self.effects = self.effects | EF_DIMLIGHT; + + + dynlight_next(); +}; +void() dynlight_wait = +{ + if (self.wait) + self.nextthink = self.ltime + self.wait; + else + self.nextthink = self.ltime + 0.1; + + self.think = dynlight_next; +}; +void() dynlight_next = +{ + local entity targ; + targ = find (world, targetname, self.target); + self.target = targ.target; + if (!self.target) + objerror ("dynlight_next: no next target"); + if (targ.wait) + self.wait = targ.wait; + else + self.wait = 0; + SUB_CalcMove (targ.origin - self.mins, self.speed, dynlight_wait); +}; +void() dynlight_find = +{ + local entity targ; + targ = find (world, targetname, self.target); + self.target = targ.target; + setorigin (self, targ.origin - self.mins); + if (!self.targetname) + { // not triggered, so start immediately + self.nextthink = self.ltime + 0.1; + self.think = start_dynlight; + } +}; +void() dynlight_use = +{ + if (self.think != dynlight_find) + return; // already activated + start_dynlight(); +}; +void() dyn_light = +{ + precache_model ("progs/null.spr"); + if (!self.speed) + self.speed = 100; + + if (!self.target) + objerror ("dyn_light without a target"); + + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_PUSH; + self.use = dynlight_use; + self.classname = "dynlight"; + + setmodel (self, "progs/null.spr"); + setsize (self, '0 0 0', '0 0 0'); + setorigin (self, self.origin); +// start trains on the second frame, to make sure their targets have had +// a chance to spawn + self.nextthink = self.ltime + 0.1; + self.think = dynlight_find; +}; diff --git a/ParoxysmII/source/flash.r b/ParoxysmII/source/flash.r new file mode 100644 index 0000000..5df628b --- /dev/null +++ b/ParoxysmII/source/flash.r @@ -0,0 +1,79 @@ +#include "config.rh" + +#include "paroxysm.rh" + +/* +POX - Flashlight code from the Flashlight Tutorial at the Inside3D website +Created by ShockMan eMail: shockman@brutality.com +Added an entity attribute to the spawn function for bot support (since self is only the bot at respwan) +*/ +void() flash_update = +{ + // The Player is dead so turn the Flashlight off + if (self.owner.deadflag != DEAD_NO) + self.effects = 0; + // The Player is alive so turn On the Flashlight + else + self.effects = EF_DIMLIGHT; + // Find out which direction player facing + makevectors (self.owner.v_angle); + // Check if there is any things infront of the flashlight + traceline (self.owner.origin , (self.owner.origin+(v_forward * 500)) , FALSE , self); + // Set the Flashlight's position + setorigin (self, trace_endpos+(v_forward * -5)); + // Repeat it in 0.02 seconds... + self.nextthink = time + 0.02; +}; +void(entity me) flash_on = +{ + // Make a new entity to hold the Flashlight + local entity myflash; + // spawn flash + myflash = spawn (); + myflash.movetype = MOVETYPE_NONE; + myflash.solid = SOLID_NOT; + // this uses the s_bubble.spr, if you want it to be completly + // invisible you need to create a one pixel trancparent spirit + // and use it here... + + //POX - changed it to a null sprite + setmodel (myflash, "progs/null.spr"); + setsize (myflash, '0 0 0', '0 0 0'); + // Wire Player And Flashlight Together + myflash.owner = me; + me.flash = myflash; + + // give the flash a Name And Make It Glow + myflash.classname = "flash"; + myflash.effects = EF_DIMLIGHT; + + // Set Start Position + makevectors (self.v_angle); + traceline (self.origin , (self.origin+(v_forward * 500)) , FALSE , self); + setorigin (myflash, trace_endpos); + // Start Flashlight Update + myflash.think = flash_update; + myflash.nextthink = time + 0.02; +}; +//POX - the user toggle is not implemented (auto light for Darkmode only) +/* + +void () flash_toggle = +{ + // If Off, Turn On + if (self.flash_flag == FALSE) + { + self.flash_flag = TRUE; + flash_on(); + } + // If On, Turn Off + else + { + self.flash_flag = FALSE; + W_SetCurrentAmmo (); + self.flash.think = SUB_Remove; + self.flash.nextthink = time + 0.1; + } +}; + +*/ diff --git a/ParoxysmII/source/items.r b/ParoxysmII/source/items.r new file mode 100644 index 0000000..1e920b9 --- /dev/null +++ b/ParoxysmII/source/items.r @@ -0,0 +1,1476 @@ + +#include "paroxysm.rh" + +// POX - had to make room for Paroxysm's DM modes...... +void() W_SetCurrentAmmo; +/* ALL LIGHTS SHOULD BE 0 1 0 IN COLOR ALL OTHER ITEMS SHOULD +BE .8 .3 .4 IN COLOR */ +.entity quadcore; // + POX - used by the dual model quad +void() SUB_regen = +{ + self.model = self.mdl; // restore original model + self.solid = SOLID_TRIGGER; // allow it to be touched again + sound (self, CHAN_VOICE, "items/itembk2.wav", 1, ATTN_NORM); // play respawn sound + setorigin (self, self.origin); + + // + POX - dual model quad... + if (self.classname == "item_artifact_super_damage") { + self.quadcore.model = self.quadcore.mdl; + setorigin (self.quadcore, self.quadcore.origin); + } + // - POX +}; +/*QUAKED noclass (0 0 0) (-8 -8 -8) (8 8 8) +prints a warning message when spawned +*/ +void() noclass = +{ + dprint ("noclass spawned at"); + dprint (vtos(self.origin)); + dprint ("\n"); + remove (self); +}; +void() q_touch; +void() q_touch = +{ + local string s; + + if (other.classname != "player") + return; + if (other.health <= 0) + return; + self.mdl = self.model; + sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + self.solid = SOLID_NOT; + other.items = other.items | IT_QUAD; + self.model = string_null; + //if (deathmatch == 4) + //{ + // other.armortype = 0; + // other.armorvalue = 0 * 0.01; + // other.ammo_cells = 0; + //} +// do the apropriate action + other.super_time = 1; + other.super_damage_finished = self.cnt; + s=ftos(rint(other.super_damage_finished - time)); + bprint (PRINT_LOW, other.netname); + //if (deathmatch == 4) + // bprint (PRINT_LOW, " recovered an OctaPower with "); + //else + bprint (PRINT_LOW, " recovered a Quad with "); + bprint (PRINT_LOW, s); + bprint (PRINT_LOW, " seconds remaining!\n"); + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + +void(float timeleft) DropQuad = +{ + local entity item; + + item = spawn (); + item.origin = self.origin; + + item.velocity_z = 300; + item.velocity_x = -100 + (random() * 200); + item.velocity_y = -100 + (random() * 200); + + item.solid = SOLID_TRIGGER; + item.movetype = MOVETYPE_TOSS; + setmodel (item, "progs/poxquad2.mdl"); + setsize (item, '-16 -16 -24', '16 16 32'); + + item.effects = EF_BLUE; + item.flags = FL_ITEM; + item.noise = "items/damage.wav"; + item.cnt = time + timeleft; + item.touch = q_touch; + item.nextthink = time + timeleft; // remove it with the time left on it + item.think = SUB_Remove; +}; + +void() r_touch; +void() r_touch = +{ + local string s; + if (other.classname != "player") + return; + if (other.health <= 0) + return; + self.mdl = self.model; + sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + self.solid = SOLID_NOT; + other.items = other.items | IT_INVISIBILITY; + self.model = string_null; +// do the apropriate action + other.invisible_time = 1; + other.invisible_finished = self.cnt; + s=ftos(rint(other.invisible_finished - time)); + bprint (PRINT_LOW, other.netname); + bprint (PRINT_LOW, " recovered a Ring with "); + bprint (PRINT_LOW, s); + bprint (PRINT_LOW, " seconds remaining!\n"); + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; +void(float timeleft) DropRing = +{ + local entity item; + item = spawn(); + item.origin = self.origin; + + item.velocity_z = 300; + item.velocity_x = -100 + (random() * 200); + item.velocity_y = -100 + (random() * 200); + + item.flags = FL_ITEM; + item.solid = SOLID_TRIGGER; + item.movetype = MOVETYPE_TOSS; + item.noise = "items/inv1.wav"; + setmodel (item, "progs/invisibl.mdl"); + setsize (item, '-16 -16 -24', '16 16 32'); + item.cnt = time + timeleft; + item.touch = r_touch; + item.nextthink = time + timeleft; // remove after 30 seconds + item.think = SUB_Remove; +}; +/* +============ +PlaceItem +plants the object on the floor +============ +*/ +void() PlaceItem = +{ + local float oldz; + self.mdl = self.model; // so it can be restored on respawn + self.flags = FL_ITEM; // make extra wide + self.solid = SOLID_TRIGGER; + self.movetype = MOVETYPE_TOSS; + self.velocity = '0 0 0'; + self.origin_z = self.origin_z + 6; + oldz = self.origin_z; + if (!droptofloor()) + { + dprint ("Bonus item fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + return; + } +}; +/* +============ +StartItem +Sets the clipping size and plants the object on the floor +============ +*/ +void() StartItem = +{ + self.nextthink = time + 0.2; // items start after other solids + self.think = PlaceItem; +}; +/* +========================================================================= +HEALTH BOX +========================================================================= +*/ +// +// T_Heal: add health to an entity, limiting health to max_health +// "ignore" will ignore max_health limit +// +float (entity e, float healamount, float ignore) T_Heal = +{ + if (e.health <= 0) + return 0; + if ((!ignore) && (e.health >= other.max_health)) + return 0; + healamount = ceil(healamount); + e.health = e.health + healamount; + if ((!ignore) && (e.health >= other.max_health)) + e.health = other.max_health; + + if (e.health > 250) + e.health = 250; + return 1; +}; +/*QUAKED item_health (.3 .3 1) (0 0 0) (32 32 32) rotten megahealth +Health box. Normally gives 25 points. +Rotten box heals 5-10 points, +megahealth will add 100 health, then +rot you down to your maximum health limit, +one point per second. +*/ +float H_ROTTEN = 1; +float H_MEGA = 2; +.float healamount, healtype; +void() health_touch; +void() item_megahealth_rot; +void() item_health = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + self.touch = health_touch; + if (self.spawnflags & H_ROTTEN) + { + precache_model("maps/b_bh10.bsp"); + precache_sound("items/health1.wav"); + setmodel(self, "maps/b_bh10.bsp"); + self.noise = "items/health1.wav"; + self.healamount = 15; + self.healtype = 0; + } + else + if (self.spawnflags & H_MEGA) + { + precache_model("maps/b_bh100.bsp"); + precache_sound("items/r_item2.wav"); + setmodel(self, "maps/b_bh100.bsp"); + self.noise = "items/r_item2.wav"; + self.healamount = 100; + self.healtype = 2; + } + else + { + precache_model("maps/b_bh25.bsp"); + precache_sound("items/health1.wav"); + setmodel(self, "maps/b_bh25.bsp"); + self.noise = "items/health1.wav"; + self.healamount = 25; + self.healtype = 1; + } + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; +void() health_touch = +{ + local string s; + + //if (deathmatch == 4) + // if (other.invincible_time > 0) + // return; + if (other.classname != "player") + return; + + if (self.healtype == 2) // Megahealth? Ignore max_health... + { + if (other.health >= 250) + return; + if (!T_Heal(other, self.healamount, 1)) + return; + } + else + { + if (!T_Heal(other, self.healamount, 0)) + return; + } + + sprint(other, PRINT_LOW, "You receive "); + s = ftos(self.healamount); + sprint(other, PRINT_LOW, s); + sprint(other, PRINT_LOW, " health\n"); + +// health touch sound + sound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + + self.model = string_null; + self.solid = SOLID_NOT; + // Megahealth = rot down the player's super health + if (self.healtype == 2) + { + other.items = other.items | IT_SUPERHEALTH; + //if (deathmatch != 4) + //{ + self.nextthink = time + 5; + self.think = item_megahealth_rot; + //} + self.owner = other; + } + else + { + //if (deathmatch != 2) // deathmatch 2 is the silly old rules + //{ + self.nextthink = time + 20; + self.think = SUB_regen; + //} + } + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; +void() item_megahealth_rot = +{ + other = self.owner; + + if (other.health > other.max_health) + { + other.health = other.health - 1; + self.nextthink = time + 1; + return; + } +// it is possible for a player to die and respawn between rots, so don't +// just blindly subtract the flag off + other.items = other.items - (other.items & IT_SUPERHEALTH); + + //if (deathmatch != 2) // deathmatch 2 is silly old rules + //{ + self.nextthink = time + 20; + self.think = SUB_regen; + //} +}; +/* + POX - see sheilds.qc +//ARMOR +/* - POX +/* +=============================================================================== +WEAPONS +=============================================================================== +*/ +void() bound_other_ammo = +{ + if (other.ammo_shells > 100) + other.ammo_shells = 100; + if (other.ammo_nails > 200) + other.ammo_nails = 200; + if (other.ammo_rockets > 100) + other.ammo_rockets = 100; + //POX - Max200 cells (changed from 100) + if (other.ammo_cells > 200) + other.ammo_cells = 200; +}; +float(float w) RankForWeapon = +{ + if (w == IT_LIGHTNING) + return 1; + if (w == IT_ROCKET_LAUNCHER) + return 2; + if (w == IT_SUPER_NAILGUN) + return 3; + if (w == IT_GRENADE_LAUNCHER) + return 4; + if (w == IT_COMBOGUN) + return 5; + if (w == IT_PLASMAGUN) + return 6; + return 7; +}; +float (float w) WeaponCode = +{ + if (w == IT_COMBOGUN) + return 3; + if (w == IT_PLASMAGUN) + return 4; + if (w == IT_SUPER_NAILGUN) + return 5; + if (w == IT_GRENADE_LAUNCHER) + return 6; + if (w == IT_ROCKET_LAUNCHER) + return 7; + if (w == IT_LIGHTNING) + return 8; + return 1; +}; +/* +============= +Deathmatch_Weapon +Deathmatch weapon change rules for picking up a weapon +.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells; +============= +*/ +void(float old, float new) Deathmatch_Weapon = +{ + local float or, nr; +// change self.weapon if desired + or = RankForWeapon (self.weapon); + nr = RankForWeapon (new); + if ( nr < or ) + self.weapon = new; +}; +/* +============= +weapon_touch +============= +*/ +float() W_BestWeapon; +void() weapon_touch = +{ + local float hadammo, best, new = NIL, old; + local entity stemp; + local float leave = NIL; + // For client weapon_switch + local float w_switch; + if (!(other.flags & FL_CLIENT)) + return; + if ((stof(infokey(other,"w_switch"))) == 0) + w_switch = 8; + else + w_switch = stof(infokey(other,"w_switch")); + +// if the player was using his best weapon, change up to the new one if better + stemp = self; + self = other; + best = W_BestWeapon(); + self = stemp; +// POX - leave is useless in POX since weapons are never allowed to be picked up if posessed + //if (deathmatch == 2 || deathmatch == 3 || deathmatch == 5) + // leave = 1; + //else + // leave = 0; +// POX - Don't bother checking if weapon is in inventory + if (other.items & self.weapon) + { + activator = other; + SUB_UseTargets(); //Just in case it's required to get out of somewhere + return; + } +// POX- changed classnames to constants + switch (self.weapon) { + case IT_PLASMAGUN: + hadammo = other.ammo_rockets; + new = IT_PLASMAGUN; + other.ammo_cells += 22; + break; + case IT_SUPER_NAILGUN: + hadammo = other.ammo_rockets; + new = IT_SUPER_NAILGUN; + other.ammo_nails += 30; + break; + case IT_COMBOGUN: + hadammo = other.ammo_rockets; + new = IT_COMBOGUN; + other.ammo_shells += 5; + other.ammo_rockets += 2; + other.which_ammo = 0; // Change ammo to shells if set to rockets + break; + case IT_ROCKET_LAUNCHER: + hadammo = other.ammo_rockets; + new = IT_ROCKET_LAUNCHER; + other.ammo_rockets += 5; + break; + case IT_GRENADE_LAUNCHER: + hadammo = other.ammo_rockets; + new = IT_GRENADE_LAUNCHER; + other.ammo_rockets += 5; + break; + default: + objerror ("weapon_touch: Unknown classname."); + } + + sprint (other, PRINT_LOW, "You got the "); + sprint (other, PRINT_LOW, self.netname); + sprint (other, PRINT_LOW, "\n"); +// weapon touch sound + sound (other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + bound_other_ammo (); +// change to the weapon + old = other.items; + other.items = other.items | new; + + stemp = self; + self = other; + //POX - check for autoswitch + if (deathmatch & DM_AUTOSWITCH) + { + if ( WeaponCode(new) <= w_switch ) + { + if (self.flags & FL_INWATER) + { + if (new != IT_LIGHTNING) + { + Deathmatch_Weapon (old, new); + } + } + else + { + Deathmatch_Weapon (old, new); + } + } + } + else + self.weapon = new; + W_SetCurrentAmmo(); + self = stemp; + if (leave) + return; + //if (deathmatch!=3 || deathmatch !=5) + //{ + // remove it in single player, or setup for respawning in deathmatch + self.model = string_null; + self.solid = SOLID_NOT; + //if (deathmatch != 2) + self.nextthink = time + 30; + self.think = SUB_regen; + //} + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; +// + POX - models/netnames changed +/*QUAKED weapon_supershotgun (0 .5 .8) (-16 -16 0) (16 16 32) +*/ +void() weapon_supershotgun = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + precache_model ("progs/g_combo.mdl"); + setmodel (self, "progs/g_combo.mdl"); + self.weapon = IT_COMBOGUN; + self.netname = "Combo Gun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; +/*QUAKED weapon_nailgun (0 .5 .8) (-16 -16 0) (16 16 32) +*/ +void() weapon_nailgun = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + precache_model ("progs/g_plasma.mdl"); + setmodel (self, "progs/g_plasma.mdl"); + self.weapon = IT_PLASMAGUN; + self.netname = "Plasma Gun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; +/*QUAKED weapon_supernailgun (0 .5 .8) (-16 -16 0) (16 16 32) +*/ +void() weapon_supernailgun = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + precache_model ("progs/g_nailg.mdl"); + setmodel (self, "progs/g_nailg.mdl"); + self.weapon = IT_SUPER_NAILGUN; + self.netname = "Nailgun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; +/*QUAKED weapon_grenadelauncher (0 .5 .8) (-16 -16 0) (16 16 32) +*/ +void() weapon_grenadelauncher = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + precache_model ("progs/g_gren.mdl"); + setmodel (self, "progs/g_gren.mdl"); + self.weapon = IT_GRENADE_LAUNCHER; + self.netname = "Grenade Launcher"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; +/*QUAKED weapon_rocketlauncher (0 .5 .8) (-16 -16 0) (16 16 32) +*/ +void() weapon_rocketlauncher = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + precache_model ("progs/g_rhino.mdl"); + setmodel (self, "progs/g_rhino.mdl"); + self.weapon = IT_ROCKET_LAUNCHER; + self.netname = "Anihilator"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; +// + POX - PlasmaGun also replaces Thunderbolt in existing levels +void() weapon_lightning = +{ + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + precache_model ("progs/g_plasma.mdl"); + setmodel (self, "progs/g_plasma.mdl"); + self.weapon = IT_PLASMAGUN; + self.netname = "Plasma Gun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; +/* +=============================================================================== +AMMO +=============================================================================== +*/ +void() ammo_touch = +{ +local entity stemp; +local float best; + if (other.classname != "player") + return; + if (other.health <= 0) + return; +// if the player was using his best weapon, change up to the new one if better + stemp = self; + self = other; + best = W_BestWeapon(); + self = stemp; +// shotgun + if (self.weapon == 1) + { + if (other.ammo_shells >= 100) + return; + other.ammo_shells = other.ammo_shells + self.aflag; + //+ POX - switch ammo to shells for ComboGun + other.which_ammo = 0; + } +// spikes + if (self.weapon == 2) + { + if (other.ammo_nails >= 200) + return; + other.ammo_nails = other.ammo_nails + self.aflag; + } +// rockets + if (self.weapon == 3) + { + if (other.ammo_rockets >= 100) + return; + other.ammo_rockets = other.ammo_rockets + self.aflag; + } +// cells + if (self.weapon == 4) + { + // + POX - changed max cells to 200 + if (other.ammo_cells >= 200) + return; + other.ammo_cells = other.ammo_cells + self.aflag; + } + bound_other_ammo (); + + sprint (other, PRINT_LOW, "You got the "); + sprint (other, PRINT_LOW, self.netname); + sprint (other, PRINT_LOW, "\n"); +// ammo touch sound + sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); +// change to a better weapon if appropriate +if (deathmatch & DM_AUTOSWITCH) +{ + if ( other.weapon == best ) + { + stemp = self; + self = other; + self.weapon = W_BestWeapon(); + W_SetCurrentAmmo (); + self = stemp; + } +} +// if changed current ammo, update it + stemp = self; + self = other; + W_SetCurrentAmmo(); + self = stemp; +// remove it in single player, or setup for respawning in deathmatch + self.model = string_null; + self.solid = SOLID_NOT; + //if (deathmatch != 2) + self.nextthink = time + 30; +// Xian -- If playing in DM 3.0 mode, halve the time ammo respawns +// if (deathmatch == 3 || deathmatch == 5) +// self.nextthink = time + 15; + self.think = SUB_regen; + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; +float WEAPON_BIG2 = 1; +/*QUAKED item_shells (0 .5 .8) (0 0 0) (32 32 32) big +*/ +void() item_shells = +{ + //if (deathmatch == 4) + // return; + + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + self.touch = ammo_touch; + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_shell1.bsp"); + setmodel (self, "maps/bspmdls/b_shell1.bsp"); + self.aflag = 40; + } + else + { + precache_model ("maps/bspmdls/b_shell0.bsp"); + setmodel (self, "maps/bspmdls/b_shell0.bsp"); + self.aflag = 20; + } + self.weapon = 1; + self.netname = "shells"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; +/*QUAKED item_spikes (0 .5 .8) (0 0 0) (32 32 32) big +*/ +void() item_spikes = +{ + //if (deathmatch == 4) + // return; + + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + self.touch = ammo_touch; + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_nail1.bsp"); + setmodel (self, "maps/bspmdls/b_nail1.bsp"); + self.aflag = 50; + } + else + { + precache_model ("maps/bspmdls/b_nail0.bsp"); + setmodel (self, "maps/bspmdls/b_nail0.bsp"); + self.aflag = 25; + } + self.weapon = 2; + self.netname = "nails"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; +/*QUAKED item_rockets (0 .5 .8) (0 0 0) (32 32 32) big +*/ +void() item_rockets = +{ + //if (deathmatch == 4) + // return; + + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + self.touch = ammo_touch; + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_rock1.bsp"); + setmodel (self, "maps/bspmdls/b_rock1.bsp"); + self.aflag = 10; + } + else + { + precache_model ("maps/bspmdls/b_rock0.bsp"); + setmodel (self, "maps/bspmdls/b_rock0.bsp"); + self.aflag = 5; + } + self.weapon = 3; + self.netname = "rockets"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; +/*QUAKED item_cells (0 .5 .8) (0 0 0) (32 32 32) big +*/ +void() item_cells = +{ + //if (deathmatch == 4) + // return; + + // + POX - no items in FFA mode + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + self.touch = ammo_touch; + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_batt1.bsp"); + setmodel (self, "maps/bspmdls/b_batt1.bsp"); + self.aflag = 12; + } + else + { + precache_model ("maps/bspmdls/b_batt0.bsp"); + setmodel (self, "maps/bspmdls/b_batt0.bsp"); + self.aflag = 6; + } + self.weapon = 4; + self.netname = "cells"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; +/*QUAKED item_weapon (0 .5 .8) (0 0 0) (32 32 32) shotgun rocket spikes big +DO NOT USE THIS!!!! IT WILL BE REMOVED! +*/ +float WEAPON_SHOTGUN = 1; +float WEAPON_ROCKET = 2; +float WEAPON_SPIKES = 4; +float WEAPON_BIG = 8; +void() item_weapon = +{ + // + POX - no items in FFA mode (just in case, for older maps) + if (deathmatch & DM_FFA) + { + remove(self); + return; + } + + self.touch = ammo_touch; + if (self.spawnflags & WEAPON_SHOTGUN) + { + if (self.spawnflags & WEAPON_BIG) + { + precache_model ("maps/bspmdls/b_shell1.bsp"); + setmodel (self, "maps/bspmdls/b_shell1.bsp"); + self.aflag = 40; + } + else + { + precache_model ("maps/bspmdls/b_shell0.bsp"); + setmodel (self, "maps/bspmdls/b_shell0.bsp"); + self.aflag = 20; + } + self.weapon = 1; + self.netname = "shells"; + } + if (self.spawnflags & WEAPON_SPIKES) + { + if (self.spawnflags & WEAPON_BIG) + { + precache_model ("maps/bspmdls/b_nail1.bsp"); + setmodel (self, "maps/bspmdls/b_nail1.bsp"); + self.aflag = 40; + } + else + { + precache_model ("maps/bspmdls/b_nail0.bsp"); + setmodel (self, "maps/bspmdls/b_nail0.bsp"); + self.aflag = 20; + } + self.weapon = 2; + self.netname = "spikes"; + } + if (self.spawnflags & WEAPON_ROCKET) + { + if (self.spawnflags & WEAPON_BIG) + { + precache_model ("maps/bspmdls/b_rock1.bsp"); + setmodel (self, "maps/bspmdls/b_rock1.bsp"); + self.aflag = 10; + } + else + { + precache_model ("maps/bspmdls/b_rock0.bsp"); + setmodel (self, "maps/bspmdls/b_rock0.bsp"); + self.aflag = 5; + } + self.weapon = 3; + self.netname = "rockets"; + } + + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; +/* +=============================================================================== +KEYS +=============================================================================== +*/ +void() key_touch = +{ + if (other.classname != "player") + return; + if (other.health <= 0) + return; + if (other.items & self.items) + return; + sprint (other, PRINT_LOW, "You got the "); + sprint (other, PRINT_LOW, self.netname); + sprint (other,PRINT_LOW, "\n"); + sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + other.items = other.items | self.items; + self.solid = SOLID_NOT; + self.model = string_null; + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; +void() key_setsounds = +{ + if (world.worldtype == 0) + { + precache_sound ("misc/medkey.wav"); + self.noise = "misc/medkey.wav"; + } + if (world.worldtype == 1) + { + precache_sound ("misc/runekey.wav"); + self.noise = "misc/runekey.wav"; + } + if (world.worldtype == 2) + { + precache_sound2 ("misc/basekey.wav"); + self.noise = "misc/basekey.wav"; + } +}; +/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) +SILVER key +In order for keys to work +you MUST set your maps +worldtype to one of the +following: +0: medieval +1: metal +2: base +*/ +void() item_key1 = +{ + if (world.worldtype == 0) + { + precache_model ("progs/w_s_key.mdl"); + setmodel (self, "progs/w_s_key.mdl"); + self.netname = "silver key"; + } + else if (world.worldtype == 1) + { + precache_model ("progs/m_s_key.mdl"); + setmodel (self, "progs/m_s_key.mdl"); + self.netname = "silver runekey"; + } + else if (world.worldtype == 2) + { + precache_model2 ("progs/b_s_key.mdl"); + setmodel (self, "progs/b_s_key.mdl"); + self.netname = "silver keycard"; + } + key_setsounds(); + self.touch = key_touch; + self.items = IT_KEY1; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; +/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) +GOLD key +In order for keys to work +you MUST set your maps +worldtype to one of the +following: +0: medieval +1: metal +2: base +*/ +void() item_key2 = +{ + if (world.worldtype == 0) + { + precache_model ("progs/w_g_key.mdl"); + setmodel (self, "progs/w_g_key.mdl"); + self.netname = "gold key"; + } + if (world.worldtype == 1) + { + precache_model ("progs/m_g_key.mdl"); + setmodel (self, "progs/m_g_key.mdl"); + self.netname = "gold runekey"; + } + if (world.worldtype == 2) + { + precache_model2 ("progs/b_g_key.mdl"); + setmodel (self, "progs/b_g_key.mdl"); + self.netname = "gold keycard"; + } + key_setsounds(); + self.touch = key_touch; + self.items = IT_KEY2; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; +/* +=============================================================================== +END OF LEVEL RUNES +=============================================================================== +*/ +void() sigil_touch = +{ + if (other.classname != "player") + return; + if (other.health <= 0) + return; + + self.target_id_finished = time + 4;//POX don't let TargetID override centerprints + + centerprint (other, "You got the rune!"); + sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + self.solid = SOLID_NOT; + self.model = string_null; + serverflags = serverflags | (self.spawnflags & 15); + self.classname = ""; // so rune doors won't find it + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + +/*QUAKED item_sigil (0 .5 .8) (-16 -16 -24) (16 16 32) E1 E2 E3 E4 +End of level sigil, pick up to end episode and return to jrstart. +*/ +void() item_sigil = +{ + if (!self.spawnflags) + objerror ("no spawnflags"); + + precache_sound ("misc/runekey.wav"); + self.noise = "misc/runekey.wav"; + if (self.spawnflags & 1) { + precache_model ("progs/end1.mdl"); + setmodel (self, "progs/end1.mdl"); + } if (self.spawnflags & 2) { + precache_model2 ("progs/end2.mdl"); + setmodel (self, "progs/end2.mdl"); + } if (self.spawnflags & 4) { + precache_model2 ("progs/end3.mdl"); + setmodel (self, "progs/end3.mdl"); + } if (self.spawnflags & 8) { + precache_model2 ("progs/end4.mdl"); + setmodel (self, "progs/end4.mdl"); + } + + self.touch = sigil_touch; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; +/* +=============================================================================== +POWERUPS +=============================================================================== +*/ +void() powerup_touch; +void() powerup_touch = +{ + if (other.classname != "player") + return; + if (other.health <= 0) + return; + sprint (other, PRINT_LOW, "You got the "); + sprint (other,PRINT_LOW, self.netname); + sprint (other,PRINT_LOW, "\n"); + self.mdl = self.model; + + if ((self.classname == "item_artifact_invulnerability") || + (self.classname == "item_artifact_invisibility")) + self.nextthink = time + 75; // POX - 5 minutes was way too long (not that these are in any maps) + else + self.nextthink = time + 60; + + self.think = SUB_regen; + sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + self.solid = SOLID_NOT; + other.items = other.items | self.items; + self.model = string_null; +// do the apropriate action + if (self.classname == "item_artifact_envirosuit") + { + other.rad_time = 1; + other.radsuit_finished = time + 30; + } + + if (self.classname == "item_artifact_invulnerability") + { + other.invincible_time = 1; + other.invincible_finished = time + 30; + } + + if (self.classname == "item_artifact_invisibility") + { + other.invisible_time = 1; + other.invisible_finished = time + 30; + } + if (self.classname == "item_artifact_super_damage") + { + self.quadcore.mdl = self.quadcore.model; + self.quadcore.model = string_null; + + other.super_time = 1; + other.super_damage_finished = time + 30; + } + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; +//POX - just changed the look of these (and some respawn times) + +/*QUAKED item_artifact_invulnerability (0 .5 .8) (-16 -16 -24) (16 16 32) +Player is invulnerable for 30 seconds +*/ +void() item_artifact_invulnerability = +{ + self.touch = powerup_touch; + precache_model ("progs/poxmegs.mdl"); + precache_sound ("items/protect.wav"); + precache_sound ("items/protect2.wav"); + precache_sound ("items/protect3.wav"); + self.noise = "items/protect.wav"; + setmodel (self, "progs/poxmegs.mdl"); + self.netname = "MegaShields"; + self.effects = self.effects | EF_RED; + self.items = IT_INVULNERABILITY; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; + +/*QUAKED item_artifact_envirosuit (0 .5 .8) (-16 -16 -24) (16 16 32) +Player takes no damage from water or slime for 30 seconds +*/ +void() item_artifact_envirosuit = +{ + self.touch = powerup_touch; + precache_model ("progs/suit.mdl"); + precache_sound ("items/suit.wav"); + precache_sound ("items/suit2.wav"); + self.noise = "items/suit.wav"; + setmodel (self, "progs/suit.mdl"); + self.netname = "Biosuit"; + self.items = IT_SUIT; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; + +/*QUAKED item_artifact_invisibility (0 .5 .8) (-16 -16 -24) (16 16 32) +Player is invisible for 30 seconds +*/ +void() item_artifact_invisibility = +{ + // + POX - Everyone's already invisible in DM_PREDATOR + if (deathmatch & DM_PREDATOR) + remove(self); + + self.touch = powerup_touch; + precache_model ("progs/cloak.mdl"); + precache_sound ("items/inv1.wav"); + precache_sound ("items/inv2.wav"); + precache_sound ("items/inv3.wav"); + self.noise = "items/inv1.wav"; + setmodel (self, "progs/cloak.mdl"); + self.netname = "Cloaking Device"; + self.items = IT_INVISIBILITY; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; + +//POX - A little hack to get a multi-model item for a cool effect +void() Spawn_QuadCore = +{ + local entity qcore; + + qcore = spawn (); + qcore.owner = self; + qcore.solid = SOLID_TRIGGER; + qcore.movetype = MOVETYPE_TOSS; + + setmodel (qcore, "progs/poxquad2.mdl"); + setsize (qcore, '-16 -16 -24', '16 16 32'); + + qcore.velocity = '0 0 0'; + setorigin(qcore, self.origin); + qcore.origin_z = qcore.origin_z + 6; + + self.quadcore = qcore; + + qcore.nextthink = time + 999999999; + qcore.think = NIL; +}; + +/*QUAKED item_artifact_super_damage (0 .5 .8) (-16 -16 -24) (16 16 32) +The next attack from the player will do 4x damage +*/ +void() item_artifact_super_damage = +{ + self.touch = powerup_touch; + precache_model ("progs/poxquad.mdl"); + precache_model ("progs/poxquad2.mdl"); + + precache_sound ("items/damage.wav"); + precache_sound ("items/damage2.wav"); + precache_sound ("items/damage3.wav"); + + self.noise = "items/damage.wav"; + setmodel (self, "progs/poxquad.mdl"); + self.netname = "Quad Damage"; + self.items = IT_QUAD; + self.effects |= EF_BLUE; + setsize (self, '-16 -16 -24', '16 16 32'); + + Spawn_QuadCore (); + + StartItem (); +}; +/* +=============================================================================== +PLAYER BACKPACKS +=============================================================================== +*/ +void() BackpackTouch = +{ + local string s; + local float best, old, new; + local entity stemp; + local float acount; + local float b_switch; + + //if (deathmatch == 4) + // if (other.invincible_time > 0) + // return; + if ((stof (infokey (other, "b_switch"))) == 0) + b_switch = 8; + else + b_switch = stof(infokey(other,"b_switch")); + + if (other.classname != "player") + return; + if (other.health <= 0) + return; + + acount = 0; + sprint (other, PRINT_LOW, "You get "); + + /* + if (deathmatch == 4) + { + other.health = other.health + 10; + sprint (other, PRINT_LOW, "10 additional health\n"); + if ((other.health > 250) && (other.health < 300)) + sound (other, CHAN_ITEM, "items/protect3.wav", 1, ATTN_NORM); + else + sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + remove(self); + if (other.health >299) + { + if (other.invincible_time != 1) + { + other.invincible_time = 1; + other.invincible_finished = time + 30; + other.items = other.items | IT_INVULNERABILITY; + + other.super_time = 1; + other.super_damage_finished = time + 30; + other.items = other.items | IT_QUAD; + other.ammo_cells = 0; + + + sound (other, CHAN_VOICE, "boss1/sight1.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + bprint (PRINT_HIGH, other.netname); + bprint (PRINT_HIGH, " attains bonus powers!!!\n"); + } + } + self = other; + return; + }*/ + + if (self.items) + if ((other.items & self.items) == 0) { + acount = 1; + sprint (other, PRINT_LOW, "the "); + sprint (other, PRINT_LOW, self.netname); + } + +// if the player was using his best weapon, change up to the new one if better + stemp = self; + self = other; + best = W_BestWeapon (); + self = stemp; +// change weapons + other.ammo_shells += self.ammo_shells; + other.ammo_nails += self.ammo_nails; + other.ammo_rockets += self.ammo_rockets; + other.ammo_cells += self.ammo_cells; + + new = self.items; + if (!new) + new = other.weapon; + old = other.items; + other.items = other.items | self.items; + + bound_other_ammo (); + if (self.ammo_shells) { + if (acount) + sprint(other, PRINT_LOW, ", "); + acount = 1; + s = ftos(self.ammo_shells); + sprint (other, PRINT_LOW, s); + sprint (other, PRINT_LOW, " shells"); + } if (self.ammo_nails) { + if (acount) + sprint(other, PRINT_LOW, ", "); + acount = 1; + s = ftos(self.ammo_nails); + sprint (other, PRINT_LOW, s); + sprint (other, PRINT_LOW, " nails"); + } if (self.ammo_rockets) { + if (acount) + sprint(other, PRINT_LOW, ", "); + acount = 1; + s = ftos(self.ammo_rockets); + sprint (other, PRINT_LOW, s); + sprint (other, PRINT_LOW, " rockets"); + } if (self.ammo_cells) { + if (acount) + sprint(other, PRINT_LOW, ", "); + acount = 1; + s = ftos(self.ammo_cells); + sprint (other, PRINT_LOW, s); + sprint (other,PRINT_LOW, " cells"); + } + + //if ( (deathmatch==3 || deathmatch == 5) & ( (WeaponCode(new)==6) || (WeaponCode(new)==7) ) & (other.ammo_rockets < 5) ) + // other.ammo_rockets = 5; + + // + POX - Health in packs for FFA mode + if (self.healamount) + { + if (!T_Heal(other, self.healamount, 0)) { + SUB_Null (); + } else { + if (acount) + sprint(other, PRINT_LOW, ", "); + + s = ftos(self.healamount); + sprint(other, PRINT_LOW, " "); + sprint(other, PRINT_LOW, s); + sprint(other, PRINT_LOW, " health"); + } + } + // - POX + + sprint (other, PRINT_LOW, "\n"); +// backpack touch sound + sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + remove(self); + self = other; + +// change to the weapon + +//POX - dm_autoswitch check +if (deathmatch & DM_AUTOSWITCH) +{ + if ( WeaponCode(new) <= b_switch ) + { + if (self.flags & FL_INWATER) + { + if (new != IT_LIGHTNING) + { + Deathmatch_Weapon (old, new); + } + } + else + { + Deathmatch_Weapon (old, new); + } + } +} + W_SetCurrentAmmo (); +}; +/* +=============== +DropBackpack +=============== +*/ +void() DropBackpack = +{ + local entity item; + + // + POX - DM_FFA check + if (!(self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells) && !(deathmatch & DM_FFA)) + return; // nothing in it + item = spawn(); + item.origin = self.origin - '0 0 24'; + + // + POX - DM_FFA (Only health in packs) + if (!(deathmatch & DM_FFA)) { + item.items = self.weapon; + if (item.items == IT_AXE) + item.netname = "Axe"; + else if (item.items == IT_TSHOT) + item.netname = "tShot"; + else if (item.items == IT_COMBOGUN) + item.netname = "Combo Gun"; + else if (item.items == IT_PLASMAGUN) + item.netname = "Plasma Gun"; + else if (item.items == IT_SUPER_NAILGUN) + item.netname = "Nail Gun"; + else if (item.items == IT_GRENADE_LAUNCHER) + item.netname = "Grenade Launcher"; + else if (item.items == IT_ROCKET_LAUNCHER) + item.netname = "Annihilator"; + else + item.netname = ""; + } else { + item.healamount = 25; + item.healtype = 1; + } + + item.ammo_shells = self.ammo_shells; + item.ammo_nails = self.ammo_nails; + item.ammo_rockets = self.ammo_rockets; + + // round rockets up to nearest integer incase someone died between rhino barrel fires + item.ammo_rockets = rint (item.ammo_rockets); + item.ammo_cells = self.ammo_cells; + item.velocity_z = 300; + item.velocity_x = -100 + (random() * 200); + item.velocity_y = -100 + (random() * 200); + + item.flags = FL_ITEM; + item.solid = SOLID_TRIGGER; + item.movetype = MOVETYPE_TOSS; + setmodel (item, "progs/backpack.mdl"); + setsize (item, '-16 -16 0', '16 16 56'); + item.touch = BackpackTouch; + // + POX - changed to 30 secs + item.nextthink = time + 30; // remove after 1 minutes + item.think = SUB_Remove; +}; diff --git a/ParoxysmII/source/misc.r b/ParoxysmII/source/misc.r new file mode 100644 index 0000000..c991c6a --- /dev/null +++ b/ParoxysmII/source/misc.r @@ -0,0 +1,716 @@ +#include "config.rh" + +#include "paroxysm.rh" + +/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) +Used as a positional target for spotlights, etc. +*/ +void() info_null = +{ + remove(self); +}; +/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) +Used as a positional target for lightning. +*/ +void() info_notnull = +{ +}; +//============================================================================ +float START_OFF = 1; +void() light_use = +{ + if (self.spawnflags & START_OFF) + { + lightstyle(self.style, "m"); + self.spawnflags = self.spawnflags - START_OFF; + } + else + { + lightstyle(self.style, "a"); + self.spawnflags = self.spawnflags + START_OFF; + } +}; +/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF +Non-displayed light. +Default light value is 300 +Default style is 0 +If targeted, it will toggle between on or off. +*/ +void() light = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + if (!self.targetname) + { // inert light + remove(self); + return; + } + if (self.style >= 32) + { + self.use = light_use; + if (self.spawnflags & START_OFF) + lightstyle(self.style, "a"); + else + lightstyle(self.style, "m"); + } +}; +/*QUAKED light_fluoro (0 1 0) (-8 -8 -8) (8 8 8) START_OFF +Non-displayed light. +Default light value is 300 +Default style is 0 +If targeted, it will toggle between on or off. +Makes steady fluorescent humming sound +*/ +void() light_fluoro = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + if (self.style >= 32) + { + self.use = light_use; + if (self.spawnflags & START_OFF) + lightstyle(self.style, "a"); + else + lightstyle(self.style, "m"); + } + + precache_sound ("ambience/fl_hum1.wav"); + ambientsound (self.origin, "ambience/fl_hum1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED light_fluorospark (0 1 0) (-8 -8 -8) (8 8 8) +Non-displayed light. +Default light value is 300 +Default style is 10 +Makes sparking, broken fluorescent sound +*/ +void() light_fluorospark = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + if (!self.style) + self.style = 10; + precache_sound ("ambience/buzz1.wav"); + ambientsound (self.origin, "ambience/buzz1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED light_globe (0 1 0) (-8 -8 -8) (8 8 8) +Sphere globe light. +Default light value is 300 +Default style is 0 +*/ +void() light_globe = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + precache_model ("progs/s_light.spr"); + setmodel (self, "progs/s_light.spr"); + makestatic (self); +}; +void() FireAmbient = +{ + precache_sound ("ambience/fire1.wav"); +// attenuate fast + ambientsound (self.origin, "ambience/fire1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED light_torch_small_walltorch (0 .5 0) (-10 -10 -20) (10 10 20) +Short wall torch +Default light value is 200 +Default style is 0 +*/ +void() light_torch_small_walltorch = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + precache_model ("progs/flame.mdl"); + setmodel (self, "progs/flame.mdl"); + FireAmbient (); + makestatic (self); +}; +/*QUAKED light_flame_large_yellow (0 1 0) (-10 -10 -12) (12 12 18) +Large yellow flame ball +*/ +void() light_flame_large_yellow = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + precache_model ("progs/flame2.mdl"); + setmodel (self, "progs/flame2.mdl"); + self.frame = 1; + FireAmbient (); + makestatic (self); +}; +/*QUAKED light_flame_small_yellow (0 1 0) (-8 -8 -8) (8 8 8) START_OFF +Small yellow flame ball +*/ +void() light_flame_small_yellow = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + precache_model ("progs/flame2.mdl"); + setmodel (self, "progs/flame2.mdl"); + FireAmbient (); + makestatic (self); +}; +/*QUAKED light_flame_small_white (0 1 0) (-10 -10 -40) (10 10 40) START_OFF +Small white flame ball +*/ +void() light_flame_small_white = +{ + // + POX - Dark Mode (dim the lights) + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "c"); + return; + } + + precache_model ("progs/flame2.mdl"); + setmodel (self, "progs/flame2.mdl"); + FireAmbient (); + makestatic (self); +}; +//============================================================================ +/*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8) +Lava Balls +*/ +void() fire_fly; +void() fire_touch; +void() misc_fireball = +{ + + precache_model ("progs/lavaball.mdl"); + self.classname = "fireball"; + self.nextthink = time + (random() * 5); + self.think = fire_fly; + if (!self.speed) + self.speed = 1000; +}; +void() fire_fly = +{ +local entity fireball; + fireball = spawn(); + fireball.solid = SOLID_TRIGGER; + fireball.movetype = MOVETYPE_TOSS; + fireball.velocity = '0 0 1000'; + fireball.velocity_x = (random() * 100) - 50; + fireball.velocity_y = (random() * 100) - 50; + fireball.velocity_z = self.speed + (random() * 200); + fireball.classname = "fireball"; + setmodel (fireball, "progs/lavaball.mdl"); + setsize (fireball, '0 0 0', '0 0 0'); + setorigin (fireball, self.origin); + fireball.nextthink = time + 5; + fireball.think = SUB_Remove; + fireball.touch = fire_touch; + + self.nextthink = time + (random() * 5) + 3; + self.think = fire_fly; +}; +void() fire_touch = +{ + T_Damage (other, self, self, 20); + remove(self); +}; +//============================================================================ +void() barrel_explode = +{ + self.takedamage = DAMAGE_NO; + self.classname = "explo_box"; + // did say self.owner + T_RadiusDamage (self, self, 160, world, ""); + sound (self, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z+32); + multicast (self.origin, MULTICAST_PHS); + remove (self); +}; +/*QUAKED misc_explobox (0 .5 .8) (0 0 0) (32 32 64) +TESTING THING +*/ +void() misc_explobox = +{ + local float oldz; + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + precache_model ("maps/b_explob.bsp"); + setmodel (self, "maps/b_explob.bsp"); + setsize (self, '0 0 0', '32 32 64'); + precache_sound ("weapons/r_exp3.wav"); + self.health = 20; + self.th_die = barrel_explode; + self.takedamage = DAMAGE_AIM; + self.origin_z = self.origin_z + 2; + oldz = self.origin_z; + droptofloor(); + if (oldz - self.origin_z > 250) + { + dprint ("item fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + } + + self.nobleed = TRUE; +}; +/*QUAKED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64) +Smaller exploding box, REGISTERED ONLY +*/ +void() misc_explobox2 = +{ + local float oldz; + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + precache_model2 ("maps/b_exbox2.bsp"); + setmodel (self, "maps/b_exbox2.bsp"); + setsize (self, '0 0 0', '32 32 32'); + precache_sound ("weapons/r_exp3.wav"); + self.health = 20; + self.th_die = barrel_explode; + self.takedamage = DAMAGE_AIM; + self.origin_z = self.origin_z + 2; + oldz = self.origin_z; + droptofloor(); + if (oldz - self.origin_z > 250) + { + dprint ("item fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + } + + self.nobleed = TRUE; +}; +//============================================================================ +float SPAWNFLAG_SUPERSPIKE = 1; +float SPAWNFLAG_LASER = 2; +void() Laser_Touch = +{ + local vector org; + + if (other == self.owner) + return; // don't explode on owner + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + sound (self, CHAN_WEAPON, "enforcer/enfstop.wav", 1, ATTN_STATIC); + org = self.origin - 8*normalize(self.velocity); + if (other.health) + { + SpawnBlood (org, 15); + other.deathtype = "laser"; + T_Damage (other, self, self.owner, 15); + } + else + { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_GUNSHOT); + WriteByte (MSG_MULTICAST, 5); + WriteCoord (MSG_MULTICAST, org_x); + WriteCoord (MSG_MULTICAST, org_y); + WriteCoord (MSG_MULTICAST, org_z); + multicast (org, MULTICAST_PVS); + } + + remove(self); +}; +void(vector org, vector vec) LaunchLaser = +{ + local vector v; + + if (self.classname == "monster_enforcer") + sound (self, CHAN_WEAPON, "enforcer/enfire.wav", 1, ATTN_NORM); + v = normalize (vec); + + newmis = spawn(); + newmis.owner = self; + newmis.movetype = MOVETYPE_FLY; + newmis.solid = SOLID_BBOX; + newmis.effects = EF_DIMLIGHT; + setmodel (newmis, "progs/laser.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, org); + newmis.velocity = v * 600; + newmis.angles = vectoangles(newmis.velocity); + newmis.nextthink = time + 5; + newmis.think = SUB_Remove; + newmis.touch = Laser_Touch; +}; +void() spikeshooter_use = +{ + if (self.spawnflags & SPAWNFLAG_LASER) + { + sound (self, CHAN_VOICE, "enforcer/enfire.wav", 1, ATTN_NORM); + LaunchLaser (self.origin, self.movedir); + } + else + { + sound (self, CHAN_VOICE, "weapons/spike2.wav", 1, ATTN_NORM); + launch_spike (self.origin, self.movedir); + newmis.velocity = self.movedir * 500; + if (self.spawnflags & SPAWNFLAG_SUPERSPIKE) + newmis.touch = superspike_touch; + } +}; +void() shooter_think = +{ + spikeshooter_use (); + self.nextthink = time + self.wait; + newmis.velocity = self.movedir * 500; +}; +/*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser +When triggered, fires a spike in the direction set in QuakeEd. +Laser is only for REGISTERED. +*/ +void() trap_spikeshooter = +{ + SetMovedir (); + self.use = spikeshooter_use; + if (self.spawnflags & SPAWNFLAG_LASER) + { + precache_model2 ("progs/laser.mdl"); + + precache_sound2 ("enforcer/enfire.wav"); + precache_sound2 ("enforcer/enfstop.wav"); + } + else + precache_sound ("weapons/spike2.wav"); +}; +/*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser +Continuously fires spikes. +"wait" time between spike (1.0 default) +"nextthink" delay before firing first spike, so multiple shooters can be stagered. +*/ +void() trap_shooter = +{ + trap_spikeshooter (); + + if (self.wait == 0) + self.wait = 1; + self.nextthink = self.nextthink + self.wait + self.ltime; + self.think = shooter_think; +}; +/* +=============================================================================== +=============================================================================== +*/ +void() make_bubbles; +void() bubble_remove; +void() bubble_bob; +/*QUAKED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8) +testing air bubbles +*/ +void() air_bubbles = +{ + remove (self); +}; +void() make_bubbles = +{ +local entity bubble; + bubble = spawn(); + setmodel (bubble, "progs/s_bubble.spr"); + setorigin (bubble, self.origin); + bubble.movetype = MOVETYPE_NOCLIP; + bubble.solid = SOLID_NOT; + bubble.velocity = '0 0 15'; + bubble.nextthink = time + 0.5; + bubble.think = bubble_bob; + bubble.touch = bubble_remove; + bubble.classname = "bubble"; + bubble.frame = 0; + bubble.cnt = 0; + setsize (bubble, '-8 -8 -8', '8 8 8'); + self.nextthink = time + random() + 0.5; + self.think = make_bubbles; +}; +void() bubble_split = +{ +local entity bubble; + bubble = spawn(); + setmodel (bubble, "progs/s_bubble.spr"); + setorigin (bubble, self.origin); + bubble.movetype = MOVETYPE_NOCLIP; + bubble.solid = SOLID_NOT; + bubble.velocity = self.velocity; + bubble.nextthink = time + 0.5; + bubble.think = bubble_bob; + bubble.touch = bubble_remove; + bubble.classname = "bubble"; + bubble.frame = 1; + bubble.cnt = 10; + setsize (bubble, '-8 -8 -8', '8 8 8'); + self.frame = 1; + self.cnt = 10; + if (self.waterlevel != 3) + remove (self); +}; +void() bubble_remove = +{ + if (other.classname == self.classname) + { +// dprint ("bump"); + return; + } + remove(self); +}; +void() bubble_bob = +{ + local float rnd1, rnd2, rnd3; + + (self.cnt)++; + + if (self.classname == "bubble") { + if (self.cnt == 4) + bubble_split(); + + if (self.cnt == 20) + remove(self); + } else { + if ((self.classname != "player") && (self.classname != "bodyque") + && (self.cnt == 50)) + remove (self); + } + + rnd1 = self.velocity_x + (-10 + (random () * 20)); + rnd2 = self.velocity_y + (-10 + (random () * 20)); + rnd3 = self.velocity_z + (10 + (random () * 10)); + + if (rnd1 > 10) + rnd1 = 5; + if (rnd1 < -10) + rnd1 = -5; + + if (rnd2 > 10) + rnd2 = 5; + if (rnd2 < -10) + rnd2 = -5; + + if (rnd3 < 10) + rnd3 = 15; + if (rnd3 > 30) + rnd3 = 25; + + self.velocity_x = rnd1; + self.velocity_y = rnd2; + self.velocity_z = rnd3; + + self.nextthink = time + 0.5; + self.think = bubble_bob; + + // let some objects bob around at the surface + if (self.classname != "bubble") { // cut down on the speed + + self.velocity_x *= 0.5; + self.velocity_y *= 0.5; + self.velocity_z *= 0.5; + + // send it back down if origin clears the water + if (pointcontents (self.origin) != CONTENT_WATER) + self.velocity_z = -(self.velocity_z); + } +}; + +/*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~> +~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/ +/*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) +Just for the debugging level. Don't use +*/ +void() viewthing = +{ + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + precache_model ("progs/player.mdl"); + setmodel (self, "progs/player.mdl"); +}; +/* +============================================================================== +SIMPLE BMODELS +============================================================================== +*/ +void() func_wall_use = +{ // change to alternate textures + self.frame = 1 - self.frame; +}; +/*QUAKED func_wall (0 .5 .8) ? +This is just a solid wall if not inhibitted +*/ +void() func_wall = +{ + self.angles = '0 0 0'; + self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + self.solid = SOLID_BSP; + self.use = func_wall_use; + setmodel (self, self.model); +}; +/*QUAKED func_illusionary (0 .5 .8) ? +A simple entity that looks solid but lets you walk through it. +*/ +void() func_illusionary = +{ + self.angles = '0 0 0'; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + setmodel (self, self.model); + makestatic (self); +}; +/*QUAKED func_episodegate (0 .5 .8) ? E1 E2 E3 E4 +This bmodel will appear if the episode has allready been completed, so players can't reenter it. +*/ +void() func_episodegate = +{ + if (!(serverflags & self.spawnflags)) + return; // can still enter episode + self.angles = '0 0 0'; + self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + self.solid = SOLID_BSP; + self.use = func_wall_use; + setmodel (self, self.model); +}; +/*QUAKED func_bossgate (0 .5 .8) ? +This bmodel appears unless players have all of the episode sigils. +*/ +void() func_bossgate = +{ + if ( (serverflags & 15) == 15) + return; // all episodes completed + self.angles = '0 0 0'; + self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + self.solid = SOLID_BSP; + self.use = func_wall_use; + setmodel (self, self.model); +}; +//============================================================================ +/*QUAKED ambient_suck_wind (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_suck_wind = +{ + precache_sound ("ambience/suck1.wav"); + ambientsound (self.origin, "ambience/suck1.wav", 1, ATTN_STATIC); +}; +/*QUAKED ambient_drone (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_drone = +{ + precache_sound ("ambience/drone6.wav"); + ambientsound (self.origin, "ambience/drone6.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_flouro_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_flouro_buzz = +{ + precache_sound ("ambience/buzz1.wav"); + ambientsound (self.origin, "ambience/buzz1.wav", 1, ATTN_STATIC); +}; +/*QUAKED ambient_drip (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_drip = +{ + precache_sound ("ambience/drip1.wav"); + ambientsound (self.origin, "ambience/drip1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_comp_hum (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_comp_hum = +{ + precache_sound ("ambience/comp1.wav"); + ambientsound (self.origin, "ambience/comp1.wav", 1, ATTN_STATIC); +}; +/*QUAKED ambient_thunder (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_thunder = +{ + precache_sound ("ambience/thunder1.wav"); + ambientsound (self.origin, "ambience/thunder1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_light_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_light_buzz = +{ + precache_sound ("ambience/fl_hum1.wav"); + ambientsound (self.origin, "ambience/fl_hum1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_swamp1 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_swamp1 = +{ + precache_sound ("ambience/swamp1.wav"); + ambientsound (self.origin, "ambience/swamp1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_swamp2 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_swamp2 = +{ + precache_sound ("ambience/swamp2.wav"); + ambientsound (self.origin, "ambience/swamp2.wav", 0.5, ATTN_STATIC); +}; +//============================================================================ +void() noise_think = +{ + self.nextthink = time + 0.5; + sound (self, 1, "enforcer/enfire.wav", 1, ATTN_NORM); + sound (self, 2, "enforcer/enfstop.wav", 1, ATTN_NORM); + sound (self, 3, "enforcer/sight1.wav", 1, ATTN_NORM); + sound (self, 4, "enforcer/sight2.wav", 1, ATTN_NORM); + sound (self, 5, "enforcer/sight3.wav", 1, ATTN_NORM); + sound (self, 6, "enforcer/sight4.wav", 1, ATTN_NORM); + sound (self, 7, "enforcer/pain1.wav", 1, ATTN_NORM); +}; +/*QUAKED misc_noisemaker (1 0.5 0) (-10 -10 -10) (10 10 10) +For optimzation testing, starts a lot of sounds. +*/ +void() misc_noisemaker = +{ + precache_sound2 ("enforcer/enfire.wav"); + precache_sound2 ("enforcer/enfstop.wav"); + precache_sound2 ("enforcer/sight1.wav"); + precache_sound2 ("enforcer/sight2.wav"); + precache_sound2 ("enforcer/sight3.wav"); + precache_sound2 ("enforcer/sight4.wav"); + precache_sound2 ("enforcer/pain1.wav"); + precache_sound2 ("enforcer/pain2.wav"); + precache_sound2 ("enforcer/death1.wav"); + precache_sound2 ("enforcer/idle1.wav"); + self.nextthink = time + 0.1 + random(); + self.think = noise_think; +}; diff --git a/ParoxysmII/source/nq/ai.qc b/ParoxysmII/source/nq/ai.qc new file mode 100644 index 0000000..6967a5e --- /dev/null +++ b/ParoxysmII/source/nq/ai.qc @@ -0,0 +1,185 @@ +void() movetarget_f; +void() t_movetarget; +//void() knight_walk1; +//void() knight_bow6; +//void() knight_bow1; +void(entity etemp, entity stemp, entity stemp, float dmg) T_Damage; +/* + +.enemy +Will be world if not currently angry at anyone. + +.movetarget +The next path spot to walk toward. If .enemy, ignore .movetarget. +When an enemy is killed, the monster will try to return to it's path. + +.huntt_ime +Set to time + something when the player is in sight, but movement straight for +him is blocked. This causes the monster to use wall following code for +movement direction instead of sighting on the player. + +.ideal_yaw +A yaw angle of the intended direction, which will be turned towards at up +to 45 deg / state. If the enemy is in view and hunt_time is not active, +this will be the exact line towards the enemy. + +.pausetime +A monster will leave it's stand state and head towards it's .movetarget when +time > .pausetime. + +walkmove(angle, speed) primitive is all or nothing +*/ + + +// +// globals +// +float current_yaw; + +// +// when a monster becomes angry at a player, that monster will be used +// as the sight target the next frame so that monsters near that one +// will wake up even if they wouldn't have noticed the player +// +entity sight_entity; +float sight_entity_time; + +float(float v) anglemod = +{ + while (v >= 360) + v = v - 360; + while (v < 0) + v = v + 360; + return v; +}; + +/* +============================================================================== + +MOVETARGET CODE + +The angle of the movetarget effects standing and bowing direction, but has no effect on movement, which allways heads to the next target. + +targetname +must be present. The name of this movetarget. + +target +the next spot to move to. If not present, stop here for good. + +pausetime +The number of seconds to spend standing or bowing for path_stand or path_bow + +============================================================================== +*/ + + +void() movetarget_f = +{ + if (!self.targetname) + objerror ("monster_movetarget: no targetname"); + + self.solid = SOLID_TRIGGER; + self.touch = t_movetarget; + setsize (self, '-8 -8 -8', '8 8 8'); + +}; + +//*QUAKED path_corner (0.5 0.3 0) (-8 -8 -8) (8 8 8) +//Monsters will continue walking towards the next target corner. + +void() path_corner = +{ + movetarget_f (); +}; + + +/* +============= +t_movetarget + +Something has bumped into a movetarget. If it is a monster +moving towards it, change the next destination and continue. +============== +*/ +void() t_movetarget = +{ +local entity temp; + + if (other.movetarget != self) + return; + + if (other.enemy) + return; // fighting, not following a path + + temp = self; + self = other; + other = temp; + + //if (self.classname == "monster_ogre") + // sound (self, CHAN_VOICE, "ogre/ogdrag.wav", 1, ATTN_IDLE);// play chainsaw drag sound + +//dprint ("t_movetarget\n"); + self.goalentity = self.movetarget = find (world, targetname, other.target); + self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); + if (!self.movetarget) + { + self.pausetime = time + 999999; + self.th_stand (); + return; + } +}; + + + +//============================================================================ + + +/* +============= +visible + +returns 1 if the entity is visible to self, even if not infront () +============= +*/ +float (entity targ) visible = +{ + local vector spot1, spot2; + + spot1 = self.origin + self.view_ofs; + spot2 = targ.origin + targ.view_ofs; + traceline (spot1, spot2, TRUE, self); // see through other monsters + + if (trace_inopen && trace_inwater) + return FALSE; // sight line crossed contents + + if (trace_fraction == 1) + return TRUE; + return FALSE; +}; + + +/* +============= +infront + +returns 1 if the entity is in front (in sight) of self +============= +*/ +float(entity targ) infront = +{ + local vector vec; + local float dot; + + makevectors (self.angles); + vec = normalize (targ.origin - self.origin); + dot = vec * v_forward; + + if ( dot > 0.3) + { + return TRUE; + } + return FALSE; +}; + + +//============================================================================ diff --git a/ParoxysmII/source/nq/client.qc b/ParoxysmII/source/nq/client.qc new file mode 100644 index 0000000..94762a0 --- /dev/null +++ b/ParoxysmII/source/nq/client.qc @@ -0,0 +1,1746 @@ + +// prototypes +void () W_WeaponFrame; +void() W_SetCurrentAmmo; +void() player_pain; +void() player_stand1; +void (vector org) spawn_tfog; +void (vector org, entity death_owner) spawn_tdeath; + +.float uwmuffle; // under water muffle sound timeout +.float onwsound; // on water sound timeout +.float outwsound; // head out of water sound flag +.float inwsound; // head back in water sound flag + +.float cshift_finished; +.float cshift_off; +.float armor_rot; + +/* +============================================================================= + + LEVEL CHANGING / INTERMISSION + +============================================================================= +*/ + +// POX v1.1 moved to weapons.qc +//float intermission_running; +float intermission_exittime; + +/*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16) +This is the camera point for the intermission. +Use mangle instead of angle, so you can set pitch or roll as well as yaw. 'pitch roll yaw' +*/ +void() info_intermission = +{ +}; + +//POX v1.2 +.float configed; + +void() SetChangeParms = +{ + //POX v1.1 - fresh start for everyone on changelevel + + //POX v1.2 - parms are used to store the Target ID toggle + parm1 = self.target_id_toggle; + +}; + +void() SetNewParms = +{ + // Quake Complains if this function isn't defined +}; + + +void() DecodeLevelParms = +{ + //POX v1.2 - parms are used to store the Taget ID toggle + if(!self.target_id_toggle && !self.target_id_temp) + self.target_id_toggle = parm1; + +}; + +/* +============ +FindIntermission + +Returns the entity to view from +============ +*/ +entity() FindIntermission = +{ + local entity spot; + local float cyc; + +// look for info_intermission first + spot = find (world, classname, "info_intermission"); + if (spot) + { // pick a random one + cyc = random() * 4; + while (cyc > 1) + { + spot = find (spot, classname, "info_intermission"); + if (!spot) + spot = find (spot, classname, "info_intermission"); + cyc = cyc - 1; + } + return spot; + } + +// then look for the start position + spot = find (world, classname, "info_player_start"); + if (spot) + return spot; + +// testinfo_player_start is only found in regioned levels + spot = find (world, classname, "testplayerstart"); + if (spot) + return spot; + + objerror ("FindIntermission: no spot"); +}; + + +string nextmap; +void() GotoNextMap = +{ + if (cvar("samelevel")) // if samelevel is set, stay on same level + changelevel (mapname); + else + changelevel (nextmap); +}; + + +void() ExitIntermission = +{ +// skip any text in deathmatch + if (deathmatch) + { + GotoNextMap (); + return; + } + + intermission_exittime = time + 1; + intermission_running = intermission_running + 1; + + GotoNextMap(); +}; + +/* +============ +IntermissionThink + +When the player presses attack or jump, change to the next level +============ +*/ +void() IntermissionThink = +{ + if (time < intermission_exittime) + return; + + if (!self.button0 && !self.button1 && !self.button2) + return; + + ExitIntermission (); +}; + +void() execute_changelevel = +{ + local entity pos; + + intermission_running = 1; + +// enforce a wait time before allowing changelevel + if (deathmatch) + intermission_exittime = time + 5; + else + intermission_exittime = time + 2; + + WriteByte (MSG_ALL, SVC_CDTRACK); + WriteByte (MSG_ALL, 3); + WriteByte (MSG_ALL, 3); + + pos = FindIntermission (); + + other = find (world, classname, "player"); + while (other != world) + { + other.view_ofs = '0 0 0'; + other.angles = other.v_angle = pos.mangle; + other.fixangle = TRUE; // turn this way immediately + other.nextthink = time + 0.5; + other.takedamage = DAMAGE_NO; + other.solid = SOLID_NOT; + other.movetype = MOVETYPE_NONE; + other.modelindex = 0; + setorigin (other, pos.origin); + other = find (other, classname, "player"); + } + + WriteByte (MSG_ALL, SVC_INTERMISSION); +}; + + +void() changelevel_touch = +{ + local entity pos; + + if (other.classname != "player") + return; + + if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start"))) + { + T_Damage (other, self, self, 50000); + return; + } + + if (coop || deathmatch) + { + bprint (other.netname); + bprint (" exited the level\n"); + } + + nextmap = self.map; + + SUB_UseTargets (); + + if ( (self.spawnflags & 1) && (deathmatch == 0) ) + { // NO_INTERMISSION + GotoNextMap(); + return; + } + + self.touch = SUB_Null; + +// we can't move people right now, because touch functions are called +// in the middle of C movement code, so set a think time to do it + self.think = execute_changelevel; + self.nextthink = time + 0.1; +}; + +/*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION +When the player touches this, he gets sent to the map listed in the "map" variable. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats. +*/ +void() trigger_changelevel = +{ + if (!self.map) + objerror ("chagnelevel trigger doesn't have map"); + + InitTrigger (); + self.touch = changelevel_touch; +}; + + +/* +============================================================================= + + PLAYER GAME EDGE FUNCTIONS + +============================================================================= +*/ + +void() set_suicide_frame; + +// called by ClientKill and DeadThink +void() respawn = +{ + /*POX v1.1 no coop... + if (coop) + { + // make a copy of the dead body for appearances sake + CopyToBodyQue (self); + // get the spawn parms as they were at level start + setspawnparms (self); + // respawn + PutClientInServer (); + } + else*/ if (deathmatch) + { + // make a copy of the dead body for appearances sake + CopyToBodyQue (self); + // set default spawn parms + //SetNewParms (); POX v1.1 + // respawn + PutClientInServer (); + } + else + { // restart the entire server + localcmd ("restart\n"); + } +}; + + +void() NextLevel; +/* +============ +ClientKill + +Player entered the suicide command +============ +*/ +void() ClientKill = +{ +// KasCam -> + if (self.classname == "KasCam") { + return; + } +// <- KasCam + bprint (self.netname); + bprint (" suicides\n"); + set_suicide_frame (); + + //self.modelindex = modelindex_player; + //getmodel(self.weapon, self); + + self.frags = self.frags - 2; // extra penalty + + //POX v1.12 - forgot about stink'n suidcides + if ((deathmatch & DM_LMS) && (self.LMS_registered)) + { + if (self.frags <= 0) + { + lms_plrcount = lms_plrcount - 1; + + self.frags = 0; + + bprint(self.netname); + bprint(" is eliminated!\n"); + + sound (self, CHAN_BODY, "nar/n_elim.wav", 1, ATTN_NONE); + + if (lms_plrcount <= 1) //1 player left so end the game + NextLevel (); + } + } + + respawn (); +}; + + +float(vector v) CheckSpawnPoint = +{ + return FALSE; +}; + +/* +============ +SelectSpawnPoint + +Returns the entity to spawn at +============ +*/ +entity() SelectSpawnPoint = +{ + local entity spot; + local entity thing; + local float pcount; + +// testinfo_player_start is only found in regioned levels + spot = find (world, classname, "testplayerstart"); + if (spot) + return spot; + +// choose a info_player_deathmatch point + if (coop) + { + lastspawn = find(lastspawn, classname, "info_player_coop"); + if (lastspawn == world) + lastspawn = find (lastspawn, classname, "info_player_start"); + if (lastspawn != world) + return lastspawn; + } + else if (deathmatch) + { + spot = lastspawn; + while (1) + { + spot = find(spot, classname, "info_player_deathmatch"); + if (spot != world) + { + if (spot == lastspawn) + return lastspawn; + pcount = 0; + thing = findradius(spot.origin, 32); + while(thing) + { + if (thing.classname == "player") + pcount = pcount + 1; + thing = thing.chain; + } + if (pcount == 0) + { + lastspawn = spot; + return spot; + } + } + } + } + + if (serverflags) + { // return with a rune to start + spot = find (world, classname, "info_player_start2"); + if (spot) + return spot; + } + + spot = find (world, classname, "info_player_start"); + if (!spot) + error ("PutClientInServer: no info_player_start on level"); + + return spot; +}; + +/* +=========== +PutClientInServer + +called each time a player is spawned +============ +*/ +//void() DecodeLevelParms; POX v1.1 +void() PlayerDie; + + +void() PutClientInServer = +{ + local entity spot; + + //spot = SelectSpawnPoint (); + + self.classname = "player"; + self.health = 100; + self.takedamage = DAMAGE_AIM; + self.solid = SOLID_SLIDEBOX; + + self.movetype = MOVETYPE_WALK; + + //POX v1.2 - remove items + self.items = self.items - (self.items & + (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) ); + + self.show_hostile = 0; + self.max_health = 100; + self.flags = FL_CLIENT; + self.air_finished = time + 12; + self.dmg = 2; // initial water damage + self.super_damage_finished = 0; + self.radsuit_finished = 0; + self.invisible_finished = 0; + self.invincible_finished = 0; + + self.effects = 0; + self.invincible_time = 0; + +// POX v1.1 - this stuff is nolonger handled by DecodeLevelParms + self.items = IT_BONESAW | IT_TSHOT; + self.ammo_shells = 25; + self.ammo_nails = 0; + self.ammo_rockets = 0; + self.ammo_cells = 0; + self.weapon = IT_TSHOT; + +// POX v1.1 - spawn with 50 armour points + self.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR1; + self.armorvalue = 50; + self.armortype = 0.9; + +//added 12/14/98 to resest reload_rocket after death + self.reload_rocket = 0; + +//added 12/22/98 to resest triple shot after death + self.prime_tshot = FALSE; + + DecodeLevelParms (); //POX v1.2 - restore Target ID value + + //W_SetCurrentAmmo (); + + self.attack_finished = time; + self.th_pain = player_pain; + self.th_die = PlayerDie; + + self.deadflag = DEAD_NO; +// paustime is set by teleporters to keep the player from moving a while + self.pausetime = 0; + + spot = SelectSpawnPoint (); + + self.origin = spot.origin + '0 0 1'; + self.angles = spot.angles; + self.fixangle = TRUE; // turn this way immediately + + +//ChaseCam + if ( self.aflag ) + CCamChasePlayer (); +//ChaseCam + + //Put client into the Dm setup menu + if (deathmatch && world.model == "maps/start.bsp") + { + InitMenu(); + return; + } + + +//Predator mode POX v1.11 - back to eyes + if (deathmatch & DM_PREDATOR) + { + sound (self, CHAN_AUTO, "items/inv1.wav", 1, ATTN_NORM); + stuffcmd (self, "bf\n"); + + self.items = self.items | IT_INVISIBILITY; + self.invisible_time = 1; + self.invisible_finished = time + 9999999999; + } + + // oh, this is a hack! + + setmodel (self, "progs/eyes.mdl"); + modelindex_eyes = self.modelindex; + + //TEMPORARY!!! + //setmodel (self, "progs/player.mdl"); + //modelindex_player = self.modelindex; + +// Sine we're hack'n... + setmodel (self, "progs/death_p.mdl"); + modelindex_death = self.modelindex; + + setmodel (self, "progs/bsaw_p.mdl"); + modelindex_saw_p = self.modelindex; + + setmodel (self, "progs/tshot_p.mdl"); + modelindex_tshot_p = self.modelindex; + + setmodel (self, "progs/combo_p.mdl"); + modelindex_combo_p = self.modelindex; + + setmodel (self, "progs/plasma_p.mdl"); + modelindex_plasma_p = self.modelindex; + + setmodel (self, "progs/nail_p.mdl"); + modelindex_nail_p = self.modelindex; + + setmodel (self, "progs/gren_p.mdl"); + modelindex_gren_p = self.modelindex; + + setmodel (self, "progs/rhino_p.mdl"); + modelindex_rock_p = self.modelindex; + +// setmodel (self, "progs/flame_p.mdl"); +// modelindex_flame_p = self.modelindex; + + setsize (self, VEC_HULL_MIN, VEC_HULL_MAX); + + self.view_ofs = '0 0 22'; + +// Mod - Xian (May.20.97) +// Bug where player would have velocity from their last kill +// Modified by numb (Nov, 98) + self.velocity = '0 0 0'; + self.velocity = normalize(self.velocity); + +//Dark Mode + if (deathmatch & DM_DARK) + { + //pOx 1.4 - reset flashlight (always start on) + //self.flash_flag = FALSE; + //stuffcmd (self, "impulse 21\n"); + + if (cvar("gl_cull") == 1) + stuffcmd (self, "gl_flashblend 0\n"); + } + + else if (cvar("gl_cull") == 1) + stuffcmd (self, "gl_flashblend 1\n"); + + +//Last Man Standing Rules + if (time < 30 && (deathmatch & DM_LMS) && !self.LMS_registered) + { + if (fraglimit_LMS == 0) + self.frags = 5; + else + self.frags = fraglimit_LMS; + + self.LMS_registered = TRUE; + lms_plrcount = lms_plrcount + 1; + } + + + player_stand1 (); + + //Check if dead in LMS mode + if ((deathmatch & DM_LMS) && self.frags <= 0) + { + self.frags = 0; + self.impulse = 250; + //CamClientInit (); + } + +// + POX v1.1 changed the way this is handled - DM_FFA + if (deathmatch & DM_FFA) + { + self.ammo_nails = 200; + self.ammo_shells = 200; + self.ammo_rockets = 100; + self.ammo_cells = 200; + self.items = self.items | IT_PLASMAGUN; + self.items = self.items | IT_SUPER_NAILGUN; + self.items = self.items | IT_COMBOGUN; + self.items = self.items | IT_ROCKET_LAUNCHER; + self.items = self.items | IT_GRENADE_LAUNCHER; + + self.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3; + self.armorvalue = 250; + self.armortype = 0.9; + self.health = 200; + self.items = self.items | IT_INVULNERABILITY; + self.invincible_time = 1; + self.invincible_finished = time + 3; + + self.weapon = IT_ROCKET_LAUNCHER; + self.currentammo = self.ammo_rockets; + self.weaponmodel = "progs/v_rhino.mdl"; + self.weaponframe = 0; + self.items = self.items | IT_ROCKETS; + + self.max_health = 200; + sound (self, CHAN_AUTO, "items/protect.wav", 1, ATTN_NORM); + stuffcmd (self, "bf\n"); + } + + if (deathmatch || coop) + { + makevectors(self.angles); + spawn_tfog (self.origin + v_forward*20); + stuffcmd (self, "tele_zoom\n"); + } + + spawn_tdeath (self.origin, self); + + W_SetCurrentAmmo (); // VisWeap MOD: I moved this to the end so it will set the right model. + +}; + + +/* +============================================================================= + + QUAKED FUNCTIONS + +============================================================================= +*/ + + +/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24) +The normal starting point for a level. +*/ +void() info_player_start = +{ +}; + + +/*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24) +Only used on start map for the return point from an episode. +*/ +void() info_player_start2 = +{ +}; + + +/* +saved out by quaked in region mode +*/ +void() testplayerstart = +{ +}; + +/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24) +potential spawning position for deathmatch games +*/ +void() info_player_deathmatch = +{ +}; + +/*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24) +potential spawning position for coop games +*/ +void() info_player_coop = +{ +}; + +/* +=============================================================================== + +RULES + +=============================================================================== +*/ + +/* +go to the next level for deathmatch +only called if a time or frag limit has expired +*/ +void() NextLevel = +{ + local entity o; + + if (mapname == "start") + { + if (!cvar("registered")) + { + mapname = "e1m1"; + } + else if (!(serverflags & 1)) + { + mapname = "e1m1"; + serverflags = serverflags | 1; + } + else if (!(serverflags & 2)) + { + mapname = "e2m1"; + serverflags = serverflags | 2; + } + else if (!(serverflags & 4)) + { + mapname = "e3m1"; + serverflags = serverflags | 4; + } + else if (!(serverflags & 8)) + { + mapname = "e4m1"; + serverflags = serverflags - 7; + } + + o = spawn(); + o.map = mapname; + } + else + { + // find a trigger changelevel + o = find(world, classname, "trigger_changelevel"); + + // go back to start if no trigger_changelevel + if (!o) + { + mapname = "start"; + o = spawn(); + o.map = mapname; + } + } + + nextmap = o.map; + gameover = TRUE; + + if (o.nextthink < time) + { + o.think = execute_changelevel; + o.nextthink = time + 0.1; + } +}; + +/* +============ +CheckRules + +Exit deathmatch games upon conditions +============ +*/ +void() CheckRules = +{ + local float timelimit; + local float fraglimit; + + if (gameover) // someone else quit the game already + return; + + timelimit = cvar("timelimit") * 60; + fraglimit = cvar("fraglimit"); + + if (timelimit && time >= timelimit) + { + NextLevel (); + return; + } + + if (fraglimit && self.frags >= fraglimit && !(deathmatch & DM_LMS)) + { + NextLevel (); + return; + } + + if (lms_gameover == TRUE) + { + NextLevel (); + return; + } + +}; + +//============================================================================ + +void() PlayerDeathThink = +{ + local entity old_self; + local float forward; + + if ((self.flags & FL_ONGROUND)) + { + forward = vlen (self.velocity); + forward = forward - 20; + if (forward <= 0) + self.velocity = '0 0 0'; + else + self.velocity = forward * normalize(self.velocity); + } + +// wait for all buttons released + if (self.deadflag == DEAD_DEAD) + { + if (self.button2 || self.button1 || self.button0) + return; + self.deadflag = DEAD_RESPAWNABLE; + return; + } + +//POX - don't let players lay around as dead guys during LMS + if (!self.button2 && !self.button1 && !self.button0 && !(deathmatch & DM_LMS)) + return; + +// wait for any button down + + self.button0 = 0; + self.button1 = 0; + self.button2 = 0; + respawn(); +}; + + +void() PlayerJump = +{ + local vector start, end; + + if (self.flags & FL_WATERJUMP) + return; + + if (self.waterlevel >= 2) + { + if (self.watertype == CONTENT_WATER) + self.velocity_z = 100; + else if (self.watertype == CONTENT_SLIME) + self.velocity_z = 80; + else + self.velocity_z = 50; + + return; + } + + if (!(self.flags & FL_ONGROUND)) + return; + + if ( !(self.flags & FL_JUMPRELEASED) ) + return; // don't pogo stick + + self.flags = self.flags - (self.flags & FL_JUMPRELEASED); + + self.flags = self.flags - FL_ONGROUND; // don't stairwalk + + self.button2 = 0; +// player jumping sound + sound (self, CHAN_VOICE, "player/plyrjmp8.wav", 1, ATTN_NORM); + self.velocity_z = self.velocity_z + 270; +}; + + +/* +========== +WaterMove + + +========== +*/ +.float dmgtime; + +void() WaterMove = +{ +//dprint (ftos(self.waterlevel)); + if (self.movetype == MOVETYPE_NOCLIP) + return; + if (self.health < 0) + return; + + if (self.waterlevel != 3) + { + if (self.air_finished < time) + sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM); + else if (self.air_finished < time + 9) + sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM); + self.air_finished = time + 12; + self.dmg = 2; + } + else if (self.air_finished < time) + { // drown! + if (self.pain_finished < time) + { + self.dmg = self.dmg + 2; + if (self.dmg > 15) + self.dmg = 10; + T_Damage (self, world, world, self.dmg); + self.pain_finished = time + 1; + } + } + + if (!self.waterlevel) + { + if (self.flags & FL_INWATER) + { + // play leave water sound + sound (self, CHAN_AUTO, "misc/outwater.wav", 1, ATTN_NORM); + self.flags = self.flags - FL_INWATER; + + //POX v1.2 - fixed rare cases of underwater sound not cancelling out + if (self.outwsound == 1) + { + sound (self, CHAN_BODY, "misc/owater2.wav", 0.5, ATTN_NORM); + self.outwsound = 0; + self.inwsound = 1; + self.uwmuffle = time; + } + + } + return; + } + + if (self.watertype == CONTENT_LAVA) + { // do damage + if (self.dmgtime < time) + { + if (self.radsuit_finished > time) + self.dmgtime = time + 1; + else + self.dmgtime = time + 0.2; + + T_Damage (self, world, world, 10*self.waterlevel); + } + } + else if (self.watertype == CONTENT_SLIME) + { // do damage + if (self.dmgtime < time && self.radsuit_finished < time) + { + self.dmgtime = time + 1; + T_Damage (self, world, world, 4*self.waterlevel); + } + } + + if ( !(self.flags & FL_INWATER) ) + { + +// player enter water sound + + if (self.watertype == CONTENT_LAVA) + sound (self, CHAN_AUTO, "player/inlava.wav", 1, ATTN_NORM); + if (self.watertype == CONTENT_WATER) + sound (self, CHAN_AUTO, "player/inh2o.wav", 1, ATTN_NORM); + if (self.watertype == CONTENT_SLIME) + sound (self, CHAN_AUTO, "player/slimbrn2.wav", 1, ATTN_NORM); + + self.flags = self.flags + FL_INWATER; + self.dmgtime = 0; + } + + if (! (self.flags & FL_WATERJUMP) ) + self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity; + +//POX v1.1 - New water movement sounds - moved from PreThink (same as QW src) +if (self.waterlevel >= 3) +{ + self.onwsound = time; + self.outwsound = 1; + + if (self.inwsound == 1) + { + sound (self, CHAN_VOICE, "misc/inh2ob.wav", 1, ATTN_NORM); + self.inwsound = 0; + } + + if (self.uwmuffle < time) + { + sound (self, CHAN_BODY, "misc/uwater.wav", 1, ATTN_STATIC); + self.uwmuffle = time + 3.58; + } + } + + if (self.waterlevel == 2) + { + if (self.outwsound == 1) + { + sound (self, CHAN_BODY, "misc/owater2.wav", 1, ATTN_NORM); + self.outwsound = 0; + self.inwsound = 1; + //POX v1.1 - not needed, see footstep routine in player.qc + //self.onwsound = time + 1.9; + } + + self.uwmuffle = time; + + /* POX v1.1 - now done in footstep routine + if (self.onwsound < time) + { + if (random() < 0.5) + sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM); + else + sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM); + + self.onwsound = time + random()*2; + } + */ + } + +}; + +void() CheckWaterJump = +{ + local vector start, end; + +// check for a jump-out-of-water + makevectors (self.angles); + start = self.origin; + start_z = start_z + 8; + v_forward_z = 0; + normalize(v_forward); + end = start + v_forward*24; + traceline (start, end, TRUE, self); + if (trace_fraction < 1) + { // solid at waist + start_z = start_z + self.maxs_z - 8; + end = start + v_forward*24; + self.movedir = trace_plane_normal * -50; + traceline (start, end, TRUE, self); + if (trace_fraction == 1) + { // open at eye level + self.flags = self.flags | FL_WATERJUMP; + self.velocity_z = 225; + self.flags = self.flags - (self.flags & FL_JUMPRELEASED); + self.teleport_time = time + 2; // safety net + return; + } + } +}; + + +/* +================ +PlayerPreThink + +Called every frame before physics are run +================ +*/ +void() PlayerPreThink = +{ + + local float mspeed, aspeed; + local float r; + + if (intermission_running) + { + IntermissionThink (); // otherwise a button could be missed between + return; // the think tics + } + + if (self.view_ofs == '0 0 0') + return; // intermission or finale + + +//ChaseCam + if ( self.aflag ) + CCamChasePlayer (); +//ChaseCam + + makevectors (self.v_angle); // is this still used + + CheckRules (); + WaterMove (); + + if (self.waterlevel == 2) + CheckWaterJump (); + + + if (self.deadflag >= DEAD_DEAD) + { + PlayerDeathThink (); + return; + } + + if (self.deadflag == DEAD_DYING) + return; // dying, so do nothing + + if (self.button2) + { + PlayerJump (); + } + else + self.flags = self.flags | FL_JUMPRELEASED; + +// teleporters can force a non-moving pause time + if (time < self.pausetime) + self.velocity = '0 0 0'; + +/* BEST WEAPON MARKER */ +if (deathmatch & DM_AUTOSWITCH) +{ + if(time > self.attack_finished && self.currentammo == 0 && self.weapon != IT_BONESAW) + { + self.weapon = W_BestWeapon (); + W_SetCurrentAmmo (); + } +} +/* BEST WEAPON MARKER */ +}; + +/* +================ +CheckPowerups + +Check for turning off powerups +================ +*/ + +void() CheckPowerups = +{ + + if (self.health <= 0) + return; + +//POX - 1.01b - Rot armour down to 150 +//Armour Rot + if (self.armorvalue > 150 && self.armor_rot < time) + { + self.armorvalue = self.armorvalue - 1; + self.armor_rot = time + 1; + + //POX v1.1 - change armour to Yellow + if (self.armorvalue == 150) + self.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR2; + } + +// invisibility + if (self.invisible_finished) + { +// sound and screen flash when items starts to run out + if (self.invisible_sound < time) + { + sound (self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE); + self.invisible_sound = time + ((random() * 3) + 1); + } + + + if (self.invisible_finished < time + 3) + { + if (self.invisible_time == 1) + { + sprint (self, "Cloak is failing...\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM); + self.invisible_time = time + 1; + } + + if (self.invisible_time < time) + { + self.invisible_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + + if (self.invisible_finished < time) + { // just stopped + self.items = self.items - IT_INVISIBILITY; + self.invisible_finished = 0; + self.invisible_time = 0; + } + + // use the eyes + self.frame = 0; + self.modelindex = modelindex_eyes; + } + else + //self.modelindex = modelindex_player; // don't use eyes + getmodel(self.weapon, self); + + +// invincibility + if (self.invincible_finished) + { +// sound and screen flash when items starts to run out + if (self.invincible_finished < time + 3) + { + if (self.invincible_time == 1) + { + sprint (self, "MegaShields are almost burned out\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM); + self.invincible_time = time + 1; + } + + if (self.invincible_time < time) + { + self.invincible_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + + if (self.invincible_finished < time) + { // just stopped + self.items = self.items - IT_INVULNERABILITY; + self.invincible_time = 0; + self.invincible_finished = 0; + } + + //POX - ignore light effects in Dark Mode + if (!(deathmatch & DM_DARK)) + { + if (self.invincible_finished > time) + self.effects = self.effects | EF_DIMLIGHT; + else + self.effects = self.effects - (self.effects & EF_DIMLIGHT); + } + } + +// super damage + if (self.super_damage_finished) + { + +// sound and screen flash when items starts to run out + + if (self.super_damage_finished < time + 3) + { + if (self.super_time == 1) + { + sprint (self, "Quad Damage is wearing off\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM); + self.super_time = time + 1; + } + + if (self.super_time < time) + { + self.super_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + + if (self.super_damage_finished < time) + { // just stopped + self.items = self.items - IT_QUAD; + self.super_damage_finished = 0; + self.super_time = 0; + } + + //POX - ignore light effects in Dark Mode + if (!(deathmatch & DM_DARK)) + { + if (self.super_damage_finished > time) + self.effects = self.effects | EF_DIMLIGHT; + else + self.effects = self.effects - (self.effects & EF_DIMLIGHT); + } + } + +// suit + if (self.radsuit_finished) + { + self.air_finished = time + 12; // don't drown + +// sound and screen flash when items starts to run out + if (self.radsuit_finished < time + 3) + { + if (self.rad_time == 1) + { + sprint (self, "Air supply in Biosuit expiring\n"); + stuffcmd (self, "bf\n"); + sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM); + self.rad_time = time + 1; + } + + if (self.rad_time < time) + { + self.rad_time = time + 1; + stuffcmd (self, "bf\n"); + } + } + + if (self.radsuit_finished < time) + { // just stopped + self.items = self.items - IT_SUIT; + self.rad_time = 0; + self.radsuit_finished = 0; + } + } + +}; + + +/* +================ +PlayerPostThink + +Called every frame after physics are run +================ +*/ +void() PlayerPostThink = +{ + + local float mspeed, aspeed; + local float r; + + if (time < 1.5) + stuffcmd (self, "bf\n");//POX v1.2 - clear overrun v_cshifts! + + + //POX v1.2 - cshift fix (colour_light) + if (self.cshift_finished < time) + { + if (!self.cshift_off) + { + stuffcmd (self, "v_cshift 0 0 0 0\n"); + self.cshift_off = TRUE; + } + } + +// KasCam -> + if ((self.classname == "KasCam") && !intermission_running) + { + CamThink(); + return; + } +// <- KasCam + + +//POX - Menu System + if (deathmatch && world.model == "maps/start.bsp" && !intermission_running) + { + MenuThink(); + return; + } + + if (self.view_ofs == '0 0 0') + return; // intermission or finale + + + if (self.deadflag) + return; + +// do weapon stuff + + W_WeaponFrame (); + +// check to see if player landed and play landing sound + if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) && (self.health > 0)) + { + //DISABLED since additions to PlayerPreThink() now handle ALL water sounds + + //if (self.watertype == CONTENT_WATER) + // sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM); + //else + + if (self.jump_flag < -650) + { + T_Damage (self, world, world, 5); + sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM); + self.deathtype = "falling"; + } + else + sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM); + + self.jump_flag = 0; + } + + if (!(self.flags & FL_ONGROUND)) + self.jump_flag = self.velocity_z; + + CheckPowerups (); + + if (time < 15 && (!deathmatch)) + centerprint(self, "Paroxysm is Deathmatch Only.\nSelect Multiplayer in the main menu\n"); + //else if (time < 3) + // centerprint(self, "Paroxysm v1.3\n"); + + +}; + + +/* +=========== +ClientConnect + +called when a player connects to a server +============ +*/ +void() ClientConnect = +{ + //local float cno; + +// KasCam -> + if ((self.netname == "CamClient") || (self.netname == "")) + { + stuffcmd (self, "autocam\n"); + //self.impulse = 250; + } + else + { +// <- KasCam + + //POX - v1.3 - default TargetID to on + self.target_id_temp = TRUE; + self.target_id_toggle = TRUE; + + //POX v.11 FINALLY nailed this one + //Players have 30 seconds to join an LMS game, or they are put into observer till changelevel + if (!deathmatch & DM_LMS) + { + bprint (self.netname); + bprint (" entered the game\n"); + centerprint(self, "Paroxysm v1.3\n"); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } + else if ((time < 30) && (deathmatch & DM_LMS)) //POX 1.21 + { + bprint (self.netname); + bprint (" entered the game\n"); + centerprint(self, "Paroxysm v1.3\nLast Man Standing Rules Apply."); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } + else + { + bprint (self.netname); + bprint (" entered the game late! Observing till next round.\n"); + + self.LMS_late = TRUE; + stuffcmd (self, "autocam\n"); + //self.impulse = 250; + } + } + + // a client connecting during an intermission can cause problems + if (intermission_running) + ExitIntermission (); + +}; + + +/* +=========== +ClientDisconnect + +called when a player disconnects from a server +============ +*/ +void() ClientDisconnect = +{ + if (gameover) + return; + // if the level end trigger has been activated, just return + // since they aren't *really* leaving + + + // let everyone else know +// KasCam -> + if (CamDisconnect()) { +// KasCam <- + bprint (self.netname); + bprint (" left the game with "); + bprint (ftos(self.frags)); + bprint (" frags\n"); + + clientSetFree (self.fClientNo); + + //POX v1.11 + if (deathmatch & DM_LMS && self.classname == "player") + { + lms_plrcount = lms_plrcount - 1; + + if (lms_plrcount <= 1) //1 player left so end the game + lms_gameover = TRUE; + } + + //sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE); + set_suicide_frame (); +// KasCam -> + } +// <- KasCam +}; + +/* +=========== +ClientObituary + +called when a player dies +============ +*/ +void(entity targ, entity attacker) ClientObituary = +{ + local float rnum; + local string deathstring, deathstring2; + local entity nar; + rnum = random(); + + //POX v1.2 - Changed this so obituary taunts work in DM_LMS + //Lastman Standing POX v1.11 cleaned this up a bit + if (deathmatch & DM_LMS) + { + if (targ.classname == "player") + targ.frags = targ.frags - 1; + + if (targ.frags <= 0 && (targ.classname == "player")) + { + bprint(targ.netname); + bprint(" is eliminated!\n"); + lms_plrcount = lms_plrcount - 1; + + sound (targ, CHAN_BODY, "nar/n_elim.wav", 1, ATTN_NONE); + + if (lms_plrcount <= 1) //1 player left so end the game + lms_gameover = TRUE; + + return; + } + + } + + + if (targ.classname == "player") + { + if (attacker.classname == "teledeath") + { + bprint (targ.netname); + bprint (" was telefragged by "); + bprint (attacker.owner.netname); + bprint ("\n"); + + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + attacker.owner.frags = attacker.owner.frags + 1; + + return; + } + + if (attacker.classname == "teledeath2") + { + bprint ("MegaShields deflect "); + bprint (targ.netname); + bprint ("'s telefrag\n"); + + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + targ.frags = targ.frags - 1; + + return; + } + + if (attacker.classname == "player") + { + if (targ == attacker) + { + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + attacker.frags = attacker.frags - 1;// killed self + + bprint (targ.netname); + + if (targ.weapon == 64 && targ.waterlevel > 1) + { + bprint (" discharges into the water.\n"); + return; + } + if (targ.weapon == IT_GRENADE_LAUNCHER) + bprint (" tries to put the pin back in\n"); + else + bprint (" becomes bored with life\n"); + + return; + } + else if ( (teamplay == 2) && (targ.team > 0)&&(targ.team == attacker.team) ) + { + if (rnum < 0.25) + deathstring = " mows down a teammate\n"; + else if (rnum < 0.50) + deathstring = " checks his glasses\n"; + else if (rnum < 0.75) + deathstring = " gets a frag for the other team\n"; + else + deathstring = " loses another friend\n"; + bprint (attacker.netname); + bprint (deathstring); + + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + attacker.frags = attacker.frags - 1; + + return; + } + else + { + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + attacker.frags = attacker.frags + 1; + + rnum = attacker.weapon; + + if (rnum == IT_BONESAW) + { + deathstring = " was butchered by "; + deathstring2 = "\n"; + } + if (rnum == IT_TSHOT) + { + deathstring = " chewed on "; + deathstring2 = "'s boomstick\n"; + } + + //Changed Combo Gun blurbs depending on the ammo + if ((rnum == IT_COMBOGUN) && (attacker.which_ammo == 1)) + { + deathstring = " swallowed "; + deathstring2 = "'s pumkin ball\n"; + if (targ.health < -40) + { + deathstring = " was gibbed by "; + deathstring2 = "'s impact grenade\n"; + } + } + + if ((rnum == IT_COMBOGUN) && (attacker.which_ammo == 0)) + { + deathstring = " ate 2 loads of "; + deathstring2 = "'s buckshot\n"; + } + + + if (rnum == IT_PLASMAGUN) + { + deathstring = " was plugged by "; + deathstring2 = "\n"; + if (targ.health < -40) + { + deathstring = " was discombobulated by "; + deathstring2 = "'s plasma burst\n"; + } + } + if (rnum == IT_SUPER_NAILGUN) + { + deathstring = " was nailed by "; + deathstring2 = "\n"; + } + if (rnum == IT_GRENADE_LAUNCHER) + { + deathstring = " eats "; + deathstring2 = "'s pineapple\n"; + if (targ.health < -40) + { + deathstring = " was gibbed by "; + deathstring2 = "'s grenade\n"; + } + } + if (rnum == IT_ROCKET_LAUNCHER) + { + deathstring = " rides "; + deathstring2 = "'s rocket\n"; + if (targ.health < -40) + { + deathstring = " was gibbed by "; + deathstring2 = "'s rockets\n" ; + } + } + + bprint (targ.netname); + bprint (deathstring); + bprint (attacker.netname); + bprint (deathstring2); + } + + return; + } + else + { + if (!(deathmatch & DM_LMS)) //POX 1.2 - do regular obituary taunts in LMS mode + targ.frags = targ.frags - 1; + + bprint (targ.netname); + + // tricks and traps + if (attacker.classname == "explo_box") + { + bprint (" blew up\n"); + return; + } + if (attacker.solid == SOLID_BSP && attacker != world) + { + bprint (" was squished\n"); + return; + } + if (attacker.classname == "trap_shooter" || attacker.classname == "trap_spikeshooter") + { + bprint (" was spiked\n"); + return; + } + if (attacker.classname == "fireball") + { + bprint (" ate a lavaball\n"); + return; + } + if (attacker.classname == "trigger_changelevel") + { + bprint (" tried to leave\n"); + return; + } + //POX - orphaned shrapnel grenade + if (attacker.classname == "shrapnel" || attacker.classname == "spikenal") + { + bprint (" got too close to a shrapnel bomb\n"); + return; + } + + // in-water deaths + + rnum = targ.watertype; + + if (rnum == -3) + { + if (random() < 0.5) + bprint (" sleeps with the fishes\n"); + else + bprint (" sucks it down\n"); + return; + } + else if (rnum == -4) + { + if (random() < 0.5) + bprint (" gulped a load of slime\n"); + else + bprint (" can't exist on slime alone\n"); + return; + } + else if (rnum == -5) + { + if (targ.health < -15) + { + bprint (" burst into flames\n"); + return; + } + if (random() < 0.5) + bprint (" turned into hot slag\n"); + else + bprint (" visits the Volcano God\n"); + return; + } + + // fell to their death? + if (targ.deathtype == "falling") + { + targ.deathtype = ""; + bprint (" fell to his death\n"); + return; + } + + // hell if I know; he's just dead!!! + bprint (" died\n"); + } + } +}; diff --git a/ParoxysmII/source/nq/combat.qc b/ParoxysmII/source/nq/combat.qc new file mode 100644 index 0000000..f16f133 --- /dev/null +++ b/ParoxysmII/source/nq/combat.qc @@ -0,0 +1,312 @@ + +void() T_MissileTouch; +void() info_player_start; +void(entity targ, entity attacker) ClientObituary; + +//void() monster_death_use; + + +//============================================================================ + +/* +============ +CanDamage + +Returns true if the inflictor can directly damage the target. Used for +explosions and melee attacks. +============ +*/ +float(entity targ, entity inflictor) CanDamage = +{ +// bmodels need special checking because their origin is 0,0,0 + if (targ.movetype == MOVETYPE_PUSH) + { + traceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), TRUE, self); + if (trace_fraction == 1) + return TRUE; + if (trace_ent == targ) + return TRUE; + return FALSE; + } + + traceline(inflictor.origin, targ.origin, TRUE, self); + if (trace_fraction == 1) + return TRUE; + traceline(inflictor.origin, targ.origin + '15 15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + traceline(inflictor.origin, targ.origin + '-15 -15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + traceline(inflictor.origin, targ.origin + '-15 15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + traceline(inflictor.origin, targ.origin + '15 -15 0', TRUE, self); + if (trace_fraction == 1) + return TRUE; + + return FALSE; +}; + + +/* +============ +Killed +============ +*/ +void(entity targ, entity attacker) Killed = +{ + local entity oself; + + oself = self; + self = targ; + + if (self.health < -99) + self.health = -99; // don't let sbar look bad if a player + + if (self.movetype == MOVETYPE_PUSH || self.movetype == MOVETYPE_NONE) + { // doors, triggers, etc + self.th_die (); + self = oself; + return; + } + + self.enemy = attacker; + +// bump the monster counter + if (self.flags & FL_MONSTER) + { + killed_monsters = killed_monsters + 1; + WriteByte (MSG_ALL, SVC_KILLEDMONSTER); + } + + ClientObituary(self, attacker); + + self.takedamage = DAMAGE_NO; + self.touch = SUB_Null; + + //monster_death_use(); + self.th_die(); + + self = oself; +}; + + +/* +============ +T_Damage + +The damage is coming from inflictor, but get mad at attacker +This should be the only function that ever reduces health. +============ +*/ +void(entity targ, entity inflictor, entity attacker, float damage) T_Damage= +{ + local vector dir; + local entity oldself; + local float save; + local float take; + + if (!targ.takedamage) + return; + +// used by buttons and triggers to set activator for target firing + damage_attacker = attacker; + +// check for quad damage powerup on the attacker + if (attacker.super_damage_finished > time) + damage = damage * 4; + +//POX - this was moved from below the armour save routines to above so armour isn't lost +// check for godmode or invincibility + if (targ.flags & FL_GODMODE) + return; + if (targ.invincible_finished >= time) + { + if (self.invincible_sound < time) + { + sound (targ, CHAN_ITEM, "items/protect3.wav", 1, ATTN_NORM); + self.invincible_sound = time + 2; + } + return; + } + +// save damage based on the target's armor level + + save = ceil(targ.armortype*damage); + if (save >= targ.armorvalue) + { + save = targ.armorvalue; + targ.armortype = 0; // lost all armor + targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)); + } + + targ.armorvalue = targ.armorvalue - save; + +//NEW Armour Stuff to dynamically change the type along with the value + if (targ.armorvalue > 150) + { + targ.armortype = 0.8; //Red Armour + targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3; + } + + else if (targ.armorvalue > 50) + { + targ.armortype = 0.8; //Yellow Armour + targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR2; + } + + else if (targ.armorvalue > 1) + { + targ.armortype = 0.8; //Blue Armour + targ.items = targ.items - (targ.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR1; + } + + take = ceil(damage-save); + +// add to the damage total for clients, which will be sent as a single +// message at the end of the frame +// FIXME: remove after combining shotgun blasts? + if (targ.flags & FL_CLIENT) + { + targ.dmg_take = targ.dmg_take + take; + targ.dmg_save = targ.dmg_save + save; + targ.dmg_inflictor = inflictor; + } + +// figure momentum add - POX - tweaked... + if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )// FrikBot ((targ.ishuman == FALSE) && (targ.flags & FL_CLIENT)) ) ) // FrikBot + { + dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5; + dir = normalize(dir); + targ.velocity = targ.velocity + dir*damage*15; + } + +// team play damage avoidance + if ( (teamplay == 1) && (targ.team > 0)&&(targ.team == attacker.team) ) + return; + +// do the damage + targ.health = targ.health - take; + + if (targ.health <= 0) + { + Killed (targ, attacker); + return; + } + +// react to the damage + oldself = self; + self = targ; + +// Changed this out so monsters don't kill each other + /* + if ( (self.flags & FL_MONSTER) && attacker != world) + { + + + // Monsters only get mad at players and spawns, spawns hate everyone + + if (self != attacker && attacker != self.enemy) + { + if ( (attacker.classname == "player") || (attacker.classname == "monster_spawn") || (self.classname == "monster_spawn" ) ) + { + if (self.enemy.classname == "player") + self.oldenemy = self.enemy; + self.enemy = attacker; + FoundTarget (); + } + } + } + */ + + if (self.th_pain) + { + self.th_pain (attacker, take); + // nightmare mode monsters don't go into pain frames often + //if (skill == 3) + // self.pain_finished = time + 5; + } + + self = oldself; +}; + +/* +============ +T_RadiusDamage +============ +*/ +void(entity inflictor, entity attacker, float damage, entity ignore) T_RadiusDamage = +{ + local float points; + local entity head; + local vector org; + + head = findradius(inflictor.origin, damage+40); + + while (head) + { + if (head != ignore) + { + if (head.takedamage) + { + org = head.origin + (head.mins + head.maxs)*0.5; + points = 0.5*vlen (inflictor.origin - org); + if (points < 0) + points = 0; + points = damage - points; + if (head == attacker) + points = points * 0.5; + if (points > 0) + { + if (CanDamage (head, inflictor)) + { // shambler takes half damage from all explosions + if (head.classname == "monster_shambler") + T_Damage (head, inflictor, attacker, points*0.5); + else + T_Damage (head, inflictor, attacker, points); + } + } + } + } + head = head.chain; + } +}; + +/* +============ +T_BeamDamage +============ +*/ +void(entity attacker, float damage) T_BeamDamage = +{ + local float points; + local entity head; + + head = findradius(attacker.origin, damage+40); + + while (head) + { + if (head.takedamage) + { + points = 0.5*vlen (attacker.origin - head.origin); + if (points < 0) + points = 0; + points = damage - points; + if (head == attacker) + points = points * 0.5; + if (points > 0) + { + if (CanDamage (head, attacker)) + { + if (head.classname == "monster_shambler") + T_Damage (head, attacker, attacker, points*0.5); + else + T_Damage (head, attacker, attacker, points); + } + } + } + head = head.chain; + } +}; + diff --git a/ParoxysmII/source/nq/defs.qc b/ParoxysmII/source/nq/defs.qc new file mode 100644 index 0000000..d934565 --- /dev/null +++ b/ParoxysmII/source/nq/defs.qc @@ -0,0 +1,780 @@ + +/* +============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + +============================================================================== +*/ + +// +// system globals +// +entity self; +entity other; +entity world; +float time; +float frametime; + +float force_retouch; // force all entities to touch triggers + // next frame. this is needed because + // non-moving things don't normally scan + // for triggers, and when a trigger is + // created (like a teleport trigger), it + // needs to catch everything. + // decremented each frame, so set to 2 + // to guarantee everything is touched +string mapname; + +float deathmatch; +float coop; +float teamplay; + +float serverflags; // propagated from level to level, used to + // keep track of completed episodes + +float total_secrets; +float total_monsters; + +float found_secrets; // number of secrets found +float killed_monsters; // number of monsters killed + + +// spawnparms are used to encode information about clients across server +// level changes +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; + +// +// global variables set by built in functions +// +vector v_forward, v_up, v_right; // set by makevectors() + +// set by traceline / tracebox +float trace_allsolid; +float trace_startsolid; +float trace_fraction; +vector trace_endpos; +vector trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; + +entity msg_entity; // destination of single entity writes + +// +// required prog functions +// +void() main; // only for testing + +void() StartFrame; + +void() PlayerPreThink; +void() PlayerPostThink; + +void() ClientKill; +void() ClientConnect; +void() PutClientInServer; // call after setting the parm1... parms +void() ClientDisconnect; + +void() SetNewParms; // called when a client first connects to + // a server. sets parms so they can be + // saved off for restarts + +void() SetChangeParms; // call to set parms for self so they can + // be saved for a level transition + + +//================================================ +void end_sys_globals; // flag for structure dumping +//================================================ + +/* +============================================================================== + + SOURCE FOR ENTVARS_T C STRUCTURE + +============================================================================== +*/ + +// +// system fields (*** = do not set in prog code, maintained by C code) +// +.float modelindex; // *** model index in the precached list +.vector absmin, absmax; // *** origin + mins / maxs + +.float ltime; // local time for entity +.float movetype; +.float solid; + +.vector origin; // *** +.vector oldorigin; // *** +.vector velocity; +.vector angles; +.vector avelocity; + +.vector punchangle; // temp angle adjust from damage or recoil + +.string classname; // spawn function +.string model; +.float frame; +.float skin; +.float effects; + +.vector mins, maxs; // bounding box extents reletive to origin +.vector size; // maxs - mins + +.void() touch; +.void() use; +.void() think; +.void() blocked; // for doors or plats, called when can't push other + +.float nextthink; +.entity groundentity; + +// stats +.float health; +.float frags; +.float weapon; // one of the IT_TSHOT, etc flags +.string weaponmodel; +.float weaponframe; +.float currentammo; +.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells; + +.float items; // bit flags + +.float takedamage; +.entity chain; +.float deadflag; + +.vector view_ofs; // add to origin to get eye point + + +.float button0; // fire +.float button1; // use +.float button2; // jump + +.float impulse; // weapon changes + +.float fixangle; +.vector v_angle; // view / targeting angle for players +.float idealpitch; // calculated pitch angle for lookup up slopes + + +.string netname; + +.entity enemy; + +.float flags; + +.float colormap; +.float team; + +.float max_health; // players maximum health is stored here + +.float teleport_time; // don't back up + +.float armortype; // save this fraction of incoming damage +.float armorvalue; + +.float waterlevel; // 0 = not in, 1 = feet, 2 = wast, 3 = eyes +.float watertype; // a contents value + +.float ideal_yaw; +.float yaw_speed; + +.entity aiment; + +.entity goalentity; // a movetarget or an enemy + +.float spawnflags; + +.string target; +.string targetname; + +// damage is accumulated through a frame. and sent as one single +// message, so the super shotgun doesn't generate huge messages +.float dmg_take; +.float dmg_save; +.entity dmg_inflictor; + +.entity owner; // who launched a missile +.vector movedir; // mostly for doors, but also used for waterjump + +.string message; // trigger messages + +.float sounds; // either a cd track number or sound number + +.string noise, noise1, noise2, noise3; // contains names of wavs to play + +//================================================ +void end_sys_fields; // flag for structure dumping +//================================================ + +/* +============================================================================== + + VARS NOT REFERENCED BY C CODE + +============================================================================== +*/ + + +// +// constants +// + +float FALSE = 0; +float TRUE = 1; + +// edict.flags +float FL_FLY = 1; +float FL_SWIM = 2; +float FL_CLIENT = 8; // set for all client edicts +float FL_INWATER = 16; // for enter / leave water splash +float FL_MONSTER = 32; +float FL_GODMODE = 64; // player cheat +float FL_NOTARGET = 128; // player cheat +float FL_ITEM = 256; // extra wide size for bonus items +float FL_ONGROUND = 512; // standing on something +float FL_PARTIALGROUND = 1024; // not all corners are valid +float FL_WATERJUMP = 2048; // player jumping out of water +float FL_JUMPRELEASED = 4096; // for jump debouncing + +// edict.movetype values +float MOVETYPE_NONE = 0; // never moves +//float MOVETYPE_ANGLENOCLIP = 1; +//float MOVETYPE_ANGLECLIP = 2; +float MOVETYPE_WALK = 3; // players only +float MOVETYPE_STEP = 4; // discrete, not real time unless fall +float MOVETYPE_FLY = 5; +float MOVETYPE_TOSS = 6; // gravity +float MOVETYPE_PUSH = 7; // no clip to world, push and crush +float MOVETYPE_NOCLIP = 8; +float MOVETYPE_FLYMISSILE = 9; // fly with extra size against monsters +float MOVETYPE_BOUNCE = 10; +float MOVETYPE_BOUNCEMISSILE = 11; // bounce with extra size + +// edict.solid values +float SOLID_NOT = 0; // no interaction with other objects +float SOLID_TRIGGER = 1; // touch on edge, but not blocking +float SOLID_BBOX = 2; // touch on edge, block +float SOLID_SLIDEBOX = 3; // touch on edge, but not an onground +float SOLID_BSP = 4; // bsp clip, touch on edge, block + +// range values +float RANGE_MELEE = 0; +float RANGE_NEAR = 1; +float RANGE_MID = 2; +float RANGE_FAR = 3; + +// deadflag values + +float DEAD_NO = 0; +float DEAD_DYING = 1; +float DEAD_DEAD = 2; +float DEAD_RESPAWNABLE = 3; + +// takedamage values + +float DAMAGE_NO = 0; +float DAMAGE_YES = 1; +float DAMAGE_AIM = 2; + +// items +float IT_BONESAW = 4096; +float IT_TSHOT = 1; +float IT_COMBOGUN = 2; +float IT_PLASMAGUN = 4; +float IT_SUPER_NAILGUN = 8; +float IT_GRENADE_LAUNCHER = 16; +float IT_ROCKET_LAUNCHER = 32; +float IT_FLAMETHROWER = 64; +float IT_EXTRA_WEAPON = 128; + +float IT_SHELLS = 256; +float IT_NAILS = 512; +float IT_ROCKETS = 1024; +float IT_CELLS = 2048; + +float IT_ARMOR1 = 8192; +float IT_ARMOR2 = 16384; +float IT_ARMOR3 = 32768; +float IT_SUPERHEALTH = 65536; + +float IT_KEY1 = 131072; +float IT_KEY2 = 262144; + +float IT_INVISIBILITY = 524288; +float IT_INVULNERABILITY = 1048576; +float IT_SUIT = 2097152; +float IT_QUAD = 4194304; + +// point content values + +float CONTENT_EMPTY = -1; +float CONTENT_SOLID = -2; +float CONTENT_WATER = -3; +float CONTENT_SLIME = -4; +float CONTENT_LAVA = -5; +float CONTENT_SKY = -6; + +float STATE_TOP = 0; +float STATE_BOTTOM = 1; +float STATE_UP = 2; +float STATE_DOWN = 3; + +vector VEC_ORIGIN = '0 0 0'; +vector VEC_HULL_MIN = '-16 -16 -24'; +vector VEC_HULL_MAX = '16 16 32'; + +vector VEC_HULL2_MIN = '-32 -32 -24'; +vector VEC_HULL2_MAX = '32 32 64'; + +// protocol bytes +float SVC_TEMPENTITY = 23; +float SVC_KILLEDMONSTER = 27; +float SVC_FOUNDSECRET = 28; +float SVC_INTERMISSION = 30; +float SVC_FINALE = 31; +float SVC_CDTRACK = 32; +float SVC_SELLSCREEN = 33; + + +float TE_SPIKE = 0; +float TE_SUPERSPIKE = 1; +float TE_GUNSHOT = 2; +float TE_EXPLOSION = 3; +float TE_TAREXPLOSION = 4; +float TE_LIGHTNING1 = 5; +float TE_LIGHTNING2 = 6; +float TE_WIZSPIKE = 7; +float TE_KNIGHTSPIKE = 8; +float TE_LIGHTNING3 = 9; +float TE_LAVASPLASH = 10; +float TE_TELEPORT = 11; + +// sound channels +// channel 0 never willingly overrides +// other channels (1-7) allways override a playing sound on that channel +float CHAN_AUTO = 0; +float CHAN_WEAPON = 1; +float CHAN_VOICE = 2; +float CHAN_ITEM = 3; +float CHAN_BODY = 4; + +float ATTN_NONE = 0; +float ATTN_NORM = 1; +float ATTN_IDLE = 2; +float ATTN_STATIC = 3; + +// update types + +float UPDATE_GENERAL = 0; +float UPDATE_STATIC = 1; +float UPDATE_BINARY = 2; +float UPDATE_TEMP = 3; + +// entity effects + +float EF_BRIGHTFIELD = 1; +float EF_MUZZLEFLASH = 2; +float EF_BRIGHTLIGHT = 4; +float EF_DIMLIGHT = 8; + + +// messages +float MSG_BROADCAST = 0; // unreliable to all +float MSG_ONE = 1; // reliable to one (msg_entity) +float MSG_ALL = 2; // reliable to all +float MSG_INIT = 3; // write to the init string + +//================================================ + +// +// globals +// +float movedist; +float gameover; // set when a rule exits + +string string_null; // null string, nothing should be held here +float empty_float; + +entity newmis; // launch_spike sets this after spawning it + +entity activator; // the entity that activated a trigger or brush + +entity damage_attacker; // set by T_Damage +float framecount; + +float skill; + +//================================================ + +// +// world fields (FIXME: make globals) +// +.string wad; +.string map; +.float worldtype; // 0=medieval 1=metal 2=base + +//================================================ + +.string killtarget; + +// +// quakeed fields +// +.float light_lev; // not used by game, but parsed by light util +.float style; + + +// +// monster ai +// +.void() th_stand; +.void() th_walk; +.void() th_run; +.void() th_missile; +.void() th_melee; +.void(entity attacker, float damage) th_pain; +.void() th_die; + +.entity oldenemy; // mad at this player before taking damage + +.float speed; + +.float lefty; + +.float search_time; +.float attack_state; + +float AS_STRAIGHT = 1; +float AS_SLIDING = 2; +float AS_MELEE = 3; +float AS_MISSILE = 4; + +// +// player only fields +// + +.entity spawnmaster; +.entity lastowner; +.float voided; +.float walkframe; + +.float attack_finished; +.float pain_finished; + +//New fields for timing second triggers +.float st_sshotgun; // next attack for SuperShotgun +.float st_pball; // next attack for PumkinBall (SuperShotgun's Second Trigger) +.float st_mplasma; // next attack for MegaPlasma Burst (PlasmaGun's Second Trigger) +.float st_plasma; // next attack for PlasmaGun +.float st_mine; // next attack for MinePlant (GrenadeLauncher's Second Trigger) +.float st_grenade; // next attack for GrenadeLauncher +.float st_nailgun; // next attack for Nailgun +.float st_shrapnel; // next attack for ShrapnelBomb (Nailgun's Second Trigger) + +.float LorR; // 1=Left or 0=Right Barrel used by PlasmaGun +.float which_ammo; // used by SuperShotgun to clean up ammo swithing on Second trigger +.float missfire_finished; // used to seperate attacks and missfires + +.float idle_soundfin; // used to allow for longer idle sounds + +.float invincible_finished; +.float invisible_finished; +.float super_damage_finished; +.float radsuit_finished; + +.float invincible_time, invincible_sound; +.float invisible_time, invisible_sound; +.float super_time, super_sound; +.float rad_time; +.float fly_sound; + +.float axhitme; + +.float show_hostile; // set to time+0.2 whenever a client fires a + // weapon or takes damage. Used to alert + // monsters that otherwise would let the player go +.float jump_flag; // player jump flag +.float swim_flag; // player swimming sound flag +.float air_finished; // when time > air_finished, start drowning +.float bubble_count; // keeps track of the number of bubbles +.string deathtype; // keeps track of how the player died + +// +// object stuff +// +.string mdl; +.vector mangle; // angle at start + +.vector oldorigin; // only used by secret door + +.float t_length, t_width; + + +// +// doors, etc +// +.vector dest, dest1, dest2; +.float wait; // time from firing to restarting +.float delay; // time from activation to firing +.entity trigger_field; // door's trigger entity +.string noise4; + +// +// monsters +// +.float pausetime; +.entity movetarget; +float AS_DODGING = 5; + + +// +// doors +// +.float aflag; +.float dmg; // damage done by door when hit + +// +// misc +// +.float cnt; // misc flag + +// +// subs +// +.void() think1; +.vector finaldest, finalangle; + +// +// triggers +// +.float count; // for counting triggers + + +// +// plats / doors / buttons +// +.float lip; +.float state; +.vector pos1, pos2; // top and bottom positions +.float height; + +// +// sounds +// +.float waitmin, waitmax; +.float distance; +.float volume; + +// +// footsteps +// +.float spawnsilent; +.vector old_velocity; + +//POX v1.2 EarthQuakes REMOVED! + +//nobleed for triggers +.float nobleed; + +// keeps track of number of bots +float botcounter; + +//=========================================================================== + + +// +// builtin functions +// + +void(vector ang) makevectors = #1; // sets v_forward, etc globals +void(entity e, vector o) setorigin = #2; +void(entity e, string m) setmodel = #3; // set movetype and solid first +void(entity e, vector min, vector max) setsize = #4; +// #5 was removed +void() break = #6; +float() random = #7; // returns 0 - 1 +//void(entity e, float chan, string samp, float vol, float atten) sound = #8;//Frik +vector(vector v) normalize = #9; +void(string e) error = #10; +void(string e) objerror = #11; +float(vector v) vlen = #12; +float(vector v) vectoyaw = #13; +entity() spawn = #14; +void(entity e) remove = #15; + +// sets trace_* globals +// nomonsters can be: +// An entity will also be ignored for testing if forent == test, +// forent->owner == test, or test->owner == forent +// a forent of world is ignored +void(vector v1, vector v2, float nomonsters, entity forent) traceline = #16; + +entity() checkclient = #17; // returns a client to look for +entity(entity start, .string fld, string match) find = #18; +string(string s) precache_sound = #19; +string(string s) precache_model = #20; +//void(entity client, string s)stuffcmd = #21;//Frik + +// added to support variable string messages +//void(entity client, string s1, string s2, string s3)stuffcmd2 = #21; + +entity(vector org, float rad) findradius = #22; +void(string s) bprint = #23; +//void(entity client, string s) sprint = #24;//Frik +void(string s) dprint = #25; +string(float f) ftos = #26; +string(vector v) vtos = #27; +void() coredump = #28; // prints all edicts +void() traceon = #29; // turns statment trace on +void() traceoff = #30; +void(entity e) eprint = #31; // prints an entire edict +float(float yaw, float dist) walkmove = #32; // returns TRUE or FALSE +// #33 was removed +float(float yaw, float dist) droptofloor= #34; // TRUE if landed on floor +void(float style, string value) lightstyle = #35; +float(float v) rint = #36; // round to nearest int +float(float v) floor = #37; // largest integer <= v +float(float v) ceil = #38; // smallest integer >= v +// #39 was removed +float(entity e) checkbottom = #40; // true if self is on ground +float(vector v) pointcontents = #41; // returns a CONTENT_* +// #42 was removed +float(float f) fabs = #43; +//vector(entity e, float speed) aim = #44;//Frik // returns the shooting vector +float(string s) cvar = #45; // return cvar.value +void(string s) localcmd = #46; // put string into local que +entity(entity e) nextent = #47; // for looping through all ents +void(vector o, vector d, float color, float count) particle = #48;// start a particle effect +void() ChangeYaw = #49; // turn towards self.ideal_yaw + // at self.yaw_speed +// #50 was removed +vector(vector v) vectoangles = #51; + +// +// direct client message generation +// +/*+FrikBot009 +void(float to, float f) WriteByte = #52; +void(float to, float f) WriteChar = #53; +void(float to, float f) WriteShort = #54; +void(float to, float f) WriteLong = #55; +void(float to, float f) WriteCoord = #56; +void(float to, float f) WriteAngle = #57; +void(float to, string s) WriteString = #58; +void(float to, entity s) WriteEntity = #59; +*///-FrikBot009 +// +// broadcast client message generation +// + +// void(float f) bWriteByte = #59; +// void(float f) bWriteChar = #60; +// void(float f) bWriteShort = #61; +// void(float f) bWriteLong = #62; +// void(float f) bWriteCoord = #63; +// void(float f) bWriteAngle = #64; +// void(string s) bWriteString = #65; +// void(entity e) bWriteEntity = #66; + +void(float step) movetogoal = #67; + +string(string s) precache_file = #68; // no effect except for -copy +void(entity e) makestatic = #69; +void(string s) changelevel = #70; + +//#71 was removed + +void(string var, string val) cvar_set = #72; // sets cvar.value + +//void(entity client, string s) centerprint = #73; // sprint, but in middle//Frik + +void(vector pos, string samp, float vol, float atten) ambientsound = #74; + +string(string s) precache_model2 = #75; // registered version only +string(string s) precache_sound2 = #76; // registered version only +string(string s) precache_file2 = #77; // registered version only + +//FrikBot009 +//void(entity e) setspawnparms = #78; // set parm1... to the + // values at level start + // for coop respawn + +//============================================================================ + +// +// subs.qc +// +void(vector tdest, float tspeed, void() func) SUB_CalcMove; +void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt; +void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove; +void() SUB_CalcMoveDone; +void() SUB_CalcAngleMoveDone; +void() SUB_Null; +void() SUB_UseTargets; +void() SUB_Remove; + +// +// combat.qc +// +void(entity targ, entity inflictor, entity attacker, float damage) T_Damage; + +float (entity e, float healamount, float ignore) T_Heal; // health function + +float(entity targ, entity inflictor) CanDamage; + +//POX - A few bot variables (these are used by clients) +//.float total_spawned_bots; + +/* +.entity bot_a; +.entity bot_b; +.entity bot_c; +.entity bot_d; +.entity bot_e; +.entity bot_f; +.entity bot_g; +.entity bot_h; +*/ + +//Used by norse_waterlevel +.float watertype2; + +//POX - New DM option constants +float DM_PREDATOR = 2; +float DM_DARK = 4; +float DM_LMS = 8; +float DM_FFA = 16; +float DM_GIB = 32; +float DM_AUTOSWITCH = 128; +float DM_NOBOTS = 256; + +//POX - seperate fraglimit for LMS mode so changing it during a game isn't possible +float fraglimit_LMS; +float lms_plrcount; //Keeps track of the number of players in an LMS game +.float LMS_registered; //Last Man Standing camera fix +.float LMS_late; //Last Man Standing camera fix + +//POX - moved this from 'client.qc' since bot's use them as well +float modelindex_eyes, modelindex_player; +//More for visibile weapons +float modelindex_saw_p, modelindex_tshot_p, modelindex_combo_p, modelindex_plasma_p, modelindex_nail_p, modelindex_gren_p, modelindex_rock_p, modelindex_flame_p, modelindex_death; + +void() flash_toggle; // pOx - 1.4 - predeclare +.float flash_flag; // flashlight toggle flag +.entity flash; // flash entity + +.vector camview; //for the chase cam (not kascam) + +//POX v1.11 +.float gl_fix; //a hack for toggling gl_flashblend + +//POX v1.12 - Target ID stuff... +.float target_id_finished; +.float target_id_toggle; +.float target_id_same; +.entity last_target_id; diff --git a/ParoxysmII/source/nq/doors.qc b/ParoxysmII/source/nq/doors.qc new file mode 100644 index 0000000..d2c585b --- /dev/null +++ b/ParoxysmII/source/nq/doors.qc @@ -0,0 +1,797 @@ + +float DOOR_START_OPEN = 1; +float DOOR_DONT_LINK = 4; +float DOOR_GOLD_KEY = 8; +float DOOR_SILVER_KEY = 16; +float DOOR_TOGGLE = 32; + +/* + +Doors are similar to buttons, but can spawn a fat trigger field around them +to open without a touch, and they link together to form simultanious +double/quad doors. + +Door.owner is the master door. If there is only one door, it points to itself. +If multiple doors, all will point to a single one. + +Door.enemy chains from the master door through all doors linked in the chain. + +*/ + +/* +============================================================================= + +THINK FUNCTIONS + +============================================================================= +*/ + +void() door_go_down; +void() door_go_up; + +void() door_blocked = +{ + T_Damage (other, self, self, self.dmg); + +// if a door has a negative wait, it would never come back if blocked, +// so let it just squash the object to death real fast + if (self.wait >= 0) + { + if (self.state == STATE_DOWN) + door_go_up (); + else + door_go_down (); + } +}; + + +void() door_hit_top = +{ + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_TOP; + if (self.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + self.think = door_go_down; + self.nextthink = self.ltime + self.wait; +}; + +void() door_hit_bottom = +{ + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_BOTTOM; +}; + +void() door_go_down = +{ + sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + + self.state = STATE_DOWN; + SUB_CalcMove (self.pos1, self.speed, door_hit_bottom); +}; + +void() door_go_up = +{ + if (self.state == STATE_UP) + return; // allready going up + + if (self.state == STATE_TOP) + { // reset top wait time + self.nextthink = self.ltime + self.wait; + return; + } + + sound (self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + self.state = STATE_UP; + SUB_CalcMove (self.pos2, self.speed, door_hit_top); + + SUB_UseTargets(); +}; + + +/* +============================================================================= + +ACTIVATION FUNCTIONS + +============================================================================= +*/ + +void() door_fire = +{ + local entity oself; + local entity starte; + + if (self.owner != self) + objerror ("door_fire: self.owner != self"); + +// play use key sound + + if (self.items) + sound (self, CHAN_VOICE, self.noise4, 1, ATTN_NORM); + + self.message = string_null; // no more message + oself = self; + + if (self.spawnflags & DOOR_TOGGLE) + { + if (self.state == STATE_UP || self.state == STATE_TOP) + { + starte = self; + do + { + door_go_down (); + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; + return; + } + } + +// trigger all paired doors + starte = self; + do + { + door_go_up (); + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; +}; + + +void() door_use = +{ + local entity oself; + + self.message = ""; // door message are for touch only + self.owner.message = ""; + self.enemy.message = ""; + oself = self; + self = self.owner; + door_fire (); + self = oself; +}; + + +void() door_trigger_touch = +{ + if (other.classname == "mine") + return; + if (other.classname == "minearm") + return; + if (other.classname == "minearmed") + return; + + if (other.health <= 0) + return; + + if (time < self.attack_finished) + return; + self.attack_finished = time + 1; + + activator = other; + + self = self.owner; + door_use (); +}; + + +void() door_killed = +{ + local entity oself; + + oself = self; + self = self.owner; + self.health = self.max_health; + self.takedamage = DAMAGE_NO; // wil be reset upon return + door_use (); + self = oself; +}; + + +/* +================ +door_touch + +Prints messages and opens key doors +================ +*/ +void() door_touch = +{ + if (other.classname != "player") + return; + if (self.owner.attack_finished > time) + return; + + self.owner.attack_finished = time + 2; + + if (self.owner.message != "") + { + centerprint (other, self.owner.message); + sound (other, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } + +// key door stuff + if (!self.items) + return; + +// FIXME: blink key on player's status bar + if ( (self.items & other.items) != self.items ) + { + if (self.owner.items == IT_KEY1) + { + if (world.worldtype == 2) + { + centerprint (other, "You need the silver keycard"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 1) + { + centerprint (other, "You need the silver runekey"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 0) + { + centerprint (other, "You need the silver key"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + } + else + { + if (world.worldtype == 2) + { + centerprint (other, "You need the gold keycard"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 1) + { + centerprint (other, "You need the gold runekey"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + else if (world.worldtype == 0) + { + centerprint (other, "You need the gold key"); + sound (self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + } + } + + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + return; + } + + other.items = other.items - self.items; + self.touch = SUB_Null; + if (self.enemy) + self.enemy.touch = SUB_Null; // get paired door + door_use (); +}; + +/* +============================================================================= + +SPAWNING FUNCTIONS + +============================================================================= +*/ + + +entity(vector fmins, vector fmaxs) spawn_field = +{ + local entity trigger; + local vector t1, t2; + + trigger = spawn(); + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.owner = self; + trigger.touch = door_trigger_touch; + + t1 = fmins; + t2 = fmaxs; + setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); + return (trigger); +}; + + +float (entity e1, entity e2) EntitiesTouching = +{ + if (e1.mins_x > e2.maxs_x) + return FALSE; + if (e1.mins_y > e2.maxs_y) + return FALSE; + if (e1.mins_z > e2.maxs_z) + return FALSE; + if (e1.maxs_x < e2.mins_x) + return FALSE; + if (e1.maxs_y < e2.mins_y) + return FALSE; + if (e1.maxs_z < e2.mins_z) + return FALSE; + return TRUE; +}; + + +/* +============= +LinkDoors + + +============= +*/ +void() LinkDoors = +{ + local entity t, starte; + local vector cmins, cmaxs; + + if (self.enemy) + return; // already linked by another door + if (self.spawnflags & 4) + { + self.owner = self.enemy = self; + return; // don't want to link this door + } + + cmins = self.mins; + cmaxs = self.maxs; + + starte = self; + t = self; + + do + { + self.owner = starte; // master door + + if (self.health) + starte.health = self.health; + if (self.targetname) + starte.targetname = self.targetname; + if (self.message != "") + starte.message = self.message; + + t = find (t, classname, self.classname); + if (!t) + { + self.enemy = starte; // make the chain a loop + + // shootable, fired, or key doors just needed the owner/enemy links, + // they don't spawn a field + + self = self.owner; + + if (self.health) + return; + if (self.targetname) + return; + if (self.items) + return; + + self.owner.trigger_field = spawn_field(cmins, cmaxs); + + return; + } + + if (EntitiesTouching(self,t)) + { + if (t.enemy) + objerror ("cross connected doors"); + + self.enemy = t; + self = t; + + if (t.mins_x < cmins_x) + cmins_x = t.mins_x; + if (t.mins_y < cmins_y) + cmins_y = t.mins_y; + if (t.mins_z < cmins_z) + cmins_z = t.mins_z; + if (t.maxs_x > cmaxs_x) + cmaxs_x = t.maxs_x; + if (t.maxs_y > cmaxs_y) + cmaxs_y = t.maxs_y; + if (t.maxs_z > cmaxs_z) + cmaxs_z = t.maxs_z; + } + } while (1 ); + +}; + + +/*QUAKED func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE +if two doors touch, they are assumed to be connected and operate as a unit. + +TOGGLE causes the door to wait in both the start and end states for a trigger event. + +START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors). + +Key doors are allways wait -1. + +"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet +"angle" determines the opening direction +"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. +"health" if set, door must be shot open +"speed" movement speed (100 default) +"wait" wait before returning (3 default, -1 = never return) +"lip" lip remaining at end of move (8 default) +"dmg" damage to inflict when blocked (2 default) +"sounds" +0) no sound +1) stone + +***Changed for MAHEM +2) Base-Hydro +3) Tech-Servo +4) Buzz-Metal Lock + +*/ + +void() func_door = + +{ + + if (world.worldtype == 0) + { + precache_sound ("doors/medtry.wav"); + precache_sound ("doors/meduse.wav"); + self.noise3 = "doors/medtry.wav"; + self.noise4 = "doors/meduse.wav"; + } + else if (world.worldtype == 1) + { + precache_sound ("doors/runetry.wav"); + precache_sound ("doors/runeuse.wav"); + self.noise3 = "doors/runetry.wav"; + self.noise4 = "doors/runeuse.wav"; + } + else if (world.worldtype == 2) + { + precache_sound ("doors/basetry.wav"); + precache_sound ("doors/baseuse.wav"); + self.noise3 = "doors/basetry.wav"; + self.noise4 = "doors/baseuse.wav"; + } + else + { + dprint ("no worldtype set!\n"); + } + if (self.sounds == 0) + { + precache_sound ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = "misc/null.wav"; + self.noise2 = "misc/null.wav"; + } + if (self.sounds == 1) + { + precache_sound ("doors/drclos4.wav"); + precache_sound ("doors/doormv1.wav"); + self.noise1 = "doors/drclos4.wav"; + self.noise2 = "doors/doormv1.wav"; + } + if (self.sounds == 2) + { + precache_sound ("doors/hydro1.wav"); + precache_sound ("doors/hydro2.wav"); + self.noise2 = "doors/hydro1.wav"; + self.noise1 = "doors/hydro2.wav"; + } + if (self.sounds == 3) + { + precache_sound ("doors/stndr1.wav"); + precache_sound ("doors/stndr2.wav"); + self.noise2 = "doors/stndr1.wav"; + self.noise1 = "doors/stndr2.wav"; + } + if (self.sounds == 4) + { + precache_sound ("doors/ddoor1.wav"); + precache_sound ("doors/ddoor2.wav"); + self.noise1 = "doors/ddoor2.wav"; + self.noise2 = "doors/ddoor1.wav"; + } + + + SetMovedir (); + + self.max_health = self.health; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + setorigin (self, self.origin); + setmodel (self, self.model); + self.classname = "door"; + + self.blocked = door_blocked; + self.use = door_use; + + if (self.spawnflags & DOOR_SILVER_KEY) + self.items = IT_KEY1; + if (self.spawnflags & DOOR_GOLD_KEY) + self.items = IT_KEY2; + + if (!self.speed) + self.speed = 100; + if (!self.wait) + self.wait = 3; + if (!self.lip) + self.lip = 8; + if (!self.dmg) + self.dmg = 2; + + self.pos1 = self.origin; + self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip); + +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (self.spawnflags & DOOR_START_OPEN) + { + setorigin (self, self.pos2); + self.pos2 = self.pos1; + self.pos1 = self.origin; + } + + self.state = STATE_BOTTOM; + + if (self.health) + { + self.takedamage = DAMAGE_YES; + self.th_die = door_killed; + + self.nobleed = TRUE; + } + + if (self.items) + self.wait = -1; + + self.touch = door_touch; + +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + self.think = LinkDoors; + self.nextthink = self.ltime + 0.1; +}; + +/* +============================================================================= + +SECRET DOORS + +============================================================================= +*/ + +void() fd_secret_move1; +void() fd_secret_move2; +void() fd_secret_move3; +void() fd_secret_move4; +void() fd_secret_move5; +void() fd_secret_move6; +void() fd_secret_done; + +float SECRET_OPEN_ONCE = 1; // stays open +float SECRET_1ST_LEFT = 2; // 1st move is left of arrow +float SECRET_1ST_DOWN = 4; // 1st move is down from arrow +float SECRET_NO_SHOOT = 8; // only opened by trigger +float SECRET_YES_SHOOT = 16; // shootable even if targeted + + +void () fd_secret_use = +{ + local float temp; + + self.health = 10000; + + // exit if still moving around... + if (self.origin != self.oldorigin) + return; + + self.message = string_null; // no more message + + SUB_UseTargets(); // fire all targets / killtargets + + if (!(self.spawnflags & SECRET_NO_SHOOT)) + { + self.th_pain = SUB_Null; + self.takedamage = DAMAGE_NO; + } + self.velocity = '0 0 0'; + + // Make a sound, wait a little... + + sound(self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.nextthink = self.ltime + 0.1; + + temp = 1 - (self.spawnflags & SECRET_1ST_LEFT); // 1 or -1 + makevectors(self.mangle); + + if (!self.t_width) + { + if (self.spawnflags & SECRET_1ST_DOWN) + self. t_width = fabs(v_up * self.size); + else + self. t_width = fabs(v_right * self.size); + } + + if (!self.t_length) + self. t_length = fabs(v_forward * self.size); + + if (self.spawnflags & SECRET_1ST_DOWN) + self.dest1 = self.origin - v_up * self.t_width; + else + self.dest1 = self.origin + v_right * (self.t_width * temp); + + self.dest2 = self.dest1 + v_forward * self.t_length; + SUB_CalcMove(self.dest1, self.speed, fd_secret_move1); + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); +}; + +// Wait after first movement... +void () fd_secret_move1 = +{ + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move2; + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); +}; + +// Start moving sideways w/sound... +void () fd_secret_move2 = +{ + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + SUB_CalcMove(self.dest2, self.speed, fd_secret_move3); +}; + +// Wait here until time to go back... +void () fd_secret_move3 = +{ + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); + if (!(self.spawnflags & SECRET_OPEN_ONCE)) + { + self.nextthink = self.ltime + self.wait; + self.think = fd_secret_move4; + } +}; + +// Move backward... +void () fd_secret_move4 = +{ + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + SUB_CalcMove(self.dest1, self.speed, fd_secret_move5); +}; + +// Wait 1 second... +void () fd_secret_move5 = +{ + self.nextthink = self.ltime + 1.0; + self.think = fd_secret_move6; + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); +}; + +void () fd_secret_move6 = +{ + sound(self, CHAN_VOICE, self.noise2, 1, ATTN_NORM); + SUB_CalcMove(self.oldorigin, self.speed, fd_secret_done); +}; + +void () fd_secret_done = +{ + if (!self.targetname || self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + self.th_pain = fd_secret_use; + } + sound(self, CHAN_VOICE, self.noise3, 1, ATTN_NORM); +}; + +void () secret_blocked = +{ + if (time < self.attack_finished) + return; + self.attack_finished = time + 0.5; + T_Damage (other, self, self, self.dmg); +}; + +/* +================ +secret_touch + +Prints messages +================ +*/ +void() secret_touch = +{ + if (other.classname != "player") + return; + if (self.attack_finished > time) + return; + + self.attack_finished = time + 2; + + if (self.message) + { + centerprint (other, self.message); + sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } +}; + + +/*QUAKED func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot +Basic secret door. Slides back, then to the side. Angle determines direction. +wait = # of seconds before coming back +1st_left = 1st move is left of arrow +1st_down = 1st move is down from arrow +always_shoot = even if targeted, keep shootable +t_width = override WIDTH to move back (or height if going down) +t_length = override LENGTH to move sideways +"dmg" damage to inflict when blocked (2 default) + +If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. +"sounds" +1) medieval +2) metal +3) base +*/ + +void () func_door_secret = +{ + if (self.sounds == 0) + self.sounds = 3; + if (self.sounds == 1) + { + precache_sound ("doors/latch2.wav"); + precache_sound ("doors/winch2.wav"); + precache_sound ("doors/drclos4.wav"); + self.noise1 = "doors/latch2.wav"; + self.noise2 = "doors/winch2.wav"; + self.noise3 = "doors/drclos4.wav"; + } + if (self.sounds == 2) + { + precache_sound ("doors/airdoor1.wav"); + precache_sound ("doors/airdoor2.wav"); + self.noise2 = "doors/airdoor1.wav"; + self.noise1 = "doors/airdoor2.wav"; + self.noise3 = "doors/airdoor2.wav"; + } + if (self.sounds == 3) + { + precache_sound ("doors/basesec1.wav"); + precache_sound ("doors/basesec2.wav"); + self.noise2 = "doors/basesec1.wav"; + self.noise1 = "doors/basesec2.wav"; + self.noise3 = "doors/basesec2.wav"; + } + + if (!self.dmg) + self.dmg = 2; + + // Magic formula... + self.mangle = self.angles; + self.angles = '0 0 0'; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.classname = "door"; + setmodel (self, self.model); + setorigin (self, self.origin); + + self.touch = secret_touch; + self.blocked = secret_blocked; + self.speed = 50; + self.use = fd_secret_use; + if ( !self.targetname || self.spawnflags&SECRET_YES_SHOOT) + { + self.health = 10000; + self.takedamage = DAMAGE_YES; + self.th_pain = fd_secret_use; + self.th_die = fd_secret_use; + + //POX v1.1 - missed this one... + self.nobleed = TRUE; + } + self.oldorigin = self.origin; + if (!self.wait) + self.wait = 5; // 5 seconds before closing +}; diff --git a/ParoxysmII/source/nq/dynlight.qc b/ParoxysmII/source/nq/dynlight.qc new file mode 100644 index 0000000..dc58399 --- /dev/null +++ b/ParoxysmII/source/nq/dynlight.qc @@ -0,0 +1,116 @@ +/* +Frank Condello 09.28.98 - originally for the M.A.H.E.M. MacQuake Mod. +Now used in paroxysm + +EMAIL: pox@planetquake.com +WEB: http://www.planetquake.com/paroxysm/ +========================================================================= +This is the code for Moving Light Entities (Dynamic Lights) +It's basically identical to the the func_train code with out +stuff like damage and blocked code used for solid objects. +The light follows Targets and waits just like a train. +A new variable (dynlight_style) defines the type of light emitted. +Quake has only three dynamic light styles: +1 - Dimlight +2 - Brightlight +3 - Brightfield (Brightlight with 'shimmering' particles) +Dynamic Light can also be triggered, They currenty only start turned off. +*/ +.float dynlight_style; +void() dynlight_next; +void() dynlight_find; + +void() start_dynlight = +{ +//POX v1.1 - changed this for QW support +//No EF_BLUE or EF_RED in Quake - defaults to EF_DIMLIGHT + if (self.dynlight_style == 2) + self.effects = self.effects | EF_BRIGHTLIGHT; + + else if (self.dynlight_style == 3) + self.effects = self.effects | EF_BRIGHTFIELD; + + else + self.effects = self.effects | EF_DIMLIGHT; + + dynlight_next(); +}; + +void() dynlight_wait = +{ + if (self.wait) + self.nextthink = self.ltime + self.wait; + else + self.nextthink = self.ltime + 0.1; + + self.think = dynlight_next; +}; + +void() dynlight_next = +{ + local entity targ; + + targ = find (world, targetname, self.target); + self.target = targ.target; + if (!self.target) + objerror ("dynlight_next: no next target"); + if (targ.wait) + self.wait = targ.wait; + else + self.wait = 0; + SUB_CalcMove (targ.origin - self.mins, self.speed, dynlight_wait); +}; + +void() dynlight_find = + +{ + local entity targ; + + targ = find (world, targetname, self.target); + self.target = targ.target; + setorigin (self, targ.origin - self.mins); + if (!self.targetname) + { // not triggered, so start immediately + self.nextthink = self.ltime + 0.1; + self.think = start_dynlight; + } +}; + + +void() dynlight_use = +{ + if (self.think != dynlight_find) + return; // already activated + start_dynlight(); +}; + + +void() dyn_light = +{ +local float eff; + + precache_model ("progs/null.spr"); + + if ((self.dynlight_style < 1) || (self.dynlight_style > 3)) + self.dynlight_style = 1; + + if (!self.speed) + self.speed = 100; + + if (!self.target) + objerror ("dyn_light without a target"); + + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_PUSH; + self.use = dynlight_use; + self.classname = "dynlight"; + + setmodel (self, "progs/null.spr"); + setsize (self, '0 0 0', '0 0 0'); + setorigin (self, self.origin); + +// start trains on the second frame, to make sure their targets have had +// a chance to spawn + self.nextthink = self.ltime + 0.1; + self.think = dynlight_find; +}; diff --git a/ParoxysmII/source/nq/flash.qc b/ParoxysmII/source/nq/flash.qc new file mode 100644 index 0000000..82a4d47 --- /dev/null +++ b/ParoxysmII/source/nq/flash.qc @@ -0,0 +1,89 @@ +/* +POX - Flashlight code from the Flashlight Tutorial at the Inside3D website +Created by ShockMan eMail: shockman@brutality.com +*/ + +void() flash_update = +{ + // The Player is dead so turn the Flashlight off + if (self.owner.deadflag != DEAD_NO) + self.effects = 0; + // The Player is alive so turn On the Flashlight + else + self.effects = EF_DIMLIGHT; + + // Find out which direction player facing + makevectors (self.owner.v_angle); + + // Check if there is any things infront of the flashlight + traceline (self.owner.origin , (self.owner.origin+(v_forward * 500)) , FALSE , self); + + // Set the Flashlight's position + + setorigin (self, trace_endpos+(v_forward * -5)); + + // Repeat it in 0.02 seconds... + self.nextthink = time + 0.02; +}; + +void() flash_on = +{ + // Make a new entity to hold the Flashlight + local entity myflash; + + // spawn flash + myflash = spawn (); + myflash.movetype = MOVETYPE_NONE; + myflash.solid = SOLID_NOT; + // this uses the s_bubble.spr, if you want it to be completly + // invisible you need to create a one pixel trancparent spirit + // and use it here... + + //POX - changed it to a null sprite + setmodel (myflash, "progs/null.spr"); + setsize (myflash, '0 0 0', '0 0 0'); + + // Wire Player And Flashlight Together + myflash.owner = self; + self.flash = myflash; + + // give the flash a Name And Make It Glow + myflash.classname = "flash"; + myflash.effects = EF_DIMLIGHT; + + // Set Start Position + makevectors (self.v_angle); + traceline (self.origin , (self.origin+(v_forward * 500)) , FALSE , self); + setorigin (myflash, trace_endpos); + + // Start Flashlight Update + myflash.think = flash_update; + myflash.nextthink = time + 0.02; +}; + + +//pOx - 1.4 - allow user toggle (Darkmode Only!) - Impulse 20 +void () flash_toggle = +{ + if (deathmatch & DM_DARK)//pOx - double Check... + { + // If Off, Turn On + if (self.flash_flag == FALSE) + { + self.flash_flag = TRUE; + flash_on(); + } + + // If On, Turn Off + else + { + self.flash_flag = FALSE; + remove(self.flash); + //W_SetCurrentAmmo ();//? + //self.flash.think = SUB_Remove; + //self.flash.nextthink = time + 0.01; + } + } + +}; + diff --git a/ParoxysmII/source/nq/items.qc b/ParoxysmII/source/nq/items.qc new file mode 100644 index 0000000..29d953f --- /dev/null +++ b/ParoxysmII/source/nq/items.qc @@ -0,0 +1,1410 @@ +void() W_SetCurrentAmmo; +/* ALL LIGHTS SHOULD BE 0 1 0 IN COLOR ALL OTHER ITEMS SHOULD +BE .8 .3 .4 IN COLOR */ + +.entity quadcore; //POX - used by the dual model quad + +void() SUB_regen = +{ + self.model = self.mdl; // restore original model + self.solid = SOLID_TRIGGER; // allow it to be touched again + sound (self, CHAN_VOICE, "items/itembk2.wav", 1, ATTN_NORM); // play respawn sound + setorigin (self, self.origin); + + if (self.classname == "item_artifact_super_damage") + { + self.quadcore.model = self.quadcore.mdl; + setorigin (self.quadcore, self.quadcore.origin); + } + + if (self.classname == "item_artifact_super_damage" || self.classname == "item_artifact_invulnerability" || self.classname == "item_artifact_invisibility") + self.effects = self.effects | EF_DIMLIGHT; +}; + + + +/*QUAKED noclass (0 0 0) (-8 -8 -8) (8 8 8) +prints a warning message when spawned +*/ +void() noclass = +{ + dprint ("noclass spawned at"); + dprint (vtos(self.origin)); + dprint ("\n"); + remove (self); +}; + + + +/* +============ +PlaceItem + +plants the object on the floor +============ +*/ +void() PlaceItem = +{ + local float oldz; + + self.mdl = self.model; // so it can be restored on respawn + self.flags = FL_ITEM; // make extra wide + self.solid = SOLID_TRIGGER; + self.movetype = MOVETYPE_TOSS; + self.velocity = '0 0 0'; + self.origin_z = self.origin_z + 6; + oldz = self.origin_z; + if (!droptofloor()) + { + dprint ("Bonus item fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + return; + } +}; + +/* +============ +StartItem + +Sets the clipping size and plants the object on the floor +============ +*/ +void() StartItem = +{ + self.nextthink = time + 0.2; // items start after other solids + self.think = PlaceItem; +}; + +/* +========================================================================= + +HEALTH BOX + +========================================================================= +*/ +// +// T_Heal: add health to an entity, limiting health to max_health +// "ignore" will ignore max_health limit +// +float (entity e, float healamount, float ignore) T_Heal = +{ + if (e.health <= 0) + return 0; + if ((!ignore) && (e.health >= other.max_health)) + return 0; + healamount = ceil(healamount); + + e.health = e.health + healamount; + if ((!ignore) && (e.health >= other.max_health)) + e.health = other.max_health; + + if (e.health > 250) + e.health = 250; + return 1; +}; + +/*QUAKED item_health (.3 .3 1) (0 0 0) (32 32 32) rotten megahealth +Health box. Normally gives 25 points. +Rotten box heals 5-10 points, +megahealth will add 100 health, then +rot you down to your maximum health limit, +one point per second. +*/ + +float H_ROTTEN = 1; +float H_MEGA = 2; +.float healamount, healtype; +void() health_touch; +void() item_megahealth_rot; + +void() item_health = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + self.touch = health_touch; + + if (self.spawnflags & H_ROTTEN) + { + precache_model("maps/bspmdls/b_bh10.bsp"); + + precache_sound("items/health1.wav"); + setmodel(self, "maps/bspmdls/b_bh10.bsp"); + self.noise = "items/health1.wav"; + self.healamount = 15; + self.healtype = 0; + } + else + if (self.spawnflags & H_MEGA) + { + precache_model("maps/bspmdls/b_bh100.bsp"); + precache_sound("items/r_item2.wav"); + setmodel(self, "maps/bspmdls/b_bh100.bsp"); + self.noise = "items/r_item2.wav"; + self.healamount = 100; + self.healtype = 2; + } + else + { + precache_model("maps/bspmdls/b_bh25.bsp"); + precache_sound("items/health1.wav"); + setmodel(self, "maps/bspmdls/b_bh25.bsp"); + self.noise = "items/health1.wav"; + self.healamount = 25; + self.healtype = 1; + } + setsize (self, '0 0 0', '32 32 56'); + self.netname = "Health Pack"; + StartItem (); +}; + + +void() health_touch = +{ + local float amount; + local string s; + + if (other.classname != "player") + return; + + if (self.healtype == 2) // Megahealth? Ignore max_health... + { + if (other.health >= 250) + return; + if (!T_Heal(other, self.healamount, 1)) + return; + } + else + { + if (!T_Heal(other, self.healamount, 0)) + return; + } + + sprint(other, "You receive "); + s = ftos(self.healamount); + sprint(other, s); + sprint(other, " health\n"); + +// health touch sound + sound(other, CHAN_ITEM, self.noise, 1, ATTN_NORM); + + stuffcmd (other, "bf\n"); + + self.model = string_null; + self.solid = SOLID_NOT; + + // Megahealth = rot down the player's super health + if (self.healtype == 2) + { + other.items = other.items | IT_SUPERHEALTH; + self.nextthink = time + 5; + self.think = item_megahealth_rot; + self.owner = other; + } + else + { + //if (deathmatch != 2) // deathmatch 2 is the silly old rules + //{ + if (deathmatch) + self.nextthink = time + 20; + self.think = SUB_regen; + //} + } + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + +void() item_megahealth_rot = +{ + other = self.owner; + + if (other.health > other.max_health) + { + other.health = other.health - 1; + self.nextthink = time + 1; + return; + } + +// it is possible for a player to die and respawn between rots, so don't +// just blindly subtract the flag off + other.items = other.items - (other.items & IT_SUPERHEALTH); + + if (deathmatch >= 1) // deathmatch 2 is silly old rules + { + self.nextthink = time + 20; + self.think = SUB_regen; + } +}; + +/* +=============================================================================== + +ARMOR - New Regen Stations Replace armor (see sheilds.qc) + +=============================================================================== +*/ +/* +=============================================================================== + +WEAPONS + +=============================================================================== +*/ + +void() bound_other_ammo = +{ + if (other.ammo_shells > 100) + other.ammo_shells = 100; + if (other.ammo_nails > 200) + other.ammo_nails = 200; + if (other.ammo_rockets > 100) + other.ammo_rockets = 100; + if (other.ammo_cells > 200) + other.ammo_cells = 200; +}; + + +float(float w) RankForWeapon = +{ + if (w == IT_FLAMETHROWER) + return 1; + if (w == IT_ROCKET_LAUNCHER) + return 2; + if (w == IT_SUPER_NAILGUN) + return 3; + if (w == IT_GRENADE_LAUNCHER) + return 4; + if (w == IT_COMBOGUN) + return 5; + if (w == IT_PLASMAGUN) + return 6; + return 7; +}; + +/* +============= +Deathmatch_Weapon + +Deathmatch weapon change rules for picking up a weapon + +.float ammo_shells, ammo_nails, ammo_rockets, ammo_cells; +============= +*/ +void(float old, float new) Deathmatch_Weapon = +{ + local float or, nr; + +// change self.weapon if desired + or = RankForWeapon (self.weapon); + nr = RankForWeapon (new); + if ( nr < or ) + self.weapon = new; +}; + +/* +============= +weapon_touch +============= +*/ +float() W_BestWeapon; + +void() weapon_touch = +{ + local float hadammo, best, new, old; + local entity stemp; + local float leave; + + if (!(other.flags & FL_CLIENT)) + return; + +// if the player was using his best weapon, change up to the new one if better + stemp = self; + self = other; + best = W_BestWeapon(); + self = stemp; + +//POX v1.1 - leave is useless in POX since weapons are never allowed to be picked up if posessed + //if (deathmatch || coop) + //if (coop) + // leave = 1; + //else + // leave = 0; + + + // POX - Don't bother checking if weapon is in inventory + if (other.items & self.weapon) + { + activator = other; + SUB_UseTargets(); //Just in case it's required to get out of somewhere + return; + } + +// POX- changed classnames to constants + + if (self.weapon == IT_PLASMAGUN) + { + hadammo = other.ammo_rockets; + new = IT_PLASMAGUN; + other.ammo_cells = other.ammo_cells + 22; + } + else if (self.weapon == IT_SUPER_NAILGUN) + { + hadammo = other.ammo_rockets; + new = IT_SUPER_NAILGUN; + other.ammo_nails = other.ammo_nails + 30; + } + else if (self.weapon == IT_COMBOGUN) + { + hadammo = other.ammo_rockets; + new = IT_COMBOGUN; + other.ammo_shells = other.ammo_shells + 5; + other.ammo_rockets = other.ammo_rockets + 2; + other.which_ammo = 0; //Change ammo to shells if it's set to rockets + } + else if (self.weapon == IT_ROCKET_LAUNCHER) + { + hadammo = other.ammo_rockets; + new = IT_ROCKET_LAUNCHER; + other.ammo_rockets = other.ammo_rockets + 5; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + { + hadammo = other.ammo_rockets; + new = IT_GRENADE_LAUNCHER; + other.ammo_rockets = other.ammo_rockets + 5; + } + else + objerror ("weapon_touch: unknown classname"); + + sprint (other, "You got the "); + sprint (other, self.netname); + sprint (other, "\n"); +// weapon touch sound + sound (other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + + bound_other_ammo (); + +// change to the weapon + old = other.items; + other.items = other.items | new; + + stemp = self; + self = other; + +// change weapon to item picked up unless DM_AUTOSWITCH + if (deathmatch & DM_AUTOSWITCH) + Deathmatch_Weapon (old, new); + else + self.weapon = new; + + W_SetCurrentAmmo(); + + self = stemp; + + if (leave) + return; + +// remove it in single player, or setup for respawning in deathmatch + self.model = string_null; + self.solid = SOLID_NOT; + if (deathmatch >= 1) + self.nextthink = time + 30; + self.think = SUB_regen; + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + + +/*QUAKED weapon_supershotgun (0 .5 .8) (-16 -16 0) (16 16 32) +*/ +void() weapon_supershotgun = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + precache_model ("progs/g_combo.mdl"); + setmodel (self, "progs/g_combo.mdl"); + self.weapon = IT_COMBOGUN; + self.netname = "Combo Gun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; + +/*QUAKED weapon_nailgun (0 .5 .8) (-16 -16 0) (16 16 32) +*/ + +void() weapon_nailgun = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + precache_model ("progs/g_plasma.mdl"); + setmodel (self, "progs/g_plasma.mdl"); + self.weapon = IT_PLASMAGUN; + self.netname = "Plasma Gun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; + +/*QUAKED weapon_supernailgun (0 .5 .8) (-16 -16 0) (16 16 32) +*/ + +void() weapon_supernailgun = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + precache_model ("progs/g_nailg.mdl"); + setmodel (self, "progs/g_nailg.mdl"); + self.weapon = IT_SUPER_NAILGUN; + self.netname = "Nailgun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; + +/*QUAKED weapon_grenadelauncher (0 .5 .8) (-16 -16 0) (16 16 32) +*/ + +void() weapon_grenadelauncher = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + precache_model ("progs/g_gren.mdl"); + setmodel (self, "progs/g_gren.mdl"); + self.weapon = IT_GRENADE_LAUNCHER; + self.netname = "Grenade Launcher"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; + +/*QUAKED weapon_rocketlauncher (0 .5 .8) (-16 -16 0) (16 16 32) +*/ + +void() weapon_rocketlauncher = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + precache_model ("progs/g_rhino.mdl"); + setmodel (self, "progs/g_rhino.mdl"); + self.weapon = IT_ROCKET_LAUNCHER; + self.netname = "Anihilator"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; + + +/*QUAKED weapon_lightning (0 .5 .8) (-16 -16 0) (16 16 32) +*/ + +void() weapon_lightning = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + precache_model ("progs/g_plasma.mdl"); + setmodel (self, "progs/g_plasma.mdl"); + self.weapon = IT_PLASMAGUN; + self.netname = "Plasma Gun"; + self.touch = weapon_touch; + setsize (self, '-16 -16 0', '16 16 56'); + StartItem (); +}; + + +/* +=============================================================================== + +AMMO + +=============================================================================== +*/ + +void() ammo_touch = +{ +local entity stemp; +local float best; + + if (other.classname != "player") + return; + if (other.health <= 0) + return; + +// if the player was using his best weapon, change up to the new one if better + stemp = self; + self = other; + best = W_BestWeapon(); + self = stemp; + + +// shotgun + if (self.weapon == 1) + { + if (other.ammo_shells >= 100) + return; + other.ammo_shells = other.ammo_shells + self.aflag; + other.which_ammo = 0; + } + +// spikes + if (self.weapon == 2) + { + if (other.ammo_nails >= 200) + return; + other.ammo_nails = other.ammo_nails + self.aflag; + } + +// rockets + if (self.weapon == 3) + { + if (other.ammo_rockets >= 100) + return; + other.ammo_rockets = other.ammo_rockets + self.aflag; + other.which_ammo = 1; + } + +// cells + if (self.weapon == 4) + { + if (other.ammo_cells >= 200) + return; + other.ammo_cells = other.ammo_cells + self.aflag; + } + + bound_other_ammo (); + + sprint (other, "You got the "); + sprint (other, self.netname); + sprint (other, "\n"); +// ammo touch sound + sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + +/* BEST WEAPON MARKER */ +// change to a better weapon if appropriate +if (deathmatch & DM_AUTOSWITCH) +{ + if ( other.weapon == best ) + { + stemp = self; + self = other; + self.weapon = W_BestWeapon(); + W_SetCurrentAmmo (); + self = stemp; + } +} +/* BEST WEAPON MARKER */ + +// if changed current ammo, update it + stemp = self; + self = other; + W_SetCurrentAmmo(); + self = stemp; + +// remove it in single player, or setup for respawning in deathmatch + self.model = string_null; + self.solid = SOLID_NOT; + if (deathmatch >= 1) + self.nextthink = time + 30; + self.think = SUB_regen; + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + + + + +float WEAPON_BIG2 = 1; + +/*QUAKED item_shells (0 .5 .8) (0 0 0) (32 32 32) big +*/ + +void() item_shells = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + self.touch = ammo_touch; + + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_shell1.bsp"); + setmodel (self, "maps/bspmdls/b_shell1.bsp"); + self.aflag = 40; + } + else + { + precache_model ("maps/bspmdls/b_shell0.bsp"); + setmodel (self, "maps/bspmdls/b_shell0.bsp"); + self.aflag = 20; + } + self.weapon = 1; + self.netname = "shells"; + self.classname = "ammo"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; + +/*QUAKED item_spikes (0 .5 .8) (0 0 0) (32 32 32) big +*/ + +void() item_spikes = +{ + + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + self.touch = ammo_touch; + + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_nail1.bsp"); + setmodel (self, "maps/bspmdls/b_nail1.bsp"); + self.aflag = 50; + } + else + { + precache_model ("maps/bspmdls/b_nail0.bsp"); + setmodel (self, "maps/bspmdls/b_nail0.bsp"); + self.aflag = 25; + } + self.weapon = 2; + self.netname = "nails"; + self.classname = "ammo"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; + +/*QUAKED item_rockets (0 .5 .8) (0 0 0) (32 32 32) big +*/ + +void() item_rockets = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + self.touch = ammo_touch; + + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_rock1.bsp"); + setmodel (self, "maps/bspmdls/b_rock1.bsp"); + self.aflag = 10; + } + else + { + precache_model ("maps/bspmdls/b_rock0.bsp"); + setmodel (self, "maps/bspmdls/b_rock0.bsp"); + self.aflag = 5; + } + self.weapon = 3; + self.netname = "rockets"; + self.classname = "ammo"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; + + +/*QUAKED item_cells (0 .5 .8) (0 0 0) (32 32 32) big +*/ + +void() item_cells = +{ + //POX-no items in FFA mode + if (deathmatch & DM_FFA) + remove(self); + + self.touch = ammo_touch; + + if (self.spawnflags & WEAPON_BIG2) + { + precache_model ("maps/bspmdls/b_batt1.bsp"); + setmodel (self, "maps/bspmdls/b_batt1.bsp"); + self.aflag = 12; + } + else + { + precache_model ("maps/bspmdls/b_batt0.bsp"); + setmodel (self, "maps/bspmdls/b_batt0.bsp"); + self.aflag = 6; + } + self.weapon = 4; + self.netname = "cells"; + self.classname = "ammo"; + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; + + +//POX - This is used in a bunch of Id's original maps, so it's left in for backward compatibility +/*QUAKED item_weapon (0 .5 .8) (0 0 0) (32 32 32) shotgun rocket spikes big +DO NOT USE THIS!!!! IT WILL BE REMOVED! +*/ + +float WEAPON_SHOTGUN = 1; +float WEAPON_ROCKET = 2; +float WEAPON_SPIKES = 4; +float WEAPON_BIG = 8; +void() item_weapon = +{ + self.touch = ammo_touch; + + if (self.spawnflags & WEAPON_SHOTGUN) + { + if (self.spawnflags & WEAPON_BIG) + { + precache_model ("maps/bspmdls/b_shell1.bsp"); + setmodel (self, "maps/bspmdls/b_shell1.bsp"); + self.aflag = 40; + } + else + { + precache_model ("maps/bspmdls/b_shell0.bsp"); + setmodel (self, "maps/bspmdls/b_shell0.bsp"); + self.aflag = 20; + } + self.weapon = 1; + self.netname = "shells"; + self.classname = "ammo"; + } + + if (self.spawnflags & WEAPON_SPIKES) + { + if (self.spawnflags & WEAPON_BIG) + { + precache_model ("maps/bspmdls/b_nail1.bsp"); + setmodel (self, "maps/bspmdls/b_nail1.bsp"); + self.aflag = 40; + } + else + { + precache_model ("maps/bspmdls/b_nail0.bsp"); + setmodel (self, "maps/bspmdls/b_nail0.bsp"); + self.aflag = 20; + } + self.weapon = 2; + self.netname = "spikes"; + self.classname = "ammo"; + } + + if (self.spawnflags & WEAPON_ROCKET) + { + if (self.spawnflags & WEAPON_BIG) + { + precache_model ("maps/bspmdls/b_rock1.bsp"); + setmodel (self, "maps/bspmdls/b_rock1.bsp"); + self.aflag = 10; + } + else + { + precache_model ("maps/bspmdls/b_rock0.bsp"); + setmodel (self, "maps/bspmdls/b_rock0.bsp"); + self.aflag = 5; + } + self.weapon = 3; + self.netname = "rockets"; + self.classname = "ammo"; + } + + setsize (self, '0 0 0', '32 32 56'); + StartItem (); +}; + + +/* +=============================================================================== + +KEYS + +=============================================================================== +*/ + +void() key_touch = +{ +local entity stemp; +local float best; + + if (other.classname != "player") + return; + if (other.health <= 0) + return; + if (other.items & self.items) + return; + + sprint (other, "You got the "); + sprint (other, self.netname); + sprint (other,"\n"); + + sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + other.items = other.items | self.items; + + if (!coop) + { + self.solid = SOLID_NOT; + self.model = string_null; + } + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + + +void() key_setsounds = +{ + if (world.worldtype == 0) + { + precache_sound ("misc/medkey.wav"); + self.noise = "misc/medkey.wav"; + } + if (world.worldtype == 1) + { + precache_sound ("misc/runekey.wav"); + self.noise = "misc/runekey.wav"; + } + if (world.worldtype == 2) + { + precache_sound2 ("misc/basekey.wav"); + self.noise = "misc/basekey.wav"; + } +}; + +/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) +SILVER key +In order for keys to work +you MUST set your maps +worldtype to one of the +following: +0: medieval +1: metal +2: base +*/ + +void() item_key1 = +{ + if (world.worldtype == 0) + { + precache_model ("progs/w_s_key.mdl"); + setmodel (self, "progs/w_s_key.mdl"); + self.netname = "silver key"; + } + else if (world.worldtype == 1) + { + precache_model ("progs/m_s_key.mdl"); + setmodel (self, "progs/m_s_key.mdl"); + self.netname = "silver runekey"; + } + else if (world.worldtype == 2) + { + precache_model2 ("progs/b_s_key.mdl"); + setmodel (self, "progs/b_s_key.mdl"); + self.netname = "silver keycard"; + } + key_setsounds(); + self.touch = key_touch; + self.items = IT_KEY1; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; + +/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) +GOLD key +In order for keys to work +you MUST set your maps +worldtype to one of the +following: +0: medieval +1: metal +2: base +*/ + +void() item_key2 = +{ + if (world.worldtype == 0) + { + precache_model ("progs/w_g_key.mdl"); + setmodel (self, "progs/w_g_key.mdl"); + self.netname = "gold key"; + } + if (world.worldtype == 1) + { + precache_model ("progs/m_g_key.mdl"); + setmodel (self, "progs/m_g_key.mdl"); + self.netname = "gold runekey"; + } + if (world.worldtype == 2) + { + precache_model2 ("progs/b_g_key.mdl"); + setmodel (self, "progs/b_g_key.mdl"); + self.netname = "gold keycard"; + } + key_setsounds(); + self.touch = key_touch; + self.items = IT_KEY2; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); +}; + + + +/* +=============================================================================== + +END OF LEVEL RUNES + +=============================================================================== +*/ + + +/* +=============================================================================== + +POWERUPS + +=============================================================================== +*/ + +void() powerup_touch; + + +void() powerup_touch = +{ +local entity stemp; +local float best; + + if (other.classname != "player") + return; + if (other.health <= 0) + return; + + sprint (other, "You got the "); + sprint (other, self.netname); + sprint (other,"\n"); + + if (deathmatch) + { + self.mdl = self.model; + + if ((self.classname == "item_artifact_invulnerability") || + (self.classname == "item_artifact_invisibility")) + self.nextthink = time + 75; + else + self.nextthink = time + 60; + + self.think = SUB_regen; + } + + sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + self.solid = SOLID_NOT; + other.items = other.items | self.items; + self.model = string_null; + + if (self.classname == "item_artifact_super_damage") + { + self.effects = self.effects - (self.effects & EF_DIMLIGHT); + self.quadcore.mdl = self.quadcore.model; + self.quadcore.model = string_null; + + other.super_time = 1; + other.super_damage_finished = time + 30; + } + +// do the apropriate action + if (self.classname == "item_artifact_envirosuit") + { + other.rad_time = 1; + other.radsuit_finished = time + 30; + } + + if (self.classname == "item_artifact_invulnerability") + { + self.effects = self.effects - (self.effects & EF_DIMLIGHT); + other.invincible_time = 1; + other.invincible_finished = time + 30; + } + + if (self.classname == "item_artifact_invisibility") + { + other.invisible_time = 1; + other.invisible_finished = time + 30; + self.effects = self.effects - (self.effects & EF_DIMLIGHT); + } + + + activator = other; + SUB_UseTargets(); // fire all targets / killtargets +}; + + + +/*QUAKED item_artifact_invulnerability (0 .5 .8) (-16 -16 -24) (16 16 32) +Player is invulnerable for 30 seconds +*/ +void() item_artifact_invulnerability = +{ + + self.touch = powerup_touch; + + precache_model ("progs/poxmegs.mdl"); + precache_sound ("items/protect.wav"); + precache_sound ("items/protect2.wav"); + precache_sound ("items/protect3.wav"); + self.noise = "items/protect.wav"; + setmodel (self, "progs/poxmegs.mdl"); + self.netname = "MegaShields"; + self.items = IT_INVULNERABILITY; + + self.effects = self.effects | EF_DIMLIGHT; + + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); + +}; + +/*QUAKED item_artifact_envirosuit (0 .5 .8) (-16 -16 -24) (16 16 32) +Player takes no damage from water or slime for 30 seconds +*/ +void() item_artifact_envirosuit = +{ + self.touch = powerup_touch; + + precache_model ("progs/suit.mdl"); + precache_sound ("items/suit.wav"); + precache_sound ("items/suit2.wav"); + self.noise = "items/suit.wav"; + setmodel (self, "progs/suit.mdl"); + self.netname = "Biosuit"; + self.items = IT_SUIT; + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); + +}; + + +/*QUAKED item_artifact_invisibility (0 .5 .8) (-16 -16 -24) (16 16 32) +Player is invisible for 30 seconds +*/ +void() item_artifact_invisibility = +{ + //POX - Everyone's already invisible + if (deathmatch & DM_PREDATOR) + remove(self); + + self.touch = powerup_touch; + + precache_model ("progs/cloak.mdl"); + precache_sound ("items/inv1.wav"); + precache_sound ("items/inv2.wav"); + precache_sound ("items/inv3.wav"); + self.noise = "items/inv1.wav"; + setmodel (self, "progs/cloak.mdl"); + self.netname = "Cloaking Device"; + self.items = IT_INVISIBILITY; + + self.effects = self.effects | EF_DIMLIGHT; + + setsize (self, '-16 -16 -24', '16 16 32'); + StartItem (); + +}; + + + +//POX - A little hack to get a multi-model item for a cool effect +void() Spawn_QuadCore = +{ + local entity qcore; + + qcore = spawn (); + qcore.owner = self; + qcore.solid = SOLID_TRIGGER; + qcore.movetype = MOVETYPE_TOSS; + + setmodel (qcore, "progs/poxquad2.mdl"); + setsize (qcore, '-16 -16 -24', '16 16 32'); + + qcore.velocity = '0 0 0'; + setorigin(qcore, self.origin); + qcore.origin_z = qcore.origin_z + 6; + + self.quadcore = qcore; + + qcore.nextthink = time + 999999999; + qcore.think = SUB_Null; +}; + + +/*QUAKED item_artifact_super_damage (0 .5 .8) (-16 -16 -24) (16 16 32) +The next attack from the player will do 4x damage +*/ +void() item_artifact_super_damage = +{ + self.touch = powerup_touch; + + precache_model ("progs/poxquad.mdl"); + precache_model ("progs/poxquad2.mdl"); + + precache_sound ("items/damage.wav"); + precache_sound ("items/damage2.wav"); + precache_sound ("items/damage3.wav"); + + self.noise = "items/damage.wav"; + setmodel (self, "progs/poxquad.mdl"); + self.netname = "Quad Damage"; + self.items = IT_QUAD; + setsize (self, '-16 -16 -24', '16 16 32'); + + self.effects = self.effects | EF_DIMLIGHT; + + Spawn_QuadCore (); + + StartItem (); + +}; + + + +/* +=============================================================================== + +PLAYER BACKPACKS + +=============================================================================== +*/ + +void() BackpackTouch = +{ + local string s; + local float best, old, new; + local entity stemp; + local float acount; + + if (other.classname != "player") + return; + if (other.health <= 0) + return; + + acount = 0; + sprint (other, "You get "); + + if (self.items) + if ((other.items & self.items) == 0) + { + acount = 1; + sprint (other, "the "); + sprint (other, self.netname); + } + +// if the player was using his best weapon, change up to the new one if better + stemp = self; + self = other; + best = W_BestWeapon(); + self = stemp; + +// change weapons + other.ammo_shells = other.ammo_shells + self.ammo_shells; + other.ammo_nails = other.ammo_nails + self.ammo_nails; + other.ammo_rockets = other.ammo_rockets + self.ammo_rockets; + other.ammo_cells = other.ammo_cells + self.ammo_cells; + + new = self.items; + if (!new) + new = other.weapon; + old = other.items; + other.items = other.items | new; + + bound_other_ammo (); + + if (self.ammo_shells) + { + if (acount) + sprint(other, ", "); + acount = 1; + s = ftos(self.ammo_shells); + sprint (other, s); + sprint (other, " shells"); + } + if (self.ammo_nails) + { + if (acount) + sprint(other, ", "); + acount = 1; + s = ftos(self.ammo_nails); + sprint (other, s); + sprint (other, " nails"); + } + if (self.ammo_rockets) + { + if (acount) + sprint(other, ", "); + acount = 1; + s = ftos(self.ammo_rockets); + sprint (other, s); + sprint (other, " rockets"); + } + if (self.ammo_cells) + { + if (acount) + sprint(other, ", "); + acount = 1; + s = ftos(self.ammo_cells); + sprint (other, s); + sprint (other, " cells"); + } + + //POX - Health in packs for FFA mode + if (self.healamount) + { + if (!T_Heal(other, self.healamount, 0)) + SUB_Null; + else + { + if (acount) + sprint(other, ", "); + s = ftos(self.healamount); + sprint(other, " "); + sprint(other, s); + sprint(other, " health"); + } + } + + sprint (other, "\n"); +// backpack touch sound + sound (other, CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM); + stuffcmd (other, "bf\n"); + +// remove the backpack, change self to the player + remove(self); + self = other; + +/* BEST WEAPON MARKER */ +// change to the weapon + //if (!deathmatch) + // self.weapon = new; + //else + if (deathmatch & DM_AUTOSWITCH) + Deathmatch_Weapon (old, new); +/* BEST WEAPON MARKER */ + + W_SetCurrentAmmo (); +}; + +/* +=============== +DropBackpack +=============== +*/ + +void() DropBackpack = +{ + local entity item; + + if (!(self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells) && !(deathmatch & DM_FFA)) + return; // nothing in it + + item = spawn(); + item.origin = self.origin - '0 0 24'; + + if (!(deathmatch & DM_FFA)) + { + item.items = self.weapon; + + //if (item.items == IT_BONESAW) + // item.netname = "Bonesaw"; + //else if (item.items == IT_TSHOT) + // item.netname = "tShot"; + //else + if (item.items == IT_COMBOGUN) + item.netname = "Combo Gun"; + else if (item.items == IT_PLASMAGUN) + item.netname = "Plasma Gun"; + else if (item.items == IT_SUPER_NAILGUN) + item.netname = "Nailgun"; + else if (item.items == IT_GRENADE_LAUNCHER) + item.netname = "Grenade Launcher"; + else if (item.items == IT_ROCKET_LAUNCHER) + item.netname = "Anihilator"; + //else if (item.items == IT_FLAMETHROWER) + // item.netname = "FlameThrower"; + else + item.netname = ""; + } + else //POX v1.1 moved this up here + { + item.healamount = 25; + item.healtype = 1; + } + + item.ammo_shells = self.ammo_shells; + item.ammo_nails = self.ammo_nails; + + item.ammo_rockets = self.ammo_rockets; +//round rockets up to nearest integer incase someone died in between rhino barrel fires + item.ammo_rockets = rint(item.ammo_rockets); + + item.ammo_cells = self.ammo_cells; + + + //FIXED? + // bot can drop ridiculous amounts of ammo so cap it + if (self.classname == "bot") + { + if (item.ammo_shells > 50) + item.ammo_shells = 30; + if (item.ammo_nails > 100) + item.ammo_nails = 70; + if (item.ammo_rockets > 50) + item.ammo_rockets = 20; + if (item.ammo_cells > 100) + item.ammo_cells = 60; + + //FIXED? + //bots sometimes have negative ammo values - this can mess up a real player + if (item.ammo_shells < 1) + item.ammo_shells = 0; + if (item.ammo_nails < 1) + item.ammo_nails = 0; + if (item.ammo_rockets < 1) + item.ammo_rockets = 0; + if (item.ammo_cells < 1) + item.ammo_cells = 0; + } + + item.velocity_z = 300; + item.velocity_x = -100 + (random() * 200); + item.velocity_y = -100 + (random() * 200); + + item.flags = FL_ITEM; + item.solid = SOLID_TRIGGER; + item.movetype = MOVETYPE_TOSS; + setmodel (item, "progs/backpack.mdl"); + setsize (item, '-16 -16 0', '16 16 56'); + item.classname = "pack"; + item.touch = BackpackTouch; + // POX v1.1 changed to 30 secs + item.nextthink = time + 30; // remove after 2 minutes + item.think = SUB_Remove; +}; diff --git a/ParoxysmII/source/nq/misc.qc b/ParoxysmII/source/nq/misc.qc new file mode 100644 index 0000000..943f57f --- /dev/null +++ b/ParoxysmII/source/nq/misc.qc @@ -0,0 +1,717 @@ +//From Enforcer +//This bit is still used by laser traps (see misc.qc) +void() Laser_Touch = +{ + local vector org; + + if (other == self.owner) + return; // don't explode on owner + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + sound (self, CHAN_WEAPON, "enforcer/enfstop.wav", 1, ATTN_STATIC); + org = self.origin - 8*normalize(self.velocity); + + if (other.health) + { + SpawnBlood (org, self.velocity*0.2, 15); + T_Damage (other, self, self.owner, 15); + } + else + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_GUNSHOT); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + } + + remove(self); +}; + +void(vector org, vector vec) LaunchLaser = +{ + local vector vec; + + if (self.classname == "monster_enforcer") + sound (self, CHAN_WEAPON, "enforcer/enfire.wav", 1, ATTN_NORM); + + vec = normalize(vec); + + newmis = spawn(); + newmis.owner = self; + newmis.movetype = MOVETYPE_FLY; + newmis.solid = SOLID_BBOX; + newmis.effects = EF_DIMLIGHT; + + setmodel (newmis, "progs/laser.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + + setorigin (newmis, org); + + newmis.velocity = vec * 600; + newmis.angles = vectoangles(newmis.velocity); + + newmis.nextthink = time + 5; + newmis.think = SUB_Remove; + newmis.touch = Laser_Touch; +}; + + +/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) +Used as a positional target for spotlights, etc. +*/ +void() info_null = +{ + remove(self); +}; + +/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) +Used as a positional target for lightning. +*/ +void() info_notnull = +{ +}; + +//============================================================================ + +float START_OFF = 1; + +void() light_use = +{ + if (self.spawnflags & START_OFF) + { + lightstyle(self.style, "m"); + self.spawnflags = self.spawnflags - START_OFF; + } + else + { + lightstyle(self.style, "a"); + self.spawnflags = self.spawnflags + START_OFF; + } +}; + +/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF +Non-displayed light. +Default light value is 300 +Default style is 0 +If targeted, it will toggle between on or off. +*/ +void() light = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + if (!self.targetname) + { // inert light + remove(self); + return; + } + + if (self.style >= 32) + { + self.use = light_use; + if (self.spawnflags & START_OFF) + lightstyle(self.style, "a"); + else + lightstyle(self.style, "m"); + } +}; + +/*QUAKED light_fluoro (0 1 0) (-8 -8 -8) (8 8 8) START_OFF +Non-displayed light. +Default light value is 300 +Default style is 0 +If targeted, it will toggle between on or off. +Makes steady fluorescent humming sound +*/ +void() light_fluoro = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + if (self.style >= 32) + { + self.use = light_use; + if (self.spawnflags & START_OFF) + lightstyle(self.style, "a"); + else + lightstyle(self.style, "m"); + } + + precache_sound ("ambience/fl_hum1.wav"); + ambientsound (self.origin, "ambience/fl_hum1.wav", 0.5, ATTN_STATIC); +}; + +/*QUAKED light_fluorospark (0 1 0) (-8 -8 -8) (8 8 8) +Non-displayed light. +Default light value is 300 +Default style is 10 +Makes sparking, broken fluorescent sound +*/ +void() light_fluorospark = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + if (!self.style) + self.style = 10; + + precache_sound ("ambience/buzz1.wav"); + ambientsound (self.origin, "ambience/buzz1.wav", 0.5, ATTN_STATIC); +}; + +/*QUAKED light_globe (0 1 0) (-8 -8 -8) (8 8 8) +Sphere globe light. +Default light value is 300 +Default style is 0 +*/ +void() light_globe = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + precache_model ("progs/s_light.spr"); + setmodel (self, "progs/s_light.spr"); + makestatic (self); +}; + +void() FireAmbient = +{ + precache_sound ("ambience/fire1.wav"); +// attenuate fast + ambientsound (self.origin, "ambience/fire1.wav", 0.5, ATTN_STATIC); +}; + +/*QUAKED light_torch_small_walltorch (0 .5 0) (-10 -10 -20) (10 10 20) +Short wall torch +Default light value is 200 +Default style is 0 +*/ +void() light_torch_small_walltorch = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + precache_model ("progs/flame.mdl"); + setmodel (self, "progs/flame.mdl"); + FireAmbient (); + makestatic (self); +}; + +/*QUAKED light_flame_large_yellow (0 1 0) (-10 -10 -12) (12 12 18) +Large yellow flame ball +*/ +void() light_flame_large_yellow = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + precache_model ("progs/flame2.mdl"); + setmodel (self, "progs/flame2.mdl"); + self.frame = 1; + FireAmbient (); + makestatic (self); +}; + +/*QUAKED light_flame_small_yellow (0 1 0) (-8 -8 -8) (8 8 8) START_OFF +Small yellow flame ball +*/ +void() light_flame_small_yellow = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + precache_model ("progs/flame2.mdl"); + setmodel (self, "progs/flame2.mdl"); + FireAmbient (); + makestatic (self); +}; + +/*QUAKED light_flame_small_white (0 1 0) (-10 -10 -40) (10 10 40) START_OFF +Small white flame ball +*/ +void() light_flame_small_white = +{ + if (deathmatch & DM_DARK) + { + lightstyle(self.style, "b"); + return; + } + + precache_model ("progs/flame2.mdl"); + setmodel (self, "progs/flame2.mdl"); + FireAmbient (); + makestatic (self); +}; + +//============================================================================ + + +/*QUAKED misc_fireball (0 .5 .8) (-8 -8 -8) (8 8 8) +Lava Balls +*/ + +void() fire_fly; +void() fire_touch; +void() misc_fireball = +{ + + precache_model ("progs/lavaball.mdl"); + self.classname = "fireball"; + self.nextthink = time + (random() * 5); + self.think = fire_fly; + if (!self.speed) + self.speed == 1000; +}; + +void() fire_fly = +{ +local entity fireball; + + fireball = spawn(); + fireball.solid = SOLID_TRIGGER; + fireball.movetype = MOVETYPE_TOSS; + fireball.velocity = '0 0 1000'; + fireball.velocity_x = (random() * 100) - 50; + fireball.velocity_y = (random() * 100) - 50; + fireball.velocity_z = self.speed + (random() * 200); + fireball.classname = "fireball"; + setmodel (fireball, "progs/lavaball.mdl"); + setsize (fireball, '0 0 0', '0 0 0'); + setorigin (fireball, self.origin); + fireball.nextthink = time + 5; + fireball.think = SUB_Remove; + fireball.touch = fire_touch; + + self.nextthink = time + (random() * 5) + 3; + self.think = fire_fly; +}; + + +void() fire_touch = +{ + T_Damage (other, self, self, 20); + remove(self); +}; + +//============================================================================ + + +void() barrel_explode = +{ + self.takedamage = DAMAGE_NO; + self.classname = "explo_box"; + // did say self.owner + T_RadiusDamage (self, self, 160, world); + sound (self, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM); + particle (self.origin, '0 0 0', 75, 255); + + self.origin_z = self.origin_z + 32; + BecomeExplosion (); +}; + +/*QUAKED misc_explobox (0 .5 .8) (0 0 0) (32 32 64) +TESTING THING +*/ + +void() misc_explobox = +{ + local float oldz; + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + precache_model ("maps/b_explob.bsp"); + setmodel (self, "maps/b_explob.bsp"); + precache_sound ("weapons/r_exp3.wav"); + self.health = 20; + self.th_die = barrel_explode; + self.takedamage = DAMAGE_AIM; + + self.nobleed = TRUE; + + self.origin_z = self.origin_z + 2; + oldz = self.origin_z; + droptofloor(); + if (oldz - self.origin_z > 250) + { + dprint ("item fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + } +}; + +/*QUAKED misc_explobox2 (0 .5 .8) (0 0 0) (32 32 64) +Smaller exploding box, REGISTERED ONLY +*/ + +void() misc_explobox2 = +{ + local float oldz; + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + precache_model2 ("maps/b_exbox2.bsp"); + setmodel (self, "maps/b_exbox2.bsp"); + precache_sound ("weapons/r_exp3.wav"); + self.health = 20; + self.th_die = barrel_explode; + self.takedamage = DAMAGE_AIM; + + self.nobleed = TRUE; + + self.origin_z = self.origin_z + 2; + oldz = self.origin_z; + droptofloor(); + if (oldz - self.origin_z > 250) + { + dprint ("item fell out of level at "); + dprint (vtos(self.origin)); + dprint ("\n"); + remove(self); + } +}; + +//============================================================================ + +float SPAWNFLAG_SUPERSPIKE = 1; +float SPAWNFLAG_LASER = 2; + +void(vector org, vector vec) LaunchLaser; + +void() spikeshooter_use = +{ + if (self.spawnflags & SPAWNFLAG_LASER) + { + sound (self, CHAN_VOICE, "enforcer/enfire.wav", 1, ATTN_NORM); + LaunchLaser (self.origin, self.movedir); + } + else + { + sound (self, CHAN_VOICE, "weapons/spike2.wav", 1, ATTN_NORM); + launch_spike (self.origin, self.movedir); + newmis.velocity = self.movedir * 500; + if (self.spawnflags & SPAWNFLAG_SUPERSPIKE) + newmis.touch = superspike_touch; + } +}; + +void() shooter_think = +{ + spikeshooter_use (); + self.nextthink = time + self.wait; + newmis.velocity = self.movedir * 500; +}; + + +/*QUAKED trap_spikeshooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser +When triggered, fires a spike in the direction set in QuakeEd. +Laser is only for REGISTERED. +*/ + +void() trap_spikeshooter = +{ + SetMovedir (); + self.use = spikeshooter_use; + if (self.spawnflags & SPAWNFLAG_LASER) + { + precache_model2 ("progs/laser.mdl"); + + precache_sound2 ("enforcer/enfire.wav"); + precache_sound2 ("enforcer/enfstop.wav"); + } + else + precache_sound ("weapons/spike2.wav"); +}; + + +/*QUAKED trap_shooter (0 .5 .8) (-8 -8 -8) (8 8 8) superspike laser +Continuously fires spikes. +"wait" time between spike (1.0 default) +"nextthink" delay before firing first spike, so multiple shooters can be stagered. +*/ +void() trap_shooter = +{ + trap_spikeshooter (); + + if (self.wait == 0) + self.wait = 1; + self.nextthink = self.nextthink + self.wait + self.ltime; + self.think = shooter_think; +}; + + + +/* +=============================================================================== + + +=============================================================================== +*/ + + +void() make_bubbles; +void() bubble_remove; +void() bubble_bob; + +/*QUAKED air_bubbles (0 .5 .8) (-8 -8 -8) (8 8 8) + +testing air bubbles +*/ + +void() air_bubbles = + +{ + if (deathmatch) + { + remove (self); + return; + } + precache_model ("progs/s_bubble.spr"); + self.nextthink = time + 1; + self.think = make_bubbles; +}; + +void() make_bubbles = +{ +local entity bubble; + + bubble = spawn(); + setmodel (bubble, "progs/s_bubble.spr"); + setorigin (bubble, self.origin); + bubble.movetype = MOVETYPE_NOCLIP; + bubble.solid = SOLID_NOT; + bubble.velocity = '0 0 15'; + bubble.nextthink = time + 0.5; + bubble.think = bubble_bob; + bubble.touch = bubble_remove; + bubble.classname = "bubble"; + bubble.frame = 0; + bubble.cnt = 0; + setsize (bubble, '-8 -8 -8', '8 8 8'); + self.nextthink = time + random() + 0.5; + self.think = make_bubbles; +}; + +void() bubble_split = +{ +local entity bubble; + bubble = spawn(); + setmodel (bubble, "progs/s_bubble.spr"); + setorigin (bubble, self.origin); + bubble.movetype = MOVETYPE_NOCLIP; + bubble.solid = SOLID_NOT; + bubble.velocity = self.velocity; + bubble.nextthink = time + 0.5; + bubble.think = bubble_bob; + bubble.touch = bubble_remove; + bubble.classname = "bubble"; + bubble.frame = 1; + bubble.cnt = 10; + setsize (bubble, '-8 -8 -8', '8 8 8'); + self.frame = 1; + self.cnt = 10; + if (self.waterlevel != 3) + remove (self); +}; + +void() bubble_remove = +{ + if (other.classname == self.classname) + { +// dprint ("bump"); + return; + } + remove(self); +}; + +void() bubble_bob = +{ +local float rnd1, rnd2, rnd3; +local vector vtmp1, modi; + + self.cnt = self.cnt + 1; + if (self.cnt == 4) + bubble_split(); + if (self.cnt == 20) + remove(self); + + rnd1 = self.velocity_x + (-10 + (random() * 20)); + rnd2 = self.velocity_y + (-10 + (random() * 20)); + rnd3 = self.velocity_z + 10 + random() * 10; + + if (rnd1 > 10) + rnd1 = 5; + if (rnd1 < -10) + rnd1 = -5; + + if (rnd2 > 10) + rnd2 = 5; + if (rnd2 < -10) + rnd2 = -5; + + if (rnd3 < 10) + rnd3 = 15; + if (rnd3 > 30) + rnd3 = 25; + + self.velocity_x = rnd1; + self.velocity_y = rnd2; + self.velocity_z = rnd3; + + self.nextthink = time + 0.5; + self.think = bubble_bob; +}; + +/*~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~> +~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~<~>~*/ + +/*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) + +Just for the debugging level. Don't use + + +void() viewthing = + +{ + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + precache_model ("progs/player.mdl"); + setmodel (self, "progs/player.mdl"); +}; +*/ + +/* +============================================================================== + +SIMPLE BMODELS + +============================================================================== +*/ + +void() func_wall_use = +{ // change to alternate textures + self.frame = 1 - self.frame; +}; + +/*QUAKED func_wall (0 .5 .8) ? +This is just a solid wall if not inhibitted +*/ +void() func_wall = +{ + self.angles = '0 0 0'; + self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + self.solid = SOLID_BSP; + self.use = func_wall_use; + setmodel (self, self.model); +}; + + +/*QUAKED func_illusionary (0 .5 .8) ? +A simple entity that looks solid but lets you walk through it. +*/ +void() func_illusionary = + +{ + self.angles = '0 0 0'; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + setmodel (self, self.model); + makestatic (); +}; + + +//============================================================================ +/*QUAKED ambient_suck_wind (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_suck_wind = +{ + precache_sound ("ambience/suck1.wav"); + ambientsound (self.origin, "ambience/suck1.wav", 1, ATTN_STATIC); +}; + +/*QUAKED ambient_drone (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_drone = +{ + precache_sound ("ambience/drone6.wav"); + ambientsound (self.origin, "ambience/drone6.wav", 0.5, ATTN_STATIC); +}; + +/*QUAKED ambient_flouro_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_flouro_buzz = +{ + precache_sound ("ambience/buzz1.wav"); + ambientsound (self.origin, "ambience/buzz1.wav", 1, ATTN_STATIC); +}; +/*QUAKED ambient_drip (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_drip = +{ + precache_sound ("ambience/drip1.wav"); + ambientsound (self.origin, "ambience/drip1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_comp_hum (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_comp_hum = +{ + precache_sound ("ambience/comp1.wav"); + ambientsound (self.origin, "ambience/comp1.wav", 1, ATTN_STATIC); +}; +/*QUAKED ambient_thunder (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_thunder = +{ + precache_sound ("ambience/thunder1.wav"); + ambientsound (self.origin, "ambience/thunder1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_light_buzz (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_light_buzz = +{ + precache_sound ("ambience/fl_hum1.wav"); + ambientsound (self.origin, "ambience/fl_hum1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_swamp1 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_swamp1 = +{ + precache_sound ("ambience/swamp1.wav"); + ambientsound (self.origin, "ambience/swamp1.wav", 0.5, ATTN_STATIC); +}; +/*QUAKED ambient_swamp2 (0.3 0.1 0.6) (-10 -10 -8) (10 10 8) +*/ +void() ambient_swamp2 = +{ + precache_sound ("ambience/swamp2.wav"); + ambientsound (self.origin, "ambience/swamp2.wav", 0.5, ATTN_STATIC); +}; + +//============================================================================ diff --git a/ParoxysmII/source/nq/old.qc b/ParoxysmII/source/nq/old.qc new file mode 100644 index 0000000..01666f8 --- /dev/null +++ b/ParoxysmII/source/nq/old.qc @@ -0,0 +1,24 @@ +//Clean up unused entities + +void() monster_ogre = {remove(self);}; +void() monster_demon1 = {remove(self);}; +void() monster_shambler = {remove(self);}; +void() monster_knight = {remove(self);}; +void() monster_army = {remove(self);}; +void() monster_wizard = {remove(self);}; +void() monster_dog = {remove(self);}; +void() monster_zombie = {remove(self);}; +void() monster_boss = {remove(self);}; +void() monster_tarbaby = {remove(self);}; +void() monster_hell_knight = {remove(self);}; +void() monster_fish = {remove(self);}; +void() monster_shalrath = {remove(self);}; +void() monster_enforcer = {remove(self);}; +void() monster_oldone = {remove(self);}; + +void() path_follow = {remove(self);}; + +void() path_follow2 = {remove(self);}; + +//POX v1.2 EarthQuakes REMOVED! +void() func_earthquake = {remove(self);}; diff --git a/ParoxysmII/source/nq/plats.qc b/ParoxysmII/source/nq/plats.qc new file mode 100644 index 0000000..bd31abb --- /dev/null +++ b/ParoxysmII/source/nq/plats.qc @@ -0,0 +1,392 @@ + + +void() plat_center_touch; +void() plat_outside_touch; +void() plat_trigger_use; +void() plat_go_up; +void() plat_go_down; +void() plat_crush; +float PLAT_LOW_TRIGGER = 1; + +.float on_moveplat; //POX - Holds a pausetime if a bot is on a moving plat + +void() plat_spawn_inside_trigger = +{ + local entity trigger; + local vector tmin, tmax; + +// +// middle trigger +// + trigger = spawn(); + trigger.touch = plat_center_touch; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.enemy = self; + + tmin = self.mins + '25 25 0'; + tmax = self.maxs - '25 25 -8'; + tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8); + if (self.spawnflags & PLAT_LOW_TRIGGER) + tmax_z = tmin_z + 8; + + if (self.size_x <= 50) + { + tmin_x = (self.mins_x + self.maxs_x) / 2; + tmax_x = tmin_x + 1; + } + if (self.size_y <= 50) + { + tmin_y = (self.mins_y + self.maxs_y) / 2; + tmax_y = tmin_y + 1; + } + + setsize (trigger, tmin, tmax); +}; + +void() plat_hit_top = +{ + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_TOP; + self.think = plat_go_down; + self.nextthink = self.ltime + 3; +}; + +void() plat_hit_bottom = +{ + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_BOTTOM; +}; + +void() plat_go_down = +{ + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + self.state = STATE_DOWN; + SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom); +}; + +void() plat_go_up = +{ + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + self.state = STATE_UP; + SUB_CalcMove (self.pos1, self.speed, plat_hit_top); +}; + +void() plat_center_touch = +{ + if (other.classname != "player") + return; + + if (other.health <= 0) + return; + + self = self.enemy; + + if (self.state == STATE_BOTTOM) + plat_go_up (); + else if (self.state == STATE_TOP) + self.nextthink = self.ltime + 1; // delay going down +}; + +void() plat_outside_touch = +{ + if (other.classname != "player") + return; + + if (other.health <= 0) + return; + + self = self.enemy; + +//dprint ("plat_outside_touch\n"); + if (self.state == STATE_TOP) + plat_go_down (); +}; + +void() plat_trigger_use = +{ + if (self.think) + return; // allready activated + plat_go_down(); +}; + + +void() plat_crush = +{ +//dprint ("plat_crush\n"); + + T_Damage (other, self, self, 1); + + if (self.state == STATE_UP) + plat_go_down (); + else if (self.state == STATE_DOWN) + plat_go_up (); + else + objerror ("plat_crush: bad self.state\n"); +}; + +void() plat_use = +{ + self.use = SUB_Null; + if (self.state != STATE_UP) + objerror ("plat_use: not in up state"); + plat_go_down(); +}; + + +/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER +speed default 150 + +Plats are always drawn in the extended position, so they will light correctly. + +If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat. + +If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height. +Set "sounds" to one of the following: +1) base fast +2) chain slow +*/ + + +void() func_plat = + +{ +local entity t; + + if (!self.t_length) + self.t_length = 80; + if (!self.t_width) + self.t_width = 10; + + if (self.sounds == 0) + self.sounds = 3; +// FIX THIS TO LOAD A GENERIC PLAT SOUND + + if (self.sounds == 1) + { + precache_sound ("plats/plat1.wav"); + precache_sound ("plats/plat2.wav"); + self.noise = "plats/plat1.wav"; + self.noise1 = "plats/plat2.wav"; + } + + if (self.sounds == 2) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } + + if (self.sounds == 3) + { + precache_sound ("doors/hydro1.wav"); + precache_sound ("doors/hydro2.wav"); + self.noise = "doors/hydro1.wav"; + self.noise1 = "doors/hydro2.wav"; + } + + if (self.sounds == 4) + { + precache_sound ("doors/stndr1.wav"); + precache_sound ("doors/stndr2.wav"); + self.noise = "doors/stndr1.wav"; + self.noise1 = "doors/stndr2.wav"; + } + + if (self.sounds == 5) + { + precache_sound ("doors/ddoor1.wav"); + precache_sound ("doors/ddoor2.wav"); + self.noise = "doors/ddoor2.wav"; + self.noise1 = "doors/ddoor1.wav"; + } + + + self.mangle = self.angles; + self.angles = '0 0 0'; + + self.classname = "plat"; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + setorigin (self, self.origin); + setmodel (self, self.model); + setsize (self, self.mins , self.maxs); + + self.blocked = plat_crush; + if (!self.speed) + self.speed = 150; + +// pos1 is the top position, pos2 is the bottom + self.pos1 = self.origin; + self.pos2 = self.origin; + if (self.height) + self.pos2_z = self.origin_z - self.height; + else + self.pos2_z = self.origin_z - self.size_z + 8; + + self.use = plat_trigger_use; + + plat_spawn_inside_trigger (); // the "start moving" trigger + + if (self.targetname) + { + self.state = STATE_UP; + self.use = plat_use; + } + else + { + setorigin (self, self.pos2); + self.state = STATE_BOTTOM; + } +}; + +//============================================================================ + +void() train_next; +void() func_train_find; + +void() train_blocked = +{ + if (time < self.attack_finished) + return; + self.attack_finished = time + 0.5; + T_Damage (other, self, self, self.dmg); +}; +void() train_use = +{ + if (self.think != func_train_find) + return; // already activated + train_next(); +}; + +void() train_wait = +{ + if (self.wait) + { + self.nextthink = self.ltime + self.wait; + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + } + else + self.nextthink = self.ltime + 0.1; + + self.think = train_next; +}; + +void() train_next = +{ + local entity targ; + + targ = find (world, targetname, self.target); + self.target = targ.target; + if (!self.target) + objerror ("train_next: no next target"); + if (targ.wait) + self.wait = targ.wait; + else + self.wait = 0; + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait); +}; + +void() func_train_find = + +{ + local entity targ; + + targ = find (world, targetname, self.target); + self.target = targ.target; + setorigin (self, targ.origin - self.mins); + if (!self.targetname) + { // not triggered, so start immediately + self.nextthink = self.ltime + 0.1; + self.think = train_next; + } +}; + +/*QUAKED func_train (0 .5 .8) ? +Trains are moving platforms that players can ride. +The targets origin specifies the min point of the train at each corner. +The train spawns at the first target it is pointing at. +If the train is the target of a button or trigger, it will not begin moving until activated. +speed default 100 +dmg default 2 +sounds +1) ratchet metal + +*/ +void() func_train = +{ + if (!self.speed) + self.speed = 100; + if (!self.target) + objerror ("func_train without a target"); + if (!self.dmg) + self.dmg = 2; + + if (self.sounds == 0) + { + self.noise = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + } + + if (self.sounds == 1) + { + self.noise = ("plats/train2.wav"); + precache_sound ("plats/train2.wav"); + self.noise1 = ("plats/train1.wav"); + precache_sound ("plats/train1.wav"); + } + + self.cnt = 1; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.blocked = train_blocked; + self.use = train_use; + self.classname = "train"; + + setmodel (self, self.model); + setsize (self, self.mins , self.maxs); + setorigin (self, self.origin); + +// start trains on the second frame, to make sure their targets have had +// a chance to spawn + self.nextthink = self.ltime + 0.1; + self.think = func_train_find; +}; + +/*QUAKED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8) +This is used for the final bos +*/ +void() misc_teleporttrain = +{ + if (!self.speed) + self.speed = 100; + if (!self.target) + objerror ("func_train without a target"); + + self.cnt = 1; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_PUSH; + self.blocked = train_blocked; + self.use = train_use; + self.avelocity = '100 200 300'; + + self.noise = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + + precache_model2 ("progs/teleport.mdl"); + setmodel (self, "progs/teleport.mdl"); + setsize (self, self.mins , self.maxs); + setorigin (self, self.origin); + +// start trains on the second frame, to make sure their targets have had +// a chance to spawn + self.nextthink = self.ltime + 0.1; + self.think = func_train_find; +}; + diff --git a/ParoxysmII/source/nq/player.qc b/ParoxysmII/source/nq/player.qc new file mode 100644 index 0000000..0176271 --- /dev/null +++ b/ParoxysmII/source/nq/player.qc @@ -0,0 +1,1073 @@ + +void() bubble_bob; + +/* +============================================================================== + +PLAYER + +============================================================================== +*/ + +//POX - Frame macros deleted (frames are now called by absolute values to simplify visible weapon animations) + +//POX - added entity discrimination for bot support +void(float weap, entity me) getmodel = + +{ + if(me.health <= 0) + return; + if (weap == IT_TSHOT) + + me.modelindex = modelindex_tshot_p; + + else if (weap == IT_COMBOGUN) + + me.modelindex = modelindex_combo_p; + + else if (weap == IT_PLASMAGUN) + + me.modelindex = modelindex_plasma_p; + + else if (weap == IT_SUPER_NAILGUN) + + me.modelindex = modelindex_nail_p; + + else if (weap == IT_GRENADE_LAUNCHER) + + me.modelindex = modelindex_gren_p; + + else if (weap == IT_ROCKET_LAUNCHER) + + me.modelindex = modelindex_rock_p; + + else + me.modelindex = modelindex_saw_p; + +}; + +/* +============================================================================== +PLAYER +============================================================================== +*/ + +void() player_run; + +void() player_stand1 =[ 6, player_stand1 ] +{ + //self.weaponframe=0; + if (self.velocity_x || self.velocity_y) + { + self.walkframe=0; + player_run(); + return; + } + + if (self.weapon == IT_BONESAW) + + { + + self.weaponframe = self.weaponframe + 1; + + if(self.weaponframe >= 3) + + { + + self.weaponframe = 0; + + if (self.view_ofs != '0 0 0') + + sound (self, CHAN_AUTO, "weapons/sawidle.wav", 1, ATTN_NORM); + + } + + } + else + self.weaponframe=0; + + if (self.walkframe >= 4) + self.walkframe = 0; + + self.frame = 6 + self.walkframe; + + self.walkframe = self.walkframe + 1; +}; + +void() player_run =[0, player_run ] +{ + //self.weaponframe=0; + if (!self.velocity_x && !self.velocity_y) + { + self.walkframe=0; + player_stand1(); + return; + } + + if (self.weapon == IT_BONESAW) + + { + + self.weaponframe = self.weaponframe + 1; + + if(self.weaponframe >= 3) + + { + + self.weaponframe = 0; + + if (self.view_ofs != '0 0 0') + + sound (self, CHAN_AUTO, "weapons/sawidle.wav", 1, ATTN_NORM); + + } + + } + else + self.weaponframe=0; + + if (self.walkframe == 6) + self.walkframe = 0; + self.frame = self.walkframe; + +//POX v1.1 - changed to include water sounds for swimming and walking in shallow water +// strat footstep routine (modified from Hipnotic's QW source code) + + self.spawnsilent = self.spawnsilent + vlen(self.origin - self.old_velocity); + self.old_velocity = self.origin; + + if (self.waterlevel < 3 && self.classname == "player") // no footsteps underwater, for observers + { + if (self.spawnsilent > 95) + { + local float r; + + if (self.spawnsilent > 190) + self.spawnsilent = 0; + else + self.spawnsilent = 0.5*(self.spawnsilent - 95); + + r = random(); + + if (self.waterlevel) + { + if (r < 0.25) + sound (self, CHAN_AUTO, "misc/water1.wav", 1, ATTN_NORM); + else if (r < 0.5) + sound (self, CHAN_AUTO, "misc/water2.wav", 1, ATTN_NORM); + else if (r < 0.75) + sound (self, CHAN_AUTO, "misc/owater2.wav", 0.75, ATTN_NORM); + } + else if (self.flags & FL_ONGROUND) + { + if (r < 0.25) + sound (self, CHAN_AUTO, "misc/foot1.wav", 0.75, ATTN_NORM); + else if (r < 0.5) + sound (self, CHAN_AUTO, "misc/foot2.wav", 0.75, ATTN_NORM); + else if (r < 0.75) + sound (self, CHAN_AUTO, "misc/foot3.wav", 0.75, ATTN_NORM); + else + sound (self, CHAN_AUTO, "misc/foot4.wav", 0.75, ATTN_NORM); + } + } + } + + self.walkframe = self.walkframe + 1; +}; + +/* +//used for PulseGun fire +void() player_pulse1 = [$shotatt1, player_pulse2 ] {self.weaponframe=1; +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_pulse2 = [$shotatt2, player_pulse3 ] {self.weaponframe=2;}; +void() player_pulse3 = [$shotatt3, player_pulse4 ] {self.weaponframe=3;}; +void() player_pulse4 = [$shotatt4, player_pulse5 ] {self.weaponframe=4;}; +void() player_pulse5 = [$shotatt5, player_run ] {self.weaponframe=5;}; + +//used for PulseGun 'flex' +//not called if deathmatch since player will slide +void() player_pflex1 = [$shotatt1, player_pflex2 ] {self.weaponframe=6;}; +void() player_pflex2 = [$shotatt2, player_pflex3 ] {self.weaponframe=7;}; +void() player_pflex3 = [$shotatt3, player_pflex4 ] {self.weaponframe=8;}; +void() player_pflex4 = [$shotatt4, player_pflex5 ] {self.weaponframe=9;}; +void() player_pflex5 = [$shotatt5, player_pflex6 ] {self.weaponframe=10;}; +void() player_pflex6 = [$shotatt5, player_run ] {self.weaponframe=11;}; +*/ + +//used for t-sht and combo gun +void() player_shot1 = [15, player_shot2 ] {self.weaponframe=1; +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_shot2 = [16, player_shot3 ] {self.weaponframe=2;}; +void() player_shot3 = [17, player_shot4 ] {self.weaponframe=3;}; +void() player_shot4 = [18, player_shot5 ] {self.weaponframe=4;}; +void() player_shot5 = [18, player_shot6 ] {self.weaponframe=5;}; +void() player_shot6 = [19, player_run ] {self.weaponframe=6;}; + +// New Frame Macro For T-Shot 3 barrel fire +void() player_tshot1 = [15, player_tshot2 ] {self.weaponframe=1; +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_tshot2 = [16, player_tshot3 ] {self.weaponframe=16;};//triple flare frame +void() player_tshot3 = [17, player_tshot4 ] {self.weaponframe=3;}; +void() player_tshot4 = [18, player_tshot5 ] {self.weaponframe=4;}; +void() player_tshot5 = [18, player_tshot6 ] {self.weaponframe=5;}; +void() player_tshot6 = [19, player_run ] {self.weaponframe=6;}; +// End 3 barrel fire Macro + +// New Frame Macro For T-Shot pump +// DM player may slide a little too much during this animation (?) +void() player_reshot1 = [15, player_reshot2 ] {self.weaponframe=7;}; +void() player_reshot2 = [17, player_reshot3 ] {self.weaponframe=8;}; +void() player_reshot3 = [18, player_reshot4 ] {self.weaponframe=9;}; +void() player_reshot4 = [19, player_reshot5 ] {self.weaponframe=10;}; +void() player_reshot5 = [19, player_reshot6 ] {self.weaponframe=11;}; +void() player_reshot6 = [18, player_reshot7 ] {self.weaponframe=12;}; +void() player_reshot7 = [17, player_reshot8 ] {self.weaponframe=13;}; +void() player_reshot8 = [17, player_reshot9 ] {self.weaponframe=14;}; +void() player_reshot9 = [15, player_run ] {self.weaponframe=15;}; +// End T-shot prime Macro + +// New Frame Macro For SuperShotgun Second Trigger (Impact Grenades) +void() player_gshot1 = [15, player_gshot2 ] {self.weaponframe=2; +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_gshot2 = [16, player_gshot3 ] {self.weaponframe=3;}; +void() player_gshot3 = [17, player_gshot4 ] {self.weaponframe=4;}; +void() player_gshot4 = [18, player_gshot5 ] {self.weaponframe=5;}; +void() player_gshot5 = [19, player_run ] {self.weaponframe=6;}; +// End SuperShotgun Second Trigger Macro + +// New Frame Macro For Nailgun Second Trigger (Shrapnel Bomb) +void() player_shrap1 = [15, player_shrap2 ] { +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_shrap2 = [16, player_run ] {}; +// End Nailgun Second Trigger Macro + +//POX - this plays the saw off animation +void() player_sawoff1 = [15, player_sawoff2] {self.weaponframe=5;sound (self, CHAN_WEAPON, "weapons/sawoff.wav", 1, ATTN_NORM);}; +void() player_sawoff2 = [16, player_sawoff3] {self.weaponframe=4;}; +void() player_sawoff3 = [15, player_run] {self.weaponframe=3;}; + +//POX - this sets up the start animation +void() player_bonesaw1 = [15, player_bonesaw2] +{ + self.weaponframe = 4; + + sound (self, CHAN_WEAPON, "weapons/sawon.wav", 1, ATTN_NORM); + + self.saw_on = TRUE; + + if (!self.button0) + {player_sawoff1();self.saw_on = FALSE;return;} + +}; + +void() player_bonesaw3; //predeclare + +//POX - this cycles the startup animation +void() player_bonesaw2 = [16, player_bonesaw3] +{ + + if (!self.button0) + {player_sawoff1();self.saw_on = FALSE;return;} + + self.weaponframe = 5; + + W_FireSaw(); + SuperDamageSound(); + +}; + +//POX - these two play the firing animation +void() player_bonesaw3 = [15, player_bonesaw4] +{ + + if (!self.button0) + {player_sawoff1();self.saw_on = FALSE;return;} + self.weaponframe = self.weaponframe + 1; + if (self.weaponframe == 12) + self.weaponframe = 6; + +}; + +void() player_bonesaw4 = [16, player_bonesaw3] +{ + + sound (self, CHAN_WEAPON, "weapons/sawatck.wav", 1, ATTN_NORM); + + if (!self.button0) + {player_sawoff1();self.saw_on = FALSE;return;} + self.weaponframe = self.weaponframe + 1; + if (self.weaponframe == 12) + self.weaponframe = 6; + + W_FireSaw(); + SuperDamageSound(); + +}; + +//============================================================================ + +/* Bye-bye Quake Axe +void() player_axe1 = [$axatt1, player_axe2 ] {self.weaponframe=1;}; +void() player_axe2 = [$axatt2, player_axe3 ] {self.weaponframe=2;}; +void() player_axe3 = [$axatt3, player_axe4 ] {self.weaponframe=3;W_FireAxe();}; +void() player_axe4 = [$axatt4, player_run ] {self.weaponframe=4;}; + +void() player_axeb1 = [$axattb1, player_axeb2 ] {self.weaponframe=5;}; +void() player_axeb2 = [$axattb2, player_axeb3 ] {self.weaponframe=6;}; +void() player_axeb3 = [$axattb3, player_axeb4 ] {self.weaponframe=7;W_FireAxe();}; +void() player_axeb4 = [$axattb4, player_run ] {self.weaponframe=8;}; + +void() player_axec1 = [$axattc1, player_axec2 ] {self.weaponframe=1;}; +void() player_axec2 = [$axattc2, player_axec3 ] {self.weaponframe=2;}; +void() player_axec3 = [$axattc3, player_axec4 ] {self.weaponframe=3;W_FireAxe();}; +void() player_axec4 = [$axattc4, player_run ] {self.weaponframe=4;}; + +void() player_axed1 = [$axattd1, player_axed2 ] {self.weaponframe=5;}; +void() player_axed2 = [$axattd2, player_axed3 ] {self.weaponframe=6;}; +void() player_axed3 = [$axattd3, player_axed4 ] {self.weaponframe=7;W_FireAxe();}; +void() player_axed4 = [$axattd4, player_run ] {self.weaponframe=8;}; +*/ +//============================================================================ + +//POX - 102b - A really stupid overlook caused the nailgun and plasmagun to ignore attack_finshed (st_nailgun and st_plasma) +//This has now been fixed, and will hopefully prevent packet errors + +void() player_nail1 =[15, player_nail2 ] +{ + if (self.st_nailgun > time) + return; + + if (self.ammo_nails < 1) + { + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_nailgun = time + 0.4; + player_run ();return; + } + else + { + self.effects = self.effects | EF_MUZZLEFLASH; + + if (!self.button0) + {player_run ();return;} + self.weaponframe = self.weaponframe + 1; + if (self.weaponframe == 9) + self.weaponframe = 1; + SuperDamageSound(); + W_FireNails (3); + + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward*-20; + + self.st_nailgun = time + 0.1; + } +}; +void() player_nail2 =[16, player_nail1 ] +{ + if (self.st_nailgun > time) + return; + + if (self.ammo_nails < 1) + { + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_nailgun = time + 0.4; + player_run ();return; + } + else + { + + self.effects = self.effects | EF_MUZZLEFLASH; + + if (!self.button0) + {player_run ();return;} + self.weaponframe = self.weaponframe + 1; + if (self.weaponframe == 9) + self.weaponframe = 1; + SuperDamageSound(); + W_FireNails (-3); + + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward*-20; + + self.st_nailgun = time + 0.1; + + } +}; + +//============================================================================ +//============================================================================ + +void() player_plasma1 =[15, player_plasma2 ] +{ + if (self.st_plasma > time) + return; + + if (self.ammo_cells < 1) + { + sound (self, CHAN_WEAPON, "weapons/mfire2.wav", 1, ATTN_NORM); + player_run (); + self.st_plasma = time + 0.4; + return; + } + else + { + self.effects = self.effects | EF_MUZZLEFLASH; + + if (!self.button0) + {player_run ();return;} + + self.weaponframe = 1; + self.LorR = 1; + + SuperDamageSound(); + W_FirePlasma (6); + + + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward*-20; + + + self.st_plasma = time + 0.1; + } + +}; + +void() player_plasma2 =[16, player_plasma1 ] +{ + if (self.st_plasma > time) + return; + + if (self.ammo_cells < 1) + { + sound (self, CHAN_WEAPON, "weapons/mfire2.wav", 1, ATTN_NORM); + player_run (); + self.st_plasma = time + 0.4; + return; + } + + else + { + + self.effects = self.effects | EF_MUZZLEFLASH; + + if (!self.button0) + {player_run ();return;} + + self.weaponframe = 2; + self.LorR = 0; + + SuperDamageSound(); + W_FirePlasma (-7); + + + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward*-20; + + + self.st_plasma = time + 0.1; + } + +}; + +//Had to seperate Grenade Macro from Anihilator +//to make room for new rhino animations +void() player_grenade1 =[15, player_grenade2 ] {self.weaponframe=1; +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_grenade2 =[16, player_grenade3 ] {self.weaponframe=2;}; +void() player_grenade3 =[17, player_grenade4 ] {self.weaponframe=3;}; +void() player_grenade4 =[18, player_grenade5 ] {self.weaponframe=4;}; +void() player_grenade5 =[18, player_grenade6 ] {self.weaponframe=5;}; +void() player_grenade6 =[19, player_run ] {self.weaponframe=6;}; + + +//MegaPlasma Burst +void() player_mplasma1 =[15, player_mplasma2 ] {self.weaponframe=0;}; +void() player_mplasma2 =[16, player_mplasma3 ] {self.weaponframe=0;}; +void() player_mplasma3 =[16, player_run ] {}; + +// rhino fire sequence +void() player_rocket1 =[14, player_rocket2 ] {self.weaponframe=1; +W_FireRocket('0 0 16'); +//sound is timed for double shot (two single shot sounds had inconsistant results) +sound (self, CHAN_WEAPON, "weapons/rhino.wav", 1, ATTN_NORM); + + if (self.flags & FL_ONGROUND) + self.velocity = v_forward* -25; + +self.punchangle_x = -3; +self.effects = self.effects | EF_MUZZLEFLASH;}; +void() player_rocket2 =[15, player_rocket3 ] {self.weaponframe=2; +W_FireRocket('0 0 24');}; +void() player_rocket3 =[16, player_run ] {self.weaponframe=3;}; + +//rhino Reload sequence +void() player_rocketload1 =[6, player_rocketload2 ] {self.weaponframe=4;}; +void() player_rocketload2 =[17, player_rocketload3 ] {self.weaponframe=5;}; +void() player_rocketload3 =[18, player_rocketload4 ] {self.weaponframe=6;}; +void() player_rocketload4 =[18, player_rocketload5 ] {self.weaponframe=7;}; +void() player_rocketload5 =[19, player_rocketload6 ] {self.weaponframe=8;}; +void() player_rocketload6 =[17, player_run ] {self.weaponframe=9;}; + + +void(float num_bubbles) DeathBubbles; + +void() PainSound = +{ +local float rs; + + if (self.health < 0) + return; + + if (damage_attacker.classname == "teledeath") + { + sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE); + return; + } + +// water pain sounds + if (self.watertype == CONTENT_WATER && self.waterlevel == 3) + { + DeathBubbles(1); + if (random() > 0.5) + sound (self, CHAN_VOICE, "player/drown1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "player/drown2.wav", 1, ATTN_NORM); + return; + } + +// slime pain sounds + if (self.watertype == CONTENT_SLIME) + { +// FIX ME put in some steam here + if (random() > 0.5) + sound (self, CHAN_VOICE, "player/lburn1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "player/lburn2.wav", 1, ATTN_NORM); + return; + } + + if (self.watertype == CONTENT_LAVA) + { + if (random() > 0.5) + sound (self, CHAN_VOICE, "player/lburn1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "player/lburn2.wav", 1, ATTN_NORM); + return; + } + + /* + if (self.pain_finished > time) + { + self.axhitme = 0; + return; + } + self.pain_finished = time + 0.5; + +// don't make multiple pain sounds right after each other + +// ax pain sound + if (self.axhitme == 1) + { + self.axhitme = 0; + sound (self, CHAN_VOICE, "player/axhit1.wav", 1, ATTN_NORM); + return; + } + */ + + rs = rint((random() * 5) + 1); + + self.noise = ""; + if (rs == 1) + self.noise = "player/pain1.wav"; + else if (rs == 2) + self.noise = "player/pain2.wav"; + else if (rs == 3) + self.noise = "player/pain3.wav"; + else if (rs == 4) + self.noise = "player/pain4.wav"; + else if (rs == 5) + self.noise = "player/pain5.wav"; + else + self.noise = "player/pain6.wav"; + + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + return; +}; + +void() player_pain1 = [10, player_pain2 ] {PainSound();self.weaponframe=0;}; +void() player_pain2 = [11, player_pain3 ] {}; +void() player_pain3 = [12, player_pain4 ] {}; +void() player_pain4 = [13, player_pain5 ] {}; +void() player_pain5 = [14, player_run ] {}; + + +void() player_painb1 = [10, player_painb2 ] {PainSound();self.weaponframe=0;}; +void() player_painb2 = [11, player_painb3 ] {}; +void() player_painb3 = [12, player_painb4 ] {}; +void() player_painb4 = [14, player_run ] {}; + + +/* +void() player_axpain1 = [ $axpain1, player_axpain2 ] {PainSound();self.weaponframe=0;}; +void() player_axpain2 = [ $axpain2, player_axpain3 ] {}; +void() player_axpain3 = [ $axpain3, player_axpain4 ] {}; +void() player_axpain4 = [ $axpain4, player_axpain5 ] {}; +void() player_axpain5 = [ $axpain5, player_axpain6 ] {}; +void() player_axpain6 = [ $axpain6, player_run ] {}; +*/ + +void() player_pain = +{ + if (self.weaponframe) + return; + + //if (self.invisible_finished > time) + // return; // eyes don't have pain frames + + /* + if (self.weapon == IT_BONESAW) + player_axpain1 (); + else + */ + + if (self.weapon == IT_ROCKET_LAUNCHER) + player_painb1 (); + else + player_pain1 (); +}; + +void() player_diea1; +void() player_dieb1; +void() player_diec1; +void() player_died1; +void() player_diee1; +//void() player_die_ax1; + +void() DeathBubblesSpawn = +{ +local entity bubble; + if (self.owner.waterlevel != 3) + return; + bubble = spawn(); + setmodel (bubble, "progs/s_bubble.spr"); + setorigin (bubble, self.owner.origin + '0 0 24'); + bubble.movetype = MOVETYPE_NOCLIP; + bubble.solid = SOLID_NOT; + bubble.velocity = '0 0 15'; + bubble.nextthink = time + 0.5; + bubble.think = bubble_bob; + bubble.classname = "bubble"; + bubble.frame = 0; + bubble.cnt = 0; + setsize (bubble, '-8 -8 -8', '8 8 8'); + self.nextthink = time + 0.1; + self.think = DeathBubblesSpawn; + self.air_finished = self.air_finished + 1; + if (self.air_finished >= self.bubble_count) + remove(self); +}; + +void(float num_bubbles) DeathBubbles = +{ +local entity bubble_spawner; + + bubble_spawner = spawn(); + setorigin (bubble_spawner, self.origin); + bubble_spawner.movetype = MOVETYPE_NONE; + bubble_spawner.solid = SOLID_NOT; + bubble_spawner.nextthink = time + 0.1; + bubble_spawner.think = DeathBubblesSpawn; + bubble_spawner.air_finished = 0; + bubble_spawner.owner = self; + bubble_spawner.bubble_count = num_bubbles; + return; +}; + + +void() DeathSound = +{ +local float rs; + + // water death sounds + if (self.waterlevel == 3) + { + DeathBubbles(20); + sound (self, CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_NONE); + return; + } + + rs = rint ((random() * 4) + 1); + if (rs == 1) + self.noise = "player/death1.wav"; + if (rs == 2) + self.noise = "player/death2.wav"; + if (rs == 3) + self.noise = "player/death3.wav"; + if (rs == 4) + self.noise = "player/death4.wav"; + if (rs == 5) + self.noise = "player/death5.wav"; + + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NONE); + return; +}; + + +void() PlayerDead = +{ + self.nextthink = -1; +// allow respawn after a certain time + self.deadflag = DEAD_DEAD; +}; + +vector(float dm) VelocityForDamage = +{ + local vector v; + + v_x = 100 * crandom(); + v_y = 100 * crandom(); + v_z = 200 + 100 * random(); + + if (dm > -50) + { +// dprint ("level 1\n"); + v = v * 0.7; + } + else if (dm > -200) + { +// dprint ("level 3\n"); + v = v * 2; + } + else + v = v * 10; + + return v; +}; + +//------------------------------------------------------------------- +//Added this to facilitate the intergration of the Wall Gib code taken +//from Zerstoer without redoing ALL other calls to VelocityForDamage +//------------------------------------------------------------------- + +vector(float dm, vector dir) VelocityForRubble = +{ + local vector v, gib_dir; + + + gib_dir = normalize(dir); + + + + v_x = 90 * gib_dir_x + (random() * 70 - 35); + + v_y = 90 * gib_dir_y + (random() * 70 - 35); + + v_z = 200 + 100 * random(); + + if (dm > -50) + { +// dprint ("level 1\n"); + v = v * 0.9; + } + else if (dm > -200) + { +// dprint ("level 3\n"); + v = v * 2; + } + else + v = v * 10; + + return v; +}; +//---------------------------------- + +void(string gibname, float dm) ThrowGib = +{ + local entity new; + + new = spawn(); + new.origin = self.origin; + setmodel (new, gibname); + setsize (new, '0 0 0', '0 0 0'); + new.velocity = VelocityForDamage (dm); + new.movetype = MOVETYPE_BOUNCE; + new.solid = SOLID_NOT; + new.avelocity_x = random()*600; + new.avelocity_y = random()*600; + new.avelocity_z = random()*600; + new.think = SUB_Remove; + new.ltime = time; + new.nextthink = time + 10 + random()*10; + new.frame = 0; + new.flags = 0; +}; + +void(string gibname, float dm) ThrowHead = +{ + setmodel (self, gibname); + self.frame = 0; + self.nextthink = -1; + self.movetype = MOVETYPE_BOUNCE; + self.takedamage = DAMAGE_NO; + self.solid = SOLID_NOT; + self.view_ofs = '0 0 8'; + setsize (self, '-16 -16 0', '16 16 56'); + self.velocity = VelocityForDamage (dm); + self.origin_z = self.origin_z - 24; + self.flags = self.flags - (self.flags & FL_ONGROUND); + self.avelocity = crandom() * '0 600 0'; +}; + +void(float weap) ThrowWeapon = + +{ + + local entity new; + + + new = spawn(); + + new.origin = self.origin; + + if (weap == IT_TSHOT) setmodel (new, "progs/d_tshot.mdl"); + + else if (weap == IT_COMBOGUN) setmodel (new, "progs/d_combo.mdl"); + + else if (weap == IT_PLASMAGUN) setmodel (new, "progs/d_plasma.mdl"); + + else if (weap == IT_SUPER_NAILGUN) setmodel (new, "progs/d_nail.mdl"); + + else if (weap == IT_GRENADE_LAUNCHER) setmodel (new, "progs/d_gren.mdl"); + + else if (weap == IT_ROCKET_LAUNCHER) setmodel (new, "progs/d_rhino.mdl"); + + else setmodel (new, "progs/d_bsaw.mdl"); + + //TEMPORARY PATCH!!! + //else + //{ + // remove(new); + // return; + //} + + + setsize (new, '0 0 0', '0 0 0'); + + new.velocity_x = 150 * crandom(); + + new.velocity_y = 150 * crandom(); + + new.velocity_z = 100 + 200 * random(); + + + + new.movetype = MOVETYPE_BOUNCE; + + new.solid = SOLID_NOT; + + new.avelocity_y = random()*600; + + new.angles_y = 50 * crandom(); + + new.angles_y = anglemod(self.angles_y + new.angles_y); + + new.frame = 0; + + new.flags = 0; + + CopyToBodyQue(new); + + remove(new); + +}; + +void() GibPlayer = +{ + //POX - make a nice gib in Gib mode + if (deathmatch & DM_GIB) + self.health = self.health - 40; + + ThrowHead ("progs/h_player.mdl", self.health); + ThrowGib ("progs/gib1.mdl", self.health); + ThrowGib ("progs/gib2.mdl", self.health); + ThrowGib ("progs/gib3.mdl", self.health); + + self.deadflag = DEAD_DEAD; + + if (damage_attacker.classname == "teledeath") + { + sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE); + return; + } + + if (damage_attacker.classname == "teledeath2") + { + sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE); + return; + } + + if (random() < 0.5) + sound (self, CHAN_VOICE, "player/gib.wav", 1, ATTN_NONE); + else + sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NONE); +}; + +void() PlayerDie = +{ + local float i; + + //self.items = self.items - (self.items & IT_INVISIBILITY); + self.invisible_finished = 0; // don't die as eyes + self.invincible_finished = 0; + self.super_damage_finished = 0; + self.radsuit_finished = 0; + //self.modelindex = modelindex_player; // don't use eyes + + self.modelindex = modelindex_death; + self.frame = 44; + + //self.skin = 0; + + //POX v1.11 - clear the target print so it doesn't obscure the scoreboard + if (self.target_id_toggle) + centerprint (self, ""); + + ThrowWeapon(self.weapon); + + self.weaponmodel=""; + self.view_ofs = '0 0 -8'; + self.deadflag = DEAD_DYING; + self.solid = SOLID_NOT; + self.flags = self.flags - (self.flags & FL_ONGROUND); + self.movetype = MOVETYPE_TOSS; + if (self.velocity_z < 10) + self.velocity_z = self.velocity_z + random()*300; + +//POX check for gib mode as well as regular gib + if (self.health < -40 || (deathmatch & DM_GIB)) + { + GibPlayer (); + return; + } + + if (deathmatch || coop) + DropBackpack(); + + DeathSound(); + + self.angles_x = 0; + self.angles_z = 0; + + /* + if (self.weapon == IT_AXE) + { + player_die_ax1 (); + return; + } + */ + + i = 1 + floor(random()*6); + + if (i == 1) + player_diea1(); + else if (i == 2) + player_dieb1(); + else if (i == 3) + player_diec1(); + else if (i == 4) + player_died1(); + else + player_diee1(); + +}; + +void() set_suicide_frame = +{ // used by klill command and diconnect command + if (self.model == "progs/h_player.mdl") + return; // allready gibbed + +//POX - 1.01b - to simplify things, and to correct some errors brought on by the Visible Weapon patch, +//just gib suicides and dissconnects + GibPlayer(); + + /* + self.modelindex = modelindex_death; + self.frame = 43; + + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_TOSS; + self.deadflag = DEAD_DEAD; + self.nextthink = -1; + */ +}; + + +// VisWeap MOD: replaced all frame refs with numbers in all death scenes. +void() player_diea1 = [ 33, player_diea2 ] {}; +void() player_diea2 = [ 34, player_diea3 ] {}; +void() player_diea3 = [ 35, player_diea4 ] {}; +void() player_diea4 = [ 36, player_diea5 ] {}; +void() player_diea5 = [ 37, player_diea6 ] {}; +void() player_diea6 = [ 38, player_diea7 ] {}; +void() player_diea7 = [ 39, player_diea8 ] {}; +void() player_diea8 = [ 40, player_diea9 ] {}; +void() player_diea9 = [ 41, player_diea10 ] {}; +void() player_diea10 = [ 42, player_diea11 ] {}; +void() player_diea11 = [ 43, player_diea11 ] {PlayerDead();}; + +void() player_dieb1 = [ 44, player_dieb2 ] {}; +void() player_dieb2 = [ 45, player_dieb3 ] {}; +void() player_dieb3 = [ 46, player_dieb4 ] {}; +void() player_dieb4 = [ 47, player_dieb5 ] {}; +void() player_dieb5 = [ 48, player_dieb6 ] {}; +void() player_dieb6 = [ 49, player_dieb7 ] {}; +void() player_dieb7 = [ 50, player_dieb8 ] {}; +void() player_dieb8 = [ 51, player_dieb9 ] {}; +void() player_dieb9 = [ 52, player_dieb9 ] {PlayerDead();}; + +void() player_diec1 = [ 53, player_diec2 ] {}; +void() player_diec2 = [ 54, player_diec3 ] {}; +void() player_diec3 = [ 55, player_diec4 ] {}; +void() player_diec4 = [ 56, player_diec5 ] {}; +void() player_diec5 = [ 57, player_diec6 ] {}; +void() player_diec6 = [ 58, player_diec7 ] {}; +void() player_diec7 = [ 59, player_diec8 ] {}; +void() player_diec8 = [ 60, player_diec9 ] {}; +void() player_diec9 = [ 61, player_diec10 ] {}; +void() player_diec10 = [ 62, player_diec11 ] {}; +void() player_diec11 = [ 63, player_diec12 ] {}; +void() player_diec12 = [ 64, player_diec13 ] {}; +void() player_diec13 = [ 65, player_diec14 ] {}; +void() player_diec14 = [ 66, player_diec15 ] {}; +void() player_diec15 = [ 67, player_diec15 ] {PlayerDead();}; + +void() player_died1 = [ 68, player_died2 ] {}; +void() player_died2 = [ 69, player_died3 ] {}; +void() player_died3 = [ 70, player_died4 ] {}; +void() player_died4 = [ 71, player_died5 ] {}; +void() player_died5 = [ 72, player_died6 ] {}; +void() player_died6 = [ 73, player_died7 ] {}; +void() player_died7 = [ 74, player_died8 ] {}; +void() player_died8 = [ 75, player_died9 ] {}; +void() player_died9 = [ 76, player_died9 ] {PlayerDead();}; + +void() player_diee1 = [ 77, player_diee2 ] {}; +void() player_diee2 = [ 78, player_diee3 ] {}; +void() player_diee3 = [ 79, player_diee4 ] {}; +void() player_diee4 = [ 80, player_diee5 ] {}; +void() player_diee5 = [ 81, player_diee6 ] {}; +void() player_diee6 = [ 82, player_diee7 ] {}; +void() player_diee7 = [ 83, player_diee8 ] {}; +void() player_diee8 = [ 84, player_diee9 ] {}; +void() player_diee9 = [ 85, player_diee9 ] {PlayerDead();}; + +/* +void() player_die_ax1 = [ $axdeth1, player_die_ax2 ] {}; +void() player_die_ax2 = [ $axdeth2, player_die_ax3 ] {}; +void() player_die_ax3 = [ $axdeth3, player_die_ax4 ] {}; +void() player_die_ax4 = [ $axdeth4, player_die_ax5 ] {}; +void() player_die_ax5 = [ $axdeth5, player_die_ax6 ] {}; +void() player_die_ax6 = [ $axdeth6, player_die_ax7 ] {}; +void() player_die_ax7 = [ $axdeth7, player_die_ax8 ] {}; +void() player_die_ax8 = [ $axdeth8, player_die_ax9 ] {}; +void() player_die_ax9 = [ $axdeth9, player_die_ax9 ] {PlayerDead();}; +*/ diff --git a/ParoxysmII/source/nq/poxmenu.qc b/ParoxysmII/source/nq/poxmenu.qc new file mode 100644 index 0000000..ca7daff --- /dev/null +++ b/ParoxysmII/source/nq/poxmenu.qc @@ -0,0 +1,278 @@ +//Paroxysm DM Setup Menu - by Frank Condello (pox@planetquake.com) +//Called when on the start map - basically takes control of the player and only allows menu specific impulses + +//This allows multiple strings variables to be centre printed +void(entity client, string s1, string s2, string s3, string s4, string s5, string s6, string s7) menucenterprint7 = #73; + +float dm_selected; +float button_off; +void() DisplayRules; + +void() CalcDM_Value = +{ +local string setdm; +local float holddm; + + holddm = cvar("deathmatch"); + + if (dm_selected == 1) + { + if (holddm & DM_PREDATOR) + holddm = holddm - DM_PREDATOR; + else + holddm = holddm + DM_PREDATOR; + } + + if (dm_selected == 2) + { + if (holddm & DM_DARK) + holddm = holddm - DM_DARK; + else + holddm = holddm + DM_DARK; + } + + if (dm_selected == 3) + { + if (holddm & DM_LMS) + holddm = holddm - DM_LMS; + else + holddm = holddm + DM_LMS; + } + + + if (dm_selected == 4) + { + if (holddm & DM_FFA) + holddm = holddm - DM_FFA; + else + holddm = holddm + DM_FFA; + } + + if (dm_selected == 5) + { + if (holddm & DM_GIB) + holddm = holddm - DM_GIB; + else + holddm = holddm + DM_GIB; + } + + if (dm_selected == 6) + { + if (holddm & DM_AUTOSWITCH) + holddm = holddm - DM_AUTOSWITCH; + else + holddm = holddm + DM_AUTOSWITCH; + } + + if (dm_selected == 7) + { + if (holddm & DM_NOBOTS) + holddm = holddm - DM_NOBOTS; + else + holddm = holddm + DM_NOBOTS; + } + + setdm = ftos(holddm); + cvar_set ("deathmatch", setdm); + +}; + +//Check for key presses +void() MenuImpulses = +{ + if (self.impulse == 10) //Cycle forward + { + sound (self, CHAN_AUTO, "misc/menu1.wav", 1, ATTN_NONE); + dm_selected = dm_selected + 1; + if (dm_selected == 8) + dm_selected = 1; + } + + if (self.impulse == 12) //Cycle Backward + { + sound (self, CHAN_AUTO, "misc/menu1.wav", 1, ATTN_NONE); + dm_selected = dm_selected - 1; + if (dm_selected == 0) + dm_selected = 7; + } + + //POX - 1.01b - Display Server Rules Impulse + if (self.impulse == 253) + DisplayRules(); + + //button_off negates 'auto-fire' problems + if ((self.button0 || self.button2) && button_off == TRUE) + { + sound (self, CHAN_AUTO, "misc/menu2.wav", 1, ATTN_NONE); + CalcDM_Value(); + button_off = FALSE; + return; + } + + if (!self.button0 && !self.button2) + button_off = TRUE; + +}; + + +//Called at PlayerPostThink, updates the printed display +void() MenuThink = +{ +local string pred, dark, lms, ffa, gib, autos, nobots; + + if (self.classname == "player") + { + centerprint (self, "Please Wait.\nThe server is being configured"); + return; + } + + if (cvar("deathmatch") & DM_PREDATOR) + { + if (dm_selected == 1) + pred = "Óåòöåò Òõìåó\n\n>on | Predator Mode \n"; + else + pred = "Óåòöåò Òõìåó\n\n on | Predator Mode \n"; + } + else + { + if (dm_selected == 1) + pred = "Óåòöåò Òõìåó\n\n>ÏÆÆ | Predator Mode \n"; + else + pred = "Óåòöåò Òõìåó\n\n ÏÆÆ | Predator Mode \n"; + } + + if (cvar("deathmatch") & DM_DARK) + { + if (dm_selected == 2) + dark = ">on | Dark Mode \n"; + else + dark = " on | Dark Mode \n"; + } + else + { + if (dm_selected == 2) + dark = ">ÏÆÆ | Dark Mode \n"; + else + dark = " ÏÆÆ | Dark Mode \n"; + } + + if (cvar("deathmatch") & DM_LMS) + { + if (dm_selected == 3) + lms = ">on | Last Man Standing\n"; + else + lms = " on | Last Man Standing\n"; + } + else + { + if (dm_selected == 3) + lms = ">ÏÆÆ | Last Man Standing\n"; + else + lms = " ÏÆÆ | Last Man Standing\n"; + } + + if (cvar("deathmatch") & DM_FFA) + { + if (dm_selected == 4) + ffa = ">on | Free For All \n"; + else + ffa = " on | Free For All \n"; + } + else + { + if (dm_selected == 4) + ffa = ">ÏÆÆ | Free For All \n"; + else + ffa = " ÏÆÆ | Free For All \n"; + } + + if (cvar("deathmatch") & DM_GIB) + { + if (dm_selected == 5) + gib = ">on | Gib \n"; + else + gib = " on | Gib \n"; + } + else + { + if (dm_selected == 5) + gib = ">ÏÆÆ | Gib \n"; + else + gib = " ÏÆÆ | Gib \n"; + } + + if (cvar("deathmatch") & DM_AUTOSWITCH) + { + if (dm_selected == 6) + autos = ">on | Weapon Autoswitch\n"; + else + autos = " on | Weapon Autoswitch\n"; + } + else + { + if (dm_selected == 6) + autos = ">ÏÆÆ | Weapon Autoswitch\n"; + else + autos = " ÏÆÆ | Weapon Autoswitch\n"; + } + + if (cvar("deathmatch") & DM_NOBOTS) + { + if (dm_selected == 7) + nobots = ">ÏÆÆ | Bots \n\n\n\nUse Weapon Cycle keys to select,\nPress Fire or Jump to toggle."; + else + nobots = " ÏÆÆ | Bots \n\n\n\nUse Weapon Cycle keys to select,\nPress Fire or Jump to toggle."; + } + else + { + if (dm_selected == 7) + nobots = ">on | Bots \n\n\n\nUse Weapon Cycle keys to select,\nPress Fire or Jump to toggle."; + else + nobots = " on | Bots \n\n\n\nUse Weapon Cycle keys to select,\nPress Fire or Jump to toggle."; + } + + menucenterprint7 (self, pred, dark, lms, ffa, gib, autos, nobots); + + MenuImpulses(); + self.impulse = 0; + +}; + + +//Null out the player +void() InitMenu = +{ +local entity other; + + setmodel (self, ""); + self.weaponmodel = ""; + setsize (self, '0 0 0', '0 0 0'); + self.velocity = '0 0 0'; + self.view_ofs = '0 0 0'; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + self.takedamage = DAMAGE_NO; + self.nextthink = -1; + self.colormap = 0; + self.effects = 0; + self.rad_time = 1; + self.radsuit_finished = 0; + self.invincible_time = 0; + self.invincible_finished = 0; + self.invisible_time = 0; + self.invisible_finished = 0; + self.super_time = 0; + self.super_damage_finished = 0; + self.attack_state = FALSE; + + other = find (world, classname, "dmmenu"); + + if (other != world) + self.classname = "player"; + else + { + self.classname = "dmmenu"; + dm_selected = 1; + } + +}; diff --git a/ParoxysmII/source/nq/progs.src b/ParoxysmII/source/nq/progs.src new file mode 100644 index 0000000..507fae2 --- /dev/null +++ b/ParoxysmII/source/nq/progs.src @@ -0,0 +1,60 @@ +//NOTE THIS CHANGE! +//in MacOS, '/' is a valid filename character and ':' is used for directory paths, +//So... I just compiled the progs.dat to the same directory as the source and moved +//it manually afterwards - this is EASY in MacOS but may not be in DOS/Win/LINUX so throw +//a '/' infront of 'progs.dat' if you feel neglected + +progs.dat + +defs.qc + + +//FrikBot +bot.qc +bot_way.qc +bot_pox.qc //Pox-specific bot routines +bot_ai.qc +bot_misc.qc +bot_phys.qc +bot_move.qc + +kascam.qc //Auto Camera +poxmenu.qc //Dm setup Menu +ccam.qc //ChaseCam + +subs.qc +//fight.qc +ai.qc +combat.qc +items.qc + +sectrig.qc //v1.1 - Separated second trigger stuff +targid.qc //v1.1 - Target Identifier + +weapons.qc + +world.qc + +flash.qc //Flashlight code (for DarkMode) + +client.qc +player.qc + +doors.qc +buttons.qc +triggers.qc +plats.qc +misc.qc + +old.qc //Old entity removal code (Monsters) + +//hiprot.qc //Hipnotic's Rotate code - removed for now (won't compile with MacQCC) + +specfx.qc //Misc. Special effects +sheilds.qc //Regen station code +wall.qc //Breakable object code +dynlight.qc //Dynamic Lighting code + + + + diff --git a/ParoxysmII/source/nq/sectrig.qc b/ParoxysmII/source/nq/sectrig.qc new file mode 100644 index 0000000..a672739 --- /dev/null +++ b/ParoxysmII/source/nq/sectrig.qc @@ -0,0 +1,944 @@ +//POX v1.1 - seperated second trigger stuff from weapons.qc to clean it up a bit. +//Re-wrote some stuff before applying the new weapons to POXworld + +//POX v1.1 moved prototypes from weapons.qc for use here +void (entity targ, entity inflictor, entity attacker, float damage) T_Damage; +void () player_run; +void(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage; +void(vector org, vector vel, float damage) SpawnBlood; +void() SuperDamageSound; +void(float shotcount, vector dir, vector spread) FireBullets2; +void(entity who,float damage) spawn_touchblood; + +void(float weapon, entity me) getmodel; // VisWeap MOD: prototype this here, so W_SetCurrentAmmo can use it. + +//void(float teem) create_bot; //add bot function +//void() bot_missile_think; //New missile think for bots (bug fix) + +.float no_obj; //fixes problem with 'hanging' mines on breakable objects destroyed by something else + +.float saw_on; //helps keep track of which BoneSaw animation to play + +float SECOND_TRIGGER = 15; // Impulse constant for second trigger (more readable than 15) +.float st_rocketload; // used to time out the rhino's trigger during reload + +.float prime_tshot; // this bit is set when the t-shot is primed for a triple barrel fire +.float st_tshotload; // used to time out the t-shot's trigger during reload + +//.float shrap_detonate; //pOx - 1.4 - moved to bot_pox.qc +.float shrap_time; //holds the bombs time out (2 minutes then boom) - just in case owner died +void(vector org) launch_shrapnel; //Predeclare + +void() player_shot1; +void() player_gshot1; +void() player_plasma1; +void() player_plasma2; +void() player_mplasma1; +void() player_nail1; +void() player_rocket1; +void() player_rocketload1; +void() player_grenade1; +void() player_reshot1; +void() player_tshot1; +void() player_shrap1; +void() player_bonesaw1; +void() player_bonesaw3; + + +//Some nitty-gritty from weapons.qc ... +float() crandom = +{ + return 2*(random() - 0.5); +}; + + +vector() wall_velocity = +{ + local vector vel; + + vel = normalize (self.velocity); + vel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5)); + vel = vel + 2*trace_plane_normal; + vel = vel * 200; + + return vel; +}; + +void() s_explode1 = [0, s_explode2] {}; +void() s_explode2 = [1, s_explode3] {}; +void() s_explode3 = [2, s_explode4] {}; +void() s_explode4 = [3, s_explode5] {}; +void() s_explode5 = [4, s_explode6] {}; +void() s_explode6 = [5, SUB_Remove] {}; + +void() BecomeExplosion = +{ + self.movetype = MOVETYPE_NONE; + self.velocity = '0 0 0'; + self.touch = SUB_Null; + setmodel (self, "progs/s_explod.spr"); + self.solid = SOLID_NOT; + s_explode1 (); +}; + + +/* +================ +Triple Barrel Shot for T-shot +Close Range Gibber - long range spread +================ +*/ +void() W_FireTShot = +{ + local vector dir; + + sound (self ,CHAN_WEAPON, "weapons/ts3fire.wav", 1, ATTN_NORM); + + self.punchangle_x = -4; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -80; + + self.currentammo = self.ammo_shells = self.ammo_shells - 3; + dir = aim (self, 100000); + //POX - 1.01b2 - increased spread, reduced amount + //POX - 1.1 - made FireBullets2 (twice the damge, half the pellets + 1 :) + FireBullets2 (12, dir, '0.16 0.1 0'); //make priming this thing worth while! +}; + + +//================================================================================= +//Start MegaPlasmaBurst - Used by PlasmaGun's Second Trigger +//================================================================================= + +void() T_MplasmaTouch = +{ + local float damg; + + if (other == self.owner) + return; // don't explode on owner + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + damg = 120 + random()*20; + + T_RadiusDamage (self, self.owner, damg, world); + + sound (self, CHAN_WEAPON, "weapons/mplasex.wav", 1, ATTN_NORM); + self.origin = self.origin - 8*normalize(self.velocity); + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + remove(self); + +}; + + +//launch_megaplasma + +void() launch_megaplasma = +{ + local vector dir; + + self.currentammo = self.ammo_cells = self.ammo_cells - 9; + + sound (self, CHAN_WEAPON, "weapons/mplasma.wav", 1, ATTN_NORM); + + self.punchangle_x = -8; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -270; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_FLYMISSILE; + newmis.solid = SOLID_BBOX; + newmis.classname = "megaplasma"; + newmis.effects = newmis.effects | EF_BRIGHTFIELD | EF_BRIGHTLIGHT; + +// set speed + dir = aim ( self, 1000 ); + newmis.velocity = dir * 0.01; + newmis.avelocity = '300 300 300'; + newmis.angles = vectoangles(newmis.velocity); + newmis.velocity = normalize(newmis.velocity); + newmis.velocity = newmis.velocity * 950; + + newmis.touch = T_MplasmaTouch; + +// set duration + newmis.think = SUB_Remove; + newmis.nextthink = time + 5; + setmodel (newmis, "progs/plasma.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin + v_forward*12 + '0 0 12'); +}; + +//End MegaPlasmaBurst +//================================================================================= + + +//============================================================================= +// +// START PumkinBall CODE - Used by SuperShotgun's Second Trigger (Impact Grenades) +// +//============================================================================= + +void() T_PballTouch = +{ + local float damg; + + if (other == self.owner) + return; // don't explode on owner + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + damg = 100 + random()*20; + + T_RadiusDamage (self, self.owner, damg, world); + +// sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM); + self.origin = self.origin - 8*normalize(self.velocity); + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + + BecomeExplosion (); +}; + +/* +================ +W_FirePball +================ +*/ +void() W_FirePball = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; + + sound (self, CHAN_AUTO, "weapons/gren2.wav", 1, ATTN_NORM); + + self.punchangle_x = -4; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -150; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_TOSS; + newmis.solid = SOLID_BBOX; + newmis.classname = "impactgrenade"; + +// set newmis speed + + makevectors (self.v_angle); + + newmis.velocity = v_forward*700 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = T_PballTouch; + +// set newmis duration + newmis.nextthink = time + 2.5; + + setmodel (newmis, "progs/grenade.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin + v_forward*4); +}; + +// END PumkinBall CODE +//============================================================================= + + + +//============================================================================= +// +// START MINE CODE (based on hipnotic's proximity mine - uh... hacked to death) +// Used for the Grenade Launcher's Second Trigger +// This is some laughable, sick code +// But it works. +// +//============================================================================= + +void() M_DamExplode = +{ + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + T_RadiusDamage (self, self.owner, 95, world); + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + + remove (self); +}; + +/* +================ +MineExplode +================ +*/ + +//explode imediately! (for doors, plats and breakable objects +void() MineImExplode = +{ + self.takedamage = DAMAGE_NO; + self.deathtype = "exploding"; + self.owner = self.lastowner; + M_DamExplode(); +}; + +void() MineExplode = +{ + self.takedamage = DAMAGE_NO; + self.deathtype = "exploding"; + self.nextthink = time + random()*0.15; //gives a more organic explosion when multiple mines explode at once + self.owner = self.lastowner; + self.think = M_DamExplode; +}; + +/* +================ +MineTouch +================ +*/ +void() MineTouch = + { + + if (other == self) + { + return; + } + + if (other.solid == SOLID_TRIGGER) + { + sound (self, CHAN_AUTO, "weapons/bounce.wav", 1, ATTN_NORM); + return; + } + + if (other.classname == "grenade") + { + sound (self, CHAN_AUTO, "weapons/bounce2.wav", 1, ATTN_NORM); + self.nextthink = time + 1; + return; + } + + if (other.classname == "mine") + { + sound (self, CHAN_AUTO, "weapons/bounce2.wav", 1, ATTN_NORM); + self.nextthink = time + 1; + return; + } + + if (other.classname == "minearm") + { + sound (self, CHAN_AUTO, "weapons/bounce2.wav", 1, ATTN_NORM); + self.nextthink = time + 1; + return; + } + + if (other.classname == "minearmed") + { + sound (self, CHAN_AUTO, "weapons/bounce.wav", 1, ATTN_NORM); + self.classname = "minearm"; + self.nextthink = time + 1; + return; + } + + if (other.classname == "player" || other.classname == "bot") + { + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + MineExplode(); + self.nextthink = time + 0.4; + return; + } + + if (other.takedamage == DAMAGE_AIM) + { + MineExplode(); + self.think(); + return; + } + + self.movetype = MOVETYPE_NONE; + self.classname = "minearm"; + self.spawnmaster = other; + self.nextthink = time + 0.1; +}; + +/* +================ +MineArm +================ +*/ +void() MineArm = + { + local entity head; + local float detonate; + + if (self.classname == "minearm") + { + sound (self, CHAN_WEAPON, "weapons/armed.wav", 1, ATTN_NORM); + setsize (self, '-3 -3 -3', '3 3 3'); + self.owner = world; + self.takedamage = DAMAGE_YES; + self.skin = 1; + self.classname = "minearmed"; + self.effects = self.effects | EF_MUZZLEFLASH; + } + + if ((time > self.delay) || (self.spawnmaster.no_obj == TRUE)) + { + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + MineImExplode(); + //self.nextthink = time + 0.4; + return; + } + + // No click or delay when velocity triggers mine (so they don't float when doors open) + // Although the 'organic' explosion' part in the detonate code might leave it hanging... + if (vlen(self.spawnmaster.velocity) > 0) + { + MineImExplode(); + return; + } + + // Mines explode on touch, but for some reason most monsters don't detonate them (?) + // So had to use a findradius function to look for monsters, it's a small radius though + // Ogres still don't always detonate mines (?) + head = findradius(self.origin, 39); + detonate = 0; + + if (self.health < 0) + detonate = 1; + + while (head) + { + if ((head != self) && (head.health > 0) && ((head.flags & (FL_CLIENT|FL_MONSTER)) || (head.classname == "bot")) && (head.classname!=self.classname)) + detonate = 1; + + traceline(self.origin,head.origin,TRUE,self); + if (trace_fraction != 1.0) + detonate = 0; + + if (detonate==1) + { + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + MineExplode(); + self.nextthink = time + 0.25; //POX v1.11 reduced getaway time + return; + } + head = head.chain; + } + + self.nextthink = time + 0.1; +}; +/* +================ +W_FireMine +================ +*/ +void() W_FireMine = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; + + sound (self, CHAN_AUTO, "weapons/gren.wav", 1, ATTN_NORM); + + self.punchangle_x = -2; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -100; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.lastowner = self; + newmis.movetype = MOVETYPE_TOSS; + newmis.solid = SOLID_BBOX; + newmis.classname = "mine"; + newmis.takedamage = DAMAGE_NO; + newmis.health = 1; + + //POX v1.2 - mines don't bleed.... + newmis.nobleed = TRUE; + +// set missile speed + + makevectors (self.v_angle); + + if (self.v_angle_x) + newmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + else + { + newmis.velocity = aim(self, 10000); + newmis.velocity = newmis.velocity * 600; + newmis.velocity_z = 200; + } + + newmis.avelocity = '100 600 100'; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = MineTouch; + +// set missile duration + newmis.nextthink = time + 0.2; + newmis.delay = time + 15; + newmis.think = MineArm; + newmis.th_die = MineExplode; + + setmodel (newmis, "progs/grenade.mdl"); + setorigin (newmis, self.origin + v_forward*4); + setsize (newmis, '-1 -1 -1', '0 0 0'); + +}; + +// END MINE CODE +//============================================================================= + + +//============================================================================= +// +// Shrapnel Bomb - Nailgun Second Trigger (1 rocket + 30 nails, remotely detonated) +// +//============================================================================= + +//---------------------------------------------------------- +//These functions launch a single spike in a random direction + +void() spikenal_touch = +{ + //if (other.solid == SOLID_TRIGGER) + // return; // trigger field, do nothing + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + +// hit something that bleeds + if (other.takedamage) + { + spawn_touchblood (other, 9); + + + T_Damage (other, self, self.owner, 12); + + remove(self); + } + + else if (random() > 0.9) + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_SPIKE); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + remove(self); + } + +}; + +//POX - Get a random vector for Shrapnel +vector() VelocityForShrapnel = +{ + local vector v; + + v_x = 200 * crandom(); + v_y = 200 * crandom(); + v_z = 200 * crandom(); + + if (random() > 0.5) + v_z = v_z - (v_z*2); + + v = v * 6; + + return v; +}; + +//POX - Shrapnel = fast, bouncy spikes with a short life +void(vector org) launch_shrapnel = +{ + newmis = spawn (); + + newmis.owner = self.owner; + newmis.voided = 0; + newmis.movetype = MOVETYPE_BOUNCE; + newmis.solid = SOLID_BBOX; + + newmis.touch = spikenal_touch; + newmis.classname = "spikenal"; + + newmis.velocity = VelocityForShrapnel(); + newmis.avelocity_x = random()*800; + newmis.avelocity_y = random()*800; + newmis.avelocity_z = random()*800; + + newmis.think = SUB_Remove; + newmis.nextthink = time + 3; + + setmodel (newmis, "progs/mwrub1.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + setorigin (newmis, org); + +}; + +//---------------------------------------------------------- +//and now the bomb code... + +void() ShrapnelExplode = +{ + local float n; + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + + +//POX - 1.0b1 - A LOT less Shrapnel! + + launch_shrapnel(self.origin); + launch_shrapnel(self.origin); + launch_shrapnel(self.origin); + launch_shrapnel(self.origin); + launch_shrapnel(self.origin); + + T_RadiusDamage (self, self.owner, 160, world); + + if (self.owner != world) + self.owner.shrap_detonate = FALSE;//Enable next launch + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + + BecomeExplosion (); +}; + +void() ShrapnelDetonate = +{ + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + self.think = ShrapnelExplode; + self.nextthink = time + 0.1; +}; + +//Wait for a detonation impulse or time up +void() ShrapnelThink = +{ + if (self.shrap_time < time) + ShrapnelDetonate(); + + if (self.owner == world) + return; + + //Owner died so change to world and wait for detonate + if (self.owner.health <= 0) + { + self.owner.shrap_detonate = FALSE;//Enable next launch + self.owner = world; + self.nextthink = self.shrap_time; + self.think = ShrapnelDetonate; + return; + } + + if (self.owner.shrap_detonate == 2) + ShrapnelDetonate(); + else + self.nextthink = time + 0.1; +}; + +void() ShrapnelTouch = +{ + local float r; + + r = random(); + + if (other == self.owner) + return; // don't explode on owner + + if (other.takedamage == DAMAGE_AIM) + { + ShrapnelDetonate(); + return; + } + + //pick a bounce sound + if (r < 0.75) + sound (self, CHAN_VOICE, "weapons/bounce.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "weapons/bounce2.wav", 1, ATTN_NORM); + + if (self.velocity == '0 0 0') + self.avelocity = '0 0 0'; +}; + +// End shrapnel bomb +//============================================================================= + + +/* +================ +W_FireShrapnel +================ +*/ +void() W_FireShrapnel = +{ + self.ammo_rockets = self.ammo_rockets - 1; + self.currentammo = self.ammo_nails = self.ammo_nails - 30; + + sound (self, CHAN_WEAPON, "weapons/gren2.wav", 1, ATTN_NORM); + + self.punchangle_x = -7; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -115; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_BOUNCE; + newmis.solid = SOLID_BBOX; + newmis.classname = "shrapnel"; + + newmis.shrap_time = time + 120; + +// set newmis speed + makevectors (self.v_angle); + + if (self.v_angle_x) + newmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + else + { + newmis.velocity = aim(self, 10000); + newmis.velocity = newmis.velocity * 600; + newmis.velocity_z = 200; + } + + newmis.avelocity = '300 300 300'; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = ShrapnelTouch; + +// set newmis duration + newmis.nextthink = time + 0.1; + newmis.think = ShrapnelThink; + + setmodel (newmis, "progs/grenade.mdl"); + newmis.skin = 2; + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin); +}; + +/* +============ +W_SecondTrigger + +Second Trigger Impulses + +POX v1.1 - seperated this from weapons.qc - cleaned it up a bit +============ +*/ +void() W_SecondTrigger = +{ + + // check for NailGun's second Trigger + if (self.weapon == IT_SUPER_NAILGUN) { + if (!self.shrap_detonate) { // Check if a bomb is already set + // check for nails and rockets + if ((self.ammo_nails < 30) || (self.ammo_rockets < 1)) { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + sprint (self, "Not enough ammo...\n"); + } else if (self.st_shrapnel < time) { + self.weaponframe = 0; + SuperDamageSound(); + self.st_shrapnel = time + 0.1; // Allow a fast detonate + player_shrap1(); + W_FireShrapnel(); + + self.shrap_detonate = TRUE; + } else { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + } + } else { + sound (self, CHAN_AUTO, "weapons/shrapdet.wav", 1, ATTN_NORM); + SuperDamageSound(); + self.st_shrapnel = time + 0.7; // Time out before next launch + self.shrap_detonate = 2; //Tell the bomb to blow! + } + } + + // check for t-shot prime + if (self.weapon == IT_TSHOT) { + + if (self.prime_tshot == TRUE) { // already primed + SUB_Null(); + } else if (self.ammo_shells < 3) { // not enough ammo + SUB_Null(); + } else { + self.st_tshotload = time + 0.9; //give the reload a chance to happen + + //make a reload sound! + sound (self, CHAN_WEAPON, "weapons/tsload.wav", 1, ATTN_NORM); + + player_reshot1(); //play prime animation + + self.prime_tshot = TRUE; //set the prime bit + } + } + + if (self.weapon == IT_ROCKET_LAUNCHER) { // check for rhino reload + if (self.reload_rocket == 0) { // already reloaded + SUB_Null; + } else if (self.ammo_rockets < 1) { // if no rockets go away + SUB_Null; + } else { + self.st_rocketload = time + 0.6; //give the reload a chance to happen + + sound (self, CHAN_WEAPON, "weapons/rhinore.wav", 1, ATTN_NORM); + + player_rocketload1(); //play reload animation + + self.reload_rocket = 0; //reset reload bit + } + } + + +// check for Plasmagun second Trigger + if (self.weapon == IT_PLASMAGUN) + { + // check for cells + if (self.ammo_cells < 9) + { + sound (self, CHAN_AUTO, "weapons/mfire2.wav", 1, ATTN_NORM); + } + + else + + if (self.st_mplasma < time) { + if (self.waterlevel > 1) { //explode under water + sound (self, CHAN_WEAPON, "weapons/mplasex.wav", 1, ATTN_NORM); + self.ammo_cells = 0; + W_SetCurrentAmmo (); + T_RadiusDamage (self, self, 250, world); + return; + } + + self.weaponframe = 0; + SuperDamageSound (); + self.st_mplasma = time + 1.9; + player_mplasma1 (); + launch_megaplasma (); + + } else { + SUB_Null; + } + } + +// check for Super Shotgun second Trigger + if (self.weapon == IT_COMBOGUN) + { + + // check for rockets + if (self.ammo_rockets < 1) + { + self.items = self.items - ( self.items & (IT_SHELLS) ); + self.items = self.items | IT_ROCKETS; + self.currentammo = self.ammo_rockets; + self.which_ammo = 1; + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + } + + else + + if (self.st_pball < time) + { + self.items = self.items - ( self.items & (IT_SHELLS) ); + self.items = self.items | IT_ROCKETS; + self.currentammo = self.ammo_rockets; + self.which_ammo = 1; + player_gshot1(); + SuperDamageSound(); + W_FirePball(); + self.st_pball = time + 0.9; + } + else + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + } + +// check for GrenadeLauncher second Trigger + if (self.weapon == IT_GRENADE_LAUNCHER) + { + // check for rockets + if (self.ammo_rockets < 1) + { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + } + + else + if ((self.st_mine < time) && (self.st_grenade < time))//pOx 1.4 - combine timeouts for mines & grenades + { + player_grenade1(); + W_FireMine(); + + //big delay between refires helps keep the # of mines down in a deathmatch game + self.st_mine = time + 1.25; + + } + else + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + } + + self.impulse = 0; + +}; diff --git a/ParoxysmII/source/nq/specfx.qc b/ParoxysmII/source/nq/specfx.qc new file mode 100644 index 0000000..5fea977 --- /dev/null +++ b/ParoxysmII/source/nq/specfx.qc @@ -0,0 +1,591 @@ +/* Frank Condello 09.28.98 - made for the Paroxysm MacQuake Mod. +EMAIL: pox@planetquake.com +WEB: http://www.planetquake.com/paroxysm +========================================================================= + +Custom Paroxysm visual and audio effects +These are simple little ambient effects that are customizable within Quiver. + +Feel free to use this code in anyway. + +========================================================================= + +Start +========================================================================= +Spark code (called 'e_spark' to seperate from plasma-gun's spark) +The .float spark_freq controls the delay between sparks in seconds +note: spark_freq is randomized slightly to give a better effect. +========================================================================= +*/ +.float spark_freq; + +void() spawn_espark; + +void() e_spark = +{ + precache_sound ("misc/spark1.wav"); + precache_sound ("misc/spark2.wav"); + precache_sound ("misc/spark3.wav"); + precache_sound ("misc/spark4.wav"); + + if (self.spark_freq < 0) + self.spark_freq = 0; // default frequency to 0 + + // not triggered so spark it up + if (!self.targetname) + { + self.nextthink = time + random(); + self.think = spawn_espark; + } + + // wait for trigger + self.use = spawn_espark; + +}; + +void() espark_touch = +{ + self.solid = SOLID_NOT; + + if (random() < 0.1) //remove some sparks on touch + remove(self); +}; + +void() spawn_espark = +{ + local entity espark; + local float r; + + r = rint(random() * 3); //play a random spark sound + if (r == 1) + sound (self, CHAN_VOICE, "misc/spark1.wav", 1, ATTN_STATIC); + else if (r == 2) + sound (self, CHAN_VOICE, "misc/spark2.wav", 1, ATTN_STATIC); + else if (r == 0) + sound (self, CHAN_VOICE, "misc/spark3.wav", 1, ATTN_STATIC); + else + sound (self, CHAN_VOICE, "misc/spark4.wav", 1, ATTN_STATIC); + + espark = spawn (); + setorigin (espark, self.origin); + espark.movetype = MOVETYPE_BOUNCE; + espark.solid = SOLID_TRIGGER; + espark.classname = "spark"; + +//set the spark velocity - currently sparks emit 360¼ around the origin in the horizontal axis + espark.velocity_x = (random() * 200) - 25; + espark.velocity_y = (random() * 200) - 25; + espark.velocity_z = 0; + + espark.touch = espark_touch; + espark.nextthink = time + random()*3; //random lifespan for sparks that aren't removed on touch + espark.think = SUB_Remove; + setmodel (espark, "progs/spark.spr"); + setsize (espark, '0 0 0', '0 0 0'); + +//user defined frequency is randomized give a more organic feel + self.nextthink = time + (random() + self.spark_freq); + self.think = spawn_espark;// spawn next spark + +}; + + +/* +========================= +end Spark code +========================= + + +============================================= +Start Drip code; a_drip is the spawn function. + +drip_freq controls the delay between drips in +seconds. + +drip_snd controls the type of sound produced. +1 = no sound - use this to silence drips in a rain group +2 = random drip sound on touch +============================================= +*/ + +.float drip_snd; +.float drip_freq; + +void() go_drip; + +// define the splash animation +void() s_splash1 = [0, s_splash2] {}; +void() s_splash2 = [1, s_splash3] {}; +void() s_splash3 = [2, s_splash4] {}; +void() s_splash4 = [3, s_splash5] {}; +void() s_splash5 = [4, s_splash6] {}; +void() s_splash6 = [5, SUB_Remove] {}; + +void() a_drip = +{ + precache_model ("progs/drip.spr"); + precache_model ("progs/splash.spr"); + precache_sound ("misc/drip1.wav"); + precache_sound ("misc/drip2.wav"); + precache_sound ("misc/drip3.wav"); + + if (self.drip_freq <= 0) //default frequency to 3 seconds + self.drip_freq = 3; + + if (!self.drip_snd) + self.drip_snd = 1; //default to no sound + + // not triggered so go drip + if (!self.targetname) + { + self.nextthink = time + random(); + self.think = go_drip; + } + + // wait for trigger + self.use = go_drip; + +}; + +void() drip_touch = +{ + local float r; + + r = rint(random() * 2); + + if (self.drip_snd == 2) // play a random drip sound + { + if (r == 1) + sound (self, CHAN_VOICE, "misc/drip1.wav", 1, ATTN_IDLE); + else if (r == 2) + sound (self, CHAN_VOICE, "misc/drip2.wav", 1, ATTN_IDLE); + else if (r == 0) + sound (self, CHAN_VOICE, "misc/drip3.wav", 1, ATTN_IDLE); + } + + self.solid = SOLID_NOT; + setmodel (self, "progs/splash.spr"); + + //added this particle effect cause GL quake tends to blur the slpash.spr out of existance. + particle (self.origin, '0 0 0.25', 0, 5); + + s_splash1(); +}; + +void() go_drip = +{ + +local entity drip; + + drip = spawn (); + + drip.solid = SOLID_TRIGGER; + drip.movetype = MOVETYPE_TOSS; + drip.drip_snd = self.drip_snd; + + //let gravity control the drip + drip.velocity_x = 0; + drip.velocity_y = 0; + drip.velocity_z = 0; + drip.classname = "drip"; + setmodel (drip, "progs/drip.spr"); + setsize (drip, '-1 -1 -1', '1 1 1'); + setorigin (drip, self.origin); + + drip.nextthink = time + 5; + drip.think = SUB_Remove; + drip.touch = drip_touch; + + drip.classname = "drip"; //added for completeness and possible future use of 'acid' drips + + self.nextthink = time + (random() + self.drip_freq); + self.think = go_drip; + +}; + +/* +========================= +end new Drip code +========================= +*/ + +/* +================================================================================================= +Custom Sound Entity (cust_sound) +The lack of a Mac sound editor that produces looping .wav files in a format that Quake understands +prompted the creation of this function. Then I added some options to make it worth while. + +snd_attn - sets the sound's attenuation (defaults to ATTN_NORM (1)) + +snd_volume - sets the sounds overall volume (typically values of 0.5 to 1) default is 1 + +strt_onoff - 0 = initially on, 1 = initially off + +snd_rep - number of times to play the sound (-2 is infinite loop) + +snd_loop - delay in seconds before playing sound again (use sound length for looping sounds) + +snd_rand - random seed, this number is multiplied by random() and added to loop (default 0 is no random) + +the_snd - directory path to the sound - example: "misc/mysound.wav" NOTE: "sound/" is NOT needed + +targetname - sounds can be triggered by using a targetname. + +TOGGLE_SND - spawn flag, determines if sounds can be toggled on and off. + +BUGS +1. Quake won't start playing these sounds on the first frame - (Unless you precache every possible sound +at world_spawn) + +HACKY FIX - If you need a sound to play right away; +Position a trigger in such a way that it is touched when the player enters the map - NOT touching the +player initially (doesn't work on frame 1 remember?). Try making a trigger 4 units tall and place +info_player_start just above it. Not pretty, but it does the trick - Don't forget to set +strt_onoff to 1 (initially off) for this to work + +2. When Quake no longer 'hears' or more arcurately - 'sees' the sound, it kills it until it comes into view +again. This causes the sound to stop playing and remsume at the NEXT loop point when it is in view, so for +sounds that must be looped constantly, I would reccomend using the ambient_sound function (see below) +================================================================================================= +*/ + +//NOTE: some of these .floats are used in ambient_sound as well (see below) +float TOGGLE_SND = 1; +.float snd_attn; +.float snd_volume; +.float strt_onoff; +.float snd_rep; +.float snd_rand; +.float snd_loop; +.float snd_strt; +.string the_snd; + +void() sound_think; +void() play_sound; + +void() sound_wait = +{ + // hang around until next use + self.nextthink = time + 60*10; + self.think = sound_wait; +}; + +void() stop_sound = +{ + // if sound is set to toggle, silence it and set use to play + if (self.spawnflags & TOGGLE_SND) + { + sound (self, CHAN_VOICE, "misc/null.wav", 0, self.snd_attn); + self.use = play_sound; + sound_wait(); + } + + // sound doesn't toggle so kill it + else + { + sound (self, CHAN_VOICE, "misc/null.wav", 0, self.snd_attn); + remove (self); + } +}; + +void() play_sound = +{ + //infinite repeat + if (self.snd_rep == -2) + { + sound (self, CHAN_VOICE, self.the_snd, self.snd_volume, self.snd_attn); + sound_think(); + } + + // sound is done + else if (self.snd_rep == 0) + remove (self); + + // play the sound and reduce repititions by 1 + if (self.snd_rep >= 1) + { + sound (self, CHAN_VOICE, self.the_snd, self.snd_volume, self.snd_attn); + self.snd_rep = self.snd_rep - 1; + sound_think(); + } + +}; + +void() sound_think = +{ + // if sound is toggled, set next use to stop + if (self.spawnflags & TOGGLE_SND) + self.use = stop_sound; + + // determine user-defined loop time then play the sound + self.nextthink = time + (random()*self.snd_rand) + self.snd_loop; + self.think = play_sound; +}; + +void() cust_sound = +{ + precache_sound (self.the_snd); + precache_sound ("misc/null.wav"); + + /* + POX v1.2 - don't change this (since NONE is valid) + if(!self.snd_attn) + self.snd_attn = 2; + + self.snd_attn = self.snd_attn - 1;// needed so the default to NORM works (since 0 = NONE) + */ + + //default volume to one if not specified + if(self.snd_volume <= 0) + self.snd_volume = 1; + + // start sound if not triggered + if (!self.targetname) + play_sound(); + + // start sound if initially on, set use to stop_sound + if (self.strt_onoff == 0) + { + self.use = stop_sound; + play_sound(); + } + + // start sound when triggered + if (self.strt_onoff == 1) + { + self.use = play_sound; + } + +}; + + +//Made this so custom ambient sounds could be called with a single function +//ONLY WORKS WITH LOOPED .WAV FILES +//Added a targetname attribute so ambient sounds can be triggered. +//NOTE: These sounds can not be stopped once started +//To my knowledge you can't make these with existing Mac sound editors +//(you can make looping wavs, just not ones Quake will loop) +//CoolEdit 1.5 (for Windows) is the only editor that can create these looping wavs (to my knowledge) + +void() ambientsound_go = +{ + ambientsound (self.origin, self.the_snd, self.snd_volume, self.snd_attn); +}; + +void() ambient_sound = +{ + precache_sound (self.the_snd); + + //default volume to one if not specified + if(self.snd_volume <= 0) + self.snd_volume = 1; + /* + POX v1.11 - don't change this (since NONE is valid) + //default attenuation to NORM if not specified + if(!self.snd_attn) + self.snd_attn = 2; + + self.snd_attn = self.snd_attn - 1;// needed so the default to NORM works (since 0 = NONE) + */ + + //start right away if not triggered + if(!self.targetname) + { + //Needs to start on the second frame since that's when entities not called in world_spawn are created + self.nextthink = time + 0.1; + self.think = ambientsound_go; + } + + //wait for trigger + else + self.use = ambientsound_go; +}; + + +/* +colour_light is a small hack to try and simulate coloured lighting. +It does this by calling the built-in v_cshift function +This can be used for 'atmospheric' effects like outdoor haze and darkness. +This function is overriden by Quake when under liquids and by the background flash when an item is picked up +The GL Quake command 'gl_polyblend 0' also negates this entity. + +colourvalue - this is the only parameter, must be in this format; +v_cshift R G B I\n + +where R is Red (0 - 255), G is Green (0 - 255), B is Rlue (0 - 255), and I is Intensity (0 - 255) + +This effect works poorly in small increments in GL Quake, no colour will be noticeable until about an +intesity of about 5 (depending on the colour) and some colour/intensity combinations result in ugly +banding. It's best to test colour levels in the console before applying them in your map. + +These entities are activated by touch (the colour shift only occurs when the player is touching the trigger field) +But can also be enabled/disabled by using a targetname. + + +The string must be exactly as shown (including the v_cshift and the carraige return +at the end) I could have used multiple stuffcmd statements to make it more user friendly but.. eh. +*/ + +void() colourlight_wait; +.string colourvalue; + +void() colourlight_off = +{ + if(other.classname != "player") + return; + + //turn it off and reset + stuffcmd (other, "v_cshift 0 0 0 0\n"); + self.use = colourlight_wait; + self.touch = SUB_Null; + +}; + +void() colourlight_toggle = +{ + //called after light is triggered a second time + self.touch = colourlight_off; +}; + +void() colourlight_use = +{ + if(other.classname != "player") + return; + + + //POX v1.2 - better clearing of v_cshift (in PostThink) + if(other.cshift_finished > time) + return; + + stuffcmd (other, self.colourvalue); + + + //POX v1.2 - better clearing of v_cshift (in PostThink) + other.cshift_off = FALSE; + other.cshift_finished = time + 0.1; //check every frame + + + //if targetted, alow it to be shut down + if(self.targetname) + self.use = colourlight_toggle; + +}; + +void() colourlight_wait = +{ + //activated by a trigger so wait for touch + self.touch = colourlight_use; + +}; + +void() colour_light = +{ + InitTrigger (); + + //Can be made active by use of a targetname + //Must be triggered by touch + + if((!self.targetname) || (!self.spawnflags & START_OFF)) + self.touch = colourlight_use; + else + { + self.use = colourlight_wait; + self.touch = SUB_Null; + } + +}; + +/* +particle_stream + +Changed for Paroxysm v1.11's new regen stations +Creates a regen stream-pulse thingy model with out a grate and no touch + +This entity should be used in conjunction with a regen_station trigger + +clr = skin# (0 = blue, 1 = yellow, 2 = red) + +NOTE: Origin is the BOTTOM of the stream (so use the middle of the bounding box in a map editor) +*/ + +.float clr; +void() regen_ambientsound; + +void() particle_stream = +{ + precache_sound("ambience/regen1.wav"); + precache_model ("progs/stream.mdl"); + + self.angles = '0 0 0'; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NONE; + setmodel(self, "progs/stream.mdl"); + + //POX v1.2 - just in case + if(self.clr > 2 || self.clr < 0) + self.clr = 0; + + self.skin = self.clr; + + //POX v1.2 Fixed sound orign (makestatic messed it up?) + regen_ambientsound (); + makestatic (self); + +}; + +/* +POX v1.2 +misc_explobox + +BSP based explo_box'es +Try to use rectangular objects, since entites use bounding box collision detection +*/ + +void() bsp_explode = +{ + self.takedamage = DAMAGE_NO; + self.trigger_field.classname = "explo_box"; + + // did say self.owner + T_RadiusDamage (self.trigger_field, self.trigger_field, self.dmg, world); + sound (self.trigger_field, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM); + particle (self.trigger_field.origin, '0 0 0', 75, 255); + + remove (self); + remove (self.trigger_field); + +}; + +void() misc_explobsp = +{ + local entity spot; + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + + setmodel (self, self.model); + setsize( self, self.mins, self.maxs ); + + precache_sound ("weapons/r_exp3.wav"); + + if (!self.health) + self.health = 20; + + if (!self.dmg) + self.dmg = 160; + + self.th_die = bsp_explode; + self.takedamage = DAMAGE_AIM; + self.nobleed = TRUE; + + //POX 1.2 - HACK! + //put a null entity at the center of the model to hold the explosion position + spot = spawn(); + setmodel (spot, string_null); + spot.origin_x = self.absmin_x + (self.size_x * 0.5); + spot.origin_y = self.absmin_y + (self.size_y * 0.5); + spot.origin_z = self.absmin_z + (self.size_z * 0.5); + setsize (spot, '0 0 0', '0 0 0'); + self.trigger_field = spot; +}; + diff --git a/ParoxysmII/source/nq/subs.qc b/ParoxysmII/source/nq/subs.qc new file mode 100644 index 0000000..29fa950 --- /dev/null +++ b/ParoxysmII/source/nq/subs.qc @@ -0,0 +1,288 @@ + + +void() SUB_Null = {}; + +void() SUB_Remove = {remove(self);}; + + +/* +QuakeEd only writes a single float for angles (bad idea), so up and down are +just constant angles. +*/ +vector() SetMovedir = +{ + if (self.angles == '0 -1 0') + self.movedir = '0 0 1'; + else if (self.angles == '0 -2 0') + self.movedir = '0 0 -1'; + else + { + makevectors (self.angles); + self.movedir = v_forward; + } + + self.angles = '0 0 0'; +}; + +/* +================ +InitTrigger +================ +*/ +void() InitTrigger = +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + if (self.angles != '0 0 0') + SetMovedir (); + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); // set size and link into world + self.movetype = MOVETYPE_NONE; + self.modelindex = 0; + self.model = ""; +}; + +/* +============= +SUB_CalcMove + +calculate self.velocity and self.nextthink to reach dest from +self.origin traveling at speed +=============== +*/ +void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt = +{ +local entity stemp; + stemp = self; + self = ent; + + SUB_CalcMove (tdest, tspeed, func); + self = stemp; +}; + +void(vector tdest, float tspeed, void() func) SUB_CalcMove = +{ +local vector vdestdelta; +local float len, traveltime; + + if (!tspeed) + objerror("No speed is defined!"); + + self.think1 = func; + self.finaldest = tdest; + self.think = SUB_CalcMoveDone; + + if (tdest == self.origin) + { + self.velocity = '0 0 0'; + self.nextthink = self.ltime + 0.1; + return; + } + +// set destdelta to the vector needed to move + vdestdelta = tdest - self.origin; + +// calculate length of vector + len = vlen (vdestdelta); + +// divide by speed to get time to reach dest + traveltime = len / tspeed; + + if (traveltime < 0.1) + { + self.velocity = '0 0 0'; + self.nextthink = self.ltime + 0.1; + return; + } + +// set nextthink to trigger a think when dest is reached + self.nextthink = self.ltime + traveltime; + +// scale the destdelta vector by the time spent traveling to get velocity + self.velocity = vdestdelta * (1/traveltime); // qcc won't take vec/float +}; + +/* +============ +After moving, set origin to exact final destination +============ +*/ +void() SUB_CalcMoveDone = +{ + setorigin(self, self.finaldest); + self.velocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1(); +}; + + +/* +============= +SUB_CalcAngleMove + +calculate self.avelocity and self.nextthink to reach destangle from +self.angles rotating + +The calling function should make sure self.think is valid +=============== +*/ +void(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt = +{ +local entity stemp; + stemp = self; + self = ent; + SUB_CalcAngleMove (destangle, tspeed, func); + self = stemp; +}; + +void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove = +{ +local vector destdelta; +local float len, traveltime; + + if (!tspeed) + objerror("No speed is defined!"); + +// set destdelta to the vector needed to move + destdelta = destangle - self.angles; + +// calculate length of vector + len = vlen (destdelta); + +// divide by speed to get time to reach dest + traveltime = len / tspeed; + +// set nextthink to trigger a think when dest is reached + self.nextthink = self.ltime + traveltime; + +// scale the destdelta vector by the time spent traveling to get velocity + self.avelocity = destdelta * (1 / traveltime); + + self.think1 = func; + self.finalangle = destangle; + self.think = SUB_CalcAngleMoveDone; +}; + +/* +============ +After rotating, set angle to exact final angle +============ +*/ +void() SUB_CalcAngleMoveDone = +{ + self.angles = self.finalangle; + self.avelocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1(); +}; + + +//============================================================================= + +void() DelayThink = +{ + activator = self.enemy; + SUB_UseTargets (); + remove(self); +}; + +/* +============================== +SUB_UseTargets + +the global "activator" should be set to the entity that initiated the firing. + +If self.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. + +Centerprints any self.message to the activator. + +Removes all entities with a targetname that match self.killtarget, +and removes them, so some events can remove other triggers. + +Search for (string)targetname in all entities that +match (string)self.target and call their .use function + +============================== +*/ +void() SUB_UseTargets = +{ + local entity t, stemp, otemp, act; + +// +// check for a delay +// + if (self.delay) + { + // create a temp object to fire at a later time + t = spawn(); + t.classname = "DelayedUse"; + t.nextthink = time + self.delay; + t.think = DelayThink; + t.enemy = activator; + t.message = self.message; + t.killtarget = self.killtarget; + t.target = self.target; + return; + } + + +// +// print the message +// + if (activator.classname == "player" && self.message != "") + { + centerprint (activator, self.message); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprintsw + if (!self.noise) + sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); + } + +// +// kill the killtagets +// + if (self.killtarget) + { + t = world; + do + { + t = find (t, targetname, self.killtarget); + if (!t) + return; + remove (t); + } while ( 1 ); + } + +// +// fire targets +// + if (self.target) + { + act = activator; + t = world; + do + { + t = find (t, targetname, self.target); + if (!t) + { + return; + } + stemp = self; + otemp = other; + self = t; + other = stemp; + if (self.use != SUB_Null) + { + if (self.use) + self.use (); + } + self = stemp; + other = otemp; + activator = act; + } while ( 1 ); + } + + +}; diff --git a/ParoxysmII/source/nq/targid.qc b/ParoxysmII/source/nq/targid.qc new file mode 100644 index 0000000..8096f4e --- /dev/null +++ b/ParoxysmII/source/nq/targid.qc @@ -0,0 +1,46 @@ +// POX - v1.1 target identifier ala Quake3 - displays the name of players who cross your sight +// by Frank Condello (POX) - http://www.planetquake.com/paroxysm/ - pox@planetquake.com +// requires visible float return from ai.qc + +// Short and sweet.... +void() ID_CheckTarget = +{ + local vector org; + local entity spot; + + //Lost target, or target died + if (self.target_id_same < time || self.last_target_id.health < 1 || !visible(self.last_target_id)) + self.last_target_id = world; + + traceline (self.origin , (self.origin+(v_forward * 800)) , FALSE , self); + + org = trace_endpos; + + spot = findradius(org, 200); + + while (spot) + { + if ((spot.classname == "player") && spot.takedamage) + { + //Same target as last time + if (self.target_id_same > time && spot == self.last_target_id) + { + self.target_id_finished = time + 1.3; + self.target_id_same = time + 3; + return; + } + else if (spot != self && visible (spot) )//Found new Target + { + self.last_target_id = spot; + self.target_id_finished = time + 1.5; + self.target_id_same = time + 3; + centerprint (self, self.last_target_id.netname); + return; + } + } + + spot = spot.chain; + + } + +}; diff --git a/ParoxysmII/source/nq/triggers.qc b/ParoxysmII/source/nq/triggers.qc new file mode 100644 index 0000000..22daefe --- /dev/null +++ b/ParoxysmII/source/nq/triggers.qc @@ -0,0 +1,707 @@ + +entity stemp, otemp, s, old; + + +void() trigger_reactivate = +{ + self.solid = SOLID_TRIGGER; +}; + +//============================================================================= + +float SPAWNFLAG_NOMESSAGE = 1; +float SPAWNFLAG_NOTOUCH = 1; + +// the wait time has passed, so set back up for another activation +void() multi_wait = +{ + if (self.max_health) + { + self.health = self.max_health; + self.takedamage = DAMAGE_YES; + self.solid = SOLID_BBOX; + } +}; + + +// the trigger was just touched/killed/used +// self.enemy should be set to the activator so it can be held through a delay +// so wait for the delay time before firing +void() multi_trigger = +{ + if (self.nextthink > time) + { + return; // allready been triggered + } + + if (self.classname == "trigger_secret") + { + if (self.enemy.classname != "player") + return; + found_secrets = found_secrets + 1; + WriteByte (MSG_ALL, SVC_FOUNDSECRET); + } + + if (self.noise) + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + +// don't trigger again until reset + self.takedamage = DAMAGE_NO; + + activator = self.enemy; + + SUB_UseTargets(); + + if (self.wait > 0) + { + self.think = multi_wait; + self.nextthink = time + self.wait; + } + else + { // we can't just remove (self) here, because this is a touch function + // called wheil C code is looping through area links... + self.touch = SUB_Null; + self.nextthink = time + 0.1; + self.think = SUB_Remove; + } +}; + +void() multi_killed = +{ + self.enemy = damage_attacker; + multi_trigger(); +}; + +void() multi_use = +{ + self.enemy = activator; + multi_trigger(); +}; + +void() multi_touch = +{ + if (other.classname != "player") + return; + +// if the trigger has an angles field, check player's facing direction + if (self.movedir != '0 0 0') + { + makevectors (other.angles); + if (v_forward * self.movedir < 0) + return; // not facing the right way + } + + self.enemy = other; + multi_trigger (); +}; + +/*QUAKED trigger_multiple (.5 .5 .5) ? notouch +Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time. +If "delay" is set, the trigger waits some time after activating before firing. +"wait" : Seconds between triggerings. (.2 default) +If notouch is set, the trigger is only fired by other entities, not by touching. +NOTOUCH has been obsoleted by trigger_relay! +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +void() trigger_multiple = +{ + if (self.sounds == 1) + { + precache_sound ("misc/secret.wav"); + self.noise = "misc/secret.wav"; + } + else if (self.sounds == 2) + { + precache_sound ("misc/talk.wav"); + self.noise = "misc/talk.wav"; + } + else if (self.sounds == 3) + { + precache_sound ("misc/trigger1.wav"); + self.noise = "misc/trigger1.wav"; + } + + if (!self.wait) + self.wait = 0.2; + self.use = multi_use; + + if (!self.classname) + self.classname = "trigger"; + + InitTrigger (); + + if (self.health) + { + if (self.spawnflags & SPAWNFLAG_NOTOUCH) + objerror ("health and notouch don't make sense\n"); + self.max_health = self.health; + self.th_die = multi_killed; + self.takedamage = DAMAGE_YES; + self.solid = SOLID_BBOX; + setorigin (self, self.origin); // make sure it links into the world + + self.nobleed = TRUE; + } + else + { + if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) ) + { + self.touch = multi_touch; + } + } +}; + + +/*QUAKED trigger_once (.5 .5 .5) ? notouch +Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching +"targetname". If "health" is set, the trigger must be killed to activate. +If notouch is set, the trigger is only fired by other entities, not by touching. +if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. +if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +void() trigger_once = +{ + self.wait = -1; + trigger_multiple(); +}; + +//============================================================================= + +/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) +This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. +*/ +void() trigger_relay = +{ + self.use = SUB_UseTargets; +}; + + +//============================================================================= + +/*QUAKED trigger_secret (.5 .5 .5) ? +secret counter trigger +sounds +1) secret +2) beep beep +3) +4) +set "message" to text string +*/ +void() trigger_secret = +{ + total_secrets = total_secrets + 1; + self.wait = -1; + if (!self.message) + self.message = "You found a secret area!"; + if (!self.sounds) + self.sounds = 1; + + if (self.sounds == 1) + { + precache_sound ("misc/secret.wav"); + self.noise = "misc/secret.wav"; + } + else if (self.sounds == 2) + { + precache_sound ("misc/talk.wav"); + self.noise = "misc/talk.wav"; + } + + trigger_multiple (); +}; + +//============================================================================= + + +void() counter_use = +{ + local string junk; + + self.count = self.count - 1; + if (self.count < 0) + return; + + if (self.count != 0) + { + if (activator.classname == "player" + && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0) + { + if (self.count >= 4) + centerprint (activator, "There are more to go..."); + else if (self.count == 3) + centerprint (activator, "Only 3 more to go..."); + else if (self.count == 2) + centerprint (activator, "Only 2 more to go..."); + else + centerprint (activator, "Only 1 more to go..."); + + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } + + return; + } + + if (activator.classname == "player" + && (self.spawnflags & SPAWNFLAG_NOMESSAGE) == 0) + { + centerprint(activator, "Sequence completed!"); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } + self.enemy = activator; + multi_trigger (); +}; + +/*QUAKED trigger_counter (.5 .5 .5) ? nomessage +Acts as an intermediary for an action that takes multiple inputs. + +If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished. + +After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself. +*/ +void() trigger_counter = +{ + self.wait = -1; + if (!self.count) + self.count = 2; + + self.use = counter_use; +}; + + +/* +============================================================================== + +TELEPORT TRIGGERS + +============================================================================== +*/ + +float PLAYER_ONLY = 1; +float SILENT = 2; + +void() play_teleport = +{ + local string tmpstr; + + if (random() > 0.5) + tmpstr = "misc/r_tele1.wav"; + else + tmpstr = "misc/r_tele2.wav"; + + sound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM); + remove (self); +}; + +void(vector org) spawn_tfog = +{ + s = spawn (); + s.origin = org; + s.nextthink = time + 0.1;//POX - quicker sound + s.think = play_teleport; + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_TELEPORT); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); +}; + + +void() tdeath_touch = +{ + if (other == self.owner) + return; + +// frag anyone who teleports in on top of an invincible player + if (other.classname == "player") + { + if (other.invincible_finished > time) + self.classname = "teledeath2"; + if (self.owner.classname != "player") + { // other monsters explode themselves + T_Damage (self.owner, self, self, 50000); + return; + } + + } + + if (other.health) + { + T_Damage (other, self, self, 50000); + } +}; + + +void(vector org, entity death_owner) spawn_tdeath = +{ +local entity death; + + death = spawn(); + death.classname = "teledeath"; + death.movetype = MOVETYPE_NONE; + death.solid = SOLID_TRIGGER; + death.angles = '0 0 0'; + setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1'); + setorigin (death, org); + death.touch = tdeath_touch; + death.nextthink = time + 0.2; + death.think = SUB_Remove; + death.owner = death_owner; + + force_retouch = 2; // make sure even still objects get hit +}; + +void() teleport_touch = +{ +local entity t; +local vector org; + + if (other.classname == "mine") + return; + if (other.classname == "minearm") + return; + if (other.classname == "minearmed") + return; + + if (self.targetname) + { + if (self.nextthink < time) + { + return; // not fired yet + } + } + + if (self.spawnflags & PLAYER_ONLY) + { + if (other.classname != "player") + return; + } + +// only teleport living creatures + if (other.health <= 0 || other.solid != SOLID_SLIDEBOX) + return; + + SUB_UseTargets (); + +// put a tfog where the player was + spawn_tfog (other.origin); + + t = find (world, targetname, self.target); + if (!t) + objerror ("couldn't find target"); + +// spawn a tfog flash in front of the destination + makevectors (t.mangle); + org = t.origin + 32 * v_forward; + + spawn_tfog (org); + spawn_tdeath(t.origin, other); + +// move the player and lock him down for a little while + if (!other.health) + { + other.origin = t.origin; + other.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y); + return; + } + + setorigin (other, t.origin); + other.angles = t.mangle; + if (other.classname == "player") + { + other.fixangle = 1; // turn this way immediately + other.teleport_time = time + 0.7; + if (other.flags & FL_ONGROUND) + other.flags = other.flags - FL_ONGROUND; + other.velocity = v_forward * 300; + if (other.classname == "player") + stuffcmd (other, "tele_zoom\n"); + + } + other.flags = other.flags - other.flags & FL_ONGROUND; +}; + +/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32) +This is the destination marker for a teleporter. It should have a "targetname" field with the same value as a teleporter's "target" field. +*/ +void() info_teleport_destination = +{ +// this does nothing, just serves as a target spot + self.mangle = self.angles; + self.angles = '0 0 0'; + self.model = ""; + self.origin = self.origin + '0 0 27'; + if (!self.targetname) + objerror ("no targetname"); +}; + +void() teleport_use = +{ + self.nextthink = time + 0.2; + force_retouch = 2; // make sure even still objects get hit + self.think = SUB_Null; +}; + +/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT +Any object touching this will be transported to the corresponding info_teleport_destination entity. You must set the "target" field, and create an object with a "targetname" field that matches. + +If the trigger_teleport has a targetname, it will only teleport entities when it has been fired. +*/ +void() trigger_teleport = +{ + local vector o; + + InitTrigger (); + self.touch = teleport_touch; + // find the destination + if (!self.target) + objerror ("no target"); + self.use = teleport_use; + + if (!(self.spawnflags & SILENT)) + { + precache_sound ("ambience/hum1.wav"); + o = (self.mins + self.maxs)*0.5; + ambientsound (o, "ambience/hum1.wav",0.5 , ATTN_STATIC); + } +}; + +/* +============================================================================== + +trigger_setskill + +============================================================================== +*/ + +void() trigger_skill_touch = +{ + if (other.classname != "player") + return; + + cvar_set ("skill", self.message); +}; + +/*QUAKED trigger_setskill (.5 .5 .5) ? +sets skill level to the value of "message". +Only used on start map. +*/ +void() trigger_setskill = +{ + InitTrigger (); + self.touch = trigger_skill_touch; +}; + + +/* +============================================================================== + +ONLY REGISTERED TRIGGERS + +============================================================================== +*/ + +void() trigger_onlyregistered_touch = +{ + if (other.classname != "player") + return; + if (self.attack_finished > time) + return; + + self.attack_finished = time + 2; + if (cvar("registered")) + { + self.message = ""; + SUB_UseTargets (); + remove (self); + } + else + { + if (self.message != "") + { + centerprint (other, self.message); + sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM); + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + } + } +}; + +/*QUAKED trigger_onlyregistered (.5 .5 .5) ? +Only fires if playing the registered version, otherwise prints the message +*/ +void() trigger_onlyregistered = +{ + precache_sound ("misc/talk.wav"); + InitTrigger (); + self.touch = trigger_onlyregistered_touch; +}; + +//============================================================================ + +void() hurt_on = +{ + self.solid = SOLID_TRIGGER; + self.nextthink = -1; +}; + +void() hurt_touch = +{ + if (other.takedamage) + { + self.solid = SOLID_NOT; + T_Damage (other, self, self, self.dmg); + self.think = hurt_on; + self.nextthink = time + 1; + } + + return; +}; + +/*QUAKED trigger_hurt (.5 .5 .5) ? +Any object touching this will be hurt +set dmg to damage amount +defalt dmg = 5 +*/ +void() trigger_hurt = +{ + InitTrigger (); + self.touch = hurt_touch; + if (!self.dmg) + self.dmg = 5; +}; + +//============================================================================ + +float PUSH_ONCE = 1; + +void() trigger_push_touch = +{ + if (other.classname == "grenade") + other.velocity = self.speed * self.movedir * 10; + + else if (other.health > 0) + { + other.velocity = self.speed * self.movedir * 10; + + if (other.classname == "player") + { + if (other.fly_sound < time) + { + other.fly_sound = time + 1.5; + sound (other, CHAN_AUTO, "ambience/windfly.wav", 1, ATTN_NORM); + } + } + } + if (self.spawnflags & PUSH_ONCE) + remove(self); +}; + + +/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE +Pushes the player +*/ +void() trigger_push = +{ + InitTrigger (); + precache_sound ("ambience/windfly.wav"); + self.touch = trigger_push_touch; + if (!self.speed) + self.speed = 1000; +}; + +/*-------------------- +trigger_bouncepad + +POX v1.2 - Adds Q3A style bounce pads +It's kind of a low level entity to create, you must grasp the concept of 3D vectors and acceleration to get it to work + +angles - must be entered as '0 0 0' (roll, pitch, yaw) in English, that's (left/right, forward/backward, up/down) +so, negative numbers are (left, back, down) positive numbers are (right, forward, up) The bigger the number, +(in the positive or negative direction) the greater the acceleration in that direction. +A value of (0 40 800) is a good starting point for a vertical, slightly-forward bounce +(Just remeber that forward (pitch) is ALWAYS NORTH when viewing a map from above) + +Use SMALL triggers for this one (it has a touch timeout to prevent serious acceleration) +---------------------*/ + +.float bounce_time; + +void() trigger_bounce_touch = +{ + if (other.bounce_time > time) + return; + + if ((other.classname == "player") && (other.health > 0)) + { + other.velocity = self.angles; + other.bounce_time = time + 0.8; + + if (other.classname == "player") + { + sound (self, CHAN_AUTO, "misc/menu2.wav", 1, ATTN_NORM); + //POX v1.21 - jump sound + sound (other, CHAN_VOICE, "player/plyrjmp8.wav", 1, ATTN_NORM); + } + } +}; + + +void() trigger_bouncepad = +{ + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); // set size and link into world + self.movetype = MOVETYPE_NONE; + self.modelindex = 0; + self.model = ""; + + self.touch = trigger_bounce_touch; + +}; + +//============================================================================ + +void() trigger_monsterjump_touch = +{ + if ( other.flags & (FL_MONSTER | FL_FLY | FL_SWIM) != FL_MONSTER ) + return; + +// set XY even if not on ground, so the jump will clear lips + other.velocity_x = self.movedir_x * self.speed; + other.velocity_y = self.movedir_y * self.speed; + + if ( !(other.flags & FL_ONGROUND) ) + return; + + other.flags = other.flags - FL_ONGROUND; + + other.velocity_z = self.height; +}; + +/*QUAKED trigger_monsterjump (.5 .5 .5) ? +Walking monsters that touch this will jump in the direction of the trigger's angle +"speed" default to 200, the speed thrown forward +"height" default to 200, the speed thrown upwards +*/ +void() trigger_monsterjump = +{ + if (!self.speed) + self.speed = 200; + if (!self.height) + self.height = 200; + if (self.angles == '0 0 0') + self.angles = '0 360 0'; + InitTrigger (); + self.touch = trigger_monsterjump_touch; +}; + diff --git a/ParoxysmII/source/nq/weapons.qc b/ParoxysmII/source/nq/weapons.qc new file mode 100644 index 0000000..7f0fb41 --- /dev/null +++ b/ParoxysmII/source/nq/weapons.qc @@ -0,0 +1,1816 @@ +/* +A note from POX (pox@planetquake.com): + +This file has been hacked to death. + +Large portions of code have been altered / deleted or commented out, +many of the new weapons are tied into big changes in client.qc and player.qc. + +When I first started, I was just hacking around so a lot of code went uncommented, and +unchecked. I cleaned it up as much as possible but it still sucks hard in some places + +There is alot of code commented out - stuff like the old Paroxysm PulseGun routines +have been left in 'casue they offer some unique hacks that may be of interest. + +You'll see FlameThrower stuff all over the place, it was removed from the mod but it +was easier to just leave it in some functions. (I may enable it some day...) + +You've been warned... + +BTW - 'Rhino' was the original name for the Anihilator... + +*/ + +//POX v1.1 moved prototypes to sectrig.qc + +// called by worldspawn +// POX - LOADS of new mdls and sounds to precache... +void() W_Precache = +{ + precache_model ("progs/plasma.mdl"); + precache_model ("progs/laser.mdl"); + precache_model ("progs/spark.spr"); + + precache_sound ("weapons/r_exp3.wav"); // new rocket explosion + precache_sound ("weapons/rocket1i.wav"); // spike gun + precache_sound ("weapons/sgun1.wav"); + precache_sound ("weapons/ric1.wav"); // ricochet (used in c code) + precache_sound ("weapons/ric2.wav"); // ricochet (used in c code) + precache_sound ("weapons/ric3.wav"); // ricochet (used in c code) + precache_sound ("weapons/spike2.wav"); // super spikes + precache_sound ("weapons/hog.wav"); // new nailgun sound + precache_sound ("weapons/tink1.wav"); // spikes tink (used in c code) + precache_sound ("weapons/tink2.wav"); + precache_sound ("weapons/gren.wav"); // grenade launcher + precache_sound ("weapons/gren2.wav"); // second trigger grenades + precache_sound ("weapons/bounce.wav"); // grenade bounce + precache_sound ("weapons/bounce2.wav"); // grenade bounce alt + precache_sound ("weapons/shotgn2.wav"); // super shotgun + + precache_sound ("weapons/mfire1.wav"); // missfire + precache_sound ("weapons/mfire2.wav"); // megaplasma burst missfire + precache_sound ("weapons/plasma.wav"); // plasmagun fire + precache_sound ("weapons/mplasma.wav"); // megaplasmagun fire + precache_sound ("weapons/mplasex.wav"); // megaplasmagun explosion + precache_sound ("weapons/gren.wav"); // super shotgun grenade fire + precache_sound ("weapons/armed.wav"); // mine armed sound + precache_sound ("weapons/minedet.wav"); //mine detonate click + + precache_sound ("weapons/rhino.wav"); //rhino firing sound + precache_sound ("weapons/rhinore.wav"); //rhino reload sound + precache_sound ("weapons/error.wav"); //weapon error sound + + precache_sound ("weapons/tsload.wav"); //t-shot load + precache_sound ("weapons/tsfire.wav"); //t-shot single fire + precache_sound ("weapons/ts3fire.wav"); //t-shot triple fire + + + //BoneSaw sounds + precache_sound ("weapons/sawon.wav"); + precache_sound ("weapons/sawoff.wav"); + precache_sound ("misc/null.wav"); //Used to silence the saw at weapon switch + precache_sound ("weapons/sawguts.wav"); + precache_sound ("weapons/sawhit.wav"); + precache_sound ("weapons/sawatck.wav"); + precache_sound ("weapons/sawidle.wav"); + + precache_sound ("weapons/shrapdet.wav"); //ShrapnelBomb detonation-confirmation beep + //Shrapnel Model + precache_model("progs/mwrub1.mdl"); + + + +// VisWeap - Player + precache_model ("progs/bsaw_p.mdl"); + precache_model ("progs/tshot_p.mdl"); + precache_model ("progs/combo_p.mdl"); + precache_model ("progs/plasma_p.mdl"); + precache_model ("progs/nail_p.mdl"); + precache_model ("progs/gren_p.mdl"); + precache_model ("progs/rhino_p.mdl"); + +// VisWeap - Weapon drop + precache_model ("progs/d_bsaw.mdl"); + precache_model ("progs/d_tshot.mdl"); + precache_model ("progs/d_combo.mdl"); + precache_model ("progs/d_plasma.mdl"); + precache_model ("progs/d_nail.mdl"); + precache_model ("progs/d_gren.mdl"); + precache_model ("progs/d_rhino.mdl"); + +//No weapon Death Model (weapons are dropped) + precache_model ("progs/death_p.mdl"); + +}; + + +/* +================ +SpawnMeatSpray +================ +*/ +void(vector org, vector vel) SpawnMeatSpray = +{ + local entity missile, mpuff; + local vector org; + + missile = spawn (); + missile.owner = self; + missile.movetype = MOVETYPE_BOUNCE; + missile.solid = SOLID_NOT; + + makevectors (self.angles); + + missile.velocity = vel; + missile.velocity_z = missile.velocity_z + 250 + 50*random(); + + missile.avelocity = '3000 1000 2000'; + +// set missile duration + missile.nextthink = time + 1; + missile.think = SUB_Remove; + + setmodel (missile, "progs/zom_gib.mdl"); + setsize (missile, '0 0 0', '0 0 0'); + setorigin (missile, org); +}; + + +/* POX - 1.01b2 - made this a little neater +================ +SpawnBlood +================ +*/ +void(vector org, vector vel, float damage) SpawnBlood = +{ + local float blood; + + //check for a bleeder + if (!trace_ent.nobleed) + blood = 73; + else + blood = 6; + + particle (org, vel*0.4, blood, damage*3); +}; + + +/* POX - 1.01b2 - made this a little neater +================ +spawn_touchblood +================ +*/ +void(entity who,float damage) spawn_touchblood = +{ + local vector vel; + local float blood; + + //check for a bleeder + if (!other.nobleed) + blood = 73; + else + blood = 6; + + vel = wall_velocity () * 0.2; + + particle (self.origin, vel*0.4, blood, damage*2); +}; + + +/* +================ +SpawnChunk +================ +*/ +void(vector org, vector vel) SpawnChunk = +{ + particle (org, vel*0.02, 0, 10); +}; + +/* +POX - The BoneSaw replaces the Pulsegun from versions .4b2 and below +This code is based on the chainsaw code from Zerstyer, with a re-write +of the sound and animation code to accomodate the new model (see player.qc) +================ + +W_FireSaw + +================ + +*/ + +void() W_FireSaw = + +{ + + local vector source; + + local vector org; + + + + makevectors (self.v_angle); + + +//POX - v1.02b - increased range + + source = self.origin + '0 0 24'; + + traceline (source, source + v_forward*64, FALSE, self); + + if (trace_fraction == 1.0) + + return; + + + + org = trace_endpos - v_forward*4; + + + + if (trace_ent.takedamage) + + { + + trace_ent.axhitme = 2; + + //POX v1.1 - nobleed fix + if (trace_ent.nobleed) + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_GUNSHOT); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + } + else + { + SpawnBlood(trace_endpos, '0 0 5', 20); + SpawnMeatSpray (self.origin + v_forward*16, ((random()*300) - 150) * v_right + (100 * v_forward)); + } + + + T_Damage (trace_ent, self, self, 17); //POX 1.01b - reduced damage - POX 1.1 increased damage :) + + + trace_ent.velocity = trace_ent.velocity * 0.5; + + sound (self, CHAN_WEAPON, "weapons/sawguts.wav", 1, ATTN_NORM); + + self.punchangle_x = -8; + + } + + else + + { // hit wall + + sound (self, CHAN_WEAPON, "weapons/sawhit.wav", 1, ATTN_NORM); + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_GUNSHOT); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + + } + +}; + + +//============================================================================ + + +/* +============================================================================== + +MULTI-DAMAGE + +Collects multiple small damages into a single damage + +============================================================================== +*/ + +entity multi_ent; +float multi_damage; + +void() ClearMultiDamage = +{ + multi_ent = world; + multi_damage = 0; +}; + +void() ApplyMultiDamage = +{ + if (!multi_ent) + return; + T_Damage (multi_ent, self, self, multi_damage); +}; + +void(entity hit, float damage) AddMultiDamage = +{ + if (!hit) + return; + + if (hit != multi_ent) + { + ApplyMultiDamage (); + multi_damage = damage; + multi_ent = hit; + } + else + multi_damage = multi_damage + damage; +}; + +/* +============================================================================== + +BULLETS + +============================================================================== +*/ + +/* +================ +TraceAttack +================ +*/ +void(float damage, vector dir) TraceAttack = +{ + local vector vel, org; + + vel = normalize(dir + v_up*crandom() + v_right*crandom()); + vel = vel + 2*trace_plane_normal; + vel = vel * 200; + + org = trace_endpos - dir*4; + +//POX - v1.1 - Fixed nobleed not working with shotguns + if (trace_ent.takedamage && !trace_ent.nobleed) + { + SpawnBlood (org, vel*0.2, damage); + AddMultiDamage (trace_ent, damage); + } + else if (trace_ent.takedamage && trace_ent.nobleed) + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_GUNSHOT); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + AddMultiDamage (trace_ent, damage); + } + else + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_GUNSHOT); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + } +}; + +/* POX - v1.1 +================ +FireBullets2 + +Made a seperate function for the tShot second trigger +- more damage with less bullets +================ +*/ +void(float shotcount, vector dir, vector spread) FireBullets2 = +{ + local vector direction; + local vector src; + + makevectors(self.v_angle); + + src = self.origin + v_forward*10; + src_z = self.absmin_z + self.size_z * 0.7; + + ClearMultiDamage (); + while (shotcount > 0) + { + direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up; + + traceline (src, src + direction*2048, FALSE, self); + if (trace_fraction != 1.0) + TraceAttack (8, direction); + + shotcount = shotcount - 1; + } + ApplyMultiDamage (); +}; + +/* +================ +FireBullets + +Used by shotgun, super shotgun, and enemy soldier firing +Go to the trouble of combining multiple pellets into a single damage call. +================ +*/ +void(float shotcount, vector dir, vector spread) FireBullets = +{ + local vector direction; + local vector src; + + makevectors(self.v_angle); + + src = self.origin + v_forward*10; + src_z = self.absmin_z + self.size_z * 0.7; + + ClearMultiDamage (); + while (shotcount > 0) + { + direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up; + + traceline (src, src + direction*2048, FALSE, self); + if (trace_fraction != 1.0) + TraceAttack (4, direction); + + shotcount = shotcount - 1; + } + ApplyMultiDamage (); +}; + + +/* +================ +W_FireShotgun +================ +*/ + +void() W_FireShotgun = +{ + local vector dir; + + sound (self, CHAN_WEAPON, "weapons/tsfire.wav", 1, ATTN_NORM); + + self.punchangle_x = -2; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -35; + + self.currentammo = self.ammo_shells = self.ammo_shells - 1; + dir = aim (self, 100000); + FireBullets (6, dir, '0.04 0.04 0'); +}; + + +/* +================ +W_FireSuperShotgun +================ +*/ +void() W_FireSuperShotgun = +{ + local vector dir; + local float bullets, used; + + bullets = 14; + used = 2; + + //POX v1.1 don't plat tShot sound... + if (self.currentammo == 1) + { + bullets = 6; + used = 1; + } + + sound (self ,CHAN_WEAPON, "weapons/shotgn2.wav", 1, ATTN_NORM); + + self.punchangle_x = -4; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -60; + + self.currentammo = self.ammo_shells = self.ammo_shells - used; + dir = aim (self, 100000); + FireBullets (bullets, dir, '0.14 0.08 0'); +}; + + + + +/* +============================================================================== + +ROCKETS + +============================================================================== +*/ + +void() T_MissileTouch = +{ + local float damg; + + if (other == self.owner) + return; // don't explode on owner + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + damg = 20 + random()*10; + + if (other.health) + T_Damage (other, self, self.owner, damg); + + + // don't do radius damage to the other, because all the damage + // was done in the impact + T_RadiusDamage (self, self.owner, 90, other); + +// For some reason, ID called explosions sounds from all over the place +// sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM); + self.origin = self.origin - 8*normalize(self.velocity); + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + + BecomeExplosion (); +}; + +/* +================ +W_FireRocket +================ +*/ +void(vector barrel) W_FireRocket = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 0.5; + + //if player fired last rocket reset the reload bit + if (self.ammo_rockets == 0) + self.reload_rocket = 0; + else //player has rockets left so ad 1 to reload count + self.reload_rocket = self.reload_rocket + 1; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_TOSS; + newmis.solid = SOLID_BBOX; + newmis.classname = "rocket"; + +// set newmis speed + + makevectors (self.v_angle); + + newmis.velocity = v_forward*1100 + v_up * 220 + v_right* -22; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = T_MissileTouch; + + +// set newmis duration + newmis.nextthink = time + 5; + newmis.think = SUB_Remove; + + setmodel (newmis, "progs/grenade.mdl"); + + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin + v_forward* 8 + v_right* 12 + barrel); + +}; + +//============================================================================= +// +// Grenade +// +//============================================================================= + +void() GrenadeExplode = +{ + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + T_RadiusDamage (self, self.owner, 120, world); + + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + + BecomeExplosion (); +}; + +void() GrenadeTouch = +{ + local float r; + + r=random(); + + if (other == self.owner) + return; // don't explode on owner + if (other.takedamage == DAMAGE_AIM) + { + GrenadeExplode(); + return; + } + + //pick a bounce sound + if (r < 0.75) + sound (self, CHAN_VOICE, "weapons/bounce.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "weapons/bounce2.wav", 1, ATTN_NORM); + + if (self.velocity == '0 0 0') + self.avelocity = '0 0 0'; +}; + +/* +================ +W_FireGrenade +================ +*/ +void() W_FireGrenade = +{ + + self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; + + sound (self, CHAN_WEAPON, "weapons/gren.wav", 1, ATTN_NORM); + + self.punchangle_x = -4; + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -75; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_BOUNCE; + newmis.solid = SOLID_BBOX; + newmis.classname = "grenade"; + +// set newmis speed + + makevectors (self.v_angle); + + if (self.v_angle_x) + newmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + else + { + newmis.velocity = aim(self, 10000); + newmis.velocity = newmis.velocity * 600; + newmis.velocity_z = 200; + } + + newmis.avelocity = '300 300 300'; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = GrenadeTouch; + + // set newmis duration + newmis.nextthink = time + 2.5; + newmis.think = GrenadeExplode; + + setmodel (newmis, "progs/grenade.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin); +}; + + +/* +================================================================================= +Start Plasma Gun Fire +================================================================================= +*/ + +void() plasma_touch = +{ +local float rand; + + if (other == self.owner) + return; + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + //if (other.solid == SOLID_TRIGGER) + // return; // trigger field, do nothing + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + +// hit something that bleeds + if (other.takedamage) + { + spawn_touchblood (other, 7); + + + T_Damage (other, self, self.owner, 7); + } + else + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_GUNSHOT); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + } + + remove(self); + +}; + +void(vector org, vector dir) launch_plasma = +{ + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_FLYMISSILE; + newmis.solid = SOLID_BBOX; + + newmis.angles = vectoangles(dir); + + newmis.touch = plasma_touch; + newmis.classname = "plasma"; + + /*POX v1.11 + if (self.classname == "bot") + { + newmis.nextthink = time + 0.05; + newmis.think = bot_missile_think; + } + else + {*/ + newmis.think = SUB_Remove; + newmis.nextthink = time + 5; + //} + + setmodel (newmis, "progs/laser.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + setorigin (newmis, org); + + newmis.velocity = dir * 1400; + +}; + +void(float ox) W_FirePlasma = +{ + local vector dir; + + makevectors (self.v_angle); + + sound (self, CHAN_WEAPON, "weapons/plasma.wav", 0.8, ATTN_NORM); + + self.currentammo = self.ammo_cells = self.ammo_cells - 1; + + self.punchangle_x = -1; + + dir = aim (self, 1000); + + launch_plasma (self.origin + v_forward*12 + '0 0 12' + v_right*ox, dir); + //POX v1.11 + //spawn_spark(ox); + + +}; + +/* +================================================================================= +Start nailgun fire +================================================================================= +*/ +void() spike_touch = +{ + + if (other == self.owner) + return; + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + +// hit something that bleeds + if (other.takedamage) + { + spawn_touchblood (other, 9); + T_Damage (other, self, self.owner, 9); + } + else + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_SPIKE); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + } + + remove(self); + +}; + +void() superspike_touch = +{ + if (other == self.owner) + return; + + //POX v1.11 + if (self.voided) + return; + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + +// hit something that bleeds + if (other.takedamage) + { + spawn_touchblood (other, 18); + + + T_Damage (other, self, self.owner, 18); + } + else + { + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_SUPERSPIKE); + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + } + + remove(self); + +}; + +void(vector org, vector dir) launch_spike = +{ + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_FLYMISSILE; + newmis.solid = SOLID_BBOX; + + newmis.angles = vectoangles(dir); + + newmis.touch = spike_touch; + newmis.classname = "spike"; + + /*POX v1.11 + if (self.classname == "bot") + { + newmis.nextthink = time + 0.05; + newmis.think = bot_missile_think; + } + else + {*/ + newmis.think = SUB_Remove; + newmis.nextthink = time + 6; + //} + + setmodel (newmis, "progs/spike.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + setorigin (newmis, org); + + newmis.velocity = dir * 1000; + +}; + +// New Nailgun fire +void(float ox) W_FireNails = +{ + local vector dir; + local entity old; + + //POX 1.21 - What The? - I goofed on this in v120b + if (!self.aflag) //don't swap models if chasecam is on + self.weaponmodel = "progs/v_nailgl.mdl"; // light up nailgun barrels + + sound (self, CHAN_WEAPON, "weapons/hog.wav", 0.8, ATTN_NORM); // new nailgun sound + + self.currentammo = self.ammo_nails = self.ammo_nails - 1; + dir = aim (self, 1000); + launch_spike (self.origin + '0 0 16' + v_right*ox, dir); + newmis.touch = spike_touch; + setmodel (newmis, "progs/spike.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + self.punchangle_x = -2; + +}; + +// Left for spike trap use +void() W_FireSuperSpikes = +{ + local vector dir; + local entity old; + + sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM); + //self.attack_finished = time + 0.15; + self.currentammo = self.ammo_nails = self.ammo_nails - 1; + dir = aim (self, 1000); + launch_spike (self.origin + '0 0 16', dir); + newmis.touch = superspike_touch; + setmodel (newmis, "progs/s_spike.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + +}; + +// Obselete? +void(float ox) W_FireSpikes = +{ + local vector dir; + local entity old; + + makevectors (self.v_angle); + + /* + if (self.weapon == IT_SUPER_NAILGUN) + { + W_FireNails (ox); // Go to the New Nail attack + return; + } + + else + {*/ + sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM); + //self.attack_finished = time + 0.2; + self.currentammo = self.ammo_nails = self.ammo_nails - 1; + dir = aim (self, 1000); + launch_spike (self.origin + '0 0 16' + v_right*ox, dir); + + self.punchangle_x = -2; + //} +}; + + +/* +=============================================================================== + +PLAYER WEAPON USE + +=============================================================================== +*/ + +void() W_SetCurrentAmmo = +{ + //POX v1.21 - Moved this to the top to avoid weirdness... + //Silence the BoneSaw and flag it off + if (self.weapon != IT_BONESAW) + { + //sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM);//Bot sound problems... + self.saw_on = FALSE; + } + + //POX - saw_on is a hack to keep the BoneSaw's animation playing correctly + if (!self.saw_on) + player_run (); // get out of any weapon firing states + + self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) ); + + self.weaponmodel = ""; //Hack for ChaseCam + + if (self.weapon == IT_BONESAW) + { + + self.currentammo = 0; + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_bsaw.mdl"; + + } + else if (self.weapon == IT_TSHOT) + { + self.currentammo = self.ammo_shells; + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_tshot.mdl"; + + self.weaponframe = 0; + + self.items = self.items | IT_SHELLS; + } + else if (self.weapon == IT_COMBOGUN) + { + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_combo.mdl"; + + self.weaponframe = 0; + + //POX - ammo depends on last active trigger + if (self.which_ammo == 1) + { + self.currentammo = self.ammo_rockets; + self.items = self.items | IT_ROCKETS; + } + else + { + self.currentammo = self.ammo_shells; + self.items = self.items | IT_SHELLS; + } + } + else if (self.weapon == IT_PLASMAGUN) + { + self.currentammo = self.ammo_cells; + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_plasma.mdl"; + + self.weaponframe = 0; + self.items = self.items | IT_CELLS; + } + else if (self.weapon == IT_SUPER_NAILGUN) + { + self.currentammo = self.ammo_nails; + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_nailg.mdl"; + + self.weaponframe = 0; + self.items = self.items | IT_NAILS; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + { + self.currentammo = self.ammo_rockets; + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_gren.mdl"; + + self.weaponframe = 0; + self.items = self.items | IT_ROCKETS; + } + else if (self.weapon == IT_ROCKET_LAUNCHER) + { + self.currentammo = self.ammo_rockets; + + if (self.aflag != TRUE) + self.weaponmodel = "progs/v_rhino.mdl"; + + self.weaponframe = 0; + self.items = self.items | IT_ROCKETS; + } + else + { + self.currentammo = 0; + self.weaponmodel = ""; + self.weaponframe = 0; + } + + getmodel(self.weapon, self); // VisWeap MOD: updates the model to take new weapon into account. + +}; + +/* BEST WEAPON MARKER */ +float() W_BestWeapon = +{ + local float it; + + it = self.items; + + // A hacky way to keep SuperShot Gun Active when out of rockets + if ((self.weapon == IT_COMBOGUN) && ( self.ammo_shells >= 2 && (self.ammo_rockets < 1))) + { + self.which_ammo = 0;//POX v1.21 - fix for DM_AUTOSWITCH bug + return IT_COMBOGUN; + } + + //if (self.waterlevel <= 1 && self.ammo_cells >= 1 && (it & IT_FLAMETHROWER) ) + // return IT_FLAMETHROWER; + if(self.ammo_cells >= 1 && (it & IT_PLASMAGUN) ) + return IT_PLASMAGUN; + if(self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) ) + return IT_SUPER_NAILGUN; + if(self.ammo_shells >= 2 && (it & IT_COMBOGUN) ) + return IT_COMBOGUN; + if(self.ammo_shells >= 1 && (it & IT_TSHOT) ) + return IT_TSHOT; + return IT_BONESAW; +}; + +float() W_CheckNoAmmo = +{ + if (self.currentammo > 0) + return TRUE; + + if (self.weapon == IT_BONESAW) + return TRUE; + + self.weapon = W_BestWeapon (); + + W_SetCurrentAmmo (); + +// drop the weapon down + return FALSE; +}; +/* BEST WEAPON MARKER */ + +/* +============ +W_Attack + +An attack impulse can be triggered now +============ +*/ + +//POX v1.1 moved protypes to sectrig.qc added intermission check +float intermission_running; + +void() W_Attack = +{ + // + POX - from URQW patch... + // 1999-07-05 Firing during intermission fix by numb - start + if (intermission_running) + return; + + if (deathmatch & DM_AUTOSWITCH) + { + if (!W_CheckNoAmmo ()) + return; + } + + makevectors (self.v_angle); // calculate forward angle for velocity + self.show_hostile = time + 1; // wake monsters up + + + if (self.weapon == IT_BONESAW) + { + if (!self.saw_on) + player_bonesaw1(); + else + player_bonesaw3(); + + self.attack_finished = time + 0.55; + + } + else if (self.weapon == IT_TSHOT) + { + if (self.ammo_shells < 1) + { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.5; + } + else if (self.st_tshotload < time) //prime done so shoot + { + //t-shot is primed so do a triple shot + //have to double check ammo since switch to ComboGun can mess it up + if ((self.prime_tshot == TRUE) && (self.ammo_shells > 2)) + { + player_tshot1 (); + W_FireTShot (); + self.attack_finished = time + 0.7; + self.prime_tshot = FALSE; + } + else //normal shot + { + player_shot1 (); + W_FireShotgun (); + self.attack_finished = time + 0.5; + //reset prime since switching to ComboGun can use up shells + self.prime_tshot = FALSE; + } + } + } + + else if ((self.weapon == IT_COMBOGUN) && (self.st_sshotgun < time)) + { + if (self.ammo_shells < 1) + { + self.currentammo = self.ammo_shells; + self.items = self.items - ( self.items & (IT_ROCKETS) ); + self.items = self.items | IT_SHELLS; + self.which_ammo = 0; + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_sshotgun = time + 0.7; + } + + else + { + self.currentammo = self.ammo_shells; + self.items = self.items - ( self.items & (IT_ROCKETS) ); + self.items = self.items | IT_SHELLS; + self.which_ammo = 0; + player_shot1 (); + W_FireSuperShotgun (); + self.st_sshotgun = time + 0.7; + } + } + else if ((self.weapon == IT_PLASMAGUN) && (self.st_plasma < time) && (self.st_mplasma < time)) + { + if (self.ammo_cells < 1) + { + sound (self, CHAN_AUTO, "weapons/mfire2.wav", 1, ATTN_NORM); + self.st_plasma = time + 0.05;//POX v1.21 + } + else if (!self.LorR) // check which barrel fired last + player_plasma1 (); + else + player_plasma2 (); + } + + else if ((self.weapon == IT_SUPER_NAILGUN) && (self.st_nailgun < time)) + { + if (self.ammo_nails < 1) + { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_nailgun = time + 0.4;//POX v1.21 + } + else + player_nail1 (); + } + else if ((self.weapon == IT_GRENADE_LAUNCHER) && (self.st_grenade < time)) + { + if (self.ammo_rockets < 1) + { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_grenade = time + 0.6; + } + else + { + player_grenade1(); + W_FireGrenade(); + self.st_grenade = time + 0.6; + } + } + else if (self.weapon == IT_ROCKET_LAUNCHER) + { + if (self.ammo_rockets < 1) //no rockets so missfire + { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.4; + } + else if (self.reload_rocket > 6) //have to reload so missfire + { + sound (self, CHAN_AUTO, "weapons/error.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.42; + } + else if (self.st_rocketload > time) // still reloading so do nothing + { + SUB_Null; + } + else + { + player_rocket1(); + self.attack_finished = time + 0.4; + } + } + +}; + +/* +============ +W_ChangeWeapon +============ +*/ +void() W_ChangeWeapon = +{ + local float it, am, fl; + + it = self.items; + am = 0; + self.which_ammo = 0; // Default ammo to shells for SuperShotgun + + if (self.impulse == 1) + { + fl = IT_BONESAW; + //self.p_flex = time + 8; + } + else if (self.impulse == 2) + { + fl = IT_TSHOT; + if (self.ammo_shells < 1) + am = 1; + } + else if (self.impulse == 3) + { + fl = IT_COMBOGUN; + if (self.ammo_shells < 2) + { + am = 1; + //POX v1.1 - fix for dm_autoswitch + if (self.ammo_rockets > 0 && !deathmatch & DM_AUTOSWITCH) // allow player to select SuperShotgun if he has rockets + { + self.which_ammo = 1; // tell W_SetCurrentAmmo to use rockets - not shells + am = 0; + } + } + } + else if (self.impulse == 4) + { + fl = IT_PLASMAGUN; + if (self.ammo_cells < 1) + am = 1; + } + else if (self.impulse == 5) + { + fl = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (self.impulse == 6) + { + fl = IT_GRENADE_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + else if (self.impulse == 7) + { + fl = IT_ROCKET_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + + self.impulse = 0; + + if (!(self.items & fl)) + { // don't have the weapon or the ammo + sprint (self, "no weapon.\n"); + return; + } + + if (am) + { // don't have the ammo + sprint (self, "not enough ammo.\n"); + return; + } + +// +// set weapon, set ammo +// + self.weapon = fl; + W_SetCurrentAmmo (); +}; + +/* +============ +CheatCommand +============ +*/ +void() CheatCommand = +{ + + if (deathmatch || coop) + return; + + self.ammo_rockets = 100; + self.ammo_nails = 200; + self.ammo_shells = 100; + self.ammo_cells = 200; + self.items = self.items | + IT_BONESAW | + IT_TSHOT | + IT_COMBOGUN | + IT_PLASMAGUN | + IT_SUPER_NAILGUN | + IT_GRENADE_LAUNCHER | + IT_ROCKET_LAUNCHER; + + self.weapon = IT_COMBOGUN; + self.impulse = 0; + W_SetCurrentAmmo (); +}; + +/* POX v1.1 - got rid of non-existent flamethrower checks +============ +CycleWeaponCommand + +Go to the next weapon with ammo +============ +*/ +void() CycleWeaponCommand = +{ + local float it, am; + + it = self.items; + self.impulse = 0; + + while (1) + { + am = 0; + self.which_ammo = 0; // Default ammo to shells for SuperShotgun + + if (self.weapon == IT_ROCKET_LAUNCHER) + { + self.weapon = IT_BONESAW; + } + else if (self.weapon == IT_BONESAW) + { + self.weapon = IT_TSHOT; + if (self.ammo_shells < 1) + am = 1; + } + else if (self.weapon == IT_TSHOT) + { + self.weapon = IT_COMBOGUN; + if (self.ammo_shells < 2) + { + am = 1; + //POX v1.1 - fix for dm_autoswitch + if (self.ammo_rockets > 0 && !deathmatch & DM_AUTOSWITCH) // allow player to select SuperShotgun if he has rockets + { + self.which_ammo = 1; // tell W_SetCurrentAmmo to use rockets - not shells + am = 0; + } + } + } + else if (self.weapon == IT_COMBOGUN) + { + self.weapon = IT_PLASMAGUN; + if (self.ammo_cells < 1) + am = 1; + } + else if (self.weapon == IT_PLASMAGUN) + { + self.weapon = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (self.weapon == IT_SUPER_NAILGUN) + { + self.weapon = IT_GRENADE_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + { + self.weapon = IT_ROCKET_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + + if ( (it & self.weapon) && am == 0) + { + W_SetCurrentAmmo (); + return; + } + } + +}; + +/* +============ +CycleWeaponReverseCommand + +Go to the prev weapon with ammo +============ +*/ +void() CycleWeaponReverseCommand = +{ + local float it, am; + + it = self.items; + self.impulse = 0; + + while (1) + { + am = 0; + self.which_ammo = 0; // Default ammo to shells for SuperShotgun + + if (self.weapon == IT_ROCKET_LAUNCHER) + { + self.weapon = IT_GRENADE_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + else if (self.weapon == IT_GRENADE_LAUNCHER) + { + self.weapon = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } + else if (self.weapon == IT_SUPER_NAILGUN) + { + self.weapon = IT_PLASMAGUN; + if (self.ammo_cells < 1) + am = 1; + } + else if (self.weapon == IT_PLASMAGUN) + { + self.weapon = IT_COMBOGUN; + + // allow player to select ComboGun if he has rockets + // BUT NOT IF DM_AUTOSWITCH!! + if (self.ammo_shells < 2) + { + am = 1; + if (self.ammo_rockets > 0 && !deathmatch & DM_AUTOSWITCH) + { + self.which_ammo = 1; // tell W_SetCurrentAmmo to use rockets - not shells + am = 0; + } + } + } + else if (self.weapon == IT_COMBOGUN) + { + self.weapon = IT_TSHOT; + if (self.ammo_shells < 1) + am = 1; + } + else if (self.weapon == IT_TSHOT) + { + self.weapon = IT_BONESAW; + //self.p_flex = time + 8; + } + else if (self.weapon == IT_BONESAW) + { + self.weapon = IT_ROCKET_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + + if ( (it & self.weapon) && am == 0) + { + W_SetCurrentAmmo (); + return; + } + } + +}; + +/* +============ +ServerflagsCommand + +Just for development +============ +*/ +void() ServerflagsCommand = +{ + serverflags = serverflags * 2 + 1; +}; + +void() QuadCheat = +{ + if (deathmatch || coop) + return; + self.super_time = 1; + self.super_damage_finished = time + 30; + self.items = self.items | IT_QUAD; + dprint ("quad cheat\n"); +}; + + +/* +============ +ImpulseCommands + +============ +*/ +//POX - put this in a sperate routine so the same impulse can be used in autocam/setup mode +void() DisplayRules = +{ + sprint (self, "\nÓåòöåò Òõìåó\n----------------------\n |\n"); + + if (deathmatch & DM_PREDATOR) + sprint (self, "on | Predator Mode\n"); + else + sprint (self, "ÏÆÆ | Predator Mode\n"); + + if (deathmatch & DM_DARK) + sprint (self, "on | Dark Mode\n"); + else + sprint (self, "ÏÆÆ | Dark Mode\n"); + + if (deathmatch & DM_LMS) + sprint (self, "on | Last Man Standing\n"); + else + sprint (self, "ÏÆÆ | Last Man Standing\n"); + + if (deathmatch & DM_FFA) + sprint (self, "on | Free For All\n"); + else + sprint (self, "ÏÆÆ | Free For All\n"); + + if (deathmatch & DM_GIB) + sprint (self, "on | Gib\n"); + else + sprint (self, "ÏÆÆ | Gib\n"); + + if (deathmatch & DM_AUTOSWITCH) + sprint (self, "on | Weapon Autoswitch\n"); + else + sprint (self, "ÏÆÆ | Weapon Autoswitch\n"); + + if (deathmatch & DM_NOBOTS) + sprint (self, "ÏÆÆ | Bots\n"); + else + sprint (self, "on | Bots\n |\n----------------------\n\n"); + +}; + +.float target_id_temp;//POX 1.2 +//POX - stuff added here, bot & camera impulses +void() ImpulseCommands = +{ + local float cells; // used for underwater MegaPlasmaBurst + + if (self.impulse >= 1 && self.impulse <= 8) + W_ChangeWeapon (); + if (self.impulse == 9) + CheatCommand (); + if (self.impulse == 10) + CycleWeaponCommand (); + if (self.impulse == 11) + ServerflagsCommand (); + if (self.impulse == 12) + CycleWeaponReverseCommand (); + if (self.impulse == 255) + QuadCheat (); + +//POX - v1.1 target identifier toggle + if (self.impulse == 16) + { + self.target_id_temp = TRUE;//POX v1.2 - hack to get targid to work accross level changes + stuffcmd (self, "play misc/talk.wav\n"); //POX v1.2 audio confirmation + + if (self.target_id_toggle) + { + self.target_id_toggle = FALSE; + + //POX v1.12 - don't centerprint if a message is up + if (self.target_id_finished < time) + centerprint (self, "Target Identifier OFF\n"); + else + sprint (self, "Target Identifier OFF\n"); + } + else + { + self.target_id_toggle = TRUE; + + //POX v1.12 - don't centerprint if a message is up + if (self.target_id_finished < time) + centerprint (self, "Target Identifier ON\n"); + else + sprint (self, "Target Identifier ON\n"); + + self.target_id_finished = time + 3; + } + } + +//POX - v1.11 GL FlashBlend (hacky fix) - Toggles dynamiclights vs lightglows + + if (self.impulse == 17) + { + if(!self.gl_fix) + { + stuffcmd (self, "gl_flashblend 0\n"); + self.gl_fix = TRUE; + } + else + { + stuffcmd (self, "gl_flashblend 1\n"); + self.gl_fix = FALSE; + } + } + + +//pOx - 1.4 - Flashlight toggle for darkmode + + if ((self.impulse == 21) && (deathmatch & DM_DARK)) + flash_toggle(); + + +// KasCam -> + if (self.impulse == 250) + { + //If client hit observer in LMS mode, drop the player count + if ((deathmatch & DM_LMS) && self.frags > 0) + { + lms_plrcount = lms_plrcount - 1; + self.LMS_registered = FALSE; //Don't display LMS dead message + self.frags = 0; + CamClientInit (); + } + else + { + self.frags = 0; + CamClientInit (); + } + } +// <- KasCam + +//ChaseCam + if (self.impulse == 254) + CCam (); + + if (self.impulse == 150) + CCamUp (2); + if (self.impulse == 151) + CCamRight (2); + if (self.impulse == 152) + CCamForward (2); + if (self.impulse == 153) + CCamUp (-2); + if (self.impulse == 154) + CCamRight (-2); + if (self.impulse == 155) + CCamForward (-2); +//ChaseCam + + //Bot impulses - no coop/single player + + //Create Enemy bot (FF or Team) + //if (self.impulse == 251 && deathmatch) + // create_bot(1); + + //Create TeamBot + //if (self.impulse == 252 && deathmatch && teamplay) + // create_bot(2); + + //Display Server Rules Impulse + if (self.impulse == 253) + DisplayRules(); + + self.impulse = 0; +}; + +/* +============ +W_WeaponFrame + +Called every frame so impulse events can be handled as well as possible +============ +*/ + +void() W_WeaponFrame = +{ + if (time < self.attack_finished) + return; + + // POX - v1.1 target identifier + if (self.target_id_toggle && (time > self.target_id_finished)) + ID_CheckTarget (); + + //POX - Don't swap nailgun skins if player chascam is active! + if ((self.weapon == IT_SUPER_NAILGUN) && (self.st_nailgun < time) && (!self.aflag)) + self.weaponmodel = "progs/v_nailg.mdl"; // cool off nailgun barrels + +// + POX - only check these if necessary (thanks to URQW patch) +// 1998-08-14 Constantly checking all impulses fix by Perged + if (self.impulse == SECOND_TRIGGER) + W_SecondTrigger (); + else if (self.impulse) + ImpulseCommands (); +// - POX + +// check for attack + if (self.button0) + { + + if ((self.weapon == IT_BONESAW) || (self.currentammo > 0))//POX - v1.3 + SuperDamageSound (); + + W_Attack (); + } +}; + +/* +======== +SuperDamageSound + +Plays sound if needed +======== +*/ +void() SuperDamageSound = +{ + if (self.super_damage_finished > time) + { + if (self.super_sound < time) + { + self.super_sound = time + 1; + sound (self, CHAN_AUTO, "items/damage3.wav", 1, ATTN_NORM); + } + } + return; +}; diff --git a/ParoxysmII/source/observe.r b/ParoxysmII/source/observe.r new file mode 100644 index 0000000..a55b5ec --- /dev/null +++ b/ParoxysmII/source/observe.r @@ -0,0 +1,263 @@ +#include "config.rh" + +#include "paroxysm.rh" + +/* +POX - Last Man Standing Observer Code +This was written to support Last Man Standing Rules, it is a crude form of Spectate that allows +observes to stay connected as players so they can play the next game. +An LMS observer is counted as a player, so you can still have the max amount of true Spectors connected +*/ +entity() SelectSpawnPoint; +void() teleport_touch; //POX v1.12 +//POX v1.12 - sets fov for observer (impulses 1 and 2) +/*------------------ +SetObserverFOV +-------------------*/ +void() SetObserverFOV = +{ + local string ob_fov; + + ob_fov = ftos(self.LMS_observer_fov); + + stuffcmd (self, "fov "); + stuffcmd (self, ob_fov); + stuffcmd (self, "\n"); +}; +//POX v1.12 - Allows observers to use teleporters +/*------------------ +ObserverTeleportTouch +-------------------*/ +void (string destination) ObserverTeleportTouch = +{ + local entity t; + + if (self.teleport_time > time) + return; + + t = find (world, targetname, destination); + + if (!t) + objerror ("couldn't find target"); + + setorigin (self, t.origin); + self.angles = t.mangle; + self.fixangle = 1; // turn this way immediately + self.teleport_time = time + 1.7;//POX v1.2 increased this + if (self.flags & FL_ONGROUND) + self.flags = self.flags - FL_ONGROUND; + self.velocity = v_forward * 300; + self.flags = self.flags - self.flags & FL_ONGROUND; +}; +/*------------------ +ObserverImpulses +Handels observer controls +-------------------*/ +void() ObserverImpulses = +{ + //Jump to a dm start point (Fire Button) + if (self.button0) + { + self.goalentity = find(self.goalentity, classname, "info_player_deathmatch"); + + if (self.goalentity == world) + self.goalentity = find(self.goalentity, classname, "info_player_deathmatch"); + + if (self.goalentity != world) + { + setorigin(self, self.goalentity.origin); + self.angles = self.goalentity.angles; + self.fixangle = TRUE; // turn this way immediately + } + } + + //Jump into a player's position (Jump Button) + if (self.button2) + { + self.goalentity = find(self.goalentity, classname, "player"); + + if (self.goalentity == world) + self.goalentity = find(self.goalentity, classname, "player"); + + if (self.goalentity != world) + { + setorigin(self, self.goalentity.origin + '0 0 1'); + self.angles = self.goalentity.angles; + self.fixangle = TRUE; // turn this way immediately + } + } + + // POX v1.2 - added auto fov increase/decrease and reset impulses + if (self.impulse == 1) + { + if (self.LMS_zoom == 1) + self.LMS_zoom = FALSE; + else + self.LMS_zoom = 1; + } + + if (self.impulse == 2) + { + if (self.LMS_zoom == 2) + self.LMS_zoom = FALSE; + else + self.LMS_zoom = 2; + } + + if (self.impulse == 3) + { + self.LMS_observer_fov = 90; + self.LMS_zoom = FALSE; + SetObserverFOV (); + } + + //Allow TargetId to be turned off + if (self.impulse == 16) + { + if (self.target_id_toggle) + { + self.target_id_toggle = FALSE; + + //POX v1.12 - don't centerprint if a message is up + if (self.target_id_finished < time) + centerprint (self, "Target Identifier OFF\n"); + else + sprint (self, PRINT_HIGH, "Target Identifier OFF\n"); + } + else + { + self.target_id_toggle = TRUE; + //POX v1.12 - don't centerprint if a message is up + if (self.target_id_finished < time) + centerprint (self, "Target Identifier ON\n"); + else + sprint (self, PRINT_HIGH, "Target Identifier ON\n"); + + self.target_id_finished = time + 3; + } + } + + self.impulse = 0; + self.button0 = 0; + self.button1 = 0; +}; +/*------------------ +ObserverThink +Rerouted from PlayerPostThink +-------------------*/ +void() ObserverThink = +{ + local entity tele; //POX v1.12 + + // POX v1.12 - display observer control instructions + if (self.LMS_observer_time > time) + { + if (self.LMS_observer == 1) + centerprint(self, "Last Man Standing game in progress...\n\nObserving till next round."); + else //was eliminated + centerprint (self, "You have been eliminated!\n\nObserving till next round."); + self.target_id_finished = time + 0.1; + } + else if (self.LMS_observer_time + 7 > time) + { + centerprint(self, "[FIRE] cycles through spawn points\n\n[JUMP] cycles through players\n\n[1] [2] zooms in/out, [3] resets zoom."); + self.target_id_finished = time + 0.1; + } + + //POX v1.12 if touching a teleporter, go through it... + tele = findradius(self.origin, 80); + + while(tele) + { + if (tele.touch == teleport_touch) + ObserverTeleportTouch (tele.target); + + tele = tele.chain; + } + + //Update target identifier + if (self.target_id_toggle && (time > self.target_id_finished)) + ID_CheckTarget (); + + //POX v1.2 - Better fov control - first impulse starts to zoom, second stops it + if (self.LMS_zoom == 1) + { + self.LMS_observer_fov = self.LMS_observer_fov - 1; + + if (self.LMS_observer_fov < 30) + { + self.LMS_zoom = FALSE; + self.LMS_observer_fov = 30; + } + + SetObserverFOV (); + } + else if (self.LMS_zoom == 2) + { + self.LMS_observer_fov = self.LMS_observer_fov + 1; + + if (self.LMS_observer_fov > 135) + { + self.LMS_zoom = FALSE; + self.LMS_observer_fov = 135; + } + + SetObserverFOV (); + } + + ObserverImpulses (); +}; +/*------------------ +SpawnObserver +-------------------*/ +void() SpawnObserver = +{ + local entity spot; + + self.classname = "LMSobserver"; + self.health = 111; + + self.takedamage = DAMAGE_NO; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NOCLIP; //I think this is cheat protected.... + //self.flags = FL_CLIENT; + self.super_damage_finished = 0; + self.radsuit_finished = 0; + self.invisible_finished = 0; + self.invincible_finished = 0; + self.effects = 0; + self.invincible_time = 0; + self.items = 0; + self.ammo_shells = 0; + self.ammo_nails = 0; + self.ammo_rockets = 0; + self.ammo_cells = 0; + self.weapon = 0; + self.armorvalue = 0; + self.armortype = 0; + self.deadflag = DEAD_NO; + spot = SelectSpawnPoint (); + self.origin = spot.origin + '0 0 1'; + self.angles = spot.angles; + self.fixangle = TRUE; // turn this way immediately + setmodel (self, string_null); + self.weaponmodel = string_null; + setsize (self, VEC_HULL_MIN, VEC_HULL_MAX); + self.view_ofs = '0 0 22'; + self.velocity = '0 0 0'; + + //POX v1.2 - had this as '==' (worked anyway :P) + self.goalentity = world; + + //POX v1.12 + if (self.LMS_registered) //This shouldn't happen but... + { + self.LMS_registered = 0; + lms_plrcount = lms_plrcount - 1; + } + + self.LMS_observer_time = time + 3; + self.LMS_observer_fov = 100; + stuffcmd (self, "fov 100\n"); + self.target_id_toggle = TRUE; //POX v1.12 default to on +}; diff --git a/ParoxysmII/source/paroxysm.rh b/ParoxysmII/source/paroxysm.rh new file mode 100644 index 0000000..13451f4 --- /dev/null +++ b/ParoxysmII/source/paroxysm.rh @@ -0,0 +1,131 @@ +#ifndef __paroxysm_rh_ +#define __paroxysm_rh_ + +/**************************************************************************** + * ENGINE-DEFINED CONSTANTS * + ****************************************************************************/ + +// Booleans + +#define FALSE 0 +#define TRUE 1 + +// Engine-defined point content values +#define CONTENT_EMPTY -1 +#define CONTENT_SOLID -2 +#define CONTENT_WATER -3 +#define CONTENT_SLIME -4 +#define CONTENT_LAVA -5 +#define CONTENT_SKY -6 + +// Entity flags +#define FL_FLY 1 +#define FL_SWIM 2 +// set for all client edicts +#define FL_CLIENT 8 +// for enter / leave water splash +#define FL_INWATER 16 +#define FL_MONSTER 32 +// cheats +#define FL_GODMODE 64 +#define FL_NOTARGET 128 +// extra wide size for bonus items +#define FL_ITEM 256 +// standing on something +#define FL_ONGROUND 512 +// not all corners are valid +#define FL_PARTIALGROUND 1024 +// player jumping out of water +#define FL_WATERJUMP 2048 +// for jump debouncing +#define FL_JUMPRELEASED 4096 + +// Entity movetype values + +// never moves +#define MOVETYPE_NONE 0 +#if 0 +# define MOVETYPE_ANGLENOCLIP 1 +# define MOVETYPE_ANGLECLIP 2 +#endif +// players only +#define MOVETYPE_WALK 3 +// discrete, not real time unless fall +#define MOVETYPE_STEP 4 +#define MOVETYPE_FLY 5 +// gravity +#define MOVETYPE_TOSS 6 +// Don't clip to world, push and crush entities +#define MOVETYPE_PUSH 7 +// Don't clip to world +#define MOVETYPE_NOCLIP 8 +// fly with extra size against monsters +#define MOVETYPE_FLYMISSILE 9 +#define MOVETYPE_BOUNCE 10 +// bounce with extra size +#define MOVETYPE_BOUNCEMISSILE 11 + +/**************************************************************************** + * GAME-DEFINED CONSTANTS * + ****************************************************************************/ + +/* + Items +*/ + +// Weapons +#define IT_TSHOT 1 +#define IT_COMBOGUN 2 +#define IT_PLASMAGUN 4 +#define IT_SUPER_NAILGUN 8 +#define IT_GRENADE_LAUNCHER 16 +#define IT_ROCKET_LAUNCHER 32 +#define IT_LIGHTNING 64 +#define IT_EXTRA_WEAPON 128 + +// Ammo +#define IT_SHELLS 256 +#define IT_NAILS 512 +#define IT_ROCKETS 1024 +#define IT_CELLS 2048 + +// weird place for it, but... +#define IT_AXE 4096 + +// Armor/Health +#define IT_ARMOR1 8192 +#define IT_ARMOR2 16384 +#define IT_ARMOR3 32768 +#define IT_SUPERHEALTH 65536 + +// Keys +#define IT_KEY1 131072 +#define IT_KEY2 262144 + +// Misc. Items +#define IT_INVISIBILITY 524288 +#define IT_INVULNERABILITY 1048576 +#define IT_SUIT 2097152 +#define IT_QUAD 4194304 + +/* + GAME MODES + + DM_PREDATOR: Predator Mode - everyone's invisible + DM_DARK: Dark Mode - lights off, give everyone a flashlight + DM_LMS: Last Man Standing - start at fraglimit, you're "out" when + you reach 0. Last player with positive score wins. + DM_FFA: Free For All - all weapons, no bonuses. Temporary invuln. + DM_GIB: Gib Mode - if you die, you gib. No backpacks. + DM_AUTOSWITCH: Automatic weapon switch when you run out of ammo. +*/ +// All of these can be used together + +#define DM_PREDATOR 2 +#define DM_DARK 4 +#define DM_LMS 8 +#define DM_FFA 16 +#define DM_GIB 32 +#define DM_AUTOSWITCH 128 + +#endif // __paroxysm_rh_ diff --git a/ParoxysmII/source/plats.r b/ParoxysmII/source/plats.r new file mode 100644 index 0000000..8ebd5a1 --- /dev/null +++ b/ParoxysmII/source/plats.r @@ -0,0 +1,333 @@ +#include "config.rh" + +#include "paroxysm.rh" + +void() plat_center_touch; +void() plat_outside_touch; +void() plat_trigger_use; +void() plat_go_up; +void() plat_go_down; +void() plat_crush; +float PLAT_LOW_TRIGGER = 1; +void() plat_spawn_inside_trigger = +{ + local entity trigger; + local vector tmin, tmax; +// +// middle trigger +// + trigger = spawn(); + trigger.touch = plat_center_touch; + trigger.movetype = MOVETYPE_NONE; + trigger.solid = SOLID_TRIGGER; + trigger.enemy = self; + + tmin = self.mins + '25 25 0'; + tmax = self.maxs - '25 25 -8'; + tmin_z = tmax_z - (self.pos1_z - self.pos2_z + 8); + if (self.spawnflags & PLAT_LOW_TRIGGER) + tmax_z = tmin_z + 8; + + if (self.size_x <= 50) + { + tmin_x = (self.mins_x + self.maxs_x) / 2; + tmax_x = tmin_x + 1; + } + if (self.size_y <= 50) + { + tmin_y = (self.mins_y + self.maxs_y) / 2; + tmax_y = tmin_y + 1; + } + + setsize (trigger, tmin, tmax); +}; +void() plat_hit_top = +{ + sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_TOP; + self.think = plat_go_down; + self.nextthink = self.ltime + 3; +}; +void() plat_hit_bottom = +{ + sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise1, 1, ATTN_NORM); + self.state = STATE_BOTTOM; +}; +void() plat_go_down = +{ + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + self.state = STATE_DOWN; + SUB_CalcMove (self.pos2, self.speed, plat_hit_bottom); +}; +void() plat_go_up = +{ + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + self.state = STATE_UP; + SUB_CalcMove (self.pos1, self.speed, plat_hit_top); +}; +void() plat_center_touch = +{ + if (other.classname != "player") + return; + + if (other.health <= 0) + return; + self = self.enemy; + if (self.state == STATE_BOTTOM) + plat_go_up (); + else if (self.state == STATE_TOP) + self.nextthink = self.ltime + 1; // delay going down +}; +void() plat_outside_touch = +{ + if (other.classname != "player") + return; + if (other.health <= 0) + return; + +//dprint ("plat_outside_touch\n"); + self = self.enemy; + if (self.state == STATE_TOP) + plat_go_down (); +}; +void() plat_trigger_use = +{ + if (self.think) + return; // allready activated + plat_go_down(); +}; +void() plat_crush = +{ +//dprint ("plat_crush\n"); + other.deathtype = "squish"; + T_Damage (other, self, self, 1); + + if (self.state == STATE_UP) + plat_go_down (); + else if (self.state == STATE_DOWN) + plat_go_up (); + else + objerror ("plat_crush: bad self.state\n"); +}; +void() plat_use = +{ + self.use = SUB_Null; + if (self.state != STATE_UP) + objerror ("plat_use: not in up state"); + plat_go_down(); +}; +/*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER +speed default 150 +Plats are always drawn in the extended position, so they will light correctly. +If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat. +If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determined by the model's height. +Set "sounds" to one of the following: +1) base fast +2) chain slow +*/ +void() func_plat = +{ + if (!self.t_length) + self.t_length = 80; + if (!self.t_width) + self.t_width = 10; + if (self.sounds == 0) + self.sounds = 3;// + POX - changed from 2 +// FIX THIS TO LOAD A GENERIC PLAT SOUND + if (self.sounds == 1) + { + precache_sound ("plats/plat1.wav"); + precache_sound ("plats/plat2.wav"); + self.noise = "plats/plat1.wav"; + self.noise1 = "plats/plat2.wav"; + } + if (self.sounds == 2) + { + precache_sound ("plats/medplat1.wav"); + precache_sound ("plats/medplat2.wav"); + self.noise = "plats/medplat1.wav"; + self.noise1 = "plats/medplat2.wav"; + } +// + POX - more sounds + if (self.sounds == 3) + { + precache_sound ("doors/hydro1.wav"); + precache_sound ("doors/hydro2.wav"); + self.noise = "doors/hydro1.wav"; + self.noise1 = "doors/hydro2.wav"; + } + if (self.sounds == 4) + { + precache_sound ("doors/stndr1.wav"); + precache_sound ("doors/stndr2.wav"); + self.noise = "doors/stndr1.wav"; + self.noise1 = "doors/stndr2.wav"; + } + if (self.sounds == 5) + { + precache_sound ("doors/ddoor1.wav"); + precache_sound ("doors/ddoor2.wav"); + self.noise = "doors/ddoor2.wav"; + self.noise1 = "doors/ddoor1.wav"; + } +// - POX + self.mangle = self.angles; + self.angles = '0 0 0'; + self.classname = "plat"; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + setorigin (self, self.origin); + setmodel (self, self.model); + setsize (self, self.mins , self.maxs); + self.blocked = plat_crush; + if (!self.speed) + self.speed = 150; +// pos1 is the top position, pos2 is the bottom + self.pos1 = self.origin; + self.pos2 = self.origin; + if (self.height) + self.pos2_z = self.origin_z - self.height; + else + self.pos2_z = self.origin_z - self.size_z + 8; + self.use = plat_trigger_use; + plat_spawn_inside_trigger (); // the "start moving" trigger + if (self.targetname) + { + self.state = STATE_UP; + self.use = plat_use; + } + else + { + setorigin (self, self.pos2); + self.state = STATE_BOTTOM; + } +}; +//============================================================================ +void() train_next; +void() func_train_find; +void() train_blocked = +{ + if (time < self.attack_finished) + return; + self.attack_finished = time + 0.5; + other.deathtype = "squish"; + T_Damage (other, self, self, self.dmg); +}; +void() train_use = +{ + if (self.think != func_train_find) + return; // already activated + train_next(); +}; +void() train_wait = +{ + if (self.wait) + { + self.nextthink = self.ltime + self.wait; + sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self.noise, 1, ATTN_NORM); + } + else + self.nextthink = self.ltime + 0.1; + + self.think = train_next; +}; +void() train_next = +{ + local entity targ; + targ = find (world, targetname, self.target); + self.target = targ.target; + if (!self.target) + objerror ("train_next: no next target"); + if (targ.wait) + self.wait = targ.wait; + else + self.wait = 0; + sound (self, CHAN_VOICE, self.noise1, 1, ATTN_NORM); + SUB_CalcMove (targ.origin - self.mins, self.speed, train_wait); +}; +void() func_train_find = +{ + local entity targ; + targ = find (world, targetname, self.target); + self.target = targ.target; + setorigin (self, targ.origin - self.mins); + if (!self.targetname) + { // not triggered, so start immediately + self.nextthink = self.ltime + 0.1; + self.think = train_next; + } +}; +/*QUAKED func_train (0 .5 .8) ? +Trains are moving platforms that players can ride. +The targets origin specifies the min point of the train at each corner. +The train spawns at the first target it is pointing at. +If the train is the target of a button or trigger, it will not begin moving until activated. +speed default 100 +dmg default 2 +sounds +1) ratchet metal +*/ +void() func_train = +{ + if (!self.speed) + self.speed = 100; + if (!self.target) + objerror ("func_train without a target"); + if (!self.dmg) + self.dmg = 2; + if (self.sounds == 0) + { + self.noise = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + } + if (self.sounds == 1) + { + self.noise = ("plats/train2.wav"); + precache_sound ("plats/train2.wav"); + self.noise1 = ("plats/train1.wav"); + precache_sound ("plats/train1.wav"); + } + self.cnt = 1; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.blocked = train_blocked; + self.use = train_use; + self.classname = "train"; + setmodel (self, self.model); + setsize (self, self.mins , self.maxs); + setorigin (self, self.origin); +// start trains on the second frame, to make sure their targets have had +// a chance to spawn + self.nextthink = self.ltime + 0.1; + self.think = func_train_find; +}; +/*QUAKED misc_teleporttrain (0 .5 .8) (-8 -8 -8) (8 8 8) +This is used for the final bos +*/ +void() misc_teleporttrain = +{ + if (!self.speed) + self.speed = 100; + if (!self.target) + objerror ("func_train without a target"); + self.cnt = 1; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_PUSH; + self.blocked = train_blocked; + self.use = train_use; + self.avelocity = '100 200 300'; + self.noise = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + self.noise1 = ("misc/null.wav"); + precache_sound ("misc/null.wav"); + precache_model2 ("progs/teleport.mdl"); + setmodel (self, "progs/teleport.mdl"); + setsize (self, self.mins , self.maxs); + setorigin (self, self.origin); +// start trains on the second frame, to make sure their targets have had +// a chance to spawn + self.nextthink = self.ltime + 0.1; + self.think = func_train_find; +}; diff --git a/ParoxysmII/source/player.r b/ParoxysmII/source/player.r new file mode 100644 index 0000000..0602d7e --- /dev/null +++ b/ParoxysmII/source/player.r @@ -0,0 +1,907 @@ + +#include "config.rh" +#include "paroxysm.rh" + +void() bubble_bob; +/* +============================================================================== +PLAYER +============================================================================== +*/ +$cd /raid/quake/id1/models/player_4 +$origin 0 -6 24 +$base base +$skin skin + +// +// running +// +$frame axrun1 axrun2 axrun3 axrun4 axrun5 axrun6 +$frame rockrun1 rockrun2 rockrun3 rockrun4 rockrun5 rockrun6 + +// +// standing +// +$frame stand1 stand2 stand3 stand4 stand5 + +$frame axstnd1 axstnd2 axstnd3 axstnd4 axstnd5 axstnd6 +$frame axstnd7 axstnd8 axstnd9 axstnd10 axstnd11 axstnd12 + +// +// pain +// +$frame axpain1 axpain2 axpain3 axpain4 axpain5 axpain6 +$frame pain1 pain2 pain3 pain4 pain5 pain6 + +// +// death +// +$frame axdeth1 axdeth2 axdeth3 axdeth4 axdeth5 axdeth6 +$frame axdeth7 axdeth8 axdeth9 + +$frame deatha1 deatha2 deatha3 deatha4 deatha5 deatha6 deatha7 deatha8 +$frame deatha9 deatha10 deatha11 + +$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8 +$frame deathb9 + +$frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8 +$frame deathc9 deathc10 deathc11 deathc12 deathc13 deathc14 deathc15 + +$frame deathd1 deathd2 deathd3 deathd4 deathd5 deathd6 deathd7 +$frame deathd8 deathd9 + +$frame deathe1 deathe2 deathe3 deathe4 deathe5 deathe6 deathe7 +$frame deathe8 deathe9 + +// +// attacks +// +$frame nailatt1 nailatt2 + +$frame light1 light2 + +$frame rockatt1 rockatt2 rockatt3 rockatt4 rockatt5 rockatt6 + +$frame shotatt1 shotatt2 shotatt3 shotatt4 shotatt5 shotatt6 + +$frame axatt1 axatt2 axatt3 axatt4 axatt5 axatt6 +$frame axattb1 axattb2 axattb3 axattb4 axattb5 axattb6 +$frame axattc1 axattc2 axattc3 axattc4 axattc5 axattc6 +$frame axattd1 axattd2 axattd3 axattd4 axattd5 axattd6 + +/* +============================================================================== +PLAYER +============================================================================== +*/ +void() player_run; +void() player_stand1 = [$axstnd1, player_stand1] +{ + self.weaponframe = 0; + if (self.velocity_x || self.velocity_y) { + self.walkframe = 0; + player_run (); + return; + } + + if (self.weapon == IT_AXE) { + if (self.walkframe >= 12) + self.walkframe = 0; + self.frame = $axstnd1 + self.walkframe; + } else { + if (self.walkframe >= 5) + self.walkframe = 0; + self.frame = $stand1 + self.walkframe; + } + self.walkframe++; +}; + +void() player_run = [$rockrun1, player_run] +{ + self.weaponframe = 0; + if (!self.velocity_x && !self.velocity_y) { + self.walkframe = 0; + player_stand1 (); + return; + } + + if (self.weapon == IT_AXE) { + if (self.walkframe == 6) + self.walkframe = 0; + self.frame = $axrun1 + self.walkframe; + } else { + if (self.walkframe == 6) + self.walkframe = 0; + self.frame = self.frame + self.walkframe; + } + + // footstep sounds + self.spawnsilent += vlen (self.origin - self.old_velocity); + self.old_velocity = self.origin; + + if (self.waterlevel < 3 && self.classname == "player") { // no footsteps underwater or for observer + if (self.spawnsilent > 95) { + local float r; + + if (self.spawnsilent > 190) + self.spawnsilent = 0; + else + self.spawnsilent = 0.5 * (self.spawnsilent - 95); + + r = random (); + + if (self.waterlevel) { + if (r < 0.25) + sound (self, CHAN_AUTO, "misc/water1.wav", 1, ATTN_NORM); + else if (r < 0.5) + sound (self, CHAN_AUTO, "misc/water2.wav", 1, ATTN_NORM); + else if (r < 0.75) + sound (self, CHAN_AUTO, "misc/owater2.wav", 0.75, ATTN_NORM); + } else if (self.flags & FL_ONGROUND) { + local float speed, vol; + + speed = vlen (self.velocity); + vol = speed * 0.002; // Scale footstep volume by speed + if (vol > 1) + vol = 1; + if (vol < 0.1) + return; // don't make a sound + + if (r < 0.25) + sound (self, CHAN_AUTO, "misc/foot1.wav", vol, ATTN_NORM); + else if (r < 0.5) + sound (self, CHAN_AUTO, "misc/foot2.wav", vol, ATTN_NORM); + else if (r < 0.75) + sound (self, CHAN_AUTO, "misc/foot3.wav", vol, ATTN_NORM); + else + sound (self, CHAN_AUTO, "misc/foot4.wav", vol, ATTN_NORM); + } + } + } + + self.walkframe++; +}; + +void () muzzleflash = +{ + WriteByte (MSG_MULTICAST, SVC_MUZZLEFLASH); + WriteEntity (MSG_MULTICAST, self); + multicast (self.origin, MULTICAST_PVS); +}; + +// POX - used for tShot and ComboGun primary triggers +void() player_shot1 = [$shotatt1, player_shot2] +{ + self.weaponframe = 1; + muzzleflash (); +}; + +void() player_shot2 = [$shotatt2, player_shot3] {self.weaponframe = 2;}; +void() player_shot3 = [$shotatt3, player_shot4] {self.weaponframe = 3;}; +void() player_shot4 = [$shotatt4, player_shot5] {self.weaponframe = 4;}; +void() player_shot5 = [$shotatt5, player_shot6] {self.weaponframe = 5;}; +void() player_shot6 = [$shotatt6, player_run] {self.weaponframe = 6;}; + +// POX - New Frame Macro For tShot 3 barrel fire +void() player_tshot1 = [$shotatt1, player_tshot2] {self.weaponframe = 1; muzzleflash();}; +void() player_tshot2 = [$shotatt2, player_tshot3] {self.weaponframe = 16;}; // triple flare frame +void() player_tshot3 = [$shotatt3, player_tshot4] {self.weaponframe = 3;}; +void() player_tshot4 = [$shotatt4, player_tshot5] {self.weaponframe = 4;}; +void() player_tshot5 = [$shotatt5, player_tshot6] {self.weaponframe = 5;}; +void() player_tshot6 = [$shotatt6, player_run] {self.weaponframe = 6;}; + +// POX - New Frame Macro For tShot 3 barrel prime +void() player_reshot1 = [$shotatt1, player_reshot2] {self.weaponframe = 7;}; +void() player_reshot2 = [$shotatt3, player_reshot3] {self.weaponframe = 8;}; +void() player_reshot3 = [$shotatt4, player_reshot4] {self.weaponframe = 9;}; +void() player_reshot4 = [$shotatt5, player_reshot5] {self.weaponframe = 10;}; +void() player_reshot5 = [$shotatt6, player_reshot6] {self.weaponframe = 11;}; +void() player_reshot6 = [$shotatt5, player_reshot7] {self.weaponframe = 12;}; +void() player_reshot7 = [$shotatt4, player_reshot8] {self.weaponframe = 13;}; +void() player_reshot8 = [$shotatt3, player_reshot9] {self.weaponframe = 14;}; +void() player_reshot9 = [$shotatt1, player_run] {self.weaponframe = 15;}; + +// POX - New Frame Macro For ComboGun Second Trigger (Impact Grenades) +void() player_gshot1 = [$shotatt1, player_gshot2] {self.weaponframe = 2; muzzleflash();}; +void() player_gshot2 = [$shotatt2, player_gshot3] {self.weaponframe = 3;}; +void() player_gshot3 = [$shotatt3, player_gshot4] {self.weaponframe = 4;}; +void() player_gshot4 = [$shotatt4, player_gshot5] {self.weaponframe = 5;}; +void() player_gshot5 = [$shotatt5, player_run] {self.weaponframe = 6;}; + +// POX - New Frame Macro For Nailgun Second Trigger (Shrapnel Bomb) +void() player_shrap1 = [$nailatt1, player_shrap2] {muzzleflash ();}; +void() player_shrap2 = [$nailatt2, player_run] {}; + +void() player_axe1 = [$axatt1, player_axe2] {self.weaponframe=1;}; +void() player_axe2 = [$axatt2, player_axe3] {self.weaponframe=2;}; +void() player_axe3 = [$axatt3, player_axe4] {self.weaponframe=3;W_FireAxe();}; +void() player_axe4 = [$axatt4, player_run] {self.weaponframe=4;}; + +void() player_axeb1 = [$axattb1, player_axeb2] {self.weaponframe=5;}; +void() player_axeb2 = [$axattb2, player_axeb3] {self.weaponframe=6;}; +void() player_axeb3 = [$axattb3, player_axeb4] {self.weaponframe=7;W_FireAxe();}; +void() player_axeb4 = [$axattb4, player_run] {self.weaponframe=8;}; + +void() player_axec1 = [$axattc1, player_axec2] {self.weaponframe=1;}; +void() player_axec2 = [$axattc2, player_axec3] {self.weaponframe=2;}; +void() player_axec3 = [$axattc3, player_axec4] {self.weaponframe=3;W_FireAxe();}; +void() player_axec4 = [$axattc4, player_run] {self.weaponframe=4;}; + +void() player_axed1 = [$axattd1, player_axed2] {self.weaponframe=5;}; +void() player_axed2 = [$axattd2, player_axed3] {self.weaponframe=6;}; +void() player_axed3 = [$axattd3, player_axed4] {self.weaponframe=7;W_FireAxe();}; +void() player_axed4 = [$axattd4, player_run] {self.weaponframe=8;}; + +// NailGun animation +void() player_nail1 = [$nailatt1, player_nail2] +{ + if (self.st_nailgun > time) + return; + + if (self.ammo_nails < 1) { + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_nailgun = time + 0.4; + player_run (); + return; + } + + muzzleflash (); + + if (!self.button0) { + player_run (); + return; + } + + if (++self.weaponframe == 9) + self.weaponframe = 1; + + SuperDamageSound (); + W_FireNails (3); + + if (self.flags & FL_ONGROUND) + self.velocity += v_forward * -20; + + self.st_nailgun = time + 0.1; +}; + +void() player_nail2 = [$nailatt2, player_nail1] +{ + if (self.st_nailgun > time) + return; + + if (self.ammo_nails < 1) { + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_nailgun = time + 0.4; + player_run (); + return; + } + + muzzleflash (); + if (!self.button0) { + player_run (); + return; + } + + if (++self.weaponframe == 9) + self.weaponframe = 1; + + SuperDamageSound(); + W_FireNails (-3); + + if (self.flags & FL_ONGROUND) + self.velocity += v_forward * -20; + + self.st_nailgun = time + 0.1; +}; + +//============================================================================ +// POX - PlasmaGun animation - In sync with Paroxysm v1.1 +void() player_plasma1 =[$nailatt1, player_plasma2 ] +{ + if (self.st_plasma > time) + return; + + if (self.ammo_cells < 1) { + sound (self, CHAN_WEAPON, "weapons/mfire2.wav", 1, ATTN_NORM); + player_run (); + self.st_plasma = time + 0.4; + return; + } + + muzzleflash (); + + if (!self.button0) { + player_run (); + return; + } + + self.weaponframe = 1; + self.LorR = 1; + + SuperDamageSound (); + W_FirePlasma (6); + + if (self.flags & FL_ONGROUND) + self.velocity += v_forward * -20; + + self.st_plasma = time + 0.1; +}; + +void() player_plasma2 =[$nailatt2, player_plasma1 ] +{ + if (self.st_plasma > time) + return; + + if (self.ammo_cells < 1) { + sound (self, CHAN_WEAPON, "weapons/mfire2.wav", 1, ATTN_NORM); + player_run (); + self.st_plasma = time + 0.4; + return; + } + + muzzleflash (); + + if (!self.button0) { + player_run (); + return; + } + + self.weaponframe = 2; + self.LorR = 0; + + SuperDamageSound(); + W_FirePlasma (-7); + + if (self.flags & FL_ONGROUND) + self.velocity += v_forward * -20; + + self.st_plasma = time + 0.1; +}; + +//============================================================================ +// POX - MegaPlasma Burst +void() player_mplasma1 =[$rockatt1, player_mplasma2 ] {self.weaponframe=0;}; +void() player_mplasma2 =[$rockatt2, player_mplasma3 ] {}; +void() player_mplasma3 =[$rockatt3, player_run ] {}; + +// POX - Grenadelauncher Animation +void() player_grenade1 =[$rockatt1, player_grenade2 ] {self.weaponframe=1;muzzleflash();}; +void() player_grenade2 =[$rockatt2, player_grenade3 ] {self.weaponframe=2;}; +void() player_grenade3 =[$rockatt3, player_grenade4 ] {self.weaponframe=3;}; +void() player_grenade4 =[$rockatt4, player_grenade5 ] {self.weaponframe=4;}; +void() player_grenade5 =[$rockatt5, player_grenade6 ] {self.weaponframe=5;}; +void() player_grenade6 =[$rockatt6, player_run ] {self.weaponframe=6;}; + +// POX - Annihilator firing sequence +void() player_rocket1 = [$rockatt1, player_rocket2] +{ + + self.weaponframe = 1; + W_FireRocket ('0 0 16'); + + // sound is timed for double shot (two single shot sounds had inconsistant results) + sound (self, CHAN_WEAPON, "weapons/rhino.wav", 1, ATTN_NORM); + + if (self.flags & FL_ONGROUND) + self.velocity = v_forward * -25; + + msg_entity = self; + + WriteByte (MSG_ONE, SVC_BIGKICK); + + muzzleflash (); +}; + +void() player_rocket2 = [$rockatt2, player_rocket3] +{ + self.weaponframe = 2; + W_FireRocket('0 0 24'); +}; + +void() player_rocket3 = [$rockatt3, player_run ] {self.weaponframe=3;}; + +// POX - Annihilator Reload sequence +void() player_rocketload1 = [$rockatt1, player_rocketload2] {self.weaponframe = 4;}; +void() player_rocketload2 = [$rockatt3, player_rocketload3] {self.weaponframe = 5;}; +void() player_rocketload3 = [$rockatt4, player_rocketload4] {self.weaponframe = 6;}; +void() player_rocketload4 = [$rockatt5, player_rocketload5] {self.weaponframe = 7;}; +void() player_rocketload5 = [$rockatt6, player_rocketload6] {self.weaponframe = 8;}; +void() player_rocketload6 = [$rockatt4, player_run] {self.weaponframe = 9;}; + +void (float num_bubbles) DeathBubbles; + +void() PainSound = +{ + local float rs = random (); + + if (self.health < 0) + return; + + if (damage_attacker.classname == "teledeath") { + sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE); + return; + } + + // water pain sounds + if (self.watertype == CONTENT_WATER && self.waterlevel == 3) { + DeathBubbles (1); + + if (rs > 0.5) + sound (self, CHAN_VOICE, "player/drown1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "player/drown2.wav", 1, ATTN_NORM); + return; + } + + if (self.watertype == CONTENT_SLIME) { // slime pain sounds + // FIXME: put in some steam here + if (rs > 0.5) + sound (self, CHAN_VOICE, "player/lburn1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "player/lburn2.wav", 1, ATTN_NORM); + return; + } + + if (self.watertype == CONTENT_LAVA) { + if (rs > 0.5) + sound (self, CHAN_VOICE, "player/lburn1.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "player/lburn2.wav", 1, ATTN_NORM); + return; + } + + if (self.pain_finished > time) { + self.axhitme = 0; + return; + } + + self.pain_finished = time + 0.5; + + // don't make multiple pain sounds right after each other + + if (self.axhitme == 1) { // axe pain sound + self.axhitme = 0; + sound (self, CHAN_VOICE, "player/axhit1.wav", 1, ATTN_NORM); + return; + } + + rs = rint ((random () * 5) + 1); + self.noise = ""; + + if (rs == 1) + self.noise = "player/pain1.wav"; + else if (rs == 2) + self.noise = "player/pain2.wav"; + else if (rs == 3) + self.noise = "player/pain3.wav"; + else if (rs == 4) + self.noise = "player/pain4.wav"; + else if (rs == 5) + self.noise = "player/pain5.wav"; + else + self.noise = "player/pain6.wav"; + + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + return; +}; + +void() player_pain1 = [$pain1, player_pain2] {PainSound (); self.weaponframe = 0;}; +void() player_pain2 = [$pain2, player_pain3] {}; +void() player_pain3 = [$pain3, player_pain4] {}; +void() player_pain4 = [$pain4, player_pain5] {}; +void() player_pain5 = [$pain5, player_pain6] {}; +void() player_pain6 = [$pain6, player_run] {}; + +void() player_axpain1 = [$axpain1, player_axpain2] {PainSound (); self.weaponframe = 0;}; +void() player_axpain2 = [$axpain2, player_axpain3] {}; +void() player_axpain3 = [$axpain3, player_axpain4] {}; +void() player_axpain4 = [$axpain4, player_axpain5] {}; +void() player_axpain5 = [$axpain5, player_axpain6] {}; +void() player_axpain6 = [$axpain6, player_run] {}; + +void() player_pain = +{ + if (self.weaponframe) + return; + + if (self.invisible_finished > time) + return; // eyes don't have pain frames + + if (self.weapon == IT_AXE) + player_axpain1 (); + else + player_pain1 (); +}; + +void() player_diea1; +void() player_dieb1; +void() player_diec1; +void() player_died1; +void() player_diee1; +void() player_die_ax1; + +void() DeathBubblesSpawn = +{ + local entity bubble; + + if (self.owner.waterlevel != 3) + return; + + bubble = spawn (); + + setmodel (bubble, "progs/s_bubble.spr"); + setorigin (bubble, self.owner.origin + '0 0 24'); + setsize (bubble, '-8 -8 -8', '8 8 8'); + + bubble.movetype = MOVETYPE_NOCLIP; + bubble.solid = SOLID_NOT; + bubble.velocity = '0 0 15'; + + bubble.classname = "bubble"; + bubble.cnt = 0; + bubble.frame = 0; + + bubble.nextthink = time + 0.5; + bubble.think = bubble_bob; + + self.think = DeathBubblesSpawn; + self.nextthink = time + 0.1; + + if (++self.air_finished >= self.bubble_count) + remove (self); +}; + +void (float num_bubbles) DeathBubbles = +{ + local entity bubble_spawner; + + bubble_spawner = spawn (); + setorigin (bubble_spawner, self.origin); + + bubble_spawner.owner = self; + + bubble_spawner.air_finished = 0; + bubble_spawner.bubble_count = num_bubbles; + bubble_spawner.movetype = MOVETYPE_NONE; + bubble_spawner.solid = SOLID_NOT; + + bubble_spawner.think = DeathBubblesSpawn; + bubble_spawner.nextthink = time + 0.1; + + return; +}; + +void() DeathSound = +{ + local float rs; + + if (self.waterlevel == 3) { // water death sounds + DeathBubbles (5); + sound (self, CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_NONE); + return; + } + + rs = rint ((random() * 4) + 1); + if (rs == 1) + self.noise = "player/death1.wav"; + if (rs == 2) + self.noise = "player/death2.wav"; + if (rs == 3) + self.noise = "player/death3.wav"; + if (rs == 4) + self.noise = "player/death4.wav"; + if (rs == 5) + self.noise = "player/death5.wav"; + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NONE); + return; +}; + +void() PlayerDead = +{ + self.nextthink = -1; // allow respawn after a certain time + self.deadflag = DEAD_DEAD; +}; + +vector(float dm) VelocityForDamage = +{ + local vector v; + + if (vlen (damage_inflictor.velocity) > 0) { + v = 0.5 * damage_inflictor.velocity; + v += 25 * normalize (self.origin - damage_inflictor.origin); + v_x += 200 * crandom (); + v_y += 200 * crandom (); + v_z = 100 + 240 * random(); +// dprint ("Velocity gib\n"); + } else { + v_x = 100 * crandom(); + v_y = 100 * crandom(); + v_z = 200 + 100 * random(); + } + +// v_x = 100 * crandom(); +// v_y = 100 * crandom(); +// v_z = 200 + 100 * random(); + + if (dm > -50) { +// dprint ("level 1\n"); + v *= 0.7; + } else if (dm > -200) { +// dprint ("level 3\n"); + v *= 2; + } else { + v *= 10; + } + + return v; +}; + + +void (string gibname, float dm) ThrowGib = +{ + local entity new; + + new = spawn (); + new.origin = self.origin; + setmodel (new, gibname); + setsize (new, '0 0 0', '0 0 0'); + + // wind tunnels + new.solid = SOLID_TRIGGER; + + if (self.waterlevel > 0) { // make gibs float in water + new.classname = "gib"; + new.movetype = MOVETYPE_FLY; + new.velocity = '100 3 15'; + new.think = bubble_bob; + new.nextthink = time + 0.5; + new.cnt = 0; + } else { + new.velocity = VelocityForDamage (dm); + new.movetype = MOVETYPE_BOUNCE; + + new.avelocity_x = random () * 600; + new.avelocity_y = random () * 600; + new.avelocity_z = random () * 600; + + new.think = SUB_Remove; + new.ltime = time; + new.nextthink = time + 10 + random () * 10; + new.flags = 0; + } + + new.frame = 0; +}; + + +void (string gibname, float dm) ThrowHead = +{ + setmodel (self, gibname); + self.frame = 0; + self.takedamage = DAMAGE_NO; + + // wind tunnels + self.solid = SOLID_TRIGGER; + + if (self.waterlevel > 0) { // float 'em + self.movetype = MOVETYPE_FLY; + + self.velocity = '100 3 15'; + + self.think = bubble_bob; + self.nextthink = time + 0.5; + self.cnt = 0; + } else { + self.movetype = MOVETYPE_BOUNCE; + + self.velocity = VelocityForDamage (dm); + self.avelocity = crandom () * '0 600 0'; + + self.nextthink = -1; + } + self.view_ofs = '0 0 8'; + setsize (self, '-16 -16 0', '16 16 56'); + self.origin_z -= 24; + + self.flags &= ~FL_ONGROUND; +}; + +void() GibPlayer = +{ + // make a nice gib in Gib mode + if (deathmatch & DM_GIB) + self.health -= 40; + + ThrowHead ("progs/h_player.mdl", self.health); + ThrowGib ("progs/gib1.mdl", self.health); + ThrowGib ("progs/gib2.mdl", self.health); + ThrowGib ("progs/gib3.mdl", self.health); + + self.deadflag = DEAD_DEAD; + + if (damage_attacker.classname == "teledeath") { + sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE); + return; + } + + if (damage_attacker.classname == "teledeath2") { + sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE); + return; + } + + if (random () < 0.5) + sound (self, CHAN_VOICE, "player/gib.wav", 1, ATTN_NONE); + else + sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NONE); +}; + +void() PlayerDie = +{ + local float i; + local string s; + + self.items &= ~IT_INVISIBILITY; + + if (stof (infokey (world, "dq"))) { // drop quad? + if (self.super_damage_finished > 0) { + DropQuad (self.super_damage_finished - time); + s = self.netname + + " lost a Quad with " + + ftos (rint (self.super_damage_finished - time)) + + " seconds remaining!\n"; + + BPRINT (PRINT_MEDIUM, s); + } + } + + if (stof (infokey (world, "dc"))) { + if (self.invisible_finished > 0) { + DropRing (self.invisible_finished - time); + s = self.netname + + " lost a Cloaking Device with " + + ftos (rint (self.invisible_finished - time)) + + " seconds remaining!\n"; + BPRINT (PRINT_LOW, s); + } + } + + self.invisible_finished = 0; // don't die as eyes + self.invincible_finished = 0; + self.super_damage_finished = 0; + self.radsuit_finished = 0; + self.modelindex = modelindex_player; // don't use eyes + + // + POX - moved below gib check (no packs when gibbed!) + //DropBackpack(); + + // + POX v1.11 - clear the target print so it doesn't obscure the scoreboard + if (self.target_id_toggle) + centerprint (self, ""); + + self.weaponmodel = ""; + self.view_ofs = '0 0 -8'; + self.deadflag = DEAD_DYING; + self.solid = SOLID_NOT; + self.flags &= ~FL_ONGROUND; + self.movetype = MOVETYPE_TOSS; + + if (self.velocity_z < 10) + self.velocity_z = self.velocity_z + random () * 300; + + // + POX check for gib mode as well as regular gib + if (self.health < -40 || (deathmatch & DM_GIB)) { + GibPlayer (); + return; + } + + DropBackpack (); + + DeathSound (); + + self.angles_x = 0; + self.angles_z = 0; + + if (self.weapon == IT_AXE) { + player_die_ax1 (); + return; + } + + i = 1 + floor (random () * 5); + switch (i) { + case 1: + player_diea1 (); + break; + case 2: + player_dieb1 (); + break; + case 3: + player_diec1 (); + break; + case 4: + player_died1 (); + break; + default: + player_diee1 (); + } +}; + +void() set_suicide_frame = +{ // used by kill command and diconnect command + if (self.model != "progs/player.mdl") + return; // allready gibbed + + self.frame = $deatha11; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_TOSS; + self.deadflag = DEAD_DEAD; + self.nextthink = -1; +}; + +void() player_diea1 = [$deatha1, player_diea2] {}; +void() player_diea2 = [$deatha2, player_diea3] {}; +void() player_diea3 = [$deatha3, player_diea4] {}; +void() player_diea4 = [$deatha4, player_diea5] {}; +void() player_diea5 = [$deatha5, player_diea6] {}; +void() player_diea6 = [$deatha6, player_diea7] {}; +void() player_diea7 = [$deatha7, player_diea8] {}; +void() player_diea8 = [$deatha8, player_diea9] {}; +void() player_diea9 = [$deatha9, player_diea10] {}; +void() player_diea10 = [$deatha10, player_diea11] {}; +void() player_diea11 = [$deatha11, player_diea11] {PlayerDead ();}; + +void() player_dieb1 = [$deathb1, player_dieb2] {}; +void() player_dieb2 = [$deathb2, player_dieb3] {}; +void() player_dieb3 = [$deathb3, player_dieb4] {}; +void() player_dieb4 = [$deathb4, player_dieb5] {}; +void() player_dieb5 = [$deathb5, player_dieb6] {}; +void() player_dieb6 = [$deathb6, player_dieb7] {}; +void() player_dieb7 = [$deathb7, player_dieb8] {}; +void() player_dieb8 = [$deathb8, player_dieb9] {}; +void() player_dieb9 = [$deathb9, player_dieb9] {PlayerDead ();}; + +void() player_diec1 = [$deathc1, player_diec2] {}; +void() player_diec2 = [$deathc2, player_diec3] {}; +void() player_diec3 = [$deathc3, player_diec4] {}; +void() player_diec4 = [$deathc4, player_diec5] {}; +void() player_diec5 = [$deathc5, player_diec6] {}; +void() player_diec6 = [$deathc6, player_diec7] {}; +void() player_diec7 = [$deathc7, player_diec8] {}; +void() player_diec8 = [$deathc8, player_diec9] {}; +void() player_diec9 = [$deathc9, player_diec10] {}; +void() player_diec10 = [$deathc10, player_diec11] {}; +void() player_diec11 = [$deathc11, player_diec12] {}; +void() player_diec12 = [$deathc12, player_diec13] {}; +void() player_diec13 = [$deathc13, player_diec14] {}; +void() player_diec14 = [$deathc14, player_diec15] {}; +void() player_diec15 = [$deathc15, player_diec15] {PlayerDead();}; + +void() player_died1 = [$deathd1, player_died2] {}; +void() player_died2 = [$deathd2, player_died3] {}; +void() player_died3 = [$deathd3, player_died4] {}; +void() player_died4 = [$deathd4, player_died5] {}; +void() player_died5 = [$deathd5, player_died6] {}; +void() player_died6 = [$deathd6, player_died7] {}; +void() player_died7 = [$deathd7, player_died8] {}; +void() player_died8 = [$deathd8, player_died9] {}; +void() player_died9 = [$deathd9, player_died9] {PlayerDead ();}; + +void() player_diee1 = [$deathe1, player_diee2] {}; +void() player_diee2 = [$deathe2, player_diee3] {}; +void() player_diee3 = [$deathe3, player_diee4] {}; +void() player_diee4 = [$deathe4, player_diee5] {}; +void() player_diee5 = [$deathe5, player_diee6] {}; +void() player_diee6 = [$deathe6, player_diee7] {}; +void() player_diee7 = [$deathe7, player_diee8] {}; +void() player_diee8 = [$deathe8, player_diee9] {}; +void() player_diee9 = [$deathe9, player_diee9] {PlayerDead();}; + +void() player_die_ax1 = [$axdeth1, player_die_ax2] {}; +void() player_die_ax2 = [$axdeth2, player_die_ax3] {}; +void() player_die_ax3 = [$axdeth3, player_die_ax4] {}; +void() player_die_ax4 = [$axdeth4, player_die_ax5] {}; +void() player_die_ax5 = [$axdeth5, player_die_ax6] {}; +void() player_die_ax6 = [$axdeth6, player_die_ax7] {}; +void() player_die_ax7 = [$axdeth7, player_die_ax8] {}; +void() player_die_ax8 = [$axdeth8, player_die_ax9] {}; +void() player_die_ax9 = [$axdeth9, player_die_ax9] {PlayerDead();}; diff --git a/ParoxysmII/source/poxdefs.r b/ParoxysmII/source/poxdefs.r new file mode 100644 index 0000000..4ae9a09 --- /dev/null +++ b/ParoxysmII/source/poxdefs.r @@ -0,0 +1,62 @@ +#include "config.rh" +#include "paroxysm.rh" + +// POX - bunch of new variable declarations and prototypes for POXworld +//New stuff for timing second triggers and other weapon stuff +.float prime_tshot; //this is set when tShot is primed for a triple barrel fire +.float st_tshotload; //used to time out the tSot's trigger during reload +.float st_sshotgun; //next attack for ComboGun +.float st_pball; //next attack for Impact grenade +.float which_ammo; //used to clean up ammo switching for ComboGun +.float st_mplasma; //next attack for MegaPlasma Burst +.float st_plasma; //next attack for PlasmaGun +.float LorR; //sets which barrel to use for next PlasmaGun shot +.float st_mine; //next attack for PhaseMine +.float st_grenade; //next attack for Grenades +.float no_obj; //fixes a problem with 'hanging' mines on breakable objects destroyed be something else +.entity spawnmaster; +.entity lastowner; +.float st_nailgun; //next attack for Nailgun +.float st_shrapnel; //next attack for ShrapnelBomb +.float shrap_detonate; //decide whether to launch or detonate a ShrapnelBomb +.float shrap_time; //holds the bombs time out (2 minutes then boom) - just in case owner died +.float reload_rocket; //keeps count of rockets fired +.float st_rocketload; //used to time out the rhino's trigger during reload +.float missfire_finished; //used to seperate attacks and missfires (POXnote : DO I STILL NEED THIS?) +.float nobleed; //set to TRUE for triggers, breakable objects, buttons, etc... +// Footsteps +.float spawnsilent; +.vector old_velocity; +//POX v1.2 REMOVED EARTHQUAKE! (not suitable for DM) +void() func_earthquake = {remove(self);}; +//Water Movement +.float uwmuffle; //underwater muffle sound timeout +.float onwsound; //on water sound timeout +.float outwsound; //head out of water sound flag +.float inwsound; //head in water sound flag +//New DM option constants +float fraglimit_LMS; // stores the fraglimit at worldspawn so it can't be changed during a game +float lms_plrcount; // Keeps track of the number of players in an LMS game +float lms_gameover; // Lets CheckRules know when one or zero players are left +.float LMS_registered; // prevents players from being counted more than once +.float LMS_observer; // Bump think to Observer code if TRUE (1 = late, 2 = killed) +.float LMS_observer_fov; // Stores observer's current fov +.float LMS_zoom; // 1 = zoom in, 2 = zoom out, 0 = stop +.float LMS_observer_time; // times the display of observer instructions +//Dark Mode stuff... +.float flash_flag; // flashlight toggle (no user toggle) +.entity flash; // flash entity +// Moved here for use in weapons.qc +float intermission_running; +.float gl_fix; //a hack for toggling gl_flashblend +//Used by Target ID impulse +.float target_id_finished; +.float target_id_toggle; +.float target_id_same; +.entity last_target_id; +void(entity client, string s1, string s2, string s3, string s4) centerprint4 = #73; +//POX v1.2 - improved reseting of colour_light +.float cshift_finished; +.float cshift_off; +//POX 1.2 - allows idtarget state to be saved across levelchanges +.float target_id_temp; diff --git a/ParoxysmII/source/progs.src b/ParoxysmII/source/progs.src new file mode 100644 index 0000000..c646053 --- /dev/null +++ b/ParoxysmII/source/progs.src @@ -0,0 +1,39 @@ +qwprogs.dat + +defs.r +builtins.r // Builtin functions + +poxdefs.r // POX - some extra prototypes and variables + +subs.r +combat.r +items.r + +sectrig.r // POX - Second Trigger Functions +targid.r // POX - Target Identifier + +weapons.r +world.r + +flash.r // POX - Flashlight code (for DarkMode) +observe.r // POX - Last Man Standing Observer + +client.r +spectate.r +player.r +doors.r +buttons.r +triggers.r +plats.r +misc.r + +server.r + +// POX - new files below... +shields.r //Regen Station +wall.r //Gibbable object +dynlight.r //Dynamic Lighting + +specfx.r //Misc Special Entities + + diff --git a/ParoxysmII/source/quakeworld.r b/ParoxysmII/source/quakeworld.r new file mode 100644 index 0000000..6c4eec1 --- /dev/null +++ b/ParoxysmII/source/quakeworld.r @@ -0,0 +1,225 @@ +/* + quakeworld.r + + QuakeWorld constant definitions + + Copyright (C) 2002 Dusk to Dawn Computing, Inc. + Portions Copyright (C) 1996-1997 Id Software, Inc. + + This program 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 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#define FALSE 0 +#define TRUE 1 + +/* Entity flags, for the .flags field (a bit field) */ +#define FL_FLY 1 // Entity is flying +#define FL_SWIM 2 +#define FL_CLIENT 8 // Set for all client entities +#define FL_INWATER 16 // Set for entities that are in water +#define FL_MONSTER 32 // Entity is a monster +#define FL_GODMODE 64 // Entity has "god mode" +#define FL_NOTARGET 128 // Entity is not targeted by monsters +#define FL_ITEM 256 // Extra wide size, for bonus items +#define FL_ONGROUND 512 // Entity is completely standing on something +#define FL_PARTIALGROUND 1024 // Not all corners are valid +#define FL_WATERJUMP 2048 // Entity is a player jumping out of water +#define FL_JUMPRELEASED 4096 // For jump debouncing (no pogo stick) + +/* Valid values for the .movetype field */ +#define MOVETYPE_NONE 0 // Does not move +#if 0 +# define MOVETYPE_ANGLENOCLIP 1 +# define MOVETYPE_ANGLECLIP 2 +#endif +#define MOVETYPE_WALK 3 // Only players can have this movement type +#define MOVETYPE_STEP 4 // Discrete, not real time unless fall +#define MOVETYPE_FLY 5 // Not affected by gravity +#define MOVETYPE_TOSS 6 // Use gravity to calculate trajectory +#define MOVETYPE_PUSH 7 // Not clipped to world, can push and crush players +#define MOVETYPE_NOCLIP 8 // Not clipped at all +#define MOVETYPE_FLYMISSILE 9 // Flying with extra size against monsters +#define MOVETYPE_BOUNCE 10 // Bounces off solid objects, use +#define MOVETYPE_BOUNCEMISSILE 11 // Bounces with extra size + +/* Valid values for the .solid field */ +#define SOLID_NOT 0 // Does not interact with other objects +#define SOLID_TRIGGER 1 // .touch called, but don't block movement +#define SOLID_BBOX 2 // .touch called and block movement +#define SOLID_SLIDEBOX 3 // .touch on edge, don't block if FL_ONGROUND +#define SOLID_BSP 4 // BSP clip, .touch on edge, block movement + +/* Range values */ +#define RANGE_MELEE 0 +#define RANGE_NEAR 1 +#define RANGE_MID 2 +#define RANGE_FAR 3 + +/* Valid values for the .deadflag field */ +#define DEAD_NO 0 // Not dead +#define DEAD_DYING 1 // Dying, will be dead soon +#define DEAD_DEAD 2 // E's shuffled off his mortal coil +#define DEAD_RESPAWNABLE 3 // Dead, but can respawn + +/* Valid values for the .takedamage field */ +#define DAMAGE_NO 0 // Does not take damage +#define DAMAGE_YES 1 // Takes all damage +#define DAMAGE_AIM 2 // Takes only direct damage + +/* Item flags */ // Weapons +#define IT_AXE 4096 +#define IT_SHOTGUN 1 +#define IT_SUPER_SHOTGUN 2 +#define IT_NAILGUN 4 +#define IT_SUPER_NAILGUN 8 +#define IT_GRENADE_LAUNCHER 16 +#define IT_ROCKET_LAUNCHER 32 +#define IT_LIGHTNING 64 +#define IT_EXTRA_WEAPON 128 + // Ammo +#define IT_SHELLS 256 +#define IT_NAILS 512 +#define IT_ROCKETS 1024 +#define IT_CELLS 2048 + // Armor types +#define IT_ARMOR1 8192 +#define IT_ARMOR2 16384 +#define IT_ARMOR3 32768 + // Keys +#define IT_KEY1 131072 +#define IT_KEY2 262144 + // Special items +#define IT_SUPERHEALTH 65536 // Megahealth +#define IT_INVISIBILITY 524288 // Ring of shadows +#define IT_INVULNERABILITY 1048576 // Pentagram of Protection +#define IT_SUIT 2097152 // Biosuit +#define IT_QUAD 4194304 // Quad Damage + +/* Values returned by pointcontents() */ +#define CONTENT_EMPTY -1 // Point is in the air somewhere +#define CONTENT_SOLID -2 // Point is outside the world +#define CONTENT_WATER -3 // Point is in water +#define CONTENT_SLIME -4 // Point is in slime +#define CONTENT_LAVA -5 // Point is in lava +#define CONTENT_SKY -6 // Point is beyond the sky + +#define STATE_TOP 0 +#define STATE_BOTTOM 1 +#define STATE_UP 2 +#define STATE_DOWN 3 + +#define VEC_ORIGIN '0 0 0' // the origin of a 3D object + + // defines the bounding box of a player +#define VEC_HULL_MIN '-16 -16 -24' +#define VEC_HULL_MAX '16 16 32' + + // defines the bounding box of some monsters +#define VEC_HULL2_MIN '-32 -32 -24' +#define VEC_HULL2_MAX '32 32 64' + +// Protocol bytes +#define SVC_TEMPENTITY 23 // server -> client: create temp entity +#define SVC_KILLEDMONSTER 27 // server -> client: killed a monster +#define SVC_FOUNDSECRET 28 // server -> client: found a secret +#define SVC_INTERMISSION 30 // server -> client: intermission start +#define SVC_FINALE 31 // server -> client: game over +#define SVC_CDTRACK 32 // server -> client: set CD audio track +#define SVC_SELLSCREEN 33 // server -> client: show "nag" screen +#define SVC_SMALLKICK 34 // server -> client: +#define SVC_BIGKICK 35 // server -> client: +#define SVC_MUZZLEFLASH 39 // server -> client: place a glow + +// Useful things SVC_TEMPENTITY can create +#define TE_SPIKE 0 // A nail +#define TE_SUPERSPIKE 1 // A big nail +#define TE_GUNSHOT 2 // Gunshot hitting a wall +#define TE_EXPLOSION 3 // Rocket/grenade explosion +#define TE_TAREXPLOSION 4 // "tarbaby" explosion +#define TE_LIGHTNING1 5 +#define TE_LIGHTNING2 6 +#define TE_WIZSPIKE 7 // Yellow trail from the Scragg +#define TE_KNIGHTSPIKE 8 // The Death Knight's ranged attack +#define TE_LIGHTNING3 9 +#define TE_LAVASPLASH 10 +#define TE_TELEPORT 11 // Teleportation effect +#define TE_BLOOD 12 // Blood puff +#define TE_LIGHTNINGBLOOD 13 + +/* + Sound channels + + Channel 0 never willingly overrides anything. + Other channels (1-7) always override a sound playing on that channel. +*/ +#define CHAN_AUTO 0 // Pick a free channel if available +#define CHAN_WEAPON 1 // Player's weapon +#define CHAN_VOICE 2 // Player's voice +#define CHAN_ITEM 3 +#define CHAN_BODY 4 // Player's body (footsteps, etc) +// special sound flags (OR together with a channel) +#define CHAN_NO_PHS_ADD 8 // Do not add to the PHS + +/* Sound attenuation for distance */ +#define ATTN_NONE 0 // No fadeout (whole world) +#define ATTN_NORM 1 +#define ATTN_IDLE 2 +#define ATTN_STATIC 3 + +// update types +#define UPDATE_GENERAL 0 +#define UPDATE_STATIC 1 +#define UPDATE_BINARY 2 +#define UPDATE_TEMP 3 + +/* entity effects */ +#if 0 // Removed for unknown reasons +# define EF_BRIGHTFIELD 1 +# define EF_MUZZLEFLASH 2 +#endif +#define EF_BRIGHTLIGHT 4 +#define EF_DIMLIGHT 8 +#define EF_FLAG1 16 // CTF hack, attach flag 1 +#define EF_FLAG2 32 // CTF hack, attach flag 2 +// GLQuakeWorld Stuff +#define EF_BLUE 64 // Blue dlight effect, for Quad +#define EF_RED 128 // Red dlight effect, for Pentagram + +/* Message types */ +#define MSG_BROADCAST 0 // unreliable to all +#define MSG_ONE 1 // reliable to one (msg_entity) +#define MSG_ALL 2 // reliable to all +#define MSG_INIT 3 // write to the init string +#define MSG_MULTICAST 4 // for multicast() call + +/* Message priority levels */ +#define PRINT_LOW 0 // Bonus pickup messages +#define PRINT_MEDIUM 1 // death messages +#define PRINT_HIGH 2 // critical messages +#define PRINT_CHAT 3 // also goes to chat console + +// multicast sets +#define MULTICAST_ALL 0 // every client +#define MULTICAST_PHS 1 // within hearing +#define MULTICAST_PVS 2 // within sight +#define MULTICAST_ALL_R 3 // every client, reliable +#define MULTICAST_PHS_R 4 // within hearing, reliable +#define MULTICAST_PVS_R 5 // within sight, reliable diff --git a/ParoxysmII/source/sectrig.r b/ParoxysmII/source/sectrig.r new file mode 100644 index 0000000..80718dc --- /dev/null +++ b/ParoxysmII/source/sectrig.r @@ -0,0 +1,817 @@ + +#include "paroxysm.rh" + +// + POX moved prototypes from weapons.qc for use here +void(float shotcount, vector dir, vector spread) FireBullets2; +void(float damage) spawn_touchblood; +void() muzzleflash; +void (entity targ, entity inflictor, entity attacker, float damage) T_Damage; +void () player_run; +void(entity bomb, entity attacker, float rad, entity ignore, string dtype) T_RadiusDamage; +void(vector org, float damage) SpawnBlood; +void() SuperDamageSound; +float SECOND_TRIGGER = 15; // Impulse constant for second trigger (more readable than 15) +void(vector org) launch_shrapnel; //Predeclare +void() player_shot1; +void() player_gshot1; +void() player_plasma1; +void() player_plasma2; +void() player_mplasma1; +void() player_nail1; +void() player_rocket1; +void() player_rocketload1; +void() player_grenade1; +void() player_reshot1; +void() player_tshot1; +void() player_shrap1; +void() player_axe1; +void() player_axeb1; +void() player_axec1; +void() player_axed1; + +void(float damage, vector dir) TraceAttack; +void() ClearMultiDamage; +void() ApplyMultiDamage; +void() Multi_Finish; +void(vector org, vector dir) launch_spike; +void() superspike_touch; + +//Some nitty-gritty from weapons.qc ... +float() crandom = +{ + return 2 * (random() - 0.5); +}; + + +vector() wall_velocity = +{ + local vector vel; + + vel = normalize (self.velocity); + vel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5)); + vel = vel + 2*trace_plane_normal; + vel = vel * 200; + + return vel; +}; +/* +================ +Triple Barrel Shot for T-shot +Close Range Gibber - long range spread +================ +*/ +void() W_FireTShot = +{ + local vector dir; + + sound (self ,CHAN_WEAPON, "weapons/ts3fire.wav", 1, ATTN_NORM); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_BIGKICK); + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -80; + + self.currentammo = self.ammo_shells = self.ammo_shells - 3; + dir = aim (self, 100000); + //POX - 1.01b2 - increased spread, reduced amount + //POX - 1.1 - made FireBullets2 (twice the damge, half the pellets + 1 :) + FireBullets2 (12, dir, '0.16 0.1 0'); //make priming this thing worth while! +}; + +//================================================================================= +//Start MegaPlasmaBurst - Used by PlasmaGun's Second Trigger +//================================================================================= + +void() T_MplasmaTouch = +{ + local float damg; + + if (other == self.owner) + return; // don't explode on owner + + if (self.voided) { + return; + } + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) { + remove(self); + return; + } + damg = 120 + random()*20; + + T_RadiusDamage (self, self.owner, damg, world, "megaplasma"); + + sound (self, CHAN_WEAPON, "weapons/mplasex.wav", 1, ATTN_NORM); + self.origin = self.origin - 8*normalize(self.velocity); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + remove(self); +}; + +//launch_megaplasma +void() launch_megaplasma = +{ + local vector dir; + + self.currentammo = self.ammo_cells = self.ammo_cells - 9; + + sound (self, CHAN_WEAPON, "weapons/mplasma.wav", 1, ATTN_NORM); + msg_entity = self; + WriteByte (MSG_ONE, SVC_BIGKICK); + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -270; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_FLYMISSILE; + newmis.solid = SOLID_BBOX; + newmis.classname = "megaplasma"; + newmis.effects = newmis.effects | EF_BLUE; + + // set speed + dir = aim ( self, 1000 ); + newmis.velocity = dir * 0.01; + newmis.avelocity = '300 300 300'; + newmis.angles = vectoangles(newmis.velocity); + newmis.velocity = normalize(newmis.velocity); + newmis.velocity = newmis.velocity * 950; + + newmis.touch = T_MplasmaTouch; + + // set duration + newmis.think = SUB_Remove; + newmis.nextthink = time + 5; + setmodel (newmis, "progs/plasma.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin + v_forward*12 + '0 0 12'); +}; +//End MegaPlasmaBurst +//================================================================================= +//============================================================================= +// +// START PumkinBall CODE - Used by SuperShotgun's Second Trigger (Impact Grenades) +// +//============================================================================= +void() T_PballTouch = +{ + local float damg; + if (other == self.owner) + return; // don't explode on owner + + if (self.voided) { + return; + } + + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + damg = 100 + random()*20; + T_RadiusDamage (self, self.owner, damg, world, "impactgrenade"); + +// sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM); + self.origin = self.origin - 8*normalize(self.velocity); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + remove(self); +}; +/* +================ +W_FirePball +================ +*/ +void() W_FirePball = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; + + sound (self, CHAN_AUTO, "weapons/gren2.wav", 1, ATTN_NORM); + msg_entity = self; + WriteByte (MSG_ONE, SVC_BIGKICK); + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -150; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_TOSS; + newmis.solid = SOLID_BBOX; + newmis.classname = "impactgrenade"; + + // set newmis speed + makevectors (self.v_angle); + newmis.velocity = v_forward*700 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = T_PballTouch; + + // set newmis duration + newmis.think = SUB_Remove; + newmis.nextthink = time + 5; + setmodel (newmis, "progs/grenade.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin + v_forward*4); +}; +// END PumkinBall CODE + +//============================================================================= + +//============================================================================= +// +// START MINE CODE (based on hipnotic's proximity mine - uh... hacked to death) +// Used for the Grenade Launcher's Second Trigger +// This is some laughable, sick code +// But it works. +// +//============================================================================= +void() M_DamExplode = +{ + if (self.voided) + return; + + self.voided = 1; + + T_RadiusDamage (self, self.owner, 95, world, "mine"); + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + + remove (self); +}; +/* +================ +MineExplode +================ +*/ +//explode immediately! (for doors, plats and breakable objects +void() MineImExplode = +{ + self.takedamage = DAMAGE_NO; + self.deathtype = "mine"; + self.owner = self.lastowner; + M_DamExplode(); +}; + +void() MineExplode = +{ + self.takedamage = DAMAGE_NO; + self.deathtype = "mine"; + self.nextthink = time + random()*0.15; //gives a more organic explosion when multiple mines explode at once + self.owner = self.lastowner; + self.think = M_DamExplode; +}; + +/* +================ +MineTouch +================ +*/ +void() MineTouch = +{ + if (other == self) + return; + + if (other.solid == SOLID_TRIGGER) { + sound (self, CHAN_AUTO, "weapons/bounce.wav", 1, ATTN_NORM); + return; + } + + if (other.classname == "grenade") { + sound (self, CHAN_AUTO, "weapons/bounce2.wav", 1, ATTN_NORM); + self.nextthink = time + 1; + return; + } + + if (other.classname == "mine") { + sound (self, CHAN_AUTO, "weapons/bounce2.wav", 1, ATTN_NORM); + self.nextthink = time + 1; + return; + } + + if (other.classname == "minearm") { + sound (self, CHAN_AUTO, "weapons/bounce2.wav", 1, ATTN_NORM); + self.nextthink = time + 1; + return; + } + + if (other.classname == "minearmed") { + sound (self, CHAN_AUTO, "weapons/bounce.wav", 1, ATTN_NORM); + self.classname = "minearm"; + self.nextthink = time + 1; + return; + } + + if (other.classname == "player") { + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + MineExplode(); + self.nextthink = time + 0.4; + return; + } + + if (other.takedamage == DAMAGE_AIM) { + MineExplode(); + self.think(); + return; + } + + self.movetype = MOVETYPE_NONE; + self.classname = "minearm"; + self.spawnmaster = other; + self.nextthink = time + 0.1; +}; +/* +================ +MineArm +================ +*/ +void() MineArm = +{ + local entity head; + local float detonate; + + if (self.classname == "minearm") { + sound (self, CHAN_WEAPON, "weapons/armed.wav", 1, ATTN_NORM); + setsize (self, '-3 -3 -3', '3 3 3'); + self.owner = world; + self.takedamage = DAMAGE_YES; + self.skin = 1; + self.classname = "minearmed"; + muzzleflash(); //Will this work? + } + if ((time > self.delay) || (self.spawnmaster.no_obj == TRUE)) + { + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + MineImExplode(); + //self.nextthink = time + 0.4; + return; + } + + // No click or delay when velocity triggers mine (so they don't float when doors open) + // Although the 'organic' explosion' part in the detonate code might leave it hanging... + if (vlen(self.spawnmaster.velocity) > 0) + { + MineImExplode(); + return; + } + + // Mines explode on touch, but for some reason most monsters don't detonate them (?) + // So had to use a findradius function to look for monsters, it's a small radius though + // Ogres still don't always detonate mines (?) + head = findradius(self.origin, 39); + detonate = 0; + + if (self.health < 0) + detonate = 1; + + while (head) { + if ((head != self) && (head.health > 0) && ((head.flags & (FL_CLIENT|FL_MONSTER)) || (head.classname == "bot")) && (head.classname!=self.classname)) + detonate = 1; + traceline(self.origin, head.origin, TRUE, self); + if (trace_fraction != 1.0) + detonate = 0; + + if (detonate==1) { + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + MineExplode(); + self.nextthink = time + 0.25; + return; + } + head = head.chain; + } + + self.nextthink = time + 0.1; +}; +/* +================ +W_FireMine +================ +*/ +void() W_FireMine = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; + sound (self, CHAN_AUTO, "weapons/gren.wav", 1, ATTN_NORM); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -100; + + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.lastowner = self; + newmis.movetype = MOVETYPE_TOSS; + newmis.solid = SOLID_BBOX; + newmis.classname = "mine"; + newmis.takedamage = DAMAGE_NO; + newmis.health = 1; + + //POX v1.2 - mines don't bleed.... + newmis.nobleed = TRUE; + + // set missile speed + makevectors (self.v_angle); + if (self.v_angle_x) { + newmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + } else { + newmis.velocity = aim(self, 10000); + newmis.velocity = newmis.velocity * 600; + newmis.velocity_z = 200; + } + + newmis.avelocity = '100 600 100'; + newmis.angles = vectoangles(newmis.velocity); + newmis.touch = MineTouch; + + // set missile duration + newmis.nextthink = time + 0.2; + newmis.delay = time + 60; + newmis.think = MineArm; + newmis.th_die = MineExplode; + + setmodel (newmis, "progs/grenade.mdl"); + setorigin (newmis, self.origin + v_forward*4); + setsize (newmis, '-1 -1 -1', '0 0 0'); +}; +// END MINE CODE + +//============================================================================= + +//============================================================================= +// +// Shrapnel Bomb - Nailgun Second Trigger (1 rocket + 30 nails, remotely detonated) +// +//============================================================================= + +//---------------------------------------------------------- +//These functions launch a single spike in a random direction +void() spikenal_touch = +{ + if (pointcontents(self.origin) == CONTENT_SKY) { + remove(self); + return; + } + + if (self.voided) { + return; + } + self.voided = 1; + + if (other.solid == SOLID_TRIGGER) + return; // trigger field, do nothing + + // hit something that bleeds + if (other.takedamage) { + spawn_touchblood (12); + other.deathtype = "shrapnel"; + T_Damage (other, self, self.owner, 12); + remove(self); + } + + else if (random() > 0.9) { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_SPIKE); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + remove(self); + } +}; + +//POX - Get a random vector for Shrapnel +vector() VelocityForShrapnel = +{ + local vector v; + v_x = 200 * crandom(); + v_y = 200 * crandom(); + v_z = 200 * crandom(); + + if (random() > 0.5) + v_z = v_z - (v_z*2); + + v = v * 6; + return v; +}; + +//POX - Shrapnel = fast, bouncy spikes with a short life +void(vector org) launch_shrapnel = +{ + newmis = spawn (); + + newmis.voided = 0; + newmis.owner = self.owner; + newmis.movetype = MOVETYPE_BOUNCE; + newmis.solid = SOLID_BBOX; + + newmis.touch = spikenal_touch; + newmis.classname = "spikenal"; + + newmis.velocity = VelocityForShrapnel(); + newmis.avelocity_x = random()*800; + newmis.avelocity_y = random()*800; + newmis.avelocity_z = random()*800; + + newmis.think = SUB_Remove; + newmis.nextthink = time + 3; + + setmodel (newmis, "progs/mwrub1.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + setorigin (newmis, org); +}; +//---------------------------------------------------------- +//and now the bomb code... +void() ShrapnelExplode = +{ + local integer i; + + local vector direction; + + if (self.voided) { + return; + } + self.voided = 1; + + // Toss the nails (this function is with the spike stuff since it uses the same touch) + for (i = 0; i < 10; i++) + launch_shrapnel(self.origin); + + for (i = 0; i < 60; i++) { // Toss some spikes + direction_x = (4096 * random ()) - 2048; + direction_y = (4096 * random ()) - 2048; + direction_z = (4096 * random ()) - 2048; + + launch_spike (self.origin, direction); + newmis.owner = self.owner; + }; + + T_RadiusDamage (self, self.owner, 160, world, "shrapnel"); + + if (self.owner != world) + self.owner.shrap_detonate = FALSE; // Enable next launch + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + remove (self); + +}; + +void() ShrapnelDetonate = +{ + sound (self, CHAN_BODY, "weapons/minedet.wav", 1, ATTN_NORM); + self.think = ShrapnelExplode; + self.nextthink = time + 0.1; +}; + +// Wait for a detonation impulse or time up +void() ShrapnelThink = +{ + if (self.shrap_time < time) + ShrapnelDetonate(); + + if (self.owner == world) + return; + + // Owner died so change to world and wait for detonate + if (self.owner.health <= 0) { + self.owner.shrap_detonate = FALSE;//Enable next launch + self.owner = world; + self.nextthink = self.shrap_time; + self.think = ShrapnelDetonate; + return; + } + + if (self.owner.shrap_detonate == 2) + ShrapnelDetonate(); + else + self.nextthink = time + 0.1; +}; + + +void() ShrapnelTouch = +{ + local float r; + + r = random(); + + if (other == self.owner) + return; // don't explode on owner + + if (other.takedamage == DAMAGE_AIM) + { + ShrapnelDetonate(); + return; + } + + //pick a bounce sound + if (r < 0.75) + sound (self, CHAN_VOICE, "weapons/bounce.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "weapons/bounce2.wav", 1, ATTN_NORM); + + if (self.velocity == '0 0 0') + self.avelocity = '0 0 0'; +}; +// End shrapnel bomb + +//============================================================================= + +/* +================ +W_FireShrapnel +================ +*/ +void() W_FireShrapnel = +{ + self.ammo_rockets = self.ammo_rockets - 1; + self.currentammo = self.ammo_nails = self.ammo_nails - 30; + sound (self, CHAN_WEAPON, "weapons/gren2.wav", 1, ATTN_NORM); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); + + //Added weapon kickback (as long as you're not in mid air) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -115; + newmis = spawn (); + newmis.voided = 0; + newmis.owner = self; + newmis.movetype = MOVETYPE_BOUNCE; + newmis.solid = SOLID_BBOX; + newmis.classname = "shrapnel"; + + newmis.shrap_time = time + 120; + + // set newmis speed + makevectors (self.v_angle); + if (self.v_angle_x) { + newmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + } else { + newmis.velocity = aim(self, 10000); + newmis.velocity = newmis.velocity * 600; + newmis.velocity_z = 200; + } + newmis.avelocity = '300 300 300'; + newmis.angles = vectoangles(newmis.velocity); + newmis.touch = ShrapnelTouch; + + // set newmis duration + newmis.nextthink = time + 0.1; + newmis.think = ShrapnelThink; + setmodel (newmis, "progs/grenade.mdl"); + newmis.skin = 2; + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin); +}; +/* +============ +W_SecondTrigger +Second Trigger Impulses +POX v1.1 - seperated this from weapons.qc - cleaned it up a bit +============ +*/ +void() W_SecondTrigger = +{ + if (intermission_running) { // Don't fire during intermission + self.impulse = 0; + return; + } + + switch (self.weapon) { + case IT_TSHOT: // T-Shot (prime) + // do it only if it hasn't already been primed and has 3 or more shells + if ((!self.prime_tshot) && (self.ammo_shells >= 3)) { + self.st_tshotload = time + 0.9; //give the reload a chance to happen + + // make a reload sound + sound (self, CHAN_WEAPON, "weapons/tsload.wav", 1, ATTN_NORM); + player_reshot1(); // play prime animation + + self.prime_tshot = TRUE; // set the prime bit + } + break; + + case IT_COMBOGUN: // impact grenade + if (self.ammo_rockets > 0) { // check for rockets + if (self.st_pball < time) { + self.items &= ~IT_SHELLS; + self.items |= IT_ROCKETS; + self.currentammo = self.ammo_rockets; + self.which_ammo = 1; + player_gshot1(); + SuperDamageSound(); + W_FirePball(); + self.st_pball = time + 0.9; + } else { // misfire if it hasn't been long enough + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + } + } else { + self.items &= ~IT_SHELLS; + self.items |= IT_ROCKETS; + self.currentammo = self.ammo_rockets; + self.which_ammo = 1; + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + } + break; + + case IT_PLASMAGUN: // plasma burst + if (self.ammo_cells >= 9 && self.st_mplasma < time) { // check for cells and time + if (self.waterlevel > 1) { // explode under water + sound (self, CHAN_WEAPON, "weapons/mplasex.wav", 1, ATTN_NORM); + self.ammo_cells = 0; + W_SetCurrentAmmo (); + T_RadiusDamage (self, self, 250, world, "waterplasma"); + break; + } + self.weaponframe = 0; + SuperDamageSound (); + self.st_mplasma = time + 1.9; + player_mplasma1 (); + launch_megaplasma (); + break; + } + sound (self, CHAN_AUTO, "weapons/mfire2.wav", 1, ATTN_NORM); + break; + + case IT_SUPER_NAILGUN: // shrapnel bomb + if (self.shrap_detonate) { // bomb is already set + sound (self, CHAN_WEAPON, "weapons/shrapdet.wav", 1, ATTN_NORM); + SuperDamageSound (); + self.st_shrapnel = time + 0.7; // Time out before next launch + self.shrap_detonate = 2; // Tell the bomb to blow! + break; + } + + if (self.ammo_nails >= 30 && self.ammo_rockets > 0) { // toss one + self.weaponframe = 0; + SuperDamageSound (); + self.st_shrapnel = time + 0.1; // Allow a fast detonate + player_shrap1 (); + W_FireShrapnel (); + + self.shrap_detonate = TRUE; + break; + } + + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + sprint (self, PRINT_HIGH, "Not enough ammo...\n"); + break; + + case IT_GRENADE_LAUNCHER: // phase mine + if (self.ammo_rockets >= 1 && self.st_mine < time) { + player_grenade1 (); + W_FireMine (); + + // big delay between refires helps keep the # of mines down in a deathmatch game + self.st_mine = time + 1.25; + break; + } + + sound (self, CHAN_WEAPON, "weapons/mfire1.wav", 1, ATTN_NORM); + break; + + case IT_ROCKET_LAUNCHER: // reload the rhino + if (self.reload_rocket && self.ammo_rockets >= 1) { + self.st_rocketload = time + 0.6; + sound (self, CHAN_WEAPON, "weapons/rhinore.wav", 1, ATTN_NORM); + player_rocketload1 (); // play reload animation + self.reload_rocket = FALSE; + } + break; + default: + break; + } + + self.impulse = 0; +}; diff --git a/ParoxysmII/source/server.r b/ParoxysmII/source/server.r new file mode 100644 index 0000000..d3b575a --- /dev/null +++ b/ParoxysmII/source/server.r @@ -0,0 +1,78 @@ + +void() monster_ogre = {remove(self);}; +void() monster_demon1 = {remove(self);}; +void() monster_shambler = {remove(self);}; +void() monster_knight = {remove(self);}; +void() monster_army = {remove(self);}; +void() monster_wizard = {remove(self);}; +void() monster_dog = {remove(self);}; +void() monster_zombie = {remove(self);}; +void() monster_boss = {remove(self);}; +void() monster_tarbaby = {remove(self);}; +void() monster_hell_knight = {remove(self);}; +void() monster_fish = {remove(self);}; +void() monster_shalrath = {remove(self);}; +void() monster_enforcer = {remove(self);}; +void() monster_oldone = {remove(self);}; +void() event_lightning = {remove(self);}; +/* +============================================================================== +MOVETARGET CODE +The angle of the movetarget effects standing and bowing direction, but has no effect on movement, which allways heads to the next target. +targetname +must be present. The name of this movetarget. +target +the next spot to move to. If not present, stop here for good. +pausetime +The number of seconds to spend standing or bowing for path_stand or path_bow +============================================================================== +*/ +/* +============= +t_movetarget +Something has bumped into a movetarget. If it is a monster +moving towards it, change the next destination and continue. +============== +*/ +void() t_movetarget = +{ +local entity temp; + if (other.movetarget != self) + return; + + if (other.enemy) + return; // fighting, not following a path + temp = self; + self = other; + other = temp; + if (self.classname == "monster_ogre") + sound (self, CHAN_VOICE, "ogre/ogdrag.wav", 1, ATTN_IDLE);// play chainsaw drag sound +//dprint ("t_movetarget\n"); + self.goalentity = self.movetarget = find (world, targetname, other.target); + self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin); + if (!self.movetarget) + { + self.pausetime = time + 999999; + self.th_stand (); + return; + } +}; +void() movetarget_f = +{ + if (!self.targetname) + objerror ("monster_movetarget: no targetname"); + + self.solid = SOLID_TRIGGER; + self.touch = t_movetarget; + setsize (self, '-8 -8 -8', '8 8 8'); + +}; +/*QUAKED path_corner (0.5 0.3 0) (-8 -8 -8) (8 8 8) +Monsters will continue walking towards the next target corner. +*/ +void() path_corner = +{ + movetarget_f (); +}; + +//============================================================================ diff --git a/ParoxysmII/source/shields.r b/ParoxysmII/source/shields.r new file mode 100644 index 0000000..6706b4d --- /dev/null +++ b/ParoxysmII/source/shields.r @@ -0,0 +1,128 @@ +#include "config.rh" +#include "paroxysm.rh" + +/* Frank Condello 09.28.98 - made for the Paroxysm Quake Mod. +EMAIL: pox@planetquake.com +WEB: http://www.planetquake.com/paroxysm/ +========================================================================= +*/ + +//Armor regeneration stations +//surprisingly little coding required to get this going +//Had to add a value/type check routine in T_Damage + +// Armor is gained by standing at regen stations. +// The color of armor only indocates how much power your armor has left; +// all armor protects the same amount. + +// A breakdown: +// 1 - 50 points is blue +// 51 - 150 points is yellow +// 151 - 255 points is red + +// Regen Station default ambient sound +void() regen_ambientsound = +{ + ambientsound (self.origin, "ambience/regen1.wav", 0.5, ATTN_STATIC); +}; + +// Particle stream replaced by a model with client side animation +#if 0 +// A particle effect for regen stations +void (void) regen_make_smoke = +{ + particle (self.origin + '0 0 +6', '1 1 36', self.clr, 12); + particle (self.origin + '0 0 +6', '-1 -1 36', self.clr, 12); + self.nextthink = time + 0.1; +}; +#endif + +// Particle-emiting entity +void (float color) regen_streamer = +{ + local entity streamer; + + streamer = spawn (); + streamer.solid = SOLID_TRIGGER; + streamer.movetype = MOVETYPE_TOSS; + + setmodel (streamer, "progs/stream.mdl"); + setsize (streamer, '-16 -16 0', '16 16 56'); + streamer.velocity = '0 0 0'; + setorigin (streamer, self.origin); + + streamer.skin = color; + + regen_ambientsound (); +}; + +void () regen_touch = +{ + if (other.classname != "player") + return; + + if (other.regen_finished > time) // already touched + return; + + if (other.armorvalue >= self.armregen) { // Station can't give more + other.regen_finished = time; + return; + } + + other.armregen = self.armregen; + other.regen_finished = time + 0.2; + +}; + +// Regen triggers for custom maps - can be BIG (whole rooms) +void () regen_station = +{ + if (self.armregen <= 0) + self.armregen = 50; + InitTrigger (); + self.touch = regen_touch; + //self.netname = "regen station"; +}; + +void (float type, float value) item_armor = +{ + if (type < 0 || type > 2) // sanity checking + return; + + precache_sound ("ambience/regen1.wav"); + + precache_model ("progs/regen.mdl"); + precache_model ("progs/stream.mdl"); + + self.touch = regen_touch; + setmodel (self, "progs/regen.mdl"); + self.skin = type; + setsize (self, '-16 -16 0', '16 16 56'); + self.solid = SOLID_TRIGGER; + + // Some existing DM maps rely on Droptofloor for proper placement + self.movetype = MOVETYPE_TOSS; + self.velocity = '0 0 0'; + self.origin_z = self.origin_z + 6; + //self.netname = "regen station"; + + self.armregen = value; + + regen_streamer (type); +}; + +// Replace armor pickups in existing maps +void () item_armor1 = +{ + item_armor (0, 50); +}; + +void () item_armor2 = +{ + item_armor (1, 150); +}; + +void() item_armorInv = +{ + item_armor (2, 250); +}; diff --git a/ParoxysmII/source/specfx.r b/ParoxysmII/source/specfx.r new file mode 100644 index 0000000..522b1a4 --- /dev/null +++ b/ParoxysmII/source/specfx.r @@ -0,0 +1,352 @@ +#include "config.rh" + +#include "paroxysm.rh" + +/* Frank Condello 09.28.98 - made for the Paroxysm MacQuake Mod. +EMAIL: pox@planetquake.com +WEB: http://www.planetquake.com/paroxysm +========================================================================= +Custom Paroxysm visual and audio effects +These are simple little ambient effects that are customizable within Quiver. +Feel free to use this code in anyway. +========================================================================= +*/ +//NOT USED IN POXworld +.float spark_freq; // To avoid 'not a feild' server errors +void() e_spark = +{ + remove (self); +}; +void() a_drip = +{ + remove (self); +}; + +/* +================================================================================================= +Custom Sound Entity (cust_sound) +The lack of a Mac sound editor that produces looping .wav files in a format that Quake understands +prompted the creation of this function. Then I added some options to make it worth while. +snd_attn - sets the sound's attenuation (defaults to ATTN_NORM (1)) +snd_volume - sets the sounds overall volume (typically values of 0.5 to 1) default is 1 +strt_onoff - 0 = initially on, 1 = initially off +snd_rep - number of times to play the sound (-2 is infinite loop) +snd_loop - delay in seconds before playing sound again (use sound length for looping sounds) +snd_rand - random seed, this number is multiplied by random() and added to loop (default 0 is no random) +the_snd - directory path to the sound - example: "misc/mysound.wav" NOTE: "sound/" is NOT needed +targetname - sounds can be triggered by using a targetname. +TOGGLE_SND - spawn flag, determines if sounds can be toggled on and off. +BUGS +1. Quake won't start playing these sounds on the first frame - (Unless you precache every possible sound +at world_spawn) +HACKY FIX - If you need a sound to play right away; +Position a trigger in such a way that it is touched when the player enters the map - NOT touching the +player initially (doesn't work on frame 1 remember?). Try making a trigger 4 units tall and place +info_player_start just above it. Not pretty, but it does the trick - Don't forget to set +strt_onoff to 1 (initially off) for this to work +2. When Quake no longer 'hears' or more arcurately - 'sees' the sound, it kills it until it comes into view +again. This causes the sound to stop playing and remsume at the NEXT loop point when it is in view, so for +sounds that must be looped constantly, I would reccomend using the ambient_sound function (see below) +================================================================================================= +*/ +//NOTE: some of these .floats are used in ambient_sound as well (see below) +float TOGGLE_SND = 1; +.float snd_attn; +.float snd_volume; +.float strt_onoff; +.float snd_rep; +.float snd_rand; +.float snd_loop; +.float snd_strt; +.string the_snd; +void() sound_think; +void() play_sound; +void() sound_wait = +{ + // hang around until next use + self.nextthink = time + 60*10; + self.think = sound_wait; +}; +void() stop_sound = +{ + // if sound is set to toggle, silence it and set use to play + if (self.spawnflags & TOGGLE_SND) + { + sound (self, CHAN_VOICE, "misc/null.wav", 0, self.snd_attn); + self.use = play_sound; + sound_wait(); + } + + // sound doesn't toggle so kill it + else + { + sound (self, CHAN_VOICE, "misc/null.wav", 0, self.snd_attn); + remove (self); + } +}; +void() play_sound = +{ + //infinite repeat + if (self.snd_rep == -2) + { + sound (self, CHAN_VOICE, self.the_snd, self.snd_volume, self.snd_attn); + sound_think(); + } + + // sound is done + else if (self.snd_rep == 0) + remove (self); + + // play the sound and reduce repititions by 1 + if (self.snd_rep >= 1) + { + sound (self, CHAN_VOICE, self.the_snd, self.snd_volume, self.snd_attn); + self.snd_rep = self.snd_rep - 1; + sound_think(); + } + +}; +void() sound_think = +{ + // if sound is toggled, set next use to stop + if (self.spawnflags & TOGGLE_SND) + self.use = stop_sound; + + // determine user-defined loop time then play the sound + self.nextthink = time + (random()*self.snd_rand) + self.snd_loop; + self.think = play_sound; +}; +void() cust_sound = +{ + precache_sound (self.the_snd); + precache_sound ("misc/null.wav"); + + //default attenuation to NORM if not specified + if(!self.snd_attn) + self.snd_attn = 2; + + self.snd_attn = self.snd_attn - 1;// needed so the default to NORM works (since 0 = NONE) + + //default volume to one if not specified + if(self.snd_volume <= 0) + self.snd_volume = 1; + + // start sound if not triggered + if (!self.targetname) + play_sound(); + + // start sound if initially on, set use to stop_sound + if (self.strt_onoff == 0) + { + self.use = stop_sound; + play_sound(); + } + + // start sound when triggered + if (self.strt_onoff == 1) + { + self.use = play_sound; + } + +}; + +//Made this so custom ambient sounds could be called with a single function +//ONLY WORKS WITH LOOPED .WAV FILES +//Added a targetname attribute so ambient sounds can be triggered. +//NOTE: These sounds can not be stopped once started +//To my knowledge you can't make these with existing Mac sound editors +//(you can make looping wavs, just not ones Quake will loop) +//CoolEdit 1.5 (for Windows) is the only editor that can create these looping wavs (to my knowledge) +void() ambientsound_go = +{ + ambientsound (self.origin, self.the_snd, self.snd_volume, self.snd_attn); +}; +void() ambient_sound = +{ + precache_sound (self.the_snd); + + //default volume to one if not specified + if(self.snd_volume <= 0) + self.snd_volume = 1; + + //default attenuation to NORM if not specified + if(!self.snd_attn) + self.snd_attn = 2; + + self.snd_attn = self.snd_attn - 1;// needed so the default to NORM works (since 0 = NONE) + + //start right away if not triggered + if(!self.targetname) + { + //Needs to start on the second frame since that's when entities not called in world_spawn are created + self.nextthink = time + 0.1; + self.think = ambientsound_go; + } + + //wait for trigger + else + self.use = ambientsound_go; +}; +/* +colour_light is a small hack to try and simulate coloured lighting. +It does this by calling the built-in v_cshift function +This can be used for 'atmospheric' effects like outdoor haze and darkness. +This function is overriden by Quake when under liquids and by the background flash when an item is picked up +The GL Quake command 'gl_polyblend 0' also negates this entity. +colourvalue - this is the only parameter, must be in this format; +v_cshift R G B I\n +where R is Red (0 - 255), G is Green (0 - 255), B is Rlue (0 - 255), and I is Intensity (0 - 255) +This effect works poorly in small increments in GL Quake, no colour will be noticeable until about an +intesity of about 5 (depending on the colour) and some colour/intensity combinations result in ugly +banding. It's best to test colour levels in the console before applying them in your map. +These entities are activated by touch (the colour shift only occurs when the player is touching the trigger field) +But can also be enabled/disabled by using a targetname. +The string must be exactly as shown (including the v_cshift and the carraige return +at the end) I could have used multiple stuffcmd statements to make it more user friendly but.. eh. +*/ +void() colourlight_wait; +.string colourvalue; +float CLSTART_OFF = 1; +void() colourlight_off = +{ + if(other.classname != "player") + return; + + //turn it off and reset + stuffcmd (other, "v_cshift 0 0 0 0\n"); + self.use = colourlight_wait; + self.touch = SUB_Null; + +}; +void() colourlight_toggle = +{ + //called after light is triggered a second time + self.touch = colourlight_off; +}; + +void() colourlight_use = +{ + if(other.classname != "player") + return; + + //POX v1.2 - better clearing of v_cshift (in PostThink) + if(other.cshift_finished > time) + return; + + stuffcmd (other, self.colourvalue); + + //POX v1.2 - better clearing of v_cshift (in PostThink) + other.cshift_off = FALSE; + other.cshift_finished = time + 0.1; //check every frame + + //if targetted, alow it to be shut down + if(self.targetname) + self.use = colourlight_toggle; + +}; +void() colourlight_wait = +{ + //activated by a trigger so wait for touch + self.touch = colourlight_use; +}; +void() colour_light = +{ + InitTrigger (); + + //Can be made active by use of a targetname + //Must be triggered by touch + + if(!self.targetname || !(self.spawnflags & CLSTART_OFF)) { + self.touch = colourlight_use; + } else { + self.use = colourlight_wait; + self.touch = SUB_Null; + } +}; +/* +particle_stream +Changed for Paroxysm v1.11's new regen stations +Creates a regen stream-pulse model with out a grate and no touch +This entity should be used in conjunction with a regen_station trigger +clr = skin# (0 = blue, 1 = yellow, 2 = red) +*/ +.float clr; +void() regen_ambientsound; +void() particle_stream = +{ + precache_sound("ambience/regen1.wav"); + precache_model ("progs/stream.mdl"); + + self.angles = '0 0 0'; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NONE; + setmodel(self, "progs/stream.mdl"); + + //POX v1.2 - just in case + if(self.clr > 2 || self.clr < 0) + self.clr = 0; + + self.skin = self.clr; + + //POX v1.2 Fixed sound orign (makestatic messed it up?) + regen_ambientsound (); + makestatic (self); + +}; +/* +POX v1.2 +misc_explobox +BSP based explo_box'es +Try to use rectangular objects, since entites use bounding box collision detection +*/ +void() bsp_explode = +{ + self.takedamage = DAMAGE_NO; + self.trigger_field.classname = "explo_box"; + + // did say self.owner + T_RadiusDamage (self.trigger_field, self.trigger_field, self.dmg, world, ""); + sound (self.trigger_field, CHAN_VOICE, "weapons/r_exp3.wav", 1, ATTN_NORM); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.trigger_field.origin_x); + WriteCoord (MSG_MULTICAST, self.trigger_field.origin_y); + WriteCoord (MSG_MULTICAST, self.trigger_field.origin_z); + multicast (self.origin, MULTICAST_PHS); + + remove (self); + remove (self.trigger_field); +}; + +void() misc_explobsp = +{ + local entity spot; + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + + setmodel (self, self.model); + setsize( self, self.mins, self.maxs ); + precache_sound ("weapons/r_exp3.wav"); + + if (!self.health) + self.health = 20; + + if (!self.dmg) + self.dmg = 160; + + self.th_die = bsp_explode; + self.takedamage = DAMAGE_AIM; + self.nobleed = TRUE; + + //POX 1.2 - HACK! + //put a null entity at the center of the model to hold the explosion position + spot = spawn(); + setmodel (spot, string_null); + spot.origin_x = self.absmin_x + (self.size_x * 0.5); + spot.origin_y = self.absmin_y + (self.size_y * 0.5); + spot.origin_z = self.absmin_z + (self.size_z * 0.5); + setsize (spot, '0 0 0', '0 0 0'); + self.trigger_field = spot; +}; diff --git a/ParoxysmII/source/spectate.r b/ParoxysmII/source/spectate.r new file mode 100644 index 0000000..34d1255 --- /dev/null +++ b/ParoxysmII/source/spectate.r @@ -0,0 +1,75 @@ +#include "config.rh" + +#include "paroxysm.rh" + +// Spectator functions +// Added Aug11'97 by Zoid +// +// These functions are called from the server if they exist. +// Note that Spectators only have one think since they movement code doesn't +// track them much. Impulse commands work as usual, but don't call +// the regular ImpulseCommand handler in weapons.qc since Spectators don't +// have any weapons and things can explode. +// +// --- Zoid. +/* +=========== +SpectatorConnect +called when a spectator connects to a server +============ +*/ +void() SpectatorConnect = +{ + bprint (PRINT_MEDIUM, "Spectator "); + bprint (PRINT_MEDIUM, self.netname); + bprint (PRINT_MEDIUM, " entered the game\n"); + self.goalentity = world; // used for impulse 1 below +}; +/* +=========== +SpectatorDisconnect +called when a spectator disconnects from a server +============ +*/ +void() SpectatorDisconnect = +{ + bprint (PRINT_MEDIUM, "Spectator "); + bprint (PRINT_MEDIUM, self.netname); + bprint (PRINT_MEDIUM, " left the game\n"); +}; +/* +================ +SpectatorImpulseCommand +Called by SpectatorThink if the spectator entered an impulse +================ +*/ +void() SpectatorImpulseCommand = +{ + if (self.impulse == 1) { + // teleport the spectator to the next spawn point + // note that if the spectator is tracking, this doesn't do + // much + self.goalentity = find(self.goalentity, classname, "info_player_deathmatch"); + if (self.goalentity == world) + self.goalentity = find(self.goalentity, classname, "info_player_deathmatch"); + if (self.goalentity != world) { + setorigin(self, self.goalentity.origin); + self.angles = self.goalentity.angles; + self.fixangle = TRUE; // turn this way immediately + } + } + self.impulse = 0; +}; +/* +================ +SpectatorThink +Called every frame after physics are run +================ +*/ +void() SpectatorThink = +{ + // self.origin, etc contains spectator position, so you could + // do some neat stuff here + if (self.impulse) + SpectatorImpulseCommand(); +}; diff --git a/ParoxysmII/source/subs.r b/ParoxysmII/source/subs.r new file mode 100644 index 0000000..4b6b4ad --- /dev/null +++ b/ParoxysmII/source/subs.r @@ -0,0 +1,246 @@ +#include "config.rh" + +#include "paroxysm.rh" + +void() SUB_Null = {}; +void() SUB_Remove = {remove(self);}; +/* +QuakeEd only writes a single float for angles (bad idea), so up and down are +just constant angles. +*/ +vector() SetMovedir = +{ + if (self.angles == '0 -1 0') + self.movedir = '0 0 1'; + else if (self.angles == '0 -2 0') + self.movedir = '0 0 -1'; + else + { + makevectors (self.angles); + self.movedir = v_forward; + } + + self.angles = '0 0 0'; +}; +/* +================ +InitTrigger +================ +*/ +void() InitTrigger = +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + if (self.angles != '0 0 0') + SetMovedir (); + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); // set size and link into world + self.movetype = MOVETYPE_NONE; + self.modelindex = 0; + self.model = ""; +}; +/* +============= +SUB_CalcMove +calculate self.velocity and self.nextthink to reach dest from +self.origin traveling at speed +=============== +*/ +void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt = +{ +local entity stemp; + stemp = self; + self = ent; + SUB_CalcMove (tdest, tspeed, func); + self = stemp; +}; + +void(vector tdest, float tspeed, void() func) SUB_CalcMove = +{ +local vector vdestdelta; +local float len, traveltime; + if (!tspeed) + objerror("No speed is defined!"); + self.think1 = func; + self.finaldest = tdest; + self.think = SUB_CalcMoveDone; + if (tdest == self.origin) + { + self.velocity = '0 0 0'; + self.nextthink = self.ltime + 0.1; + return; + } + +// set destdelta to the vector needed to move + vdestdelta = tdest - self.origin; + +// calculate length of vector + len = vlen (vdestdelta); + +// divide by speed to get time to reach dest + traveltime = len / tspeed; + if (traveltime < 0.03) + traveltime = 0.03; + +// set nextthink to trigger a think when dest is reached + self.nextthink = self.ltime + traveltime; +// scale the destdelta vector by the time spent traveling to get velocity + self.velocity = vdestdelta * (1/traveltime); // qcc won't take vec/float +}; + +/* + After moving, set origin to exact final destination +*/ +void() SUB_CalcMoveDone = +{ + setorigin (self, self.finaldest); + self.velocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1 (); +}; +/* +============= +SUB_CalcAngleMove +calculate self.avelocity and self.nextthink to reach destangle from +self.angles rotating +The calling function should make sure self.think is valid +=============== +*/ +void(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt = +{ + local entity stemp; + + stemp = self; + self = ent; + SUB_CalcAngleMove (destangle, tspeed, func); + self = stemp; +}; + +void (vector destangle, float tspeed, void() func) SUB_CalcAngleMove = +{ + local vector destdelta; + local float len, traveltime; + + if (!tspeed) + objerror ("No speed is defined!"); + + // set destdelta to the vector needed to move + destdelta = destangle - self.angles; + + // calculate length of vector + len = vlen (destdelta); + + // divide by speed to get time to reach dest + traveltime = len / tspeed; + + // set nextthink to trigger a think when dest is reached + self.nextthink = self.ltime + traveltime; + + // scale the destdelta vector by the time spent traveling to get velocity + self.avelocity = destdelta * (1 / traveltime); + + self.think1 = func; + self.finalangle = destangle; + self.think = SUB_CalcAngleMoveDone; +}; + +/* +============ +After rotating, set angle to exact final angle +============ +*/ +void() SUB_CalcAngleMoveDone = +{ + self.angles = self.finalangle; + self.avelocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1(); +}; +//============================================================================= +void() DelayThink = +{ + activator = self.enemy; + SUB_UseTargets (); + remove(self); +}; +/* +============================== +SUB_UseTargets +the global "activator" should be set to the entity that initiated the firing. +If self.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. +Centerprints any self.message to the activator. +Removes all entities with a targetname that match self.killtarget, +and removes them, so some events can remove other triggers. +Search for (string)targetname in all entities that +match (string)self.target and call their .use function +============================== +*/ +void() SUB_UseTargets = +{ + local entity t, stemp, otemp, act; + + + /* + check for a delay + */ + if (self.delay) { // create a temp object to fire at a later time + t = spawn(); + t.classname = "DelayedUse"; + t.nextthink = time + self.delay; + t.think = DelayThink; + t.enemy = activator; + t.message = self.message; + t.killtarget = self.killtarget; + t.target = self.target; + return; + } + + // print the message + if ((activator.classname == "player") && (self.message != "")) { + self.target_id_finished = time + 4; // don't let TargetID override centerprints + + centerprint (activator, self.message); + if (!self.noise) + sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM); + } + + // kill the killtagets + if (self.killtarget) { + t = world; + do { + if (!(t = find (t, targetname, self.killtarget))) + return; + + remove (t); + } while ( 1 ); + } + + // fire targets + if (self.target) { + act = activator; + t = world; + + do { + if (!(t = find (t, targetname, self.target))) + return; + + stemp = self; + otemp = other; + self = t; + other = stemp; + + if (self.use != SUB_Null) { + if (self.use) + self.use (); + } + + self = stemp; + other = otemp; + activator = act; + } while ( 1 ); + } + +}; diff --git a/ParoxysmII/source/targid.r b/ParoxysmII/source/targid.r new file mode 100644 index 0000000..d0e0fa7 --- /dev/null +++ b/ParoxysmII/source/targid.r @@ -0,0 +1,76 @@ +#include "config.rh" + +#include "paroxysm.rh" + +// POX - v1.1 target identifier ala Quake3 - displays the name of players who cross your sight +// by Frank Condello (POX) - http://www.planetquake.com/paroxysm/ - pox@planetquake.com +/* POX - from original Quake ai.qc +============= +visible +returns 1 if the entity is visible to self, even if not infront () +============= +*/ +float (entity targ) visible = +{ + local vector spot1, spot2; + + spot1 = self.origin + self.view_ofs; + spot2 = targ.origin + targ.view_ofs; + traceline (spot1, spot2, TRUE, self); // see through other monsters + + if (trace_inopen && trace_inwater) + return FALSE; // sight line crossed contents + if (trace_fraction == 1) + return TRUE; + + return FALSE; +}; +// Short and sweet.... +void() ID_CheckTarget = +{ + local vector org; + local entity spot; + local string idfrags; //POX v1.12 + + //Lost target, or target died + if (self.target_id_same < time || self.last_target_id.health < 1 || !visible(self.last_target_id)) + self.last_target_id = world; + + traceline (self.origin , (self.origin+(v_forward * 800)) , FALSE , self); + org = trace_endpos; + + spot = findradius(org, 200); + while (spot) + { + if ((spot.classname == "player") && spot.takedamage) + { + //Same target as last time + if (self.target_id_same > time && spot == self.last_target_id) + { + self.target_id_finished = time + 1.5; + self.target_id_same = time + 3; + return; + } + else if (spot != self && visible (spot) )//Found new Target + { + self.last_target_id = spot; + self.target_id_finished = time + 1.5; + self.target_id_same = time + 3; + + //POX v1.12 print the target's frags is observing + if (self.classname == "LMSobserver") + { + idfrags = ftos (self.last_target_id.frags); + centerprint4 (self, self.last_target_id.netname, "\n\n", idfrags, " frags remaining"); + } + else + centerprint (self, self.last_target_id.netname); + + return; + } + } + + spot = spot.chain; + + } +}; diff --git a/ParoxysmII/source/triggers.r b/ParoxysmII/source/triggers.r new file mode 100644 index 0000000..41bc813 --- /dev/null +++ b/ParoxysmII/source/triggers.r @@ -0,0 +1,661 @@ +#include "config.rh" + +#include "paroxysm.rh" + +entity stemp, otemp, s, old; +void() trigger_reactivate = +{ + self.solid = SOLID_TRIGGER; +}; +//============================================================================= + +#define SPAWNFLAG_NOMESSAGE 1 +#define SPAWNFLAG_NOTOUCH 1 + +// the wait time has passed, so set back up for another activation +void() multi_wait = +{ + if (self.max_health) { + self.health = self.max_health; + self.takedamage = DAMAGE_YES; + self.solid = SOLID_BBOX; + } +}; + +// the trigger was just touched/killed/used +// self.enemy should be set to the activator so it can be held through a delay +// so wait for the delay time before firing +void() multi_trigger = +{ + if (self.nextthink > time) { + return; // allready been triggered + } + + if (self.classname == "trigger_secret") { + if (self.enemy.classname != "player") + return; + + found_secrets++; + WriteByte (MSG_ALL, SVC_FOUNDSECRET); + } + + if (self.noise) + sound (self, CHAN_VOICE, self.noise, 1, ATTN_NORM); + + // don't trigger again until reset + self.takedamage = DAMAGE_NO; + activator = self.enemy; + + SUB_UseTargets (); + if (self.wait > 0) { + self.think = multi_wait; + self.nextthink = time + self.wait; + } else { + // we can't just remove (self) here, because this is a touch function + // called wheil C code is looping through area links... + + self.touch = NIL; + self.nextthink = time + 0.1; + self.think = SUB_Remove; + } +}; + +void() multi_killed = +{ + self.enemy = damage_attacker; + multi_trigger (); +}; + +void() multi_use = +{ + self.enemy = activator; + multi_trigger (); +}; + +void() multi_touch = +{ + if (other.classname != "player") + return; + + // if the trigger has an angles field, check player's facing direction + if (self.movedir != '0 0 0') { + makevectors (other.angles); + if (v_forward * self.movedir < 0) + return; // not facing the right way + } + + self.enemy = other; + multi_trigger (); +}; +/*QUAKED trigger_multiple (.5 .5 .5) ? notouch +Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time. +If "delay" is set, the trigger waits some time after activating before firing. +"wait" : Seconds between triggerings. (.2 default) +If notouch is set, the trigger is only fired by other entities, not by touching. +NOTOUCH has been obsoleted by trigger_relay! +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +void() trigger_multiple = +{ + if (self.sounds == 1) + { + precache_sound ("misc/secret.wav"); + self.noise = "misc/secret.wav"; + } + else if (self.sounds == 2) + { + precache_sound ("misc/talk.wav"); + self.noise = "misc/talk.wav"; + } + else if (self.sounds == 3) + { + precache_sound ("misc/trigger1.wav"); + self.noise = "misc/trigger1.wav"; + } + + if (!self.wait) + self.wait = 0.2; + self.use = multi_use; + InitTrigger (); + if (self.health) + { + if (self.spawnflags & SPAWNFLAG_NOTOUCH) + objerror ("health and notouch don't make sense\n"); + self.max_health = self.health; + self.th_die = multi_killed; + self.takedamage = DAMAGE_YES; + self.solid = SOLID_BBOX; + setorigin (self, self.origin); // make sure it links into the world + + // + POX + self.nobleed = TRUE; + // - POX + + } + else + { + if ( !(self.spawnflags & SPAWNFLAG_NOTOUCH) ) + { + self.touch = multi_touch; + } + } +}; +/*QUAKED trigger_once (.5 .5 .5) ? notouch +Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching +"targetname". If "health" is set, the trigger must be killed to activate. +If notouch is set, the trigger is only fired by other entities, not by touching. +if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. +if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +void() trigger_once = +{ + self.wait = -1; + trigger_multiple(); +}; +//============================================================================= +/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) +This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. +*/ +void() trigger_relay = +{ + self.use = SUB_UseTargets; +}; +//============================================================================= +/*QUAKED trigger_secret (.5 .5 .5) ? +secret counter trigger +sounds +1) secret +2) beep beep +3) +4) +set "message" to text string +*/ +void() trigger_secret = +{ + total_secrets = total_secrets + 1; + self.wait = -1; + if (!self.message) + self.message = "You found a secret area!"; + if (!self.sounds) + self.sounds = 1; + + if (self.sounds == 1) + { + precache_sound ("misc/secret.wav"); + self.noise = "misc/secret.wav"; + } + else if (self.sounds == 2) + { + precache_sound ("misc/talk.wav"); + self.noise = "misc/talk.wav"; + } + trigger_multiple (); +}; +//============================================================================= + +void() counter_use = +{ + (self.count)--; + if (self.count < 0) + return; + + if (self.count != 0) { + if (activator.classname == "player" && !(self.spawnflags & SPAWNFLAG_NOMESSAGE)) { + self.target_id_finished = time + 4;//POX v1.12 don't let TargetID override centerprints + + switch (self.count) { + case 1: + centerprint (activator, "Only 1 more to go..."); + break; + case 2: + centerprint (activator, "Only 2 more to go..."); + break; + case 3: + centerprint (activator, "Only 3 more to go..."); + break; + default: + centerprint (activator, "There are more to go..."); + break; + } + } + return; + } + + if (activator.classname == "player" && !(self.spawnflags & SPAWNFLAG_NOMESSAGE)) { + self.target_id_finished = time + 4;//POX don't let TargetID override centerprints + centerprint (activator, "Sequence completed!"); + } + + self.enemy = activator; + multi_trigger (); +}; + +/*QUAKED trigger_counter (.5 .5 .5) ? nomessage +Acts as an intermediary for an action that takes multiple inputs. +If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished. +After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself. +*/ +void() trigger_counter = +{ + self.wait = -1; + + if (!self.count) + self.count = 2; + + self.use = counter_use; +}; + +/* +============================================================================== +TELEPORT TRIGGERS +============================================================================== +*/ + +#define PLAYER_ONLY 1 +#define SILENT 2 + +void() play_teleport = +{ + local string tmpstr; + + // + POX - only 2 sounds now + if (random() > 0.5) + tmpstr = "misc/r_tele1.wav"; + else + tmpstr = "misc/r_tele2.wav"; + + sound (self, CHAN_VOICE, tmpstr, 1, ATTN_NORM); + remove (self); +}; + +void (vector org) spawn_tfog = +{ + s = spawn (); + s.origin = org; + s.nextthink = time + 0.1; // + POX - quicker + s.think = play_teleport; + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_TELEPORT); + WriteCoord (MSG_MULTICAST, org_x); + WriteCoord (MSG_MULTICAST, org_y); + WriteCoord (MSG_MULTICAST, org_z); + multicast (org, MULTICAST_PHS); +}; + +void() tdeath_touch = +{ + local entity other2; + if (other == self.owner) + return; + + // frag anyone who teleports in on top of an invincible player + if (other.classname == "player") { + if (other.invincible_finished > time && + self.owner.invincible_finished > time) { + self.classname = "teledeath3"; + other.invincible_finished = 0; + self.owner.invincible_finished = 0; + T_Damage (other, self, self, 50000); + other2 = self.owner; + self.owner = other; + T_Damage (other2, self, self, 50000); + } + + if (other.invincible_finished > time) + { + self.classname = "teledeath2"; + T_Damage (self.owner, self, self, 50000); + return; + } + + } + if (other.health) + { + T_Damage (other, self, self, 50000); + } +}; + +void(vector org, entity death_owner) spawn_tdeath = { + local entity death; + + death = spawn(); + death.classname = "teledeath"; + death.movetype = MOVETYPE_NONE; + death.solid = SOLID_TRIGGER; + death.angles = '0 0 0'; + setsize (death, death_owner.mins - '1 1 1', death_owner.maxs + '1 1 1'); + setorigin (death, org); + death.touch = tdeath_touch; + death.nextthink = time + 0.2; + death.think = SUB_Remove; + death.owner = death_owner; + + force_retouch = 2; // make sure even still objects get hit +}; + +void() teleport_touch = +{ + local entity t; + local vector org; + + if (other.teleport_time > time) + return; + +#if 0 + if ((other.classname != "player") + && (other.classname != "grenade") + && (other.classname != "missile") + && (other.classname != "spike") + && (other.classname != "")) { + return; + } +#endif + + if (self.targetname) { + if (self.nextthink < time) { + return; // not fired yet + } + } + + if (self.spawnflags & PLAYER_ONLY) { + if (other.classname != "player") + return; + } + + // allow teleporting objects that aren't alive + if ((other.health <= 0) && (other.solid == SOLID_NOT)) + return; + if (other.classname == "trigger_teleport") + return; + + SUB_UseTargets (); + + // put a tfog where the player was + spawn_tfog (other.origin); + t = find (world, targetname, self.target); + if (!t) + objerror ("couldn't find target"); + + // spawn a tfog flash in front of the destination + makevectors (t.mangle); + org = t.origin + 32 * v_forward; + + // only telefrag if it's a player + if (other.classname == "player") { + spawn_tdeath(t.origin, other); + } + spawn_tfog (org); + + if (!other.health) { // set up for nonliving objects + other.origin = t.origin; + other.velocity = (v_forward * other.velocity_x) + (v_forward * other.velocity_y); + other.fixangle = 1; + other.angles = t.mangle; + + other.teleport_time = time + 0.7; + return; + } + + setorigin (other, t.origin); + other.angles = t.mangle; + + if (other.classname == "player") { + other.fixangle = 1; // turn this way immediately + other.teleport_time = time + 0.7; + other.velocity = v_forward * 300; + + // + POX - hardcoded this (although WinGLQW doesn't allow fov changes) + stuffcmd (other, "fov 40;wait;fov 55;wait;fov 70;wait;fov 90;\n"); + // - POX + } + other.flags &= ~FL_ONGROUND; +}; +/*QUAKED info_teleport_destination (.5 .5 .5) (-8 -8 -8) (8 8 32) +This is the destination marker for a teleporter. It should have a "targetname" field with the same value as a teleporter's "target" field. +*/ + +void() info_teleport_destination = +{ +// this does nothing, just serves as a target spot + self.mangle = self.angles; + self.angles = '0 0 0'; + self.model = ""; + self.origin = self.origin + '0 0 27'; + if (!self.targetname) + objerror ("no targetname"); +}; + +void() teleport_use = +{ + self.nextthink = time + 0.2; + force_retouch = 2; // make sure even still objects get hit + self.think = SUB_Null; +}; + +/*QUAKED trigger_teleport (.5 .5 .5) ? PLAYER_ONLY SILENT +Any object touching this will be transported to the corresponding +info_teleport_destination entity. You must set the "target" field, and create +an object with a "targetname" field that matches. + +If the trigger_teleport has a targetname, it will only teleport entities when +it has been fired. +*/ +void() trigger_teleport = +{ + local vector o; + + InitTrigger (); + self.touch = teleport_touch; + + // make the target bigger + self.flags |= FL_ITEM; + + // find the destination + if (!self.target) + objerror ("no target"); + + self.use = teleport_use; + if (self.spawnflags & SILENT) + return; + + precache_sound ("ambience/hum1.wav"); + o = (self.mins + self.maxs) * 0.5; + ambientsound (o, "ambience/hum1.wav", 0.5, ATTN_STATIC); +}; + +/* +============================================================================== +trigger_setskill +============================================================================== +*/ +/*QUAKED trigger_setskill (.5 .5 .5) ? +sets skill level to the value of "message". +Only used on start map. +*/ +void() trigger_setskill = +{ + remove (self); +}; + +/* +============================================================================== +ONLY REGISTERED TRIGGERS +============================================================================== +*/ +void() trigger_onlyregistered_touch = +{ + if (other.classname != "player") + return; + + if (self.attack_finished > time) + return; + + self.attack_finished = time + 2; + + if (cvar ("registered")) { + self.message = ""; + SUB_UseTargets (); + remove (self); + } else { + if (self.message != "") { + self.target_id_finished = time + 4;//POX don't let TargetID override centerprints + + centerprint (other, self.message); + sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM); + } + } +}; + +/*QUAKED trigger_onlyregistered (.5 .5 .5) ? +Only fires if playing the registered version, otherwise prints the message +*/ +void() trigger_onlyregistered = +{ + precache_sound ("misc/talk.wav"); + InitTrigger (); + self.touch = trigger_onlyregistered_touch; +}; + +//============================================================================ +void() hurt_on = +{ + self.solid = SOLID_TRIGGER; + self.nextthink = -1; +}; + +void() hurt_touch = +{ + if (other.takedamage) { + self.solid = SOLID_NOT; + T_Damage (other, self, self, self.dmg); + self.think = hurt_on; + self.nextthink = time + 1; + } + return; +}; + +/*QUAKED trigger_hurt (.5 .5 .5) ? +Any object touching this will be hurt +set dmg to damage amount +defalt dmg = 5 +*/ +void() trigger_hurt = +{ + InitTrigger (); + self.touch = hurt_touch; + if (!self.dmg) + self.dmg = 5; +}; + +//============================================================================ +#define PUSH_ONCE 1 + +void() trigger_push_touch = +{ + if (other.classname == "grenade") + other.velocity = self.speed * self.movedir * 10; + else if (other.health > 0) { + other.velocity = self.speed * self.movedir * 10; + if (other.classname == "player") { + if (other.fly_sound < time) { + other.fly_sound = time + 1.5; + sound (other, CHAN_AUTO, "ambience/windfly.wav", 1, ATTN_NORM); + } + } + } + + if (self.spawnflags & PUSH_ONCE) + remove (self); +}; + +/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE +Pushes the player +*/ +void() trigger_push = +{ + InitTrigger (); + precache_sound ("ambience/windfly.wav"); + self.touch = trigger_push_touch; + if (!self.speed) + self.speed = 1000; +}; + +.float bounce_time; + +void() trigger_bounce_touch = +{ + if (other.bounce_time > time) + return; + + if ((other.classname == "player") && (other.health > 0)) { + other.velocity = self.angles; + other.bounce_time = time + 0.8; + + sound (other, CHAN_AUTO, "misc/menu2.wav", 1, ATTN_NORM); + } +}; + +/*QUAKEED trigger_bouncepad (.5 .5 .5) ? +Q3A-style bounce pad. +Set angles to the desired acceleration: +x is east, y is north, z is vertical. A value of '0 40 800' is a vertical bounce with a slight northerly direction. +*/ +void() trigger_bouncepad = +{ + self.solid = SOLID_TRIGGER; + setmodel (self, self.model); // set size and link into world + self.movetype = MOVETYPE_NONE; + self.modelindex = 0; + self.model = ""; + self.touch = trigger_bounce_touch; +}; + +//============================================================================ + +void() trigger_monsterjump_touch = +{ + if ((other.flags & (FL_MONSTER | FL_FLY | FL_SWIM)) != FL_MONSTER) + return; + + // set XY even if not on ground, so the jump will clear lips + other.velocity_x = self.movedir_x * self.speed; + other.velocity_y = self.movedir_y * self.speed; + + if (!(other.flags & FL_ONGROUND)) + return; + + other.flags &= ~FL_ONGROUND; + other.velocity_z = self.height; +}; + +/*QUAKED trigger_monsterjump (.5 .5 .5) ? +Walking monsters that touch this will jump in the direction of the trigger's angle +"speed" default to 200, the speed thrown forward +"height" default to 200, the speed thrown upwards +*/ +void() trigger_monsterjump = +{ + if (!self.speed) + self.speed = 200; + + if (!self.height) + self.height = 200; + + if (self.angles == '0 0 0') + self.angles = '0 360 0'; + + InitTrigger (); + + self.touch = trigger_monsterjump_touch; +}; diff --git a/ParoxysmII/source/wall.r b/ParoxysmII/source/wall.r new file mode 100644 index 0000000..67f3674 --- /dev/null +++ b/ParoxysmII/source/wall.r @@ -0,0 +1,329 @@ +#include "config.rh" + +#include "paroxysm.rh" + +/* + Breakable object code + + These are really cool entities, but try not to overuse them. +*/ +#define SPAWN_GLASS 2 +#define SPAWN_METAL 4 +#define SPAWN_WOOD 8 + +.integer gib_caught; // if a gib bounces more than 4 times it's removed + +vector (float dm, vector dir) VelocityForRubble = +{ + local vector v, gib_dir; + + gib_dir = normalize (dir); + + v_x = 90 * gib_dir_x + (random () * 70 - 35); + v_y = 90 * gib_dir_y + (random () * 70 - 35); + v_z = 200 + 100 * random (); + + if (dm > -50) { + v *= 0.9; + } else if (dm > -200) { + v *= 2; + } else { + v *= 10; + } + + return v; +}; + +void () brik_touch = +{ + local float vol; // randomize the volume so it don't sound like shit + + vol = random (); + + if (self.velocity == '0 0 0') { + self.avelocity = '0 0 0'; + self.solid = SOLID_NOT; + self.touch = NIL; + self.think = SUB_Remove; + self.nextthink = time + (2 * random()); + return; + } + + if (self.gib_caught > 4) { + remove (self); + return; + } + + // Gib already bounced twice, so remove damage (too easy to get killed) + if (self.gib_caught > 1) + self.dmg = 0; + + if (self.dmg) { // do damage if set + if (other.takedamage) { + T_Damage (other, self, self.owner, self.dmg); + remove (self); + } + } + + self.gib_caught++; + + if (!(self.cnt)) + return; + + if (vol < 0.3) // mute low volume + return; + + if (pointcontents(self.origin) == CONTENT_SOLID) { // bounce sound + switch (self.cnt) { + case 1: + sound (self, CHAN_AUTO, "ambience/brik_hit.wav", vol, ATTN_NORM); + break; + case 2: + sound (self, CHAN_AUTO, "ambience/brikhit2.wav", vol, ATTN_NORM); + break; + case 3: + sound (self, CHAN_AUTO, "ambience/methit1.wav", vol, ATTN_NORM); + break; + case 4: + sound (self, CHAN_AUTO, "ambience/woodhit1.wav", vol, ATTN_NORM); + break; + case 6: + sound (self, CHAN_AUTO, "ambience/methit2.wav", vol, ATTN_NORM); + break; + case 8: + sound (self, CHAN_AUTO, "ambience/woodhit2.wav", vol, ATTN_NORM); + break; + } + } +}; + +void (string gibname, float dm, vector ddir) ThrowRubble = +{ + local entity new; + local float sndrnd; + + new = spawn (); + sndrnd = random (); + + // new.origin = self.origin doesnt work because the origin + // is at world (0,0,0). + new.origin_x = self.absmin_x + (random() * self.size_x); + new.origin_y = self.absmin_y + (random() * self.size_y); + new.origin_z = self.absmin_z + (random() * self.size_z); + + setmodel (new, gibname); + setsize (new, '0 0 0', '0 0 0'); + + if (sndrnd < 0.25) + new.cnt = 1; + else if (sndrnd < 0.5) + new.cnt = 2; + + // No bounce sound for glass since initial sound drags on for a bit + if (self.spawnflags & SPAWN_GLASS) + new.cnt = 0; + + if (self.spawnflags & SPAWN_METAL) + new.cnt *= 3; // Play metal bounce on 3 & 6 + + if (self.spawnflags & SPAWN_WOOD) { + new.cnt *= 4; // Play wood bounce on 4 & 8 + new.skin = 1; // Change skin to wood if wood gib + } + + new.velocity = VelocityForRubble (dm, ddir); + new.movetype = MOVETYPE_BOUNCE; + new.classname = "rubble"; + new.solid = SOLID_BBOX; + new.touch = brik_touch; + new.avelocity_x = random () * 600; + new.avelocity_y = random () * 600; + new.avelocity_z = random () * 600; + new.think = SUB_Remove; + new.ltime = time; + new.nextthink = time + 10 + random () * 10; + new.dmg = self.dmg; + new.frame = 0; + new.flags = 0; +}; + +/* + wall_killed + + Called when a wall is destroyed. Throws "gibs" (rubble). +*/ +void () wall_killed = +{ + local entity sndspot; + local float rubble_count = 0; + local string md1, md2, md3, md4; + + sndspot = spawn(); + sndspot.origin = self.absmin; + setorigin (sndspot, sndspot.origin); + + if (self.spawnflags & SPAWN_GLASS) + sound (sndspot, CHAN_AUTO, "ambience/glassbrk.wav", 1, ATTN_NORM); + else if (self.spawnflags & SPAWN_METAL) + sound (sndspot, CHAN_AUTO, "ambience/metbrk.wav", 1, ATTN_NORM); + else if (self.spawnflags & SPAWN_WOOD) + sound (sndspot, CHAN_AUTO, "ambience/woodbrk.wav", 1, ATTN_NORM); + else // New rubble sound + sound (sndspot, CHAN_AUTO, "ambience/wall01.wav", 1, ATTN_NORM); + + remove (sndspot); + + // determine volume of destroyed wall and throw rubble accordingly + rubble_count = (self.size_x * self.size_y * self.size_z) / 64000; + + if (rubble_count > 5) + rubble_count = 6; + + if (self.spawnflags & SPAWN_GLASS) { + md4 = md3 = md2 = md1 = "progs/glassrub.md"; + + while (rubble_count > -1) { + self.dest_x = (random () * 100) - 50; + self.dest_y = (random () * 100) - 50; + self.dest_z = (random () * 100); + + // This was cut down by 1/5 to deal with packet overflow errors + ThrowRubble (md1, -100, self.dest); + ThrowRubble (md2, -100, self.dest); + ThrowRubble (md3, -100, self.dest); + ThrowRubble (md4, -100, self.dest); + rubble_count--; + } + } else { + if (self.spawnflags & SPAWN_METAL) { + md1 = "progs/mwrub1.mdl"; + md2 = "progs/mwrub2.mdl"; + md4 = md3 = "progs/mwrub3.mdl"; + } else if (self.spawnflags & SPAWN_WOOD) { + md1 = "progs/mwrub1.mdl"; + md4 = md2 = "progs/mwrub2.mdl"; + md3 = "progs/mwrub3.mdl"; + } else { + md1 = "progs/rubble1.mdl"; + md4 = md2 = "progs/rubble2.mdl"; + md3 = "progs/rubble3.mdl"; + } + + while (rubble_count > -1) { + self.dest_x = (random () * 100) - 50; + self.dest_y = (random () * 100) - 50; + self.dest_z = (random () * 100); + + ThrowRubble (md1, self.health, self.dest); + ThrowRubble (md2, self.health, self.dest); + ThrowRubble (md3, self.health, self.dest); + ThrowRubble (md4, self.health, self.dest); + rubble_count--; + } + } + activator = self; + + SUB_UseTargets (); + + self.no_obj = TRUE; // mine fix - mines will detonate if spawnmaster.no_obj = TRUE (blown up) + remove (self); +}; + +void() wall_pain = +{ + if (self.health > 0) + self.health = self.max_health; +}; + +void() wall_use = +{ + self.health = -100; + self.dest_x = (random () * 10) - 5; + self.dest_y = (random () * 10) - 5; + self.dest_z = (random () * 10); + wall_killed (); +}; + +/*QUAKED exploding_wall +When the exploding wall is shot, it "gibs" into rubble. +Can also be triggered to explode. + +"target" all entities with a matching targetname will be used when killed +"health" the amount of damage needed to destroy the wall instead of touched +"dmg" damage caused when hit by a gib + +New Spawnflags added for PAROXYSM: + +SPAWN_GLASS - glass explosion + +SPAWN_METAL - metal shrapnel + +SPAWN_WOOD - wood splintering + +- no spawnflags is concrete rubble + +Although it is possible to combine different types of explosions on one object, +it is not recommended. You can easily get overflow errors on large objects AND +since wood and metal share a gib model, no metal skins will be used if the +wood spawnflag is set. +*/ +void() exploding_wall = +{ + setmodel (self, self.model); + + // New precache routine + + precache_sound("zombie/z_hit.wav"); // for damage + + if (self.spawnflags & SPAWN_GLASS) { + precache_model("progs/glassrub.mdl"); + precache_sound("ambience/glassbrk.wav"); + } else if (self.spawnflags & SPAWN_METAL) { + precache_model("progs/mwrub1.mdl"); + precache_model("progs/mwrub2.mdl"); + precache_model("progs/mwrub3.mdl"); + precache_sound("ambience/metbrk.wav"); + precache_sound("ambience/methit1.wav"); + precache_sound("ambience/methit2.wav"); + } else if (self.spawnflags & SPAWN_WOOD) { + precache_model("progs/mwrub1.mdl"); + precache_model("progs/mwrub2.mdl"); + precache_model("progs/mwrub3.mdl"); + precache_sound("ambience/woodbrk.wav"); + precache_sound("ambience/woodhit1.wav"); + precache_sound("ambience/woodhit2.wav"); + } else { // precache concrete + precache_model("progs/rubble1.mdl"); + precache_model("progs/rubble2.mdl"); + precache_model("progs/rubble3.mdl"); + precache_sound("ambience/wall01.wav"); + precache_sound("ambience/brik_hit.wav"); + precache_sound("ambience/brikhit2.wav"); + } + + self.solid = SOLID_BBOX; + self.movetype = MOVETYPE_NONE; + + // POX v1.2 - default gib damage to 1 + if (!self.dmg) + self.dmg = 1; + + self.nobleed = TRUE; + + if (self.health) { + self.max_health = self.health; + self.th_die = wall_killed; + self.takedamage = DAMAGE_YES; + } else { + self.max_health = 100; + self.th_die = wall_killed; + self.takedamage = DAMAGE_YES; + } + + self.th_pain = wall_pain; + + if (self.targetname) { + self.use = wall_use; + self.max_health = 10000; + } +}; diff --git a/ParoxysmII/source/weapons.r b/ParoxysmII/source/weapons.r new file mode 100644 index 0000000..a920c4f --- /dev/null +++ b/ParoxysmII/source/weapons.r @@ -0,0 +1,1529 @@ +#include "paroxysm.rh" + +/* +*/ + + +// called by worldspawn - POX - added lots'o'junk here... +void() W_Precache = +{ + precache_model ("progs/plasma.mdl"); + precache_model ("progs/laser.mdl"); + + precache_sound ("weapons/r_exp3.wav"); // new rocket explosion + precache_sound ("weapons/rocket1i.wav"); // spike gun + precache_sound ("weapons/sgun1.wav"); + precache_sound ("weapons/ric1.wav"); // ricochet (used in c code) + precache_sound ("weapons/ric2.wav"); // ricochet (used in c code) + precache_sound ("weapons/ric3.wav"); // ricochet (used in c code) + precache_sound ("weapons/spike2.wav"); // super spikes + precache_sound ("weapons/hog.wav"); // new nailgun sound + precache_sound ("weapons/tink1.wav"); // spikes tink (used in c code) + precache_sound ("weapons/tink2.wav"); + precache_sound ("weapons/gren.wav"); // grenade launcher + precache_sound ("weapons/gren2.wav"); // second trigger grenades + precache_sound ("weapons/bounce.wav"); // grenade bounce + precache_sound ("weapons/bounce2.wav"); // grenade bounce alt + precache_sound ("weapons/shotgn2.wav"); // super shotgun + + precache_sound ("weapons/mfire1.wav"); // misfire + precache_sound ("weapons/mfire2.wav"); // megaplasma burst misfire + precache_sound ("weapons/plasma.wav"); // plasmagun fire + precache_sound ("weapons/mplasma.wav"); // megaplasmagun fire + precache_sound ("weapons/mplasex.wav"); // megaplasmagun explosion + precache_sound ("weapons/gren.wav"); // super shotgun grenade fire + precache_sound ("weapons/armed.wav"); // mine armed sound + precache_sound ("weapons/minedet.wav"); //mine detonate click + + precache_sound ("weapons/rhino.wav"); //rhino firing sound + precache_sound ("weapons/rhinore.wav"); //rhino reload sound + precache_sound ("weapons/error.wav"); //weapon error sound + + precache_sound ("weapons/tsload.wav"); //t-shot load + precache_sound ("weapons/tsfire.wav"); //t-shot single fire + precache_sound ("weapons/ts3fire.wav"); //t-shot triple fire + + + precache_sound ("weapons/shrapdet.wav"); //ShrapnelBomb detonation-confirmation beep + + /* + Precached models + */ + precache_model ("progs/mwrub1.mdl"); //Shrapnel Model + +/* + // VisWeap - Player + precache_model ("progs/bsaw_p.mdl"); + precache_model ("progs/tshot_p.mdl"); + precache_model ("progs/combo_p.mdl"); + precache_model ("progs/plasma_p.mdl"); + precache_model ("progs/nail_p.mdl"); + precache_model ("progs/gren_p.mdl"); + precache_model ("progs/rhino_p.mdl"); + + // VisWeap - Weapon drop + precache_model ("progs/d_bsaw.mdl"); + precache_model ("progs/d_tshot.mdl"); + precache_model ("progs/d_combo.mdl"); + precache_model ("progs/d_plasma.mdl"); + precache_model ("progs/d_nail.mdl"); + precache_model ("progs/d_gren.mdl"); + precache_model ("progs/d_rhino.mdl"); + + // No weapon Death Model (weapons are dropped) + precache_model ("progs/death_p.mdl"); +*/ +}; + +/* +================ +W_FireAxe +================ +*/ +void() W_FireAxe = +{ + local vector source; + local vector org; + + makevectors (self.v_angle); + source = self.origin + '0 0 16'; + traceline (source, source + v_forward*64, FALSE, self); + if (trace_fraction == 1.0) + return; + + org = trace_endpos - v_forward*4; + + if (trace_ent.takedamage) { + trace_ent.axhitme = 1; + SpawnBlood (org, 20); + //if (deathmatch > 3) + // T_Damage (trace_ent, self, self, 75); + //else + T_Damage (trace_ent, self, self, 20); + } else { // hit wall + sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_GUNSHOT); + WriteByte (MSG_MULTICAST, 3); + WriteCoord (MSG_MULTICAST, org_x); + WriteCoord (MSG_MULTICAST, org_y); + WriteCoord (MSG_MULTICAST, org_z); + multicast (org, MULTICAST_PVS); + } +}; + + +//============================================================================ + +/* +================ +SpawnMeatSpray +================ +*/ +void(vector org, vector vel) SpawnMeatSpray = +{ + local entity missile; + + missile = spawn (); + missile.owner = self; + missile.movetype = MOVETYPE_BOUNCE; + missile.solid = SOLID_NOT; + + makevectors (self.angles); + + missile.velocity = vel; + missile.velocity_z = missile.velocity_z + 250 + 50*random(); + + missile.avelocity = '3000 1000 2000'; + +// set missile duration + missile.nextthink = time + 1; + missile.think = SUB_Remove; + + setmodel (missile, "progs/zom_gib.mdl"); + setsize (missile, '0 0 0', '0 0 0'); + setorigin (missile, org); +}; + +/* +================ +SpawnBlood +================ +*/ +void(vector org, float damage) SpawnBlood = +{ + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + + if (trace_ent.nobleed) { + WriteByte (MSG_MULTICAST, TE_GUNSHOT); + WriteByte (MSG_MULTICAST, 5); + } else { + WriteByte (MSG_MULTICAST, TE_BLOOD); + WriteByte (MSG_MULTICAST, 1); + } + WriteCoord (MSG_MULTICAST, org_x); + WriteCoord (MSG_MULTICAST, org_y); + WriteCoord (MSG_MULTICAST, org_z); + multicast (org, MULTICAST_PVS); +}; + + +/* +================ +spawn_touchblood +================ +*/ +void(float damage) spawn_touchblood = +{ + local vector vel; + + vel = wall_velocity () * 0.2; + + SpawnBlood (self.origin + vel*0.01, damage); +}; + +/* +============================================================================== + +MULTI-DAMAGE + +Collects multiple small damages into a single damage + +============================================================================== +*/ + +entity multi_ent; +float multi_damage; + +vector blood_org; +float blood_count; + +vector puff_org; +float puff_count; + +void() ClearMultiDamage = +{ + multi_ent = world; + multi_damage = 0; + blood_count = 0; + puff_count = 0; +}; + +void() ApplyMultiDamage = +{ + if (!multi_ent) + return; + T_Damage (multi_ent, self, self, multi_damage); +}; + +void(entity hit, float damage) AddMultiDamage = +{ + if (!hit) + return; + + if (hit != multi_ent) + { + ApplyMultiDamage (); + multi_damage = damage; + multi_ent = hit; + } + else + multi_damage = multi_damage + damage; +}; + +void() Multi_Finish = +{ + if (puff_count) + { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_GUNSHOT); + WriteByte (MSG_MULTICAST, puff_count); + WriteCoord (MSG_MULTICAST, puff_org_x); + WriteCoord (MSG_MULTICAST, puff_org_y); + WriteCoord (MSG_MULTICAST, puff_org_z); + multicast (puff_org, MULTICAST_PVS); + } + + if (blood_count) + { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_BLOOD); + WriteByte (MSG_MULTICAST, blood_count); + WriteCoord (MSG_MULTICAST, blood_org_x); + WriteCoord (MSG_MULTICAST, blood_org_y); + WriteCoord (MSG_MULTICAST, blood_org_z); + multicast (puff_org, MULTICAST_PVS); + } +}; + +/* +============================================================================== +BULLETS +============================================================================== +*/ + +/* +================ +TraceAttack +================ +*/ +void(float damage, vector dir) TraceAttack = +{ + local vector vel, org; + + vel = normalize(dir + v_up*crandom() + v_right*crandom()); + vel = vel + 2*trace_plane_normal; + vel = vel * 200; + + org = trace_endpos - dir*4; + + // + POX - added nobleed check + if (trace_ent.takedamage && !trace_ent.nobleed) + { + blood_count = blood_count + 1; + blood_org = org; + AddMultiDamage (trace_ent, damage); + } + + else if (trace_ent.takedamage && trace_ent.nobleed) + { + puff_count = puff_count + 1; + puff_org = org; + AddMultiDamage (trace_ent, damage); + } + // - POX + + else + { + puff_count = puff_count + 1; + } +}; + +/* POX - for the tShot second trigger, half the bullets twice the damage +================ +FireBullets2 + +Used by shotgun, super shotgun, and enemy soldier firing +Go to the trouble of combining multiple pellets into a single damage call. +================ +*/ +void(float shotcount, vector dir, vector spread) FireBullets2 = +{ + local vector direction; + local vector src; + + makevectors(self.v_angle); + + src = self.origin + v_forward*10; + src_z = self.absmin_z + self.size_z * 0.7; + + ClearMultiDamage (); + + traceline (src, src + dir*2048, FALSE, self); + puff_org = trace_endpos - dir*4; + + while (shotcount > 0) + { + direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up; + traceline (src, src + direction*2048, FALSE, self); + if (trace_fraction != 1.0) + TraceAttack (8, direction); //POX 4*2 + + shotcount = shotcount - 1; + } + ApplyMultiDamage (); + Multi_Finish (); +}; + +/* +================ +FireBullets + +Used by shotgun, super shotgun, and enemy soldier firing +Go to the trouble of combining multiple pellets into a single damage call. +================ +*/ +void(float shotcount, vector dir, vector spread) FireBullets = +{ + local vector direction; + local vector src; + + makevectors(self.v_angle); + + src = self.origin + v_forward*10; + src_z = self.absmin_z + self.size_z * 0.7; + + ClearMultiDamage (); + + traceline (src, src + dir*2048, FALSE, self); + puff_org = trace_endpos - dir*4; + + while (shotcount > 0) + { + direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up; + traceline (src, src + direction*2048, FALSE, self); + if (trace_fraction != 1.0) + TraceAttack (4, direction); + + shotcount = shotcount - 1; + } + ApplyMultiDamage (); + Multi_Finish (); +}; + +/* +================ +W_FireShotgun +================ +*/ +void() W_FireShotgun = +{ + local vector dir; + + sound (self, CHAN_WEAPON, "weapons/tsfire.wav", 1, ATTN_NORM); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); + + //POX - May not need this (SVC_SMALLKICK?) + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -35; + + self.currentammo = self.ammo_shells = self.ammo_shells - 1; + + dir = aim (self, 100000); + FireBullets (6, dir, '0.04 0.04 0'); +}; + + +/* +================ +W_FireSuperShotgun +================ +*/ +void() W_FireSuperShotgun = +{ + local vector dir; + local float bullets, used; + + bullets = 14; + used = 2; + + //POX v1.1 don't plat tShot sound... + if (self.currentammo == 1) + { + bullets = 6; + used = 1; + } + + sound (self ,CHAN_WEAPON, "weapons/shotgn2.wav", 1, ATTN_NORM); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_BIGKICK); + + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -60; + + //if (deathmatch != 4) + self.currentammo = self.ammo_shells = self.ammo_shells - used; + + dir = aim (self, 100000); + FireBullets (14, dir, '0.14 0.08 0'); +}; + + +/* +============================================================================== + +ROCKETS + +============================================================================== +*/ + +void() T_MissileTouch = +{ + local float damg; + + if (other == self.owner) + return; // don't explode on owner + + if (self.voided) { + return; + } + self.voided = 1; + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + + damg = 20 + random()*10; + + if (other.health) + { + other.deathtype = "rocket"; + T_Damage (other, self, self.owner, damg ); + } + + // don't do radius damage to the other, because all the damage + // was done in the impact + + + T_RadiusDamage (self, self.owner, 90, other, "rocket"); + +// sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM); + self.origin = self.origin - 8 * normalize(self.velocity); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + + remove(self); +}; + + + +/* +================ +W_FireRocket +================ +*/ +void(vector barrel) W_FireRocket = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 0.5; + + //if player fired last rocket reset the reload bit + if (self.ammo_rockets == 0) + self.reload_rocket = 0; + else //player has rockets left so ad 1 to reload count + self.reload_rocket = self.reload_rocket + 1; + + newmis = spawn (); + newmis.owner = self; + newmis.movetype = MOVETYPE_TOSS; + newmis.solid = SOLID_BBOX; + +// set newmis speed + + makevectors (self.v_angle); + + newmis.velocity = v_forward*1100 + v_up * 220 + v_right* -22; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = T_MissileTouch; + + newmis.voided = 0; + +// set newmis duration + newmis.nextthink = time + 5; + newmis.think = SUB_Remove; + newmis.classname = "rocket"; + + setmodel (newmis, "progs/grenade.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin + v_forward* 8 + v_right* 12 + barrel); +}; + +//============================================================================= + + +void() GrenadeExplode = +{ + if (self.voided) { + return; + } + self.voided = 1; + + T_RadiusDamage (self, self.owner, 120, world, "grenade"); + + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_EXPLOSION); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + + remove (self); +}; + +void() GrenadeTouch = +{ + local float r; + + r=random(); + + if (other == self.owner) + return; // don't explode on owner + if (other.takedamage == DAMAGE_AIM) + { + GrenadeExplode(); + return; + } + + //pick a bounce sound + if (r < 0.75) + sound (self, CHAN_VOICE, "weapons/bounce.wav", 1, ATTN_NORM); + else + sound (self, CHAN_VOICE, "weapons/bounce2.wav", 1, ATTN_NORM); + + if (self.velocity == '0 0 0') + self.avelocity = '0 0 0'; +}; + +/* +================ +W_FireGrenade +================ +*/ +void() W_FireGrenade = +{ + self.currentammo = self.ammo_rockets = self.ammo_rockets - 1; + + sound (self, CHAN_WEAPON, "weapons/gren.wav", 1, ATTN_NORM); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); + + if (self.flags & FL_ONGROUND) + self.velocity = self.velocity + v_forward* -75; + + newmis = spawn (); + newmis.voided=0; + newmis.owner = self; + newmis.movetype = MOVETYPE_BOUNCE; + newmis.solid = SOLID_BBOX; + newmis.classname = "grenade"; + +// set newmis speed + + makevectors (self.v_angle); + + if (self.v_angle_x) + newmis.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10; + else + { + newmis.velocity = aim(self, 10000); + newmis.velocity = newmis.velocity * 600; + newmis.velocity_z = 200; + } + + newmis.avelocity = '300 300 300'; + + newmis.angles = vectoangles(newmis.velocity); + + newmis.touch = GrenadeTouch; + + + newmis.nextthink = time + 2.5; + + newmis.think = GrenadeExplode; + + setmodel (newmis, "progs/grenade.mdl"); + setsize (newmis, '0 0 0', '0 0 0'); + setorigin (newmis, self.origin); +}; + + +//============================================================================= +// + POX - Plasma + +void() plasma_touch = +{ + if (other == self.owner) + return; + + if (self.voided) { + return; + } + self.voided = 1; + + if (other.solid == SOLID_TRIGGER) + return; // trigger field, do nothing + + if (pointcontents(self.origin) == CONTENT_SKY) + { + remove(self); + return; + } + +// hit something that bleeds + if (other.takedamage) + { + spawn_touchblood (7); + other.deathtype = "plasma"; + T_Damage (other, self, self.owner, 7); + } + else + { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_SPIKE); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + } + + remove(self); + +}; + +void(vector org, vector dir) launch_plasma = +{ + newmis = spawn (); + newmis.voided=0; + newmis.owner = self; + newmis.movetype = MOVETYPE_FLYMISSILE; + newmis.solid = SOLID_BBOX; + + newmis.angles = vectoangles(dir); + + newmis.touch = plasma_touch; + newmis.classname = "plasma"; + newmis.think = SUB_Remove; + newmis.nextthink = time + 5; + setmodel (newmis, "progs/laser.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + setorigin (newmis, org); + + newmis.velocity = dir * 1400; +}; + +void(float ox) W_FirePlasma = +{ + local vector dir; + + makevectors (self.v_angle); + + sound (self, CHAN_WEAPON, "weapons/plasma.wav", 1, ATTN_NORM); + + self.currentammo = self.ammo_cells = self.ammo_cells - 1; + + dir = aim (self, 1000); + + launch_plasma (self.origin + v_forward*12 + '0 0 12' + v_right*ox, dir); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); +}; + +// - POX - Plasma +//=========================================================================== + +void() spike_touch = +{ + if (self.voided) { + return; + } + self.voided = 1; + + if (other.solid == SOLID_TRIGGER) + return; // trigger field, do nothing + + if (pointcontents (self.origin) == CONTENT_SKY) { + remove (self); + return; + } + + // hit something that bleeds + if (other.takedamage) { + spawn_touchblood (9); + other.deathtype = "nail"; + T_Damage (other, self, self.owner, 9); + } else { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_SPIKE); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + } + + remove (self); +}; + +void() superspike_touch = +{ + if (self.voided) { + return; + } + self.voided = 1; + + if (other.solid == SOLID_TRIGGER) + return; // trigger field, do nothing + + if (pointcontents (self.origin) == CONTENT_SKY) { + remove (self); + return; + } + + // hit something that bleeds + if (other.takedamage) { + spawn_touchblood (18); + other.deathtype = "supernail"; + T_Damage (other, self, self.owner, 18); + } else { + WriteByte (MSG_MULTICAST, SVC_TEMPENTITY); + WriteByte (MSG_MULTICAST, TE_SUPERSPIKE); + WriteCoord (MSG_MULTICAST, self.origin_x); + WriteCoord (MSG_MULTICAST, self.origin_y); + WriteCoord (MSG_MULTICAST, self.origin_z); + multicast (self.origin, MULTICAST_PHS); + } + + remove (self); +}; + + + +/* +=============== +launch_spike + +Used for both the player and the ogre +=============== +*/ +void(vector org, vector dir) launch_spike = +{ + newmis = spawn (); + newmis.voided=0; + newmis.owner = self; + newmis.movetype = MOVETYPE_FLYMISSILE; + newmis.solid = SOLID_BBOX; + + newmis.angles = vectoangles(dir); + + newmis.touch = spike_touch; + newmis.classname = "spike"; + newmis.think = SUB_Remove; + newmis.nextthink = time + 6; + setmodel (newmis, "progs/spike.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + setorigin (newmis, org); + + newmis.velocity = dir * 1000; +}; + +void(float ox) W_FireNails = +{ + local vector dir; + + makevectors (self.v_angle); + + self.weaponmodel = "progs/v_nailgl.mdl"; // light up nailgun barrels + + sound (self, CHAN_WEAPON, "weapons/hog.wav", 0.8, ATTN_NORM); + + self.currentammo = self.ammo_nails = self.ammo_nails - 1; + + dir = aim (self, 1000); + launch_spike (self.origin + '0 0 16' + v_right*ox, dir); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); +}; + +// POX - not used by players...but maybe traps (?) +void() W_FireSuperSpikes = +{ + local vector dir; + + sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.2; + //if (deathmatch != 4) + self.currentammo = self.ammo_nails = self.ammo_nails - 2; + dir = aim (self, 1000); + launch_spike (self.origin + '0 0 16', dir); + newmis.touch = superspike_touch; + setmodel (newmis, "progs/s_spike.mdl"); + setsize (newmis, VEC_ORIGIN, VEC_ORIGIN); + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); +}; + +void(float ox) W_FireSpikes = +{ + local vector dir; + + makevectors (self.v_angle); + + if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN) { + W_FireSuperSpikes (); + return; + } + + if (self.ammo_nails < 1) { + self.weapon = W_BestWeapon (); + W_SetCurrentAmmo (); + return; + } + + sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.2; + + self.currentammo = --self.ammo_nails; + + dir = aim (self, 1000); + launch_spike (self.origin + '0 0 16' + v_right * ox, dir); + + msg_entity = self; + WriteByte (MSG_ONE, SVC_SMALLKICK); +}; + + +// - POX ? +//.float hit_z; + +/* +=============================================================================== + +PLAYER WEAPON USE + +=============================================================================== +*/ +// + POX - changed weapon models, added ammo check for ComboGun +void() W_SetCurrentAmmo = +{ + player_run (); // get out of any weapon firing states + + self.items &= ~(IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS); + self.weaponframe = 0; + + switch (self.weapon) { + case IT_AXE: + self.weaponmodel = "progs/v_axe.mdl"; + self.currentammo = 0; + break; + case IT_TSHOT: + self.weaponmodel = "progs/v_tshot.mdl"; + self.currentammo = self.ammo_shells; + self.items |= IT_SHELLS; + break; + case IT_COMBOGUN: + self.weaponmodel = "progs/v_combo.mdl"; + // ammo depends on last active trigger + if (self.which_ammo == 1 && self.ammo_rockets > 0) { + self.currentammo = self.ammo_rockets; + self.items |= IT_ROCKETS; + } else { + self.which_ammo = 0; + self.currentammo = self.ammo_shells; + self.items |= IT_SHELLS; + } + break; + case IT_PLASMAGUN: + self.weaponmodel = "progs/v_plasma.mdl"; + self.currentammo = self.ammo_cells; + self.items |= IT_CELLS; + break; + case IT_SUPER_NAILGUN: + self.weaponmodel = "progs/v_nailg.mdl"; + self.currentammo = self.ammo_nails; + self.items |= IT_NAILS; + break; + case IT_GRENADE_LAUNCHER: + self.weaponmodel = "progs/v_gren.mdl"; + self.currentammo = self.ammo_rockets; + self.items |= IT_ROCKETS; + break; + case IT_ROCKET_LAUNCHER: + self.weaponmodel = "progs/v_rhino.mdl"; + self.currentammo = self.ammo_rockets; + self.items |= IT_ROCKETS; + break; + default: + self.weaponmodel = ""; + self.currentammo = 0; + } +}; + +float() W_BestWeapon = +{ + local float it = self.items; + + // A hacky way to keep Super Shotgun active when out of rockets + if ((self.weapon == IT_COMBOGUN) && (self.ammo_shells >= 2 && (self.ammo_rockets < 1))) + return IT_COMBOGUN; + + if (self.ammo_rockets > 0 && (it & IT_ROCKET_LAUNCHER)) + return IT_ROCKET_LAUNCHER; + + if (self.ammo_cells > 0 && (it & IT_PLASMAGUN)) + return IT_PLASMAGUN; + + if (self.ammo_nails > 0 && (it & IT_SUPER_NAILGUN)) + return IT_SUPER_NAILGUN; + + if (self.ammo_shells > 1 && (it & IT_COMBOGUN)) + return IT_COMBOGUN; + + if (self.ammo_shells > 0 && (it & IT_TSHOT)) + return IT_TSHOT; + + return IT_AXE; +}; + +float() W_CheckNoAmmo = +{ + if (self.currentammo > 0) + return TRUE; + + if (self.weapon == IT_AXE) + return TRUE; + + self.weapon = W_BestWeapon (); + + W_SetCurrentAmmo (); + + // drop the weapon down + return FALSE; +}; + +/* POX - in sync with Paroxysm v1.1 +============ +W_Attack + +An attack impulse can be triggered now +============ +*/ + +void() W_Attack = +{ + local float r; + + if (intermission_running) + return; + + if (deathmatch & DM_AUTOSWITCH) { + if (!W_CheckNoAmmo ()) + return; + } + + makevectors (self.v_angle); // calculate forward angle for velocity + self.show_hostile = time + 1; // wake monsters up + + switch (self.weapon) { + case IT_AXE: + self.attack_finished = time + 0.5; + sound (self, CHAN_WEAPON, "weapons/ax1.wav", 1, ATTN_NORM); + + r = random (); + if (r < 0.25) + player_axe1 (); + else if (r < 0.5) + player_axeb1 (); + else if (r < 0.75) + player_axec1 (); + else + player_axed1 (); + return; + + case IT_TSHOT: + if (self.ammo_shells < 1) { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.5; + return; + } + + if (self.st_tshotload > time) // T-Shot still priming + return; + + if ((self.prime_tshot) && (self.ammo_shells > 2)) { // OK for triple + player_tshot1 (); + W_FireTShot (); + self.attack_finished = time + 0.7; + } else { // Normal shot + player_shot1 (); + W_FireShotgun (); + self.attack_finished = time + 0.5; + } + + // reset prime + self.prime_tshot = FALSE; + return; + + case IT_COMBOGUN: + if (self.st_sshotgun > time) + return; + + self.items &= ~IT_ROCKETS; + self.items |= IT_SHELLS; + + self.currentammo = self.ammo_shells; + self.which_ammo = 0; + + if (self.ammo_shells < 1) { // misfire + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_sshotgun = time + 0.7; + return; + } + + player_shot1 (); + W_FireSuperShotgun (); + self.st_sshotgun = time + 0.7; + return; + + case IT_PLASMAGUN: + if ((self.st_plasma > time) || (self.st_mplasma > time)) + return; + + if (self.ammo_cells < 1) { + sound (self, CHAN_AUTO, "weapons/mfire2.wav", 1, ATTN_NORM); + return; + } + + if (!self.LorR) // which barrel is supposed to fire? + player_plasma1 (); + else + player_plasma2 (); + + return; + + case IT_SUPER_NAILGUN: + if (self.st_nailgun > time) + return; + + if (self.ammo_nails < 1) { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + return; + } + + player_nail1 (); + return; + + case IT_GRENADE_LAUNCHER: + if (self.st_grenade > time) + return; + + if (self.ammo_rockets < 1) { + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.st_grenade = time + 0.6; + return; + } + + player_grenade1 (); + W_FireGrenade (); + self.st_grenade = time + 0.6; + return; + + case IT_ROCKET_LAUNCHER: + if (self.st_rocketload > time) // still reloading + return; + + if (self.ammo_rockets < 1) { // no ammo + sound (self, CHAN_AUTO, "weapons/mfire1.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.4; + return; + } + + if (self.reload_rocket > 6) { + sound (self, CHAN_AUTO, "weapons/error.wav", 1, ATTN_NORM); + self.attack_finished = time + 0.42; + return; + } + + player_rocket1 (); + self.attack_finished = time + 0.4; + return; + } +}; + +/* POX - in sync with Paroxysm v1.1 +============ +W_ChangeWeapon +============ +*/ +void() W_ChangeWeapon = +{ + local float no_ammo = 0; + local float selected = NIL; + + self.which_ammo = 0; // Default ammo to shells for SuperShotgun + + switch (self.impulse) { + case 1: + selected = IT_AXE; + break; + case 2: + selected = IT_TSHOT; + if (self.ammo_shells < 1) + no_ammo = 1; + break; + case 3: + selected = IT_COMBOGUN; + if (self.ammo_shells < 2) { + no_ammo = 1; + // allow player to still select SuperShotgun if he has rockets + if (self.ammo_rockets > 0 && (!(deathmatch & DM_AUTOSWITCH))) { + self.which_ammo = 1; // tell W_SetCurrentAmmo to use rockets, not shells + no_ammo = 0; + } + } + break; + case 4: + selected = IT_PLASMAGUN; + if (self.ammo_cells < 1) + no_ammo = 1; + break; + case 5: + selected = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + no_ammo = 1; + break; + case 6: + selected = IT_GRENADE_LAUNCHER; + if (self.ammo_rockets < 1) + no_ammo = 1; + break; + case 7: + selected = IT_ROCKET_LAUNCHER; + if (self.ammo_rockets < 1) + no_ammo = 1; + break; + } + + self.impulse = 0; + + if (!(self.items & selected)) { // don't have the weapon or the ammo + sprint (self, PRINT_HIGH, "no weapon.\n"); + return; + } + + if (no_ammo) { // don't have the ammo + sprint (self, PRINT_HIGH, "not enough ammo.\n"); + return; + } + +// +// set weapon, set ammo +// + self.weapon = selected; + W_SetCurrentAmmo (); +}; + +/* +============ +CheatCommand +============ +*/ +void() CheatCommand = +{ +// if (deathmatch || coop) + return; +}; + +/* + POX - in sync with Paroxysm v1.1 - changed BONESAW back to AXE +============ +CycleWeaponCommand + +Go to the next weapon with ammo +============ +*/ +void () CycleWeaponCommand = +{ + local float am; + + self.impulse = 0; + + while (1) { + am = 0; + self.which_ammo = 0; // Default ammo to shells for SuperShotgun + + switch (self.weapon) { + case IT_ROCKET_LAUNCHER: + self.weapon = IT_AXE; + break; + case IT_AXE: + self.weapon = IT_TSHOT; + if (self.ammo_shells < 1) + am = 1; + break; + case IT_TSHOT: + self.weapon = IT_COMBOGUN; + if (self.ammo_shells < 2) { + am = 1; + // allow player to select SuperShotgun if he has rockets + if (self.ammo_rockets > 0 && (!(deathmatch & DM_AUTOSWITCH))) { + self.which_ammo = 1; // tell W_SetCurrentAmmo to use rockets, not shells + am = 0; + } + } + break; + case IT_COMBOGUN: + self.weapon = IT_PLASMAGUN; + if (self.ammo_cells < 1) + am = 1; + break; + case IT_PLASMAGUN: + self.weapon = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + break; + case IT_SUPER_NAILGUN: + self.weapon = IT_GRENADE_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + break; + case IT_GRENADE_LAUNCHER: + self.weapon = IT_ROCKET_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + break; + } + + if ((self.items & self.weapon) && am == 0) { + W_SetCurrentAmmo (); + return; + } + } +}; + + +/* + POX - in sync with Paroxysm v1.1 - *went back to AXE for QW +============ +CycleWeaponReverseCommand + +Go to the prev weapon with ammo +============ +*/ +void() CycleWeaponReverseCommand = +{ + local float am; + + self.impulse = 0; + + while (1) { + am = 0; + self.which_ammo = 0; // Default ammo to shells for SuperShotgun + + if (self.weapon == IT_ROCKET_LAUNCHER) { + self.weapon = IT_GRENADE_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } else if (self.weapon == IT_GRENADE_LAUNCHER) { + self.weapon = IT_SUPER_NAILGUN; + if (self.ammo_nails < 2) + am = 1; + } else if (self.weapon == IT_SUPER_NAILGUN) { + self.weapon = IT_PLASMAGUN; + if (self.ammo_cells < 1) + am = 1; + } else if (self.weapon == IT_PLASMAGUN) { + self.weapon = IT_COMBOGUN; + + // allow player to select ComboGun if he has rockets + // BUT NOT IF DM_AUTOSWITCH!! + if (self.ammo_shells < 2) { + am = 1; + if (self.ammo_rockets > 0 && !(deathmatch & DM_AUTOSWITCH)) { + self.which_ammo = 1; // tell W_SetCurrentAmmo to use rockets - not shells + am = 0; + } + } + } else if (self.weapon == IT_COMBOGUN) { + self.weapon = IT_TSHOT; + if (self.ammo_shells < 1) + am = 1; + } else if (self.weapon == IT_TSHOT) { + self.weapon = IT_AXE; + } else if (self.weapon == IT_AXE) { + self.weapon = IT_ROCKET_LAUNCHER; + if (self.ammo_rockets < 1) + am = 1; + } + + if ((self.items & self.weapon) && am == 0) { + W_SetCurrentAmmo (); + return; + } + } + +}; + + +/* +============ +ServerflagsCommand + +Just for development +============ +*/ +void() ServerflagsCommand = +{ + serverflags = serverflags * 2 + 1; +}; + +// + POX - Displays the server rules +void() DisplayRules = +{ + sprint (self, PRINT_HIGH, "\nÓåòöåò Òõìåó\n----------------------\n |\n"); + + if (deathmatch & DM_PREDATOR) + sprint (self, PRINT_HIGH, "on | Predator Mode\n"); + else + sprint (self, PRINT_HIGH, "ÏÆÆ | Predator Mode\n"); + + if (deathmatch & DM_DARK) + sprint (self, PRINT_HIGH, "on | Dark Mode\n"); + else + sprint (self, PRINT_HIGH, "ÏÆÆ | Dark Mode\n"); + + if (deathmatch & DM_LMS) + sprint (self, PRINT_HIGH, "on | Last Man Standing\n"); + else + sprint (self, PRINT_HIGH, "ÏÆÆ | Last Man Standing\n"); + + if (deathmatch & DM_FFA) + sprint (self, PRINT_HIGH, "on | Free For All\n"); + else + sprint (self, PRINT_HIGH, "ÏÆÆ | Free For All\n"); + + if (deathmatch & DM_GIB) + sprint (self, PRINT_HIGH, "on | Gib\n"); + else + sprint (self, PRINT_HIGH, "ÏÆÆ | Gib\n"); + + if (deathmatch & DM_AUTOSWITCH) + sprint (self, PRINT_HIGH, "on | Weapon Autoswitch\n |\n----------------------\n\n"); + else + sprint (self, PRINT_HIGH, "ÏÆÆ | Weapon Autoswitch\n |\n----------------------\n\n"); + +}; + + + +/* +============ +ImpulseCommands + +============ +*/ +void() ImpulseCommands = +{ + switch (self.impulse) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + W_ChangeWeapon (); + break; + case 9: + CheatCommand (); + break; + case 10: + CycleWeaponCommand (); + break; + case 11: + ServerflagsCommand (); + break; + case 12: + CycleWeaponReverseCommand (); + break; + case 16: // target identifier + self.target_id_temp = TRUE; // Make it work across level change + stuffcmd (self, "play misc/talk.wav\n"); // audio confirmation + + if (self.target_id_toggle) { + self.target_id_toggle = FALSE; + + // don't centerprint if a message is up + if (self.target_id_finished < time) + centerprint (self, "Target Identifier OFF\n"); + else + sprint (self, PRINT_HIGH, "Target Identifier OFF\n"); + } else { + self.target_id_toggle = TRUE; + + // don't centerprint if a message is up + if (self.target_id_finished < time) + centerprint (self, "Target Identifier ON\n"); + else + sprint (self, PRINT_HIGH, "Target Identifier ON\n"); + + self.target_id_finished = time + 3; + } + break; + case 17: // FIXME - Toggle dlights vs lightglows + if(self.gl_fix) { + stuffcmd (self, "gl_flashblend 1\n"); + self.gl_fix = FALSE; + } else { + stuffcmd (self, "gl_flashblend 0\n"); + self.gl_fix = TRUE; + } + break; + + case 253: // Display Server Game Modes + DisplayRules (); + break; + } + + self.impulse = 0; +}; + +/* +============ +W_WeaponFrame + +Called every frame so impulse events can be handled as well as possible +============ +*/ +void() W_WeaponFrame = +{ + if (time < self.attack_finished) + return; + + //POX - v1.1 target identifier + if (self.target_id_toggle && (time > self.target_id_finished)) + ID_CheckTarget (); + + //POX - Don't swap nailgun skins if player is invisible or chascam is active! + if (self.weapon == IT_SUPER_NAILGUN && self.st_nailgun < time) + self.weaponmodel = "progs/v_nailg.mdl"; // cool off nailgun barrels + +// + POX - only check these if necessary (thanks to URQW patch) +// 1998-08-14 Constantly checking all impulses fix by Perged + if (self.impulse == SECOND_TRIGGER) + W_SecondTrigger (); + else if (self.impulse) + ImpulseCommands (); +// - POX + +// check for attack + if (self.button0) + { + SuperDamageSound (); + W_Attack (); + } +}; + +/* +======== +SuperDamageSound + +Plays sound if needed +======== +*/ +void() SuperDamageSound = +{ + if (self.super_damage_finished > time) + { + if (self.super_sound < time) + { + self.super_sound = time + 1; + sound (self, CHAN_BODY, "items/damage3.wav", 1, ATTN_NORM); + } + } + return; +}; + + diff --git a/ParoxysmII/source/world.r b/ParoxysmII/source/world.r new file mode 100644 index 0000000..d526b06 --- /dev/null +++ b/ParoxysmII/source/world.r @@ -0,0 +1,509 @@ +#include "config.rh" + +#include "paroxysm.rh" + +void() bubble_bob; + +void() InitBodyQue; +void() main = +{ + dprint ("main function\n"); + + // these are just commands the the prog compiler to copy these files + precache_file ("progs.dat"); + precache_file ("gfx.wad"); + precache_file ("quake.rc"); + precache_file ("default.cfg"); + + precache_file ("end1.bin"); + precache_file2 ("end2.bin"); + + precache_file ("demo1.dem"); + precache_file ("demo2.dem"); + precache_file ("demo3.dem"); + + // these are all of the lumps from the cached.ls files + precache_file ("gfx/palette.lmp"); + precache_file ("gfx/colormap.lmp"); + + precache_file2 ("gfx/pop.lmp"); + + precache_file ("gfx/complete.lmp"); + precache_file ("gfx/inter.lmp"); + + precache_file ("gfx/ranking.lmp"); + precache_file ("gfx/vidmodes.lmp"); + precache_file ("gfx/finale.lmp"); + precache_file ("gfx/conback.lmp"); + precache_file ("gfx/qplaque.lmp"); + + precache_file ("gfx/menudot1.lmp"); + precache_file ("gfx/menudot2.lmp"); + precache_file ("gfx/menudot3.lmp"); + precache_file ("gfx/menudot4.lmp"); + precache_file ("gfx/menudot5.lmp"); + precache_file ("gfx/menudot6.lmp"); + + precache_file ("gfx/menuplyr.lmp"); + precache_file ("gfx/bigbox.lmp"); + precache_file ("gfx/dim_modm.lmp"); + precache_file ("gfx/dim_drct.lmp"); + precache_file ("gfx/dim_ipx.lmp"); + precache_file ("gfx/dim_tcp.lmp"); + precache_file ("gfx/dim_mult.lmp"); + precache_file ("gfx/mainmenu.lmp"); + + precache_file ("gfx/box_tl.lmp"); + precache_file ("gfx/box_tm.lmp"); + precache_file ("gfx/box_tr.lmp"); + + precache_file ("gfx/box_ml.lmp"); + precache_file ("gfx/box_mm.lmp"); + precache_file ("gfx/box_mm2.lmp"); + precache_file ("gfx/box_mr.lmp"); + + precache_file ("gfx/box_bl.lmp"); + precache_file ("gfx/box_bm.lmp"); + precache_file ("gfx/box_br.lmp"); + + precache_file ("gfx/sp_menu.lmp"); + precache_file ("gfx/ttl_sgl.lmp"); + precache_file ("gfx/ttl_main.lmp"); + precache_file ("gfx/ttl_cstm.lmp"); + + precache_file ("gfx/mp_menu.lmp"); + + precache_file ("gfx/netmen1.lmp"); + precache_file ("gfx/netmen2.lmp"); + precache_file ("gfx/netmen3.lmp"); + precache_file ("gfx/netmen4.lmp"); + precache_file ("gfx/netmen5.lmp"); + + precache_file ("gfx/sell.lmp"); + + precache_file ("gfx/help0.lmp"); + precache_file ("gfx/help1.lmp"); + precache_file ("gfx/help2.lmp"); + precache_file ("gfx/help3.lmp"); + precache_file ("gfx/help4.lmp"); + precache_file ("gfx/help5.lmp"); + + precache_file ("gfx/pause.lmp"); + precache_file ("gfx/loading.lmp"); + + precache_file ("gfx/p_option.lmp"); + precache_file ("gfx/p_load.lmp"); + precache_file ("gfx/p_save.lmp"); + precache_file ("gfx/p_multi.lmp"); + + // sounds loaded by C code + precache_sound ("misc/menu1.wav"); + precache_sound ("misc/menu2.wav"); + precache_sound ("misc/menu3.wav"); + + precache_sound ("ambience/water1.wav"); + precache_sound ("ambience/wind2.wav"); + + // shareware + precache_file ("maps/start.bsp"); + + precache_file ("maps/e1m1.bsp"); + precache_file ("maps/e1m2.bsp"); + precache_file ("maps/e1m3.bsp"); + precache_file ("maps/e1m4.bsp"); + precache_file ("maps/e1m5.bsp"); + precache_file ("maps/e1m6.bsp"); + precache_file ("maps/e1m7.bsp"); + precache_file ("maps/e1m8.bsp"); + + // registered + precache_file2 ("gfx/pop.lmp"); + + precache_file2 ("maps/e2m1.bsp"); + precache_file2 ("maps/e2m2.bsp"); + precache_file2 ("maps/e2m3.bsp"); + precache_file2 ("maps/e2m4.bsp"); + precache_file2 ("maps/e2m5.bsp"); + precache_file2 ("maps/e2m6.bsp"); + precache_file2 ("maps/e2m7.bsp"); + + precache_file2 ("maps/e3m1.bsp"); + precache_file2 ("maps/e3m2.bsp"); + precache_file2 ("maps/e3m3.bsp"); + precache_file2 ("maps/e3m4.bsp"); + precache_file2 ("maps/e3m5.bsp"); + precache_file2 ("maps/e3m6.bsp"); + precache_file2 ("maps/e3m7.bsp"); + + precache_file2 ("maps/e4m1.bsp"); + precache_file2 ("maps/e4m2.bsp"); + precache_file2 ("maps/e4m3.bsp"); + precache_file2 ("maps/e4m4.bsp"); + precache_file2 ("maps/e4m5.bsp"); + precache_file2 ("maps/e4m6.bsp"); + precache_file2 ("maps/e4m7.bsp"); + precache_file2 ("maps/e4m8.bsp"); + + precache_file2 ("maps/end.bsp"); + + precache_file2 ("maps/dm1.bsp"); + precache_file2 ("maps/dm2.bsp"); + precache_file2 ("maps/dm3.bsp"); + precache_file2 ("maps/dm4.bsp"); + precache_file2 ("maps/dm5.bsp"); + precache_file2 ("maps/dm6.bsp"); +}; + +entity lastspawn; + +/*QUAKED worldspawn (0 0 0) ? + Only used for the world entity. + Set message to the level name. + Set sounds to the cd track to play. + + World Types: + 0: medieval + 1: metal + 2: base +*/ +void() worldspawn = +{ +#ifdef NETQUAKE + BotInit (); +#endif + + lastspawn = world; + InitBodyQue (); + +#ifdef NETQUAKE + CamSpawn (); +#endif + + // custom map attributes + if (self.model == "maps/e1m8.bsp") + cvar_set ("sv_gravity", "100"); + else + cvar_set ("sv_gravity", "790"); + +#ifdef QUAKEWORLD + cvar_set ("sv_friction", "3.12"); + + // make sure gamedir is "paroxysm" + if (infokey (world, "*gamedir") != "paroxysm") + error ("Gamedir must be \"paroxysm\"\n"); +#else + // Set deathmatch to options specified in the setup menu + deathmatch = cvar("deathmatch"); +#endif + + // LMS has its own fraglimit + fraglimit_LMS = cvar("fraglimit"); + + // the area based ambient sounds MUST be the first precache_sounds + + // player precaches + W_Precache (); // get weapon precaches + + + // Menu sounds (needed for C code) + precache_sound ("misc/menu1.wav"); + precache_sound ("misc/menu2.wav"); + precache_sound ("misc/menu3.wav"); + + /* + These are sounds used by the C physics code + */ + precache_sound ("demon/dland2.wav"); // landing thud + precache_sound ("misc/h2ohit1.wav"); // landing splash + + // setup precaches, always needed + precache_sound ("items/itembk2.wav"); // item respawn sound + precache_sound ("player/plyrjmp8.wav"); // player jump + precache_sound ("player/land.wav"); // player landing + precache_sound ("player/land2.wav"); // player hurt landing + precache_sound ("player/drown1.wav"); // drowning pain + precache_sound ("player/drown2.wav"); // drowning pain + precache_sound ("player/gasp1.wav"); // gasping for air + precache_sound ("player/gasp2.wav"); // taking breath + precache_sound ("player/h2odeath.wav"); // drowning death + + precache_sound ("items/protect.wav"); // Needed for FFA + precache_sound ("items/protect2.wav"); // Needed for FFA + precache_sound ("items/protect3.wav"); // Needed for FFA + + precache_sound ("items/inv1.wav"); // Needed for Predator mode + precache_sound ("items/inv2.wav"); // Needed for Predator mode + precache_sound ("items/inv3.wav"); // Needed for Predator mode + precache_model ("progs/eyes.mdl"); + + precache_sound ("misc/talk.wav"); // talk + precache_sound ("player/teledth1.wav"); // telefrag + precache_sound ("misc/r_tele1.wav"); // teleport sounds + precache_sound ("misc/r_tele2.wav"); +/* + precache_sound ("misc/r_tele3.wav"); + precache_sound ("misc/r_tele4.wav"); +*/ + precache_sound ("misc/r_tele5.wav"); + + precache_sound ("weapons/lock4.wav"); // ammo pick up + precache_sound ("weapons/pkup.wav"); // weapon up + precache_sound ("items/armor1.wav"); // armor up + precache_sound ("weapons/lhit.wav"); // lightning + precache_sound ("weapons/lstart.wav"); // lightning start + precache_sound ("items/damage3.wav"); + +// precache_sound ("misc/power.wav"); // lightning for boss + + // player gib sounds + precache_sound ("player/gib.wav"); // player gib sound + precache_sound ("player/udeath.wav"); // player gib sound + precache_sound ("player/tornoff2.wav"); // gib sound + + // player pain sounds + precache_sound ("player/pain1.wav"); + precache_sound ("player/pain2.wav"); + precache_sound ("player/pain3.wav"); + precache_sound ("player/pain4.wav"); + precache_sound ("player/pain5.wav"); + precache_sound ("player/pain6.wav"); + + // player death sounds + precache_sound ("player/death1.wav"); + precache_sound ("player/death2.wav"); + precache_sound ("player/death3.wav"); + precache_sound ("player/death4.wav"); + precache_sound ("player/death5.wav"); + precache_sound ("boss1/sight1.wav"); + + // Footstep sounds + precache_sound ("misc/foot1.wav"); + precache_sound ("misc/foot2.wav"); + precache_sound ("misc/foot3.wav"); + precache_sound ("misc/foot4.wav"); + + // Narration ("Eliminated!") for LMS + precache_sound ("nar/n_elim.wav"); + + // Regen Stations (v1.2) + precache_sound ("items/shield1.wav"); + precache_sound ("items/shield2.wav"); + precache_sound ("items/shield3.wav"); + precache_sound ("misc/null.wav"); + +#ifdef QUAKEWORLD + // Axe sounds + precache_sound ("weapons/ax1.wav"); // ax swoosh + precache_sound ("player/axhit1.wav"); // ax hit meat + precache_sound ("player/axhit2.wav"); // ax hit world +#endif + + // World sounds + precache_sound ("player/h2ojump.wav"); // player jumping into water + precache_sound ("player/inh2o.wav"); // player enter water + precache_sound ("misc/inh2ob.wav"); // player reenter water + precache_sound ("misc/outwater.wav"); // leaving water sound + precache_sound ("misc/owater2.wav"); // leaving water sound + precache_sound ("misc/water1.wav"); // swimming + precache_sound ("misc/water2.wav"); // swimming + precache_sound ("misc/uwater.wav"); // swimming + + precache_sound ("player/slimbrn2.wav"); // player enter slime + + precache_sound ("player/inlava.wav"); // player enter lava + precache_sound ("player/lburn1.wav"); // lava burn + precache_sound ("player/lburn2.wav"); // lava burn + + precache_model ("progs/player.mdl"); + + precache_model ("progs/null.mdl"); + precache_model ("progs/h_player.mdl"); + precache_model ("progs/gib1.mdl"); + precache_model ("progs/gib2.mdl"); + precache_model ("progs/gib3.mdl"); + + precache_model ("progs/s_bubble.spr"); // drowning bubbles + precache_model ("progs/s_explod.spr"); // sprite explosion + + // Weapon models +#ifdef QUAKEWORLD + precache_model ("progs/v_axe.mdl"); +#else + precache_model ("progs/v_bsaw.mdl"); +#endif + precache_model ("progs/v_tshot.mdl"); + precache_model ("progs/v_plasma.mdl"); + precache_model ("progs/v_gren.mdl"); + precache_model ("progs/v_combo.mdl"); + precache_model ("progs/v_nailg.mdl"); + precache_model ("progs/v_nailgl.mdl"); + precache_model ("progs/v_rhino.mdl"); +// precache_model ("progs/v_light.mdl"); // flashlight model -- not yet + precache_model ("progs/null.spr"); // current flashlight model + +#if 0 + // Cloaked weapons - removed v1.11 + precache_model ("progs/vb_bsaw.mdl"); + precache_model ("progs/vb_tshot.mdl"); + precache_model ("progs/vb_plasm.mdl"); + precache_model ("progs/vb_gren.mdl"); + precache_model ("progs/vb_combo.mdl"); + precache_model ("progs/vb_nailg.mdl"); + precache_model ("progs/vb_rhino.mdl"); +#endif + + // "bullet" models + precache_model ("progs/grenade.mdl"); + precache_model ("progs/lavaball.mdl"); + precache_model ("progs/s_spike.mdl"); + precache_model ("progs/spike.mdl"); + + precache_model ("progs/backpack.mdl"); + precache_model ("progs/zom_gib.mdl"); + + /* + Light animation tables. 'a' is total darkness, 'z' is maxbright. + */ + // 0 normal + lightstyle (0, "m"); + + // 1 FLICKER (first variety) + lightstyle (1, "mmnmmommommnonmmonqnmmo"); + + // 2 SLOW STRONG PULSE + lightstyle (2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); + + // 3 CANDLE (first variety) + lightstyle (3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); + + // 4 FAST STROBE + lightstyle (4, "mamamamamama"); + + // 5 GENTLE PULSE 1 + lightstyle (5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj"); + + // 6 FLICKER (second variety) + lightstyle (6, "nmonqnmomnmomomno"); + + // 7 CANDLE (second variety) + lightstyle (7, "mmmaaaabcdefgmmmmaaaammmaamm"); + + // 8 CANDLE (third variety) + lightstyle (8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); + + // 9 SLOW STROBE (fourth variety) + lightstyle (9, "zzzzzzgggggggggggg"); + + /* + New lightstyles + */ + // 12 SLOW STROBE2 + lightstyle (12, "ggggggzzzzzzgggggg"); + // 13 SLOW STROBE3 + lightstyle (13, "ggggggggggggzzzzzz"); + + // 10 FLUORESCENT FLICKER + lightstyle (10, "bbbbbmmmccmmamambbbmmbbbbmmaammmcccmamamm"); + // 11 SLOW PULSE NOT FADE TO BLACK + lightstyle (11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); + + // styles 32-62 are assigned by the light program for switchable lights + // 63 testing + lightstyle (63, "a"); +}; + +void() StartFrame = +{ +#ifdef NETQUAKE + BotFrame (); +#endif + + teamplay = cvar ("teamplay"); + +#ifdef QUAKEWORLD + timelimit = cvar ("timelimit") * 60; + fraglimit = cvar ("fraglimit"); + + deathmatch = cvar ("deathmatch"); +#else + skill = cvar ("skill"); +#endif + + framecount = framecount + 1; +}; + +/* + BODY QUEUE +*/ +entity bodyque_head; + +void() bodyque = +{ // just here so spawn functions don't complain after the world + // creates bodyques +}; + +void() InitBodyQue = +{ + bodyque_head = spawn(); + bodyque_head.classname = "bodyque"; + bodyque_head.owner = spawn(); + bodyque_head.owner.classname = "bodyque"; + bodyque_head.owner.owner = spawn(); + bodyque_head.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner = spawn(); + bodyque_head.owner.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner.owner = bodyque_head; + +#ifdef NETQUAKE + // VisWeap: Double the length of the bodyque to account for weapons. + bodyque_head.owner.owner.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner.owner.owner = spawn(); + bodyque_head.owner.owner.owner.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner.owner.owner.owner = spawn(); + bodyque_head.owner.owner.owner.owner.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner.owner.owner.owner.owner = spawn(); + bodyque_head.owner.owner.owner.owner.owner.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner.owner.owner.owner.owner.owner = spawn(); + bodyque_head.owner.owner.owner.owner.owner.owner.owner.owner.classname = "bodyque"; + bodyque_head.owner.owner.owner.owner.owner.owner.owner.owner.owner = bodyque_head; +#endif +}; + +// make a body que entry for the given ent so the ent can be +// respawned elsewhere +void(entity ent) CopyToBodyQue = +{ + bodyque_head.angles = ent.angles; + bodyque_head.model = ent.model; + bodyque_head.modelindex = ent.modelindex; + bodyque_head.frame = ent.frame; + bodyque_head.colormap = ent.colormap; + bodyque_head.movetype = ent.movetype; + bodyque_head.velocity = ent.velocity; + +#ifdef NETQUAKE + bodyque_head.skin = ent.skin; // Multiple skin support +#endif + + bodyque_head.flags = 0; + + // allow heads to still float after player respawns + if ((ent.classname == "player") && (ent.think == bubble_bob)) { + bodyque_head.think = ent.think; + bodyque_head.nextthink = ent.nextthink; + bodyque_head.cnt = ent.cnt; + } + + setorigin (bodyque_head, ent.origin); + setsize (bodyque_head, ent.mins, ent.maxs); + +#ifdef NETQUAKE + CamCopyBody (ent, bodyque_head); +#endif + + bodyque_head = bodyque_head.owner; +}; + +#ifdef NETQUAKE +float lms_gameover; // Send a game over message if true +#endif