commit b26ca6a026cd7a3c9ab6914ebdb3c3386734a51d Author: unknown Date: Tue Feb 8 13:42:28 2022 -0500 Initial commit diff --git a/.github/workflows/compile-and-release.yml b/.github/workflows/compile-and-release.yml new file mode 100644 index 0000000..e64bbab --- /dev/null +++ b/.github/workflows/compile-and-release.yml @@ -0,0 +1,88 @@ +name: Compile QuakeC and Publish Release +on: [push] +jobs: + Compile-QC: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Clone Repository + working-directory: ./tools + run: | + sudo -i + ./qc-compiler-lin.sh + echo "QC COMPILE STATUS - ${{ job.status }}." + - name: Zip Progs + working-directory: ./build + run: | + zip -r -j pc-nzp-qc.zip pc/* + zip -r -j psp-nzp-qc.zip psp/* + zip -r -j nx-nzp-qc.zip nx/* + zip -r -j vita-nzp-qc.zip vita/* + - name: Generate Build Date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d-%H-%M-%S')" + - name: Delete Old Release + uses: dev-drprasad/delete-tag-and-release@v0.2.0 + with: + delete_release: true + tag_name: bleeding-edge + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: bleeding-edge + release_name: Automated Release ${{ steps.date.outputs.date }} + body: | + This is a **bleeding edge** NZ:P QuakeC release, stability is not guarenteed and you may need to grab an updated asset release if this update depends on new models/textures/etc. + + To install: + - Grab the .ZIP archive for your desired platform below (*-nzp-qc.zip) + - Extract the contents of the .ZIP archive to your nzp folder (Example: copy `progs.dat` and `progs.lno` to `PSP/GAME/nzportable/nzp`). + draft: true + prerelease: false + - name: Upload NX QC + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/nx-nzp-qc.zip + asset_name: nx-nzp-qc.zip + asset_content_type: application/zip + - name: Upload PC QC + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/pc-nzp-qc.zip + asset_name: pc-nzp-qc.zip + asset_content_type: application/zip + - name: Upload PSP QC + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/psp-nzp-qc.zip + asset_name: psp-nzp-qc.zip + asset_content_type: application/zip + - name: Upload VITA QC + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/vita-nzp-qc.zip + asset_name: vita-nzp-qc.zip + asset_content_type: application/zip + - name: Publish Release + uses: StuYarrow/publish-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + id: ${{ steps.create_release.outputs.id }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3cf22d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/* +bin/fteqcc.log \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bf96377 --- /dev/null +++ b/LICENSE @@ -0,0 +1,87 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 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. +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) 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; or, + +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. +If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. + +5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. + +7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. + +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. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1c59d8 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Nazi Zombies: Portable QuakeC + +## About +This is the QuakeC portion of the NZ:P source code. QuakeC is responsible for most game-related code such as weapon logic, ai, and Perks. You can read more about QuakeC on the [Wikipedia page](https://en.wikipedia.org/wiki/QuakeC). NZ:P makes use of CSQC for PC/FTE. + +## Project Structure +Here is a brief explanation for each of the (sub)directories in this repository: +* `bin`: Command line binaries for [FTEQCC](https://www.fteqcc.org/) + the ini configuration file. +* `progs`: `*.src` files, a list of QuakeC source files each platform is dependent on. +* `source`: + * `client`: FTE-exclusive CSQC, used for the HUD, achievements, and other server->client requests. + * `server`: Game code relevant to all platforms, contains most expected logic. + * `shared`: Definitions for weapon stats and some utility functions shared by both the `client` and `server`. +* `tools`: Build scripts to compile the QuakeC into `.dat` and `.lno` files. + +## Updating +While it's usually recommended to stay on the QuakeC version provided with your build of NZ:P, you may want to update it to the current development builds to test new features and changes. To do this, navigate to the [Releases](https://github.com/nzp-team/quakec/releases/tag/bleeding-edge) page and follow the instructions there for downloading and installing. + +## Building (Beginner Friendly) +There are no prerequisites or dependancies needed to build QuakeC other than a working Windows or Linux-based machine (MacOS is **not** natively supported, but you can use [WINE](https://www.winehq.org/)). + +Before you can build the NZ:P QuakeC, you must either [download](https://github.com/nzp-team/quakec/archive/refs/heads/main.zip) this repository (easy) or [clone it](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) (for developers). + +To build, simply navigate to the `tools` directory and run the `qc-compiler-*` script for your platform. If unfamiliar with executing shell (`.sh`) scripts on Linux systems, give this [itsFOSS article](https://itsfoss.com/run-shell-script-linux/) a read. + +After having done this, a `build` directory will have been created, and inside of it will be more directories named after every platform. Copy the contents of the platform directories into your `nzp` game directory. (Example: copy `progs.dat` and `progs.lno` from `build/psp` to `PSP/GAME/nzportable/nzp`). \ No newline at end of file diff --git a/bin/fteqcc-cli-lin b/bin/fteqcc-cli-lin new file mode 100644 index 0000000..59db0a6 Binary files /dev/null and b/bin/fteqcc-cli-lin differ diff --git a/bin/fteqcc-cli-win.exe b/bin/fteqcc-cli-win.exe new file mode 100644 index 0000000..429f7d5 Binary files /dev/null and b/bin/fteqcc-cli-win.exe differ diff --git a/bin/fteqcc.ini b/bin/fteqcc.ini new file mode 100644 index 0000000..02dffdf --- /dev/null +++ b/bin/fteqcc.ini @@ -0,0 +1,216 @@ +optimisation t default # c = a*b is performed in one operation rather than two, and can + # cause older decompilers to fail. +optimisation i default # if (!a) was traditionally compiled in two statements. This optimisation + # does it in one, but can cause some decompilers to get confused. +optimisation p default # In the original qcc, function parameters were specified as a vector + # store even for floats. This fixes that. +optimisation c default # This optimisation strips out the names of constants (but not strings) + # from your progs, resulting in smaller files. It makes decompilers + # leave out names or fabricate numerical ones. +optimisation cs default # This optimisation strips out the names of string constants from + # your progs. However, this can break addons, so don't use it in + # those cases. +optimisation d default # This will merge definitions of constants which are the same value. + # Pay extra attention to assignment to constant warnings. +optimisation s default # This will compact the string table that is stored in the progs. + # It will be considerably smaller with this. +optimisation l default # Strips out local names and definitions. Most decompiles will break + # on this. +optimisation n default # This strips out the names of functions which are never called. + # Doesn't make much of an impact though. +optimisation f default # This strips out the filenames of the progs. This can confuse the + # really old decompilers, but is nothing to the more recent ones. +optimisation u default # Removes the entries of unreferenced variables. Doesn't make a + # difference in well maintained code. +optimisation r default # Optimises the pr_globals count by overlapping temporaries. In + # QC, every multiplication, division or operation in general produces + # a temporary variable. This optimisation prevents excess, and in + # the case of Hexen2's gamecode, reduces the count by 50k. This + # is the most important optimisation, ever. +optimisation a default # 5*6 actually emits an operation into the progs. This prevents + # that happening, effectivly making the compiler see 30 +optimisation pf default # Strip out stuff wasted used in function calls and strings to the + # precache_file builtin (which is actually a stub in quake). +optimisation ro default # Functions ending in a return statement do not need a done statement + # at the end of the function. This can confuse some decompilers, + # making functions appear larger than they were. +optimisation cj default # This optimisation plays an effect mostly with nested if/else statements, + # instead of jumping to an unconditional jump statement, it'll jump + # to the final destination instead. This will bewilder decompilers. +optimisation sf default # Strips out the 'defs' of functions that were only ever called + # directly. This does not affect saved games. This can affect FTE_MULTIPROGS. +optimisation lo default # Store all locals in a single section of the pr_globals. Vastly + # reducing it. This effectivly does the job of overlaptemps. + # However, locals are no longer automatically initialised to 0 (and + # never were in the case of recursion, but at least then its the + # same type). + # If locals appear uninitialised, fteqcc will disable this optimisation + # for the affected functions, you can optionally get a warning about + # these locals using: #pragma warning enable F302 +optimisation vc default # Where a function is called with just a vector, this causes the + # function call to store three floats instead of one vector. This + # can save a good number of pr_globals where those vectors contain + # many duplicate coordinates but do not match entirly. +optimisation cf default # Strip class field names. This will harm debugging and can result + # in 'gibberish' names appearing in saved games. Has no effect on + # engines other than FTEQW, which will not recognise these anyway. +keyword asm true # Disables the 'asm' keyword. Use the writeasm flag to see an example + # of the asm. +keyword break true # Disables the 'break' keyword. +keyword case true # Disables the 'case' keyword. +keyword class true # Disables the 'class' keyword. +keyword const true # Disables the 'const' keyword. +keyword continue true # Disables the 'continue' keyword. +keyword default true # Disables the 'default' keyword. +keyword entity true # Disables the 'entity' keyword. +keyword enum true # Disables the 'enum' keyword. +keyword enumflags true # Disables the 'enumflags' keyword. +keyword extern true # Disables the 'extern' keyword. Use only on functions inside addons. +keyword float true # Disables the 'float' keyword. (Disables the float keyword without + # 'local' preceeding it) +keyword for true # Disables the 'for' keyword. Syntax: for(assignment; while; increment) + # {codeblock;} +keyword goto true # Disables the 'goto' keyword. +keyword int true # Disables the 'int' keyword. +keyword integer true # Disables the 'integer' keyword. +keyword noref true # Disables the 'noref' keyword. +keyword unused false # Disables the 'unused' keyword. 'unused' means that the variable + # is unused, you're aware that its unused, and you'd rather not + # know about all the warnings this results in. +keyword used false # Disables the 'used' keyword. 'used' means that the variable is + # used even if the qcc can't see how - thus preventing it from ever + # being stripped. +keyword static true # Disables the 'static' keyword. 'static' means that a variable + # has altered scope. On globals, the variable is visible only to + # the current .qc file. On locals, the variable's value does not + # change between calls to the function. On class variables, specifies + # that the field is a scoped global instead of a local. On class + # functions, specifies that 'this' is expected to be invalid and + # that the function will access any memembers via it. +keyword nonstatic true # Disables the 'nonstatic' keyword. 'nonstatic' acts upon globals+functions, + # reverting the defaultstatic pragma on a per-variable basis. For + # use by people who prefer to keep their APIs explicit. +keyword ignore false # Disables the 'ignore' keyword. 'ignore' is expected to typically + # be hidden behind a 'csqconly' define, and in such a context can + # be used to conditionally compile functions a little more gracefully. + # The opposite of the 'used' keyword. These variables/functions/members + # are ALWAYS stripped, and effectively ignored. +keyword nosave true # Disables the 'nosave' keyword. +keyword inline true # Disables the 'inline' keyword. +keyword strip true # Disables the 'strip' keyword. +keyword shared true # Disables the 'shared' keyword. +keyword state false # Disables the 'state' keyword. +keyword optional true # Disables the 'optional' keyword. +keyword inout false # Disables the 'inout' keyword. +keyword string true # Disables the 'string' keyword. +keyword struct true # Disables the 'struct' keyword. +keyword switch true # Disables the 'switch' keyword. +keyword thinktime false # Disables the 'thinktime' keyword which is used in HexenC +keyword until false # Disables the 'until' keyword which is used in HexenC +keyword loop false # Disables the 'loop' keyword which is used in HexenC +keyword typedef true # Disables the 'typedef' keyword. +keyword union true # Disables the 'union' keyword. +keyword var true # Disables the 'var' keyword. +keyword vector true # Disables the 'vector' keyword. +keyword wrap true # Disables the 'wrap' keyword. +keyword weak true # Disables the 'weak' keyword. +flag kce true # If you want keywords to NOT be disabled when they a variable by + # the same name is defined, check here. +flag parms false # if PARM0 PARM1 etc should be defined by the compiler. These are + # useful if you make use of the asm keyword for function calls, + # or you wish to create your own variable arguments. This is an + # easy way to break decompilers. +flag autoproto false # Causes compilation to take two passes instead of one. The first + # pass, only the definitions are read. The second pass actually + # compiles your code. This means you never have to remember to prototype + # functions again. +flag wasm false # Writes out a qc.asm which contains all your functions but in assembler. + # This is a great way to look for bugs in fteqcc, but can also be + # used to see exactly what your functions turn into, and thus how + # to optimise statements better. +flag annotate false # Annotate source code with assembler statements on compile (requires + # gui). +flag nullemptystr false # Empty string immediates will have the raw value 0 instead of 1. +flag ifstring false # Causes if(string) to behave identically to if(string!=) This is + # most useful with addons of course, but also has adverse effects + # with FRIK_FILE's fgets, where it becomes impossible to determin + # the end of the file. In such a case, you can still use asm {IF + # string 2;RETURN} to detect eof and leave the function. +flag iffloat false # Fixes certain floating point logic. +flag ifvector true # Fixes conditional vector logic. +flag vectorlogic true # Fixes conditional vector logic. +flag brokenarray false # Treat references to arrays as references to the first index of + # said array, to replicate an old fteqcc bug. +flag rootconstructor false # When enabled, the root constructor should be called first like + # in c++. +flag acc false # Reacc is a pascall like compiler. It was released before the Quake + # source was released. This flag has a few effects. It sorts all + # qc files in the current directory into alphabetical order to compile + # them. It also allows Reacc global/field distinctions, as well + # as allows ¦ as EOF. Whilst case insensitivity and lax type checking + # are supported by reacc, they are seperate compiler flags in fteqcc. +flag caseinsens false # Causes fteqcc to become case insensitive whilst compiling names. + # It's generally not advised to use this as it compiles a little + # more slowly and provides little benefit. However, it is required + # for full reacc support. +flag lax false # Disables many errors (generating warnings instead) when function + # calls or operations refer to two normally incompatible types. + # This is required for reacc support, and can also allow certain + # (evil) mods to compile that were originally written for frikqcc. +flag hashonly false # Allows use of only #constant for precompiler constants, allows + # certain preqcc using mods to compile +flag lo false # This changes the behaviour of your code. It generates additional + # if operations to early-out in if statements. With this flag, the + # line if (0 && somefunction()) will never call the function. It + # can thus be considered an optimisation. However, due to the change + # of behaviour, it is not considered so by fteqcc. Note that due + # to inprecisions with floats, this flag can cause runaway loop + # errors within the player walk and run functions (without iffloat + # also enabled). This code is advised: + # player_stand1: + # if (self.velocity_x || self.velocity_y) + # player_run + # if (!(self.velocity_x || self.velocity_y)) +flag msvcstyle false # Generates warning and error messages in a format that msvc understands, + # to facilitate ide integration. +flag debugmacros false # Print out the contents of macros that are expanded. This can help + # look inside macros that are expanded and is especially handy if + # people are using preprocessor hacks. +flag filetimes false # Recompiles the progs only if the file times are modified. +flag fastarrays false # Generates extra instructions inside array handling functions to + # detect engine and use extension opcodes only in supporting engines. + # Adds a global which is set by the engine if the engine supports + # the extra opcodes. Note that this applies to all arrays or none. +flag assumeint false # Numerical constants are assumed to be integers, instead of floats. +flag subscope false # Restrict the scope of locals to the block they are actually defined + # within, as in C. +flag verbose false # Lots of extra compiler messages. +flag typeexplicit false # All type conversions must be explicit or directly supported by + # instruction set. +flag noboundchecks false # Disable array index checks, speeding up array access but can result + # in your code misbehaving. +flag qccx false # WARNING: This syntax makes mods inherantly engine specific. + # Do NOT use unless you know what you're doing.This is provided + # for compatibility only + # Any entity hacks will be unsupported in FTEQW, DP, and others, + # resulting in engine crashes if the code in question is executed. +flag embedsrc false # Write the sourcecode into the output file. +showall off # Show all keyword options in the gui +compileonstart off # Recompile on GUI startup +log off # Write out a compile log +enginebinary fteglqw.exe # Location of the engine binary to run. Change this to something + # else to run a different engine, but not all support debugging. +basedir ../.. # The base directory of the game that contains your sub directory +engineargs "-window -nohome -game game" + # The engine commandline to use when debugging. You'll likely want + # to ensure this contains -window as well as the appropriate -game + # argument. +srcfile progs.src # The progs.src file to load to find ordering of other qc files. +src # Additional subdir to read qc files from. Typically blank (ie: + # the working directory). +extramargins off # Enables line number and folding margins. +hexen2 off # Enable the extra tweaks needed for compatibility with hexen2 engines. +extendedopcodes off # Utilise an extended instruction set, providing support for pointers + # and faster arrays and other speedups. +parameters # Other additional parameters that are not supported by the gui. + # Likely including -DFOO diff --git a/progs/fte-client.src b/progs/fte-client.src new file mode 100644 index 0000000..424127a --- /dev/null +++ b/progs/fte-client.src @@ -0,0 +1,11 @@ +../../build/pc/csprogs.dat + +../source/client/defs/fte.qc +../source/shared/defs/custom.qc +../source/shared/sound_enhanced.qc +../source/shared/weapon_defines.qc +../source/client/defs/custom.qc +../source/client/menu.qc +../source/client/achievements.qc +../source/client/hud.qc +../source/client/main.qc diff --git a/progs/fte-server.src b/progs/fte-server.src new file mode 100644 index 0000000..973f9d0 --- /dev/null +++ b/progs/fte-server.src @@ -0,0 +1,37 @@ +../../build/pc/qwprogs.dat + +../source/server/defs/fte.qc +../source/shared/defs/custom.qc +../source/shared/weapon_defines.qc +../source/server/defs/custom.qc +../source/server/clientfuncs.qc + +../source/server/dummies/generic.qc +../source/server/rounds.qc +../source/server/nzdparser.qc +../source/server/main.qc +../source/server/player.qc +../source/server/damage.qc + +../source/server/entities/sub_functions.qc +../source/server/entities/sounds.qc +../source/server/entities/triggers.qc +../source/server/entities/map_entities.qc +../source/server/entities/lights.qc +../source/server/entities/doors.qc +../source/server/entities/window.qc +../source/server/entities/machines.qc + +../source/server/weapons/frames_core.qc +../source/server/weapons/weapon_core.qc + +../source/server/entities/powerups.qc + +../source/server/ai/pathfind_code.qc +../source/server/ai/ai_core.qc +../source/server/ai/fte/waypoints_core.qc +../source/server/ai/zombie_core.qc +../source/server/ai/crawler_core.qc +../source/server/ai/dog_core.qc + +../source/server/items.qc diff --git a/progs/nx.src b/progs/nx.src new file mode 100644 index 0000000..acd9c6f --- /dev/null +++ b/progs/nx.src @@ -0,0 +1,40 @@ +../../build/nx/progs.dat + +#pragma warning disable Q208 + +../source/server/defs/nx.qc +../source/shared/defs/custom.qc +../source/shared/weapon_defines.qc +../source/server/defs/custom.qc +../source/server/clientfuncs.qc + +../source/server/dummies/vitanx.qc + +../source/server/dummies/generic.qc +../source/server/rounds.qc +../source/server/nzdparser.qc +../source/server/main.qc +../source/server/player.qc +../source/server/damage.qc + +../source/server/entities/sub_functions.qc +../source/server/entities/sounds.qc +../source/server/entities/triggers.qc +../source/server/entities/map_entities.qc +../source/server/entities/lights.qc +../source/server/entities/doors.qc +../source/server/entities/window.qc +../source/server/entities/machines.qc + +../source/server/weapons/frames_core.qc +../source/server/weapons/weapon_core.qc +../source/server/entities/powerups.qc + +../source/server/ai/ai_core.qc +../source/server/ai/standard/waypoints_func.qc +../source/server/ai/standard/waypoints_core.qc +../source/server/ai/zombie_core.qc +../source/server/ai/crawler_core.qc +../source/server/ai/dog_core.qc + +../source/server/items.qc diff --git a/progs/psp.src b/progs/psp.src new file mode 100644 index 0000000..f9889c9 --- /dev/null +++ b/progs/psp.src @@ -0,0 +1,40 @@ +../../build/psp/progs.dat + +#pragma warning disable Q208 + +../source/server/defs/psp.qc +../source/shared/defs/custom.qc +../source/shared/weapon_defines.qc +../source/server/defs/custom.qc +../source/server/clientfuncs.qc + +../source/server/psp_specifics.qc + +../source/server/dummies/generic.qc +../source/server/rounds.qc +../source/server/nzdparser.qc +../source/server/main.qc +../source/server/player.qc +../source/server/damage.qc + +../source/server/entities/sub_functions.qc +../source/server/entities/sounds.qc +../source/server/entities/triggers.qc +../source/server/entities/map_entities.qc +../source/server/entities/lights.qc +../source/server/entities/doors.qc +../source/server/entities/window.qc +../source/server/entities/machines.qc + +../source/server/weapons/frames_core.qc +../source/server/weapons/weapon_core.qc +../source/server/entities/powerups.qc + +../source/server/ai/ai_core.qc +../source/server/ai/standard/waypoints_func.qc +../source/server/ai/standard/waypoints_core.qc +../source/server/ai/zombie_core.qc +../source/server/ai/crawler_core.qc +../source/server/ai/dog_core.qc + +../source/server/items.qc diff --git a/progs/vita.src b/progs/vita.src new file mode 100644 index 0000000..65e69b1 --- /dev/null +++ b/progs/vita.src @@ -0,0 +1,40 @@ +../../build/vita/progs.dat + +#pragma warning disable Q208 + +../source/server/defs/vita.qc +../source/shared/defs/custom.qc +../source/shared/weapon_defines.qc +../source/server/defs/custom.qc +../source/server/clientfuncs.qc + +../source/server/dummies/vitanx.qc + +../source/server/dummies/generic.qc +../source/server/rounds.qc +../source/server/nzdparser.qc +../source/server/main.qc +../source/server/player.qc +../source/server/damage.qc + +../source/server/entities/sub_functions.qc +../source/server/entities/sounds.qc +../source/server/entities/triggers.qc +../source/server/entities/map_entities.qc +../source/server/entities/lights.qc +../source/server/entities/doors.qc +../source/server/entities/window.qc +../source/server/entities/machines.qc + +../source/server/weapons/frames_core.qc +../source/server/weapons/weapon_core.qc +../source/server/entities/powerups.qc + +../source/server/ai/ai_core.qc +../source/server/ai/standard/waypoints_func.qc +../source/server/ai/standard/waypoints_core.qc +../source/server/ai/zombie_core.qc +../source/server/ai/crawler_core.qc +../source/server/ai/dog_core.qc + +../source/server/items.qc diff --git a/source/client/achievements.qc b/source/client/achievements.qc new file mode 100644 index 0000000..441e4be --- /dev/null +++ b/source/client/achievements.qc @@ -0,0 +1,175 @@ +/* + client/achievements.qc + + main achievement code + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/* + +Thanks to everyone who submitted Achievement ideas in 2019! + +- Derped_Crusader +- TheSmashers +- Chyll +- mrFlamist +- mario135790 +- Mission +- RedneckHax0r +- Revnova +- EpicFoxx64 +- Detwyler_ +- DisrespectfulOtter +- xnick2222x +- greg +- Omar Alejandro +- Cosmicrush +- blubs +- Bernerd + +*/ + +void(float id, string graphic, string name, string description) Achievement_Create = +{ + if (id >= MAX_ACHIEVEMENTS) + return; + + achievements[id].img = strcat("gfx/achievement/", graphic, ".tga"); + achievements[id].name = name; + achievements[id].description = description; +} + +void() Achievement_Save = +{ + local float file, i; + + // re-write the achievement file + file = fopen("ach.dat", FILE_WRITE); + for (i = 0; i < MAX_ACHIEVEMENTS; i++) { + fputs(file, strcat(ftos(achievements[i].unlocked), "\n")); + fputs(file, strcat(ftos(achievements[i].progress), "\n")); + } + + fclose(file); +} + +void() Achievement_Load = +{ + float file, i; + string val; + + file = fopen("ach.dat", FILE_READ); + + if (file == -1) { + fclose(file); + file = fopen("ach.dat", FILE_WRITE); + + for (i = 0; i < MAX_ACHIEVEMENTS * 2; i++) { + fputs(file, "0\n"); + } + + fclose(file); + file = fopen("ach.dat", FILE_READ); + } + + for (i = 0; i < MAX_ACHIEVEMENTS; i++) { + val = fgets(file); + achievements[i].unlocked = stof(val); + val = fgets(file); + achievements[i].progress = stof(val); + } + + fclose(file); +} + +void() Achievement_Init = +{ + Achievement_Create(0, "ready", "Ready..", "Reach Round 5"); + Achievement_Create(1, "steady", "Steady..", "Reach Round 10"); + Achievement_Create(2, "go_hell_no", "Go? Hell No...", "Reach Round 15"); + Achievement_Create(3, "where_legs_go", "Where Did Legs Go?", "Turn a Zombie into a Crawler"); + Achievement_Create(4, "the_f_bomb", "The F Bomb", "Use the Nuke Power-Up to kill a single Zombie"); + Achievement_Create(5, "no_perks_no_problem", "No Perks? No Problem", "Survive an entire Round without Perks past Round 15 or higher"); + Achievement_Create(6, "dipsomaniac", "Dipsomaniac", "Hold all Perk-A-Colas at once in a single Game"); + Achievement_Create(7, "oops", "Oops!", "Die from fall damage"); + Achievement_Create(8, "abstinence_program", "Abstinence Program", "Survive to Round 10 without taking any enemy damage"); + Achievement_Create(9, "pro_gamer_move", "Pro-Gamer Move", "Die on Round 1 with no Ammo remaining"); + Achievement_Create(10, "spinning_plates", "Spinning Plates", "Keep entry points Barricaded all the way to Round 10"); + Achievement_Create(11, "unlucky", "Unlucky", "Have the Mystery Box move 10 times"); + Achievement_Create(12, "the_collector", "The Collector", "Buy every weapon off the wall in a single Game"); + Achievement_Create(13, "barrels_o_fun", "Barrels o' Fun", "In Nacht der Untoten, kill 15 Zombies with Explosive Barrels"); + Achievement_Create(14, "its_a_trap", "It's a Trap!", "In Kino der Toten, kill 5 Crawlers with the use of 1 Trap"); + Achievement_Create(15, "uplink", "Up-Link", "In Kino der Toten, teleport to the Pack-A-Punch 5 times in a single Game"); + Achievement_Create(16, "undone", "Undone", "Survive a total of 150 Rounds on Nacht der Untoten"); + Achievement_Create(17, "moviegoer", "Moviegoer", "Play 10 total Matches on Kino der Toten"); + Achievement_Create(18, "cmere_cupcake", "C'mere, Cupcake!", "Kill yourself with your own Grenade"); + Achievement_Create(19, "orbital_strike", "Orbital Strike", "Kill 5 Zombies at once, with the Panzerschreck, while in the air"); + Achievement_Create(20, "long_name", "A House Divided, Multiplied, then Subtracted", "In Nacht der Untoten, reach Round 10 without going upstairs & without fixing Barriers, then go upstairs & allow those Barriers to be Destroyed"); + Achievement_Create(21, "colt_hearted_killer", "Colt-Hearted Killer", "Reach Round 10 using only the Colt M1911"); + Achievement_Create(22, "cache_and_carry", "Cannot Cache and Carry", "Collected a Max Ammo power-up whilst already being at fully ammo capacity"); + Achievement_Create(23, "divide_and_conquer", "Divide and Conquer", "Turn all Zombies into Crawlers in a single Round"); + Achievement_Create(24, "tough_luck", "Tough Luck!", "Die before reaching Round 5"); + Achievement_Create(25, "gregg", "All Are One with Gregg!", "???"); + Achievement_Create(26, "slasher", "Slasher", "Perform 100 total Melee kills"); + Achievement_Create(27, "made_by_children", "Made by Children", "Get a Zombie stuck for 5 minutes"); + Achievement_Create(28, "increase_firepower", "Increase your Firepower!", "Use the Pack-A-Punch for the first time"); + Achievement_Create(29, "kraut_tongue", "Kraut Got Your Tongue?", "Get 25 Headshots"); + Achievement_Create(30, "mindblowing", "Mindblowing", "Get 250 Headshots"); + Achievement_Create(31, "thanks_explosions", "Big Thanks to Explosions", "Kill 10 Zombies with a single Grenade"); + Achievement_Create(32, "mbox_maniac", "Mystery Box Manaic", "Use the Mystery Box 20 times in a single Game"); + Achievement_Create(33, "instant_help", "Instant Help", "Get 100 total Kills with the Insta-Kill Power-Up"); + Achievement_Create(34, "blow_the_bank", "Blow the Bank", "Earn 1,000,000 Points"); + Achievement_Create(35, "why_wait", "..Why Are We Waiting?", "Stand Still for 2 minutes"); + Achievement_Create(36, "one_clip", "One Clip", "Survive a Round using the MG42 and without Reloading"); + Achievement_Create(37, "2021", "Twenty-Twenty-One", "Land 20 Headshots, with 20 Bullets, with one Clip"); + Achievement_Create(38, "warmed_up", "Getting Warmed Up", "Earn 10 Achievements"); + Achievement_Create(39, "half_way", "About Half-Way", "Earn 20 Achievements"); + Achievement_Create(40, "75_percent", "I mean.. 75% is Passing", "Earn 30 Achievements"); + Achievement_Create(41, "over_achiever", "Over-Achiever", "Earn all 42 Achievements"); + + Achievement_Load(); + + achievement_pages = ceil(MAX_ACHIEVEMENTS/3); + current_achievement_page = 1; + + active_achievement = -1; +} + +void(float id) Achievement_Unlock = +{ + local float file, i; + + achievements[id].unlocked = 1; + + // tell the HUD to start drawing + active_achievement = id; + + Achievement_Save(); +} + +void(float id, float pg) Achievement_UpdateProgress = +{ + achievements[id].progress = pg; + + Achievement_Save(); +} \ No newline at end of file diff --git a/source/client/defs/custom.qc b/source/client/defs/custom.qc new file mode 100644 index 0000000..8bf3937 --- /dev/null +++ b/source/client/defs/custom.qc @@ -0,0 +1,219 @@ +/* + client/defs/custom.qc + + Various globals that are used in this FTEQW example are defined + here, they are not used by the engine itself. + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#pragma warning disable Q302 + +#define true 1 +#define false 0 + +vector cursor_pos; /* Current mouse cursor position, updated in csqc_inputevent */ +float g_width, g_height; /* Globals for screen width and height */ + +float walk; + +//Menu stuff +float in_menu; //what menu are we in +float time_in_menu; + +entity player; +entity vmodel; +entity v2model; +entity mzlflash; +entity dummy; + +float weapon; + +float tracercounter; +.float rate; +.vector targetpos; + +vector vmodel_targetpos; +vector v2model_targetpos; +vector vmodel_currentpos; +vector v2model_currentpos; +vector vmodel_velocity; +vector v2model_velocity; +vector vmodel_avelocity; +vector v2model_avelocity; +vector vmodel_muzzleoffset; +vector v2model_muzzleoffset; + +const float MENU_NONE = 0; +const float MENU_MAIN = 1; +const float MENU_SINGLE = 2; +const float MENU_MULTI = 4; +const float MENU_SETTINGS = 8; +const float MENU_ABOUT = 16; +const float MENU_JOIN = 32; +const float MENU_PAUSE = 64; +const float MENU_IGS = 128; +const float MENU_RES = 256; +const float MENU_GSETTINGS = 512; +const float MENU_CSETTINGS = 1024; +const float MENU_CUSTOMS = 2048; +const float MENU_ACHIEVEMENTS = 4096; +const float MENU_CONSETTINGS = 8192; +const float MENU_AUDSETTINGS = 16384; + + +float useprint_type; +float useprint_weapon; +float useprint_cost; +float useprint_time; + +string scrolltext; +float stext; +float scrollopacity; +float scrollheight; + +float HUD_Change_time; +float Hitmark_time; +float crosshair_spread_time; +float zoom_2_time; +float broadcast_time; +float broadcast_type; + +float weaponframetime; +float weapon2frametime; +float oldweaponframe; +float oldweapon2frame; +float curweaponframe; +float curweapon2frame; +float interpolating; +float interpolating2; + + +float rounds; +float perks; +float rounds_change; + +float playerpoints[4]; // player point holders, player 1 points are index 0, player 2 at 1... +float playerkills[4]; // player kill holders, player 1 points are index 0, player 2 at 1... +string playernames[4]; // player name holders, player 1 name at index 0, player 2 at 1... +float player_count; +float score_show; + +#define MAX_ACHIEVEMENTS 42 + +var struct achievementlist_t +{ + string img; + float unlocked; + string name; + string description; + float progress; +} achievements[MAX_ACHIEVEMENTS]; + +float active_achievement; +float current_achievement_page; +float achievement_pages; + + +float K_LEFTDOWN, K_RIGHTDOWN, K_BACKDOWN, K_FORWARDDOWN; + +#define P_JUG 1 +#define P_DOUBLE 2 +#define P_SPEED 4 +#define P_REVIVE 8 +#define P_FLOP 16 +#define P_STAMIN 32 + +vector TEXT_LIGHTBLUE = [(2/255), (118/255), (181/255)]; +vector TEXT_ORANGE = [(235/255), (189/255), 0]; +vector TEXT_GREEN = [0, (230/255), (34/255)]; +vector TEXT_RED = [1, 0, 0]; + +float fade_time; +float fade_type; + +float menu_initialized; + +float customs_interact; + +vector camang; // used for punches + +//world text +string chaptertitle; +string location; +string date; +string person; + +//custom hud +string huddir; + +//this just cleans up settings a bit.. +#define S_ENABLED "Enabled" +#define S_DISABLED "Disabled" +float wasigs; + +//controls +#define MAX_BINDS 14 + +float editBind[MAX_BINDS]; +string buttonBind[MAX_BINDS]; +string tempBind[MAX_BINDS]; + +// resolution +#define MAX_43 3 +#define MAX_54 1 +#define MAX_1610 5 +#define MAX_169 10 +#define MAX_219 2 +float aspectratio; +float fullscreenval; +float active_swidth; +float active_sheight; +float screen_width_43[MAX_43]; +float screen_height_43[MAX_43]; +float screen_width_54[MAX_54]; +float screen_height_54[MAX_54]; +float screen_width_1610[MAX_1610]; +float screen_height_1610[MAX_1610]; +float screen_width_169[MAX_169]; +float screen_height_169[MAX_169]; +float screen_width_219[MAX_219]; +float screen_height_219[MAX_219]; + +//controller buttons + +/* + reference: + 0: A + 1: B + 2: X + 3: Y + 4: DPAD UP + 5: DPAD DOWN + 6: DPAD LEFT + 7: DPAD RIGHT + +*/ +float GPActive[32]; + +string build_datetime; +#define VERSION_STRING "v1.0" \ No newline at end of file diff --git a/source/client/defs/fte.qc b/source/client/defs/fte.qc new file mode 100644 index 0000000..d852dbc --- /dev/null +++ b/source/client/defs/fte.qc @@ -0,0 +1,2114 @@ +/* +This file was automatically generated by FTE Quake v1.06 +This file can be regenerated by issuing the following command: +pr_dumpplatform -O csdefs -Tcs +Available options: +-Ffte - target only FTE (optimations and additional extensions) +-Tnq - dump specifically NQ fields +-Tqw - dump specifically QW fields +-Tcs - dump specifically CSQC fields +-Tmenu - dump specifically menuqc fields +-Fdefines - generate #defines instead of constants +-Faccessors - use accessors instead of basic types via defines +-O - write to a different qc file +*/ +#pragma noref 1 +//#pragma flag enable logicops +#pragma warning error Q101 /*too many parms*/ +#pragma warning error Q105 /*too few parms*/ +#pragma warning error Q106 /*assignment to constant/lvalue*/ +#pragma warning error Q208 /*system crc unknown*/ +#pragma warning disable F211 /*system crc outdated (eg: dp's csqc)*/ +#pragma warning enable F301 /*non-utf-8 strings*/ +#pragma warning enable F302 /*uninitialised locals*/ +#ifndef CSQC +#define CSQC +#endif +#define FTE_PEXT_SETVIEW /* NQ's svc_setview works correctly even in quakeworld */ +#define DP_ENT_SCALE +#define FTE_PEXT_LIGHTSTYLECOL +#define DP_ENT_ALPHA +#define FTE_PEXT_VIEW2 +#define FTE_PEXT_ACURATETIMINGS +#define FTE_PEXT_SOUNDDBL +#define FTE_PEXT_FATNESS +#define DP_HALFLIFE_MAP +#define FTE_PEXT_TE_BULLET +#define FTE_PEXT_HULLSIZE +#define FTE_PEXT_MODELDBL +#define FTE_PEXT_ENTITYDBL +#define FTE_PEXT_ENTITYDBL2 +#define FTE_PEXT_FLOATCOORDS +#define FTE_PEXT_VWEAP +#define FTE_PEXT_Q2BSP +#define FTE_PEXT_Q3BSP +#define DP_ENT_COLORMOD +#define FTE_HEXEN2 +#define FTE_PEXT_SPAWNSTATIC +#define FTE_PEXT_CUSTOMTENTS +#define FTE_PEXT_256PACKETENTITIES +#define TEI_SHOWLMP2 +#define DP_GFX_QUAKE3MODELTAGS +#define FTE_PK3DOWNLOADS +#define PEXT_CHUNKEDDOWNLOADS +#define EXT_CSQC_SHARED +#define PEXT_DPFLAGS +#define EXT_CSQC +#define BX_COLOREDTEXT +#define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */ +#define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */ +#define DP_EF_ADDITIVE +#define DP_EF_BLUE +#define DP_EF_FULLBRIGHT +#define DP_EF_NODEPTHTEST +#define DP_EF_NODRAW +#define DP_EF_NOGUNBOB +#define DP_EF_NOSHADOW +#define DP_EF_RED +#define DP_ENT_CUSTOMCOLORMAP +#define DP_ENT_EXTERIORMODELTOCLIENT +#define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum("myeffectname"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */ +#define DP_ENT_VIEWMODEL +#define DP_GECKO_SUPPORT +#define DP_GFX_SKINFILES +#define DP_GFX_SKYBOX +#define DP_HALFLIFE_MAP_CVAR +#define DP_INPUTBUTTONS +#define DP_LIGHTSTYLE_STATICVALUE +#define DP_LITSUPPORT +#define DP_MD3_TAGSINFO +#define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */ +#define DP_MOVETYPEBOUNCEMISSILE +#define DP_MOVETYPEFOLLOW +#define DP_QC_ASINACOSATANATAN2TAN +#define DP_QC_CHANGEPITCH +#define DP_QC_COPYENTITY +#define DP_QC_CRC16 +#define DP_QC_CVAR_DEFSTRING +#define DP_QC_CVAR_STRING +#define DP_QC_CVAR_TYPE +#define DP_QC_EDICT_NUM +#define DP_QC_ENTITYDATA +#define DP_QC_ETOS +#define DP_QC_FINDCHAIN +#define DP_QC_FINDCHAINFLOAT +#define DP_QC_FINDFLAGS +#define DP_QC_FINDCHAINFLAGS +#define DP_QC_FINDFLOAT +#define DP_QC_FS_SEARCH +#define DP_QC_GETSURFACE +#define DP_QC_GETSURFACEPOINTATTRIBUTE +#define DP_QC_MINMAXBOUND +#define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */ +#define DP_SV_PRINT /* Says that the print builtin can be used from nqssqc (as well as just csqc), bypassing the developer cvar issues. */ +#define DP_QC_RANDOMVEC +#define DP_QC_RENDER_SCENE /* clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it. */ +#define DP_QC_SINCOSSQRTPOW +#define DP_QC_SPRINTF /* Provides the sprintf builtin, which allows for rich formatting along the lines of C's function with the same name. Not to be confused with QC's sprint builtin. */ +#define DP_QC_STRFTIME +#define DP_QC_STRING_CASE_FUNCTIONS +#define DP_QC_STRINGBUFFERS +#define DP_QC_STRINGCOLORFUNCTIONS +#define DP_QC_STRREPLACE +#define DP_QC_TOKENIZEBYSEPARATOR +#define DP_QC_TRACEBOX +#define DP_QC_TRACETOSS +#define DP_QC_TRACE_MOVETYPE_HITMODEL +#define DP_QC_TRACE_MOVETYPE_WORLDONLY +#define DP_QC_TRACE_MOVETYPES +#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns. */ +#define DP_QC_URI_ESCAPE +#define DP_QC_URI_GET +#define DP_QC_URI_POST +#define DP_QC_VECTOANGLES_WITH_ROLL +#define DP_QC_VECTORVECTORS +#define DP_QC_WHICHPACK +#define DP_QUAKE2_MODEL +#define DP_QUAKE2_SPRITE +#define DP_QUAKE3_MODEL +#define DP_REGISTERCVAR +#define DP_SND_SOUND7_WIP2 +#define DP_SND_STEREOWAV +#define DP_SND_OGGVORBIS +#define DP_SOLIDCORPSE +#define DP_SPRITE32 +#define DP_SV_BOTCLIENT +#define DP_SV_CLIENTCOLORS /* Provided only for compatibility with DP. */ +#define DP_SV_CLIENTNAME /* Provided only for compatibility with DP. */ +#define DP_SV_DRAWONLYTOCLIENT +#define DP_SV_DROPCLIENT /* Equivelent to quakeworld's stuffcmd(self,"disconnect\n"); hack */ +#define DP_SV_EFFECT +#define DP_SV_EXTERIORMODELFORCLIENT +#define DP_SV_NODRAWTOCLIENT +#define DP_SV_PLAYERPHYSICS /* Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction. */ +#define DP_SV_POINTSOUND +#define DP_SV_PRECACHEANYTIME /* Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...) */ +#define DP_SV_SETCOLOR +#define DP_SV_SPAWNFUNC_PREFIX +#define DP_SV_WRITEPICTURE +#define DP_SV_WRITEUNTERMINATEDSTRING +#define DP_TE_BLOOD +#define DP_TE_CUSTOMFLASH +#define DP_TE_EXPLOSIONRGB +#define DP_TE_PARTICLECUBE +#define DP_TE_PARTICLERAIN +#define DP_TE_PARTICLESNOW +#define DP_TE_SMALLFLASH +#define DP_TE_SPARK +#define DP_TE_STANDARDEFFECTBUILTINS +#define DP_VIEWZOOM +#define EXT_BITSHIFT +#define EXT_DIMENSION_VISIBILITY +#define EXT_DIMENSION_PHYSICS +#define EXT_DIMENSION_GHOST +#define FRIK_FILE +#define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */ +#define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */ +#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ +#define FTE_CSQC_HALFLIFE_MODELS +#define FTE_CSQC_SERVERBROWSER /* Provides builtins to query the engine's serverbrowser servers list from ssqc. Note that these builtins are always available in menuqc. */ +#define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */ +#define FTE_CSQC_RAWIMAGES /* Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer. */ +#define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */ +#define FTE_CSQC_REVERB /* Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver. */ +#define FTE_CSQC_WINDOWCAPTION /* Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out. */ +#define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */ +#define FTE_ENT_UNIQUESPAWNID +#define FTE_EXTENDEDTEXTCODES +#define FTE_FORCESHADER /* Allows csqc to override shaders on models with an explicitly named replacement. Also allows you to define shaders with a fallback if it does not exist on disk. */ +#define FTE_FORCEINFOKEY /* Provides an easy way to change a user's userinfo from the server. */ +#define FTE_GFX_QUAKE3SHADERS /* specifies that the engine has full support for vanilla quake3 shaders */ +#define FTE_GFX_REMAPSHADER /* With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface. */ +#define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */ +#define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */ +#define FTE_MEDIA_AVI /* playfilm command supports avi files. */ +#define FTE_MEDIA_CIN /* playfilm command supports q2 cin files. */ +#define FTE_MEDIA_ROQ /* playfilm command supports q3 roq files. */ +#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand. */ +#define FTE_MULTITHREADED /* Faux multithreading, allowing multiple contexts to run in sequence. */ +#define FTE_MVD_PLAYERSTATS /* In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case. */ +#define FTE_QC_NPCCHAT +#define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/*.cfg, the format of which is documented elsewhere. */ +#define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */ +#define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */ +#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */ +#define FTE_QC_FILE_BINARY /* Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers. */ +#define FTE_QC_CHANGELEVEL_HUB /* Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data. */ +#define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */ +#define FTE_QC_CHECKPVS +#define FTE_QC_CROSSPRODUCT +#define FTE_QC_FS_SEARCH_SIZEMTIME +#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed. */ +#define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */ +#define FTE_QC_INFOKEY /* QuakeWorld's infokey builtin works, and reports at least name+topcolor+bottomcolor+ping(in ms)+ip(unmasked, but not always ipv4)+team(aka bottomcolor in nq). Does not require actual localinfo/serverinfo/userinfo, but they're _highly_ recommended to any engines with csqc */ +#define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */ +#define FTE_QC_MATCHCLIENTNAME +#define FTE_QC_MULTICAST /* QuakeWorld's multicast builtin works along with MSG_MULTICAST, but also with unicast support. */ +#define FTE_QC_PAUSED +#define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */ +#define FTE_QC_RAGDOLL_WIP +#define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */ +#define FTE_QC_STUFFCMDFLAGS /* Variation on regular stuffcmd that gives control over how spectators/mvds should be treated. */ +#define FTE_QC_TRACETRIGGER +#define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */ +#define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */ +#define FTE_QUAKE3_CLIENT /* This engine is able to act as a quake3 client */ +#define FTE_QUAKE3_SERVER /* This engine is able to act as a quake3 server */ +#define FTE_SOLID_LADDER /* Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS */ +#define FTE_SQL /* Provides sql* builtins which can be used for sql database access */ +#define FTE_SQL_SQLITE /* SQL functionality is able to utilise sqlite databases */ +#define FTE_STRINGS /* Extra builtins (and additional behaviour) to make string manipulation easier */ +#define FTE_SV_POINTPARTICLES /* Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions. */ +#define FTE_SV_REENTER +#define FTE_TE_STANDARDEFFECTBUILTINS /* Provides builtins to replace writebytes, with a QW compatible twist. */ +#define FTE_TERRAIN_MAP /* This engine supports .hmp files, as well as terrain embedded within bsp files. */ +#define FTE_RAW_MAP /* This engine supports directly loading .map files, as well as realtime editing of the various brushes. */ +#define KRIMZON_SV_PARSECLIENTCOMMAND /* SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc. */ +#define NEH_CMD_PLAY2 +#define NEH_RESTOREGAME +#define QSG_CVARSTRING +#define QW_ENGINE +#define QWE_MVD_RECORD /* You can use the easyrecord command to record MVD demos serverside. */ +#define TEI_MD3_MODEL +#define TENEBRAE_GFX_DLIGHTS /* Allows ssqc to attach rtlights to entities with various special properties. */ +#define ZQ_MOVETYPE_FLY /* MOVETYPE_FLY works on players. */ +#define ZQ_MOVETYPE_NOCLIP /* MOVETYPE_NOCLIP works on players. */ +#define ZQ_MOVETYPE_NONE /* MOVETYPE_NONE works on players. */ +#define ZQ_VWEP +#define ZQ_QC_STRINGS /* The strings-only subset of FRIK_FILE is supported. */ + +#ifdef _ACCESSORS +accessor strbuf : float; +accessor searchhandle : float; +accessor hashtable : float; +accessor infostring : string; +accessor filestream : float; +accessor filestream : float; +#else +#define strbuf float +#define searchhandle float +#define hashtable float +#define infostring string +#define filestream float +#endif + +entity self; /* The magic me */ +entity other; /* Valid in touch functions, this is the entity that we touched. */ +entity world; /* The null entity. Hurrah. Readonly after map spawn time. */ +float time; /* The current game time. Stops when paused. */ +float cltime; /* A local timer that ticks relative to local time regardless of latency, packetloss, or pause. */ +float frametime; /* The time since the last physics/render/input frame. */ +float player_localentnum; /* This is entity number the player is seeing from/spectating, or the player themself, can change mid-map. */ +float player_localnum; /* The 0-based player index, valid for getplayerkeyvalue calls. */ +float maxclients; /* Maximum number of player slots on the server. */ +float clientcommandframe; /* This is the input-frame sequence. frames < clientcommandframe have been sent to the server. frame==clientcommandframe is still being generated and can still change. */ +float servercommandframe; /* This is the input-frame that was last acknowledged by the server. Input frames greater than this should be applied to the player's entity. */ +string mapname; /* The short name of the map. */ +float intermission; +vector v_forward, v_up, v_right; +vector view_angles; /* +x=DOWN */ +float trace_allsolid, trace_startsolid, trace_fraction; +vector trace_endpos, trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; +float input_timelength; +vector input_angles; /* +x=DOWN */ +vector input_movevalues; +float input_buttons; +float input_impulse; +void end_sys_globals; +.float modelindex; /* This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it. */ +.vector absmin; /* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */ +.vector absmax; /* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */ +.float entnum; /* The entity number as its known on the server. */ +.float drawmask; /* Acts as a filter in the addentities call. */ +.float() predraw; /* Called by addentities after the filter and before the entity is actually drawn. Do your interpolation and animation in here. Should return one of the PREDRAW_* constants. */ +.float movetype; /* Describes how the entity moves. One of the MOVETYPE_ constants. */ +.float solid; /* Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants */ +.vector origin; /* The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction. */ +.vector oldorigin; /* This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck. */ +.vector velocity; /* The direction and speed that the entity is moving in world space. */ +.vector angles; /* The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN. */ +.vector avelocity; /* The amount the entity's angles change by per second. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy if you're changing more than one angle at a time. */ +.float pmove_flags; +.string classname; /* Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit. */ +.float renderflags; +.string model; /* The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible. */ +.float frame; /* The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc. */ +.float frame1time; /* The absolute time into the animation/framegroup specified by .frame. */ +.float frame2; /* The alternative frame. Visible only when lerpfrac is set to 1. */ +.float frame2time; /* The absolute time into the animation/framegroup specified by .frame2. */ +.float lerpfrac; /* If 0, use frame1 only. If 1, use frame2 only. Mix them together for values between. */ +.float skin; /* The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water. */ +.float effects; /* Lots of random flags that change random effects. See EF_* constants. */ +.vector mins; /* The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel. */ +.vector maxs; /* like mins, but in the other direction. */ +.vector size; /* maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel) */ +.void() touch; +.void() think; +.void() blocked; +.float nextthink; /* The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal. */ +.entity chain; +.entity enemy; +.float flags; +.float colormap; +.entity owner; +void end_sys_fields; +vector input_cursor_screen; +vector input_cursor_trace_start; +vector input_cursor_trace_endpos; +float input_cursor_trace_entnum; +int trace_endcontents; +int trace_surfaceflags; +int trace_brush_id; +int trace_brush_faceid; +int trace_surface_id; /* 1-based. 0 if not known. */ +int trace_bone_id; /* 1-based. 0 if not known. typically needs MOVE_HITMODEL. */ +int trace_triangle_id; /* 1-based. 0 if not known. */ +vector global_gravitydir = '0 0 -1'; /* The direction gravity should act in if not otherwise specified per entity. */ +int serverid; /* The unique id of this server within the server cluster. */ +.vector punchangle; +.float gravity; +.float hull; /* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */ +.entity movechain; /* This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity. */ +.void() chainmoved; /* Called when the entity is moved as a result of being part of another entity's .movechain */ +.void(float old, float new) contentstransition; /* This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own. */ +.float dimension_solid; /* This is the bitmask of dimensions which the entity is solid within. */ +.float dimension_hit; /* This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through. */ +.int hitcontentsmaski; /* Traces performed for this entity will impact against surfaces that match this contents mask. */ +.float dphitcontentsmask; /* Some crappy field that inefficiently requires translating to the native contents flags. Ditch the 'dp', do it properly. */ +.float scale; /* Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16. */ +.float fatness; /* How many QuakeUnits to push the entity's verticies along their normals by. */ +.float alpha; /* The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility. */ +.float modelflags; /* Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile. */ +.float basebone; /* The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects. */ +.float baseframe; /* See basebone */ +.void() customphysics; /* Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate. */ +.entity tag_entity; +.float tag_index; +.float skeletonindex; /* This object serves as a container for the skeletal bone states used to override the animation data. */ +.vector colormod; /* Provides a colour tint for the entity. */ +.vector glowmod; +.vector gravitydir; /* Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings. */ +.vector(vector org, vector ang) camera_transform; /* Provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */ +.float geomtype; +.float friction; +.float erp; +.float jointtype; +.float mass; +.float bouncefactor; +.float bouncestop; +.float idealpitch; +.float pitch_speed; +.float drawflags; /* Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE. */ +.float abslight; /* Allows overriding light levels. Use drawflags to state that this field should actually be used. */ +.vector color; /* This affects the colour of realtime lights that were enabled via the pflags field. */ +.float light_lev; /* This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field). */ +.float style; /* Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights. */ +.float pflags; /* Realtime lighting flags */ +.float frame3; /* Some people just don't understand how to use framegroups... */ +.float frame4; +.float lerpfrac3; +.float lerpfrac4; +.float forceshader; /* Contains a shader handle used to replace all surfaces upon the entity. */ +.float baseframe2; /* See basebone */ +.float baseframe1time; /* See basebone */ +.float baseframe2time; /* See basebone */ +.float baselerpfrac; /* See basebone */ +.float bonecontrol1; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol2; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol3; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol4; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol5; /* Halflife model format bone controller. This typically affects the mouth. */ +.float subblendfrac; /* Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch. */ +.float basesubblendfrac; /* See basebone */ +void(float reqid, float responsecode, string resourcebody) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ +void(float apilevel, string enginename, float engineversion) CSQC_Init; /* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */ +void() CSQC_WorldLoaded; /* Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp. */ +void() CSQC_Shutdown; /* Specifies that the csqc is going down. Save your persistant settings here. */ +void(float vwidth, float vheight, float notmenu) CSQC_UpdateView; /* Called every single video frame. The CSQC is responsible for rendering the entire screen. */ +void(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading; /* Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it. */ +void(string msg) CSQC_Parse_StuffCmd; /* Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely. */ +float(string msg) CSQC_Parse_CenterPrint; /* Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint. */ +float(float save, float take, vector inflictororg) CSQC_Parse_Damage; /* Called as a result of player.dmg_save or player.dmg_take being set on the server. +Return true to completely inhibit the engine's colour shift and damage rolls, allowing you to do your own thing. +You can use punch_roll += (normalize(inflictororg-player.origin)*v_right)*(take+save)*autocvar_v_kickroll; as a modifier for the roll angle should the player be hit from the side, and slowly fade it away over time. */ +void(string printmsg, float printlvl) CSQC_Parse_Print; /* Gives the CSQC a chance to intercept sprint/bprint builtin calls. CSQC should filter by the client's current msg setting and then pass the message on to the print command, or handle them itself. */ +void() CSQC_Parse_Event; /* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */ +float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent; /* Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */ +__used void() CSQC_Input_Frame; /* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */ +void(string rendererdescription) CSQC_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */ +float(string cmd) CSQC_ConsoleCommand; /* Called if the user uses any console command registed via registercommand. */ +float(string text, string info) CSQC_ConsoleLink; /* Called if the user clicks a ^[text\infokey\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself. +WARNING: link text can potentially come from other players, so be careful about what you allow to be changed. */ +void(float entnum) CSQC_Ent_Spawn; /* Clumsily defined function for compat with DP. Should call spawn, set that ent's entnum field, and return the entity inside the 'self' global which will then be used for fllowing Ent_Updates. MUST NOT PARSE ANY NETWORK DATA (which makes it kinda useless). */ +void(float isnew) CSQC_Ent_Update; /* Parses the data sent by ssqc's various SendEntity functions (must use the exact same reads as the ssqc used writes - to debug this rule more easily, you may wish to use sv_csqcdebug). 'self' provides context between frames, and self.entnum should normally report which ssqc entity . Be aware that interpolation will need to happen separately. */ +void() CSQC_Ent_Remove; +float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags) CSQC_Event_Sound; +float(string resname, string restype) CSQC_LoadResource; /* Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called. */ +float() CSQC_Parse_TempEntity; /* Please don't use this. Use CSQC_Parse_Event and multicasts instead. +The use of serverside protocol translation to handle QW vs NQ protocols mean that you're likely to end up reading slightly different data. Which is bad. +Return true to say that you fully handled the tempentity. Return false to have the client attempt to rewind the network stream and parse the message itself. */ +void(string cmdtext) GameCommand; +string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) Cef_GeneratePage; /* Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are +-separated key/value pairs (use tokenizebyseparator). */ +void(float prevprogs) init; /* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */ +void() initents; /* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */ +__used var float physics_mode = 2; /* 0: original csqc - physics are not run +1: DP-compat. Thinks occur, but not true movetypes. +2: movetypes occur just as they do in ssqc. */ +float gamespeed; /* Set by the engine, this is the value of the sv_gamespeed cvar */ +float numclientseats; /* This is the number of splitscreen clients currently running on this client. */ +var vector drawfontscale = '1 1 0'; /* Specifies a scaler for all text rendering. There are other ways to implement this. */ +float drawfont; /* Allows you to choose exactly which font is to be used to draw text. Fonts can be registered/allocated with the loadfont builtin. */ +const float FONT_DEFAULT = 0; +const float TRUE = 1; +const float FALSE = 0; /* File not found... */ +const float M_PI = 3.14159; +const float MOVETYPE_NONE = 0; +const float MOVETYPE_WALK = 3; +const float MOVETYPE_STEP = 4; +const float MOVETYPE_FLY = 5; +const float MOVETYPE_TOSS = 6; +const float MOVETYPE_PUSH = 7; +const float MOVETYPE_NOCLIP = 8; +const float MOVETYPE_FLYMISSILE = 9; +const float MOVETYPE_BOUNCE = 10; +const float MOVETYPE_BOUNCEMISSILE = 11; +const float MOVETYPE_FOLLOW = 12; +const float MOVETYPE_6DOF = 30; /* A glorified MOVETYPE_FLY. Players using this movetype will get some flightsim-like physics, with fully independant rotations (order-dependant transforms). */ +const float MOVETYPE_WALLWALK = 31; /* Players using this movetype will be able to orient themselves to walls, and then run up them. */ +const float MOVETYPE_PHYSICS = 32; /* Enable the use of ODE physics upon this entity. */ +const float SOLID_NOT = 0; +const float SOLID_TRIGGER = 1; +const float SOLID_BBOX = 2; +const float SOLID_SLIDEBOX = 3; +const float SOLID_BSP = 4; /* Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH. */ +const float SOLID_CORPSE = 5; /* Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .solid value to SOLID_BBOX or so, perform the traceline, then revert the player's .solid value. */ +const float SOLID_LADDER = 20; /* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */ +const float SOLID_PORTAL = 21; /* CSG subtraction volume combined with entity transformations on impact. */ +const float SOLID_PHYSICS_BOX = 32; +const float SOLID_PHYSICS_SPHERE = 33; +const float SOLID_PHYSICS_CAPSULE = 34; +const float SOLID_PHYSICS_TRIMESH = 35; +const float SOLID_PHYSICS_CYLINDER = 36; +const float GEOMTYPE_NONE = -1; +const float GEOMTYPE_SOLID = 0; +const float GEOMTYPE_BOX = 1; +const float GEOMTYPE_SPHERE = 2; +const float GEOMTYPE_CAPSULE = 3; +const float GEOMTYPE_TRIMESH = 4; +const float GEOMTYPE_CYLINDER = 5; +const float GEOMTYPE_CAPSULE_X = 6; +const float GEOMTYPE_CAPSULE_Y = 7; +const float GEOMTYPE_CAPSULE_Z = 8; +const float GEOMTYPE_CYLINDER_X = 9; +const float GEOMTYPE_CYLINDER_Y = 10; +const float GEOMTYPE_CYLINDER_Z = 11; +const float JOINTTYPE_FIXED = -1; +const float JOINTTYPE_POINT = 1; +const float JOINTTYPE_HINGE = 2; +const float JOINTTYPE_SLIDER = 3; +const float JOINTTYPE_UNIVERSAL = 4; +const float JOINTTYPE_HINGE2 = 5; +const float GE_MAXENTS = -1; /* Valid for getentity, ignores the entity argument. Returns the maximum number of entities which may be valid, to avoid having to poll 65k when only 100 are used. */ +const float GE_ACTIVE = 0; /* Valid for getentity. Returns whether this entity is known to the client or not. */ +const float GE_ORIGIN = 1; /* Valid for getentity. Returns the interpolated .origin. */ +const float GE_FORWARD = 2; /* Valid for getentity. Returns the interpolated forward vector. */ +const float GE_RIGHT = 3; /* Valid for getentity. Returns the entity's right vector. */ +const float GE_UP = 4; /* Valid for getentity. Returns the entity's up vector. */ +const float GE_SCALE = 5; /* Valid for getentity. Returns the entity .scale. */ +const float GE_ORIGINANDVECTORS = 6; /* Valid for getentity. Returns interpolated .origin, but also sets v_forward, v_right, and v_up accordingly. Use vectoangles(v_forward,v_up) to determine the angles. */ +const float GE_ALPHA = 7; /* Valid for getentity. Returns the entity alpha. */ +const float GE_COLORMOD = 8; /* Valid for getentity. Returns the colormod vector. */ +const float GE_PANTSCOLOR = 9; /* Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value. */ +const float GE_SHIRTCOLOR = 10; /* Valid for getentity. Returns the entity's lower color (from .colormap), as a palette range value. */ +const float GE_SKIN = 11; /* Valid for getentity. Returns the entity's .skin index. */ +const float GE_MINS = 12; /* Valid for getentity. Guesses the entity's .min vector. */ +const float GE_MAXS = 13; /* Valid for getentity. Guesses the entity's .max vector. */ +const float GE_ABSMIN = 14; /* Valid for getentity. Guesses the entity's .absmin vector. */ +const float GE_ABSMAX = 15; /* Valid for getentity. Guesses the entity's .absmax vector. */ +const float GE_MODELINDEX = 200; /* Valid for getentity. Guesses the entity's .modelindex float. */ +const float GE_MODELINDEX2 = 201; /* Valid for getentity. Guesses the entity's .vw_index float. */ +const float GE_EFFECTS = 202; /* Valid for getentity. Guesses the entity's .effects float. */ +const float GE_FRAME = 203; /* Valid for getentity. Guesses the entity's .frame float. */ +const float GE_ANGLES = 204; /* Valid for getentity. Guesses the entity's .angles vector. */ +const float GE_FATNESS = 205; /* Valid for getentity. Guesses the entity's .fatness float. */ +const float GE_DRAWFLAGS = 206; /* Valid for getentity. Guesses the entity's .drawflags float. */ +const float GE_ABSLIGHT = 207; /* Valid for getentity. Guesses the entity's .abslight float. */ +const float GE_GLOWMOD = 208; /* Valid for getentity. Guesses the entity's .glowmod vector. */ +const float GE_GLOWSIZE = 209; /* Valid for getentity. Guesses the entity's .glowsize float. */ +const float GE_GLOWCOLOUR = 210; /* Valid for getentity. Guesses the entity's .glowcolor float. */ +const float GE_RTSTYLE = 211; /* Valid for getentity. Guesses the entity's .style float. */ +const float GE_RTPFLAGS = 212; /* Valid for getentity. Guesses the entity's .pflags float. */ +const float GE_RTCOLOUR = 213; /* Valid for getentity. Guesses the entity's .color vector. */ +const float GE_RTRADIUS = 214; /* Valid for getentity. Guesses the entity's .light_lev float. */ +const float GE_TAGENTITY = 215; /* Valid for getentity. Guesses the entity's .tag_entity float. */ +const float GE_TAGINDEX = 216; /* Valid for getentity. Guesses the entity's .tag_index float. */ +const float GE_GRAVITYDIR = 217; /* Valid for getentity. Guesses the entity's .gravitydir vector. */ +const float GE_TRAILEFFECTNUM = 218; /* Valid for getentity. Guesses the entity's .traileffectnum float. */ +const float CONTENT_EMPTY = -1; +const float CONTENT_SOLID = -2; +const float CONTENT_WATER = -3; +const float CONTENT_SLIME = -4; +const float CONTENT_LAVA = -5; +const float CONTENT_SKY = -6; +const float CONTENT_LADDER = -16; /* If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume. */ +const int CONTENTBIT_NONE = 0x00000000i; +const int CONTENTBIT_SOLID = 0x00000001i; +const int CONTENTBIT_LAVA = 0x00000008i; +const int CONTENTBIT_SLIME = 0x00000010i; +const int CONTENTBIT_WATER = 0x00000020i; +const int CONTENTBIT_FTELADDER = 0x00004000i; +const int CONTENTBIT_PLAYERCLIP = 0x00010000i; +const int CONTENTBIT_MONSTERCLIP = 0x00020000i; +const int CONTENTBIT_BODY = 0x02000000i; +const int CONTENTBIT_CORPSE = 0x04000000i; +const int CONTENTBIT_Q2LADDER = 0x20000000i; /* Content bit specific to q2bsp */ +const int CONTENTBIT_SKY = 0x80000000i; +const int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY; /* Bits that traceline would normally consider solid */ +const int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP; /* Bits that tracebox would normally consider solid */ +const int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY; +const int SPA_POSITION; /* These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin */ +const int SPA_S_AXIS = 1; +const int SPA_T_AXIS = 2; +const int SPA_R_AXIS = 3; /* aka: SPA_NORMAL */ +const int SPA_TEXCOORDS0 = 4; +const int SPA_LIGHTMAP0_TEXCOORDS = 5; +const int SPA_LIGHTMAP0_COLOR = 6; +const float CHAN_AUTO = 0; /* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */ +const float CHAN_WEAPON = 1; +const float CHAN_VOICE = 2; +const float CHAN_ITEM = 3; +const float CHAN_BODY = 4; +const float SOUNDFLAG_ABSVOLUME = 16; /* The sample's volume is not scaled by the volume cvar. Use with caution */ +const float SOUNDFLAG_FORCELOOP = 2; /* The sound will restart once it reaches the end of the sample. */ +const float SOUNDFLAG_NOSPACIALISE = 4; /* The different audio channels are played at the same volume regardless of which way the player is facing, without needing to use 0 attenuation. */ +const float SOUNDFLAG_NOREVERB = 32; /* Disables the use of underwater/reverb effects on this sound effect. */ +const float SOUNDFLAG_FOLLOW = 64; /* The sound's origin will updated to follow the emitting entity. */ +const float ATTN_NONE = 0; /* Sounds with this attenuation can be heard throughout the map */ +const float ATTN_NORM = 1; /* Standard attenuation */ +const float ATTN_IDLE = 2; /* Extra attenuation so that sounds don't travel too far. */ +const float ATTN_STATIC = 3; /* Even more attenuation to avoid torches drowing out everything else throughout the map. */ +const string INFOKEY_P_PING = "ping"; /* The player's ping time, in milliseconds. */ +const string INFOKEY_P_NAME = "name"; /* The player's name. */ +const string INFOKEY_P_SPECTATOR = "*spectator"; /* Whether the player is a spectator or not. */ +const string INFOKEY_P_TOPCOLOR = "topcolor"; /* The player's upper/shirt colour (palette index). */ +const string INFOKEY_P_BOTTOMCOLOR = "bottomcolor"; /* The player's lower/pants/trouser colour (palette index). */ +const string INFOKEY_P_TOPCOLOR_RGB = "topcolor_rgb"; /* The player's upper/shirt colour as an rgb value in a format usable with stov. */ +const string INFOKEY_P_BOTTOMCOLOR_RGB = "bottomcolor_rgb"; /* The player's lower/pants/trouser colour as an rgb value in a format usable with stov. */ +const string INFOKEY_P_MUTED = "ignored"; /* 0: we can see the result of the player's say/say_team commands. 1: we see no say/say_team messages from this player. Use the ignore command to toggle this value. */ +const string INFOKEY_P_VOIP_MUTED = "vignored"; /* 0: we can hear this player when they speak (assuming voip is generally enabled). 1: we ignore everything this player says. Use cl_voip_mute to change the values. */ +const string INFOKEY_P_ENTERTIME = "entertime"; /* Reads the timestamp at which the player entered the game, in terms of csqc's time global. */ +const string INFOKEY_P_FRAGS = "frags"; /* Reads a player's frag count. */ +const string INFOKEY_P_PACKETLOSS = "pl"; /* Reads a player's packetloss, as a percentage. */ +const string INFOKEY_P_VOIPSPEAKING = "voipspeaking"; /* Boolean value that says whether the given player is currently sending voice information. */ +const string INFOKEY_P_VOIPLOUDNESS = "voiploudness"; /* Only valid for the local player. Gives a value between 0 and 1 to indicate to the user how loud their mic is. */ +const string SERVERKEY_IP = "ip"; /* The address of the server we connected to. */ +const string SERVERKEY_SERVERNAME = "servername"; /* The hostname that was last passed to the connect command. */ +const string SERVERKEY_CONSTATE = "constate"; /* The current connection state. Will be set to one of: disconnected (menu-only mode), active (gamestate received and loaded), connecting(connecting, downloading, or precaching content, aka: loading screen). */ +const string SERVERKEY_TRANSFERRING = "transferring"; /* Set to the hostname of the server that we are attempting to connect or transfer to. */ +const string SERVERKEY_LOADSTATE = "loadstate"; /* loadstage, loading image name, current step, max steps +Stages are: 1=connecting, 2=serverside, 3=clientside +Key will be empty if we are not loading. */ +const string SERVERKEY_PAUSESTATE = "pausestate"; /* 1 if the server claimed to be paused. 0 otherwise */ +const string SERVERKEY_DLSTATE = "dlstate"; /* The progress of any current downloads. Empty string if no download is active, otherwise a tokenizable string containing this info: +files-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes +If the current file info is omitted, then we are waiting for a download to start. */ +const string SERVERKEY_PROTOCOL = "protocol"; /* The protocol we are connected to the server with. */ +const string SERVERKEY_MAXPLAYERS = "maxplayers"; /* The number of player/spectator slots allocated on the server. */ +const float FL_FLY = 1; +const float FL_SWIM = 2; +const float FL_CLIENT = 8; +const float FL_INWATER = 16; +const float FL_MONSTER = 32; +const float FL_ITEM = 256; +const float FL_ONGROUND = 512; +const float FL_PARTIALGROUND = 1024; +const float FL_WATERJUMP = 2048; +const float FL_JUMPRELEASED = 4096; +const float FL_FINDABLE_NONSOLID = 16384; /* Allows this entity to be found with findradius */ +const float MOVE_NORMAL = 0; +const float MOVE_NOMONSTERS = 1; /* The trace will ignore all non-solid_bsp entities. */ +const float MOVE_MISSILE = 2; /* The trace will use a bbox size of +/- 15 against entities with FL_MONSTER set. */ +const float MOVE_HITMODEL = 4; /* Traces will impact the actual mesh of the model instead of merely their bounding box. Should generally only be used for tracelines. Note that this flag is unreliable as an object can animate through projectiles. The bounding box MUST be set to completely encompass the entity or those extra areas will be non-solid (leaving a hole for things to go through). */ +const float MOVE_TRIGGERS = 16; /* This trace type will impact only triggers. It will ignore non-solid entities. */ +const float MOVE_EVERYTHING = 32; /* This type of trace will hit solids and triggers alike. Even non-solid entities. */ +const float MOVE_ENTCHAIN = 128; /* Returns a list of entities impacted via the trace_ent.chain field */ +const float MOVE_OTHERONLY = 256; /* Traces that use this trace type will collide against *only* the entity specified via the 'other' global, and will ignore all owner/solid_not/dimension etc rules, they will still adhere to contents and bsp/bbox rules though. */ +const float RESTYPE_MODEL = 0; /* RESTYPE_* constants are used as arguments with the resourcestatus builtin. */ +const float RESTYPE_SOUND = 1; /* precache_sound */ +const float RESTYPE_PARTICLE = 2; /* particleeffectnum */ +const float RESTYPE_PIC = 3; /* precache_pic. Status results are an amalgomation of the textures used by the named shader. */ +const float RESTYPE_SKIN = 4; /* setcustomskin */ +const float RESTYPE_TEXTURE = 5; /* Individual textures within shaders. These are not directly usable, but may be named as part of a skin file, or a shader. */ +const float RESSTATE_NOTKNOWN = 0; /* RESSTATE_* constants are return values from the resourcestatus builtin. The engine doesn't know about the resource if it is in this state. This means you will need to precache it. Attempting to use it anyway may result in warnings, errors, or silently succeed, depending on engine version and resource type. */ +const float RESSTATE_NOTLOADED = 1; /* The resource was precached, but has been flushed and there has not been an attempt to reload it. If you use the resource normally, chances are it'll be loaded but at the cost of a stall. */ +const float RESSTATE_LOADING = 2; /* Resources in this this state are queued for loading, and will be loaded at the engine's convienience. If you attempt to query the resource now, the engine will stall until the result is available. sounds in this state may be delayed, while models/pics/shaders may be invisible. */ +const float RESSTATE_FAILED = 3; /* Resources in this state are unusable/could not be loaded. You will get placeholders or dummy results. Queries will not stall the engine. The engine may display placeholder content. */ +const float RESSTATE_LOADED = 4; /* Resources in this state are finally usable, everything will work okay. Hurrah. Queries will not stall the engine. */ +const float EF_BRIGHTFIELD = 1; +const float EF_MUZZLEFLASH = 2; +const float EF_BRIGHTLIGHT = 4; +const float EF_DIMLIGHT = 8; +const float EF_NODRAW = 16; +const float EF_ADDITIVE = 32; /* The entity will be drawn with an additive blend. */ +const float EF_BLUE = 64; /* A blue glow */ +const float EF_RED = 128; /* A red glow */ +const float EF_GREEN = 262144; /* A green glow */ +const float EF_ORANGE = 524288; /* A orange glow */ +const float EF_FULLBRIGHT = 512; /* This entity will ignore lighting */ +const float EF_NOSHADOW = 4096; /* This entity will not cast shadows */ +const float EF_NODEPTHTEST = 8192; /* This entity will be drawn over the top of other things that are closer. */ +const float MF_ROCKET = 1; +const float MF_GRENADE = 2; +const float MF_GIB = 4; /* Regular blood trail */ +const float MF_ROTATE = 8; +const float MF_TRACER = 16; /* AKA: green scrag trail */ +const float MF_ZOMGIB = 32; /* Dark blood trail */ +const float MF_TRACER2 = 64; /* AKA: hellknight projectile trail */ +const float MF_TRACER3 = 128; /* AKA: purple vore trail */ +const float PFLAGS_NOSHADOW = 1; /* Associated RT lights attached will not cast shadows, making them significantly faster to draw. */ +const float PFLAGS_CORONA = 2; /* Enables support of coronas on the associated rtlights. */ +const float EV_STRING = 1; +const float EV_FLOAT = 2; +const float EV_VECTOR = 3; +const float EV_ENTITY = 4; +const float EV_FIELD = 5; +const float EV_FUNCTION = 6; +const float EV_POINTER = 7; +const float EV_INTEGER = 8; +const float EV_VARIANT = 9; +hashtable gamestate; /* Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted). */ +const float HASH_REPLACE = 256; /* Used with hash_add. Attempts to remove the old value instead of adding two values for a single key. */ +const float HASH_ADD = 512; /* Used with hash_add. The new entry will be inserted in addition to the existing entry. */ +const float STAT_HEALTH = 0; /* Player's health. */ +const float STAT_WEAPONMODELI = 2; /* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */ +const float STAT_AMMO = 3; /* player.currentammo */ +const float STAT_ARMOR = 4; +const float STAT_WEAPONFRAME = 5; +const float STAT_SHELLS = 6; +const float STAT_NAILS = 7; +const float STAT_ROCKETS = 8; +const float STAT_CELLS = 9; +const float STAT_ACTIVEWEAPON = 10; /* player.weapon */ +const float STAT_TOTALSECRETS = 11; +const float STAT_TOTALMONSTERS = 12; +const float STAT_FOUNDSECRETS = 13; +const float STAT_KILLEDMONSTERS = 14; +const float STAT_ITEMS = 15; /* self.items | (self.items2<<23). In order to decode this stat properly, you need to use getstatbits(STAT_ITEMS,0,23) to read self.items, and getstatbits(STAT_ITEMS,23,11) to read self.items2 or getstatbits(STAT_ITEMS,28,4) to read the visible part of serverflags, whichever is applicable. */ +const float STAT_VIEWHEIGHT = 16; /* player.view_ofs_z */ +const float STAT_VIEW2 = 20; /* This stat contains the number of the entity in the server's .view2 field. */ +const float STAT_VIEWZOOM = 21; /* Scales fov and sensitiity. Part of DP_VIEWZOOM. */ +const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */ +const float VF_MIN = 1; /* The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins. */ +const float VF_MIN_X = 2; +const float VF_MIN_Y = 3; +const float VF_SIZE = 4; /* The width+height of the 3d viewport in screenspace. */ +const float VF_SIZE_X = 5; +const float VF_SIZE_Y = 6; +const float VF_VIEWPORT = 7; /* vector+vector. Two argument shortcut for VF_MIN and VF_SIZE */ +const float VF_FOV = 8; /* sets both fovx and fovy. consider using afov instead. */ +const float VF_FOVX = 9; /* horizontal field of view. does not consider aspect at all. */ +const float VF_FOVY = 10; /* vertical field of view. does not consider aspect at all. */ +const float VF_ORIGIN = 11; /* The origin of the view. Not of the player. */ +const float VF_ORIGIN_X = 12; +const float VF_ORIGIN_Y = 13; +const float VF_ORIGIN_Z = 14; +const float VF_ANGLES = 15; /* The angles the view will be drawn at. Not the angle the client reports to the server. */ +const float VF_ANGLES_X = 16; +const float VF_ANGLES_Y = 17; +const float VF_ANGLES_Z = 18; +const float VF_DRAWWORLD = 19; /* boolean. If set to 1, the engine will draw the world and static/persistant rtlights. If 0, the world will be skipped and everything will be fullbright. */ +const float VF_DRAWENGINESBAR = 20; /* boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically. */ +const float VF_DRAWCROSSHAIR = 21; /* boolean. If set to 1, the engine will draw its default crosshair. */ +const float VF_MINDIST = 23; /* The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs. */ +const float VF_MAXDIST = 24; /* The distance of the far clip plane from the view position. If 0, will be considered infinite. */ +const float VF_CL_VIEWANGLES = 33; +const float VF_CL_VIEWANGLES_X = 34; +const float VF_CL_VIEWANGLES_Y = 35; +const float VF_CL_VIEWANGLES_Z = 36; +const float VF_PERSPECTIVE = 200; /* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled. */ +#define VF_LPLAYER VF_ACTIVESEAT +const float VF_ACTIVESEAT = 202; /* The 'seat' number, used when running splitscreen. */ +const float VF_AFOV = 203; /* Aproximate fov. Matches the 'fov' cvar. The engine handles the aspect ratio for you. */ +const float VF_SCREENVSIZE = 204; /* Provides a reliable way to retrieve the current virtual screen size (even if the screen is automatically scaled to retain aspect). */ +const float VF_SCREENPSIZE = 205; /* Provides a reliable way to retrieve the current physical screen size (cvars need vid_restart for them to take effect). */ +const float VF_VIEWENTITY = 206; /* Changes the RF_EXTERNALMODEL flag on entities to match the new selection, and removes entities flaged with RF_VIEWENTITY. Requires cunning use of .entnum and typically requires calling addentities(MASK_VIEWMODEL) too. */ +const float VF_RT_DESTCOLOUR = 212; /* The texture name to write colour info into, this includes both 3d and 2d drawing. +Additional arguments are: format (rgba8=1,rgba16f=2,rgba32f=3), sizexy. +Written to by both 3d and 2d rendering. +Note that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour. */ +const float VF_RT_SOURCECOLOUR = 209; /* The texture name to use with shaders that specify a $sourcecolour map. */ +const float VF_RT_DEPTH = 210; /* The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (16bit=4,24bit=5,32bit=6), sizexy. */ +const float VF_RT_RIPPLE = 211; /* The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy. */ +const float VF_ENVMAP = 220; /* The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything. */ +const float RF_VIEWMODEL = 1; /* Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob. */ +const float RF_EXTERNALMODEL = 2; /* Specifies that this entity should be displayed in mirrors (and may still cast shadows), but will not otherwise be visible. */ +const float RF_DEPTHHACK = 4; /* Hacks the depth values such that the entity uses depth values as if it were closer to the screen. This is useful when combined with viewmodels to avoid weapons poking in to walls. */ +const float RF_ADDITIVE = 8; /* Shaders from this entity will temporarily be hacked to use an additive blend mode instead of their normal blend mode. */ +const float RF_USEAXIS = 16; /* The entity will be oriented according to the current v_forward+v_right+v_up vector values instead of the entity's .angles field. */ +const float RF_NOSHADOW = 32; /* This entity will not cast shadows. Often useful on view models. */ +const float RF_FRAMETIMESARESTARTTIMES = 64; /* Specifies that the frame1time, frame2time field are timestamps (denoting the start of the animation) rather than time into the animation. */ +const float IE_KEYDOWN = 0; /* Specifies that a key was pressed. Second argument is the scan code. Third argument is the unicode (printable) char value. Fourth argument denotes which keyboard(or mouse, if its a mouse 'scan' key) the event came from. Note that some systems may completely separate scan codes and unicode values, with a 0 value for the unspecified argument. */ +const float IE_KEYUP = 1; /* Specifies that a key was released. Arguments are the same as IE_KEYDOWN. On some systems, this may be fired instantly after IE_KEYDOWN was fired. */ +const float IE_MOUSEDELTA = 2; /* Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use _windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event. */ +const float IE_MOUSEABS = 3; /* Specifies that a mouse cursor or touch event was moved to a specific location relative to the virtual screen space. Second argument is the new X position, third argument is the new Y position. Fourth argument is which mouse or touch event triggered the event. */ +const float IE_ACCELEROMETER = 4; +const float IE_FOCUS = 5; /* Specifies that input focus was given. parama says mouse focus, paramb says keyboard focus. If either are -1, then it is unchanged. */ +const float IE_JOYAXIS = 6; /* Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller. */ +const float IE_GYROSCOPE = 7; +const float FILE_READ = 0; /* The file may be read via fgets to read a single line at a time. */ +const float FILE_APPEND = 1; /* Like FILE_WRITE, but writing starts at the end of the file. */ +const float FILE_WRITE = 2; /* fputs will be used to write to the file. */ +const float FILE_READNL = 4; /* Like FILE_READ, except newlines are not special. fgets reads the entire file into a tempstring. */ +const float FILE_MMAP_READ = 5; /* The file will be loaded into memory. fgets returns a pointer to the first byte (and will always return the same value for this file). Cast this to your datatype. */ +const float FILE_MMAP_RW = 6; /* Like FILE_MMAP_READ, except any changes to the data will be written back to disk once the file is closed. */ +const float MASK_ENGINE = 1; /* Valid as an argument for addentities. If specified, all non-csqc entities will be added to the scene. */ +const float MASK_VIEWMODEL = 2; /* Valid as an argument for addentities. If specified, the regular engine viewmodel will be added to the scene. */ +const float PREDRAW_AUTOADD = 0; /* Valid as a return value from the predraw function. Returning this will cause the engine to automatically invoke addentity(self) for you. */ +const float PREDRAW_NEXT = 1; /* Valid as a return value from the predraw function. Returning this will simply move on to the next entity without the autoadd behaviour, so can be used for particle/invisible/special entites, or entities that were explicitly drawn with addentity. */ +const float LFIELD_ORIGIN = 0; +const float LFIELD_COLOUR = 1; +const float LFIELD_RADIUS = 2; +const float LFIELD_FLAGS = 3; +const float LFIELD_STYLE = 4; +const float LFIELD_ANGLES = 5; +const float LFIELD_FOV = 6; +const float LFIELD_CORONA = 7; +const float LFIELD_CORONASCALE = 8; +const float LFIELD_CUBEMAPNAME = 9; +const float LFIELD_AMBIENTSCALE = 10; +const float LFIELD_DIFFUSESCALE = 11; +const float LFIELD_SPECULARSCALE = 12; +const float LFIELD_ROTATION = 13; +const float LFIELD_DIETIME = 14; +const float LFIELD_RGBDECAY = 15; +const float LFIELD_RADIUSDECAY = 16; +const float LFLAG_NORMALMODE = 1; +const float LFLAG_REALTIMEMODE = 2; +const float LFLAG_LIGHTMAP = 4; +const float LFLAG_FLASHBLEND = 8; +const float LFLAG_NOSHADOWS = 256; +const float LFLAG_SHADOWMAP = 512; +const float LFLAG_CREPUSCULAR = 1024; +const float TEREDIT_RELOAD = 0; +const float TEREDIT_SAVE = 1; +const float TEREDIT_SETHOLE = 2; +const float TEREDIT_HEIGHT_SET = 3; +const float TEREDIT_HEIGHT_SMOOTH = 4; +const float TEREDIT_HEIGHT_SPREAD = 5; +const float TEREDIT_HEIGHT_RAISE = 6; +const float TEREDIT_HEIGHT_FLATTEN = 18; +const float TEREDIT_HEIGHT_LOWER = 7; +const float TEREDIT_TEX_KILL = 8; +const float TEREDIT_TEX_GET = 9; +const float TEREDIT_TEX_BLEND = 10; +const float TEREDIT_TEX_UNIFY = 11; +const float TEREDIT_TEX_NOISE = 12; +const float TEREDIT_TEX_BLUR = 13; +const float TEREDIT_TEX_REPLACE = 19; +const float TEREDIT_TEX_SETMASK = 25; +const float TEREDIT_WATER_SET = 14; +const float TEREDIT_MESH_ADD = 15; +const float TEREDIT_MESH_KILL = 16; +const float TEREDIT_TINT = 17; +const float TEREDIT_RESET_SECT = 20; +const float TEREDIT_RELOAD_SECT = 21; +const float TEREDIT_ENT_GET = 26; +const float TEREDIT_ENT_SET = 27; +const float TEREDIT_ENT_ADD = 28; +const float TEREDIT_ENT_COUNT = 29; +const float SLIST_HOSTCACHEVIEWCOUNT = 0; +const float SLIST_HOSTCACHETOTALCOUNT = 1; +const float SLIST_MASTERQUERYCOUNT = 2; +const float SLIST_MASTERREPLYCOUNT = 3; +const float SLIST_SERVERQUERYCOUNT = 4; +const float SLIST_SERVERREPLYCOUNT = 5; +const float SLIST_SORTFIELD = 6; +const float SLIST_SORTDESCENDING = 7; +const float SLIST_TEST_CONTAINS = 0; +const float SLIST_TEST_NOTCONTAIN = 1; +const float SLIST_TEST_LESSEQUAL = 2; +const float SLIST_TEST_LESS = 3; +const float SLIST_TEST_EQUAL = 4; +const float SLIST_TEST_GREATER = 5; +const float SLIST_TEST_GREATEREQUAL = 6; +const float SLIST_TEST_NOTEQUAL = 7; +const float SLIST_TEST_STARTSWITH = 8; +const float SLIST_TEST_NOTSTARTSWITH = 9; +void(vector vang) makevectors = #1; /* + Takes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors. */ + +void(entity e, vector o) setorigin = #2; /* + Changes e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid */ + +void(entity e, string m) setmodel = #3; /* + Looks up m in the model precache list, and sets both e.model and e.modelindex to match. BSP models will set e.mins and e.maxs accordingly, other models depend upon the value of sv_gameplayfix_setmodelrealbox - for compatibility you should always call setsize after all pickups or non-bsp models. Also relinks collision state. */ + +void(entity e, vector min, vector max) setsize = #4; /* + Sets the e's mins and maxs fields. Also relinks collision state, which sets absmin and absmax too. */ + +float() random = #7; /* + Returns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays. */ + +void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8; /* + Starts a sound centered upon the given entity. + chan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample + 'samp' must have been precached first + if specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed. + If flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants + timeofs should be negative in order to provide a delay before the sound actually starts. */ + +vector(vector v) normalize = #9; /* + Shorten or lengthen a direction vector such that it is only one quake unit long. */ + +void(string e) error = #10; /* + Ends the game with an easily readable error message. */ + +void(string e) objerror = #11; /* + Displays a non-fatal easily readable error message concerning the self entity, including a field dump. self will be removed! */ + +float(vector v) vlen = #12; /* + Returns the square root of the dotproduct of a vector with itself. Or in other words the length of a distance vector, in quake units. */ + +float(vector v, optional entity reference) vectoyaw = #13; /* + Given a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction. */ + +entity() spawn = #14; /* + Adds a brand new entity into the world! Hurrah, you're now a parent! */ + +void(entity e) remove = #15; /* + Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */ + +void(vector v1, vector v2, float flags, entity ent) traceline = #16; /* + Traces a thin line through the world from v1 towards v2. + Will not collide with ent, ent.owner, or any entity who's owner field refers to ent. + The passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace. + There are no side effects beyond the trace_* globals being written. + flags&MOVE_NOMONSTERS will not impact on non-bsp entities. + flags&MOVE_MISSILE will impact with increased size. + flags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes. + flags&MOVE_TRIGGERS will also stop on triggers + flags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities. + flags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation. */ + +entity(entity start, .string fld, string match) find = #18; /* + Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more. + If you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */ + +string(string s) precache_sound = #19; /* + Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard. */ + +string(string s) precache_model = #20; /* + Precaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name. + Modelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server. */ + +entity(vector org, float rad, optional .entity chainfield) findradius = #22; /* + Finds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. */ + +void(string s, ...) dprint = #25; /* + NQ: Prints the given message on the server's console, but only if the developer cvar is set. Arguments will be concatenated into a single message. */ + +void(string s, ...) dprint = #25; /* + QW: Unconditionally prints the given message on the server's console. Arguments will be concatenated into a single message. */ + +string(float val) ftos = #26; /* + Returns a tempstring containing a representation of the given float. Precision depends upon engine. */ + +string(vector val) vtos = #27; /* + Returns a tempstring containing a representation of the given vector. Precision depends upon engine. */ + +void() coredump = #28; /* + Writes out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging. */ + +void() traceon = #29; /* + Enables tracing. This may be spammy, slow, and stuff. Set debugger 1 in order to use fte's qc debugger. */ + +void() traceoff = #30; /* + Disables tracing again. */ + +void(entity e) eprint = #31; /* + Debugging builtin that prints all fields of the given entity to the console. */ + +float(float yaw, float dist, optional float settraceglobals) walkmove = #32; /* + Attempt to walk the entity at a given angle for a given distance. + if settraceglobals is set, the trace_* globals will be set, showing the results of the movement. + This function will trigger touch events. */ + +float() droptofloor = #34; /* + Instantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE. */ + +void(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35; /* + Specifies an auto-animating string that specifies the light intensity for entities using that lightstyle. + a is off, z is fully lit. Should be lower case only. + rgb will recolour all lights using that lightstyle. */ + +float(float) rint = #36; /* + Rounds the given float up or down to the closest integeral value. X.5 rounds away from 0 */ + +float(float) floor = #37; /* + Rounds the given float downwards, even when negative. */ + +float(float) ceil = #38; /* + Rounds the given float upwards, even when negative. */ + +float(entity ent) checkbottom = #40; /* + Expensive checks to ensure that the entity is actually sitting on something solid, returns true if it is. */ + +float(vector pos) pointcontents = #41; /* + Checks the given point to see what is there. Returns one of the SOLID_* constants. Just because a spot is empty does not mean that the player can stand there due to the size of the player - use tracebox for such tests. */ + +float(float) fabs = #43; /* + Removes the sign of the float, making it positive if it is negative. */ + +float(string) cvar = #45; /* + Returns the numeric value of the named cvar */ + +void(string, ...) localcmd = #46; /* + Adds the string to the console command queue. Commands will not be executed immediately, but rather at the start of the following frame. */ + +entity(entity) nextent = #47; /* + Returns the following entity. Skips over removed entities. Returns world when passed the last valid entity. */ + +void(vector pos, vector dir, float colour, float count) particle = #48; /* + Spawn 'count' particles around 'pos' moving in the direction 'dir', with a palette colour index between 'colour' and 'colour+8'. */ + +#define ChangeYaw changeyaw +void() changeyaw = #49; /* + Changes the self.angles_y field towards self.ideal_yaw by up to self.yaw_speed. */ + +vector(vector fwd, optional vector up) vectoangles = #51; /* + Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning. */ + +float(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW + Forgive me father, for I have trigonometry homework. */ + +float(float angle) cos = #61; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float value) sqrt = #62; /* Part of DP_QC_SINCOSSQRTPOW*/ +void(entity ent) changepitch = #63; /* Part of DP_QC_CHANGEPITCH*/ +void(entity ent, entity ignore) tracetoss = #64; +string(entity ent) etos = #65; /* Part of DP_QC_ETOS*/ +void(float step) movetogoal = #67; /* + Runs lots and lots of fancy logic in order to try to step the entity the specified distance towards its goalentity. */ + +string(string s) precache_file = #68; /* + This builtin does nothing. It was used only as a hint for pak generation. */ + +void(entity e) makestatic = #69; /* + Sends a copy of the entity's renderable fields to all clients, and REMOVES the entity, preventing further changes. This means it will be unmutable and non-solid. */ + +void(string cvarname, string valuetoset) cvar_set = #72; /* + Instantly sets a cvar to the given string value. */ + +void (vector pos, string samp, float vol, float atten) ambientsound = #74; +string(string str) precache_model2 = #75; +string(string str) precache_sound2 = #76; +string(string str) precache_file2 = #77; +string(entity e, string key) infokey = #80; /* Part of FTE_QC_INFOKEY, QW_ENGINE + If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */ + +float(string) stof = #81; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ +string(float style, optional __out vector rgb) getlightstyle = #0:getlightstyle; /* + Obtains the light style string for the given style. */ + +vector(float style) getlightstylergb = #0:getlightstylergb; /* + Obtains the current rgb value of the specified light style. In csqc, this is correct with regard to the current frame, while ssqc gives no guarentees about time and ignores client cvars. Note: use getlight if you want the actual light value at a point. */ + +void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent) tracebox = #90; /* Part of DP_QC_TRACEBOX + Exactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values. */ + +vector() randomvec = #91; /* Part of DP_QC_RANDOMVEC + Returns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive. */ + +vector(vector org) getlight = #92; +float(string cvarname, string defaultvalue) registercvar = #93; /* Part of DP_REGISTERCVAR + Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op. + This builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop. + In engines that support it, you will generally find the autocvar feature easier and more efficient to use. */ + +float(float a, float b, ...) min = #94; /* Part of DP_QC_MINMAXBOUND + Returns the lowest value of its arguments. */ + +float(float a, float b, ...) max = #95; /* Part of DP_QC_MINMAXBOUND + Returns the highest value of its arguments. */ + +float(float minimum, float val, float maximum) bound = #96; /* Part of DP_QC_MINMAXBOUND + Returns val, unless minimum is higher, or maximum is less. */ + +float(float value, float exp) pow = #97; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float v, optional float base) logarithm = #0:logarithm; /* + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + +#define findentity findfloat +entity(entity start, .__variant fld, __variant match) findfloat = #98; /* Part of DP_QC_FINDFLOAT + Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. + world is returned when there are no more entities. */ + +float(string extname) checkextension = #99; /* + Checks for an extension by its name (eg: checkextension("FRIK_FILE") says that its okay to go ahead and use strcat). + Use cvar("pr_checkextension") to see if this builtin exists. */ + +float(__variant funcref) checkbuiltin = #0:checkbuiltin; /* + Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available). */ + +float(float value) anglemod = #102; +filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE + Opens a file, typically prefixed with "data/", for either read or write access. */ + +void(filestream fhandle) fclose = #111; /* Part of FRIK_FILE*/ +string(filestream fhandle) fgets = #112; /* Part of FRIK_FILE + Reads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use if not(string) to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank */ + +void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE + Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */ + +int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* Part of FTE_QC_FILE_BINARY + Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */ + +int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* Part of FTE_QC_FILE_BINARY + Writes binary data out of the file. */ + +#define ftell fseek //c compat +int(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* Part of FTE_QC_FILE_BINARY + Changes the current position of the file, if specified. Returns prior position, in bytes. */ + +int(filestream fhandle, optional int newsize) fsize = #0:fsize; /* Part of FTE_QC_FILE_BINARY + Reports the total size of the file, in bytes. Can also be used to truncate/extend the file */ + +float(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +vector(string s) stov = #117; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS + Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods. */ + +void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS + Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */ + +void(string soundname, optional float channel, optional float volume) localsound = #177; /* + Plays a sound... locally... probably best not to call this from ssqc. Also disables reverb. */ + +float(string modelname, optional float queryonly) getmodelindex = #200; /* + Acts as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex; + If queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */ + +__variant(float prnum, string funcname, ...) externcall = #201; /* Part of FTE_MULTIPROGS + Directly call a function in a different/same progs by its name. + prnum=0 is the 'default' or 'main' progs. + prnum=-1 means current progs. + prnum=-2 will scan through the active progs and will use the first it finds. */ + +float(string progsname) addprogs = #202; /* Part of FTE_MULTIPROGS + Loads an additional .dat file into the current qcvm. The returned handle can be used with any of the externcall/externset/externvalue builtins. + There are cvars that allow progs to be loaded automatically. */ + +__variant(float prnum, string varname) externvalue = #203; /* Part of FTE_MULTIPROGS + Reads a global in the named progs by the name of that global. + prnum=0 is the 'default' or 'main' progs. + prnum=-1 means current progs. + prnum=-2 will scan through the active progs and will use the first it finds. */ + +void(float prnum, __variant newval, string varname) externset = #204; /* Part of FTE_MULTIPROGS + Sets a global in the named progs by name. + prnum=0 is the 'default' or 'main' progs. + prnum=-1 means current progs. + prnum=-2 will scan through the active progs and will use the first it finds. */ + +void(entity portal, float state) openportal = #207; /* + Opens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call. */ + +void(optional __variant ret) abort = #211; /* Part of FTE_MULTITHREADED + QC execution is aborted. Parent QC functions on the stack will be skipped, effectively this forces all QC functions to 'return ret' until execution returns to the engine. If ret is ommited, it is assumed to be 0. */ + +void(vector org, vector dmin, vector dmax, float colour, float effect, float count) particle2 = #215; /* Part of FTE_HEXEN2*/ +void(vector org, vector box, float colour, float effect, float count) particle3 = #216; /* Part of FTE_HEXEN2*/ +void(vector org, float radius, float colour, float effect, float count) particle4 = #217; /* Part of FTE_HEXEN2*/ +float(float number, float quantity) bitshift = #218; /* Part of EXT_BITSHIFT*/ +void(vector pos) te_lightningblood = #219; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/ +float(string s1, string sub, optional float startidx) strstrofs = #221; /* Part of FTE_STRINGS + Returns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1. + If startidx is set, this builtin will ignore matches before that 0-based offset. */ + +float(string str, float index) str2chr = #222; /* Part of FTE_STRINGS + Retrieves the character value at offset 'index'. */ + +string(float chr, ...) chr2str = #223; /* Part of FTE_STRINGS + The input floats are considered character values, and are concatenated. */ + +string(float ccase, float redalpha, float redchars, string str, ...) strconv = #224; /* Part of FTE_STRINGS + Converts quake chars in the input string amongst different representations. + ccase specifies the new case for letters. + 0: not changed. + 1: forced to lower case. + 2: forced to upper case. + redalpha and redchars switch between colour ranges. + 0: no change. + 1: Forced white. + 2: Forced red. + 3: Forced gold(low) (numbers only). + 4: Forced gold (high) (numbers only). + 5+6: Forced to white and red alternately. + You should not use this builtin in combination with UTF-8. */ + +string(float pad, string str1, ...) strpad = #225; /* Part of FTE_STRINGS + Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right. */ + +string(infostring old, string key, string value) infoadd = #226; /* Part of FTE_STRINGS + Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \ character. */ + +string(infostring info, string key) infoget = #227; /* Part of FTE_STRINGS + Reads a named value from an infostring. The returned value is a tempstring */ + +#define strcmp strncmp +float(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs) strncmp = #228; /* Part of FTE_STRINGS + Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0. + Returns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher. */ + +float(string s1, string s2) strcasecmp = #229; /* Part of FTE_STRINGS + Compares the two strings without case sensitivity. + Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */ + +float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs) strncasecmp = #230; /* Part of FTE_STRINGS + Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0. + Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */ + +string(string s) strtrim = #0:strtrim; /* + Trims the whitespace from the start+end of the string. */ + +void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY + Asks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv. + timeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue) + The strftime builtin is more versatile and less weird. */ + +void(vector angle) rotatevectorsbyangle = #235; /* + rotates the v_forward,v_right,v_up matrix by the specified angles. */ + +void(vector fwd, vector right, vector up) rotatevectorsbyvectors = #236; +float(float mdlindex, string skinname) skinforname = #237; +float(string shadername, optional string defaultshader, ...) shaderforname = #238; /* Part of FTE_FORCESHADER + Caches the named shader and returns a handle to it. + If the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used. + defaultshader if not empty should include the outer {} that you would ordinarily find in a shader. */ + +void(vector org, optional float count) te_bloodqw = #239; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/ +float(vector viewpos, entity entity) checkpvs = #240; /* Part of FTE_QC_CHECKPVS*/ +vector(entity ent, float tagnum) rotatevectorsbytag = #244; +float(float dividend, float divisor) mod = #245; +int(string) stoi = #259; /* Part of FTE_QC_INTCONV + Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string. */ + +string(int) itos = #260; /* Part of FTE_QC_INTCONV + Converts the passed true integer into a base10 string. */ + +int(string) stoh = #261; /* Part of FTE_QC_INTCONV + Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P */ + +string(int) htos = #262; /* Part of FTE_QC_INTCONV + Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */ + +int(float) ftoi = #0:ftoi; /* Part of FTE_QC_INTCONV + Converts the given float into a true integer without depending on extended qcvm instructions. */ + +float(int) itof = #0:itof; /* Part of FTE_QC_INTCONV + Converts the given true integer into a float without depending on extended qcvm instructions. */ + +float(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS + Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model. + eg: self.skeletonobject = skel_create(self.modelindex); */ + +float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac) skel_build = #264; /* Part of FTE_CSQC_SKELETONOBJECTS + Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object. + If retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based. */ + +float(float skel) skel_get_numbones = #265; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrives the number of bones in the model. The valid range is 1<=bone<=numbones. */ + +string(float skel, float bonenum) skel_get_bonename = #266; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrieves the name of the specified bone. Mostly only for debugging. */ + +float(float skel, float bonenum) skel_get_boneparent = #267; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrieves which bone this bone's position is relative to. Bone 0 refers to the entity's position rather than an actual bone */ + +float(float skel, string tagname) skel_find_bone = #268; /* Part of FTE_CSQC_SKELETONOBJECTS + Finds a bone by its name, from the model that was used to create the skeletal object. */ + +vector(float skel, float bonenum) skel_get_bonerel = #269; /* Part of FTE_CSQC_SKELETONOBJECTS + Gets the bone position and orientation relative to the bone's parent. Return value is the offset, and v_forward, v_right, v_up contain the orientation. */ + +vector(float skel, float bonenum) skel_get_boneabs = #270; /* Part of FTE_CSQC_SKELETONOBJECTS + Gets the bone position and orientation relative to the entity. Return value is the offset, and v_forward, v_right, v_up contain the orientation. + Use gettaginfo for world coord+orientation. */ + +void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_set_bone = #271; /* Part of FTE_CSQC_SKELETONOBJECTS + Sets a bone position relative to its parent. If the orientation arguments are not specified, v_forward+v_right+v_up are used instead. */ + +void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bone = #272; /* Part of FTE_CSQC_SKELETONOBJECTS + Transforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */ + +void(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bones = #273; /* Part of FTE_CSQC_SKELETONOBJECTS + Transforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones. */ + +void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; /* Part of FTE_CSQC_SKELETONOBJECTS + Copy bone data from one skeleton directly into another. */ + +void(float skel) skel_delete = #275; /* Part of FTE_CSQC_SKELETONOBJECTS + Deletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object. */ + +float(float modidx, string framename) frameforname = #276; /* Part of FTE_CSQC_SKELETONOBJECTS + Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error. */ + +float(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrieves the duration (in seconds) of the specified framegroup. */ + +void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback) processmodelevents = #0:processmodelevents; /* + Calls a callback for each event that has been reached. Basetime is set to targettime. */ + +float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data) getnextmodelevent = #0:getnextmodelevent; /* + Reports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime). + WARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported). */ + +float(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data) getmodeleventidx = #0:getmodeleventidx; /* + Reports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp. */ + +#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2)) +vector(vector v1, vector v2) crossproduct = #0:crossproduct; /* Part of FTE_QC_CROSSPRODUCT + Small helper function to calculate the crossproduct of two vectors. */ + +float(entity pusher, vector move, vector amove) pushmove = #0:pushmove; +void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Part of FTE_TERRAIN_MAP + Realtime terrain editing. Actions are the TEREDIT_ constants. */ + +typedef struct +{ + string shadername; + vector planenormal; + float planedist; + vector sdir; + float sbias; + vector tdir; + float tbias; +} brushface_t; +int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Part of FTE_RAW_MAP + Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */ + +int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Part of FTE_RAW_MAP + Inserts a new brush into the model. Return value is the new brush's id. */ + +void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Part of FTE_RAW_MAP + Destroys the specified brush. */ + +float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Part of FTE_RAW_MAP + Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */ + +int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Part of FTE_RAW_MAP + Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */ + +int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Part of FTE_RAW_MAP + Determines the points of the specified face, if the specified brush were to actually be created. */ + +int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Part of FTE_RAW_MAP + Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */ + +void(optional entity ent, optional vector neworigin) touchtriggers = #279; /* + Triggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity. */ + +float(entity skelent, string dollcmd, float animskel) skel_ragupdate = #281; /* + Updates the skeletal object attached to the entity according to its origin and other properties. + if animskel is non-zero, the ragdoll will animate towards the bone state in the animskel skeletal object, otherwise they will pick up the model's base pose which may not give nice results. + If dollcmd is not set, the ragdoll will update (this should be done each frame). + If the doll is updated without having a valid doll, the model's default .doll will be instanciated. + commands: + doll foo.doll : sets up the entity to use the named doll file + dollstring TEXT : uses the doll file directly embedded within qc, with that extra prefix. + cleardoll : uninstanciates the doll without destroying the skeletal object. + animate 0.5 : specifies the strength of the ragdoll as a whole + animatebody somebody 0.5 : specifies the strength of the ragdoll on a specific body (0 will disable ragdoll animations on that body). + enablejoint somejoint 1 : enables (or disables) a joint. Disabling joints will allow the doll to shatter. */ + +float*(float skel) skel_mmap = #282; /* + Map the bones in VM memory. They can then be accessed via pointers. Each bone is 12 floats, the four vectors interleaved (sadly). */ + +void(entity ent, float bonenum, vector org, optional vector angorfwd, optional vector right, optional vector up) skel_set_bone_world = #283; /* + Sets the world position of a bone within the given entity's attached skeletal object. The world position is dependant upon the owning entity's position. If no orientation argument is specified, v_forward+v_right+v_up are used for the orientation instead. If 1 is specified, it is understood as angles. If 3 are specified, they are the forawrd/right/up vectors to use. */ + +string(float modidx, float framenum) frametoname = #284; +string(float modidx, float skin) skintoname = #285; +float(float resourcetype, float tryload, string resourcename) resourcestatus = #286; /* + resourcetype must be one of the RESTYPE_ constants. Returns one of the RESSTATE_ constants. Tryload 0 is a query only. Tryload 1 will attempt to reload the content if it was flushed. */ + +hashtable(float tabsize, optional float defaulttype) hash_createtab = #287; /* Part of FTE_QC_HASHTABLES + Creates a hash table object with at least 'tabsize' slots. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return). */ + +void(hashtable table) hash_destroytab = #288; /* Part of FTE_QC_HASHTABLES + Destroys a hash table object. */ + +void(hashtable table, string name, __variant value, optional float typeandflags) hash_add = #289; /* Part of FTE_QC_HASHTABLES + Adds the given key with the given value to the table. + If flags&HASH_REPLACE, the old value will be removed, if not set then multiple values may be added for a single key, they won't overwrite. + The type argument describes how the value should be stored and saved to files. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games. */ + +__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index) hash_get = #290; /* Part of FTE_QC_HASHTABLES + looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was. If requiretype is specified, then values not of the specified type will be ignored. Hurrah for multiple types with the same name. */ + +__variant(hashtable table, string name) hash_delete = #291; /* Part of FTE_QC_HASHTABLES + removes the named key. returns the value of the object that was destroyed, or 0 on error. */ + +string(hashtable table, float idx) hash_getkey = #292; /* Part of FTE_QC_HASHTABLES + gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all. */ + +float(string name) checkcommand = #294; /* Part of FTE_QC_CHECKCOMMAND + Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist. */ + +string(string s) argescape = #295; /* + Marks up a string so that it can be reliably tokenized as a single argument later. */ + +float(float mdlidx) modelframecount = #0:modelframecount; /* + Retrieves the number of frames in the specified model. */ + +void() clearscene = #300; /* + Forgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values. */ + +void(float mask) addentities = #301; /* + Walks through all entities effectively doing this: + if (ent.drawmask&mask){ if (!ent.predaw()) addentity(ent); } + If mask&MASK_DELTA, non-csqc entities, particles, and related effects will also be added to the rentity list. + If mask&MASK_STDVIEWMODEL then the default view model will also be added. */ + +void(entity ent) addentity = #302; /* + Copies the entity fields into a new rentity for later rendering via addscene. */ + +void(entity ent) removeentity = #0:removeentity; /* + Undoes all addentities added to the scene from the given entity, without removing ALL entities (useful for splitscreen/etc, readd modified versions as desired). */ + +typedef float vec2[2]; +typedef float vec3[3]; +typedef float vec4[4]; +typedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t; +void(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes) addtrisoup_simple = #0:addtrisoup_simple; /* + Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame. */ + +#define setviewprop setproperty +float(float property, ...) setproperty = #303; /* + Allows you to override default view properties like viewport, fov, and whether the engine hud will be drawn. Different VF_ values have slightly different arguments, some are vectors, some floats. */ + +void() renderscene = #304; /* + Draws all entities, polygons, and particles on the rentity list (which were added via addentities or addentity), using the various view properties set via setproperty. There is no ordering dependancy. + The scene must generally be cleared again before more entities are added, as entities will persist even over to the next frame. + You may call this builtin multiple times per frame, but should only be called from CSQC_UpdateView. */ + +float(vector org, float radius, vector lightcolours, optional float style, optional string cubemapname, optional float pflags) dynamiclight_add = #305; /* + Adds a temporary dlight, ready to be drawn via addscene. Cubemap orientation will be read from v_forward/v_right/v_up. */ + +void(string texturename, optional float flags, optional float is2d) R_BeginPolygon = #306; /* + Specifies the shader to use for the following polygons, along with optional flags. + If is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects. */ + +void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; /* + Specifies a polygon vertex with its various properties. */ + +void() R_EndPolygon = #308; /* + Ends the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon if you wish to draw another polygon with the same shader. */ + +#define getviewprop getproperty +__variant(float property) getproperty = #309; /* + Retrieve a currently-set (typically view) property, allowing you to read the current viewport or other things. Due to cheat protection, certain values may be unretrievable. */ + +vector (vector v) unproject = #310; /* + Transform a 2d screen-space point (with depth) into a 3d world-space point, according the various origin+angle+fov etc settings set via setproperty. */ + +vector (vector v) project = #311; /* + Transform a 3d world-space point into a 2d screen-space point, according the various origin+angle+fov etc settings set via setproperty. */ + +void(vector pos, vector size, float alignflags, string text) drawtextfield = #0:drawtextfield; /* + Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. */ + +void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional float drawflag) drawline = #315; /* + Draws a 2d line between the two 2d points. */ + +float(string name) iscachedpic = #316; /* + Checks to see if the image is currently loaded. Engines might lie, or cache between maps. */ + +string(string name, optional float trywad) precache_pic = #317; /* + Forces the engine to load the named image. If trywad is specified, the specified name must any lack path and extension. */ + +void(string imagename, int width, int height, int *pixeldata) r_uploadimage = #0:r_uploadimage; /* Part of FTE_CSQC_RAWIMAGES + Updates a texture with the specified rgba data. Will be created if needed. */ + +int*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* Part of FTE_CSQC_RAWIMAGES + Reads and decodes an image from disk, providing raw pixel data. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */ + +#define draw_getimagesize drawgetimagesize +vector(string picname) drawgetimagesize = #318; /* + Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. */ + +void(string name) freepic = #319; /* + Tells the engine that the image is no longer needed. The image will appear to be new the next time its needed. */ + +float(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag) drawcharacter = #320; /* + Draw the given quake character at the given position. + If flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range). + size should normally be something like '8 8 0'. + rgb should normally be '1 1 1' + alpha normally 1. + Software engines may assume the named defaults. + Note that ALL text may be rescaled on the X axis due to variable width fonts. The X axis may even be ignored completely. */ + +float(vector position, string text, vector size, vector rgb, float alpha, optional float drawflag) drawrawstring = #321; /* + Draws the specified string without using any markup at all, even in engines that support it. + If UTF-8 is globally enabled in the engine, then that encoding is used (without additional markup), otherwise it is raw quake chars. + Software engines may assume a size of '8 8 0', rgb='1 1 1', alpha=1, flag&3=0, but it is not an error to draw out of the screen. */ + +float(vector position, string pic, vector size, vector rgb, float alpha, optional float drawflag) drawpic = #322; /* + Draws an shader within the given 2d screen box. Software engines may omit support for rgb+alpha, but must support rescaling, and must clip to the screen without crashing. */ + +float(vector position, vector size, vector rgb, float alpha, optional float drawflag) drawfill = #323; /* + Draws a solid block over the given 2d box, with given colour, alpha, and blend mode (specified via flags). + flags&3=0 simple blend. + flags&3=1 additive blend */ + +void(float x, float y, float width, float height) drawsetcliparea = #324; /* + Specifies a 2d clipping region (aka: scissor test). 2d draw calls will all be clipped to this 2d box, the area outside will not be modified by any 2d draw call (even 2d polygons). */ + +void(void) drawresetcliparea = #325; /* + Reverts the scissor/clip area to the whole screen. */ + +float(vector position, string text, vector size, vector rgb, float alpha, float drawflag) drawstring = #326; /* + Draws a string, interpreting markup and recolouring as appropriate. */ + +float(string text, float usecolours, optional vector fontsize) stringwidth = #327; /* + Calculates the width of the screen in virtual pixels. If usecolours is 1, markup that does not affect the string width will be ignored. Will always be decoded as UTF-8 if UTF-8 is globally enabled. + If the char size is not specified, '8 8 0' will be assumed. */ + +void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, optional float drawflag) drawsubpic = #328; /* + Draws a rescaled subsection of an image to the screen. */ + +void(vector pivot, vector mins, vector maxs, string pic, vector rgb, float alpha, float angle) drawrotpic = #0:drawrotpic; /* + Draws an image rotating at the pivot. To rotate in the center, use mins+maxs of half the size with mins negated. Angle is in degrees. */ + +void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles) drawrotsubpic = #0:drawrotsubpic; /* + Overcomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature. */ + +#define getstati_punf(stnum) (float)(__variant)getstati(stnum) +int(float stnum) getstati = #330; /* + Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat. Use getstati_punf if you wish to type-pun a float stat as an int to avoid truncation issues in DP. */ + +#define getstatbits getstatf +float(float stnum, optional float firstbit, optional float bitcount) getstatf = #331; /* + Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat (converted into a float, so there are no VM dependancies). */ + +string(float stnum) getstats = #332; /* + Retrieves the value of the given EV_STRING stat, as a tempstring. + Older engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but FTE Quake uses a separate namespace for string stats and has a much higher length limit. */ + +__variant(float playernum, float statnum, float stattype) getplayerstat = #0:getplayerstat; /* + Retrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits. */ + +void(entity e, float mdlindex) setmodelindex = #333; /* + Sets a model by precache index instead of by name. Otherwise identical to setmodel. */ + +string(float mdlindex) modelnameforindex = #334; /* + Retrieves the name of the model based upon a precache index. This can be used to reduce csqc network traffic by enabling model matching. */ + +float(string effectname) particleeffectnum = #335; /* Part of DP_ENT_TRAILEFFECTNUM, FTE_SV_POINTPARTICLES + Precaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined. + Different engines will have different particle systems, this specifies the QC API only. */ + +void(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /* Part of FTE_SV_POINTPARTICLES + Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */ + +void(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* Part of FTE_SV_POINTPARTICLES + Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument. + For regular particles, the dir vector is multiplied by the 'veladd' property (while orgadd will push the particles along it). Decals will use it as a hint to align to the correct surface. In both cases, it should normally be a unit vector, but other lengths will still work. If it has length 0 then FTE will assume downwards. */ + +void(string s, ...) cprint = #338; /* + Print into the center of the screen just as ssqc's centerprint would appear. */ + +void(string s, ...) print = #339; /* Part of DP_SV_PRINT + Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar). */ + +string(float keynum) keynumtostring = #340; /* + Returns a hunam-readable name for the given keycode, as a tempstring. */ + +float(string keyname) stringtokeynum = #341; /* + Looks up the key name in the same way that the bind command would, returning the keycode for that key. */ + +string(float keynum) getkeybind = #342; /* + Returns the current binding for the given key (returning only the command executed when no modifiers are pressed). */ + +void(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale) setcursormode = #343; /* + Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port. */ + +float(float effective) getcursormode = #0:getcursormode; /* + Reports the cursor mode this module previously attempted to use. If 'effective' is true, reports the cursor mode currently active (if was overriden by a different module which has precidence, for instance, or if there is only a touchscreen and no mouse). */ + +vector() getmousepos = #344; /* + Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent for such things in csqc mods. */ + +float(float inputsequencenum) getinputstate = #345; /* + Looks up an input frame from the log, setting the input_* globals accordingly. + The sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe. + The sequence equal to clientcommandframe will change between input frames. */ + +void(float sens) setsensitivityscaler = #346; /* + Temporarily scales the player's mouse sensitivity based upon something like zoom, avoiding potential cvar saving and thus corruption. */ + +void(entity ent) runstandardplayerphysics = #347; /* + Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement. */ + +string(float playernum, string keyname) getplayerkeyvalue = #348; /* + Look up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver. + Also includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness. */ + +float(float playernum, string keyname, optional float assumevalue) getplayerkeyfloat = #0:getplayerkeyfloat; /* + Cheaper version of getplayerkeyvalue that avoids the need for so many tempstrings. */ + +float() isdemo = #349; /* + Returns if the client is currently playing a demo or not */ + +float() isserver = #350; /* + Returns if the client is acting as the server (aka: listen server) */ + +void(vector origin, vector forward, vector right, vector up, optional float reverbtype) SetListener = #351; /* + Sets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. For reverbtype, see setup_reverb or treat as 'underwater'. */ + +typedef struct { + float flDensity; + float flDiffusion; + float flGain; + float flGainHF; + float flGainLF; + float flDecayTime; + float flDecayHFRatio; + float flDecayLFRatio; + float flReflectionsGain; + float flReflectionsDelay; + vector flReflectionsPan; + float flLateReverbGain; + float flLateReverbDelay; + vector flLateReverbPan; + float flEchoTime; + float flEchoDepth; + float flModulationTime; + float flModulationDepth; + float flAirAbsorptionGainHF; + float flHFReference; + float flLFReference; + float flRoomRolloffFactor; + int iDecayHFLimit; +} reverbinfo_t; +void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* Part of FTE_CSQC_REVERB + Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL. */ + +void(string cmdname) registercommand = #352; /* + Register the given console command, for easy console use. + Console commands that are later used will invoke CSQC_ConsoleCommand. */ + +float(entity ent) wasfreed = #353; /* + Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust. */ + +string(string key) serverkey = #354; /* + Look up a key in the server's public serverinfo string */ + +float(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat; /* + Version of serverkey that returns the value as a float (which avoids tempstrings). */ + +string(optional string resetstring) getentitytoken = #355; /* + Grab the next token in the map's entity lump. + If resetstring is not specified, the next token will be returned with no other sideeffects. + If empty, will reset from the map before returning the first token, probably {. + If not empty, will tokenize from that string instead. + Always returns tempstrings. */ + +float(string s) findfont = #356; /* + Looks up a named font slot. Matches the actual font name as a last resort. */ + +float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357; /* + too convoluted for me to even try to explain correct usage. Try drawfont = loadfont("", "cour", "16", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high), if you have the freetype2 library in windows.. */ + +void(string evname, string evargs, ...) sendevent = #359; /* + Invoke Cmd_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors. */ + +float() readbyte = #360; +float() readchar = #361; +float() readshort = #362; +float() readlong = #363; +float() readcoord = #364; +float() readangle = #365; +string() readstring = #366; +float() readfloat = #367; +float() readentitynum = #368; +float(string modelname, float(float isnew) updatecallback, float flags) deltalisten = #371; /* + Specifies a per-modelindex callback to listen for engine-networking entity updates. Such entities are automatically interpolated by the engine (unless flags specifies not to). + The various standard entity fields will be overwritten each frame before the updatecallback function is called. */ + +__variant(float lno, float fld) dynamiclight_get = #372; /* + Retrieves a property from the given dynamic/rt light. Return type depends upon the light field requested. */ + +void(float lno, float fld, __variant value) dynamiclight_set = #373; /* + Changes a property on the given dynamic/rt light. Value type depends upon the light field to be changed. */ + +string(float efnum, float body) particleeffectquery = #374; /* + Retrieves either the name or the body of the effect with the given number. The effect body is regenerated from internal state, and can be changed before being reapplied via the localcmd builtin. */ + +void(string shadername, vector origin, vector up, vector side, vector rgb, float alpha) adddecal = #375; /* + Adds a temporary clipped decal shader to the scene, centered at the given point with given orientation. Will be drawn by the next renderscene call, and freed by the next clearscene call. */ + +void(entity e, string skinfilename, optional string skindata) setcustomskin = #376; /* + Sets an entity's skin overrides. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format: + surfacename,shadername - makes the named surface use the named shader + replace "surfacename" "shadername" - same. + qwskin "foo" - use an unmodified quakeworld player skin (including crop+repalette rules) + q1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red + q1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue + compose "surfacename" "shader" "imagename@x,y:w,h$s,t,s2,t2?r,g,b,a" - compose a skin texture from multiple images. + The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line. + Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). */ + +__variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC + Allocate an arbitary block of memory */ + +void(__variant *ptr) memfree = #385; /* Part of FTE_MEMALLOC + Frees a block of memory that was allocated with memfree */ + +void(__variant *dst, __variant *src, int size) memcpy = #386; /* Part of FTE_MEMALLOC + Copys memory from one location to another */ + +void(__variant *dst, int val, int size) memfill8 = #387; /* Part of FTE_MEMALLOC + Sets an entire block of memory to a specified value. Pretty much always 0. */ + +__variant(__variant *dst, float ofs) memgetval = #388; /* + Looks up the 32bit value stored at a pointer-with-offset. */ + +void(__variant *dst, float ofs, __variant val) memsetval = #389; /* + Changes the 32bit value stored at the specified pointer-with-offset. */ + +__variant*(__variant *base, float ofs) memptradd = #390; /* + Perform some pointer maths. Woo. */ + +float(string s) memstrsize = #0:memstrsize; /* + strlen, except ignores utf-8 */ + +string(string conname, string field, optional string newvalue) con_getset = #391; /* Part of FTE_CSQC_ALTCONSOLES + Reads or sets a property from a console object. The old value is returned. Iterrate through consoles with the 'next' field. Valid properties: title, name, next, unseen, markup, forceutf8, close, clear, hidden, linecount */ + +void(string conname, string messagefmt, ...) con_printf = #392; /* Part of FTE_CSQC_ALTCONSOLES + Prints onto a named console. */ + +void(string conname, vector pos, vector size, float fontsize) con_draw = #393; /* Part of FTE_CSQC_ALTCONSOLES + Draws the named console. */ + +float(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES + Forwards input events to the named console. Mouse updates should be absolute only. */ + +void(string newcaption) setwindowcaption = #0:setwindowcaption; /* Part of FTE_CSQC_WINDOWCAPTION + Replaces the title of the game window, as seen when task switching or just running in windowed mode. */ + +float() cvars_haveunsaved = #0:cvars_haveunsaved; /* + Returns true if any archived cvar has an unsaved value. */ + +float(entity e, float nowreadonly) entityprotection = #0:entityprotection; /* + Changes the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea). */ + +entity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY + Copies all fields from one entity to another. */ + +entity(.string field, string match, optional .entity chainfield) findchain = #402; /* Part of DP_QC_FINDCHAIN*/ +entity(.float fld, float match, optional .entity chainfield) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/ +void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; /* Part of DP_SV_EFFECT + Spawns a self-animating sprite */ + +void(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOOD*/ +void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/ +void(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of DP_TE_PARTICLERAIN*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of DP_TE_PARTICLESNOW*/ +void(vector org, vector vel, float howmany) te_spark = #411; /* Part of DP_TE_SPARK*/ +void(vector org) te_gunshotquad = #412; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_spikequad = #413; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_superspikequad = #414; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_explosionquad = #415; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_smallflash = #416; /* Part of DP_TE_SMALLFLASH*/ +void(vector org, float radius, float lifetime, vector color) te_customflash = #417; /* Part of DP_TE_CUSTOMFLASH*/ +void(vector org, optional float count) te_gunshot = #418; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_spike = #419; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_superspike = #420; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_explosion = #421; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_tarexplosion = #422; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_wizspike = #423; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_knightspike = #424; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_lavasplash = #425; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_teleport = #426; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org, float color, float colorlength) te_explosion2 = #427; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_lightning1 = #428; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_lightning2 = #429; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_lightning3 = #430; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_beam = #431; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/ +void(vector dir) vectorvectors = #432; /* Part of DP_QC_VECTORVECTORS*/ +void(vector org) te_plasmaburn = #433; /* Part of _DP_TE_PLASMABURN*/ +float(entity e, float s) getsurfacenumpoints = #434; /* Part of DP_QC_GETSURFACE*/ +vector(entity e, float s, float n) getsurfacepoint = #435; /* Part of DP_QC_GETSURFACE*/ +vector(entity e, float s) getsurfacenormal = #436; /* Part of DP_QC_GETSURFACE*/ +string(entity e, float s) getsurfacetexture = #437; /* Part of DP_QC_GETSURFACE*/ +float(entity e, vector p) getsurfacenearpoint = #438; /* Part of DP_QC_GETSURFACE*/ +vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; /* Part of DP_QC_GETSURFACE*/ +float(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ +string(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ +void(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/ +searchhandle(string pattern, float caseinsensitive, float quiet) search_begin = #444; /* Part of DP_QC_FS_SEARCH + initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. */ + +void(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH*/ +float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH + Retrieves the number of files that were found. */ + +string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH + Retrieves name of one of the files that was found by the initial search. */ + +float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME + Retrieves the size of one of the files that was found by the initial search. */ + +string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME + Retrieves modification time of one of the files. */ + +string(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/ +entity(entity start, .float fld, float match) findflags = #449; /* Part of DP_QC_FINDFLAGS*/ +entity(.float fld, float match, optional .entity chainfield) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/ +float(entity ent, string tagname) gettagindex = #451; /* Part of DP_MD3_TAGSINFO*/ +vector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_MD3_TAGSINFO + Obtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation. */ + +void(vector org, vector vel, float howmany) te_flamejet = #457; /* Part of _DP_TE_FLAMEJET*/ +entity(float entnum) edict_num = #459; /* Part of DP_QC_EDICT_NUM*/ +strbuf() buf_create = #460; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle) buf_del = #461; /* Part of DP_QC_STRINGBUFFERS*/ +float(strbuf bufhandle) buf_getsize = #462; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle_from, strbuf bufhandle_to) buf_copy = #463; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #464; /* Part of DP_QC_STRINGBUFFERS*/ +string(strbuf bufhandle, string glue) buf_implode = #465; /* Part of DP_QC_STRINGBUFFERS*/ +string(strbuf bufhandle, float string_index) bufstr_get = #466; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle, float string_index, string str) bufstr_set = #467; /* Part of DP_QC_STRINGBUFFERS*/ +float(strbuf bufhandle, string str, float ordered) bufstr_add = #468; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle, float string_index) bufstr_free = #469; /* Part of DP_QC_STRINGBUFFERS*/ +float(float s) asin = #471; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float c) acos = #472; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float t) atan = #473; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float c, float s) atan2 = #474; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float a) tan = #475; /* Part of DP_QC_ASINACOSATANATAN2TAN + Forgive me father, for I have a sunbed and I'm not afraid to use it. */ + +float(string s) strlennocol = #476; /* Part of DP_QC_STRINGCOLORFUNCTIONS + Returns the number of characters in the string after any colour codes or other markup has been parsed. */ + +string(string s) strdecolorize = #477; /* Part of DP_QC_STRINGCOLORFUNCTIONS + Flattens any markup/colours, removing them from the string. */ + +string(float uselocaltime, string format, ...) strftime = #478; /* Part of DP_QC_STRFTIME*/ +float(string s, string separator1, ...) tokenizebyseparator = #479; /* Part of DP_QC_TOKENIZEBYSEPARATOR*/ +string(string s) strtolower = #480; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ +string(string s) strtoupper = #481; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ +string(string s) cvar_defstring = #482; /* Part of DP_QC_CVAR_DEFSTRING*/ +void(vector origin, string sample, float volume, float attenuation) pointsound = #483; /* Part of DP_SV_POINTSOUND*/ +string(string search, string replace, string subject) strreplace = #484; /* Part of DP_QC_STRREPLACE*/ +string(string search, string replace, string subject) strireplace = #485; /* Part of DP_QC_STRREPLACE*/ +vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/ +float(string name, optional string initialURI) gecko_create = #487; /* Part of DP_GECKO_SUPPORT + Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */ + +void(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT + Destroy a shader. */ + +void(string name, string URI) gecko_navigate = #489; /* Part of DP_GECKO_SUPPORT + Sends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus. */ + +float(string name, float key, float eventtype, optional float charcode) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT + Send a key event to a media decoder. This applies only to interactive decoders like browsers. */ + +void(string name, float x, float y) gecko_mousemove = #491; /* Part of DP_GECKO_SUPPORT + Sets a media decoder shader's mouse position. Values should be 0-1. */ + +void(string name, float w, float h) gecko_resize = #492; /* Part of DP_GECKO_SUPPORT + Request to resize a media decoder. */ + +vector(string name) gecko_get_texture_extent = #493; /* Part of DP_GECKO_SUPPORT + Retrieves a media decoder current image pixel sizes. */ + +string(string shadname, string propname) gecko_getproperty = #0:gecko_getproperty; /* + Queries the media decoder (especially browser ones) for decoder-specific properties. The cef plugin recognises url, title, status. */ + +float(string file, string id) cin_open = #0:cin_open; +void(string id) cin_close = #0:cin_close; +void(string id, float newstate) cin_setstate = #0:cin_setstate; +float(string id) cin_getstate = #0:cin_getstate; +void(string file) cin_restart = #0:cin_restart; +float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/ +float(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/ +float() numentityfields = #496; /* Part of DP_QC_ENTITYDATA + Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */ + +float(string fieldname) findentityfield = #0:findentityfield; /* + Find a field index by name. */ + +typedef .__variant field_t; +field_t(float fieldnum) entityfieldref = #0:entityfieldref; /* + Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */ + +string(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA + Retrieves the name of the given entity field. */ + +float(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA + Provides information about the type of the field specified by the field num. Returns one of the EV_ values. */ + +string(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/ +float(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/ +string() ReadPicture = #501; /* + Reads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then. */ + +void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags) boxparticles = #502; +string(string filename, optional float makereferenced) whichpack = #503; /* Part of DP_QC_WHICHPACK + Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */ + +__variant(float entnum, float fieldnum) getentity = #504; /* + Looks up fields from non-csqc-visible entities. The entity will need to be within the player's pvs. fieldnum should be one of the GE_ constants. */ + +string(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE*/ +string(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE*/ +float(entity ent) num_for_edict = #512; +#define uri_post uri_get +float(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_URI_GET, DP_QC_URI_POST + uri_get() gets content from an URL and calls a callback "uri_get_callback" with it set as string; an unique ID of the transfer is returned + returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string + For a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded. + For a GET request, omit the mime+data entirely. + Consult your webserver/php/etc documentation for best-practise. */ + +float(string str) tokenize_console = #514; /* + Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches. */ + +float(float idx) argv_start_index = #515; /* + Returns the character index that the tokenized arg started at. */ + +float(float idx) argv_end_index = #516; /* + Returns the character index that the tokenized arg stopped at. */ + +void(strbuf strbuf, string pattern, string antipattern) buf_cvarlist = #517; +string(string cvarname) cvar_description = #518; /* + Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful. */ + +float(optional float timetype) gettime = #519; +string(float keynum) keynumtostring_omgwtf = #520; +string(string command, optional float bindmap) findkeysforcommand = #521; /* + Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE. */ + +string(string command, optional float bindmap) findkeysforcommandex = #0:findkeysforcommandex; /* + Returns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys. */ + +void(string s) loadfromdata = #529; /* + Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ + +void(string s) loadfromfile = #530; /* + Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ + +float(float v, optional float base) log = #532; /* Part of ??MVDSV_BUILTINS + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + +float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0:soundupdate; /* + Changes the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero. */ + +float(entity e, float channel) getsoundtime = #533; /* + Returns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol). */ + +float(string sample) soundlength = #534; /* + Provides a way to query the duration of a sound sample, allowing you to set up a timer to chain samples. */ + +float(string filename, strbuf bufhandle) buf_loadfile = #535; /* + Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable. */ + +float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /* + Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */ + +void(entity e, float physics_enabled) physics_enable = #540; /* + Enable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time. */ + +void(entity e, vector force, vector relative_ofs) physics_addforce = #541; /* + Apply some impulse directional force upon a MOVETYPE_PHYSICS entity. */ + +void(entity e, vector torque) physics_addtorque = #542; /* + Apply some impulse rotational force upon a MOVETYPE_PHYSICS entity. */ + +void(float trg) setmousetarget = #603; +float() getmousetarget = #604; +void(.../*, string funcname*/) callfunction = #605; /* + Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is */ + +void(filestream fh, entity e) writetofile = #606; /* + Writes an entity's fields to the named frik_file file handle. */ + +float(string s) isfunction = #607; /* + Returns true if the named function exists and can be called with the callfunction builtin. */ + +vector(float vidmode, optional float forfullscreen) getresolution = #608; /* + Supposed to query the driver for supported video modes. FTE does not query drivers in this way, nor would it trust drivers anyway. */ + +string(float keynum) keynumtostring_menu = #609; +float(float type) gethostcachevalue = #611; /* Part of FTE_CSQC_SERVERBROWSER*/ +string(float type, float hostnr) gethostcachestring = #612; /* Part of FTE_CSQC_SERVERBROWSER*/ +float(entity e, string s, optional float offset) parseentitydata = #613; /* + Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {"foo1" "bar" "foo2" "5"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to. */ + +string(entity e) generateentitydata = #0:generateentitydata; /* + Dumps the entities fields into a string which can later be parsed with parseentitydata. */ + +float(string key) stringtokeynum_menu = #614; +void() resethostcachemasks = #615; /* Part of FTE_CSQC_SERVERBROWSER*/ +void(float mask, float fld, string str, float op) sethostcachemaskstring = #616; /* Part of FTE_CSQC_SERVERBROWSER*/ +void(float mask, float fld, float num, float op) sethostcachemasknumber = #617; /* Part of FTE_CSQC_SERVERBROWSER*/ +void() resorthostcache = #618; /* Part of FTE_CSQC_SERVERBROWSER*/ +void(float fld, float descending) sethostcachesort = #619; /* Part of FTE_CSQC_SERVERBROWSER*/ +void() refreshhostcache = #620; /* Part of FTE_CSQC_SERVERBROWSER*/ +float(float fld, float hostnr) gethostcachenumber = #621; /* Part of FTE_CSQC_SERVERBROWSER*/ +float(string key) gethostcacheindexforkey = #622; /* Part of FTE_CSQC_SERVERBROWSER*/ +void(string key) addwantedhostcachekey = #623; /* Part of FTE_CSQC_SERVERBROWSER*/ +string() getextresponse = #624; /* Part of FTE_CSQC_SERVERBROWSER*/ +string(string dnsname, optional float defport) netaddress_resolve = #625; +string(string fmt, ...) sprintf = #627; /* Part of DP_QC_SPRINTF*/ +float(entity e, float s) getsurfacenumtriangles = #628; +vector(entity e, float s, float n) getsurfacetriangle = #629; +vector() getbindmaps = #631; +float(vector bm) setbindmaps = #632; +string(string digest, string data, ...) digest_hex = #639; +#if defined(CSQC) || defined(MENU) +#define K_TAB 9 +#define K_ENTER 13 +#define K_ESCAPE 27 +#define K_SPACE 32 +#define K_BACKSPACE 127 +#define K_UPARROW 128 +#define K_DOWNARROW 129 +#define K_LEFTARROW 130 +#define K_RIGHTARROW 131 +#define K_LALT 132 +#define K_RALT -245 +#define K_LCTRL 133 +#define K_RCTRL -246 +#define K_LSHIFT 134 +#define K_RSHIFT -247 +#define K_F1 135 +#define K_F2 136 +#define K_F3 137 +#define K_F4 138 +#define K_F5 139 +#define K_F6 140 +#define K_F7 141 +#define K_F8 142 +#define K_F9 143 +#define K_F10 144 +#define K_F11 145 +#define K_F12 146 +#define K_INS 147 +#define K_DEL 148 +#define K_PGDN 149 +#define K_PGUP 150 +#define K_HOME 151 +#define K_END 152 +#define K_KP_HOME 164 +#define K_KP_UPARROW 165 +#define K_KP_PGUP 166 +#define K_KP_LEFTARROW 161 +#define K_KP_5 162 +#define K_KP_RIGHTARROW 163 +#define K_KP_END 158 +#define K_KP_DOWNARROW 159 +#define K_KP_PGDN 160 +#define K_KP_ENTER 172 +#define K_KP_INS 157 +#define K_KP_DEL 167 +#define K_KP_SLASH 168 +#define K_KP_MINUS 170 +#define K_KP_PLUS 171 +#define K_KP_NUMLOCK 154 +#define K_KP_STAR 169 +#define K_KP_EQUALS 173 +#define K_MOUSE1 512 +#define K_MOUSE2 513 +#define K_MOUSE3 514 +#define K_MOUSE4 517 +#define K_MOUSE5 518 +#define K_MOUSE6 519 +#define K_MOUSE7 520 +#define K_MOUSE8 521 +#define K_MOUSE9 522 +#define K_MOUSE10 523 +#define K_MWHEELUP 515 +#define K_MWHEELDOWN 516 +#define K_LWIN 239 +#define K_RWIN 240 +#define K_APP -241 +#define K_SEARCH -242 +#define K_POWER 130 +#define K_VOLUP -243 +#define K_VOLDOWN -244 +#define K_JOY1 768 +#define K_JOY2 769 +#define K_JOY3 770 +#define K_JOY4 771 +#define K_AUX1 784 +#define K_AUX2 785 +#define K_AUX3 786 +#define K_AUX4 787 +#define K_AUX5 788 +#define K_AUX6 789 +#define K_AUX7 790 +#define K_AUX8 791 +#define K_AUX9 792 +#define K_AUX10 793 +#define K_AUX11 794 +#define K_AUX12 795 +#define K_AUX13 796 +#define K_AUX14 797 +#define K_AUX15 798 +#define K_AUX16 799 +#define K_AUX17 800 +#define K_AUX18 801 +#define K_AUX19 802 +#define K_AUX20 803 +#define K_AUX21 804 +#define K_AUX22 805 +#define K_AUX23 806 +#define K_AUX24 807 +#define K_AUX25 808 +#define K_AUX26 809 +#define K_AUX27 810 +#define K_AUX28 811 +#define K_AUX29 812 +#define K_AUX30 813 +#define K_AUX31 814 +#define K_AUX32 815 +#define K_PAUSE 153 +#define K_PRINTSCREEN 174 +#define K_CAPSLOCK 155 +#define K_SCROLLLOCK 156 +#define K_SEMICOLON 59 +#define K_PLUS 43 +#define K_TILDE 126 +#define K_BACKQUOTE 96 +#define K_BACKSLASH 92 +#define K_GP_A 826 +#define K_GP_B 827 +#define K_GP_X 828 +#define K_GP_Y 829 +#define K_GP_LSHOULDER 824 +#define K_GP_RSHOULDER 825 +#define K_GP_LTRIGGER 830 +#define K_GP_RTRIGGER 831 +#define K_GP_BACK 821 +#define K_GP_START 820 +#define K_GP_LTHUMB 822 +#define K_GP_RTHUMB 823 +#define K_GP_DPAD_UP 816 +#define K_GP_DPAD_DOWN 817 +#define K_GP_DPAD_LEFT 818 +#define K_GP_DPAD_RIGHT 819 +#define K_GP_GUIDE 202 +#define K_GP_UNKNOWN 255 +#endif +#ifdef _ACCESSORS +accessor strbuf : float +{ + inline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));}; + inline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));}; + get string[float] = bufstr_get; + set string[float] = bufstr_set; + get float length = buf_getsize; +}; +accessor searchhandle : float +{ + get string[float] = search_getfilename; + get float length = search_getsize; +}; +accessor hashtable : float +{ + inline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);}; + inline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);}; + inline get string s[string key] = {return hash_get(this, key, "", EV_STRING);}; + inline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);}; + inline get float f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);}; + inline set float f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);}; + inline get __variant[string key] = {return hash_get(this, key, __NULL__);}; + inline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);}; +}; +accessor infostring : string +{ + get string[string] = infoget; + inline set* string[string fld] = {(*this) = infoadd(*this, fld, value);}; +}; +accessor filestream : float +{ + get string = fgets; + inline set string = {fputs(this,value);}; +}; +#endif +#pragma noref 0 diff --git a/source/client/hud.qc b/source/client/hud.qc new file mode 100644 index 0000000..c148bbd --- /dev/null +++ b/source/client/hud.qc @@ -0,0 +1,1430 @@ +/* + client/hud.qc + + HUD Drawing Code + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +//return hud images, makes drawpic slightly cleaner.. +string(string img) getImage = { + return strcat(huddir, img); +} + + +/******************* +* HUD_Health * +*******************/ + +void(float width, float height) HUD_Health = +{ + local float health; + float alpha; + + health = getstatf(STAT_HEALTH); + + if (health < 100) + { + alpha = (100.0 - ((1.25 * health) - 25))/100*255; + if (alpha <= 0.0) + return; + float modifier = (sin(time * 10) * 20) - 20;//always negative + if(modifier < -35.0) + modifier = -35.0; + + alpha += modifier; + + if(alpha < 0.0) + return; + float color = 255.0 + modifier; + + drawpic([0,0,0], getImage("blood.tga"), [width, height, 0], [10,0,0], alpha/2000); // naievil -- alpha factor division here makes it easy to use legacy code + } + +} + +/******************* +* HUD_Ammo * +*******************/ + +void(float width, float height) HUD_Ammo = +{ + local float ammo, curmag, benis, benis2; + string ammostring, ammostring_1, ammostring_2; + vector color; + + curmag = getstatf(STAT_CURRENTMAG); + ammo = getstatf(STAT_AMMO); + + benis = strlen(ftos(ammo)); + benis2 = strlen(ftos(curmag)); + benis = benis + benis2; + + if (GetLowAmmo(getstatf(STAT_ACTIVEWEAPON), 1) >= getstatf(STAT_CURRENTMAG)) + color = [215/255, 0, 0]; + else + color = [1, 1, 1]; + + if (GetLowAmmo(getstatf(STAT_ACTIVEWEAPON), 0) >= getstatf(STAT_AMMO)) + vector color_2 = [215/255, 0, 0]; + else + color_2 = [1, 1, 1]; + + + if (IsDualWeapon(getstatf(STAT_ACTIVEWEAPON))) { + float curmag2 = getstatf(STAT_CURRENTMAG2); + + ammostring = strcat(ftos(curmag2), " ", ftos(curmag), "/", ftos(ammo)); + drawstring([width - (42/480)*width - (benis*20*width/1024), 0.96*height, 0], ammostring, [0.02*width, 0.02*width, 0], [1,1,1], 1, 0); + } else { + ammostring_1 = ftos(curmag); + ammostring_2 = strcat("/",ftos(ammo)); + drawstring([width - (42/480)*width - (benis*20*width/1024), 0.96*height, 0], ammostring_1, [0.02*width, 0.02*width, 0], color, 1, 0); + drawstring([width - (42/480)*width - ((benis-strlen(ftos(curmag)))*20*width/1024), 0.96*height, 0], ammostring_2, [0.02*width, 0.02*width, 0], color_2, 1, 0); + } +} + +float ammoopac, ammoloop; +void() HUD_AmmoString = +{ + vector textcolor = [1, 1, 1]; + string message = ""; + + if (GetLowAmmo(getstatf(STAT_ACTIVEWEAPON), 1) >= getstatf(STAT_CURRENTMAG) && getstatf(STAT_WEAPONZOOM) != 2) + { + if (0 < getstatf(STAT_AMMO) && getstatf(STAT_CURRENTMAG) >= 0) { + message = "Reload"; + textcolor = [1, 1, 1]; + } else if (0 < getstatf(STAT_CURRENTMAG)) { + message = "LOW AMMO"; + textcolor = [219/255, 203/255, 19/255]; + } else { + message = "NO AMMO"; + textcolor = [215/255, 0, 0]; + } + + if (ammoloop == 0) { + ammoopac -= frametime; + if (ammoopac < 0.5) { + ammoopac = 0.5; + ammoloop = 1; + } + } else { + ammoopac += frametime; + if (ammoopac >= 1) { + ammoopac = 1; + ammoloop = 0; + } + } + + drawstring([g_width/2, g_height/2 + (30/272)*g_height, 0], message, [0.02*g_width, 0.02*g_width, 0], textcolor, ammoopac, 0); + } else { + ammoopac = 1; + ammoloop = 0; + } +} + +/******************* +* HUD_Points * +*******************/ + +#define MAX_POINT_ELEMENTS 64 // the maximum amount of point differential elements that can be spawned before + // we iterate back from 0 + +var struct +{ + float difference; // the difference of points + float x_position; // current x position + float y_position; // current y position + float x_velocity; // how fast the element moves on the x axis + float y_velocity; // how fast the element moves on the y axis + float opacity; // the opacity of the text_string as it progresses + float string_width; // the width of text_string + string text_string; // either '+(difference)' or '-(difference)' +} point_elements[MAX_POINT_ELEMENTS]; + +float last_point_element_index; + +void(float amount) RegisterPointChange = +{ + if (last_point_element_index >= MAX_POINT_ELEMENTS) + last_point_element_index = 0; + + float index = last_point_element_index; + + // set the difference amount + point_elements[index].difference = amount; + + // fill our text string + if (point_elements[index].difference > 0) { + point_elements[index].text_string = strcat("+", ftos(point_elements[index].difference)); + } else { + point_elements[index].text_string = ftos(point_elements[index].difference); + } + + // determine the width of the text string + point_elements[index].string_width = stringwidth(point_elements[index].text_string, 0, [0.022*g_width, 0.022*g_width, 0]); + + // generate a velocity + point_elements[index].y_velocity = random()/4; + while (point_elements[index].x_velocity < 0.33) { + point_elements[index].x_velocity = random(); + } + + if (point_elements[index].x_velocity > 0.70) + point_elements[index].x_velocity -= 0.2; + + // should the vertical velocity be positive or negative? + float rng = random(); + + // negative + if (rng < 0.5) { + point_elements[index].y_velocity = -point_elements[index].y_velocity; + } + + // set our x and y positions + point_elements[index].x_position = 0; + point_elements[index].y_position = 0.720 * g_height; + + // start with an opacity of 1 + point_elements[index].opacity = 1; + + // iterate + last_point_element_index++; +} + +void(float pwidth, float width, float height) PointUpdate = +{ + vector POINT_DIFF_COLOR; + + for (float i = 0; i < MAX_POINT_ELEMENTS; i++) { + // should the text be red or orange? + if (point_elements[i].difference > 0) { + POINT_DIFF_COLOR = TEXT_ORANGE; + } else { + POINT_DIFF_COLOR = TEXT_RED; + } + + if (point_elements[i].difference != 0 && point_elements[i].opacity > 0 /* && + (point_elements[i].x_position - point_elements[i].string_width) < g_width * 0.05*/) { + + drawstring([pwidth - point_elements[i].string_width - point_elements[i].x_position, + point_elements[i].y_position, 0], point_elements[i].text_string, [0.022*width, 0.022*width, 0], + POINT_DIFF_COLOR, point_elements[i].opacity, 0); + } + + point_elements[i].x_position += point_elements[i].x_velocity * (frametime*375); + point_elements[i].y_position += point_elements[i].y_velocity * (frametime*375); + point_elements[i].opacity -= (frametime*4); + } +} + +void(float width, float height) HUD_Points = +{ + local float pointlength = 0, increm = 10, i = 0, pointwidth = 0, x = 0; + local float backwidth = 0.8*width; + vector TEXTCOLOR = '0 0 0'; + + for (i = 3; i >= 0; i = i - 1) + { + if (playerpoints[i] == -1) + continue; + + switch(i) { + case 1: TEXTCOLOR = TEXT_LIGHTBLUE; break; + case 2: TEXTCOLOR = TEXT_ORANGE; break; + case 3: TEXTCOLOR = TEXT_GREEN; break; + default: TEXTCOLOR = [1, 1, 1]; break; + } + + if ((i+1) == getstatf(STAT_PLAYERNUM)) { + pointwidth = stringwidth(ftos(playerpoints[i]), 0, [0.022*width, 0.022*width, 0]); + x = ((width + backwidth) - pointwidth)/2; + drawpic([backwidth ,0.70*height - (.061*height*i),0], getImage("moneyback.tga"), [0.2*width, 0.07*height, 0], [1,1,1], 1); + drawstring([x, 0.715*height - 0.06*height*i, 0], ftos(playerpoints[i]), [0.022*width, 0.022*width, 0], TEXTCOLOR, 1, 0); + PointUpdate(backwidth * 1.025, width, height); + } + else { + backwidth = 0.85*width; + pointwidth = stringwidth(ftos(playerpoints[i]), 0, [0.022*width*0.8, 0.022*width*0.8, 0]); + x = ((width + backwidth) - pointwidth)/2; + drawpic([backwidth, 0.70*height - (.071*height*i*0.8),0], getImage("moneyback.tga"), [0.2*width*0.8, 0.07*height*0.8, 0], [1,1,1], 0); + drawstring([x, 0.715*height - 0.07*height*i*0.8, 0], ftos(playerpoints[i]), [0.022*width*0.8, 0.022*width*0.8, 0], TEXTCOLOR, 1, 0); + } + } +} + +/******************* +* HUD_Grenades * +*******************/ + +void(float width, float height) HUD_Grenades = +{ + local float grenades; + local float betties; + + grenades = getstatf(STAT_GRENADES); + betties = getstatf(STAT_SECGRENADES); + drawpic([0.9*width,0.825*height,0], getImage("frag.tga"), [0.11*height, 0.11*height, 0], [1,1,1], 1); + drawstring([0.935*width, 0.900*height, 0], ftos(grenades), [0.025*width, 0.025*width, 0], [1, 1, 1], 1, 0); + + if (betties != -1) { + drawpic([0.825*width,0.825*height,0], getImage("betty.tga"), [0.11*height, 0.11*height, 0], [1,1,1], 1); + drawstring([0.935*width, 0.900*height, 0], ftos(betties), [0.025*width, 0.025*width, 0], [1, 1, 1], 1, 0); + } +} + +/******************* +* HUD_Rounds * +*******************/ + +float color_shift[3]; +float color_shift_end[3]; +float color_shift_steps[3]; +int color_shift_init; +int blinking; +string sb_round[5]; +string sb_round_num[10]; +int alphabling; +float endroundchange; +float round_center_x; +float round_center_y; +//motolegacy -- 'round' text +float pwidth; +float rcolor, rinit, ralpha, localpha; + +void(float width, float height) HUD_Rounds = +{ + float roundheight = 48/272*height; + float roundwidth = 0.02291666*width; + + float roundwidth_4 = (60/480)*width; + float roundheight_4 = (48/272)*height; + + float roundheight_num = 48/272*height; + float roundwidth_num = 48/480*height; // naievil -- was 32, but more square makes it look better + +// drawstring([0.01*width, 0.70*height, 0], ftos(rounds_change), [0.03*width, 0.03*width, 0], [1, 1, 1], 1, 1); // draw roundchange + + int i, x_offset, icon_num, savex; + int num[3]; + x_offset = 0; + savex = 0; + + for (float j = 0; j < 10; j++) { + if (j < 5) { + sb_round[j] = getImage(strcat("r", ftos(j+1), ".tga")); + } + + sb_round_num[j] = getImage(strcat("r_num", ftos(j), ".tga")); + } + + if (rounds_change == 1 || rounds_change == 2) { + if (!rinit) { + rcolor = rinit = 1; + ralpha = 1; + } + + pwidth = (width - stringwidth("Round", 0, [0.030*width, 0.030*width, 0]))/2; + drawstring([pwidth, 0.35*height, 0], "Round", [0.030*width, 0.030*width, 0], [1, rcolor, rcolor], ralpha, 0); + + rcolor -= frametime/2.5; + if (rcolor < 0) { + rcolor = 0; + ralpha -= frametime/2.5; + if (ralpha > 0) { + localpha += frametime*0.4; + if (localpha > 1) + localpha = 1; + } + if (ralpha < 0) { + ralpha = 0; + localpha -= frametime*0.4; + if (localpha < 0) + localpha = 0; + } + } + + drawstring([0.01*width, 0.66*height, 0], chaptertitle, [0.015*width, 0.015*width, 0], [1, 1, 1], localpha, 0); + drawstring([0.01*width, 0.69*height, 0], location, [0.015*width, 0.015*width, 0], [1, 1, 1], localpha, 0); + drawstring([0.01*width, 0.72*height, 0], date, [0.015*width, 0.015*width, 0], [1, 1, 1], localpha, 0); + drawstring([0.01*width, 0.75*height, 0], person, [0.015*width, 0.015*width, 0], [1, 1, 1], localpha, 0); + } + + if (rounds_change == 1)//this is the rounds icon at the middle of the screen + { + alphabling = alphabling + (frametime*500); + + if (alphabling < 0) + alphabling = 0; + else if (alphabling > 255) + alphabling = 255; + + round_center_x = (g_width - (11*g_width/480)) /2; + round_center_y = (g_height - (48*g_height/272)) /2; + + drawpic([round_center_x,round_center_y,0], sb_round[0], [roundwidth, roundheight, 1], [107/255,1/255,0], alphabling/255); + } + else if (rounds_change == 2)//this is the rounds icon moving from middle + { + float round_center_y_offset = 0; + float round_center_x_offset = 0; + + drawpic([round_center_x + round_center_x_offset,round_center_y + round_center_y_offset,0], sb_round[0], [roundwidth,roundheight, 1], [107/255,(1/255),0], 1); + + round_center_x = round_center_x - (((229/108)*2 - 0.2)*(width/480)/8) * (frametime*250); + round_center_y = round_center_y + ((2*(height/272))/8) * (frametime*250); + if (round_center_x <= (5/480)*height) + round_center_x = (5/480)*height; + if (round_center_y >= (220/272)*height) + round_center_y = (220/272)*height; + + } + else if (rounds_change == 3)//shift to white + { + if (!color_shift_init) + { + color_shift[0] = 107; + color_shift[1] = 1; + color_shift[2] = 0; + for (i = 0; i < 3; i = i + 1) + { + color_shift_end[i] = 255; + color_shift_steps[i] = (color_shift_end[i] - color_shift[i])/60; + } + color_shift_init = 1; + } + for (i = 0; i < 3; i = i + 1) + { + if (color_shift[i] < color_shift_end[i]) + color_shift[i] = color_shift[i] + color_shift_steps[i]*(frametime*100); + + if (color_shift[i] >= color_shift_end[i]) + color_shift[i] = color_shift_end[i]; + } + if (rounds > 0 && rounds < 11) + { + + for (i = 0; i < rounds; i = i + 1) + { + if (i == 4) + { + drawpic([5,height - roundheight, 0], sb_round[4], [roundwidth_4,roundheight_4, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + savex = x_offset + (10*width/480); + x_offset = x_offset + (10*width/480); + continue; + } + if (i == 9) + { + drawpic([5 + savex, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round[icon_num], [roundwidth,roundheight, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth + (3*width/480); + } + } + else + { + if (rounds >= 100) + { + num[2] = (int)(rounds/100); + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[2]], [roundwidth_num, roundheight_num, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[2] = 0; + if (rounds >= 10) + { + num[1] = (int)((rounds - num[2]*100)/10); + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[1]], [roundwidth_num,roundheight_num, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[1] = 0; + + num[0] = rounds - num[2]*100 - num[1]*10; + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[0]], [roundwidth_num,roundheight_num, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + } + else if (rounds_change == 4)//blink white + { + + if (endroundchange > time) + { + blinking = ((int)(time*1000)&510) - 255; + blinking = fabs(blinking); + } + else + { + if (blinking) + blinking = blinking - 1; + else + blinking = 0; + } + + + if (rounds > 0 && rounds < 11) + { + for (i = 0; i < rounds; i = i + 1) + { + if (i == 4) + { + drawpic([5, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [1,1,1], blinking/255); + + savex = x_offset + (10*width/480); + x_offset = x_offset + (10*width/480); + continue; + } + if (i == 9) + { + drawpic([5 + savex, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [1,1,1], blinking/255); + + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round[icon_num], [roundwidth,roundheight, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth + (3*width/480); + } + } + else + { + if (rounds >= 100) + { + num[2] = (int)(rounds/100); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[2]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[2] = 0; + if (rounds >= 10) + { + num[1] = (int)((rounds - num[2]*100)/10); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[1]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[1] = 0; + + num[0] = rounds - num[2]*100 - num[1]*10; + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[0]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + + if (endroundchange == 0) + endroundchange = time + 2; + } + else if (rounds_change == 5)//blink white + { + if (blinking > 0) + blinking = blinking - (frametime*5000); + if (blinking < 0) + blinking = 0; + if (rounds > 0 && rounds < 11) + { + for (i = 0; i < rounds; i = i + 1) + { + if (i == 4) + { + drawpic([5, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [1,1,1], blinking/255); + + savex = x_offset + (10*width/480); + x_offset = x_offset + (10*width/480); + continue; + } + if (i == 9) + { + drawpic([5 + savex, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [1,1,1], blinking/255); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round[icon_num], [roundwidth,roundheight, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth + (3*width/480); + } + } + else + { + if (rounds >= 100) + { + num[2] = (int)(rounds/100); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[2]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[2] = 0; + if (rounds >= 10) + { + num[1] = (int)((rounds - num[2]*100)/10); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[1]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[1] = 0; + + num[0] = rounds - num[2]*100 - num[1]*10; + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[0]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + } + else if (rounds_change == 6)//blink white while fading back + { + endroundchange = 0; + + color_shift_init = 0; + blinking = ((int)(time*1000)&510) - 255; + + blinking = fabs(blinking); + + if (rounds > 0 && rounds < 11) + { + for (i = 0; i < rounds; i = i + 1) + { + if (i == 4) + { + drawpic([5, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [1,1,1], blinking/255); + + savex = x_offset + (10*width/480); + x_offset = x_offset + (10*width/480); + continue; + } + if (i == 9) + { + drawpic([5 + savex, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [1,1,1], blinking/255); + + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round[icon_num], [roundwidth,roundheight, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth + (3*width/480); + } + } + else + { + if (rounds >= 100) + { + num[2] = (int)(rounds/100); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[2]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[2] = 0; + if (rounds >= 10) + { + num[1] = (int)((rounds - num[2]*100)/10); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[1]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[1] = 0; + + num[0] = rounds - num[2]*100 - num[1]*10; + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[0]], [roundwidth_num,roundheight_num, 1], [1,1,1], blinking/255); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + } + else if (rounds_change == 7)//blink white while fading back + { + if (!color_shift_init) + { + color_shift_end[0] = 107; + color_shift_end[1] = 1; + color_shift_end[2] = 0; + for (i = 0; i < 3; i = i + 1) + { + color_shift[i] = 255; + color_shift_steps[i] = (color_shift[i] - color_shift_end[i])*(frametime*1.5); + } + color_shift_init = 1; + } + for (i = 0; i < 3; i = i + 1) + { + if (color_shift[i] > color_shift_end[i]) + color_shift[i] = color_shift[i] - color_shift_steps[i]; + + if (color_shift[i] < color_shift_end[i]) + color_shift[i] = color_shift_end[i]; + } + if (rounds > 0 && rounds < 11) + { + for (i = 0; i < rounds; i = i + 1) + { + if (i == 4) + { + drawpic([5, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + savex = x_offset + (10*width/480); + x_offset = x_offset + (10*width/480); + continue; + } + if (i == 9) + { + drawpic([5 + savex, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round[icon_num], [roundwidth,roundheight, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth + (3*width/480); + } + } + else + { + if (rounds >= 100) + { + num[2] = (int)(rounds/100); + drawpic([2+x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[2]], [roundwidth_num,roundheight_num, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[2] = 0; + if (rounds >= 10) + { + num[1] = (int)((rounds - num[2]*100)/10); + drawpic([2+x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[1]], [roundwidth_num,roundheight_num, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[1] = 0; + + num[0] = rounds - num[2]*100 - num[1]*10; + drawpic([2+x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[0]], [roundwidth_num,roundheight_num, 1], [color_shift[0]/255,color_shift[1]/255,color_shift[2]/255], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + } + else + { + color_shift[0] = 107; + color_shift[1] = 1; + color_shift[2] = 0; + color_shift_init = 0; + alphabling = 0; + if (rounds > 0 && rounds < 11) + { + for (i = 0; i < rounds; i = i + 1) + { + if (i == 4) + { + drawpic([5, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [107/255,1/255,0], 1); + + savex = x_offset + (10*width/480); + x_offset = x_offset + (10*width/480); + continue; + } + if (i == 9) + { + drawpic([5 + savex, height - roundheight - (4*height/272),0], sb_round[4], [roundwidth_4,roundheight_4, 1], [107/255,1/255,0], 1); + + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + drawpic([5 + x_offset, height - roundheight - (4*height/272),0], sb_round[icon_num], [roundwidth,roundheight, 1], [107/255,1/255,0], 1); + + x_offset = x_offset + roundwidth + (3*width/480); + } + } + else + { + if (rounds >= 100) + { + num[2] = (int)(rounds/100); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[2]], [roundwidth_num,roundheight_num, 1], [107/255,1/255,0], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[2] = 0; + if (rounds >= 10) + { + num[1] = (int)((rounds - num[2]*100)/10); + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[1]], [roundwidth_num,roundheight_num, 1], [107/255,1/255,0], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + else + num[1] = 0; + + num[0] = rounds - num[2]*100 - num[1]*10; + + if(rounds == 0) + return; + + drawpic([2 + x_offset, height - roundheight - (4*height/272),0], sb_round_num[num[0]], [roundwidth_num,roundheight_num, 1], [107/255,1/255,0], 1); + + x_offset = x_offset + roundwidth_num - (8*width/480); + } + } +} + +/******************* +* HUD_Useprint * +*******************/ + +void(float width, float height) HUD_Useprint = +{ + string usestring, usebutton, usespace; + float print_width, x, button_width; + + usestring = ""; + usespace = ""; + + tokenize(findkeysforcommand("+button7")); + usebutton = strtoupper(keynumtostring(stof(argv(0)))); + + for(float i = 0; i < strlen(usebutton); i++) { + usespace = strcat(usespace, " "); + } + + switch (useprint_type) { + case 0://clear + usestring = ""; + break; + case 1://door + usestring = strcat("Hold ",usespace, " to open Door [Cost:", ftos(useprint_cost),"]"); + break; + case 2://debris + usestring = strcat("Hold ",usespace, " to remove Debris [Cost:", ftos(useprint_cost),"]"); + break; + case 3://ammo + usestring = strcat("Hold ",usespace, " to buy Ammo for ", GetWeaponName(useprint_weapon), " [Cost:", ftos(useprint_cost),"]"); + break; + case 4://weapon + usestring = strcat("Hold ",usespace, " to buy ", GetWeaponName(useprint_weapon), " [Cost:", ftos(useprint_cost),"]"); + break; + case 5://window + usestring = strcat("Hold ",usespace, " to Rebuild Barrier"); + break; + case 6://box + usestring = strcat("Hold ",usespace, " to buy a Random Weapon [Cost:", ftos(useprint_cost),"]"); + break; + case 7://box take + usestring = strcat("Hold ",usespace, " to take Weapon"); + break; + case 8://power + usestring = "The Power must be Activated first"; + break; + case 9://perk + usestring = strcat("Hold ",usespace, " to buy ", GetPerkName(useprint_weapon), " [Cost:", ftos(useprint_cost),"]"); + break; + case 10://turn on power + usestring = strcat("Hold ",usespace, " to Turn On the Power"); + break; + case 11://turn on trap + usestring = strcat("Hold ",usespace, " to Activate the Trap [Cost:", ftos(useprint_cost), "]"); + break; + case 12://packapunch + usestring = strcat("Hold ",usespace, " to Pack-a-Punch [Cost:", ftos(useprint_cost), "]"); + break; + case 13://revive + usestring = strcat("Hold ",usespace, " to Revive Player"); + break; + case 14://use teleporter (free) + usestring = strcat("Hold ", usespace, " to use Teleporter"); + break; + case 15://use teleporter (cost) + usestring = strcat("Hold ", usespace, " to use Teleporter [Cost: ", ftos(useprint_cost), "]"); + break; + case 16://tp cooldown + usestring = "Teleporter is cooling down"; + break; + case 17://link + usestring = strcat("Hold ", usespace, " to initate link to pad"); + break; + case 18://no link + usestring = "Link not active"; + break; + case 19://finish link + usestring = strcat("Hold ", usespace, " to link pad with core"); + break; + case 20://buyable ending + usestring = strcat("Hold ", usespace, " to End the Game [Cost: ", ftos(useprint_cost), "]"); + break; + default: + usestring = "This should not happen you dum fuck"; //yikes + break; + } + + print_width = stringwidth (usestring, 0, [0.015*width, 0.015*width, 0]); + x = (width - print_width)/2; + drawstring([x, 0.67*height, 0], usestring, [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + + // Draw highlighted usebutton (or button icon in the future) + if (substring(usestring, 0, 4) == "Hold") { + button_width = x + stringwidth ("Hold ", 0, [0.015*width, 0.015*width, 0]); + drawstring([button_width, 0.67*height, 0], usebutton, [0.015*width, 0.015*width, 0], [1, 1, 0], 1, 0); + } +} + +/******************* +* HUD_Perks * +*******************/ + +int perk_order[9]; +int current_perk_order; + +void(float width, float height) HUD_Perks = +{ + int i; + float y = 0.725; + float x = 0.005; + + for (i = 0; i < 9; i = i + 1) { + if (perk_order[i]) { + switch(perk_order[i]) { + case P_JUG: drawpic([x*width,y*height,0], getImage("jug.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_DOUBLE: drawpic([x*width,y*height,0], getImage("double.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_SPEED: drawpic([x*width,y*height,0], getImage("speed.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_REVIVE: drawpic([x*width,y*height,0], getImage("revive.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_FLOP: drawpic([x*width,y*height,0], getImage("flopper.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_STAMIN: drawpic([x*width,y*height,0], getImage("stamin.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_DEAD: drawpic([x*width,y*height,0], getImage("dead.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + case P_MULE: drawpic([x*width,y*height,0], getImage("mule.tga"), [0.075*height, 0.075*height, 1], [1,1,1], 1); break; + default: break; + } + + y -= 0.08; + } + } +} + +/******************* +* HUD Weapons * +*******************/ + +void(float width, float height) HUD_Weaponstring = +{ + string weaponstring; + float ws_offset; + + weaponstring = GetWeaponName(getstatf(STAT_ACTIVEWEAPON)); + ws_offset = (strlen(weaponstring)); + + drawstring([0.925*width + (40*width/1024) - ws_offset*20*width/1024, 0.75*height + 10, 0], weaponstring, [0.02*width, 0.02*width, 0], [1, 1, 1], 1, 0); +} + +/******************* +* HUD Progress Bar * +*******************/ + +void(float width, float height) HUD_Progressbar = +{ + float percent = getstatf(STAT_PROGRESSBAR); + if (!percent) { + return; + } + + string progress; + local float ws_offset; + + progress = ftos(percent); + ws_offset = (strlen(progress) - 1); + + float bar_width = 0.0175*width * 20; + float bar_height = 0.0175*width; + float bar_x = (width - bar_width)/2; + float bar_y = height*0.75; + + drawfill ([bar_x - 1, bar_y - 1, 0], [bar_width+2, bar_height, 0], [0, 0, 0], 0.4, 0); + drawfill ([bar_x, bar_y, 0], [bar_width * percent, bar_height-2, 0], [1, 1, 1], 0.4, 0); + + drawstring([(width - (88))/2, height*0.77 + 10, 0], "Reviving...", [0.0175*width, 0.0175*width, 0], [1, 1, 1], 1, 0); +} + +/******************* +* HUD Hitmark * +*******************/ + +void() HUD_Hitmark = +{ + drawpic([g_width/2 - (8/480)*g_width, g_height/2 - (8/480)*g_width,0], "gfx/hud/hit_marker.tga", [(16/480)*g_width, (16/480)*g_width, 1], [1,1,1], 1); +} + +/******************* +* HUD Crosshair * +*******************/ + +float croshhairmoving; // naievil --used t o see if we are moving or not +float cur_spread; +float crosshair_offset_step; +float crosshair_opacity; +vector crosshair_color; + +void() Draw_Crosshair = +{ + if (!crosshair_opacity) + crosshair_opacity = 1; + + if (K_BACKDOWN || K_FORWARDDOWN || K_LEFTDOWN || K_RIGHTDOWN || input_buttons == 2) { + croshhairmoving = 1; + + crosshair_opacity -= frametime; + + if (crosshair_opacity < 0.5) + crosshair_opacity = 0.5; + + if (cur_spread >= CrossHairMaxSpread(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE))) { + cur_spread = CrossHairMaxSpread(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE)); + } else { + cur_spread += (frametime*80); + + } + } else { + croshhairmoving = 0; + + if (cur_spread > 0) + cur_spread -= (frametime*80); + else + cur_spread = 0; + + crosshair_opacity += frametime; + + if (crosshair_opacity > 1) + crosshair_opacity = 1; + } + + // Update values to be red if we're facing an enemy + // FIXME - this should be handled COMPLETELY client side, NOT as a stat. + if (getstatf(STAT_FACINGENEMY)) + crosshair_color = TEXT_RED; + else + crosshair_color = [1, 1, 1]; + + if (getstatf(STAT_WEAPONZOOM) == 2 && zoom_2_time < time) + { + setmodel(vmodel, ""); + setmodel(v2model, ""); + drawfill('0 0 0', [g_width/2 - g_height/2, g_height, 0], '0 0 0', 1, 0); + drawpic([(g_width/2 - g_height/2),0,0], "gfx/hud/scope.tga", [g_height, g_height, 1], [1,1,1], 1); + drawfill([(g_width/2 + g_height/2),0,0], [g_width, g_height, 0], '0 0 0', 1, 0); + } + + if (getstatf(STAT_HEALTH) < 11) + return; + + if (crosshair_spread_time > time && crosshair_spread_time) + { + cur_spread = cur_spread + 10; + if (cur_spread >= CrossHairMaxSpread(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE))) + cur_spread = CrossHairMaxSpread(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE)); + + if (!croshhairmoving) + cur_spread *= 1/2; + } + else if (crosshair_spread_time < time && crosshair_spread_time) + { + cur_spread = cur_spread - 0.25; + if (cur_spread <= 0) + { + cur_spread = 0; + crosshair_spread_time = 0; + } + } + + if (getstatf(STAT_ACTIVEWEAPON) == W_M2 || getstatf(STAT_ACTIVEWEAPON) == W_TESLA || getstatf(STAT_ACTIVEWEAPON) == W_DG3) + { + drawstring([(g_width/2 - g_width*0.0175/2), (g_height/2 - g_height*0.0175/2), 0], "O", [0.0175*g_width, 0.0175*g_width, 0], crosshair_color, 1, 0); + } + else if (getstatf(STAT_WEAPONZOOM) != 1 && getstatf(STAT_WEAPONZOOM) != 2 && getstatf(STAT_ACTIVEWEAPON) != W_PANZER && getstatf(STAT_ACTIVEWEAPON) != W_LONGINUS) // naievil (FIXME) crosshair cvar + { + int x_value, y_value; + int crosshair_offset; + + crosshair_offset = CrossHairWeapon(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE)) + cur_spread; + + if (CrossHairMaxSpread(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE)) < crosshair_offset) + crosshair_offset = CrossHairMaxSpread(getstatf(STAT_ACTIVEWEAPON), getstatf(STAT_PLAYERSTANCE)); + + if (perks & P_DEAD) + crosshair_offset *= 0.65; + + crosshair_offset_step += (crosshair_offset - crosshair_offset_step) * 0.5; + + // Left + x_value = g_width/2 - crosshair_offset_step*2; + y_value = g_height/2; + drawstring([x_value, y_value, 0], "-", [0.0175*g_width, 0.0175*g_width, 0], crosshair_color, crosshair_opacity, 0); + + // Right + x_value = g_width/2 + crosshair_offset_step*2; + y_value = g_height/2; + drawstring([x_value, y_value, 0], "-", [0.0175*g_width, 0.0175*g_width, 0], crosshair_color, crosshair_opacity, 0); + + // Down + x_value = g_width/2; + y_value = g_height/2 - crosshair_offset_step*2; + drawstring([x_value, y_value, 0], "|", [0.0175*g_width, 0.0175*g_width, 0], crosshair_color, crosshair_opacity, 0); + + // Up + x_value = g_width/2; + y_value = g_height/2 + crosshair_offset_step*2; + drawstring([x_value, y_value, 0], "|", [0.0175*g_width, 0.0175*g_width, 0], crosshair_color, crosshair_opacity, 0); + + } + else if (/*crosshair.value && */getstatf(STAT_WEAPONZOOM) != 1 && getstatf(STAT_WEAPONZOOM) != 2) // naievil (FIXME) crosshair cvar + drawstring([((480 - 8)/2/480)*g_width, ((272 - 8)/2)*(g_height/272), 0], ".", [0.0175*g_width, 0.0175*g_width, 0], [1, 1, 1], 1, 0); +} + +void() HUD_Powerups = { + + if(getstatf(STAT_X2)) + drawpic([(g_width/2 - (28/272)*g_height),0,0], "gfx/hud/2x.tga", [(28/272)*g_height, (28/272)*g_height, 1], [1,1,1], 1); + if(getstatf(STAT_INSTA)) + drawpic([(g_width/2 + (5/480)*g_width),0,0], "gfx/hud/in_kill.tga", [(28/272)*g_height, (28/272)*g_height, 1], [1,1,1], 1); +} + +void() HUD_Broadcast = { + + string broadcast_msg = ""; + float health = getstatf(STAT_HEALTH); + + switch(broadcast_type) { + case 2: broadcast_msg = "A Player needs to be revived"; break; + default: broadcast_msg = "benis"; break; + } + float print_width = stringwidth (broadcast_msg, 0, [0.015*g_width, 0.015*g_width, 0]); + float x = (g_width - print_width)/2; + if (broadcast_type == 2 && health > 19) + drawstring([x, g_height/2, 0], broadcast_msg, [0.015*g_width, 0.015*g_width, 0], [1, 1, 1], 1, 0); +} + +void() HUD_Scores = +{ + string subtext = "Name Kills Score"; + float xback, i, x; + vector TEXTCOLOR = [1, 1, 1]; + + if(serverkey("constate") != "disconnected") + { + float print_width = stringwidth(subtext, 0, [0.015*g_width, 0.015*g_width, 0]); + x = (g_width - print_width)/2; + drawstring([x,g_height*(5/12), 0], subtext, [0.015*g_width, 0.015*g_width, 0], [1, 1, 1], 1, 0); + + xback = x; + + for (i = 0; i < 4; i = i + 1) + { + if (playerpoints[i] == -1) + break; + + switch(i) { + case 1: TEXTCOLOR = TEXT_LIGHTBLUE; break; + case 2: TEXTCOLOR = TEXT_ORANGE; break; + case 3: TEXTCOLOR = TEXT_GREEN; break; + default: TEXTCOLOR = [1, 1, 1]; break; + } + + print_width = stringwidth(ftos(playerpoints[i]), 0, [0.015*g_width, 0.015*g_width, 0]); + x = (g_width + (0.5*g_width) - print_width)/2; + drawstring([x,g_height*(5.75/12)+(i*g_width*0.03), 0], ftos(playerpoints[i]), [0.015*g_width, 0.015*g_width, 0], TEXTCOLOR, 1, 0); + drawstring([x/1.36,g_height*(5.75/12)+(i*g_width*0.03), 0], ftos(playerkills[i]), [0.015*g_width, 0.015*g_width, 0], TEXTCOLOR, 1, 0); + drawstring([xback,g_height*(5.75/12)+(i*g_width*0.03), 0], playernames[i], [0.015*g_width, 0.015*g_width, 0], TEXTCOLOR, 1, 0); + } + } +} + +void() HUD_Endgame = { + + string message = "GAME OVER"; + string rnd = " rounds"; + + if (rounds == 1) + rnd = " round"; + + string survive = strcat("You survived ", ftos(rounds), rnd); + + // first message + float print_width = stringwidth(message, 0, [(1/30)*g_width, (1/30)*g_width, 0]); + float x = (g_width - print_width)/2; + drawstring([x, g_height*(1/4), 0], message, [(1/30)*g_width, (1/30)*g_width, 0], [1, 1, 1], 1, 0); + + // second message + print_width = stringwidth(survive, 0, [0.025*g_width, 0.025*g_width, 0]); + x = (g_width - print_width)/2; + drawstring([x,g_height*(1/3), 0], survive, [0.025*g_width, 0.025*g_width, 0], [1, 1, 1], 1, 0); + + // we can reuse our tab scores for the endgame + HUD_Scores(); +} + +float oldfade_alpha; +void() HUD_Fade = +{ + float alpha; + + if (fade_type == 1) { + alpha = cos(fade_time - time); + if (oldfade_alpha > 0.95) + alpha = 1; + + alpha = invertfloat(alpha); + + drawfill ([0, 0, 0], [g_width, g_height, 0], [0, 0, 0], alpha, 0); // background + oldfade_alpha = alpha; + } + else if (fade_type == 2) { + alpha = sin(((fade_time - time) * 2)); + if (oldfade_alpha > 0.95) + alpha = 1; + + drawfill ([0, 0, 0], [g_width, g_height, 0], [0, 0, 0], alpha, 0); // background + oldfade_alpha = alpha; + } +} + +void(float width, float height) HUD_ScrollText = { + local float print_width = stringwidth (scrolltext, 0, [0.015*width, 0.015*width, 0]); + local float x = (width - print_width)/2; + + drawstring([x, scrollheight*height, 0], scrolltext, [0.015*width, 0.015*width, 0], [1, 1, 1], scrollopacity, 0); //low + if (scrollheight > 0.70) { + scrollheight -= frametime/10; + if (scrollopacity > 0) + scrollopacity -= frametime; + } else { + stext = 0; + } +} + +float achievement_init; +float achievement_ypos; +float achievement_desc_ypos; +float achievement_img_ypos; +float achievement_time; +void(float width, float height) HUD_Achievements = { + if (active_achievement == -1) + return; + + if (!achievement_init) { + achievement_time = 0; + achievement_ypos = -0.16; + achievement_desc_ypos = -0.13; + achievement_img_ypos = -0.164; + achievement_init = true; + } + + + drawstring([0.2*width, achievement_ypos*height, 0], "ACHIEVEMENT GET!", [0.015*width, 0.015*width, 0], [1, 1, 0], 1, 0); + drawstring([0.2*width, achievement_desc_ypos*height, 0], achievements[active_achievement].name, [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawpic([0.005*width, achievement_img_ypos*height,0], achievements[active_achievement].img, [0.16*width, 0.08*width, 0], [1,1,1], 1); + + if (achievement_time <= 120) { + achievement_ypos += (frametime*0.15); + achievement_desc_ypos += (frametime*0.15); + achievement_img_ypos += (frametime*0.15); + } else { + achievement_ypos -= (frametime*0.15); + achievement_desc_ypos -= (frametime*0.15); + achievement_img_ypos -= (frametime*0.15); + if (achievement_desc_ypos <= -0.13) { + achievement_init = 0; + active_achievement = -1; + } + } + + if (achievement_desc_ypos > 0.045) { + if (achievement_time <= 120) { + achievement_ypos = 0.015; + achievement_desc_ypos = 0.045; + achievement_img_ypos = 0.01; + } + + achievement_time += (frametime*25); + } +} + +string(float index) GetButtonString = +{ + // place holder + if (index == 100) + return "LMOUSE"; + else if (index == 101) + return "RMOUSE"; + + tokenize(findkeysforcommand(buttonBind[index])); + string temps = strtoupper(keynumtostring(stof(argv(0)))); + + return temps; +} + +void(float width, float height) HUD_Waypoint = +{ + drawstring([0.015*width, 0.015*height, 0], "WAYPOINT MODE", [0.030*width, 0.030*width, 0], [1, 1, 1], 1, 0); + drawfill([0.015*width, 0.035*height + height*0.035, 0], [strlen("WAYPOINT MODE")*0.030*width, 0.005*width, 0], [1, 1,1], 1, 0); + drawstring([0.015*width, 0.095*height, 0], strcat("Press ", GetButtonString(100), " to Create a Waypoint"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.125*height, 0], strcat("Press ", GetButtonString(9), " to Select a Waypoint"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.155*height, 0], strcat("Press ", GetButtonString(101), " to Link a Waypoint"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.185*height, 0], strcat("Press ", GetButtonString(11), " to Remove a Waypoint"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.215*height, 0], strcat("Press ", GetButtonString(7), " to Move a Waypoint Here"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.245*height, 0], strcat("Press ", GetButtonString(10), " to Create a Special Waypoint"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.275*height, 0], strcat("Press ", "=", " to load map waypoints"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); + drawstring([0.015*width, 0.305*height, 0], strcat("Press ", "-", " to save current waypoints"), [0.015*width, 0.015*width, 0], [1, 1, 1], 1, 0); +} + +/******************* +* HUD Draw * +*******************/ + +void(float width, float height) HUD_Draw = +{ + if (cvar("cl_cinematic")) + return; + + HUD_Achievements(width, height); + + if (!getstatf(STAT_SPECTATING) && (getstatf(STAT_HEALTH) > 10) && !score_show) + { + + if (vmodel.model == GetWeaponModel(getstatf(STAT_ACTIVEWEAPON), FALSE) && vmodel.model != "" || getstatf(STAT_WEAPONZOOM) == 2) + Draw_Crosshair(); + + if (!cvar("waypoint_mode")) { + HUD_Health(width, height); + HUD_Points(width, height); + HUD_Rounds(width, height); + HUD_Perks(width, height); + HUD_Progressbar(width, height); + HUD_Powerups(); + + if (vmodel.model == GetWeaponModel(getstatf(STAT_ACTIVEWEAPON), FALSE) && vmodel.model != "") + HUD_AmmoString(); + + if (HUD_Change_time > time) + { + HUD_Grenades(width, height); + + if (vmodel.model == GetWeaponModel(getstatf(STAT_ACTIVEWEAPON), FALSE) && vmodel.model != "") { + HUD_Ammo(width, height); + HUD_Weaponstring(width, height); + } + } + + if (useprint_time > time) + HUD_Useprint(width, height); + + if (Hitmark_time > time) + HUD_Hitmark(); + + if (stext) { + HUD_ScrollText(width, height); + } else { + scrollopacity = 1; + scrollheight = 0.80; + } + } else { + HUD_Waypoint(width, height); + } + } + + // Only keep broadcast messages outside in case they are important + if (broadcast_time > time) + HUD_Broadcast(); + + if (getstatf(STAT_HEALTH) <= 10 && getstatf(STAT_SPECTATING) && !find(world, classname, "ai_zombie")) + HUD_Endgame(); + + if (score_show) + HUD_Scores(); + + if (fade_time > time) + HUD_Fade(); +} + +void UpdatePerks(float newperks) { + float s; + + // avoids out of bounds err - moto + if (newperks == 0) + current_perk_order = 0; + + for(float i = 1; i < 129; i *= 2) { + if (newperks & i && !(perks & i)) { + perk_order[current_perk_order] = i; + current_perk_order += 1; + } + } + + for(float i = 1; i < 129; i *= 2) { + if (perks & i && !(newperks & i)) + { + for(s = 0; s < 8; s++) + { + if (perk_order[s] == i) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + } + } + + perks = newperks; +} diff --git a/source/client/main.qc b/source/client/main.qc new file mode 100644 index 0000000..cd60192 --- /dev/null +++ b/source/client/main.qc @@ -0,0 +1,1091 @@ +/* + client/main.qc + + main csqc code + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() ToggleMenu = +{ + if(serverkey("constate") != "disconnected") + { + if (player_count == 0) + localcmd("cmd pause\n"); + if(in_menu == MENU_NONE) + { + in_menu = MENU_PAUSE; + time_in_menu = 0; + local float i; + for(i = 0; i < BUTTONS_COUNT; i++) + { + buttons[i].active = 1; + } + setcursormode(TRUE, cvar_string("cl_cursor"), __NULL__, cvar("cl_cursor_scale")); + } + else + { + in_menu = MENU_NONE; + setcursormode(FALSE); + } + } + else + { + in_menu = MENU_MAIN; + time_in_menu = 0; + setcursormode(TRUE, cvar_string("cl_cursor"), __NULL__, cvar("cl_cursor_scale")); + } +} + +noref void(float apiver, string enginename, float enginever) CSQC_Init = +{ + setwindowcaption("Nazi Zombies: Portable"); + + precache_sound("sounds/menu/enter.wav"); + precache_sound("sounds/menu/navigate.wav"); + + registercommand("togglemenu"); + registercommand("startwalk"); + registercommand("stopwalk"); + registercommand("promptjoin"); + registercommand("showscores"); + + cvar_set("sv_cheats", ftos(1)); + + //print("CSQC Started\n"); + dummy = spawn(); + if(serverkey("constate") == "disconnected") + ToggleMenu(); + //bgpos = 0; + + // default button binds + + buttonBind[0] = "+forward"; + buttonBind[1] = "+back"; + buttonBind[2] = "+moveleft"; + buttonBind[3] = "+moveright"; + buttonBind[4] = "impulse 10"; + buttonBind[5] = "+sprint"; + buttonBind[6] = "impulse 30"; + buttonBind[7] = "impulse 110"; + buttonBind[8] = "impulse 111"; + buttonBind[9] = "+button7"; + buttonBind[10] = "+button5"; + buttonBind[11] = "+button6"; + buttonBind[12] = "+button3"; + buttonBind[13] = "impulse 25"; + + // intialize screen resolutions + + // default/current width and height + active_swidth = cvar("vid_width"); + active_sheight = cvar("vid_height"); + fullscreenval = cvar("vid_fullscreen_standalone"); + + // 5:4 + screen_width_54[0] = 1280; + screen_height_54[0] = 1024; + //4:3 + screen_width_43[0] = 640; + screen_height_43[0] = 480; + screen_width_43[1] = 800; + screen_height_43[1] = 600; + screen_width_43[2] = 1024; + screen_height_43[2] = 768; + //16:10 + screen_width_1610[0] = 1280; + screen_height_1610[0] = 800; + screen_width_1610[1] = 1440; + screen_height_1610[1] = 900; + screen_width_1610[2] = 1680; + screen_height_1610[2] = 1050; + screen_width_1610[3] = 1920; + screen_height_1610[3] = 1200; + screen_width_1610[4] = 2560; + screen_height_1610[4] = 1600; + //16:9 + screen_width_169[0] = 640; + screen_height_169[0] = 360; + screen_width_169[1] = 1280; + screen_height_169[1] = 720; + screen_width_169[2] = 1360; + screen_height_169[2] = 768; + screen_width_169[3] = 1366; + screen_height_169[3] = 768; + screen_width_169[4] = 1536; + screen_height_169[4] = 864; + screen_width_169[5] = 1600; + screen_height_169[5] = 900; + screen_width_169[6] = 1920; + screen_height_169[6] = 1080; + screen_width_169[7] = 2048; + screen_height_169[7] = 1152; + screen_width_169[8] = 2560; + screen_height_169[8] = 1440; + screen_width_169[9] = 3840; + screen_height_169[9] = 2160; + //21:9 + screen_width_219[0] = 2560; + screen_height_219[0] = 1080; + screen_width_219[1] = 3440; + screen_height_219[1] = 1440; + + // cvars for custom settings + cvar_set("cl_adsmode", "0"); + cvar_set("cl_cinematic", "0"); + cvar_set("nzp_particles", "1"); + cvar_set("nzp_decals", "1"); + + // per-channel volume + cvar_set("snd_channel1volume", "1"); + cvar_set("snd_channel2volume", "1"); + cvar_set("snd_channel3volume", "1"); + cvar_set("snd_channel4volume", "1"); + cvar_set("snd_channel5volume", "1"); + cvar_set("snd_channel6volume", "1"); + + // force nearest filtering for hud elements to avoid blur + cvar_set("gl_texturemode2d", "gl_nearest"); + cvar_set("r_font_linear", "0"); + + // force build date text in menu + cvar_set("cl_showbuildtime", "1"); + + // retrieve custom maps + Customs_Get(); + + // + // get the build date + // + float file = fopen("build_date.txt", FILE_READ); + + if (file != -1) { + build_datetime = fgets(file); + fclose(file); + } +}; + +noref void() CSQC_WorldLoaded = +{ + //precache_model("models/weapons/mg/mzl.iqm"); + //precache_model("models/blood.iqm"); + + if(!player) + player = edict_num(player_localnum); + if(!vmodel) + vmodel = spawn(); + if(!v2model) + v2model = spawn(); + if(!mzlflash) + mzlflash = spawn(); + + v2model.renderflags = vmodel.renderflags = RF_VIEWMODEL; + v2model.origin = vmodel.origin = '24 -12 -18'; + v2model_targetpos = v2model_currentpos = v2model.origin = vmodel_targetpos = vmodel_currentpos = vmodel.origin; + v2model_velocity = vmodel_velocity = '0 0 0'; + //v2model_muzzleoffset = vmodel_muzzleoffset = '48 -1 2'; + setmodel(vmodel,""); + setmodel(v2model,""); + mzlflash.renderflags = vmodel.renderflags; + mzlflash.origin = vmodel.origin + vmodel_muzzleoffset; + + playerpoints[1] = -1; + playerpoints[2] = -1; + playerpoints[3] = -1; + + Achievement_Init(); + + // Dummy so that our other point particles work! + pointparticles(particleeffectnum("weapons.impact_decal"), '0 0 0', '0 0 0', 1); + pointparticles(particleeffectnum("muzzle.muzzle_part0"), '0 0 0', '0 0 0', 1); + + huddir = "gfx/hud/"; +}; + +// This is from COW lol! +#define ADS_THOMPSON '-3 +5.80 +4.52' + +/*vector adsOffset; +float adsAmount; +float adsDir;*/ +vector weapon_bob_factor; +float weapon_bob_factor_z_coef; +vector dampening_factor; +float vaccel; +float vzaccel; +void() Update_Vmodel = +{ + local vector offset, adsoffset; + local vector dir; + local float ads; + + if (cvar("r_drawviewmodel") == FALSE) { + vmodel.origin = '-10000 -10000 -10000'; + return; + } + + //Walk bob + if (getstatf(STAT_WEAPONZOOM)) { //ADS bob + weapon_bob_factor_x = 0; + weapon_bob_factor_y = 0.4; + weapon_bob_factor_z = 0.2; + weapon_bob_factor_z_coef = 0.1; + + dampening_factor_x = 0.1*sin(time*weapon_bob_factor_x); + dampening_factor_y = 0.1*cos(time*weapon_bob_factor_y); + dampening_factor_z = 0.1*sin(time*weapon_bob_factor_z); + } else if (K_FORWARDDOWN || K_BACKDOWN || K_LEFTDOWN || K_RIGHTDOWN && !getstatf(STAT_WEAPONZOOM)) { + weapon_bob_factor_x = 8; + weapon_bob_factor_y = 8; + weapon_bob_factor_z = 13.4; + weapon_bob_factor_z_coef = 0.2; + + dampening_factor_x = 0.2*sin(time*weapon_bob_factor_x); + dampening_factor_y = 0.2*cos(time*weapon_bob_factor_y); + dampening_factor_z = 0.2*sin(time*weapon_bob_factor_z); + } else { //Still bob + + // Naievil -- stupid ass calcs... + // Basically we have a dampening factor per offset and we have to decrease it back down to zero + // Problem is that we may be going in postive OR negative direction? + // Better solution: use fabs() lol + if (dampening_factor_x > 0 && sin(time*weapon_bob_factor_x) > 0) { + if ((dampening_factor_x - 0.00000068* sin(time*weapon_bob_factor_x)) < 0) + dampening_factor_x -= (0.00000068 - dampening_factor_x*sin(time*weapon_bob_factor_x)); + else + dampening_factor_x -= 0.00000068* sin(time*weapon_bob_factor_x); + + } else if (dampening_factor_x < 0 && sin(time*weapon_bob_factor_x) < 0) { + if ((dampening_factor_x + 0.00000068* sin(time*weapon_bob_factor_x)) > 0) + dampening_factor_x += (0.00000068 - dampening_factor_x*sin(time*weapon_bob_factor_x)); + else + dampening_factor_x += 0.00000068* sin(time*weapon_bob_factor_x); + } else { + weapon_bob_factor_x = 1.2; + } + + if (weapon_bob_factor_x - 0.00000068 < 1.2) + weapon_bob_factor_x = 1.2; + else + weapon_bob_factor_x -= 0.00000068; + + + if (dampening_factor_y > 0 && cos(time*weapon_bob_factor_y) > 0) { + if ((dampening_factor_y - 0.00000065* cos(time*weapon_bob_factor_y)) < 0) + dampening_factor_y -= (0.00000065 - dampening_factor_y*cos(time*weapon_bob_factor_y)); + else + dampening_factor_y -= 0.00000065* cos(time*weapon_bob_factor_y); + + } else if (dampening_factor_y < 0 && cos(time*weapon_bob_factor_y) < 0) { + if ((dampening_factor_y + 0.00000065* cos(time*weapon_bob_factor_y)) > 0) + dampening_factor_y += (0.00000065 - dampening_factor_y*cos(time*weapon_bob_factor_y)); + else + dampening_factor_y += 0.00000065* cos(time*weapon_bob_factor_y); + } else { + weapon_bob_factor_y = 1.5; + } + + + if (weapon_bob_factor_y - 0.00000065 < 1.5) + weapon_bob_factor_y = 1.5; + else + weapon_bob_factor_y -= 0.00000065; + + if (dampening_factor_z > 0 && sin(time*weapon_bob_factor_z) > 0) { + if ((dampening_factor_z - 0.00000123* sin(time*weapon_bob_factor_z)) < 0) + dampening_factor_z -= (0.00000123 - dampening_factor_z*sin(time*weapon_bob_factor_z)); + else + dampening_factor_z -= 0.00000123* sin(time*weapon_bob_factor_z); + + } else if (dampening_factor_z <= 0 && sin(time*weapon_bob_factor_z) < 0) { + if ((dampening_factor_z + 0.00000123* sin(time*weapon_bob_factor_z)) > 0) + dampening_factor_z += (0.00000123 - dampening_factor_z*sin(time*weapon_bob_factor_z)); + else + dampening_factor_z += 0.00000123* sin(time*weapon_bob_factor_z); + } else { + weapon_bob_factor_z = 1.1; + } + + if (weapon_bob_factor_z - 0.00000123 < 1.1) + weapon_bob_factor_z = 1.1; + else + weapon_bob_factor_z -= 0.00000123; + + if (weapon_bob_factor_z_coef <= 0.1) + weapon_bob_factor_z_coef = 0.1; + else + weapon_bob_factor_z_coef -= 0.0000001*sin(time*weapon_bob_factor_z); + } + + offset_x = 0.2 * (sin(time * weapon_bob_factor_x) + dampening_factor_x); + offset_y = 0.2 * (cos(time * weapon_bob_factor_y) + dampening_factor_y); + offset_z = weapon_bob_factor_z_coef * (sin(weapon_bob_factor_z*time + 0.5) + dampening_factor_z); + + vector tempv; + tempv_x = 0.2 * (sin(time * weapon_bob_factor_x)); + tempv_y = 0.2 * (cos(time * weapon_bob_factor_y)); + tempv_z = weapon_bob_factor_z_coef * (sin(weapon_bob_factor_z*time + 0.5)); + + dir = vmodel_targetpos - vmodel_currentpos; + if(vlen(dir) < (0.15 * 128 * frametime)) + vmodel_currentpos = vmodel_targetpos; + else + vmodel_currentpos += (dir * 0.15 * 128) * frametime; + + + if(vlen(vmodel.angles) < (0.1 * 128 * frametime)) + vmodel.angles = '0 0 0'; + else + vmodel.angles += (-vmodel.angles * 0.2 * 128) * frametime; + + vmodel_currentpos += (vmodel_velocity * 128) * frametime; + vmodel_velocity *= 1 - frametime * 30; + + vmodel.angles += (vmodel_avelocity * 128) * frametime; + vmodel_avelocity *= 1 - frametime * 30; + + adsoffset = GetWeaponADSPos(weapon); + + ads = getstatf(STAT_WEAPONZOOM); //get the zoomtoggle value + if(ads == 1) + { + vmodel_currentpos += (adsoffset * 0.15 * 128) * frametime; + vmodel_currentpos += offset*0.025; + vmodel.origin = vmodel_currentpos + adsoffset; + } + else if (ads == 2) + { + vmodel_currentpos += (adsoffset * 0.15 * 128) * frametime; + vmodel_currentpos += offset*0.025; + vmodel.origin = vmodel_currentpos + adsoffset; + } + else + { + switch(getstatf(STAT_PLAYERSTANCE)) { + case 2: + break; + case 1: + vmodel_currentpos = '0 0.6 -0.3'; + break; + case 0: + vmodel_currentpos = '1.5 0.6 -0.3'; + break; + default: break; + } + vmodel.origin = vmodel_currentpos + offset; + + vmodel.origin_y += vaccel; + vmodel.origin_z += vzaccel; + + if (in_menu != MENU_NONE) + return; + + if (K_LEFTDOWN) { + vaccel -= 0.01; + if (vaccel < -0.8) + vaccel = -0.8; + } else if (K_RIGHTDOWN) { + vaccel += 0.01; + if (vaccel > 0.8) + vaccel = 0.8; + } else if (vaccel != 0) { + if (vaccel > 0) + vaccel -= 0.01; + else + vaccel += 0.01; + } + + //black ops -0.01 till -0.6 (looks better) + if (K_BACKDOWN || K_FORWARDDOWN) { + vzaccel -= 0.01; + + if (vzaccel < -0.6) + vzaccel = -0.6; + } else if (vzaccel != 0) { + if (vzaccel > 0) + vzaccel -= 0.02; + else + vzaccel += 0.02; + } + + } + + + if(weapon == 1) + { + if(mzlflash.scale < 1) + mzlflash.scale += (10 * random()) * frametime; + } + else if(weapon == 2) + { + if(mzlflash.scale < 1.5) + mzlflash.scale += (20 * random()) * frametime; + } + mzlflash.origin = vmodel.origin + vmodel_muzzleoffset; + mzlflash.alpha -= (0.15 * 100) * frametime; + if(mzlflash.alpha < 0.1) + mzlflash.alpha = 0.01; +} + +noref void(float isnew) CSQC_Ent_Update = +{ + if(isnew) + addentity(self); +} + + +// CALLED EVERY CLIENT RENDER FRAME +noref void(float width, float height, float menushown) CSQC_UpdateView = +{ + //clear and update our global screen resolution vars + clearscene(); + g_width = width; + g_height = height; + + // camang is controlled by our punchangles + camang = getproperty(VF_ANGLES); + + //disable quake status bar and quake crosshair + setviewprop(VF_DRAWENGINESBAR, 0); + setviewprop(VF_DRAWCROSSHAIR, 0); + + setsensitivityscaler(getstatf(STAT_VIEWZOOM)); + setviewprop(VF_AFOV, autocvar(fov,90)*getstatf(STAT_VIEWZOOM)); + + //autoadd entities received from servers for drawing + addentities(MASK_ENGINE); + + //do viewmodel manipulation, purely cosmetic stuff + if(vmodel) + { + Update_Vmodel(); + local vector vorg; + local vector vang; + vorg = getviewprop(VF_ORIGIN); + vang = getviewprop(VF_ANGLES); + //vmodel.origin += vorg; + //vmodel.angles += vang; + addentity(vmodel); + addentity(v2model); + //vmodel.origin -= vorg; + //vmodel.angles -= vang; + //addentity(vmodel); + float weaponframe, weapon2frame; + weaponframe = getstatf(STAT_WEAPONFRAME); + + float duration = getstatf(STAT_WEAPONDURATION); + + if (!duration) { + duration = 0.1; + } + + if (curweaponframe != weaponframe) { + interpolating = TRUE; + vmodel.lerpfrac = 0; + curweaponframe = weaponframe; + } + + if (interpolating == TRUE) { + vmodel.lerpfrac += frametime * (1/duration); + + if (vmodel.lerpfrac >= 1.0) { + oldweaponframe = curweaponframe; + vmodel.lerpfrac = 0; + interpolating = FALSE; + } + + } + vmodel.frame = oldweaponframe; + vmodel.frame2 = curweaponframe; + + if (IsDualWeapon(getstatf(STAT_ACTIVEWEAPON))) { + float duration2 = getstatf(STAT_WEAPON2DURATION); + if (!duration2) { + duration2 = 0.1; + } + weapon2frame = getstatf(STAT_WEAPON2FRAME); + if (curweapon2frame != weapon2frame) { + interpolating2 = TRUE; + v2model.lerpfrac = 0; + curweapon2frame = weapon2frame; + } + + if (interpolating2 == TRUE) { + v2model.lerpfrac += frametime * (1/duration2); + + if (v2model.lerpfrac >= 1.0) { + oldweapon2frame = curweapon2frame; + v2model.lerpfrac = 0; + interpolating2 = FALSE; + } + + } + + v2model.frame = oldweapon2frame; + v2model.frame2 = curweapon2frame; + + } else { + //v2model.frame = getstatf(STAT_WEAPON2FRAME); + v2model.lerpfrac = vmodel.lerpfrac; + v2model.frame = oldweaponframe; + v2model.frame2 = curweaponframe; + } + v2model.angles = vmodel.angles; + v2model.origin = vmodel.origin; + if(mzlflash.alpha > 0.09) + { + makevectors(view_angles); + local vector offset = vmodel.origin + vmodel_muzzleoffset; + local vector muzzlepos; + muzzlepos = getviewprop(VF_ORIGIN); + muzzlepos += v_forward * offset_x; + muzzlepos -= v_right * offset_y; + muzzlepos += v_up * (offset_z + 6); + + if (cvar("r_drawviewmodel")) { + if (IsPapWeapon(getstatf(STAT_ACTIVEWEAPON))) + dynamiclight_add(muzzlepos, 128 * mzlflash.alpha, '0.7 0 0.7' * mzlflash.alpha); + else + dynamiclight_add(muzzlepos, 128 * mzlflash.alpha, '1.2 0.7 0.2' * mzlflash.alpha); + + addentity(mzlflash); + } + } + } + + //deltalisten makes engine call the "add_outline" func for each entity with the set model every frame + /*deltalisten("models/mg_ammo.iqm", add_outline, 0); + deltalisten("models/weapons/mg/mg.iqm", add_outline, 0); + deltalisten("models/weapons/pistol/pistol.iqm", add_outline, 0);*/ + //deltalisten("models/humanoid_simplerig.iqm", add_outline, 0); + + //does what you think it does + renderscene(); + + if(in_menu) + { + //in menu.qc + Draw_Menu(); + } + else + { + HUD_Draw(g_width, g_height); + } + if(serverkey("constate") != "active" && serverkey("disconnected")) + { + drawfill('0 0 0', [width, height, 0], '0.2 0.4 0.7', 1, 0); + drawstring([width/2 - 60, height/2, 0], "Loading...", [16,16,0],[1,1,1],1,0); + } +}; + +noref float(string cmd) CSQC_ConsoleCommand = +{ + //self = theplayer; + //if (!self) + // return FALSE; + + tokenize(cmd); + switch(argv(0)) + { + case "togglemenu": + ToggleMenu(); + return TRUE; + break; + case "map": + return FALSE; + break; + case "startwalk": + walk = TRUE; + return FALSE; + case "stopwalk": + walk = FALSE; + return FALSE; + case "promptjoin": + menu_join(); + return TRUE; + case "showscores": + if (score_show) + score_show = FALSE; + else + score_show = TRUE; + return TRUE; + break; + default: + return FALSE; + } + return FALSE; +}; +//**********************************************************************// +// Input_Movecheck // +// // +// Called at InputEvent and allows to set var if key is at that state // +// NOTE: ALL movekeys are called in order to prevent unsetting keys // +//**********************************************************************// + +void(float scanx, float setval) Input_Movecheck = +{ + tokenize(findkeysforcommand("+moveleft")); + if (scanx == stof(argv(0))) + K_LEFTDOWN = setval; + + tokenize(findkeysforcommand("+moveright")); + if (scanx == stof(argv(0))) + K_RIGHTDOWN = setval; + + tokenize(findkeysforcommand("+forward")); + if (scanx == stof(argv(0))) + K_FORWARDDOWN = setval; + + tokenize(findkeysforcommand("+back")); + if (scanx == stof(argv(0))) + K_BACKDOWN = setval; +} + +void(float button, string key) setToBind = +{ + local string fullbind, unbind, oldkey; + local string btn; + editBind[button] = FALSE; + + btn = buttonBind[button]; + + tokenize(findkeysforcommand(btn)); + oldkey = keynumtostring(stof(argv(0))); + + unbind = strcat("bind ", oldkey, " null\n"); + fullbind = strcat("bind ", key, " \"", btn, "\"\n"); + + localcmd(unbind); + localcmd(fullbind); +} + +noref float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent = +{ + switch(evtype) + { + case IE_KEYDOWN: + if(in_menu != MENU_NONE) + { + if(scanx == K_MOUSE1) { + Menu_Click(0); + + if (in_menu == MENU_CUSTOMS) + Menu_Click_Custom(); + return TRUE; + } else if (scanx == K_MOUSE2) { + Menu_Click(1); + return TRUE; + } + + if (in_menu == MENU_CSETTINGS) { + for (int i = 0; i < MAX_BINDS; i += 1) { + if (editBind[i] == TRUE) + setToBind(i, keynumtostring(scanx)); + } + } + + // Controller Menu Navigation + if (scanx == K_JOY2) { + GPActive[0] = TRUE; + } + + if (scanx == K_JOY4) { + GPActive[1] = TRUE; + } + + if (scanx == K_JOY1) { + GPActive[2] = TRUE; + } + + } + + Input_Movecheck(scanx, 1); + + return FALSE; + case IE_KEYUP: + + // Controller Menu Navigation + if(in_menu != MENU_NONE) { + if (scanx == K_JOY2) { + GPActive[0] = FALSE; + } + + if (scanx == K_JOY4) { + GPActive[1] = FALSE; + } + + if (scanx == K_JOY1) { + GPActive[2] = FALSE; + } + } + + + Input_Movecheck(scanx, 0); + + return FALSE; + case IE_MOUSEDELTA: + return FALSE; + case IE_MOUSEABS: + //if(devid != 0 && devid != 1) + // return FALSE; + cursor_pos_x = scanx; + cursor_pos_y = chary; + return FALSE; + } + return FALSE; +}; + +noref void() CSQC_Input_Frame = +{ + if(walk) + { + input_movevalues *= 0.5; + } +} + +float() tracerthink = +{ + //makevectors(self.angles); + local vector cross1 = normalize(crossproduct( getviewprop(VF_ORIGIN) - self.targetpos, self.targetpos - self.origin)); + local vector cross2 = normalize(crossproduct( getviewprop(VF_ORIGIN) - self.origin, self.targetpos - self.origin)); + R_BeginPolygon("tracers/mg"); + + R_PolygonVertex(self.origin + cross2, '0 1 0', '1 1 1', self.alpha - 0.6); + R_PolygonVertex(self.origin - cross2, '0 0 0', '1 1 1', self.alpha - 0.6); + R_PolygonVertex(self.targetpos - cross1, '1 0 0', '1 1 1', self.alpha); + + R_PolygonVertex(self.targetpos + cross1, '1 1 0', '1 1 1', self.alpha); + R_PolygonVertex(self.origin + cross2, '0 1 0', '1 1 1', self.alpha - 0.6); + R_PolygonVertex(self.targetpos - cross1, '1 0 0', '1 1 1', self.alpha); + + /*R_PolygonVertex(self.origin + v_right, '0 1 0', '1 1 1', self.alpha - 0.4); + R_PolygonVertex(self.origin - v_right, '0 0 0', '1 1 1', self.alpha - 0.4); + R_PolygonVertex(self.targetpos - v_right, '1 0 0', '1 1 1', self.alpha); + + R_PolygonVertex(self.targetpos + v_right, '1 1 0', '1 1 1', self.alpha); + R_PolygonVertex(self.origin + v_right, '0 1 0', '1 1 1', self.alpha - 0.4); + R_PolygonVertex(self.targetpos - v_right, '1 0 0', '1 1 1', self.alpha);*/ + + + R_EndPolygon(); + + self.alpha -= (frametime * self.rate); + if(self.alpha <= 0) + remove(self); + + return 0; +} + +float() alphafade = +{ + + self.alpha -= (frametime * 3); + self.scale += (frametime * 9); + if(self.alpha <= 0.05) + remove(self); + + local vector cross1 = 10 * self.scale * normalize(crossproduct( getviewprop(VF_ORIGIN) - self.origin + self.targetpos * self.scale, self.targetpos * self.scale)); + local vector cross2 = 10 * self.scale * normalize(crossproduct( getviewprop(VF_ORIGIN) - self.origin, self.targetpos * self.scale)); + R_BeginPolygon("bloodsplat2"); + + R_PolygonVertex(self.origin + cross2, '0 1 0', '1 1 1', self.alpha); + R_PolygonVertex(self.origin - cross2, '0 0 0', '1 1 1', self.alpha); + R_PolygonVertex(self.origin + (self.targetpos*self.scale*18) - cross1, '1 0 0', '1 1 1', self.alpha); + + R_PolygonVertex(self.origin + (self.targetpos*self.scale*18) + cross1, '1 1 0', '1 1 1', self.alpha); + R_PolygonVertex(self.origin + cross2, '0 1 0', '1 1 1', self.alpha); + R_PolygonVertex(self.origin + (self.targetpos*self.scale*18) - cross1, '1 0 0', '1 1 1', self.alpha); + + R_EndPolygon(); + + return 0; +} + +noref void() CSQC_Parse_Event = +{ + local float first = readbyte(); + + switch (first) { + case EVENT_PISTOLFIRE: + local float entnum, traceent, side; + local vector pos, norm; + entnum = readentitynum(); + side = readfloat(); + pos_x = readcoord(); + pos_y = readcoord(); + pos_z = readcoord(); + norm_x = readcoord(); + norm_y = readcoord(); + norm_z = readcoord(); + + traceent = readentitynum(); + + if(entnum == player_localentnum) + { + makevectors(view_angles); + local vector offset = vmodel.origin + vmodel_muzzleoffset; + local vector muzzlepos; + muzzlepos = getviewprop(VF_ORIGIN); + + local vector muzzle_offset = GetWeaponFlash_Offset(getstatf(STAT_ACTIVEWEAPON))/1000; + + // ADS offset + if(getstatf(STAT_WEAPONZOOM) == 1) + { + muzzle_offset += GetWeaponADSPos(weapon); + muzzle_offset[0] -= 2; + } + + // Firing from the left? Flip x-axis for muzzleflash + if (side == 0 && IsDualWeapon(getstatf(STAT_ACTIVEWEAPON))) + muzzle_offset[0] = -muzzle_offset[0]; + + muzzlepos += v_forward * muzzle_offset_z; + muzzlepos += v_right * muzzle_offset_x; + muzzlepos += v_up * muzzle_offset_y; + mzlflash.alpha = 1; + + if (cvar("nzp_particles") && cvar("r_drawviewmodel")) { + if (IsPapWeapon(getstatf(STAT_ACTIVEWEAPON))) + pointparticles(particleeffectnum("muzzle.muzzle_pap_part0"), muzzlepos, norm*24, 1); + else + pointparticles(particleeffectnum("muzzle.muzzle_part0"), muzzlepos, norm*24, 1); + } + } + + if(traceent == 0 && GetFiretype(getstatf(STAT_ACTIVEWEAPON)) != 3) + { + if (cvar("nzp_particles")) + pointparticles(particleeffectnum("weapons.impact"), pos, norm*24, 1); + + if (cvar("nzp_decals")) + pointparticles(particleeffectnum("weapons.impact_decal"), pos, '0 0 0', 1); + } + else + { + local vector entorg; + entorg = getentity(traceent, GE_ORIGIN); + norm = (norm * 0.25) + (normalize(pos - (entorg + '0 0 12')) * 0.75); + local entity splat = spawn(); + splat.drawmask = 1; + splat.alpha = 1; + splat.scale = 0.05; + splat.angles = [random()*360, random()*360, random()*360]; + splat.targetpos = norm * 1.5; + splat.predraw = alphafade; + } + break; + case EVENT_EXPLOSION: + local vector org; + org_x = readcoord(); + org_y = readcoord(); + org_z = readcoord(); + + if (cvar("nzp_decals")) + pointparticles(particleeffectnum("weapons.explode"), org, '0 0 0', 1); + break; + case EVENT_BLOOD: + local vector loc; + + loc_x = readcoord(); + loc_y = readcoord(); + loc_z = readcoord(); + + if (cvar("nzp_particles")) + pointparticles(particleeffectnum("blood.blood_particle"), loc, '0 0 0', 1); + break; + case EVENT_WEAPONCHANGE: + local float to; + to = readbyte(); + setmodel(vmodel,GetWeaponModel(to, FALSE)); + + if (to == W_KAR_SCOPE && vmodel.model == "models/weapons/kar/v_kar.mdl") { + setmodel(v2model,"models/weapons/kar/v_karscope.mdl"); + } else { + setmodel(v2model,""); + } + + v2model.origin = vmodel.origin = '0 0 0'; + v2model.angles = vmodel.angles = '-60 0 0'; + v2model_currentpos = vmodel_currentpos = vmodel.origin + '0 0 -24'; + v2model_targetpos = vmodel_targetpos = vmodel.origin; + v2model_muzzleoffset = vmodel_muzzleoffset = '12 0 1'; + weapon = to; + + HUD_Change_time = time + 6; + break; + case EVENT_UPDATEVMODEL: + local string new; + local float skin2; + new = readstring(); + setmodel(vmodel,new); + vmodel.skin = readbyte(); + break; + case EVENT_UPDATEV2MODEL: + local string new2; + new2 = readstring(); + setmodel(v2model,new2); + v2model.skin = readbyte(); + break; + case EVENT_USEPRINT: + useprint_type = readbyte(); + useprint_cost = readshort(); + useprint_weapon = readbyte(); + + useprint_time = time + 0.1; + break; + + case EVENT_NEWROUND: + rounds = readbyte(); + HUD_Change_time = time + 6; + break; + case EVENT_SETROUND: + rounds = readbyte(); + break; + case EVENT_PERK: + float newperks; + + newperks = readlong(); + + UpdatePerks(newperks); + break; + case EVENT_UPDATE: + float updatetype = readbyte(); + float var_1 = readbyte(); + float var_2 = readbyte(); + float var_3 = readbyte(); + + switch (updatetype) + { + case 1: + HUD_Change_time = time + var_1; + break; + case 2: + rounds_change = var_1; + break; + case 3: + if (Hitmark_time < time) + Hitmark_time = time + 0.2; + break; + case 4: + zoom_2_time = time + 0.20; + break; + case 5: + crosshair_spread_time = time + 70/getWeaponRecoilReturn(getstatf(STAT_ACTIVEWEAPON)); + break; + default: + break; + } + break; + case EVENT_BROADCAST: + broadcast_time = readbyte(); + broadcast_type = readbyte(); + break; + case EVENT_POINTUPDATE: + float playernum = readbyte(); + float temppoints = readlong(); + RegisterPointChange(readlong()); + float tempkills = readlong(); + string tempname = readstring(); + + switch(playernum) { + case 1: playerpoints[0] = temppoints; playerkills[0] = tempkills; playernames[0] = tempname; break; + case 2: playerpoints[1] = temppoints; playerkills[1] = tempkills; playernames[1] = tempname; break; + case 3: playerpoints[2] = temppoints; playerkills[2] = tempkills; playernames[2] = tempname; break; + case 4: playerpoints[3] = temppoints; playerkills[3] = tempkills; playernames[3] = tempname; break; + } + break; + case EVENT_BLACKOUT: + fade_time = readbyte(); + fade_type = readbyte(); + break; + case EVENT_PUNCHANGLE: + vector changetest; + changetest_x = readcoord(); + changetest_y = readcoord(); + changetest_z = readcoord(); + break; + case EVENT_SCROLLTEXT: + scrolltext = readstring(); + stext = TRUE; + break; + case EVENT_WORLDDATA: + chaptertitle = readstring(); + location = readstring(); + date = readstring(); + person = readstring(); + + if (chaptertitle == "") + chaptertitle = "'Nazi Zombies'"; + break; + case EVENT_ACHIEVEMENT: + float ach = readbyte(); + + if (achievements[ach].unlocked == true) + return; + Achievement_Unlock(ach); + break; + case EVENT_ACHIEVEMENTPROGRESS: + float id = readbyte(); + float pg = readfloat(); + + if (achievements[id].unlocked == true) + return; + + Achievement_UpdateProgress(id, pg); + break; + case EVENT_PLAYERUPDATE: + player_count = readbyte(); + break; + case EVENT_WEAPONUPDATE: + float wepnum = readbyte(); + string wepname = readstring(); + string wvmodel = readstring(); + float mag = readbyte(); + float reserve = readbyte(); + vector cads = stov(readstring()); + float cmin = readbyte(); + float cmax = readbyte(); + vector flash = stov(readstring()); + float flashsize = readbyte(); + string v2 = readstring(); + float isd = readbyte(); + /*W_CUSTOMNAME[wepnum] = wepname; + W_CVMODEL[wepnum] = wvmodel; + W_CMAG[wepnum] = mag; + W_CRESERVE[wepnum] = reserve; + W_CCROSSMIN[wepnum] = cmin; + W_CCROSSMAX[wepnum] = cmax; + W_CADS[wepnum] = cads; + W_CFLASHSIZE[wepnum] = flashsize; + W_CVMODEL2[wepnum] = v2; + W_CDUAL[wepnum] = isd;*/ + break; + case EVENT_HUDUPDATE: + G_HUD = readstring(); + G_HUDHOR = readbyte(); + + if (G_HUD != "") + huddir = strcat("gfx/hud/", G_HUD, "/"); + break; + } +} diff --git a/source/client/menu.qc b/source/client/menu.qc new file mode 100644 index 0000000..39b2d1f --- /dev/null +++ b/source/client/menu.qc @@ -0,0 +1,1932 @@ +/* + client/menu.qc + + menu stuff (very ugly) + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +float current_custom_map_page; +float custom_map_pages; +float last_active_custom_select; +float active_custom_select; + + +void() menu_single = +{ + in_menu = MENU_SINGLE; + time_in_menu = 0; +}; + +void() menu_restart = +{ + in_menu = MENU_RES; +}; + +void() menu_resy = +{ + localcmd("restart\n"); +}; + +void() menu_resn = +{ + in_menu = MENU_PAUSE; +}; + +void() menu_multi = +{ + in_menu = MENU_MULTI; + time_in_menu = 0; +}; + +void() menu_settings = +{ + if (in_menu == MENU_PAUSE) + in_menu = MENU_IGS; + else + in_menu = MENU_SETTINGS; +}; + +void() menu_about = +{ + in_menu = MENU_ABOUT; + time_in_menu = 0; +}; + +void() menu_quit = +{ + localcmd("quit\n"); +}; + +void() menu_back = +{ + if (in_menu == MENU_GSETTINGS || in_menu == MENU_CSETTINGS) { + if (wasigs == TRUE) + in_menu = MENU_IGS; + else + in_menu = MENU_SETTINGS; + + wasigs = FALSE; + } else if (in_menu == MENU_IGS) { + in_menu = MENU_PAUSE; + } else if (in_menu == MENU_CUSTOMS) { + in_menu = MENU_SINGLE; + } else + in_menu = MENU_MAIN; +}; + +void() menu_loadndu = +{ + localcmd("map ndu\n"); +}; + +void() menu_loadwh = +{ + localcmd("map warehouse\n"); +}; + +void() menu_loadch = +{ + localcmd("map christmas_special\n"); +}; + +void() menu_loadwn = +{ + localcmd("map wahnsinn\n"); +}; + +void() menu_loadkn = +{ + localcmd("map kino\n"); +}; + +void() menu_join = +{ + setcursormode(TRUE, cvar_string("cl_cursor"), __NULL__, cvar("cl_cursor_scale")); + in_menu = MENU_JOIN; +}; + +void() game_join = +{ + localcmd("cmd joingame\n"); + in_menu = MENU_NONE; + setcursormode(FALSE); +}; + +void() game_spec = +{ + localcmd("cmd specgame\n"); + in_menu = MENU_NONE; + setcursormode(FALSE); +}; + +void() menu_resume = +{ + if (player_count == 0) + localcmd("pause"); + in_menu = MENU_NONE; + setcursormode(FALSE); +}; + +void() menu_main = +{ + in_menu = MENU_MAIN; + localcmd("disconnect\n"); +}; + +void() menu_graphics = +{ + if (in_menu == MENU_IGS) + wasigs = TRUE; + else + wasigs = FALSE; + + in_menu = MENU_GSETTINGS; +}; + +void() menu_consettings = +{ + if (in_menu == MENU_IGS) + wasigs = TRUE; + else + wasigs = FALSE; + + in_menu = MENU_CONSETTINGS; +} + +void() menu_audsettings = +{ + if (in_menu == MENU_IGS) + wasigs = TRUE; + else + wasigs = FALSE; + + in_menu = MENU_AUDSETTINGS; +} + +void() menu_controls = +{ + if (in_menu == MENU_IGS) + wasigs = TRUE; + else + wasigs = FALSE; + + in_menu = MENU_CSETTINGS; +} + +void() menu_customs = +{ + in_menu = MENU_CUSTOMS; + customs_interact = FALSE; +} + +void() menu_achievements = +{ + in_menu = MENU_ACHIEVEMENTS; +} + +void() menu_waypoint = +{ + localcmd("waypoint_mode 1"); +}; + +// +//settings adjustments +// + +void() settings_fps = { + local float fps = cvar("show_fps"); + + if (fps == 0) + fps = 1; + else + fps = 0; + + cvar_set("show_fps", ftos(fps)); +} + +void() settings_brite = { + local float br = cvar("gamma"); + + br += 0.1; + + if (br > 2) { + br = 2; + } + + cvar_set("gamma", ftos(br)); +} + +void() settings_brite2 = { + local float br = cvar("gamma"); + + br -= 0.1; + + if (br < 0.5) { + br = 0.5; + } + + cvar_set("gamma", ftos(br)); +} + +void() settings_fov = { + // Increase FOV by 5 + local float fov = cvar("fov"); + + if (fov < 120) + fov += 5; + + cvar_set("fov", ftos(fov)); +} + +void() settings_fov2 = { + // Decrease FOV by 5 + local float fov = cvar("fov"); + + if (fov > 50) + fov -= 5; + + cvar_set("fov", ftos(fov)); +} + +void() settings_mfps = { + // Increase max fps by 5 + if (cvar("vid_vsync")) + return; + + local float fps = cvar("cl_maxfps"); + + if (fps < 500) + fps += 5; + + cvar_set("cl_maxfps", ftos(fps)); +} + +void() settings_mfps2 = { + // Decrease max fps by 5 + if (cvar("vid_vsync")) + return; + + local float fps = cvar("cl_maxfps"); + + if (fps > 20) + fps -= 5; + + cvar_set("cl_maxfps", ftos(fps)); +} + +void() settings_vs = { + local float vs = cvar("vid_vsync"); + + if (vs == 0) + vs = 1; + else + vs = 0; + + cvar_set("vid_vsync", ftos(vs)); +} + +void() settings_fs = { + if (fullscreenval == 0) + fullscreenval = 1; + else + fullscreenval = 0; +} + +void() settings_dc = { + local float dc = cvar("nzp_decals"); + + if (dc == 0) + dc = 1; + else + dc = 0; + + cvar_set("nzp_decals", ftos(dc)); +} + +void() settings_pt = { + local float pt = cvar("nzp_particles"); + + if (pt == 0) + pt = 1; + else + pt = 0; + + cvar_set("nzp_particles", ftos(pt)); +} + +void() settings_fb = { + local float fb = cvar("r_fullbright"); + + if (fb == 0) + fb = 1; + else + fb = 0; + + cvar_set("r_fullbright", ftos(fb)); +} + +void() settings_ro = { + if (cvar_string("gl_texturemode") == "gl_nearest") + cvar_set("gl_texturemode", "gl_linear_mipmap_linear"); + else + cvar_set("gl_texturemode", "gl_nearest"); +} + +void() UpdateResolutions = { + switch(aspectratio) { + case 0: + active_swidth = screen_width_43[0]; + active_sheight = screen_height_43[0]; + break; + case 1: + active_swidth = screen_width_54[0]; + active_sheight = screen_height_54[0]; + break; + case 2: + active_swidth = screen_width_169[0]; + active_sheight = screen_height_169[0]; + break; + case 3: + active_swidth = screen_width_1610[0]; + active_sheight = screen_height_1610[0]; + break; + case 4: + active_swidth = screen_width_219[0]; + active_sheight = screen_height_219[0]; + break; + } +} + +void() settings_ar = { + // aspect ratio up + // 0 - 4:3 + // 1 - 5:4 + // 2 - 16:9 + // 3 - 16:10 + // 4 - 21:9 + aspectratio++; + + if (aspectratio > 4) + aspectratio = 0; + + UpdateResolutions(); +} + +void() settings_ar2 = { + // aspect ratio down + // 0 - 4:3 + // 1 - 5:4 + // 2 - 16:9 + // 3 - 16:10 + // 4 - 21:9 + aspectratio--; + + if (aspectratio < 0) + aspectratio = 4; + + UpdateResolutions(); +} + +void() settings_re = { + float neww, newh; + neww = 0; + newh = 0; + + switch(aspectratio) { + case 0: + if (active_swidth == screen_width_43[MAX_43-1]) { + neww = screen_width_43[0]; + newh = screen_height_43[0]; + } else { + for(float i = 0; i < MAX_43; i++) { + if (active_swidth == screen_width_43[i]) { + neww = screen_width_43[i+1]; + newh = screen_height_43[i+1]; + } + } + + // if still 0, different aspect ratio + if (neww == 0) { + neww = screen_width_43[0]; + newh = screen_height_43[0]; + } + } + break; + case 1: + if (active_swidth == screen_width_43[MAX_54-1]) { + neww = screen_width_54[0]; + newh = screen_height_54[0]; + } else { + for(float i = 0; i < MAX_54; i++) { + if (active_swidth == screen_width_54[i]) { + neww = screen_width_54[i+1]; + newh = screen_height_54[i+1]; + } + } + + // if still 0, different aspect ratio + if (neww == 0) { + neww = screen_width_54[0]; + newh = screen_height_54[0]; + } + } + break; + case 2: + if (active_swidth == screen_width_169[MAX_169-1]) { + neww = screen_width_169[0]; + newh = screen_height_169[0]; + } else { + for(float i = 0; i < MAX_169; i++) { + if (active_swidth == screen_width_169[i]) { + neww = screen_width_169[i+1]; + newh = screen_height_169[i+1]; + } + } + + // if still 0, different aspect ratio + if (neww == 0) { + neww = screen_width_169[0]; + newh = screen_height_169[0]; + } + } + break; + case 3: + if (active_swidth == screen_width_1610[MAX_1610-1]) { + neww = screen_width_1610[0]; + newh = screen_height_1610[0]; + } else { + for(float i = 0; i < MAX_1610; i++) { + if (active_swidth == screen_width_1610[i]) { + neww = screen_width_1610[i+1]; + newh = screen_height_1610[i+1]; + } + } + + // if still 0, different aspect ratio + if (neww == 0) { + neww = screen_width_1610[0]; + newh = screen_height_1610[0]; + } + } + break; + case 4: + if (active_swidth == screen_width_219[MAX_219-1]) { + neww = screen_width_219[0]; + newh = screen_height_219[0]; + } else { + for(float i = 0; i < MAX_219; i++) { + if (active_swidth == screen_width_219[i]) { + neww = screen_width_219[i+1]; + newh = screen_height_219[i+1]; + } + } + + // if still 0, different aspect ratio + if (neww == 0) { + neww = screen_width_219[0]; + newh = screen_height_219[0]; + } + } + break; + default: + break; + } + + active_swidth = neww; + active_sheight = newh; +} + +void() settings_apply = { + if (in_menu == MENU_GSETTINGS) { + localcmd(strcat("vid_width ", ftos(active_swidth), "\n")); + localcmd(strcat("vid_height ", ftos(active_sheight), "\n")); + localcmd(strcat("vid_fullscreen_standalone ", ftos(fullscreenval), "\n")); + localcmd("vid_restart\n"); + } + +} + +void() bind_walkf = { + if (editBind[0] == FALSE) + editBind[0] = TRUE; + else + editBind[0] = FALSE; +} + +void() bind_walkb = { + if (editBind[1] == FALSE) + editBind[1] = TRUE; + else + editBind[1] = FALSE; +} + +void() bind_walkl = { + if (editBind[2] == FALSE) + editBind[2] = TRUE; + else + editBind[2] = FALSE; +} + +void() bind_walkr = { + if (editBind[3] == FALSE) + editBind[3] = TRUE; + else + editBind[3] = FALSE; +} + +void() bind_jump = { + if (editBind[4] == FALSE) + editBind[4] = TRUE; + else + editBind[4] = FALSE; +} + +void() bind_sprint = { + if (editBind[5] == FALSE) + editBind[5] = TRUE; + else + editBind[5] = FALSE; +} + +void() bind_stance = { + if (editBind[6] == FALSE) + editBind[6] = TRUE; + else + editBind[6] = FALSE; +} + +void() bind_wpnn = { + if (editBind[7] == FALSE) + editBind[7] = TRUE; + else + editBind[7] = FALSE; +} + +void() bind_wpnp = { + if (editBind[8] == FALSE) + editBind[8] = TRUE; + else + editBind[8] = FALSE; +} + +void() bind_interact = { + if (editBind[9] == FALSE) + editBind[9] = TRUE; + else + editBind[9] = FALSE; +} + +void() bind_reload = { + if (editBind[10] == FALSE) + editBind[10] = TRUE; + else + editBind[10] = FALSE; +} + +void() bind_knife = { + if (editBind[11] == FALSE) + editBind[11] = TRUE; + else + editBind[11] = FALSE; +} + +void() bind_nade = { + if (editBind[12] == FALSE) + editBind[12] = TRUE; + else + editBind[12] = FALSE; +} + +void() bind_switchnade = { + if (editBind[13] == FALSE) + editBind[13] = TRUE; + else + editBind[13] = FALSE; +} + +float next_gray_out; +float back_gray_out; + +void() menu_custom_next = +{ + if (in_menu == MENU_CUSTOMS) { + if (current_custom_map_page < custom_map_pages) { + current_custom_map_page++; + back_gray_out = 0; + + if (current_custom_map_page == custom_map_pages) + next_gray_out = 1; + } + } else { + if (current_achievement_page < achievement_pages) { + current_achievement_page++; + back_gray_out = 0; + + if (current_achievement_page == achievement_pages) + next_gray_out = 1; + } + } +} + +void() menu_custom_back = +{ + if (in_menu == MENU_CUSTOMS) { + if (current_custom_map_page > 1) { + current_custom_map_page--; + next_gray_out = 0; + + if (current_custom_map_page == 1) + back_gray_out = 1; + } + } else { + if (current_achievement_page > 1) { + current_achievement_page--; + next_gray_out = 0; + + if (current_achievement_page == 1) + back_gray_out = 1; + } + } +} + +void() settings_sens = { + float sens = cvar("sensitivity"); + + sens += 0.5; + + cvar_set("sensitivity", ftos(sens)); +} + +void() settings_sens2 = { + float sens = cvar("sensitivity"); + + sens -= 0.5; + + cvar_set("sensitivity", ftos(sens)); +} + +void() settings_adsmode = +{ + float adsmode = cvar("cl_adsmode"); + + if (adsmode == 0) + cvar_set("cl_adsmode", ftos(1)); + else + cvar_set("cl_adsmode", ftos(0)); +} + +void() setting_mastervol = +{ + float vol = cvar("volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("volume", ftos(vol)); +} + +void() setting_mastervol2 = +{ + float vol = cvar("volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("volume", ftos(vol)); +} + +void() setting_chann1vol = +{ + float vol = cvar("snd_channel1volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("snd_channel1volume", ftos(vol)); +} + +void() setting_chann1vol2 = +{ + float vol = cvar("snd_channel1volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("snd_channel1volume", ftos(vol)); +} + +void() setting_chann2vol = +{ + float vol = cvar("snd_channel2volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("snd_channel2volume", ftos(vol)); +} + +void() setting_chann2vol2 = +{ + float vol = cvar("snd_channel2volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("snd_channel2volume", ftos(vol)); +} + +void() setting_chann3vol = +{ + float vol = cvar("snd_channel3volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("snd_channel3volume", ftos(vol)); +} + +void() setting_chann3vol2 = +{ + float vol = cvar("snd_channel3volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("snd_channel3volume", ftos(vol)); +} + +void() setting_chann4vol = +{ + float vol = cvar("snd_channel4volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("snd_channel4volume", ftos(vol)); +} + +void() setting_chann4vol2 = +{ + float vol = cvar("snd_channel4volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("snd_channel4volume", ftos(vol)); +} + +void() setting_chann5vol = +{ + float vol = cvar("snd_channel5volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("snd_channel5volume", ftos(vol)); +} + +void() setting_chann5vol2 = +{ + float vol = cvar("snd_channel5volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("snd_channel5volume", ftos(vol)); +} + +void() setting_chann6vol = +{ + float vol = cvar("snd_channel6volume"); + + vol += 0.1; + + if (vol > 1) + vol = 1; + + cvar_set("snd_channel6volume", ftos(vol)); +} + +void() setting_chann6vol2 = +{ + float vol = cvar("snd_channel6volume"); + + vol -= 0.1; + + if (vol < 0) + vol = 0; + + cvar_set("snd_channel6volume", ftos(vol)); +} + +//rmb null +void() null = { + return; +} + +/* + + MENU GUIDELINES (to preserve consistency) + + * Buttons that belong to the same category should have a spacing + difference of 0.05. + + * Buttons that do not belong to the same category should have + a spacing difference of 0.075. + +*/ + +//struct for all buttons, note scale is going to be multiplied w/ width/height in draw step, keep between 0 and 1 +var struct +{ + vector pos; + string text; + float active; //used for mouseover hilight (positive values) and alpha (negative values) + void() action; //function that is called when the button is pressed with lmb + void() action2; //rmb function + float group; //a bit field, button will be usable/visible when these bits are active + float gray_out; // if not to be accessed +} buttons[] = +{ + //Main + {[0.025, 0.175, 0], "Solo", -1, menu_single, null, MENU_MAIN, 0}, + //{[0.025, 0.225, 0], "Cooperative", -1, menu_multi, null, MENU_MAIN}, + {[0.025, 0.300, 0], "Settings", -1, menu_settings, null, MENU_MAIN, 0}, + {[0.025, 0.350, 0], "Achievements", -1, menu_achievements, null, MENU_MAIN, 0}, + {[0.025, 0.425, 0], "Credits", -1, menu_about, null, MENU_MAIN, 0}, + {[0.025, 0.500, 0], "Exit", -1, menu_quit, null, MENU_MAIN, 0}, + + //Solo + {[0.025, 0.175, 0], "Nacht der Untoten", -1, menu_loadndu, null, MENU_SINGLE, 0}, + {[0.025, 0.275, 0], "Warehouse", -1, menu_loadwh, null, MENU_SINGLE, 0}, + {[0.025, 0.450, 0], "Custom Maps", -1, menu_customs, null, MENU_SINGLE, 0}, + + //Pause + {[0.025, 0.500, 0], "Resume", -1, menu_resume, null, MENU_PAUSE, 0}, + {[0.025, 0.550, 0], "Restart", -1, menu_restart, null, MENU_PAUSE, 0}, + {[0.025, 0.600, 0], "Settings", -1, menu_settings, null, MENU_PAUSE, 0}, + {[0.025, 0.650, 0], "Main Menu", -1, menu_main, null, MENU_PAUSE, 0}, + {[0.025, 0.850, 0], "Waypoint Menu", -1, menu_waypoint, null, MENU_PAUSE, 0}, + + //Restart + {[0.025, 0.500, 0], "Yes", -1, menu_resy, null, MENU_RES, 0}, + {[0.025, 0.550, 0], "No", -1, menu_resn, null, MENU_RES, 0}, + + //Settings + {[0.025, 0.175, 0], "Graphics Settings", -1, menu_graphics, null, MENU_SETTINGS + MENU_IGS, 0}, + {[0.025, 0.350, 0], "Controls", -1, menu_controls, null, MENU_SETTINGS + MENU_IGS, 0}, + + //Graphics + {[0.025, 0.175, 0], "Resolution", -1, settings_re, settings_re, MENU_GSETTINGS, 0}, + {[0.025, 0.225, 0], "Aspect Ratio", -1, settings_ar, settings_ar2, MENU_GSETTINGS, 0}, + {[0.025, 0.275, 0], "Fullscreen", -1, settings_fs, settings_fs, MENU_GSETTINGS, 0}, + {[0.025, 0.325, 0], "Show FPS", -1, settings_fps, settings_fps, MENU_GSETTINGS, 0}, + {[0.025, 0.375, 0], "Max FPS", -1, settings_mfps, settings_mfps2, MENU_GSETTINGS, 0}, + {[0.025, 0.425, 0], "VSync", -1, settings_vs, settings_vs, MENU_GSETTINGS, 0}, + {[0.025, 0.475, 0], "Field of View", -1, settings_fov, settings_fov2, MENU_GSETTINGS, 0}, + {[0.025, 0.525, 0], "Brightness", -1, settings_brite, settings_brite2, MENU_GSETTINGS, 0}, + {[0.025, 0.575, 0], "Decals", -1, settings_dc, settings_dc, MENU_GSETTINGS, 0}, + {[0.025, 0.625, 0], "Particles", -1, settings_pt, settings_pt, MENU_GSETTINGS, 0}, + {[0.025, 0.675, 0], "Fullbright", -1, settings_fb, settings_fb, MENU_GSETTINGS, 0}, + {[0.025, 0.725, 0], "Retro", -1, settings_ro, settings_ro, MENU_GSETTINGS, 0}, + {[0.025, 0.800, 0], "Save & Apply", -1, settings_apply, settings_apply, MENU_GSETTINGS + MENU_CONSETTINGS + + MENU_AUDSETTINGS, 0}, + + //Controls + {[0.025, 0.175, 0], "Walk Forward", -1, bind_walkf, null, MENU_CSETTINGS, 0}, + {[0.025, 0.225, 0], "Walk Backward", -1, bind_walkb, null, MENU_CSETTINGS, 0}, + {[0.025, 0.275, 0], "Walk Left", -1, bind_walkl, null, MENU_CSETTINGS, 0}, + {[0.025, 0.325, 0], "Walk Right", -1, bind_walkr, null, MENU_CSETTINGS, 0}, + {[0.025, 0.375, 0], "Jump", -1, bind_jump, null, MENU_CSETTINGS, 0}, + {[0.025, 0.425, 0], "Sprint", -1, bind_sprint, null, MENU_CSETTINGS, 0}, + {[0.025, 0.475, 0], "Change Stance", -1, bind_stance, null, MENU_CSETTINGS, 0}, + + {[0.025, 0.525, 0], "Weapon Next", -1, bind_wpnn, null, MENU_CSETTINGS, 0}, + {[0.025, 0.575, 0], "Weapon Previous", -1, bind_wpnp, null, MENU_CSETTINGS, 0}, + + {[0.025, 0.625, 0], "Interact", -1, bind_interact, null, MENU_CSETTINGS, 0}, + {[0.025, 0.675, 0], "Reload", -1, bind_reload, null, MENU_CSETTINGS, 0}, + {[0.025, 0.725, 0], "Melee", -1, bind_knife, null, MENU_CSETTINGS, 0}, + {[0.025, 0.775, 0], "Grenade", -1, bind_nade, null, MENU_CSETTINGS, 0}, + {[0.025, 0.825, 0], "Secondary Grenade", -1, bind_switchnade, null, MENU_CSETTINGS, 0}, + + // Custom Maps + {[0.025, 0.825, 0], "Next Page", -1, menu_custom_next, null, MENU_CUSTOMS + MENU_ACHIEVEMENTS, 0}, + {[0.025, 0.875, 0], "Previous Page", -1, menu_custom_back, null, MENU_CUSTOMS + MENU_ACHIEVEMENTS, 0}, + + //Everything, pretty much + {[0.025, 0.925, 0], "Back", -1, menu_back, null, MENU_SINGLE + MENU_MULTI + MENU_SETTINGS + MENU_ABOUT + + MENU_IGS + MENU_GSETTINGS + MENU_CSETTINGS + MENU_CUSTOMS + + MENU_ACHIEVEMENTS + MENU_CONSETTINGS + MENU_AUDSETTINGS, 0}, + + {[0.025, 0.275, 0], "Control Settings", -1, menu_consettings, null, MENU_SETTINGS + MENU_IGS, 0}, // 47 + {[0.025, 0.175, 0], "ADS Mode", -1, settings_adsmode, null, MENU_CONSETTINGS, 0}, // 48 + {[0.025, 0.225, 0], "Look Sensitivity", -1, settings_sens, settings_sens2, MENU_CONSETTINGS, 0}, // 49 + + {[0.025, 0.325, 0], "Wahnsinn", -1, menu_loadwn, null, MENU_SINGLE, 1}, // 50 + {[0.025, 0.375, 0], "Christmas Special", -1, menu_loadch, null, MENU_SINGLE, 0}, // 51 + {[0.025, 0.225, 0], "Kino der Toten", -1, menu_loadch, null, MENU_SINGLE, 1}, // 52 + + {[0.025, 0.225, 0], "Audio Settings", -1, menu_audsettings, null, MENU_SETTINGS + MENU_IGS, 0}, // 53 + {[0.025, 0.175, 0], "Master Volume", -1, setting_mastervol, setting_mastervol2, MENU_AUDSETTINGS, 0}, // 54 + {[0.025, 0.225, 0], "Music Volume", -1, setting_chann1vol, setting_chann1vol2, MENU_AUDSETTINGS, 0}, // 55 + {[0.025, 0.275, 0], "SFX Volume", -1, setting_chann2vol, setting_chann2vol2, MENU_AUDSETTINGS, 0}, // 56 + {[0.025, 0.325, 0], "Channel 3 Volume", -1, setting_chann3vol, setting_chann3vol2, MENU_AUDSETTINGS, 0}, // 57 + {[0.025, 0.375, 0], "Channel 4 Volume", -1, setting_chann4vol, setting_chann4vol2, MENU_AUDSETTINGS, 0}, // 58 + {[0.025, 0.425, 0], "Channel 5 Volume", -1, setting_chann5vol, setting_chann5vol2, MENU_AUDSETTINGS, 0}, // 59 + {[0.025, 0.475, 0], "Channel 6 Volume", -1, setting_chann6vol, setting_chann6vol2, MENU_AUDSETTINGS, 0}, // 60 +}; + +//REMEMBER TO UPDATE THIS CONST IF YOU ADD BUTTONS +const float BUTTONS_COUNT = 61; +float lastActive; + + +//this function handles drawing the buttons and checking if they should be active/hilighted +void(float index) Update_Button = +{ + float btnscale = 0.030*g_height; + + if(in_menu != in_menu & buttons[index].group) + buttons[index].active = -1; + + if(buttons[index].active == -1) + { + if(in_menu == in_menu & buttons[index].group) + buttons[index].active = 1; + else + return; + } + + local vector pos = buttons[index].pos; + pos_x *= g_width; + pos_y *= g_height; + + if (next_gray_out == 1) + buttons[44].gray_out = 1; + else + buttons[44].gray_out = 0; + + if (back_gray_out == 1) + buttons[45].gray_out = 1; + else + buttons[45].gray_out = 0; + + if (buttons[index].gray_out == 0) { + if(buttons[index].active > 0) { + drawstring(pos, buttons[index].text, [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0); + } else { + drawstring(pos, buttons[index].text, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + } + } else { + drawstring(pos, buttons[index].text, [g_height * 0.030, g_height * 0.030, 1], [0.5,0.5,0.5], 1, 0); + } + + + if (cursor_pos_x > pos_x && cursor_pos_x < pos_x + (g_height * 0.6) && cursor_pos_y > pos_y && cursor_pos_y < pos_y + btnscale && buttons[index].gray_out == 0) { + if (index != lastActive) + localsound_enhanced("sounds/menu/navigate.wav", CHAN_SFX, 1); + + buttons[index].active = 1; + lastActive = index; + last_active_custom_select = active_custom_select = -1; + } + + if (lastActive == -1) { + buttons[index].active = 0; + } else { + buttons[lastActive].active = 1; + } + + + if(buttons[index].active > 0) + { + buttons[index].active = 0; + } +}; + +float xd; +void() Controller_UpdateButton = +{ + /*if (GPActive[0]) { + + }*/ + drawstring([0.025*g_width, 0.375*g_height, 0], ftos(GPActive[2]), [g_height * 0.020, g_height * 0.020, 1], [0.8,0.8,0.8], 1, 0); +} + + +void(float index, float type) Button_Click = +{ + float btnscale = 0.030*g_height; + + //don't click if not active + if(buttons[index].active == -1) + return; + + local vector pos = buttons[index].pos; + pos_x *= g_width; + pos_y *= g_height; + + if (cursor_pos_x > pos_x && cursor_pos_x < pos_x + (g_height * 0.6) && cursor_pos_y > pos_y && cursor_pos_y < pos_y + btnscale) { + if (type == 0) { //left click + buttons[index].action(); + } else { //right click + buttons[index].action2(); + } + + lastActive = -1; + localsound_enhanced("sounds/menu/enter.wav", CHAN_SFX, 1); + } + + if (in_menu == MENU_CUSTOMS) + customs_interact = FALSE; +}; + + +void(float type) Menu_Click = +{ + float i; + for(i = 0; i < BUTTONS_COUNT; i++) + { + Button_Click(i, type); + } +}; + +void() Menu_Click_Custom = +{ + customs_interact = TRUE; +}; + +void() Draw_Extra_Main = +{ + local string main_desc = ""; + + // Draw darkened co-op button + drawstring([0.025*g_width, 0.225*g_height, 0], "Co-Op (Coming Soon!)", [g_height * 0.03, g_height * 0.03, 1], [0.5, 0.5, 0.5], 1, 0); + + // Division lines + drawfill ([0.025*g_width, 0.275*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + drawfill ([0.025*g_width, 0.400*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + drawfill ([0.025*g_width, 0.475*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + + // Get description for menu option + switch(lastActive) { + case 0: + main_desc = "Take on the Hordes by yourself."; + break; + case 1: + main_desc = "Adjust your Settings to Optimize your Experience."; + break; + case 2: + main_desc = "View locked or unlocked Achievements."; + break; + case 3: + main_desc = "See who made NZ:P possible."; + break; + case 4: + main_desc = "Return to System."; + break; + default: + main_desc = ""; + break; + } + + // Draw desc + drawstring([0.025*g_width, 0.865*g_height, 0], main_desc, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); +} + +void() Draw_Extra_Solo = +{ + local string solo_desc = ""; + local string solo_desc2 = ""; + local string solo_desc3 = ""; + local string solo_img = ""; + + // Division lines + drawfill ([0.025*g_width, 0.425*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + + // Map descriptions + switch(lastActive) { + case 5: + solo_desc = "Lock and Load; Crashed Plane."; + solo_desc2 = "Divided. Thousands of Undead."; + solo_desc3 = "This is the Night of the Dead."; + solo_img = "gfx/menu/nacht_der_untoten.png"; + break; + case 6: + solo_desc = "Old Warehouse full of Zombies!"; + solo_desc2 = "Fight your way to the Power"; + solo_desc3 = "Switch through the Hordes!"; + solo_img = "gfx/menu/warehouse.png"; + break; + case 50: + solo_desc = "An abandoned and bleak mental house"; + solo_desc2 = "surrounded by debris and fog. Teddy"; + solo_desc3 = "is deceitful, find him."; + solo_img = "gfx/menu/wahnsinn.png"; + break; + case 51: + solo_desc = "No Santa this year. Though we're"; + solo_desc2 = "sure you will get presents from"; + solo_desc3 = "the undead! Will you accept them?"; + solo_img = "gfx/menu/christmas_special.png"; + break; + case 52: + solo_desc = "A WW2-era theatre left to rot in"; + solo_desc2 = "West Berlin. See what secrets await"; + solo_desc3 = "in this Theatre of the Damned."; + solo_img = "gfx/menu/kino_der_toten.png"; + break; + case 7: + solo_desc = "Custom Maps made by Community on"; + solo_desc2 = "the Forum and Discord!"; + solo_img = "gfx/menu/custom.png"; + break; + default: + solo_desc = ""; + solo_img = ""; + break; + } + + // Map img + if (solo_img != "") + drawpic([0.490*g_width, 0.115*g_height, 0], solo_img, [0.425*g_width, 0.385*g_height, 0], [1,1,1], 1); + + // Draw desc + drawstring([0.425*g_width, 0.540*g_height, 0], solo_desc, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.425*g_width, 0.580*g_height, 0], solo_desc2, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.425*g_width, 0.620*g_height, 0], solo_desc3, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); +} + +void() Draw_Extra_Settings = +{ + + // Division lines + drawfill ([0.025*g_width, 0.325*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + + local string set_desc = ""; + + // Descriptions + switch(lastActive) { + case 15: + set_desc = "Adjust settings relating to Graphical Fidelity."; + break; + case 16: + set_desc = "Customize your Control Scheme."; + break; + case 47: + set_desc = "Adjust settings in relation to how NZ:P Controls."; + break; + case 53: + set_desc = "Adjust Audio Levels."; + break; + default: + set_desc = ""; + break; + } + + // Draw desc + drawstring([0.025*g_width, 0.865*g_height, 0], set_desc, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); +} + +//FIXME -- credits look ugly, font scaling & such +void() Draw_Extra_Credits = +{ + drawstring([0.03*g_width, 0.15*g_height, 0], "Blubswillrule: Coding, Models, GFX, Sounds, Music", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.15*g_height + g_height*0.025 + 3, 0], [strlen("Blubswillrule")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.20*g_height, 0], "Ju[s]tice: Maps, Models, GFX", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.20*g_height + g_height*0.025 + 3, 0], [strlen("Ju[s]tice")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.25*g_height, 0], "Jukki: Coding", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.25*g_height + g_height*0.025 + 3, 0], [strlen("Jukki")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.30*g_height, 0], "Biodude: Sounds", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.30*g_height + g_height*0.025 + 3, 0], [strlen("Biodude")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.35*g_height, 0], "Dr_Mabuse1981: Coding", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.35*g_height + g_height*0.025 + 3, 0], [strlen("Dr_Mabuse1981")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.40*g_height, 0], "Naievil: Coding, NX Maintaining", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.40*g_height + g_height*0.025 + 3, 0], [strlen("Naievil")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.45*g_height, 0], "MotoLegacy: Coding, GFX, Music", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.45*g_height + g_height*0.025 + 3, 0], [strlen("MotoLegacy")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.50*g_height, 0], "Derped_Crusader: Models, GFX", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.50*g_height + g_height*0.025 + 3, 0], [strlen("Derped_Crusader")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.60*g_height, 0], "Special Thanks:", [g_height * 0.025, g_height * 0.025, 1], [0.8, 0.8, 0.8], 1, 0); + drawfill ([.03*g_width, 0.60*g_height + g_height*0.025 + 3, 0], [strlen("Special Thanks")*12.5, 0.0025*g_height, 0], [0.8, 0.8, 0.8], 1, 0); + + drawstring([0.03*g_width, 0.640*g_height, 0], "- Spike: FTEQW", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.660*g_height, 0], "- Shpuld: Clean-CSQC", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.680*g_height, 0], "- Crow_Bar: DQuake", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.700*g_height, 0], "- st1x51: DQuakePlus", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.720*g_height, 0], "- fgsfdsfgs: QuakespasmNX", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.740*g_height, 0], "- Azenn: GFX help", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.760*g_height, 0], "- tavo: Music help", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); + drawstring([0.03*g_width, 0.780*g_height, 0], "- BCDeshiG: Heavy bug testing", [g_height * 0.020, g_height * 0.020, 1], [0.8, 0.8, 0.8], 1, 0); +} + +void() Draw_Extra_Restart = +{ + drawstring([0.025*g_width, 0.375*g_height, 0], "Are you sure you want to restart?", [g_height * 0.020, g_height * 0.020, 1], [0.8,0.8,0.8], 1, 0); + drawstring([0.02*g_width, 0.425*g_height, 0], "Your current progress will be lost!", [g_height * 0.020, g_height * 0.020, 1], [0.8,0.8,0.8], 1, 0); +} + +void() Draw_Extra_CSettings = +{ + string cset_desc; + cset_desc = ""; + + // ADS Mode + if (cvar("cl_adsmode") == 0) + drawstring([0.475*g_width, 0.175*g_height, 0], "HOLD", [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + else + drawstring([0.475*g_width, 0.175*g_height, 0], "TOGGLE", [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Look Sensitivity + drawfill ([0.475*g_width, 0.2255*g_height, 0], [g_height * (cvar("sensitivity")/6), g_height * 0.030, 1], [0.8, 0.8, 0.8], 0, 0); + + + // Descriptions + switch(lastActive) { + case 48: + cset_desc = "Switch between Toggle/Hold to Aim Down Sight."; + break; + case 49: + cset_desc = "Change Look Sensitivity to change Cursor Responsiveness."; + break; + default: + cset_desc = ""; + break; + } + + // Division lines + drawfill ([0.025*g_width, 0.775*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + + // Draw desc + drawstring([0.025*g_width, 0.865*g_height, 0], cset_desc, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); +} + +void() Draw_Extra_GSettings = +{ + local string fpsval; + local string vsyncval; + local string decalval; + local string partval; + local string fullval; + local string retroval; + local string sval; + local string arval = ""; + local string resval; + local string gset_desc = ""; + + // Resolution + resval = strcat(ftos(active_swidth), "x", ftos(active_sheight)); + drawstring([0.475*g_width, 0.175*g_height, 0], resval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Aspect Ratio + switch(aspectratio) { + case 0: + arval = "4:3"; + break; + case 1: + arval = "5:4"; + break; + case 2: + arval = "16:9"; + break; + case 3: + arval = "16:10"; + break; + case 4: + arval = "21:9"; + break; + default: + arval = "ERR"; + break; + } + drawstring([0.475*g_width, 0.225*g_height, 0], arval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Fullscreen + if (fullscreenval == 0) + sval = S_DISABLED; + else + sval = S_ENABLED; + + drawstring([0.475*g_width, 0.275*g_height, 0], sval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Show FPS + if (cvar("show_fps") == 0) + fpsval = S_DISABLED; + else + fpsval = S_ENABLED; + + drawstring([0.475*g_width, 0.325*g_height, 0], fpsval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Max FPS + // can't adjust when vsync is enabled, so grey out + if (!cvar("vid_vsync")) + drawstring([0.475*g_width, 0.375*g_height, 0], ftos(cvar("cl_maxfps")), [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + else + drawstring([0.475*g_width, 0.375*g_height, 0], ftos(cvar("cl_maxfps")), [g_height * 0.030, g_height * 0.030, 1], [0.4,0.4,0.4], 1, 0); + + // VSync + if (cvar("vid_vsync") == 0) + vsyncval = S_DISABLED; + else + vsyncval = S_ENABLED; + + drawstring([0.475*g_width, 0.425*g_height, 0], vsyncval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Field of View + drawstring([0.475*g_width, 0.475*g_height, 0], ftos(cvar("fov")), [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Brightness + // have to use a drawfill because of how gamma values are subtracted.. for some reason + drawfill ([0.475*g_width, 0.5255*g_height, 0], [g_height * (cvar("gamma")/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Decals + if (cvar("nzp_decals") == 0) + decalval = S_DISABLED; + else + decalval = S_ENABLED; + + drawstring([0.475*g_width, 0.575*g_height, 0], decalval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Particles + if (cvar("nzp_particles") == 0) + partval = S_DISABLED; + else + partval = S_ENABLED; + + drawstring([0.475*g_width, 0.625*g_height, 0], partval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Fullbright + if (cvar("r_fullbright") == 0) + fullval = S_DISABLED; + else + fullval = S_ENABLED; + + drawstring([0.475*g_width, 0.675*g_height, 0], fullval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Retro + if (cvar_string("gl_texturemode") == "gl_nearest") + retroval = S_ENABLED; + else + retroval = S_DISABLED; + + drawstring([0.475*g_width, 0.725*g_height, 0], retroval, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + + // Descriptions + switch(lastActive) { + case 17: + gset_desc = "Adjust Window Resolution."; + break; + case 18: + gset_desc = "Change Window Aspect Ratio."; + break; + case 19: + gset_desc = "Toggle Fullscreen (May fail depending on GPU)."; + break; + case 20: + gset_desc = "Toggle Framerate Overlay."; + break; + case 21: + gset_desc = "Increase of Decrease Max Frames per Second."; + break; + case 22: + gset_desc = "Toggle Vertical Sync."; + break; + case 23: + gset_desc = "Adjust Game Field of View."; + break; + case 24: + gset_desc = "Increase or Decrease Game Brightness."; + break; + case 25: + gset_desc = "Toggle Bullet and Explosive Decals."; + break; + case 26: + gset_desc = "Toggle Appearence of (most) Particles."; + break; + case 27: + gset_desc = "Toggle all non-realtime lights (Requires Map Restart)."; + break; + case 28: + gset_desc = "Toggle texture filtering."; + break; + default: + gset_desc = ""; + break; + } + + // Division lines + drawfill ([0.025*g_width, 0.775*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + + // Draw desc + drawstring([0.025*g_width, 0.865*g_height, 0], gset_desc, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); +} + +void() Draw_Extra_Controls = +{ + local string buttoncon[MAX_BINDS]; + local string binding = "_"; + + //prevent warning + buttoncon[0] = ""; + + for (int i = 0; i < MAX_BINDS; i += 1) { + tokenize(findkeysforcommand(buttonBind[i])); + + if (editBind[i] == FALSE) + buttoncon[i] = strtoupper(keynumtostring(stof(argv(0)))); + else + buttoncon[i] = binding; + + if (buttoncon[i] == "01") + buttoncon[i] = "UNBOUND"; + + drawstring([0.475*g_width, (0.175+(0.05*i))*g_height, 0], buttoncon[i], [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + } +} + +// remove prefix and suffix from custom map line +// (maps/) & (.bsp) +string(string s) strip_custominfo = +{ + string s2; + s2 = substring(s, 5, strlen(s)); // maps/ + s2 = substring(s2, 0, strlen(s2) - 4); // .bsp + + return s2; +} + +var struct +{ + float occupied; + float map_allow_game_settings; + float map_use_thumbnail; + string map_name; + string map_name_pretty; + string map_desc_1; + string map_desc_2; + string map_desc_3; + string map_desc_4; + string map_desc_5; + string map_desc_6; + string map_desc_7; + string map_desc_8; + string map_author; +} custom_maps[500]; + +void() Customs_Get = +{ + searchhandle maps; + float map_count; + + maps = search_begin("maps/*.bsp", 0, 0); + map_count = search_getsize(maps); + + custom_map_pages = ceil(map_count/10); + + for (float i = 0; i < map_count; i++) { + custom_maps[i].occupied = TRUE; + custom_maps[i].map_name = strip_custominfo(search_getfilename(maps, i)); + + float settings = fopen(strcat("maps/", custom_maps[i].map_name, ".txt"), FILE_READ); + if (settings != -1) { + custom_maps[i].map_name_pretty = fgets(settings); + custom_maps[i].map_desc_1 = fgets(settings); + custom_maps[i].map_desc_2 = fgets(settings); + custom_maps[i].map_desc_3 = fgets(settings); + custom_maps[i].map_desc_4 = fgets(settings); + custom_maps[i].map_desc_5 = fgets(settings); + custom_maps[i].map_desc_6 = fgets(settings); + custom_maps[i].map_desc_7 = fgets(settings); + custom_maps[i].map_desc_8 = fgets(settings); + custom_maps[i].map_author = fgets(settings); + custom_maps[i].map_use_thumbnail = stof(fgets(settings)); + custom_maps[i].map_allow_game_settings = stof(fgets(settings)); + fclose(settings); + } + + } + + current_custom_map_page = 1; + + search_end(maps); +} + +void() Draw_Extra_Customs = +{ + string custom_map_names[10]; + float custom_map_active[10]; + float multiplier; + + if (current_custom_map_page > 1) + multiplier = (current_custom_map_page - 1) * 10; + else + multiplier = 0; + + for (float i = 0; i < 10; i++) { + if (custom_maps[i + multiplier].occupied) { + custom_map_names[i] = custom_maps[i + multiplier].map_name; + } + } + + for (float i = 0; i < 10; i++) { + if (cursor_pos_x > 0.025*g_width && cursor_pos_x < (0.025*g_width + g_height * 0.6) && + cursor_pos_y > (0.175 + (0.05 * i))*g_height && cursor_pos_y < (0.175 + (0.05 * i))*g_height + + (0.030*g_height)) { + + if (i != last_active_custom_select) + localsound_enhanced("sounds/menu/navigate.wav", CHAN_SFX, 1); + + if (customs_interact) + localcmd(strcat("map "), custom_map_names[i]); + + if (last_active_custom_select != -1) + custom_map_active[last_active_custom_select] = FALSE; + custom_map_active[i] = TRUE; + last_active_custom_select = i; + lastActive = -1; + } + } + + for (float i = 0; i < 10; i++) { + if (custom_map_active[i] == TRUE || last_active_custom_select == i) { + if (custom_maps[i + multiplier].map_name_pretty) + drawstring([0.025*g_width, (0.175 + (0.05 * i))*g_height, 0], custom_maps[i + multiplier].map_name_pretty, [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0); + else + drawstring([0.025*g_width, (0.175 + (0.05 * i))*g_height, 0], custom_map_names[i], [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0); + + float line_increment; + line_increment = 0; + + if (custom_maps[i + multiplier].map_desc_1 != "" && custom_maps[i + multiplier].map_desc_1 != " ") { + drawstring([0.425*g_width, 0.540*g_height, 0], custom_maps[i + multiplier].map_desc_1, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_2 != "" && custom_maps[i + multiplier].map_desc_2 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_2, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_3 != "" && custom_maps[i + multiplier].map_desc_3 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_3, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_4 != "" && custom_maps[i + multiplier].map_desc_4 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_4, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_5 != "" && custom_maps[i + multiplier].map_desc_5 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_5, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_6 != "" && custom_maps[i + multiplier].map_desc_6 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_6, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_7 != "" && custom_maps[i + multiplier].map_desc_7 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_7, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_desc_8 != "" && custom_maps[i + multiplier].map_desc_8 != " ") { + line_increment++; + drawstring([0.425*g_width, (0.540 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_desc_8, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + if (custom_maps[i + multiplier].map_author != "" && custom_maps[i + multiplier].map_author != " ") { + line_increment++; + drawstring([0.425*g_width, (0.560 + (0.040*line_increment))*g_height, 0], custom_maps[i + multiplier].map_author, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0], 1, 0); + } + + if (custom_maps[i + multiplier].map_use_thumbnail) { + drawpic([0.490*g_width, 0.115*g_height, 0], strcat("menu/custom/", custom_map_names[i], ".png"), [0.425*g_width, 0.385*g_height, 0], [1,1,1], 1); + } + + } else { + if (custom_maps[i + multiplier].map_name_pretty) + drawstring([0.025*g_width, (0.175 + (0.05 * i))*g_height, 0], custom_maps[i + multiplier].map_name_pretty, [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + else + drawstring([0.025*g_width, (0.175 + (0.05 * i))*g_height, 0], custom_map_names[i], [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0); + } + + } + + customs_interact = FALSE; +} + +void() Draw_Extra_Achievements = +{ + float multiplier; + + if (current_achievement_page > 1) { + multiplier = (current_achievement_page - 1) * 3; + } else { + multiplier = 0; + back_gray_out = true; + } + + for (float i = 0; i < 3; i++) { + if (i + multiplier > MAX_ACHIEVEMENTS - 1) + continue; + + string name1, name2; + string desc1, desc2, desc3, desc4; + name2 = ""; + desc2 = ""; + desc3 = ""; + desc4 = ""; + + if (strlen(achievements[i + multiplier].name) >= 33) { + name1 = substring(achievements[i + multiplier].name, 0, 33); + name2 = substring(achievements[i + multiplier].name, 33, strlen(achievements[i + multiplier].name)); + } else { + name1 = achievements[i + multiplier].name; + } + + if (substring(name2, 0, 1) == " ") + name2 = substring(name2, 1, strlen(name2)); + + desc1 = substring(achievements[i + multiplier].description, 0, 36); + desc2 = substring(achievements[i + multiplier].description, 36, 34); + desc3 = substring(achievements[i + multiplier].description, 70, 35); + desc4 = substring(achievements[i + multiplier].description, 105, strlen(achievements[i + multiplier].description)); + + if (substring(desc2, 0, 1) == " ") + desc2 = substring(desc2, 1, strlen(desc2)); + + if (substring(desc3, 0, 1) == " ") + desc3 = substring(desc3, 1, strlen(desc3)); + + if (substring(desc4, 0, 1) == " ") + desc4 = substring(desc4, 1, strlen(desc4)); + + if (achievements[i + multiplier].unlocked == false) { + int lock_width_pos; + + drawstring([0.450*g_height, (0.150 + (0.212 * i))*g_height, 0], name1, [g_height * 0.030, g_height * 0.030, 1], [0.5,0.5,0.5], 1, 0); + drawstring([0.450*g_height, (0.180 + (0.212 * i))*g_height, 0], name2, [g_height * 0.030, g_height * 0.030, 1], [0.5,0.5,0.5], 1, 0); + + if (name2 == "") { + lock_width_pos = stringwidth(name1, 0, [g_height * 0.030, g_height * 0.030, 1]); + drawstring([0.450*g_height + lock_width_pos, (0.150 + (0.212 * i))*g_height, 0], " (LOCKED)", [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0); + } else { + lock_width_pos = stringwidth(name2, 0, [g_height * 0.030, g_height * 0.030, 1]); + drawstring([0.450*g_height + lock_width_pos, (0.180 + (0.212 * i))*g_height, 0], " (LOCKED)", [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0); + } + + drawstring([0.450*g_height, (0.210 + (0.212 * i))*g_height, 0], desc1, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + drawstring([0.450*g_height, (0.240 + (0.212 * i))*g_height, 0], desc2, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + drawstring([0.450*g_height, (0.270 + (0.212 * i))*g_height, 0], desc3, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + drawstring([0.450*g_height, (0.300 + (0.212 * i))*g_height, 0], desc4, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + + drawpic([0.025*g_width, (0.150 + (0.212 * i))*g_height, 0], "gfx/achievement/achievement_locked.tga", [0.38*g_height, 0.19*g_height, 0], [1,1,1], 1); + } else { + drawstring([0.450*g_height, (0.150 + (0.212 * i))*g_height, 0], name1, [g_height * 0.030, g_height * 0.030, 1], [1,1,0], 1, 0); + drawstring([0.450*g_height, (0.180 + (0.212 * i))*g_height, 0], name2, [g_height * 0.030, g_height * 0.030, 1], [1,1,0], 1, 0); + + drawstring([0.450*g_height, (0.210 + (0.212 * i))*g_height, 0], desc1, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + drawstring([0.450*g_height, (0.240 + (0.212 * i))*g_height, 0], desc2, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + drawstring([0.450*g_height, (0.270 + (0.212 * i))*g_height, 0], desc3, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + drawstring([0.450*g_height, (0.300 + (0.212 * i))*g_height, 0], desc4, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0); + + drawpic([0.025*g_width, (0.150 + (0.212 * i))*g_height, 0], achievements[i + multiplier].img, [0.38*g_height, 0.19*g_height, 0], [1,1,1], 1); + } + } +} + +void() Draw_Extra_ASettings = +{ + string aset_desc = ""; + float master_volume = cvar("volume"); + float chann1_volume = cvar("snd_channel1volume"); + float chann2_volume = cvar("snd_channel2volume"); + float chann3_volume = cvar("snd_channel3volume"); + float chann4_volume = cvar("snd_channel4volume"); + float chann5_volume = cvar("snd_channel5volume"); + float chann6_volume = cvar("snd_channel6volume"); + + // Master Volume + drawfill ([0.475*g_width, 0.1755*g_height, 0], [g_height * (master_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Music Volume + drawfill ([0.475*g_width, 0.2255*g_height, 0], [g_height * (chann1_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // SFX Volume + drawfill ([0.475*g_width, 0.2755*g_height, 0], [g_height * (chann2_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Channel 3 Volume + drawfill ([0.475*g_width, 0.3255*g_height, 0], [g_height * (chann3_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Channel 4 Volume + drawfill ([0.475*g_width, 0.3755*g_height, 0], [g_height * (chann4_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Channel 5 Volume + drawfill ([0.475*g_width, 0.4255*g_height, 0], [g_height * (chann5_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Channel 6 Volume + drawfill ([0.475*g_width, 0.4755*g_height, 0], [g_height * (chann6_volume/3), g_height * 0.030, 1], [0.8, 0.8, 0.8], 1, 0); + + // Descriptions + switch(lastActive) { + case 54: + aset_desc = "Adjusts the overall volume of the game."; + break; + case 55: + aset_desc = "Adjusts the volume of the music in the game."; + break; + case 56: + aset_desc = "Adjusts the volume of sound effects in the game."; + break; + case 57: + aset_desc = "Audio Level for Channel 3."; + break; + case 58: + aset_desc = "Audio Level for Channel 4."; + break; + case 59: + aset_desc = "Audio Level for Channel 5."; + break; + case 60: + aset_desc = "Audio Level for Channel 6."; + break; + default: + aset_desc = ""; + break; + } + + // Division lines + drawfill ([0.025*g_width, 0.775*g_height, 0], [g_height * 0.600, g_height * 0.005, 1], [0.4, 0.4, 0.4], 1, 0); + + // Draw desc + drawstring([0.025*g_width, 0.865*g_height, 0], aset_desc, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); +} + +float menu_sound_length; +float menu_sound_time; + +void() Draw_Menu = +{ + // main menu theme iterating + if (menu_initialized == false && in_menu == MENU_MAIN) { + localsound_enhanced("tracks/tensioned_by_the_damned.mp3", CHAN_MUSIC, 1); + menu_sound_length = 490; // have to hard code because soundlength() doesn't like MP3 media it seems.. + menu_sound_time = 0; + menu_initialized = true; + } + + // restart the track if it has ended + if (menu_initialized == true && (menu_sound_time >= menu_sound_length - 1)) { + localsound_enhanced("tracks/tensioned_by_the_damned.mp3", CHAN_MUSIC, 1); + menu_sound_time = 0; + } + + menu_sound_time += frametime; + + // background + if(serverkey("constate") == "disconnected") + { + if(g_width <= g_height * 1.77778) + drawpic([0,0,0], "menu/bg", [g_height * 1.77778, g_height, 1], [1, 1, 1], 1); + else + drawpic([0,0,0], "menu/bg", [g_width, g_height, 1], [1, 1, 1], 1); + } + + // Darken the BG + drawfill ([0, 0, 0], [g_width, g_height, 1], [0, 0, 0], 0.4, 0); + + // version string + if (in_menu == MENU_MAIN || in_menu == MENU_ABOUT || in_menu == MENU_SINGLE || in_menu == MENU_ACHIEVEMENTS + || in_menu == MENU_CUSTOMS || in_menu == MENU_SETTINGS) { + string vers_string; + + if (cvar("cl_showbuildtime") > 0) { + vers_string = build_datetime; + } else { + vers_string = VERSION_STRING; + } + + drawstring([(0.975*g_width - stringwidth(vers_string, 0, [0.03*g_height, 0.03*g_height, 0])), 0.025*g_height, 0], vers_string, [g_height * 0.03, g_height * 0.03, 1], [0.8, 0.8, 0.8], 1, 0); + } + + //menu title + local string title = ""; + switch(in_menu) + { + case MENU_MAIN: + title = "MAIN MENU"; + Draw_Extra_Main(); + break; + case MENU_SINGLE: + title = "SOLO"; + Draw_Extra_Solo(); + break; + case MENU_MULTI: + title = "COOP"; + break; + case MENU_ABOUT: + title = "CREDITS"; + Draw_Extra_Credits(); + break; + case MENU_PAUSE: + title = "PAUSED"; + break; + case MENU_IGS: + case MENU_SETTINGS: + title = "SETTINGS"; + Draw_Extra_Settings(); + break; + case MENU_GSETTINGS: + title = "GRAPHICS SETTINGS"; + Draw_Extra_GSettings(); + break; + case MENU_CSETTINGS: + title = "CONTROLS"; + Draw_Extra_Controls(); + break; + case MENU_RES: + title = "RESTART"; + Draw_Extra_Restart(); + break; + case MENU_CUSTOMS: + title = "CUSTOM MAPS"; + Draw_Extra_Customs(); + break; + case MENU_ACHIEVEMENTS: + title = "ACHIEVEMENTS"; + Draw_Extra_Achievements(); + break; + case MENU_CONSETTINGS: + title = "CONTROL SETTINGS"; + Draw_Extra_CSettings(); + break; + case MENU_AUDSETTINGS: + title = "AUDIO SETTINGS"; + Draw_Extra_ASettings(); + break; + default: + title = "Nazi Zombies: Portable"; + } + + drawstring([0.025*g_width, 0.025*g_width, 0], title, [g_height * 0.045, g_height * 0.045, 1], [0.8, 0.8, 0.8], 1, 0); + + //Update buttons + local float i; + + for(i = 0; i < BUTTONS_COUNT; i++) + { + Update_Button(i); + } + + // TODO - Controller Support + /*if (cvar("in_xinput")) { + Controller_UpdateButton(); + }*/ +}; diff --git a/source/server/ai/ai_core.qc b/source/server/ai/ai_core.qc new file mode 100644 index 0000000..88950b8 --- /dev/null +++ b/source/server/ai/ai_core.qc @@ -0,0 +1,748 @@ +/* + server/ai/ai_core.qc + + ai stuff + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void(float what) play_sound_z; + +void() path_corner_touch = +{ + self.origin_z = self.origin_z + 32; + setorigin(self, self.origin); + self.classname = "path_corner"; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + self.touch = SUB_Null; + setsize(self, '0 0 0 ', '0 0 0'); + if(!self.target) + { + if (self.spawnflags & 1) + return; + bprint(PRINT_HIGH, "path_corner with name: "); + bprint(PRINT_HIGH, self.targetname); + bprint(PRINT_HIGH, " has no target!\n"); + } +} + +//We want the path corner to drop to the ground and then we set it up 32 units so it's exact +void() path_corner = +{ + self.classname = "path_corner_set"; + self.movetype = MOVETYPE_BOUNCE; + self.solid = SOLID_BBOX; + self.touch = path_corner_touch; + setsize(self, '0 0 0 ', '0 0 0'); +}; + + +void removeZombie(); +void() Respawn = +{ + Current_Zombies--; + removeZombie(); +}; + +entity(entity blarg) find_new_enemy = +{ + local entity player; + local entity closest; + local float bestdist; + local float dist; + bestdist = 10000; + closest = 0; + + // FIXME - change to hellhound for old/unreleased Demo map support? + if(self.classname == "ai_zombie" || self.classname == "ai_dog") { + player = find(world,classname,"monkey"); + if(player) { + return player; + } + + player = find(world,classname,"player"); + /////////////////////////////// + if(!player) { + return world; + } + ////////////////////////////// + while(player) { + if (player.downed == true) { + return world; + } + + dist = vlen(self.origin - player.origin); + if (dist < bestdist) { + closest = player; + bestdist = dist; + } + player = find(player,classname,"player"); + } + return closest; + + } + if (self.classname != "wunder") + bprint(PRINT_HIGH, "Error: Find_New_Enemy returns world! \n"); + return world; +}; + +float() avoid_zombies = +{ + local entity ent; + ent = findradius(self.origin,23);//22.67 + makevectors(self.angles); + float left_amount, right_amount; + left_amount = right_amount = 0; + while(ent) + { + if(ent.classname == "ai_zombie" && ent != self) + { + local vector vec_b; + local float dot; + //vec_b = normalize(self.origin - ent.origin); + //dot = v_right * vec_b; + //dot = self.angles_y - (dot > 0.5) ? 90 : 270; + + vec_b = (self.origin - ent.origin); + dot = (v_right_x * vec_b_x) + (v_right_y * vec_b_y);//dot product + if(dot > 0)// on right + right_amount++; + else// on left + left_amount++; + } + ent = ent.chain; + } + if(left_amount + right_amount == 0) + return 0; + + return (left_amount > right_amount) ? 15 : -15; +}; + +float() push_away_zombies = +{ + local entity ent; + ent = findradius(self.origin,11); + float return_value; + return_value = 0; + while(ent) + { + if(ent.classname == "ai_zombie" && ent != self) + { + vector push; + push = ent.origin - self.origin; + push_z = 0; + push = normalize(push) * 10; + + ent.velocity += push; + return_value = 1; + } + ent = ent.chain; + } + return return_value; +} + + +void(float dist, vector vec) do_walk_to_vec = +{ + if(dist == 0) + return; + + self.ideal_yaw = vectoyaw(vec - self.origin); + if(self.outside == false) + { + push_away_zombies(); + //self.ideal_yaw += avoid_zombies(); //no longer relevant since our direction doesn't care about our yaw any more + } + ChangeYaw(); + vector new_velocity; + + float len; + len = vlen(self.origin - vec); + + if(dist > len)//if we're moving past our goal position + { + dist = len; + } + //This movement method is moving directly towards the goal, regardless of where our yaw is facing (fixes several issues) + new_velocity = normalize(vec - self.origin) * dist * 10; + + new_velocity_z = self.velocity_z; + self.velocity = new_velocity; +}; + +void(float dist) do_walk = +{ + do_walk_to_vec(dist,self.goalentity.origin); +}; + +void(float dist) walk_to_window = +{ + do_walk_to_vec(dist,self.goalorigin); +}; + +// unused +void(vector org, float scale) interpolateToVector = +{ + self.origin_x += (org_x - self.origin_x) * scale; + self.origin_y += (org_y - self.origin_y) * scale; + setorigin(self,self.origin); + self.zoom = 1;//value to let engine know to not check for collisions +} + +float(vector where) nearby = +{ + if(self.classname == "ai_zombie") + { + float xdist; + float ydist; + float zdist; + xdist = fabs(self.origin_x - where_x); + ydist = fabs(self.origin_y - where_y); + zdist = fabs(self.origin_z - where_z); + + if(xdist < 4 && ydist < 4)//horizontal distance is fairly close + { + if(zdist < 50)//vertical distance just has to be arbitrarily close + { + return 1; + } + } + return 0; + } + /*else if(self.classname == "ai_hellhound") + { + if(vlen(self.origin - what.origin) < 35) + { + if(vlen(self.origin - what.origin) < 6) + { + return 1; + } + else + { + interpolateToVector(what.origin,0.25); + } + } + }*/ + return 0; +}; + +void(float dist) Window_Walk = +{ + if(self.reload_delay < time) + Respawn(); + + if(self.hop_step == 0)//following path corners + { + if(self.goalentity == world) + { + if((!self.target) && (self.outside == TRUE)) + { + bprint(PRINT_HIGH, "Error: Outside zombie spawn point has no target.\n"); + Respawn(); + } + self.goalentity = find(world,targetname, self.target); + if(!self.goalentity) + { + bprint(PRINT_HIGH, "Error: Outside zombie spawn point target does not exist.\n"); + Respawn(); + } + } + + if(self.goalentity.classname == "path_corner" && nearby(self.goalentity.origin)) + { + if (self.goalentity.spawnflags & 1) //this path corner sets zombie on inside. + { + self.outside = FALSE; + self.goalentity = world; + self.enemy = find_new_enemy(self); + self.th_walk(); + return; + } + self.reload_delay = time + 30; + self.goalentity = find(world,targetname,self.goalentity.target); + + //Assumption is that when the zombie is outside, we can always walk from one path_corner to the next in a straight line, any devation should be corrected by the mapper + } + + do_walk(dist); + + if(self.goalentity.classname == "window") + { + if(!self.goalentity.box1owner) + { + //self.used = WBOX1; + self.goalentity.box1owner = self; + self.goalorigin = self.goalentity.box1; + self.hop_step = 3; + self.reload_delay = time + 30; + } + else if(!self.goalentity.box2owner) + { + //self.used = WBOX2; + self.goalentity.box2owner = self; + self.goalorigin = self.goalentity.box2; + self.hop_step = 3; + self.reload_delay = time + 30; + } + else if(!self.goalentity.box3owner) + { + //self.used = WBOX3; + self.goalentity.box3owner = self; + self.goalorigin = self.goalentity.box3; + self.hop_step = 3; + self.reload_delay = time + 30; + } + else if(vlen(self.origin - self.goalentity.origin) < 150) + { + //we don't claim the idlebox + //self.used = WIDLEBOX; + self.goalorigin = self.goalentity.idlebox; + self.hop_step = 1; + self.reload_delay = time + 30; + } + //else we continue walking to window until we either find one that's good, or we are close enough to chase idle_spot + } + } + else if(self.hop_step == 1)//walking to the window's idle location + { + if(nearby(self.goalorigin)) + { + self.hop_step = 2; + self.reload_delay = time + 30; + self.th_idle(); + } + else + { + walk_to_window(dist); + } + } + else if(self.hop_step == 2)//we're at idle box, waiting for a window attack box to be free... + { + if(!self.goalentity.box1owner) + { + //self.used = WBOX1; + self.goalentity.box1owner = self; + self.goalorigin = self.goalentity.box1; + self.hop_step = 3; + self.reload_delay = time + 30; + self.th_walk(); + } + else if(!self.goalentity.box2owner) + { + //self.used = WBOX2; + self.goalentity.box2owner = self; + self.goalorigin = self.goalentity.box2; + self.hop_step = 3; + self.reload_delay = time + 30; + self.th_walk(); + } + else if(!self.goalentity.box3owner) + { + //self.used = WBOX3; + self.goalentity.box3owner = self; + self.goalorigin = self.goalentity.box3; + self.hop_step = 3; + self.reload_delay = time + 30; + self.th_walk(); + } + } + else if(self.hop_step == 3)//walking to window attack box + { + if(nearby(self.goalorigin)) + { + self.hop_step = 4; + self.reload_delay = time + 30; + self.th_idle(); + } + else + { + walk_to_window(dist); + } + } + else if(self.hop_step == 4)//attacking box + { + if(self.chase_time < time) + { + if(self.angles_z != self.goalentity.angles_z) + { + self.ideal_yaw = self.goalentity.angles_z; + ChangeYaw(); + return; + } + if(self.goalentity.health > 0) + { + self.reload_delay = time + 30; + self.th_melee(); + if(rounds <= 5) + self.chase_time = time + 1.5; + else + self.chase_time = time + 0.75; + return; + } + } + if(self.goalentity.health <= 0) + { + self.outside = 2; + self.chase_time = 0; + self.hop_step = 0; + } + else return; + } +}; + +// +// kind of a shoddy fix, but essentially what we do to fix +// issues with zomb ents colliding with each other during hopping +// is make sure we wait a bit longer before freeing the window for +// another usage. +// +void() free_window = +{ + self.usedent = world; +} + +void(float dist) Window_Hop = +{ + if(self.hop_step == 0) { + if(self.goalentity.box1owner == self) {//we're at center box. + self.hop_step = 4; + } else { + self.hop_step = 1;//wait for box1 to be free so we can claim it and walk to it. + self.th_idle(); + } + } + if(self.hop_step == 1) {//waiting idly for box1 to be free, when free, we will claim it. + if(!self.goalentity.box1owner || self.goalentity.box1owner == self) { + self.goalentity.box1owner = self; + + if(self.goalentity.box2owner == self) + self.goalentity.box2owner = world; + if(self.goalentity.box3owner == self) + self.goalentity.box3owner = world; + + //self.used = WBOX1; + + self.goalorigin = self.goalentity.box1; + self.hop_step = 2; + self.th_walk(); + } + } + if(self.hop_step == 2) {//we've claimed it, walk to box1 + if(nearby(self.goalorigin)) { + self.hop_step = 4; + self.angles = self.goalentity.angles; + } else { + walk_to_window(dist); + } + } + + if(self.hop_step == 4 && self.chase_time < time) {//we're at this step because we already own box1, so don't even check if window is busy... + if(!self.goalentity.usedent) { + self.hop_step = 5; + self.angles = self.goalentity.angles; + self.goalentity.box1owner = world;//free box1 + self.goalentity.usedent = self;//we own the window + //don't need to set goalorigin here + //self.used = WWINDOW; + self.chase_time = 0; + self.th_windowhop(); + return; + } else { + self.tries++; + self.chase_time = time + 0.2; + if(self.tries > 10) { + // wait enough time before freeing window, to give time for zomb to move. + self.goalentity.think = free_window; + self.goalentity.nextthink = time + 0.5; + //self.goalentity.usedent = world;//free up the window if we've been waiting to hop + } + } + } + + if(self.hop_step == 6) { + self.outside = FALSE; + //self.goalentity.usedent = world;//free up the window, we're done hopping it + //self.used = 0; + self.goalentity.think = free_window; + self.goalentity.nextthink = time + 0.5; + self.goalentity = world; + self.enemy = find_new_enemy(self); + //self.th_die(); + self.th_walk(); + } +} + +float() TryWalkToEnemy = +{ + //was tracebox + float TraceResult; + TraceResult = tracemove(self.origin,VEC_HULL_MIN,VEC_HULL_MAX,self.enemy.origin,TRUE,self); + if(TraceResult == 1) { + self.goalentity = self.enemy; + self.chase_time = time + 7; + return 1; + } else { + return 0; + } +}; + +#ifndef PC +float() SetUpGoalDummy = +{ + self.goaldummy.origin = Get_Waypoint_Near(self); + setorigin(self.goaldummy,self.goaldummy.origin); + self.goalentity = self.goaldummy; + + if (!self.goalentity) + bprint(PRINT_HIGH, "Could not find waypoint for zombie\n"); + + return 1; //because we can't actually know if it was good or not... yet +}; +#endif + +void() PathfindToEnemy = +{ + float path_result; + float path_failure; + + //just to stop any warns. + path_failure = 0; + + #ifndef PC + path_result = Do_Pathfind_psp(self, self.enemy); + #else + path_result = Do_Pathfind(self, self.enemy); + #endif + + //FIXME - further test if this can equate to 0 in FTE.. + if (path_result >= 1) { + + #ifndef PC + self.goaldummy.origin = Get_First_Waypoint(self, self.origin, VEC_HULL_MIN, VEC_HULL_MAX); + setorigin(self.goaldummy, self.goaldummy.origin); + path_failure = path_result; + #else + self.goalway = path_result; + setorigin(self.goaldummy,waypoints[self.goalway].org); + path_failure = self.goalway; + #endif + + self.goalentity = self.goaldummy; + self.chase_time = time + 7; + + } else if (path_failure == -1) { + self.goalentity = self.enemy; + self.chase_time = time + 6; + } else { + #ifdef PC + bprint(PRINT_HIGH, "FirstPathfind Failure\n"); + #endif + } +} + +void() NextPathfindToEnemy { + // same as PathfindToEnemy on non-FTE platforms + #ifndef PC + float path_success; + path_success = Do_Pathfind_psp(self,self.enemy); + if(path_success ==1) { + self.goaldummy.origin = Get_Next_Waypoint(self,self.origin,VEC_HULL_MIN,VEC_HULL_MAX); + setorigin(self.goaldummy,self.goaldummy.origin); + self.goalentity = self.goaldummy; + self.chase_time = time + 7; + } else if(path_success == -1){ + self.goalentity = self.enemy; + self.chase_time = time + 6; + } else { + bprint(PRINT_HIGH, "NextPathfind Failure\n"); // this lags like hell + } + #else + self.way_cur++; + + if (self.way_cur < 40 && self.way_path[self.way_cur] != -1) { + self.goalway = self.way_path[self.way_cur]; + setorigin(self.goaldummy,waypoints[self.goalway-1].org); + } else { + self.way_cur = 0; + } + #endif +} + +#ifdef PC +float(vector start, vector min, vector max, vector end, float nomonsters, entity forent) tracemove +{ + //was tracebox + traceline(start,end,nomonsters,forent); + + if(trace_ent == forent || trace_endpos == end) { + return 1; + } else { + return 0; + } +} +#endif + +void(float dist) Inside_Walk = { + if(self.enemy_timeout < time || self.enemy == world) { + self.enemy_timeout = time + 5; + local entity oldEnemy; + oldEnemy = self.enemy; + self.enemy = find_new_enemy(self); + } + //================Check for proximity to player =========== + if(vlen(self.enemy.origin - self.origin) < 60) { + if(self.enemy.classname == "monkey") { + self.th_idle(); + } + + if(self.attack_delay < time) { + self.attack_delay = time + 1 + (1 * random()); + self.th_melee(); + self.goalentity = self.enemy; + self.chase_time = time + 5; + } + return; + } + + if(vlen(self.enemy.origin - self.origin) < 600) {//50 feet + if(self.goalentity == self.enemy && self.chase_enemy_time > time) { + return; + } + if(TryWalkToEnemy()) + { + self.chase_enemy_time = time + 0.5; + return; + } + } + + if(self.goalentity == self.enemy) { + self.goalentity = self.goaldummy; + self.chase_time = 0; + } + //============= No Target ==================== + if(self.goalentity == world) {//not sure when this would ever occur... but whatever. + self.goalentity = self.goaldummy; + } + //============ GoalDummy is Target ============ + if(self.goalentity == self.goaldummy) { + if(nearby(self.goaldummy.origin)) { + #ifndef PC + NextPathfindToEnemy(); + #else + PathfindToEnemy(); + #endif + } + + if(self.chase_time < time) { + if(self.goaldummy.origin != world.origin && tracemove(self.origin,VEC_HULL_MIN,VEC_HULL_MAX,self.goalentity.origin,TRUE,self) == 1) { + self.chase_time = time + 7; + } else { + PathfindToEnemy(); + } + } + } +} + +.float droptime; +void(float dist) Zombie_Walk = { + //Resetting velocity from last frame (except for vertical) + self.velocity_x = 0; + self.velocity_y = 0; + //self.flags = self.flags | FL_PARTIALGROUND; + + //check_onfire(); + if (!(self.flags & FL_ONGROUND)) { + + if (!self.droptime) { + self.droptime = time + 1; + } else if (self.droptime < time) { + self.droptime = 0; + //bprint(PRINT_HIGH, "not on ground\n"); + self.th_fall(); + return; + } + } + + if(self.outside == TRUE) { + //handle special walk case for walking to org + Window_Walk(dist); + return; + } + + if(self.outside == 2) { + //play_sound_z(2); + Window_Hop(dist); + //handle special walk case for walking to org + return; + } + if(self.outside == FALSE) { + if(self.goalentity == self.enemy) { + if(vlen(self.origin - self.enemy.origin) < 60) { + return; + } + } + } + do_walk(dist); +} + +void() Zombie_AI = { + //dist = 0; + float dist = 0; + self.flags = self.flags | FL_PARTIALGROUND; + //check_onfire(); + + if(self.outside == TRUE) { + play_sound_z(2); + //self.calc_time = time + (0.3 * random()); + //Window_Walk(dist); + return; + } else if(self.outside == 2) { + play_sound_z(2); + //Window_Hop(0); + return; + } else if(self.outside == FALSE) { + play_sound_z(2); + //self.calc_time = time + (0.25 + (0.15 * random())); + Inside_Walk(dist); + } +} + +//This function ensures that only one zombie's ai is done at a time, brings down lag significantly +void() Do_Zombie_AI = { + local entity z; + + z = find(lastzombie,aistatus,"1"); + if(z == world) { + z = find(world,aistatus,"1"); + if(z == world) { + return;//no zombies alive. + } + } + local entity oself; + oself = self; + self = z; + //if(z.classname == "ai_zombie")//removed because only zombies atm + Zombie_AI(); + self = oself; + lastzombie = z; +} \ No newline at end of file diff --git a/source/server/ai/crawler_core.qc b/source/server/ai/crawler_core.qc new file mode 100644 index 0000000..efe865e --- /dev/null +++ b/source/server/ai/crawler_core.qc @@ -0,0 +1,274 @@ +/* + server/ai/crawler_core.qc + + crawler things + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +//========================= Crawling =========================== +void() zombie_crawl1; +void() crawlerify1; + +void() Crawler_Walk_Setup +{ + zombie_crawl1(); +} + + +void() crawler_footstep +{ + if(self.laststep == 1) + { + self.laststep = 0; + sound(self,CHAN_VOICE,"sounds/zombie/sc0.wav",1,ATTN_NORM); + } + else + { + self.laststep = 1; + sound(self,CHAN_VOICE,"sounds/zombie/sc1.wav",1,ATTN_NORM); + } +} + +//14-28 +void() zombie_crawl1 =[ 0, zombie_crawl2 ] {Zombie_Walk(2.955);SetZombieHitBox(CRAWLER_BBOX);/*if(freeze_time < time)*/ self.frame = 14;Zombie_Think();}; +void() zombie_crawl2 =[ 1, zombie_crawl3 ] {Zombie_Walk(3.841); self.frame = 15;Zombie_Think();}; +void() zombie_crawl3 =[ 2, zombie_crawl4 ] {Zombie_Walk(0.886); self.frame = 16;Zombie_Think();}; +void() zombie_crawl4 =[ 3, zombie_crawl5 ] {Zombie_Walk(1.871); self.frame = 17;Zombie_Think();}; +void() zombie_crawl5 =[ 4, zombie_crawl6 ] {Zombie_Walk(3.25); self.frame = 18;Zombie_Think();}; +void() zombie_crawl6 =[ 5, zombie_crawl7 ] {Zombie_Walk(1.314); self.frame = 19;Zombie_Think();}; +void() zombie_crawl7 =[ 6, zombie_crawl8 ] {Zombie_Walk(0.908); self.frame = 20;Zombie_Think();crawler_footstep();};//footstep +void() zombie_crawl8 =[ 7, zombie_crawl9 ] {Zombie_Walk(0.776); self.frame = 21;Zombie_Think();}; +void() zombie_crawl9 =[ 8, zombie_crawl10 ] {Zombie_Walk(0.698); self.frame = 22;Zombie_Think();}; +void() zombie_crawl10 =[ 9, zombie_crawl11 ] {Zombie_Walk(0.641); self.frame = 23;Zombie_Think();}; +void() zombie_crawl11 =[ 10, zombie_crawl12 ] {Zombie_Walk(0.588); self.frame = 24;Zombie_Think();}; +void() zombie_crawl12 =[ 11, zombie_crawl13 ] {Zombie_Walk(0.295); self.frame = 25;Zombie_Think();}; +void() zombie_crawl13 =[ 12, zombie_crawl14 ] {Zombie_Walk(1.182); self.frame = 26;Zombie_Think();}; +void() zombie_crawl14 =[ 13, zombie_crawl15 ] {Zombie_Walk(0.492); self.frame = 27;Zombie_Think();}; +void() zombie_crawl15 =[ 14, Crawler_Walk_Setup ] {Zombie_Walk(2.856); self.frame = 28;Zombie_Think();crawler_footstep();};//footstep + +//====================== Zombie To Crawler ======================= +//0-4 +void() crawlerify1 =[ 0, crawlerify2 ] {SetZombieHitBox(CRAWLER_BBOX); self.frame = 0;Zombie_Think();}; +void() crawlerify2 =[ 1, crawlerify3 ] {self.frame = 1;Zombie_Think();}; +void() crawlerify3 =[ 2, crawlerify4 ] {self.frame = 2;Zombie_Think();}; +void() crawlerify4 =[ 3, crawlerify5 ] {self.frame = 3;Zombie_Think();crawler_footstep();crawler_footstep();};//hit floor +void() crawlerify5 =[ 4, Crawler_Walk_Setup ] {self.frame = 4;Zombie_Think();if(self.health <= 0) self.th_die();};//need to check for death since a grenade will not kill a zombie, only make him a crawler + +//====================== Crawler Idle ============================== +//4-13 +void() crawler_idle1 =[ 0, crawler_idle2 ] {SetZombieHitBox(CRAWLER_BBOX);Zombie_Walk(0); self.frame = 4;Zombie_Think();}; +void() crawler_idle2 =[ 1, crawler_idle3 ] {Zombie_Walk(0); self.frame = 5;Zombie_Think();}; +void() crawler_idle3 =[ 2, crawler_idle4 ] {Zombie_Walk(0); self.frame = 6;Zombie_Think();}; +void() crawler_idle4 =[ 3, crawler_idle5 ] {Zombie_Walk(0); self.frame = 7;Zombie_Think();}; +void() crawler_idle5 =[ 4, crawler_idle6 ] {Zombie_Walk(0); self.frame = 8;Zombie_Think();}; +void() crawler_idle6 =[ 5, crawler_idle7 ] {Zombie_Walk(0); self.frame = 9;Zombie_Think();}; +void() crawler_idle7 =[ 6, crawler_idle8 ] {Zombie_Walk(0); self.frame = 10;Zombie_Think();}; +void() crawler_idle8 =[ 7, crawler_idle9 ] {Zombie_Walk(0); self.frame = 11;Zombie_Think();}; +void() crawler_idle9 =[ 8, crawler_idle10 ] {Zombie_Walk(0); self.frame = 12;Zombie_Think();}; +void() crawler_idle10 =[ 9, crawler_idle1 ] {Zombie_Walk(0); self.frame = 13;Zombie_Think();}; +//===================== Crawler Attack =========================== +//29-36 +void() crawler_atk1 =[ 0, crawler_atk2] {SetZombieHitBox(CRAWLER_BBOX);self.frame = 29;Zombie_Think();}; +void() crawler_atk2 =[ 1, crawler_atk3] {self.frame = 30;Zombie_Think();}; +void() crawler_atk3 =[ 2, crawler_atk4] {self.frame = 31;Zombie_Think();}; +void() crawler_atk4 =[ 3, crawler_atk5] {self.frame = 32;zombie_attack2();Zombie_Think();}; +void() crawler_atk5 =[ 4, crawler_atk6] {self.frame = 33;Zombie_Think();}; +void() crawler_atk6 =[ 5, crawler_atk7] {self.frame = 34;Zombie_Think();}; +void() crawler_atk7 =[ 6, crawler_atk8] {self.frame = 35;Zombie_Think();}; +void() crawler_atk8 =[ 7, zombie_decide] {self.frame = 36;Zombie_Think();}; + +//====================== Crawler Delay Attack ============================== +//4-13 + +void(float which) crawler_attack_choose = +{ + self.angles_y = vectoyaw(self.enemy.origin - self.origin); + if(which != 1) + { + if(random() > 0.2) + { + return; + } + } + if(vlen(self.enemy.origin - self.origin) > 60)//too far, cancel attack + { + zombie_decide(); + return; + } + play_sound_z(1); + + crawler_atk1(); +}; + + +void() crawler_da1 =[ 0, crawler_da2 ] {SetZombieHitBox(CRAWLER_BBOX);crawler_attack_choose(0); self.frame = 4;Zombie_Think();}; +void() crawler_da2 =[ 1, crawler_da3 ] {crawler_attack_choose(0); self.frame = 5;Zombie_Think();}; +void() crawler_da3 =[ 2, crawler_da4 ] {crawler_attack_choose(0); self.frame = 6;Zombie_Think();}; +void() crawler_da4 =[ 3, crawler_da5 ] {crawler_attack_choose(0); self.frame = 7;Zombie_Think();}; +void() crawler_da5 =[ 4, crawler_da6 ] {crawler_attack_choose(0); self.frame = 8;Zombie_Think();}; +void() crawler_da6 =[ 5, crawler_da7 ] {crawler_attack_choose(0); self.frame = 9;Zombie_Think();}; +void() crawler_da7 =[ 6, crawler_da8 ] {crawler_attack_choose(0); self.frame = 10;Zombie_Think();}; +void() crawler_da8 =[ 7, crawler_da9 ] {crawler_attack_choose(0); self.frame = 11;Zombie_Think();}; +void() crawler_da9 =[ 8, crawler_da10 ] {crawler_attack_choose(0); self.frame = 12;Zombie_Think();}; +void() crawler_da10 =[ 9, crawler_da1 ] {crawler_attack_choose(0); self.frame = 13;Zombie_Think();}; + + +//====================== Crawler Death ============================ +//37-39 +void() crawler_die1 =[ 0, crawler_die2] {SetZombieHitBox(CRAWLER_BBOX);self.frame = 37;}; +void() crawler_die2 =[ 1, crawler_die3] {self.frame = 38;}; +void() crawler_die3 =[ 2, SUB_Null] {self.iszomb = 0; self.frame = 39;self.nextthink = time + 3;self.think = removeZombie; addmoney(other, 60, true); if (crawler_num > 0) {crawler_num = crawler_num - 1;}}; + +//======================== Climb Over Barricade ==================== +//40-70 +void() crawler_climbBarricade1 =[ 0, crawler_climbBarricade2] {self.movetype = MOVETYPE_STEP;self.zoom = 1;self.frame = 40;Zombie_Think();}; +void() crawler_climbBarricade2 =[ 1, crawler_climbBarricade3] {self.frame = 41; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade3 =[ 2, crawler_climbBarricade4] {self.frame = 42; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade4 =[ 3, crawler_climbBarricade5] {self.frame = 43; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade5 =[ 4, crawler_climbBarricade6] {self.frame = 44; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade6 =[ 5, crawler_climbBarricade7] {self.frame = 45; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade7 =[ 6, crawler_climbBarricade8] {self.frame = 46; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade8 =[ 7, crawler_climbBarricade9] {self.frame = 47; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade9 =[ 8, crawler_climbBarricade10] {self.frame = 48; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade10 =[ 9, crawler_climbBarricade11] {self.frame = 49; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade11 =[ 10, crawler_climbBarricade12] {self.frame = 50; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade12 =[ 11, crawler_climbBarricade13] {self.frame = 51; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade13 =[ 12, crawler_climbBarricade14] {self.frame = 52; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade14 =[ 13, crawler_climbBarricade15] {self.frame = 53; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade15 =[ 14, crawler_climbBarricade16] {self.frame = 54; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade16 =[ 15, crawler_climbBarricade17] {self.frame = 55; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade17 =[ 16, crawler_climbBarricade18] {self.frame = 56; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade18 =[ 17, crawler_climbBarricade19] {self.frame = 57; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade19 =[ 18, crawler_climbBarricade20] {self.frame = 58; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade20 =[ 19, crawler_climbBarricade21] {self.frame = 59; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade21 =[ 20, crawler_climbBarricade22] {self.frame = 60; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade22 =[ 21, crawler_climbBarricade23] {self.frame = 61; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade23 =[ 22, crawler_climbBarricade24] {self.frame = 62; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade24 =[ 23, crawler_climbBarricade25] {self.frame = 63; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade25 =[ 24, crawler_climbBarricade26] {self.frame = 64; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade26 =[ 25, crawler_climbBarricade27] {self.frame = 65; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade27 =[ 26, crawler_climbBarricade28] {self.frame = 66; moveforwardalittle(0);Zombie_Think();}; +void() crawler_climbBarricade28 =[ 27, crawler_climbBarricade29] {self.frame = 67; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade29 =[ 28, crawler_climbBarricade30] {self.frame = 68; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade30 =[ 29, crawler_climbBarricade31] {self.frame = 69; moveforwardalittle(1);Zombie_Think();}; +void() crawler_climbBarricade31 =[ 30, SUB_Null] {self.frame = 70; self.movetype = MOVETYPE_WALK; self.zoom = 1;self.state = 0;self.hop_step = 6; self.th_walk();Zombie_Think();}; + +//======================== Crawler Fall ============================= +//71 - 77 +void() crawler_fall1 =[ 0, crawler_fall2] {SetZombieHitBox(CRAWLER_BBOX);self.fall = TRUE;CheckLand();self.frame=71;Zombie_Think();}; +void() crawler_fall2 =[ 1, crawler_fall3] {self.fall = TRUE;CheckLand();self.frame=72;Zombie_Think();}; +void() crawler_fall3 =[ 2, crawler_fall4] {self.fall = TRUE;CheckLand();self.frame=73;Zombie_Think();}; +void() crawler_fall4 =[ 3, crawler_fall5] {self.fall = TRUE;CheckLand();self.frame=74;Zombie_Think();}; +void() crawler_fall5 =[ 4, crawler_fall6] {self.fall = TRUE;CheckLand();self.frame=75;Zombie_Think();}; +void() crawler_fall6 =[ 5, crawler_fall7] {self.fall = TRUE;CheckLand();self.frame=76;Zombie_Think();}; +void() crawler_fall7 =[ 6, crawler_fall1] {self.fall = TRUE;CheckLand();self.frame=77;Zombie_Think();}; +//======================== Crawler Land ============================ +//77-86 +void() crawler_land1 =[ 0, crawler_land2] {SetZombieHitBox(CRAWLER_BBOX);self.frame=77;Zombie_Think();}; +void() crawler_land2 =[ 1, crawler_land3] {self.frame=78;Zombie_Think();}; +void() crawler_land3 =[ 2, crawler_land4] {self.frame=79;Zombie_Think();}; +void() crawler_land4 =[ 3, crawler_land5] {self.frame=80;Zombie_Think();}; +void() crawler_land5 =[ 4, crawler_land6] {self.frame=81;Zombie_Think();}; +void() crawler_land6 =[ 5, crawler_land7] {self.frame=82;Zombie_Think();}; +void() crawler_land7 =[ 6, crawler_land8] {self.frame=83;Zombie_Think();}; +void() crawler_land8 =[ 7, crawler_land9] {self.frame=84;Zombie_Think();}; +void() crawler_land9 =[ 8, crawler_land10] {self.frame=85;Zombie_Think();}; +void() crawler_land10 =[ 9, Crawler_Walk_Setup] {self.frame=86;Zombie_Think();}; +//======================== Crawler Rip Board ======================= +//103-126 + +void() crawler_rip_board1 =[ 0, crawler_rip_board2] {SetZombieHitBox(CRAWLER_BBOX);self.frame=103;Zombie_Think();}; +void() crawler_rip_board2 =[ 1, crawler_rip_board3] {self.frame=104;Zombie_Think();}; +void() crawler_rip_board3 =[ 2, crawler_rip_board4] {self.frame=105;Zombie_Think();}; +void() crawler_rip_board4 =[ 3, crawler_rip_board5] {self.frame=106;Zombie_Think();}; +void() crawler_rip_board5 =[ 4, crawler_rip_board6] {self.frame=107;Zombie_Think();}; +void() crawler_rip_board6 =[ 5, crawler_rip_board7] {self.frame=108;Zombie_Think();}; +void() crawler_rip_board7 =[ 6, crawler_rip_board8] {self.frame=109;Zombie_Think();}; +void() crawler_rip_board8 =[ 7, crawler_rip_board9] {self.frame=110;Zombie_Think();}; +void() crawler_rip_board9 =[ 8, crawler_rip_board10] {self.frame=111;Zombie_Think();}; +void() crawler_rip_board10 =[ 9, crawler_rip_board11] {self.frame=112;Zombie_Think();}; +void() crawler_rip_board11 =[ 10, crawler_rip_board12] {self.frame=113;Zombie_Think();}; +void() crawler_rip_board12 =[ 11, crawler_rip_board13] {self.frame=114;Zombie_Think();}; +void() crawler_rip_board13 =[ 12, crawler_rip_board14] {self.frame=115;zombie_attack2();}; +void() crawler_rip_board14 =[ 13, crawler_rip_board15] {self.frame=116;Zombie_Think();}; +void() crawler_rip_board15 =[ 14, crawler_rip_board16] {self.frame=117;Zombie_Think();}; +void() crawler_rip_board16 =[ 15, crawler_rip_board17] {self.frame=118;Zombie_Think();}; +void() crawler_rip_board17 =[ 16, crawler_rip_board18] {self.frame=119;Zombie_Think();}; +void() crawler_rip_board18 =[ 17, crawler_rip_board19] {self.frame=120;Zombie_Think();}; +void() crawler_rip_board19 =[ 18, crawler_rip_board20] {self.frame=121;Zombie_Think();}; +void() crawler_rip_board20 =[ 19, crawler_rip_board21] {self.frame=122;Zombie_Think();}; +void() crawler_rip_board21 =[ 20, crawler_rip_board22] {self.frame=123;Zombie_Think();}; +void() crawler_rip_board22 =[ 21, crawler_rip_board23] {self.frame=124;Zombie_Think();}; +void() crawler_rip_board23 =[ 22, crawler_rip_board24] {self.frame=125;Zombie_Think();}; +void() crawler_rip_board24 =[ 23, zombie_decide] {self.frame=126;Zombie_Think();}; + +void(entity who) makeCrawler = +{ + if(who.state == 1) + { + who.crawling = 2; + return; + } + who.crawling = 2;//this means we haven't technically initiated it, but we're going to... + + who.th_die = Zombie_Death; + who.th_walk = Crawler_Walk_Setup; + who.th_melee = zombie_attack; + who.th_idle = crawler_idle1; + who.th_windowhop = crawler_climbBarricade1; + + who.th_fall = crawler_fall1; + who.th_land = crawler_land1; + + //who.th_jump = zombie_jump1; + //who.th_grabledge = zombie_grabledge1; + //who.th_diewunder = die_wunder1; + + setmodel(who,"models/ai/zcbod.mdl"); + if(who.head.deadflag) + { + setmodel(who.head,"models/ai/zchead.mdl"); + //updateLimb (who, 0, world); + } + if(who.larm.deadflag) + { + setmodel(who.larm,"models/ai/zclarm.mdl"); + //updateLimb (who, 1, world); + } + if(who.rarm.deadflag) + { + setmodel(who.rarm,"models/ai/zcrarm.mdl"); + //updateLimb (who, 2, world); + } + who.crawling = 1; + + setsize(who,'-8 -8 -32','8 8 -15'); + + local entity oself; + oself = self; + self = who; + crawlerify1(); + self = oself; + + crawler_num = crawler_num + 1; +} diff --git a/source/server/ai/dog_core.qc b/source/server/ai/dog_core.qc new file mode 100644 index 0000000..3b614fe --- /dev/null +++ b/source/server/ai/dog_core.qc @@ -0,0 +1,374 @@ +/* + server/ai/dog_core.qc + + dog things + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() Dog_Think; +void() Dog_Death; + +// DOG RUN +// 0-6 +$frame dogrun1 dogrun2 dogrun3 dogrun4 dogrun5 dogrun6 dogrun7 +void() dog_runanim =[ $dogrun1, dog_runanim2 ] {Dog_Think();Zombie_Walk(25);self.frame = 0;}; +void() dog_runanim2 =[ $dogrun2, dog_runanim3 ] {Dog_Think();Zombie_Walk(15);self.frame = 1;}; +void() dog_runanim3 =[ $dogrun3, dog_runanim4 ] {Dog_Think();Zombie_Walk(15);self.frame = 2;}; +void() dog_runanim4 =[ $dogrun4, dog_runanim5 ] {Dog_Think();Zombie_Walk(15);self.frame = 3;}; +void() dog_runanim5 =[ $dogrun5, dog_runanim6 ] {Dog_Think();Zombie_Walk(15);self.frame = 4;}; +void() dog_runanim6 =[ $dogrun6, dog_runanim7 ] {Dog_Think();Zombie_Walk(15);self.frame = 5;}; +void() dog_runanim7 =[ $dogrun7, dog_runanim ] {Dog_Think();Zombie_Walk(15);self.frame = 6;}; + +// DOG WALK +// 7-18 +$frame dogwalk1 dogwalk2 dogwalk3 dogwalk4 dogwalk5 dogwalk6 dogwalk7 dogwalk8 dogwalk9 dogwalk10 dogwalk11 dogwalk12 +void() dog_walkanim =[ $dogwalk1, dog_walkanim2 ] {Dog_Think();Zombie_Walk(8);self.frame = 7;}; +void() dog_walkanim2 =[ $dogwalk2, dog_walkanim3 ] {Dog_Think();Zombie_Walk(8);self.frame = 8;}; +void() dog_walkanim3 =[ $dogwalk3, dog_walkanim4 ] {Dog_Think();Zombie_Walk(8);self.frame = 9;}; +void() dog_walkanim4 =[ $dogwalk4, dog_walkanim5 ] {Dog_Think();Zombie_Walk(8);self.frame = 10;}; +void() dog_walkanim5 =[ $dogwalk5, dog_walkanim6 ] {Dog_Think();Zombie_Walk(8);self.frame = 11;}; +void() dog_walkanim6 =[ $dogwalk6, dog_walkanim7 ] {Dog_Think();Zombie_Walk(8);self.frame = 12;}; +void() dog_walkanim7 =[ $dogwalk7, dog_walkanim8 ] {Dog_Think();Zombie_Walk(8);self.frame = 13;}; +void() dog_walkanim8 =[ $dogwalk8, dog_walkanim9 ] {Dog_Think();Zombie_Walk(8);self.frame = 14;}; +void() dog_walkanim9 =[ $dogwalk9, dog_walkanim10 ] {Dog_Think();Zombie_Walk(8);self.frame = 15;}; +void() dog_walkanim10 =[ $dogwalk10, dog_walkanim11 ] {Dog_Think();Zombie_Walk(8);self.frame = 16;}; +void() dog_walkanim11 =[ $dogwalk11, dog_walkanim12 ] {Dog_Think();Zombie_Walk(8);self.frame = 17;}; +void() dog_walkanim12 =[ $dogwalk12, dog_walkanim ] {Dog_Think();Zombie_Walk(8);self.frame = 18;}; + +// DOG IDLE +// 19-24 +$frame dogstand1 dogstand2 dogstand3 dogstand4 dogstand5 dogstand6 +void() dog_idleanim =[ $dogstand1, dog_idleanim2 ] {self.frame = 19;}; +void() dog_idleanim2 =[ $dogstand2, dog_idleanim3 ] {self.frame = 20;}; +void() dog_idleanim3 =[ $dogstand3, dog_idleanim4 ] {self.frame = 21;}; +void() dog_idleanim4 =[ $dogstand4, dog_idleanim5 ] {self.frame = 22;}; +void() dog_idleanim5 =[ $dogstand5, dog_idleanim6 ] {self.frame = 23;}; +void() dog_idleanim6 =[ $dogstand6, dog_idleanim ] {self.frame = 24;}; + +// DOG MELEE +// 25-34 +$frame dogmelee1 dogmelee2 dogmelee3 dogmelee4 dogmelee5 dogmelee6 dogmelee7 dogmelee8 dogmelee9 dogmelee10 +void() dog_meleeanim =[ $dogmelee1, dog_meleeanim2 ] {Zombie_Walk(0);self.frame = 25;}; +void() dog_meleeanim2 =[ $dogmelee2, dog_meleeanim3 ] {Zombie_Walk(0);self.frame = 26;}; +void() dog_meleeanim3 =[ $dogmelee3, dog_meleeanim4 ] {Zombie_Walk(0);self.frame = 27;}; +void() dog_meleeanim4 =[ $dogmelee4, dog_meleeanim5 ] {Zombie_Walk(0);self.frame = 28;}; +void() dog_meleeanim5 =[ $dogmelee5, dog_meleeanim6 ] {Zombie_Walk(0);self.frame = 29;}; +void() dog_meleeanim6 =[ $dogmelee6, dog_meleeanim7 ] {Zombie_Walk(0);zombie_attack2();self.frame = 30;}; +void() dog_meleeanim7 =[ $dogmelee7, dog_meleeanim8 ] {Zombie_Walk(0);self.frame = 31;}; +void() dog_meleeanim8 =[ $dogmelee8, dog_meleeanim9 ] {Zombie_Walk(0);self.frame = 32;}; +void() dog_meleeanim9 =[ $dogmelee9, dog_meleeanim10 ] {Zombie_Walk(0);self.frame = 33;}; +void() dog_meleeanim10 =[ $dogmelee10, dog_runanim ] {Zombie_Walk(0);self.frame = 34;}; + +// DOG DEATH +// 35 - 36 +$frame dogdeath1 dogdeath2 +void() dog_deathanim =[ $dogdeath1, dog_deathanim2 ] {self.frame = 35;}; +void() dog_deathanim2 =[ $dogdeath2, SUB_Null ] {self.nextthink = time + 3; self.think = removeZombie; self.frame = 36;}; + +// DOG WAFFE DEATH +// 37-46 +$frame dogwunder1 dogwunder2 dogwunder3 dogwunder4 dogwunder5 dogwunder6 dogwunder7 dogwunder8 dogwunder9 dogwunder10 +void() die_dog_wunder1 =[ $dogwunder1, die_dog_wunder2 ] {Zombie_Walk(0);Zombie_Tesla_Light();self.frame = 37;}; +void() die_dog_wunder2 =[ $dogwunder2, die_dog_wunder3 ] {Zombie_Walk(0);self.frame = 38;}; +void() die_dog_wunder3 =[ $dogwunder3, die_dog_wunder4 ] {Zombie_Walk(0);self.frame = 39;}; +void() die_dog_wunder4 =[ $dogwunder4, die_dog_wunder5 ] {Zombie_Walk(0);self.frame = 40;}; +void() die_dog_wunder5 =[ $dogwunder5, die_dog_wunder6 ] {Zombie_Walk(0);self.frame = 41;}; +void() die_dog_wunder6 =[ $dogwunder6, die_dog_wunder7 ] {Zombie_Walk(0);Zombie_Find_Tesla_Target();self.frame = 42;}; +void() die_dog_wunder7 =[ $dogwunder7, die_dog_wunder8 ] {Zombie_Walk(0);self.frame = 43;}; +void() die_dog_wunder8 =[ $dogwunder8, die_dog_wunder9 ] {Zombie_Walk(0);self.frame = 44;}; +void() die_dog_wunder9 =[ $dogwunder9, die_dog_wunder10 ] {Zombie_Walk(0);self.frame = 45;}; +void() die_dog_wunder10 =[ $dogwunder10, die_dog_wunder10 ] {Zombie_Walk(0);self.frame = 46;self.health = 0; self.th_die();}; + +// DOG LIGHTNING SPAWN +// 1-13 +$frame dls1 dls2 dls3 dls4 dls5 dls6 dls7 dls8 dls9 dls10 dls11 dls12 dls13 +void() dog_lightninganim =[ $dls1, dog_lightninganim2 ] {self.frame = 1;}; +void() dog_lightninganim2 =[ $dls2, dog_lightninganim3 ] {self.frame = 2;}; +void() dog_lightninganim3 =[ $dls3, dog_lightninganim4 ] {self.frame = 3;}; +void() dog_lightninganim4 =[ $dls4, dog_lightninganim5 ] {self.frame = 4;}; +void() dog_lightninganim5 =[ $dls5, dog_lightninganim6 ] {self.frame = 5;}; +void() dog_lightninganim6 =[ $dls6, dog_lightninganim7 ] {self.frame = 6;}; +void() dog_lightninganim7 =[ $dls7, dog_lightninganim8 ] {self.frame = 7;}; +void() dog_lightninganim8 =[ $dls8, dog_lightninganim9 ] {self.frame = 8;}; +void() dog_lightninganim9 =[ $dls9, dog_lightninganim10 ] {self.frame = 9;}; +void() dog_lightninganim10 =[ $dls10, dog_lightninganim11 ] {self.frame = 10;}; +void() dog_lightninganim11 =[ $dls11, dog_lightninganim12 ] {self.frame = 11;}; +void() dog_lightninganim12 =[ $dls12, dog_lightninganim13 ] {self.frame = 12;}; +void() dog_lightninganim13 =[ $dls13, SUB_Null ] {self.frame = 13; remove(self);}; + +// DOG EXPLODE SPRITE +// 1-5 +$frame des1 des2 des3 des4 des5 des6 +void() dog_explodeanim =[ $des1, dog_explodeanim2 ] {self.frame = 1;}; +void() dog_explodeanim2 =[ $des2, dog_explodeanim3 ] {self.frame = 2;}; +void() dog_explodeanim3 =[ $des3, dog_explodeanim4 ] {self.frame = 3;}; +void() dog_explodeanim4 =[ $des4, dog_explodeanim5 ] {self.frame = 4;}; +void() dog_explodeanim5 =[ $des5, dog_explodeanim6 ] {self.frame = 5;}; +void() dog_explodeanim6 =[ $des5, SUB_Null ] {remove(self);}; + +void() Dog_Walk_Setup = +{ + if (self.walktype == 1) + dog_walkanim(); + else + dog_runanim(); +}; + +// decide our walktype based on player distance +void() Dog_Think = +{ + // FIXME - crash + self.walktype = 2; + /* + if(vlen(self.enemy.origin - self.origin) < 300) + self.walktype = 2; + else + self.walktype = 1;*/ +} + +float dogCount; +void() Dog_Death = +{ + self.aistatus = "0"; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NONE; + self.takedamage = DAMAGE_NO; + + sound(self,CHAN_WEAPON,"sounds/null.wav",1,ATTN_NORM); + + self.usedent = world; + + self.health = 0; + Remaining_Zombies = Remaining_Zombies - 1; + dogCount -= 1; + + play_sound_z(3); + //Gotta' make sure we set it back down instead of glitching it up, yo' + if(self.s_time > 0 && sounds_playing > 0) + { + sounds_playing --; + self.s_time = 0; + } + + if (rounds == dogRound) + { + if (!Remaining_Zombies) + Spawn_Powerup(self.origin + '0 0 12', 1); + } + + float r = random(); + + // explode chance + if (r < 0.75) { + self.frame = 0; + setmodel (self, "models/sprites/explosion.spr"); + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/explode.wav", 1, ATTN_NORM); + dog_explodeanim(); + } else { + dog_deathanim(); + } + +}; + +void(entity where) spawn_a_dogB = +{ + local entity sdog;//USED FOR WHAT TO SPAWN + + sdog = getFreeZombieEnt(self); + if(sdog == world) + { + return; + } + + sdog.origin = where.origin; + sdog.frame = 0; + + sdog.target = where.target; + + sdog.solid = SOLID_CORPSE; + sdog.movetype = MOVETYPE_WALK; + setmodel(sdog, "models/ai/dog.mdl"); + sdog.hop_step = 0; + sdog.gravity = 1.0; + + sdog.mins = '-8 -8 -32';//-16 16 -32 + sdog.maxs = '8 8 30';//16 16 40 + setsize (sdog, sdog.mins, sdog.maxs); + + if(pointcontents(sdog.origin - '0 0 36') == -2) + { + while(pointcontents (sdog.origin - '0 0 36') == -2) + { + sdog.origin = sdog.origin + '0 0 1'; + setorigin(sdog,sdog.origin ); + } + } + + sdog.classname = "ai_dog"; + sdog.aistatus = "1"; + setorigin (sdog.goaldummy, '0 0 0'); + sdog.origin_z = sdog.origin_z + 1; + sdog.takedamage = DAMAGE_YES; + setorigin(sdog, sdog.origin); + sdog.flags = sdog.flags | FL_PARTIALGROUND | FL_MONSTER; + + sdog.spawnflags = where.spawnflags; + sdog.spawnflags = sdog.spawnflags | 1; + + sdog.ideal_yaw = sdog.angles_y; + sdog.yaw_speed = 20; + sdog.health = z_health; + sdog.th_die = Dog_Death; + sdog.th_walk = Dog_Walk_Setup; + sdog.outside = FALSE; + sdog.iszomb = 1; + + sdog.th_melee = dog_meleeanim; + sdog.th_idle = dog_idleanim; + sdog.th_diewunder = die_dog_wunder1; + + SetZombieWalk(sdog); + sdog.walktype = 5; + + sdog.reload_delay = 30 + time;//save floats, equals respawn time. + local entity old_self; + old_self = self; + self = sdog; + //droptofloor(); + self.th_walk(); + self = old_self; +}; + +void() dogsprite_think = +{ + self.frame++; + + if (self.frame >= 3) + self.frame = 0; + + // suicide timer! + if(self.ltime < time) { + spawn_a_dogB(self.owner); + remove(self); + } + + self.nextthink = time + 0.05; +} + +void(entity where) spawn_dog_lightning = +{ + local entity tempe; + local entity doglight; + local entity dogsprite; + + // lightning model + doglight = spawn(); + setmodel(doglight, "models/ai/dog_lightning.mdl"); + setorigin(doglight, where.origin - '0 0 20'); + tempe = self; + self = doglight; + dog_lightninganim(); + self = tempe; + + // lightning sprite + dogsprite = spawn(); + setmodel(dogsprite, "models/sprites/lightning.spr"); + setorigin(dogsprite, where.origin); + dogsprite.owner = where; + dogsprite.think = dogsprite_think; + dogsprite.nextthink = time + 0.05; + dogsprite.ltime = time + 1.3; // we use ltime here to be out remove timer, + // since using frames interrupts think() +} + +float() spawn_a_dogA = +{ + local float pcount; + local entity thing, szombie; + local float FAIL; + + FAIL = false; + pcount = 0; + szombie = getFreeZombieEnt(self); + if(szombie == world || dogCount >= (2 * (player_count + 1))) + { + return 0; + } + lastspawn = find(lastspawn, classname, "spawn_dog"); + while (random() < 0.4) + { + lastspawn = find(lastspawn, classname, "spawn_dog"); + } + + while(lastspawn) + { + thing = findradius(lastspawn.origin, 60); + while (thing) + { + pcount = 0; + if (thing.classname == "ai_dog") + { + pcount = 1; + break; + } + thing = thing.chain; + } + if (!pcount && random() < 0.6) + { + //spawn_a_dogB(lastspawn); + spawn_dog_lightning(lastspawn); + dogCount++; + spawn_delay = time + 2; + return true; + } + lastspawn = find(lastspawn, classname, "spawn_dog"); + } + return 0; //no free locations fround +}; + +void() spawn_dog = +{ + precache_model("models/ai/dog.mdl"); + precache_model("models/ai/dog_lightning.mdl"); + precache_model("models/sprites/lightning.spr"); + precache_model("models/sprites/explosion.spr"); + + precache_sound("sounds/rounds/droundend.wav"); + precache_sound("sounds/rounds/droundstart.wav"); + + setsize(self, '0 0 0', '0 0 0'); + if (self.spawnflags & INACTIVE) + { + if (cvar("developer")) + setmodel(self, "models/player.mdl"); + self.classname = "spawn_dog_in"; + } + else + { + if (cvar("developer")) + setmodel(self, "models/ai/dog.mdl"); + self.classname = "spawn_dog"; + } + + self.solid = SOLID_NOT; +}; \ No newline at end of file diff --git a/source/server/ai/fte/waypoints_core.qc b/source/server/ai/fte/waypoints_core.qc new file mode 100644 index 0000000..f0f8778 --- /dev/null +++ b/source/server/ai/fte/waypoints_core.qc @@ -0,0 +1,828 @@ +/* + server/ai/fte/waypoints_core.qc + + pc waypointing + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() creator_way_touch = +{ + if (cvar("waypoint_mode")) { + if (other.classname != "player") { + return; + } + + current_way = self; + } +} + +void () Create_New_Waypoint = +{ + float way_count; + float tempf; + entity tempe; + entity new_way; + + way_count = -1; + tempe = find (world, classname, "waypoint"); + while (tempe) { + tempf = stof(tempe.waynum); + if (tempf > way_count) { + way_count = tempf; + } + tempe = find (tempe, classname, "waypoint"); + } + new_way = spawn(); + + setorigin(new_way, self.origin); + + + //new_way.flags = FL_ITEM; + new_way.solid = SOLID_TRIGGER; + + + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.waynum = ftos(way_count + 1); + new_way.targetname = strzone(new_way.targetname); + bprint (PRINT_HIGH, "Created waypoint "); + bprint (PRINT_HIGH, new_way.waynum); + bprint (PRINT_HIGH, "\n"); + + new_way.touch = creator_way_touch; +} + + +void () Make_Special_Waypoint = +{ + if (self.classname != "player" || !active_way) { + return; + } + if (active_way.targetname != "") {//Toggling it back off + setmodel(active_way, "models/way/current_way.spr"); + active_way.targetname = ""; + bprint (PRINT_HIGH, "Waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, " is no longer a special waypoint\n"); + return; + } + + if (active_way) { + if(self.active_door == world) { + bprint (PRINT_HIGH, "Error: no door selected!\n"); + return; + } + + if(self.active_door.wayTarget == "") { + bprint (PRINT_HIGH, "Error: Door has no wayTarget value!\n"); + return; + } + setmodel(active_way, "models/way/current_way_door.spr"); + active_way.targetname = self.active_door.wayTarget; + + bprint (PRINT_HIGH, "special waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, " named "); + bprint (PRINT_HIGH, active_way.targetname); + bprint (PRINT_HIGH, "\n"); + } +} + + +void () Move_Waypoint = +{ + if (!active_way) + return; + + setorigin (active_way, self.origin); + bprint (PRINT_HIGH, "Moved waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, "\n"); + +} + +void () Select_Waypoint = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + + entity tempe; + + if (current_way == active_way) + active_way = world; + else + active_way = current_way; + + tempe = find (world, classname, "waypoint"); + while (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/normal_way_door.spr"); + else + setmodel(tempe, "models/way/normal_way.spr"); + tempe = find (tempe, classname, "waypoint"); + } + if (active_way) + { + if (active_way.targetname != "") + setmodel(active_way, "models/way/current_way_door.spr"); + else + setmodel(active_way, "models/way/current_way.spr"); + + bprint (PRINT_HIGH, "Selected waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + if(active_way.targetname != "") + { + bprint (PRINT_HIGH, ", special tag "); + bprint (PRINT_HIGH, active_way.targetname); + } + bprint (PRINT_HIGH, "\n"); + + float i; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + tempe = find (world, waynum, active_way.targets[i]); + if (tempe) { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + } else { + active_way.targets[i] = ""; + } + } + } +} + + +void() Remove_Waypoint = +{ + entity tempe; + float i; + if (!active_way) + return; + + tempe = find (world, classname, "waypoint"); + while (tempe) { + + for (i = 0; i < MAX_WAY_TARGETS; i++) { + if (tempe.targets[i] == active_way.waynum) { + tempe.targets[i] = ""; + } + } + + tempe = find (tempe, classname, "waypoint"); + } + + bprint(PRINT_HIGH, "Removed waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, "\n"); + remove (active_way); +} + +float Waypoint_Linked_To(entity from, entity to) { + float i; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + if (from.waynum == to.targets[i]) { + bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + return 1; + } + } + + return 0; +} + +float Link (entity from, entity to) { + float i; + entity tempe; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + tempe = find (world, waynum, from.targets[i]); + + if (tempe == world || tempe == to) { + from.targets[i] = to.waynum; + bprint(PRINT_HIGH, "Linked waypoint "); + bprint(PRINT_HIGH, to.waynum); + bprint(PRINT_HIGH, " to "); + bprint(PRINT_HIGH, from.waynum); + bprint(PRINT_HIGH, "\n"); + + if (to.targetname != "") { + setmodel(to, "models/way/last_way_door.spr"); + } else { + setmodel(to, "models/way/last_way.spr"); + } + + return 1; + } + } + + return 0; +} + +void () Link_Waypoints = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + if (!active_way) + return; + if (current_way == active_way) + return; + + if (Waypoint_Linked_To(current_way, active_way)) { + bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + return; + } + + float i; + entity tempe; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + #ifdef PC + tempe = findfloat (world, waynum, active_way.targets[i]); + #else + tempe = find (world, waynum, active_way.targets[i]); + #endif + + if (tempe == world) { + if (Link(active_way, current_way)) { + return; + } + } + } + + + bprint(PRINT_HIGH, "no targets remaining!\n"); +} + + + +void() Dual_Link_Waypoints = +{ + if (self.classname != "player" || !current_way || !active_way || current_way == active_way) { + return; + } + + float result1,result2; + result1 = Waypoint_Linked_To(current_way,active_way); + result2 = Waypoint_Linked_To(active_way,current_way); + + if(result1 && result2) { + bprint(PRINT_HIGH, "Both waypoints already linked!\n"); + return; + } + + if(!result1) + { + if (Link(current_way,active_way)) { + bprint(PRINT_HIGH, strcat("Linked waypoint ", strcat(current_way.waynum, strcat(" to ",strcat(active_way.waynum, "\n"))))); + } else { + bprint(PRINT_HIGH, strcat("ERROR: Could not link waypoint ", strcat(current_way.waynum, strcat(" to ", strcat(active_way.waynum, "\n"))))); + } + } + if(!result2) + { + if (Link(active_way,current_way)) { + bprint(PRINT_HIGH, strcat("Linked waypoint ", strcat(active_way.waynum, strcat(" to ", strcat(current_way.waynum, "\n"))))); + } else { + bprint(PRINT_HIGH, strcat("ERROR: Could not link waypoint ", strcat(active_way.waynum, strcat(" to ", strcat(current_way.waynum, "\n"))))); + } + } +} + + + +//alter auto_link_waypoints to iterate through the closest waypoints from closest to furthest +// on the innermost loop, we find the next closest waypoint that is further away from the last closest waypoint, and we use that! + +void() Auto_Link_Waypoints = +{ + entity tempe1, tempe2; + + tempe1 = find(world,classname,"waypoint"); + while(tempe1 != world) + { + tempe2 = find(world,classname,"waypoint"); + while(tempe2 != world) + { + if(tempe1 == tempe2) + { + tempe2 = find(tempe2,classname,"waypoint"); + continue; + } + + if(tracemove(tempe1.origin,VEC_HULL_MIN,VEC_HULL_MAX,tempe2.origin,TRUE,self)) + { + Link(tempe1,tempe2); + } + + tempe2 = find(tempe2,classname,"waypoint"); + } + tempe1 = find(tempe1,classname,"waypoint"); + } +} +//alter auto_link_waypoints to iterate through the closest waypoints from closest to furthest +// on the innermost loop, we find the next closest waypoint that is further away from the last closest waypoint, and we use that! + +void() Remove_Links = +{ + entity tempe; + + tempe = find(world,classname,"waypoint"); + while(tempe != world) + { + float i; + for (i = 0; i < MAX_WAY_TARGETS; i = i + 1) { + if (tempe.targetname != "") + setmodel(tempe, "models/way/normal_way_door.spr"); + else + setmodel(tempe, "models/way/normal_way.spr"); + tempe.targets[i] = ""; + } + tempe = find(tempe,classname,"waypoint"); + } +} + +void() Save_Waypoints +{ + float file; + string h; + float i; + entity tempe; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_WRITE); + + dprint (strcat("Saving waypoints ", strcat(h, "\n"))); + + local entity dway; + //fputs(file, "begin\n"); + dway = find(world, classname, "waypoint"); + while (dway) + { + fputs(file,"waypoint\n"); + fputs(file,"{\n"); + fputs(file, strcat(" id: ", strcat(dway.waynum, "\n"))); + fputs(file, strcat(" origin: ", strcat(vtos(dway.origin), "\n"))); + if (dway.targetname != "") { + fputs(file, strcat(" door: ", strcat(dway.targetname, "\n"))); + } + + fputs(file, " targets:\n"); + fputs(file, " [\n"); + for (i = 0; i < MAX_WAY_TARGETS; i++) { + if (dway.targets[i] != "") { + #ifdef PC + tempe = findfloat (world, waynum, dway.targets[i]); + #else + tempe = find (world, waynum, dway.targets[i]); + #endif + + if (tempe != world) { + fputs(file, strcat(" ", strcat(dway.targets[i], "\n"))); + } else { + tempe = find (world, waynum, dway.targets[i]); + if (tempe != world) { + fputs(file, strcat(" ", strcat(dway.targets[i], "\n"))); + } + } + } + } + fputs(file, " ]\n"); + fputs(file,"}\n"); + + + dway = find(dway, classname, "waypoint"); + if (dway) + fputs(file,"\n"); + } + fclose(file); +} + +void (vector here, float which, string special, string trg, string trg2, string trg3, string trg4, string trg5, string trg6, string trg7, string trg8) Create_Waypoint = +{ + entity new_way; + new_way = spawn(); + + setorigin(new_way, here); + + + //new_way.flags = FL_ITEM; + new_way.solid = SOLID_TRIGGER; + + if (cvar("waypoint_mode")) + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.waynum = ftos(which); + dprint ("Created waypoint "); + dprint (new_way.waynum); + dprint ("\n"); + + if (special != "") + { + if (!cvar("waypoint_mode")) + new_way.classname = "waypoint_s"; + if (cvar("waypoint_mode")) + setmodel(new_way, "models/way/normal_way_door.spr"); + new_way.targetname = special; + dprint ("Special waypoint "); + dprint (new_way.targetname); + dprint ("\n"); + //current_special++; + } + + new_way.targets[0] = trg; + new_way.targets[1] = trg2; + new_way.targets[2] = trg3; + new_way.targets[3] = trg4; + new_way.targets[4] = trg5; + new_way.targets[5] = trg6; + new_way.targets[6] = trg7; + new_way.targets[7] = trg8; + + new_way.touch = creator_way_touch; +} + +float waypoints_loaded; +void() Load_Waypoints +{ + float file, point; + string h; + float targetcount, loop; + + entity new_way; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + dprint("Error: file not found \n"); + return; + } + + new_way = spawn(); + targetcount = 0; + point = 0; + loop = 1; + while (loop) + { + string line; + line = fgets(file); + if not (line) { + bprint(PRINT_HIGH, "End of file\n"); + loop = 0; + break; + } + h = strtrim(line); + + //bprint(PRINT_HIGH, strcat(h, "\n")); + if (h == "") { + continue; + } + + switch (point) { + case 0: + if (h == "waypoint") { + new_way = spawn(); + new_way.solid = SOLID_TRIGGER; + new_way.model = "models/way/normal_way.spr"; + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.touch = creator_way_touch; + point = 1; + targetcount = 0; + } else if (h == "Waypoint") { + bprint(PRINT_HIGH, "Identified .way as legacy..\n"); + point = 99; + Load_Waypoints_Legacy(); + loop = 0; + break; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown point ", strcat(h, "\n"))); + } + break; + case 1: + if (h == "{") { + point = 2; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected {\n"))); + } + break; + case 2: + tokenize(h); + + string value, variable; + variable = strtrim(argv(0)); + value = strtrim(argv(2)); + + //bprint(PRINT_HIGH, strcat(variable, "\n")); + + switch (variable) { + case "origin": + + print(strcat(value, "\n")); + new_way.origin = stov(value); + setorigin(new_way, new_way.origin); + break; + case "id": + new_way.waynum = value; + break; + case "door": + new_way.targetname = value; + + setmodel(new_way, "models/way/normal_way_door.spr"); + break; + case "targets": + point = 3; + break; + case "}": + point = 0; + break; + default: + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(variable, "\n"))); + break; + } + break; + case 3: + if (h == "[") { + point = 4; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected [\n"))); + } + break; + case 4: + if (targetcount >= MAX_WAY_TARGETS) { + bprint(PRINT_HIGH, "Error: Target count too high for waypoint\n"); + } else if (h == "]") { + point = 2; + } else { + bprint(PRINT_HIGH, strcat(strcat("WAYPOINT TARGET: ", strcat(strcat(ftos(targetcount), " "), h)), "\n")); + new_way.targets[targetcount] = h; + targetcount++; + } + break; + } + } + + fclose(file); + waypoints_loaded = 1; +} + +void() Load_Waypoints_Legacy +{ + float file, which; + string h, special, trg, trg2, trg3, trg4, trg5, trg6, trg7, trg8; + local vector where; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + dprint("Error: file not found \n"); + return; + } + + while (1) + { + dprint("Loading waypoint\n"); + // the first line is just a comment, ignore it + h = fgets(file); + if (h != "Waypoint") + { + bprint(PRINT_HIGH, "Last waypoint\n"); + fclose(file); + return; + } + h = fgets(file); + h = fgets(file); + h = substring(h, 9, 20); + where = stov(h); + + h = (fgets(file)); + h = substring(h, 5, 20); + which = stof(h); + + h = (fgets(file)); + special = substring(h, 10, 20); + + h = (fgets(file)); + trg = substring(h, 9, 20); + + h = (fgets(file)); + trg2 = substring(h, 10, 20); + + h = (fgets(file)); + trg3 = substring(h, 10, 20); + + h = (fgets(file)); + trg4 = substring(h, 10, 20); + + h = (fgets(file)); + trg5 = substring(h, 10, 20); + + h = (fgets(file)); + trg6 = substring(h, 10, 20); + + h = (fgets(file)); + trg7 = substring(h, 10, 20); + + h = (fgets(file)); + trg8 = substring(h, 10, 20); + + #ifndef PC + Create_Waypoint(where, which, special, trg, trg2, trg3, trg4, trg5, trg6, trg7, trg8); + #else + waypoint_ai waypoint; + waypoint = waypoints[which]; + + waypoints[which].id = which; + waypoints[which].org = where; + waypoints[which].targetdoor = special; + + waypoints[which].target_id[0] = stof(trg); + waypoints[which].target_id[1] = stof(trg2); + waypoints[which].target_id[2] = stof(trg3); + waypoints[which].target_id[3] = stof(trg4); + waypoints[which].target_id[4] = stof(trg5); + waypoints[which].target_id[5] = stof(trg6); + waypoints[which].target_id[6] = stof(trg7); + waypoints[which].target_id[7] = stof(trg8); + #endif + + h = (fgets(file)); + h = (fgets(file)); + } +} + + +void VisualizePathfind() { + if (self.classname != "player") + return; + if (!current_way) + return; + if (!active_way) + return; + if (current_way == active_way) + return; + + Pathfind(self, stof(active_way.waynum), stof(current_way.waynum)); +} + +.float waypoint_delay; + +//Waypoint logic functions +void () Waypoint_Functions = +{ + // naievil -- archaic + /* + switch (self.impulse) { + case 100: + Create_New_Waypoint(); + break; + case 101: + Make_Special_Waypoint(); + break; + case 102: + Select_Waypoint(); + break; + case 103: + Move_Waypoint(); + break; + case 104: + Link_Waypoints (); + break; + case 105: + Dual_Link_Waypoints(); + break; + case 106: + Save_Waypoints(); + break; + case 107: + Load_Waypoints(); + break; + case 108: + Load_Waypoints_Legacy(); + break; + case 109: + Remove_Waypoint(); + //VisualizePathfind(); + break; + case 110: + Auto_Link_Waypoints(); + break; + case 111: + Remove_Links(); + break; + } + + self.impulse = 0; + */ + + switch (self.impulse) { + case 23: + Save_Waypoints(); + break; + case 22: + if (!waypoints_loaded) + Load_Waypoints(); + break; + case 110: + Move_Waypoint(); + break; + } + + self.impulse = 0; + + // Match what we display on the screen + if (self.button0 && self.waypoint_delay < time) { + Create_New_Waypoint(); + self.waypoint_delay = time + 1; + } + + if (self.button7 && self.waypoint_delay < time) { + Select_Waypoint(); + self.waypoint_delay = time + 0.25; + } + + if (self.button8 && self.waypoint_delay < time) { + Link_Waypoints (); + self.waypoint_delay = time + 0.5; + } + + if (self.button6 && self.waypoint_delay < time) { + Remove_Waypoint(); + self.waypoint_delay = time + 0.5; + } + + if (self.button5 && self.waypoint_delay < time) { + Make_Special_Waypoint(); + self.waypoint_delay = time + 1; + } + +}; + +void () Waypoint_Logic = +{ + if (!waypoint_mode) { + waypoint_mode = 1; + entity zent; + + zent = find (world, classname, "ai_zombie"); + while (zent) + { + remove (zent); + zent = find (zent, classname, "ai_zombie"); + } + + zent = find (world, classname, "door_nzp_cost"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent = find (zent, classname, "door_nzp_cost"); + } + zent = find (world, classname, "door_nzp"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent.solid = SOLID_NOT; + zent = find (zent, classname, "door_nzp"); + } + zent = find (world, classname, "window"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent = find (zent, classname, "window"); + } + + } + Waypoint_Functions(); +}; \ No newline at end of file diff --git a/source/server/ai/pathfind_code.qc b/source/server/ai/pathfind_code.qc new file mode 100644 index 0000000..660a7c6 --- /dev/null +++ b/source/server/ai/pathfind_code.qc @@ -0,0 +1,469 @@ +/* + server/ai/pathfind_core.qc + + generic waypoint pathfinding + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() Load_Waypoints_Legacy; + +float Distance (vector from, vector to) { + return vlen(from - to); +} + +float HeuristicCostEstimate (float start_way, float end_way) +{ + //for now we will just look the distance between. + return Distance(waypoints[start_way].org, waypoints[end_way].org); +} + +void ReconstructPath(entity z, float start_way, float end_way) { + +} + +float open_first; +float open; +float closed_init; +float closed; +float closed_first; +void SV_WP_ClearSet() +{ + float i; + + for (i = 0; i < MAX_WAYPOINTS; i = i + 1) + { + waypoints[i].set = 0; + } +} + +float IsActive(float way) { + if (waypoints[way].targetdoor != "") { + entity door; + + door = find(world, wayTarget, waypoints[way].targetdoor); + + if (door != world) { + if (door.state == STATE_BOTTOM) { + return 0; + } + } + } + + return 1; +} + +void SV_WP_SetAdd(float wp, float isopen) +{ + if (isopen) + { + // we only get added here from none, so no cleanup required + // gotta check if last entry was removed before this call + if (waypoints[open_first].set == SET_CLOSED) + { + //Con_Printf("Empty set: %i is new first\n", wp); + open_first = wp; + waypoints[wp].prev = wp; + } + else + { + waypoints[wp].prev = open; + waypoints[open].next = wp; + } + waypoints[wp].next = wp; + waypoints[wp].set = SET_OPEN; + open = wp; + } + else + { + // even if wp is the only one in the set, it doesn't really matter + if (wp == open_first) + open_first = waypoints[wp].next; + if (wp == open) + open = waypoints[wp].prev; + + // update links so open set doesn't get fucked + waypoints[waypoints[wp].prev].next = waypoints[wp].next; + waypoints[waypoints[wp].next].prev = waypoints[wp].prev; + + if (!closed_init) + { + closed_first = wp; + waypoints[wp].prev = wp; + closed_init = true; + } + else + { + waypoints[closed].next = wp; + waypoints[wp].prev = closed; + } + waypoints[wp].next = wp; + waypoints[wp].set = SET_CLOSED; + closed = wp; + } +} + +float Pathfind (entity z, float s_wp, float e_wp) { + float current; + float i, j; + float bestf; + + + if (s_wp == e_wp) { + return 2; + } + + SV_WP_ClearSet(); + + open_first = s_wp; + open = s_wp; + waypoints[s_wp].next = s_wp; + waypoints[s_wp].prev = s_wp; + waypoints[s_wp].set = SET_OPEN; + waypoints[s_wp].g = 0; + waypoints[s_wp].f = HeuristicCostEstimate(s_wp, e_wp); + waypoints[s_wp].step = s_wp; + current = s_wp; + + closed_init = false; + + // while openset is not empty + while (waypoints[open_first].set == SET_OPEN) { + i = open_first; + + bestf = 320000; + //print("Current ", ftos(current), "\n"); + + // go from first open to last, pick the one with lowest f + do { + if (waypoints[i].f < bestf) { + current = i; + bestf = waypoints[i].f; + } + i = waypoints[i].next; + } while (i != open); + + // mark node as visited + //print("Removing ", ftos(current), " from open\n"); + SV_WP_SetAdd(current, false); + + // we found a result, loop back with pathlength + if (current == e_wp) { + j = i = current; + // walk constructed path backwards + // keep 2 next steps with us + //print("Result\n"); + //print(ftos(current), "\n"); + while (waypoints[current].step != current) + { + j = i; + i = current; + current = waypoints[current].step; + } + + // go to the furthest one on the path that we can actually see + + if (tracemove(waypoints[s_wp].org,VEC_HULL_MIN,VEC_HULL_MAX,waypoints[i].org,TRUE,z)) + { + //VectorCopy(waypoints[i].origin, res); + //print("Giving third wp ", ftos(i), "\n"); + return i; + } + else if (tracemove(waypoints[s_wp].org,VEC_HULL_MIN,VEC_HULL_MAX,waypoints[j].org,TRUE,z)) + { + //VectorCopy(waypoints[j].origin, res); + //print("Giving second wp ", ftos(j), "\n"); + return j; + } + else + { + //VectorCopy(waypoints[current].origin, res); + //print("Giving first wp ", ftos(current), "\n"); + return current; + } + } + + + // check neighbours to add to openset + for (j = 0; j < MAX_WAY_TARGETS; j++) + { + i = waypoints[current].target_id[j]; + if (waypoints[i].set != SET_CLOSED && i != current && IsActive(i)) + { + bestf = waypoints[current].g + HeuristicCostEstimate(i, current); + if (waypoints[i].set == SET_NONE || bestf < waypoints[i].g) + { + waypoints[i].step = current; + waypoints[i].g = bestf; + waypoints[i].f = bestf + HeuristicCostEstimate(i, e_wp); + + if (waypoints[i].set == SET_NONE) + { + //print("Adding ", ftos(i), " into open\n"); + SV_WP_SetAdd(i, true); + } + } + } + } + } + print("Waypointing failed\n"); + return -1; +} + + +float Do_Pathfind(entity from, entity to) { + + float i; + float TraceResult; + + float dist, best_dist, best, best_target; + + best = 0; + best_target = 0; + dist = 0; + best_dist = 100000000; + + #ifndef PSP + for (i = 0; i < MAX_WAYPOINTS; i = i + 1) { + if (IsActive(i)) { + TraceResult = tracemove (from.origin, VEC_HULL_MIN, VEC_HULL_MAX, waypoints[i].org, TRUE ,from); + if (TraceResult) { + dist = Distance(waypoints[i].org, from.origin); + + if(dist < best_dist) { + best_dist = dist; + best = i; + } + } + } + } + dist = 0; + best_dist = 100000000; + + for (i = 0; i < MAX_WAYPOINTS; i = i + 1) { + if (IsActive(i)) { + TraceResult = tracemove (to.origin, VEC_HULL_MIN, VEC_HULL_MAX, waypoints[i].org, TRUE ,to); + if (TraceResult) { + dist = Distance(waypoints[i].org, to.origin); + + if(dist < best_dist) { + best_dist = dist; + best_target = i; + } + } + } + } + + //print("Starting waypoint: ", ftos(best)," Ending waypoint: ", ftos(best_target), "\n"); + + + + + return Pathfind(from, best, best_target); + #else + return 0; + #endif +} + +void CalcDistances() { + float i, s; + + for (i = 1; i < MAX_WAYPOINTS; i++) { + waypoint_ai way; + way = waypoints[i]; + if (way.id > 0) { + for (s = 0; s < MAX_WAY_TARGETS; s++) { + float targ; + targ = way.target_id[s]; + if (targ > 0) { + way.dist[s] = Distance(way.org, waypoints[targ].org); + } + } + } + } +} + + +void creator_way_touch(); +void LoadWaypointData() { + float file, point; + string h; + float targetcount, loop, waycount; + entity new_way; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + dprint("Error: file not found \n"); + return; + } + + float i, s; + + + #ifndef PSP + for (i = 0; i < MAX_WAYPOINTS; i = i + 1) { + waypoint_ai way; + + way = waypoints[i]; + way.org = '0 0 0'; + way.id = 0; + way.g = 0; + way.f = 0; + way.set = 0; + way.targetdoor = ""; + + for (s = 0; s < MAX_WAY_TARGETS; s = s + 1) { + way.target_id[s] = 0; + way.dist[s] = 0; + } + + } + #endif + + new_way = spawn(); + point = 0; + waycount = 0; + targetcount = 0; + loop = 1; + while (loop) + { + string line; + line = fgets(file); + if not (line) { + //bprint(PRINT_HIGH, "End of file\n"); + loop = 0; + break; + } + h = strtrim(line); + + //print(h, "\n"); + if (h == "") { + continue; + } + + switch (point) { + case 0: + if (h == "waypoint") { + new_way = spawn(); + new_way.solid = SOLID_TRIGGER; + + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.touch = creator_way_touch; + point = 1; + targetcount = 0; + } else if (h == "Waypoint") { + bprint(PRINT_HIGH, "Identified .way as legacy..\n"); + point = 99; + Load_Waypoints_Legacy(); + } else { + bprint(PRINT_HIGH, strcat("Error: unknown point ", strcat(h, "\n"))); + } + break; + case 1: + if (h == "{") { + point = 2; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected {\n"))); + } + break; + case 2: + tokenize(h); + + string value, variable; + variable = strtrim(argv(0)); + value = strtrim(argv(2)); + + switch (variable) { + case "origin": + setorigin(new_way, stov(value)); + break; + case "id": + new_way.waynum = value; + break; + case "door": + new_way.targetname = value; + break; + case "targets": + point = 3; + break; + case "}": + float id = stof(new_way.waynum); + waypoint_ai waypoint; + waypoint = waypoints[id]; + + waypoints[id].id = id; + waypoints[id].org = new_way.origin; + waypoints[id].targetdoor = new_way.targetname; + + for (i = 0; i < MAX_WAY_TARGETS; i++) { + waypoints[id].target_id[i] = stof(new_way.targets[i]); + } + + if (!cvar("waypoint_mode")) { + remove(new_way); + } + + + point = 0; + break; + default: + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(variable, "\n"))); + break; + } + break; + case 3: + if (h == "[") { + point = 4; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected [\n"))); + } + break; + case 4: + if (targetcount >= MAX_WAY_TARGETS) { + bprint(PRINT_HIGH, "Error: Target count too high for waypoint\n"); + } else if (h == "]") { + point = 2; + } else { + new_way.targets[targetcount] = h; + targetcount++; + } + break; + } + } + + if (new_way) { + if (!cvar("waypoint_mode")) { + remove(new_way); + } + } + + fclose(file); + + #ifndef PSP + CalcDistances(); + #endif +} \ No newline at end of file diff --git a/source/server/ai/standard/waypoints_core.qc b/source/server/ai/standard/waypoints_core.qc new file mode 100644 index 0000000..6824f5b --- /dev/null +++ b/source/server/ai/standard/waypoints_core.qc @@ -0,0 +1,117 @@ +/* + server/ai/standard/waypoints_core.qc + + non-pc waypoint impulsing + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +float waypoint_action; + +void () Waypoint_Functions = +{ + switch (self.impulse) + { + case 15: + Make_Special_Waypoint(); + break; + case 10: + Move_Waypoint(); + break; + case 18: + Remove_Waypoint (); + break; + case 21: + Link_Waypoints (); + break; + case 20: + Dual_Link_Waypoints(); + break; + case 22: + Auto_Link_Waypoints(); + break; + case 101: + Save_Waypoints(); + break; + case 102: + Load_Waypoints(); + break; + } + + if(self.button8 && !self.semi) + { + self.semi = true; + Dual_Link_Waypoints(); + waypoint_action = waypoint_action + 1; + } + if (self.button0 && !self.semi) + { + Create_New_Waypoint(); + waypoint_action = waypoint_action + 1; + self.semi = true; + } + + if (self.button7 && !self.semiuse) + { + Select_Waypoint(); + waypoint_action = waypoint_action + 1; + self.semiuse = true; + } + + if(self.button4 && !self.semiuse) + { + self.semiuse = true; + waypoint_action = waypoint_action + 1; + Move_Waypoint(); + } + if(self.button6 && !self.semiuse) + { + Remove_Waypoint (); + waypoint_action = waypoint_action + 1; + self.semiuse = true; + } + if(self.button5 && !self.semiuse) + { + Make_Special_Waypoint(); + waypoint_action = waypoint_action + 1; + self.semiuse = true; + } + + if(!self.button0 && !self.button7 && !self.button4 && !self.button5 && !self.button6 && !self.button8) + { + self.semiuse = false; + self.semi = false; + } + + self.impulse = 0; +}; + +void () Waypoint_Logic = +{ + if (waypoint_action > 20) { + if (cvar("autosave_waypoint")) + Save_Waypoints(); + waypoint_action = 0; + } + + Waypoint_Functions(); +}; \ No newline at end of file diff --git a/source/server/ai/standard/waypoints_func.qc b/source/server/ai/standard/waypoints_func.qc new file mode 100644 index 0000000..b0c9b96 --- /dev/null +++ b/source/server/ai/standard/waypoints_func.qc @@ -0,0 +1,907 @@ +/* + server/ai/standard/waypoints_func.qc + + non-pc waypointing + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +//float current_special; +entity active_way, current_way; + + +float (entity way1, entity way2) progs_Link_Waypoints = +{ + + if (way1 == world) + return 0; + if (way2 == world) + return 0; + if (way2 == way1) + return 0; + + if( + way1.target == way2.waynum || + way1.target2 == way2.waynum || + way1.target3 == way2.waynum || + way1.target4 == way2.waynum || + way1.target5 == way2.waynum || + way1.target6 == way2.waynum || + way1.target7 == way2.waynum || + way1.target8 == way2.waynum + ) + { + //bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + return 2;//already linked + } + + if (way1.target == "") + { + way1.target = way2.waynum; + way1.target = strzone(way1.target); + } + else if (way1.target2 == "") + { + way1.target2 = way2.waynum; + way1.target2 = strzone(way1.target2); + } + else if (way1.target3 == "") + { + way1.target3 = way2.waynum; + way1.target3 = strzone(way1.target3); + } + else if (way1.target4 == "") + { + way1.target4 = way2.waynum; + way1.target4 = strzone(way1.target4); + } + else if (way1.target5 == "") + { + way1.target5 = way2.waynum; + way1.target5 = strzone(way1.target5); + } + else if (way1.target6 == "") + { + way1.target6 = way2.waynum; + way1.target6 = strzone(way1.target6); + } + else if (way1.target7 == "") + { + way1.target7 = way2.waynum; + way1.target7 = strzone(way1.target7); + } + else if (way1.target8 == "") + { + way1.target8 = way2.waynum; + way1.target8 = strzone(way1.target8); + } + else + { + //bprint(PRINT_HIGH, "no targets remaining!\n"); + return -1;//no targets + } + + return 1; +} + +//alter auto_link_waypoints to iterate through the closest waypoints from closest to furthest +// on the innermost loop, we find the next closest waypoint that is further away from the last closest waypoint, and we use that! + +void() Auto_Link_Waypoints = +{ + entity tempe1, tempe2; + + tempe1 = find(world,classname,"waypoint"); + while(tempe1 != world) + { + tempe2 = find(world,classname,"waypoint"); + while(tempe2 != world) + { + if(tempe1 == tempe2) + { + tempe2 = find(tempe2,classname,"waypoint"); + continue; + } + + if(tracemove(tempe1.origin,VEC_HULL_MIN,VEC_HULL_MAX,tempe2.origin,TRUE,self)) + { + progs_Link_Waypoints(tempe1,tempe2); + } + + tempe2 = find(tempe2,classname,"waypoint"); + } + tempe1 = find(tempe1,classname,"waypoint"); + } +} + +void() Remove_Waypoint = +{ + entity tempe; + if (!active_way) + return; + + tempe = find (world, classname, "waypoint"); + while (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/normal_way_door.spr"); + else + setmodel(tempe, "models/way/normal_way.spr"); //restore the original one, shall we + tempe = find (tempe, classname, "waypoint"); + } + + tempe = find (world, target, active_way.waynum); + if (tempe) + tempe.target = ""; + tempe = find (world, target2, active_way.waynum); + if (tempe) + tempe.target2 = ""; + tempe = find (world, target3, active_way.waynum); + if (tempe) + tempe.target3 = ""; + tempe = find (world, target4, active_way.waynum); + if (tempe) + tempe.target4 = ""; + tempe = find (world, target5, active_way.waynum); + if (tempe) + tempe.target5 = ""; + tempe = find (world, target6, active_way.waynum); + if (tempe) + tempe.target6 = ""; + tempe = find (world, target7, active_way.waynum); + if (tempe) + tempe.target7 = ""; + tempe = find (world, target8, active_way.waynum); + if (tempe) + tempe.target8 = ""; + + + //if (active_way.targetname != "") + // current_special--; + + bprint (PRINT_HIGH, "Removed waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, "\n"); + + strunzone (active_way.classname); + strunzone (active_way.targetname); + strunzone (active_way.waynum); + strunzone (active_way.target); + strunzone (active_way.target2); + strunzone (active_way.target3); + strunzone (active_way.target4); + strunzone (active_way.target5); + strunzone (active_way.target6); + strunzone (active_way.target7); + strunzone (active_way.target8); + remove (active_way); +} + +void() creator_way_touch = +{ + if (cvar("waypoint_mode")) + { + if (other.classname != "player") + return; + + current_way = self; + } +} + +void () Create_New_Waypoint = +{ + float way_count; + float tempf; + entity tempe; + entity new_way; + string temps; + new_way = spawn(); + + setorigin(new_way, self.origin); + + + //new_way.flags = FL_ITEM; + new_way.solid = SOLID_TRIGGER; + + + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + tempe = find (world, classname, "waypoint"); + while (tempe) + { + tempf = stof (tempe.waynum); + if (tempf > way_count) + way_count = tempf; + tempe = find (tempe, classname, "waypoint"); + } + + temps = ftos(way_count + 1); + new_way.waynum = temps; + new_way.waynum = strzone(new_way.waynum); + new_way.targetname = strzone(new_way.targetname); + bprint (PRINT_HIGH, "Created waypoint "); + bprint (PRINT_HIGH, new_way.waynum); + bprint (PRINT_HIGH, "\n"); + + new_way.touch = creator_way_touch; +} +void () Move_Waypoint = +{ + if (!active_way) + return; + + setorigin (active_way, self.origin); + bprint(PRINT_HIGH, "Moved waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, "\n"); + +} + +void() Dual_Link_Waypoints = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + if (!active_way) + return; + if (current_way == active_way) + return; + + local float result1,result2; + result1 = progs_Link_Waypoints(current_way,active_way); + result2 = progs_Link_Waypoints(active_way,current_way); + + if(result1 == 2 && result2 == 2) + { + bprint(PRINT_HIGH, "Both waypoints already linked!\n"); + } + + if(result1 == 1) + { + bprint(PRINT_HIGH, " Linked waypoint "); + bprint(PRINT_HIGH, current_way.waynum); + bprint(PRINT_HIGH, " to "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, "\n"); + } + if(result2 == 1) + { + bprint(PRINT_HIGH, " Linked waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, " to "); + bprint(PRINT_HIGH, current_way.waynum); + bprint(PRINT_HIGH, "\n"); + } + + if(result1 == 2 && result2 != 2)//if both are already linked, we only want the simple message above, not detailed one + { + bprint(PRINT_HIGH, " Waypoint "); + bprint(PRINT_HIGH, current_way.waynum); + bprint(PRINT_HIGH, " already linked to "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, "\n"); + } + if(result2 == 2 && result1 != 2)//if both are already linked, we only want the simple message above, not detailed one + { + bprint(PRINT_HIGH, " Waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, " already linked to "); + bprint(PRINT_HIGH, current_way.waynum); + bprint(PRINT_HIGH, "\n"); + } + if(result1 == -1) + { + bprint(PRINT_HIGH, " Waypoint "); + bprint(PRINT_HIGH, current_way.waynum); + bprint(PRINT_HIGH, " has no open targets remaining\n"); + } + if(result2 == -1) + { + bprint(PRINT_HIGH, " Waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, " has no open targets remaining!\n"); + } + if(result1 == 1 && result2 == 1) + { + bprint(PRINT_HIGH, "Both waypoints linked succesfully!\n"); + } + + if (current_way.targetname != "") + setmodel(current_way, "models/way/last_way_door.spr"); + else + setmodel(current_way, "models/way/last_way.spr"); + + if (active_way.targetname != "") + setmodel(active_way, "models/way/current_way_door.spr"); + else + setmodel(active_way, "models/way/current_way.spr"); +} + +void () Link_Waypoints = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + if (!active_way) + return; + if (current_way == active_way) + return; + + if( + active_way.target == current_way.waynum || + active_way.target2 == current_way.waynum || + active_way.target3 == current_way.waynum || + active_way.target4 == current_way.waynum || + active_way.target5 == current_way.waynum || + active_way.target6 == current_way.waynum || + active_way.target7 == current_way.waynum || + active_way.target8 == current_way.waynum + ) + { + bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + return; + } + + if (active_way.target == "") + { + active_way.target = current_way.waynum; + active_way.target = strzone(active_way.target); + } + else if (active_way.target2 == "") + { + active_way.target2 = current_way.waynum; + active_way.target2 = strzone(active_way.target2); + } + else if (active_way.target3 == "") + { + active_way.target3 = current_way.waynum; + active_way.target3 = strzone(active_way.target3); + } + else if (active_way.target4 == "") + { + active_way.target4 = current_way.waynum; + active_way.target4 = strzone(active_way.target4); + } + else if (active_way.target5 == "") + { + active_way.target5 = current_way.waynum; + active_way.target5 = strzone(active_way.target5); + } + else if (active_way.target6 == "") + { + active_way.target6 = current_way.waynum; + active_way.target6 = strzone(active_way.target6); + } + else if (active_way.target7 == "") + { + active_way.target7 = current_way.waynum; + active_way.target7 = strzone(active_way.target7); + } + else if (active_way.target8) + { + active_way.target8 = current_way.waynum; + active_way.target8 = strzone(active_way.target8); + } + else + { + bprint(PRINT_HIGH, "no targets remaining!\n"); + return; + } + + bprint(PRINT_HIGH, "Linked waypoint "); + bprint (PRINT_HIGH, current_way.waynum); + bprint(PRINT_HIGH, " to "); + bprint (PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, "\n"); + if (current_way.targetname != "") + setmodel(current_way, "models/way/last_way_door.spr"); + else + setmodel(current_way, "models/way/last_way.spr"); +} +void () Select_Waypoint = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + + entity tempe; + + if (current_way == active_way) + active_way = world; + else + active_way = current_way; + + tempe = find (world, classname, "waypoint"); + while (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/normal_way_door.spr"); + else + setmodel(tempe, "models/way/normal_way.spr"); + tempe = find (tempe, classname, "waypoint"); + } + if (active_way) + { + if (active_way.targetname != "") + setmodel(active_way, "models/way/current_way_door.spr"); + else + setmodel(active_way, "models/way/current_way.spr"); + + bprint(PRINT_HIGH, "Selected waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + if(active_way.targetname != "") + { + bprint(PRINT_HIGH, ", special tag "); + bprint(PRINT_HIGH, active_way.targetname); + } + bprint(PRINT_HIGH, "\n"); + + if(active_way.target != "") + { + tempe = find (world, waynum, active_way.target); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, active_way.target); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target = ""; + } + } + if(active_way.target2 != "") + { + tempe = find (world, waynum, active_way.target2); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, active_way.target2); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target2 = ""; + } + } + if(active_way.target3 != "") + { + tempe = find (world, waynum, active_way.target3); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, tempe.target3); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target3 = ""; + } + } + if(active_way.target4 != "") + { + tempe = find (world, waynum, active_way.target4); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, active_way.target4); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target4 = ""; + } + } + if(active_way.target5 != "") + { + tempe = find (world, waynum, active_way.target5); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, active_way.target5); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target5 = ""; + } + } + if(active_way.target6 != "") + { + tempe = find (world, waynum, active_way.target6); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target6 = ""; + } + } + if(active_way.target7 != "") + { + tempe = find (world, waynum, active_way.target7); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, active_way.target7); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target7 = ""; + } + } + if(active_way.target8 != "") + { + tempe = find (world, waynum, active_way.target8); + if (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + bprint(PRINT_HIGH, active_way.target8); + bprint(PRINT_HIGH, "\n"); + } + else + { + active_way.target8 = ""; + } + } + } +} +entity() closestDoor = +{ + local float bestdist,dist; + bestdist = 10000; + dist = 0; + local entity d,best; + d = find(world,classname,"door_nzp"); + while(d) + { + bprint(PRINT_HIGH, "Door! "); + dist = vlen(self.origin - d.pos1);//door origin is world, so use pos1 for start pos + bprint(PRINT_HIGH, ftos(bestdist)); + bprint(PRINT_HIGH, ", "); + bprint(PRINT_HIGH, ftos(dist)); + bprint(PRINT_HIGH, "\n"); + if(dist < bestdist) + { + bestdist = dist; + best = d; + } + bprint(PRINT_HIGH, "\n"); + d = find(d,classname,"door_nzp"); + } + return best; +} +void () Make_Special_Waypoint = +{ + if (self.classname != "player") + return; + if (!active_way) + return; + if (active_way.targetname != "")//Toggling it back off + { + setmodel(active_way, "models/way/current_way.spr"); + active_way.targetname = ""; + bprint(PRINT_HIGH, "Waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, " is no longer a special waypoint\n"); + return; + } + if (active_way) + { + strunzone(active_way.targetname); + //local entity nearDoor; + //nearDoor = closestDoor(); + if(self.active_door == world) + { + bprint(PRINT_HIGH, "Error: no door selected!\n"); + return; + } + if(self.active_door.wayTarget == "") + { + bprint(PRINT_HIGH, "Error: Door has no wayTarget value!\n"); + return; + } + setmodel(active_way, "models/way/current_way_door.spr"); + active_way.targetname = self.active_door.wayTarget; + //active_way.targetname = ftos(current_special); + //active_way.targetname = strzone(active_way.targetname); + //active_way.targetname = strcat("d", active_way.targetname); + //active_way.targetname = strzone(active_way.targetname); + bprint(PRINT_HIGH, "special waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, " named "); + bprint (PRINT_HIGH, active_way.targetname); + bprint(PRINT_HIGH, "\n"); + //current_special++; + } +} + +void (vector here, float which, string special, string trg, string trg2, string trg3, string trg4, string trg5, string trg6, string trg7, string trg8) Create_Waypoint = +{ + entity new_way; + new_way = spawn(); + + setorigin(new_way, here); + + + //new_way.flags = FL_ITEM; + new_way.solid = SOLID_TRIGGER; + + if (cvar("waypoint_mode")) + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + new_way.classname = strzone(new_way.classname); + + new_way.waynum = ftos(which); + new_way.waynum = strzone(new_way.waynum); + dprint ("Created waypoint "); + dprint (new_way.waynum); + dprint ("\n"); + + if (special != "") + { + if (!cvar("waypoint_mode")) + new_way.classname = "waypoint_s"; + if (cvar("waypoint_mode")) + setmodel(new_way, "models/way/normal_way_door.spr"); + new_way.targetname = special; + new_way.targetname = strzone(new_way.targetname); + dprint ("Special waypoint "); + dprint (new_way.targetname); + dprint ("\n"); + //current_special++; + } + if (trg) + { + new_way.target = trg; + new_way.target = strzone(new_way.target); + dprint ("Waypoint target "); + dprint (new_way.target); + dprint ("\n"); + } + if (trg2) + { + new_way.target2 = trg2; + new_way.target2 = strzone(new_way.target2); + dprint ("Waypoint target2 "); + dprint (new_way.target2); + dprint ("\n"); + } + if (trg3) + { + new_way.target3 = trg3; + new_way.target3 = strzone(new_way.target3); + dprint ("Waypoint target3 "); + dprint (new_way.target3); + dprint ("\n"); + } + if (trg4) + { + new_way.target4 = trg4; + new_way.target4 = strzone(new_way.target4); + dprint ("Waypoint target4 "); + dprint (new_way.target4); + dprint ("\n"); + } + if (trg5) + { + new_way.target5 = trg5; + new_way.target5 = strzone(new_way.target5); + dprint ("Waypoint target5 "); + dprint (new_way.target5); + dprint ("\n"); + } + if (trg6) + { + new_way.target6 = trg6; + new_way.target6 = strzone(new_way.target6); + dprint ("Waypoint target6 "); + dprint (new_way.target6); + dprint ("\n"); + } + if (trg7) + { + new_way.target7 = trg7; + new_way.target7 = strzone(new_way.target7); + dprint ("Waypoint target7 "); + dprint (new_way.target7); + dprint ("\n"); + } + if (trg8) + { + new_way.target8 = trg8; + new_way.target8 = strzone(new_way.target8); + dprint ("Waypoint target8 "); + dprint (new_way.target8); + dprint ("\n"); + } + + new_way.touch = creator_way_touch; +} + +string tempstest; +void() Save_Waypoints +{ + local float file; + string h; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_WRITE); + + local entity dway; + //fputs(file, "begin\n"); + dway = find(world, classname, "waypoint"); + while (dway) + { + dprint ("Saving waypoints\n"); + fputs(file,"Waypoint\n"); + fputs(file,"{\norigin = "); + tempstest = vtos(dway.origin); + tempstest = strzone(tempstest); + fputs(file,tempstest); + strunzone (tempstest); + fputs(file,"\nid = "); + fputs(file,dway.waynum); + fputs(file,"\nspecial = "); + fputs(file,dway.targetname); + fputs(file,"\ntarget = "); + fputs(file,dway.target); + fputs(file,"\ntarget2 = "); + fputs(file,dway.target2); + fputs(file,"\ntarget3 = "); + fputs(file,dway.target3); + fputs(file,"\ntarget4 = "); + fputs(file,dway.target4); + fputs(file,"\ntarget5 = "); + fputs(file,dway.target5); + fputs(file,"\ntarget6 = "); + fputs(file,dway.target6); + fputs(file,"\ntarget7 = "); + fputs(file,dway.target7); + fputs(file,"\ntarget8 = "); + fputs(file,dway.target8); + fputs(file,"\n"); + fputs(file,"}\n"); + + + dway = find(dway, classname, "waypoint"); + if (dway) + fputs(file,"\n"); + } + fclose(file); +} +void() Load_Waypoints +{ + float file, which; + string h, special, trg, trg2, trg3, trg4, trg5, trg6, trg7, trg8; + local vector where; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + dprint("Error: file not found \n"); + return; + } + + while (1) + { + dprint("Loading waypoint\n"); + // the first line is just a comment, ignore it + h = fgets(file); + if (h != "Waypoint") + { + bprint(PRINT_HIGH, "Last waypoint\n"); + fclose(file); + return; + } + h = fgets(file); + h = fgets(file); + h = substring(h, 9, 20); + where = stov(h); + + h = (fgets(file)); + h = substring(h, 5, 20); + which = stof(h); + + h = (fgets(file)); + special = substring(h, 10, 20); + special = strzone(special); + + h = (fgets(file)); + trg = substring(h, 9, 20); + trg = strzone(trg); + + h = (fgets(file)); + trg2 = substring(h, 10, 20); + trg2 = strzone(trg2); + + h = (fgets(file)); + trg3 = substring(h, 10, 20); + trg3 = strzone(trg3); + + h = (fgets(file)); + trg4 = substring(h, 10, 20); + trg4 = strzone(trg4); + + h = (fgets(file)); + trg5 = substring(h, 10, 20); + trg5 = strzone(trg5); + + h = (fgets(file)); + trg6 = substring(h, 10, 20); + trg6 = strzone(trg6); + + h = (fgets(file)); + trg7 = substring(h, 10, 20); + trg7 = strzone(trg7); + + h = (fgets(file)); + trg8 = substring(h, 10, 20); + trg8 = strzone(trg8); + + Create_Waypoint(where, which, special, trg, trg2, trg3, trg4, trg5, trg6, trg7, trg8); + + strunzone (trg); + strunzone (trg2); + strunzone (trg3); + strunzone (trg4); + strunzone (trg5); + strunzone (trg6); + strunzone (trg7); + strunzone (trg8); + h = (fgets(file)); + h = (fgets(file)); + } +} \ No newline at end of file diff --git a/source/server/ai/waypoints_core.qc b/source/server/ai/waypoints_core.qc new file mode 100644 index 0000000..b4bc73d --- /dev/null +++ b/source/server/ai/waypoints_core.qc @@ -0,0 +1,828 @@ +/* + server/ai/pathfind_core.qc + + generic waypoints + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() creator_way_touch = +{ + if (cvar("waypoint_mode")) { + if (other.classname != "player") { + return; + } + + current_way = self; + } +} + +void () Create_New_Waypoint = +{ + float way_count; + float tempf; + entity tempe; + entity new_way; + + way_count = -1; + tempe = find (world, classname, "waypoint"); + while (tempe) { + tempf = stof(tempe.waynum); + if (tempf > way_count) { + way_count = tempf; + } + tempe = find (tempe, classname, "waypoint"); + } + new_way = spawn(); + + setorigin(new_way, self.origin); + + + //new_way.flags = FL_ITEM; + new_way.solid = SOLID_TRIGGER; + + + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.waynum = ftos(way_count + 1); + new_way.targetname = strzone(new_way.targetname); + bprint (PRINT_HIGH, "Created waypoint "); + bprint (PRINT_HIGH, new_way.waynum); + bprint (PRINT_HIGH, "\n"); + + new_way.touch = creator_way_touch; +} + + +void () Make_Special_Waypoint = +{ + if (self.classname != "player" || !active_way) { + return; + } + if (active_way.targetname != "") {//Toggling it back off + setmodel(active_way, "models/way/current_way.spr"); + active_way.targetname = ""; + bprint (PRINT_HIGH, "Waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, " is no longer a special waypoint\n"); + return; + } + + if (active_way) { + if(self.active_door == world) { + bprint (PRINT_HIGH, "Error: no door selected!\n"); + return; + } + + if(self.active_door.wayTarget == "") { + bprint (PRINT_HIGH, "Error: Door has no wayTarget value!\n"); + return; + } + setmodel(active_way, "models/way/current_way_door.spr"); + active_way.targetname = self.active_door.wayTarget; + + bprint (PRINT_HIGH, "special waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, " named "); + bprint (PRINT_HIGH, active_way.targetname); + bprint (PRINT_HIGH, "\n"); + } +} + + +void () Move_Waypoint = +{ + if (!active_way) + return; + + setorigin (active_way, self.origin); + bprint (PRINT_HIGH, "Moved waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + bprint (PRINT_HIGH, "\n"); + +} + +void () Select_Waypoint = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + + entity tempe; + + if (current_way == active_way) + active_way = world; + else + active_way = current_way; + + tempe = find (world, classname, "waypoint"); + while (tempe) + { + if (tempe.targetname != "") + setmodel(tempe, "models/way/normal_way_door.spr"); + else + setmodel(tempe, "models/way/normal_way.spr"); + tempe = find (tempe, classname, "waypoint"); + } + if (active_way) + { + if (active_way.targetname != "") + setmodel(active_way, "models/way/current_way_door.spr"); + else + setmodel(active_way, "models/way/current_way.spr"); + + bprint (PRINT_HIGH, "Selected waypoint "); + bprint (PRINT_HIGH, active_way.waynum); + if(active_way.targetname != "") + { + bprint (PRINT_HIGH, ", special tag "); + bprint (PRINT_HIGH, active_way.targetname); + } + bprint (PRINT_HIGH, "\n"); + + float i; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + tempe = find (world, waynum, active_way.targets[i]); + if (tempe) { + if (tempe.targetname != "") + setmodel(tempe, "models/way/last_way_door.spr"); + else + setmodel(tempe, "models/way/last_way.spr"); + } else { + active_way.targets[i] = ""; + } + } + } +} + + +void() Remove_Waypoint = +{ + entity tempe; + float i; + if (!active_way) + return; + + tempe = find (world, classname, "waypoint"); + while (tempe) { + + for (i = 0; i < MAX_WAY_TARGETS; i++) { + if (tempe.targets[i] == active_way.waynum) { + tempe.targets[i] = ""; + } + } + + tempe = find (tempe, classname, "waypoint"); + } + + bprint(PRINT_HIGH, "Removed waypoint "); + bprint(PRINT_HIGH, active_way.waynum); + bprint(PRINT_HIGH, "\n"); + remove (active_way); +} + +float Waypoint_Linked_To(entity from, entity to) { + float i; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + if (from.waynum == to.targets[i]) { + bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + return 1; + } + } + + return 0; +} + +float Link (entity from, entity to) { + float i; + entity tempe; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + tempe = find (world, waynum, from.targets[i]); + + if (tempe == world || tempe == to) { + from.targets[i] = to.waynum; + bprint(PRINT_HIGH, "Linked waypoint "); + bprint(PRINT_HIGH, to.waynum); + bprint(PRINT_HIGH, " to "); + bprint(PRINT_HIGH, from.waynum); + bprint(PRINT_HIGH, "\n"); + + if (to.targetname != "") { + setmodel(to, "models/way/last_way_door.spr"); + } else { + setmodel(to, "models/way/last_way.spr"); + } + + return 1; + } + } + + return 0; +} + +void () Link_Waypoints = +{ + if (self.classname != "player") + return; + if (!current_way) + return; + if (!active_way) + return; + if (current_way == active_way) + return; + + if (Waypoint_Linked_To(current_way, active_way)) { + bprint(PRINT_HIGH, "These waypoints are already linked!\n"); + return; + } + + float i; + entity tempe; + for (i = 0; i < MAX_WAY_TARGETS; i++) { + #ifdef PC + tempe = findfloat (world, waynum, active_way.targets[i]); + #else + tempe = find (world, waynum, active_way.targets[i]); + #endif + + if (tempe == world) { + if (Link(active_way, current_way)) { + return; + } + } + } + + + bprint(PRINT_HIGH, "no targets remaining!\n"); +} + + + +void() Dual_Link_Waypoints = +{ + if (self.classname != "player" || !current_way || !active_way || current_way == active_way) { + return; + } + + float result1,result2; + result1 = Waypoint_Linked_To(current_way,active_way); + result2 = Waypoint_Linked_To(active_way,current_way); + + if(result1 && result2) { + bprint(PRINT_HIGH, "Both waypoints already linked!\n"); + return; + } + + if(!result1) + { + if (Link(current_way,active_way)) { + bprint(PRINT_HIGH, strcat("Linked waypoint ", strcat(current_way.waynum, strcat(" to ",strcat(active_way.waynum, "\n"))))); + } else { + bprint(PRINT_HIGH, strcat("ERROR: Could not link waypoint ", strcat(current_way.waynum, strcat(" to ", strcat(active_way.waynum, "\n"))))); + } + } + if(!result2) + { + if (Link(active_way,current_way)) { + bprint(PRINT_HIGH, strcat("Linked waypoint ", strcat(active_way.waynum, strcat(" to ", strcat(current_way.waynum, "\n"))))); + } else { + bprint(PRINT_HIGH, strcat("ERROR: Could not link waypoint ", strcat(active_way.waynum, strcat(" to ", strcat(current_way.waynum, "\n"))))); + } + } +} + + + +//alter auto_link_waypoints to iterate through the closest waypoints from closest to furthest +// on the innermost loop, we find the next closest waypoint that is further away from the last closest waypoint, and we use that! + +void() Auto_Link_Waypoints = +{ + entity tempe1, tempe2; + + tempe1 = find(world,classname,"waypoint"); + while(tempe1 != world) + { + tempe2 = find(world,classname,"waypoint"); + while(tempe2 != world) + { + if(tempe1 == tempe2) + { + tempe2 = find(tempe2,classname,"waypoint"); + continue; + } + + if(tracemove(tempe1.origin,VEC_HULL_MIN,VEC_HULL_MAX,tempe2.origin,TRUE,self)) + { + Link(tempe1,tempe2); + } + + tempe2 = find(tempe2,classname,"waypoint"); + } + tempe1 = find(tempe1,classname,"waypoint"); + } +} +//alter auto_link_waypoints to iterate through the closest waypoints from closest to furthest +// on the innermost loop, we find the next closest waypoint that is further away from the last closest waypoint, and we use that! + +void() Remove_Links = +{ + entity tempe; + + tempe = find(world,classname,"waypoint"); + while(tempe != world) + { + float i; + for (i = 0; i < MAX_WAY_TARGETS; i = i + 1) { + if (tempe.targetname != "") + setmodel(tempe, "models/way/normal_way_door.spr"); + else + setmodel(tempe, "models/way/normal_way.spr"); + tempe.targets[i] = ""; + } + tempe = find(tempe,classname,"waypoint"); + } +} + +void() Save_Waypoints +{ + float file; + string h; + float i; + entity tempe; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_WRITE); + + dprint (strcat("Saving waypoints ", strcat(h, "\n"))); + + local entity dway; + //fputs(file, "begin\n"); + dway = find(world, classname, "waypoint"); + while (dway) + { + fputs(file,"waypoint\n"); + fputs(file,"{\n"); + fputs(file, strcat(" id: ", strcat(dway.waynum, "\n"))); + fputs(file, strcat(" origin: ", strcat(vtos(dway.origin), "\n"))); + if (dway.targetname != "") { + fputs(file, strcat(" door: ", strcat(dway.targetname, "\n"))); + } + + fputs(file, " targets:\n"); + fputs(file, " [\n"); + for (i = 0; i < MAX_WAY_TARGETS; i++) { + if (dway.targets[i] != "") { + #ifdef PC + tempe = findfloat (world, waynum, dway.targets[i]); + #else + tempe = find (world, waynum, dway.targets[i]); + #endif + + if (tempe != world) { + fputs(file, strcat(" ", strcat(dway.targets[i], "\n"))); + } else { + tempe = find (world, waynum, dway.targets[i]); + if (tempe != world) { + fputs(file, strcat(" ", strcat(dway.targets[i], "\n"))); + } + } + } + } + fputs(file, " ]\n"); + fputs(file,"}\n"); + + + dway = find(dway, classname, "waypoint"); + if (dway) + fputs(file,"\n"); + } + fclose(file); +} + +void (vector here, float which, string special, string trg, string trg2, string trg3, string trg4, string trg5, string trg6, string trg7, string trg8) Create_Waypoint = +{ + entity new_way; + new_way = spawn(); + + setorigin(new_way, here); + + + //new_way.flags = FL_ITEM; + new_way.solid = SOLID_TRIGGER; + + if (cvar("waypoint_mode")) + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.waynum = ftos(which); + dprint ("Created waypoint "); + dprint (new_way.waynum); + dprint ("\n"); + + if (special != "") + { + if (!cvar("waypoint_mode")) + new_way.classname = "waypoint_s"; + if (cvar("waypoint_mode")) + setmodel(new_way, "models/way/normal_way_door.spr"); + new_way.targetname = special; + dprint ("Special waypoint "); + dprint (new_way.targetname); + dprint ("\n"); + //current_special++; + } + + new_way.targets[0] = trg; + new_way.targets[1] = trg2; + new_way.targets[2] = trg3; + new_way.targets[3] = trg4; + new_way.targets[4] = trg5; + new_way.targets[5] = trg6; + new_way.targets[6] = trg7; + new_way.targets[7] = trg8; + + new_way.touch = creator_way_touch; +} + +float waypoints_loaded; +void() Load_Waypoints +{ + float file, point; + string h; + float targetcount, loop; + + entity new_way; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + dprint("Error: file not found \n"); + return; + } + + new_way = spawn(); + targetcount = 0; + point = 0; + loop = 1; + while (loop) + { + string line; + line = fgets(file); + if not (line) { + bprint(PRINT_HIGH, "End of file\n"); + loop = 0; + break; + } + h = strtrim(line); + + //bprint(PRINT_HIGH, strcat(h, "\n")); + if (h == "") { + continue; + } + + switch (point) { + case 0: + if (h == "waypoint") { + new_way = spawn(); + new_way.solid = SOLID_TRIGGER; + new_way.model = "models/way/normal_way.spr"; + setmodel(new_way, "models/way/normal_way.spr"); + new_way.classname = "waypoint"; + + new_way.touch = creator_way_touch; + point = 1; + targetcount = 0; + } else if (h == "Waypoint") { + bprint(PRINT_HIGH, "Identified .way as legacy..\n"); + point = 99; + Load_Waypoints_Legacy(); + loop = 0; + break; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown point ", strcat(h, "\n"))); + } + break; + case 1: + if (h == "{") { + point = 2; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected {\n"))); + } + break; + case 2: + tokenize(h); + + string value, variable; + variable = strtrim(argv(0)); + value = strtrim(argv(2)); + + //bprint(PRINT_HIGH, strcat(variable, "\n")); + + switch (variable) { + case "origin": + + print(strcat(value, "\n")); + new_way.origin = stov(value); + setorigin(new_way, new_way.origin); + break; + case "id": + new_way.waynum = value; + break; + case "door": + new_way.targetname = value; + + setmodel(new_way, "models/way/normal_way_door.spr"); + break; + case "targets": + point = 3; + break; + case "}": + point = 0; + break; + default: + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(variable, "\n"))); + break; + } + break; + case 3: + if (h == "[") { + point = 4; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected [\n"))); + } + break; + case 4: + if (targetcount >= MAX_WAY_TARGETS) { + bprint(PRINT_HIGH, "Error: Target count too high for waypoint\n"); + } else if (h == "]") { + point = 2; + } else { + bprint(PRINT_HIGH, strcat(strcat("WAYPOINT TARGET: ", strcat(strcat(ftos(targetcount), " "), h)), "\n")); + new_way.targets[targetcount] = h; + targetcount++; + } + break; + } + } + + fclose(file); + waypoints_loaded = 1; +} + +void() Load_Waypoints_Legacy +{ + float file, which; + string h, special, trg, trg2, trg3, trg4, trg5, trg6, trg7, trg8; + local vector where; + + h = strcat(mappath, ".way"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + dprint("Error: file not found \n"); + return; + } + + while (1) + { + dprint("Loading waypoint\n"); + // the first line is just a comment, ignore it + h = fgets(file); + if (h != "Waypoint") + { + bprint(PRINT_HIGH, "Last waypoint\n"); + fclose(file); + return; + } + h = fgets(file); + h = fgets(file); + h = substring(h, 9, 20); + where = stov(h); + + h = (fgets(file)); + h = substring(h, 5, 20); + which = stof(h); + + h = (fgets(file)); + special = substring(h, 10, 20); + + h = (fgets(file)); + trg = substring(h, 9, 20); + + h = (fgets(file)); + trg2 = substring(h, 10, 20); + + h = (fgets(file)); + trg3 = substring(h, 10, 20); + + h = (fgets(file)); + trg4 = substring(h, 10, 20); + + h = (fgets(file)); + trg5 = substring(h, 10, 20); + + h = (fgets(file)); + trg6 = substring(h, 10, 20); + + h = (fgets(file)); + trg7 = substring(h, 10, 20); + + h = (fgets(file)); + trg8 = substring(h, 10, 20); + + #ifndef PC + Create_Waypoint(where, which, special, trg, trg2, trg3, trg4, trg5, trg6, trg7, trg8); + #else + waypoint_ai waypoint; + waypoint = waypoints[which]; + + waypoints[which].id = which; + waypoints[which].org = where; + waypoints[which].targetdoor = special; + + waypoints[which].target_id[0] = stof(trg); + waypoints[which].target_id[1] = stof(trg2); + waypoints[which].target_id[2] = stof(trg3); + waypoints[which].target_id[3] = stof(trg4); + waypoints[which].target_id[4] = stof(trg5); + waypoints[which].target_id[5] = stof(trg6); + waypoints[which].target_id[6] = stof(trg7); + waypoints[which].target_id[7] = stof(trg8); + #endif + + h = (fgets(file)); + h = (fgets(file)); + } +} + + +void VisualizePathfind() { + if (self.classname != "player") + return; + if (!current_way) + return; + if (!active_way) + return; + if (current_way == active_way) + return; + + Pathfind(self, stof(active_way.waynum), stof(current_way.waynum)); +} + +.float waypoint_delay; + +//Waypoint logic functions +void () Waypoint_Functions = +{ + // naievil -- archaic + /* + switch (self.impulse) { + case 100: + Create_New_Waypoint(); + break; + case 101: + Make_Special_Waypoint(); + break; + case 102: + Select_Waypoint(); + break; + case 103: + Move_Waypoint(); + break; + case 104: + Link_Waypoints (); + break; + case 105: + Dual_Link_Waypoints(); + break; + case 106: + Save_Waypoints(); + break; + case 107: + Load_Waypoints(); + break; + case 108: + Load_Waypoints_Legacy(); + break; + case 109: + Remove_Waypoint(); + //VisualizePathfind(); + break; + case 110: + Auto_Link_Waypoints(); + break; + case 111: + Remove_Links(); + break; + } + + self.impulse = 0; + */ + + switch (self.impulse) { + case 23: + Save_Waypoints(); + break; + case 22: + if (!waypoints_loaded) + Load_Waypoints(); + break; + case 110: + Move_Waypoint(); + break; + } + + self.impulse = 0; + + // Match what we display on the screen + if (self.button0 && self.waypoint_delay < time) { + Create_New_Waypoint(); + self.waypoint_delay = time + 1; + } + + if (self.button7 && self.waypoint_delay < time) { + Select_Waypoint(); + self.waypoint_delay = time + 0.25; + } + + if (self.button8 && self.waypoint_delay < time) { + Link_Waypoints (); + self.waypoint_delay = time + 0.5; + } + + if (self.button6 && self.waypoint_delay < time) { + Remove_Waypoint(); + self.waypoint_delay = time + 0.5; + } + + if (self.button5 && self.waypoint_delay < time) { + Make_Special_Waypoint(); + self.waypoint_delay = time + 1; + } + +}; + +void () Waypoint_Logic = +{ + if (!waypoint_mode) { + waypoint_mode = 1; + entity zent; + + zent = find (world, classname, "ai_zombie"); + while (zent) + { + remove (zent); + zent = find (zent, classname, "ai_zombie"); + } + + zent = find (world, classname, "door_nzp_cost"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent = find (zent, classname, "door_nzp_cost"); + } + zent = find (world, classname, "door_nzp"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent.solid = SOLID_NOT; + zent = find (zent, classname, "door_nzp"); + } + zent = find (world, classname, "window"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent = find (zent, classname, "window"); + } + + } + Waypoint_Functions(); +}; \ No newline at end of file diff --git a/source/server/ai/zombie_core.qc b/source/server/ai/zombie_core.qc new file mode 100644 index 0000000..613e7b5 --- /dev/null +++ b/source/server/ai/zombie_core.qc @@ -0,0 +1,1679 @@ +/* + server/ai/zombie_core.qc + + zombie things + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() Zombie_Walk_Setup; +void() crawler_rip_board1; +void() crawler_da1; +void() crawler_die1; + + +float EMPTY_BBOX = 0; +float BASE_BBOX = 1; +float IDLE_BBOX = 2; +//Rising from ground bbox +float WALK1_BBOX = 4; +float WALK2_BBOX = 5; +float WALK3_BBOX = 6; +float JOG_BBOX = 7; +float RUN_BBOX = 8; +float CRAWLER_BBOX = 9; + +void(float what) play_sound_z = +{ + if(self.classname == "ai_zombie") + { + local float e; + e = random(); + + if(what == 1)//attack + { + if (e < 0.125) + sound(self,CHAN_VOICE,"sounds/zombie/a0.wav",1,ATTN_NORM); + else if (e < 0.25) + sound(self,CHAN_VOICE,"sounds/zombie/a1.wav",1,ATTN_NORM); + else if (e < 0.375) + sound(self,CHAN_VOICE,"sounds/zombie/a2.wav",1,ATTN_NORM); + else if( e < 0.5) + sound(self,CHAN_VOICE,"sounds/zombie/a3.wav",1,ATTN_NORM); + else if( e < 0.625) + sound(self,CHAN_VOICE,"sounds/zombie/a4.wav",1,ATTN_NORM); + else if( e < 0.75) + sound(self,CHAN_VOICE,"sounds/zombie/a5.wav",1,ATTN_NORM); + else if( e < 0.875) + sound(self,CHAN_VOICE,"sounds/zombie/a6.wav",1,ATTN_NORM); + else + sound(self,CHAN_VOICE,"sounds/zombie/a7.wav",1,ATTN_NORM); + return; + } + if(what == 2)//walking! + { + + if(self.s_time < time && self.s_time > 0) + { + sounds_playing --; + self.s_time = 0; + } + if(sounds_playing >= 3) + { + return; + } + sounds_playing++; + self.s_time = time + 1; + + + if(self.sound_time < time) + { + play_sound_z(4); + + if(self.crawling == TRUE) + { + self.sound_time = time + 3 + random()*2; + if(e < 0.2) + sound(self,CHAN_VOICE,"sounds/zombie/t0.wav",1,ATTN_NORM); + else if (e < 0.4) + sound(self,CHAN_VOICE,"sounds/zombie/t1.wav",1,ATTN_NORM); + else if (e < 0.6) + sound(self,CHAN_VOICE,"sounds/zombie/t2.wav",1,ATTN_NORM); + else if (e < 0.8) + sound(self,CHAN_VOICE,"sounds/zombie/t3.wav",1,ATTN_NORM); + else + sound(self,CHAN_VOICE,"sounds/zombie/t4.wav",1,ATTN_NORM); + return; + } + if(self.walktype == 1 || self.walktype == 2 || self.walktype == 3) + { + self.sound_time = time + 3 + random(); + if (e < 0.1) + sound(self,CHAN_VOICE,"sounds/zombie/w0.wav",1,ATTN_NORM); + else if (e < 0.2) + sound(self,CHAN_VOICE,"sounds/zombie/w1.wav",1,ATTN_NORM); + else if (e < 0.3) + sound(self,CHAN_VOICE,"sounds/zombie/w2.wav",1,ATTN_NORM); + else if( e < 0.4) + sound(self,CHAN_VOICE,"sounds/zombie/w3.wav",1,ATTN_NORM); + else if( e < 0.5) + sound(self,CHAN_VOICE,"sounds/zombie/w4.wav",1,ATTN_NORM); + else if( e < 0.6) + sound(self,CHAN_VOICE,"sounds/zombie/w5.wav",1,ATTN_NORM); + else if( e < 0.7) + sound(self,CHAN_VOICE,"sounds/zombie/w6.wav",1,ATTN_NORM); + else if( e < 0.8) + sound(self,CHAN_VOICE,"sounds/zombie/w7.wav",1,ATTN_NORM); + else if( e < 0.9) + sound(self,CHAN_VOICE,"sounds/zombie/w8.wav",1,ATTN_NORM); + else + sound(self,CHAN_VOICE,"sounds/zombie/w9.wav",1,ATTN_NORM); + return; + } + else if(self.walktype == 4 || self.walktype == 5) + { + self.sound_time = time + 4 + random()*2; + if (e < 0.1) + sound(self,CHAN_VOICE,"sounds/zombie/r0.wav",1,ATTN_NORM); + else if (e < 0.2) + sound(self,CHAN_VOICE,"sounds/zombie/r1.wav",1,ATTN_NORM); + else if (e < 0.3) + sound(self,CHAN_VOICE,"sounds/zombie/r2.wav",1,ATTN_NORM); + else if( e < 0.4) + sound(self,CHAN_VOICE,"sounds/zombie/r3.wav",1,ATTN_NORM); + else if( e < 0.5) + sound(self,CHAN_VOICE,"sounds/zombie/r4.wav",1,ATTN_NORM); + else if( e < 0.6) + sound(self,CHAN_VOICE,"sounds/zombie/r5.wav",1,ATTN_NORM); + else if( e < 0.7) + sound(self,CHAN_VOICE,"sounds/zombie/r6.wav",1,ATTN_NORM); + else if( e < 0.8) + sound(self,CHAN_VOICE,"sounds/zombie/r7.wav",1,ATTN_NORM); + else if( e < 0.9) + sound(self,CHAN_VOICE,"sounds/zombie/r8.wav",1,ATTN_NORM); + else + sound(self,CHAN_VOICE,"sounds/zombie/r9.wav",1,ATTN_NORM); + return; + } + + } + } + if(what == 3)//death! + { + if (e < 0.125) + sound(self,CHAN_VOICE,"sounds/zombie/d0.wav",1,ATTN_NORM); + else if (e < 0.25) + sound(self,CHAN_VOICE,"sounds/zombie/d1.wav",1,ATTN_NORM); + else if (e < 0.375) + sound(self,CHAN_VOICE,"sounds/zombie/d2.wav",1,ATTN_NORM); + else if( e < 0.5) + sound(self,CHAN_VOICE,"sounds/zombie/d3.wav",1,ATTN_NORM); + else if( e < 0.625) + sound(self,CHAN_VOICE,"sounds/zombie/d4.wav",1,ATTN_NORM); + else if( e < 0.75) + sound(self,CHAN_VOICE,"sounds/zombie/d5.wav",1,ATTN_NORM); + else if( e < 0.875) + sound(self,CHAN_VOICE,"sounds/zombie/d6.wav",1,ATTN_NORM); + else + sound(self,CHAN_VOICE,"sounds/zombie/d7.wav",1,ATTN_NORM); + return; + + } + if(what == 4)//Taunt! + { + if(random() < 0.83) + return; + + if(e < 0.2) + sound(self,CHAN_VOICE,"sounds/zombie/t0.wav",1,ATTN_NORM); + else if (e < 0.4) + sound(self,CHAN_VOICE,"sounds/zombie/t1.wav",1,ATTN_NORM); + else if (e < 0.6) + sound(self,CHAN_VOICE,"sounds/zombie/t2.wav",1,ATTN_NORM); + else if (e < 0.8) + sound(self,CHAN_VOICE,"sounds/zombie/t3.wav",1,ATTN_NORM); + else + sound(self,CHAN_VOICE,"sounds/zombie/t4.wav",1,ATTN_NORM); + return; + } + } +}; + +void(float which) SetZombieHitBox = +{ + + //Zombie's bbox is constant across all hitbox setups, so we don't update it. + + if(self.currentHitBoxSetup == which) + return; + + switch(which) + { + case EMPTY_BBOX: + { + /*setorigin(self.head,'0 0 0'); + setsize (self.head, '0 0 0', '0 0 0'); + + setorigin(self.larm, '0 0 0'); + setsize (self.larm, '0 0 0','0 0 0'); + + setorigin(self.rarm, '0 0 0'); + setsize (self.rarm, '0 0 0','0 0 0');*/ + + self.head.view_ofs = '0 0 0'; + self.rarm.view_ofs = '0 0 0'; + self.larm.view_ofs = '0 0 0'; + + self.currentHitBoxSetup = EMPTY_BBOX; + return; + } + case IDLE_BBOX: + { + //setsize (self.head, '-5 -5 -5', '5 5 5'); + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '0 10 35'; + + //setsize (self.larm, '-5 -5 -15', '5 5 15'); + self.larm.bbmins = '-5 -5 -15'; + self.larm.bbmaxs = '5 5 15'; + self.larm.view_ofs = '-10 10 10'; + + //setsize (self.rarm, '-5 -5 -15', '5 5 15'); + self.rarm.bbmins = '-5 -5 -15'; + self.rarm.bbmaxs = '5 5 15'; + self.rarm.view_ofs = '10 10 10'; + + self.currentHitBoxSetup = IDLE_BBOX; + return; + } + case WALK1_BBOX: + { + //setsize (self.head, '-5 -5 -5', '5 5 5'); + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '2 -3 35'; + + //setsize (self.head, '-3 -8 30', '7 2 40'); + //self.head.view_ofs = '0 0 0'; + + //setsize (self.larm, '-5 -7 -15', '5 7 15'); + self.larm.bbmins = '-5 -7 -15'; + self.larm.bbmaxs = '5 7 15'; + self.larm.view_ofs = '-10 -4 15'; + + //setsize (self.rarm, '-5 -8 -11', '5 8 11'); + self.rarm.bbmins = '-5 -8 -11'; + self.rarm.bbmaxs = '5 8 11'; + self.rarm.view_ofs = '10 11 11'; + + self.currentHitBoxSetup = WALK1_BBOX; + return; + } + case WALK2_BBOX: + { + //setsize (self.head, '-7 -5 -5', '7 5 5'); + self.head.bbmins = '-7 -5 -5'; + self.head.bbmaxs = '7 5 5'; + self.head.view_ofs = '-11 6 36'; + + //setsize (self.larm, '-5 -6 -15', '5 6 15'); + self.larm.bbmins = '-5 -6 -15'; + self.larm.bbmaxs = '5 6 15'; + self.larm.view_ofs = '-14 3 15'; + + //setsize (self.rarm, '-5 -5 -11', '5 5 11'); + self.rarm.bbmins = '-5 -5 -11'; + self.rarm.bbmaxs = '5 5 11'; + self.rarm.view_ofs = '4 13 11'; + + self.currentHitBoxSetup = WALK2_BBOX; + return; + } + case WALK3_BBOX: + { + //setsize (self.head, '-5 -5 -5', '5 5 5'); + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '-2 13 31'; + + //setsize (self.larm, '-4 -6 -14', '4 6 14'); + self.larm.bbmins = '-4 -6 -14'; + self.larm.bbmaxs = '4 6 14'; + self.larm.view_ofs = '-12 3 11'; + + //setsize (self.rarm, '-5 -5 -11', '5 5 11'); + self.rarm.bbmins = '-5 -5 -11'; + self.rarm.bbmaxs = '5 5 11'; + self.rarm.view_ofs = '14 6 12'; + + self.currentHitBoxSetup = WALK3_BBOX; + return; + } + case JOG_BBOX: + { + //setsize (self.head, '-5 -5 -5', '5 5 5'); + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '0 5 36'; + + //setsize (self.larm, '-5 -13 -5', '5 13 5'); + self.larm.bbmins = '-5 -13 -5'; + self.larm.bbmaxs = '5 13 5'; + self.larm.view_ofs = '-8 19 24'; + + //setsize (self.rarm, '-5 -13 -5', '5 13 5'); + self.rarm.bbmins = '-5 -13 -5'; + self.rarm.bbmaxs = '5 13 5'; + self.rarm.view_ofs = '10 19 24'; + + self.currentHitBoxSetup = JOG_BBOX; + return; + } + case RUN_BBOX: + { + //setsize (self.head, '-5 -5 -5', '5 5 5'); + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '3 17 32'; + + //setsize (self.larm, '-4 -10 -9', '4 10 9'); + self.larm.bbmins = '-4 -10 -9'; + self.larm.bbmaxs = '4 10 9'; + self.larm.view_ofs = '-12 28 18'; + + //setsize (self.rarm, '-4 -13 -10', '4 13 10'); + self.rarm.bbmins = '-4 -13 -10'; + self.rarm.bbmaxs = '4 13 10'; + self.rarm.view_ofs = '12 -2 19'; + + self.currentHitBoxSetup = RUN_BBOX; + return; + } + case CRAWLER_BBOX: + { + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '2 18 -14'; + + self.larm.bbmins = '-4 -10 -9'; + self.larm.bbmaxs = '4 10 9'; + self.larm.view_ofs = '-9 24 -27'; + + self.rarm.bbmins = '-4 -13 -10'; + self.rarm.bbmaxs = '4 13 10'; + self.rarm.view_ofs = '12 15 -25'; + + self.currentHitBoxSetup = CRAWLER_BBOX; + return; + } + default: //also known as BASE_BBOX + { + //setsize (self.head, '-5 -5 -5', '5 5 5'); + self.head.bbmins = '-5 -5 -5'; + self.head.bbmaxs = '5 5 5'; + self.head.view_ofs = '0 0 35'; + + //setsize (self.larm, '-5 -5 -15', '5 5 15'); + self.larm.bbmins = '-5 -5 -15'; + self.larm.bbmaxs = '5 5 15'; + self.larm.view_ofs = '-10 0 5'; + + //setsize (self.rarm, '-5 -5 -15', '5 5 15'); + self.rarm.bbmins = '-5 -5 -15'; + self.rarm.bbmaxs = '5 5 15'; + self.rarm.view_ofs = '10 0 5'; + + self.currentHitBoxSetup = BASE_BBOX; + return; + } + } +}; + +void() Zombie_Think = //called every frame for zombies +{ + if (self.onfire) { + #ifdef PC + te_flamejet(self.origin, v_up*8, 10); + #else + particle (self.origin, v_up*8, 0, 0); + #endif + + if (self.ltime < time && self.onfire){ + DamageHandler(self, self.firer, 300, S_NORMAL); + self.ltime = time + 2; + + self.spins++; + if (self.spins >= 5) { + self.spins = 0; + self.onfire = false; + } + } + } + + if (self.classname == "wunder") { + self.th_diewunder(); + return; + } + + if (self.bleedingtime < time && !self.head.deadflag) + { + self.bleedingtime = time + 2; + DamageHandler (self, self.usedent, z_health * 0.05, S_HEADSHOT); + } +} + +////////////////////////////////////////////////////////////////////// +//37-51 +/* +$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 walk11 walk12 walk13 walk14 walk15 +void() zombie_walk1 =[ $walk1, zombie_walk2 ] {self.nextthink = time + 0.1;Zombie_Walk(3.95);*/ /*if(freeze_time < time)*/ /* self.frame = 37;}; +void() zombie_walk2 =[ $walk2, zombie_walk3 ] {self.nextthink = time + 0.1;Zombie_Walk(1); self.frame = 38;}; +void() zombie_walk3 =[ $walk3, zombie_walk4 ] {self.nextthink = time + 0.1;Zombie_Walk(9.472); self.frame = 39;}; +void() zombie_walk4 =[ $walk4, zombie_walk5 ] {self.nextthink = time + 0.1;Zombie_Walk(5.046); self.frame = 40;};//FOOTSTEP +void() zombie_walk5 =[ $walk5, zombie_walk6 ] {self.nextthink = time + 0.1;Zombie_Walk(6.425); self.frame = 41;};// +void() zombie_walk6 =[ $walk6, zombie_walk7 ] {self.nextthink = time + 0.1;Zombie_Walk(6.299); self.frame = 42;}; +void() zombie_walk7 =[ $walk7, zombie_walk8 ] {self.nextthink = time + 0.1;Zombie_Walk(6.788); self.frame = 43;}; +void() zombie_walk8 =[ $walk8, zombie_walk9 ] {self.nextthink = time + 0.1;Zombie_Walk(5.516); self.frame = 44;};//FOOTSTEP +void() zombie_walk9 =[ $walk9, zombie_walk10 ] {self.nextthink = time + 0.1;Zombie_Walk(2.191); self.frame = 45;}; +void() zombie_walk10 =[ $walk10, zombie_walk11 ] {self.nextthink = time + 0.1;Zombie_Walk(5.845); self.frame = 46;}; +void() zombie_walk11 =[ $walk11, zombie_walk12 ] {self.nextthink = time + 0.1;Zombie_Walk(7.42); self.frame = 47;};//FOOTSTEP +void() zombie_walk12 =[ $walk12, zombie_walk13 ] {self.nextthink = time + 0.1;Zombie_Walk(2.683); self.frame = 48;}; +void() zombie_walk13 =[ $walk13, zombie_walk14 ] {self.nextthink = time + 0.1;Zombie_Walk(6.001); self.frame = 49;}; +void() zombie_walk14 =[ $walk14, zombie_walk15 ] {self.nextthink = time + 0.1;Zombie_Walk(5.845); self.frame = 50;}; +void() zombie_walk15 =[ $walk15, Zombie_Walk_Setup ] {self.nextthink = time + 0.1;Zombie_Walk(6.793); self.frame = 51;};//FOOTSTEP +*/ + +void() zombie_footstep +{ + if(self.laststep == 1) + { + self.laststep = 0; + sound(self,5,"sounds/zombie/s0.wav",1,ATTN_NORM); + } + else + { + self.laststep = 1; + sound(self,5,"sounds/zombie/s1.wav",1,ATTN_NORM); + } +} + +////////////////////////////////////////////////////////////////////// +//37-52 +$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10 walk11 walk12 walk13 walk14 walk15 walk16 +//walk15 +//walk 1! +void() zombie_walk1 =[ $walk1, zombie_walk2 ] {self.nextthink = time + 0.12;Zombie_Walk(8);SetZombieHitBox(WALK1_BBOX);/*if(freeze_time < time)*/ self.frame = 37;Zombie_Think();}; +void() zombie_walk2 =[ $walk2, zombie_walk3 ] {self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 38;Zombie_Think();}; +void() zombie_walk3 =[ $walk3, zombie_walk4 ] {self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 39;Zombie_Think();}; +void() zombie_walk4 =[ $walk4, zombie_walk5 ] {self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 40;Zombie_Think();}; +void() zombie_walk5 =[ $walk5, zombie_walk6 ] {self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 41;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walk6 =[ $walk6, zombie_walk7 ] {self.nextthink = time + 0.12;Zombie_Walk(3.5); self.frame = 42;Zombie_Think();}; +void() zombie_walk7 =[ $walk7, zombie_walk8 ] {self.nextthink = time + 0.12;Zombie_Walk(8.8); self.frame = 43;Zombie_Think();}; +void() zombie_walk8 =[ $walk8, zombie_walk9 ] {self.nextthink = time + 0.12;Zombie_Walk(9); self.frame = 44;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walk9 =[ $walk9, zombie_walk10 ] {self.nextthink = time + 0.12;Zombie_Walk(4); self.frame = 45;Zombie_Think();}; +void() zombie_walk10 =[ $walk10, zombie_walk11 ] {self.nextthink = time + 0.12;Zombie_Walk(4); self.frame = 46;Zombie_Think();}; +void() zombie_walk11 =[ $walk11, zombie_walk12 ] {self.nextthink = time + 0.12;Zombie_Walk(7.8); self.frame = 47;Zombie_Think();}; +void() zombie_walk12 =[ $walk12, zombie_walk13 ] {self.nextthink = time + 0.12;Zombie_Walk(5.2); self.frame = 48;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walk13 =[ $walk13, zombie_walk14 ] {self.nextthink = time + 0.12;Zombie_Walk(2.4); self.frame = 49;Zombie_Think();}; +void() zombie_walk14 =[ $walk14, zombie_walk15 ] {self.nextthink = time + 0.12;Zombie_Walk(2.8); self.frame = 50;Zombie_Think();}; +void() zombie_walk15 =[ $walk15, zombie_walk16 ] {self.nextthink = time + 0.12;Zombie_Walk(6.5); self.frame = 51;Zombie_Think();}; +void() zombie_walk16 =[ $walk16, Zombie_Walk_Setup ] {self.nextthink = time + 0.12;Zombie_Walk(7.7); self.frame = 52;Zombie_Think();zombie_footstep();};//footstep + +///////////////////////////////////////////////////////////////////////// +//53-66 +//Walk 2! +$frame bwalk1 bwalk2 bwalk3 bwalk4 bwalk5 bwalk6 bwalk7 bwalk8 bwalk9 bwalk10 bwalk11 bwalk12 bwalk13 bwalk14 + +void() zombie_walkb1 =[ $bwalk1, zombie_walkb2 ] {self.nextthink = time + 0.13;Zombie_Walk(4.2);SetZombieHitBox(WALK2_BBOX);/*if(freeze_time < time)*/ self.frame = 53;Zombie_Think();}; +void() zombie_walkb2 =[ $bwalk2, zombie_walkb3 ] {self.nextthink = time + 0.13;Zombie_Walk(5.9); self.frame = 54;Zombie_Think();}; +void() zombie_walkb3 =[ $bwalk3, zombie_walkb4 ] {self.nextthink = time + 0.13;Zombie_Walk(3); self.frame = 55;Zombie_Think();}; +void() zombie_walkb4 =[ $bwalk4, zombie_walkb5 ] {self.nextthink = time + 0.13;Zombie_Walk(5.7); self.frame = 56;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walkb5 =[ $bwalk5, zombie_walkb6 ] {self.nextthink = time + 0.13;Zombie_Walk(4.2); self.frame = 57;Zombie_Think();}; +void() zombie_walkb6 =[ $bwalk6, zombie_walkb7 ] {self.nextthink = time + 0.13;Zombie_Walk(7.2); self.frame = 58;Zombie_Think();}; +void() zombie_walkb7 =[ $bwalk7, zombie_walkb8 ] {self.nextthink = time + 0.13;Zombie_Walk(4.5); self.frame = 59;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walkb8 =[ $bwalk8, zombie_walkb9 ] {self.nextthink = time + 0.13;Zombie_Walk(3.4); self.frame = 60;Zombie_Think();}; +void() zombie_walkb9 =[ $bwalk9, zombie_walkb10 ] {self.nextthink = time + 0.13;Zombie_Walk(1.2); self.frame = 61;Zombie_Think();}; +void() zombie_walkb10 =[ $bwalk10, zombie_walkb11 ] {self.nextthink = time + 0.13;Zombie_Walk(6.7); self.frame = 62;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walkb11 =[ $bwalk11, zombie_walkb12 ] {self.nextthink = time + 0.13;Zombie_Walk(8.4); self.frame = 63;Zombie_Think();}; +void() zombie_walkb12 =[ $bwalk12, zombie_walkb13 ] {self.nextthink = time + 0.13;Zombie_Walk(1.3); self.frame = 64;Zombie_Think();}; +void() zombie_walkb13 =[ $bwalk13, zombie_walkb14 ] {self.nextthink = time + 0.13;Zombie_Walk(6.3); self.frame = 65;Zombie_Think();}; +void() zombie_walkb14 =[ $bwalk14, Zombie_Walk_Setup ] {self.nextthink = time + 0.13;Zombie_Walk(3.6); self.frame = 66;Zombie_Think();zombie_footstep();};//footstep + +///////////////////////////////////////////////////////////////////////// +//67-82 +//Walk 3! +$frame cwalk1 cwalk2 cwalk3 cwalk4 cwalk5 cwalk6 cwalk7 cwalk8 cwalk9 cwalk10 cwalk11 cwalk12 cwalk13 cwalk14 cwalk15 cwalk16 + +void() zombie_walkc1 =[ $cwalk1, zombie_walkc2 ] {self.nextthink = time + 0.13;Zombie_Walk(3.4);SetZombieHitBox(WALK3_BBOX);/*if(freeze_time < time)*/ self.frame = 67;Zombie_Think();}; +void() zombie_walkc2 =[ $cwalk2, zombie_walkc3 ] {self.nextthink = time + 0.13;Zombie_Walk(2); self.frame = 68;Zombie_Think();}; +void() zombie_walkc3 =[ $cwalk3, zombie_walkc4 ] {self.nextthink = time + 0.13;Zombie_Walk(5.4); self.frame = 69;Zombie_Think();}; +void() zombie_walkc4 =[ $cwalk4, zombie_walkc5 ] {self.nextthink = time + 0.13;Zombie_Walk(5.6); self.frame = 70;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walkc5 =[ $cwalk5, zombie_walkc6 ] {self.nextthink = time + 0.13;Zombie_Walk(3); self.frame = 71;Zombie_Think();}; +void() zombie_walkc6 =[ $cwalk6, zombie_walkc7 ] {self.nextthink = time + 0.13;Zombie_Walk(3.1); self.frame = 72;Zombie_Think();}; +void() zombie_walkc7 =[ $cwalk7, zombie_walkc8 ] {self.nextthink = time + 0.13;Zombie_Walk(3); self.frame = 73;Zombie_Think();}; +void() zombie_walkc8 =[ $cwalk8, zombie_walkc9 ] {self.nextthink = time + 0.13;Zombie_Walk(2.9); self.frame = 74;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walkc9 =[ $cwalk9, zombie_walkc10 ] {self.nextthink = time + 0.13;Zombie_Walk(3.3); self.frame = 75;Zombie_Think();}; +void() zombie_walkc10 =[ $cwalk10, zombie_walkc11 ] {self.nextthink = time + 0.13;Zombie_Walk(2.2); self.frame = 76;Zombie_Think();}; +void() zombie_walkc11 =[ $cwalk11, zombie_walkc12 ] {self.nextthink = time + 0.13;Zombie_Walk(3.7); self.frame = 77;Zombie_Think();}; +void() zombie_walkc12 =[ $cwalk12, zombie_walkc13 ] {self.nextthink = time + 0.13;Zombie_Walk(4.2); self.frame = 78;Zombie_Think();zombie_footstep();};//footstep +void() zombie_walkc13 =[ $cwalk13, zombie_walkc14 ] {self.nextthink = time + 0.13;Zombie_Walk(5.3); self.frame = 79;Zombie_Think();}; +void() zombie_walkc14 =[ $cwalk14, zombie_walkc15 ] {self.nextthink = time + 0.13;Zombie_Walk(5.2); self.frame = 80;Zombie_Think();}; +void() zombie_walkc15 =[ $cwalk15, zombie_walkc16 ] {self.nextthink = time + 0.13;Zombie_Walk(2.2); self.frame = 81;Zombie_Think();}; +void() zombie_walkc16 =[ $cwalk16, Zombie_Walk_Setup ] {self.nextthink = time + 0.13;Zombie_Walk(2.4); self.frame = 82;Zombie_Think();zombie_footstep();};//footstep + + +///////////////////////////////////////////////////////////////////// +//116- 129 +$frame jog1 jog2 jog3 jog4 jog5 jog6 jog7 jog8 + +void() zombie_jog1 =[ $jog1, zombie_jog2 ] {self.nextthink = time + 0.08;Zombie_Walk(6);SetZombieHitBox(JOG_BBOX);/*if(freeze_time < time)*/ self.frame = 83;Zombie_Think();};//FOOTSTEP +void() zombie_jog2 =[ $jog2, zombie_jog3 ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 84;Zombie_Think();}; +void() zombie_jog3 =[ $jog3, zombie_jog4 ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 85;Zombie_Think();}; +void() zombie_jog4 =[ $jog4, zombie_jog5 ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 86;Zombie_Think();zombie_footstep();};//footstep +void() zombie_jog5 =[ $jog5, zombie_jog6 ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 87;Zombie_Think();}; +void() zombie_jog6 =[ $jog6, zombie_jog7 ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 88;Zombie_Think();}; +void() zombie_jog7 =[ $jog7, zombie_jog8 ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 89;Zombie_Think();}; +void() zombie_jog8 =[ $jog8, Zombie_Walk_Setup ] {self.nextthink = time + 0.08;Zombie_Walk(6); self.frame = 90;Zombie_Think();zombie_footstep();};//footstep + +///////////////////////////////////////////////////////////////////// +//78-85 +$frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 +//12 unless 14 +void() zombie_run1 =[ $run1, zombie_run2 ] {Zombie_Walk(15);/*if(freeze_time < time)*/ self.frame = 92;SetZombieHitBox(RUN_BBOX);Zombie_Think();zombie_footstep();};//footstep +void() zombie_run2 =[ $run2, zombie_run3 ] {Zombie_Walk(15);self.frame = 93;Zombie_Think();}; +void() zombie_run3 =[ $run3, zombie_run4 ] {Zombie_Walk(15); self.frame = 94;Zombie_Think();zombie_footstep();};//footstep +void() zombie_run4 =[ $run4, zombie_run5 ] {Zombie_Walk(15); self.frame = 95;Zombie_Think();}; +void() zombie_run5 =[ $run5, zombie_run6 ] {Zombie_Walk(15); self.frame = 96;Zombie_Think();}; +void() zombie_run6 =[ $run6, zombie_run7 ] {Zombie_Walk(15); self.frame = 97;Zombie_Think();zombie_footstep();};//footstep +void() zombie_run7 =[ $run7, zombie_run8 ] {Zombie_Walk(15); self.frame = 98;Zombie_Think();}; +void() zombie_run8 =[ $run8, zombie_run9 ] {Zombie_Walk(15); self.frame = 99;Zombie_Think();zombie_footstep();};//footstep +void() zombie_run9 =[ $run9, zombie_run10 ] {Zombie_Walk(15); self.frame = 100;Zombie_Think();}; +void() zombie_run10 =[ $run10, Zombie_Walk_Setup ] {Zombie_Walk(15); self.frame = 101;Zombie_Think();}; + +void() Zombie_Walk_Setup = +{ + if(Remaining_Zombies == 1 && rounds >= 4) + { + self.walktype = 5; + } + + if(self.walktype == 1) + { + zombie_walk1(); + } + + else if(self.walktype == 2) + { + zombie_walkb1(); + } + + else if(self.walktype == 3) + { + zombie_walkc1(); + } + + else if(self.walktype == 4) + { + zombie_jog1(); + } + else if(self.walktype == 5) + { + zombie_run1(); + } +}; +///////////////////////////////////////////////////////////////////// ZOMBIE IDLE 1 +//0-12 +$frame idle1 idle2 idle3 idle4 idle5 idle6 idle7 idle8 idle9 idle10 idle11 idle12 idle13 idle14 +void() zombie_idle =[ $idle1, zombie_idle1 ] {Zombie_Walk(0);self.frame = 0;SetZombieHitBox(IDLE_BBOX);Zombie_Think();}; +void() zombie_idle1 =[ $idle2, zombie_idle2 ] {Zombie_Walk(0);self.frame = 1;Zombie_Think();}; +void() zombie_idle2 =[ $idle3, zombie_idle3 ] {Zombie_Walk(0);self.frame = 2;Zombie_Think();}; +void() zombie_idle3 =[ $idle4, zombie_idle4 ] {Zombie_Walk(0);self.frame = 3;Zombie_Think();}; +void() zombie_idle4 =[ $idle5, zombie_idle5 ] {Zombie_Walk(0);self.frame = 4;Zombie_Think();}; +void() zombie_idle5 =[ $idle6, zombie_idle6 ] {Zombie_Walk(0);self.frame = 5;Zombie_Think();}; +void() zombie_idle6 =[ $idle7, zombie_idle7 ] {Zombie_Walk(0);self.frame = 6;Zombie_Think();}; +void() zombie_idle7 =[ $idle8, zombie_idle8 ] {Zombie_Walk(0);self.frame = 7;Zombie_Think();}; +void() zombie_idle8 =[ $idle9, zombie_idle9 ] {Zombie_Walk(0);self.frame = 8;Zombie_Think();}; +void() zombie_idle9 =[ $idle10, zombie_idle10 ] {Zombie_Walk(0);self.frame = 9;Zombie_Think();}; +void() zombie_idle10 =[ $idle12, zombie_idle11 ] {Zombie_Walk(0);self.frame = 10;Zombie_Think();}; +void() zombie_idle11 =[ $idle13, zombie_idle12 ] {Zombie_Walk(0);self.frame = 11;Zombie_Think();}; +void() zombie_idle12 =[ $idle14, zombie_idle1 ] {Zombie_Walk(0);self.frame = 12;Zombie_Think();}; + +void() zombie_decide = +{ + if(self.outside == TRUE) + { + self.th_idle(); + } + else + { + self.th_walk(); + } +}; +///////////////////////////////////////////////////////// + +void() Window_Damage; +void() Window_Damage_Setup = +{ + local entity old_self; + old_self = self; + self = self.goalentity; + Window_Damage(); + self = old_self; +}; +void() zombie_attack2 = +{ + if(self.outside == TRUE) + { + if(self.goalentity.classname == "window") + { + if(self.enemy.classname != "window") + { + self.enemy = self.goalentity; + } + } + } + + if(self.goalentity.classname == "window") + { + Window_Damage_Setup(); + } + else if (self.enemy.classname == "player") + { + if(vlen(self.enemy.origin - self.origin) < 60) + { + if (self.classname == "ai_dog") + DamageHandler (self.enemy, self, 40, S_ZOMBIE); + else + DamageHandler (self.enemy, self, 50, S_ZOMBIE); + } + } + else return; +}; + +//////////////////////////////WINDOW HOPPING +//113-122 +void(float pushaway) moveforwardalittle = +{ + makevectors(self.angles); + local float amount; + amount = 4.35;//was 8.7, but running twice as much so... + if(self.crawling == TRUE) + amount = 2.33; + + self.origin = self.origin + (v_forward * amount); + setorigin(self, self.origin); + self.zoom = 1; + + if(pushaway == 1) + { + local entity ent; + ent = findradius(self.origin,50); + while(ent) + { + if(ent.classname == "player" && !ent.downed) + { + ent.velocity = ent.velocity + (v_forward * 250); + } + ent = ent.chain; + } + } +}; + +$frame hop1 hop2 hop3 hop4 hop5 hop6 hop7 hop8 hop9 hop10 +void() zombie_hop1 =[ $hop1, zombie_hop1_5 ] {self.movetype = self.head.movetype = MOVETYPE_STEP;self.frame = self.head.frame = 113;self.nextthink = time + 0.05;moveforwardalittle(1); self.state = 1;Zombie_Think();}; +void() zombie_hop1_5 =[ $hop1, zombie_hop2 ] {self.frame = self.head.frame = 113;self.nextthink = time + 0.05;moveforwardalittle(0); }; +void() zombie_hop2 =[ $hop2, zombie_hop2_5 ] {self.frame = self.head.frame = 114;self.nextthink = time + 0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop2_5 =[ $hop2, zombie_hop3 ] {self.frame = self.head.frame = 114;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop3 =[ $hop3, zombie_hop3_5 ] {self.frame = self.head.frame = 115;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop3_5 =[ $hop3, zombie_hop4 ] {self.frame = self.head.frame = 115;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop4 =[ $hop4, zombie_hop4_5 ] {self.frame = self.head.frame = 116;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop4_5 =[ $hop4, zombie_hop5 ] {self.frame = self.head.frame = 116;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop5 =[ $hop5, zombie_hop5_5 ] {self.frame = self.head.frame = 117;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop5_5 =[ $hop5, zombie_hop6 ] {self.frame = self.head.frame = 117;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop6 =[ $hop6, zombie_hop6_5 ] {self.frame = self.head.frame = 118;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop6_5 =[ $hop6, zombie_hop7 ] {self.frame = self.head.frame = 118;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop7 =[ $hop7, zombie_hop7_5 ] {self.frame = self.head.frame = 119;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop7_5 =[ $hop7, zombie_hop8 ] {self.frame = self.head.frame = 119;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop8 =[ $hop8, zombie_hop8_5 ] {self.frame = self.head.frame = 120;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();zombie_footstep();}; +void() zombie_hop8_5 =[ $hop8, zombie_hop9 ] {self.frame = self.head.frame = 120;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop9 =[ $hop9, zombie_hop9_5 ] {self.frame = self.head.frame = 121;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop9_5 =[ $hop9, zombie_hop10 ] {self.frame = self.head.frame = 121;self.nextthink = time+0.05;moveforwardalittle(0);}; +void() zombie_hop10 =[ $hop10, zombie_hop10_5 ] {self.frame = self.head.frame = 122;self.nextthink = time+0.05;moveforwardalittle(1);Zombie_Think();}; +void() zombie_hop10_5=[ $hop10, SUB_Null ] {self.frame = self.head.frame = 122; + self.oldorigin = self.origin = self.goalentity.hop_spot; + setorigin(self, self.origin); + self.state = 0; + self.hop_step = 6; + if(self.crawling == 2) + { + makeCrawler(self); + return; + } + self.movetype = MOVETYPE_WALK; + self.th_walk(); + }; + +//ripboard1 +//181-191 +$frame zrip1 zrip2 zrip3 zrip4 zrip5 zrip6 zrip7 zrip8 zrip9 zrip10 zrip11 +void() zombie_ripboardA1 =[ $zrip1, zombie_ripboardA2 ] {self.frame = 181;SetZombieHitBox(IDLE_BBOX);Zombie_Think();}; +void() zombie_ripboardA2 =[ $zrip2, zombie_ripboardA3 ] {self.frame = 182;Zombie_Think();}; +void() zombie_ripboardA3 =[ $zrip3, zombie_ripboardA4 ] {self.frame = 183;Zombie_Think();}; +void() zombie_ripboardA4 =[ $zrip4, zombie_ripboardA5 ] {self.frame = 184;Zombie_Think();}; +void() zombie_ripboardA5 =[ $zrip5, zombie_ripboardA6 ] {self.frame = 185;Zombie_Think();}; +void() zombie_ripboardA6 =[ $zrip6, zombie_ripboardA7 ] {self.frame = 186;Zombie_Think();}; +void() zombie_ripboardA7 =[ $zrip7, zombie_ripboardA8 ] {self.frame = 187;zombie_attack2();Zombie_Think();}; +void() zombie_ripboardA8 =[ $zrip8, zombie_ripboardA9 ] {self.frame = 188;Zombie_Think();}; +void() zombie_ripboardA9 =[ $zrip9, zombie_ripboardA10 ] {self.frame = 189;Zombie_Think();}; +void() zombie_ripboardA10 =[ $zrip10, zombie_ripboardA11 ] {self.frame = 190;Zombie_Think();}; +void() zombie_ripboardA11 =[ $zrip11, zombie_decide ] {self.frame = 191;Zombie_Think();}; +///////////////////////////////////////////////////////// +//ripboard2 +//192-201 +$frame zripB1 zripB2 zripB3 zripB4 zripB5 zripB6 zripB7 zripB8 zripB9 zripB10 zripB11 +void() zombie_ripboardB1 =[ $zripB1, zombie_ripboardB2 ] {self.frame = 192;SetZombieHitBox(IDLE_BBOX);Zombie_Think();}; +void() zombie_ripboardB2 =[ $zripB2, zombie_ripboardB3 ] {self.frame = 193;Zombie_Think();}; +void() zombie_ripboardB3 =[ $zripB3, zombie_ripboardB4 ] {self.frame = 194;Zombie_Think();}; +void() zombie_ripboardB4 =[ $zripB4, zombie_ripboardB5 ] {self.frame = 195;Zombie_Think();}; +void() zombie_ripboardB5 =[ $zripB5, zombie_ripboardB6 ] {self.frame = 196;Zombie_Think();}; +void() zombie_ripboardB6 =[ $zripB6, zombie_ripboardB7 ] {self.frame = 197;zombie_attack2();Zombie_Think();}; +void() zombie_ripboardB7 =[ $zripB7, zombie_ripboardB8 ] {self.frame = 198;Zombie_Think();}; +void() zombie_ripboardB8 =[ $zripB8, zombie_ripboardB9 ] {self.frame = 199;Zombie_Think();}; +void() zombie_ripboardB9 =[ $zripB9, zombie_ripboardB10 ] {self.frame = 200;Zombie_Think();}; +void() zombie_ripboardB10 =[ $zripB10, zombie_decide ] {self.frame = 201;Zombie_Think();}; +///////////////////////////////////////////////////////// +//attack through window +//180-181 +//181-185 +//186-188 +void() zombie_attack_through_window = +{ + play_sound_z(1); + local entity who; + who = findradius(self.origin,70); + while(who) + { + if(who.classname == "player") + { + DamageHandler (who, self, 50, S_ZOMBIE); + } + who = who.chain; + } +}; +//202-210 +$frame zatkw1 zatkw2 zatkw3 zatkw4 zatkw5 zatkw6 zatkw7 zatkw8 zatkw9 +void() zombie_attack_through_w1 =[ $zatkw1, zombie_attack_through_w2 ] {self.frame = 202;SetZombieHitBox(IDLE_BBOX);Zombie_Think();};//START + +void() zombie_attack_through_w2 =[ $zatkw2, zombie_attack_through_w3 ] {self.frame = 203;zombie_attack_through_window();Zombie_Think();};//SWINGING +void() zombie_attack_through_w3 =[ $zatkw3, zombie_attack_through_w4 ] {self.frame = 204;Zombie_Think();};//SWINGING +void() zombie_attack_through_w4 =[ $zatkw4, zombie_attack_through_w5 ] {self.frame = 205;Zombie_Think();};//SWINGING +void() zombie_attack_through_w5 =[ $zatkw5, zombie_attack_through_w6 ] {self.frame = 206;Zombie_Think();};//SWINGING +void() zombie_attack_through_w6 =[ $zatkw6, zombie_attack_through_w7 ] {self.frame = 207;Zombie_Think();};//SWINGING + +void() zombie_attack_through_w7 =[ $zatkw2, zombie_attack_through_w8 ] {self.frame = 203;Zombie_Think();};//SWINGING +void() zombie_attack_through_w8 =[ $zatkw3, zombie_attack_through_w9 ] {self.frame = 204;Zombie_Think();};//SWINGING +void() zombie_attack_through_w9 =[ $zatkw4, zombie_attack_through_w10 ] {self.frame = 205;Zombie_Think();};//SWINGING +void() zombie_attack_through_w10 =[ $zatkw5, zombie_attack_through_w11 ] {self.frame = 206;Zombie_Think();};//SWINGING +void() zombie_attack_through_w11 =[ $zatkw6, zombie_attack_through_w12 ] {self.frame = 207;Zombie_Think();};//SWINGING + +void() zombie_attack_through_w12 =[ $zatkw2, zombie_attack_through_w13 ] {self.frame = 203;Zombie_Think();};//SWINGING +void() zombie_attack_through_w13 =[ $zatkw3, zombie_attack_through_w14 ] {self.frame = 204;Zombie_Think();};//SWINGING +void() zombie_attack_through_w14 =[ $zatkw4, zombie_attack_through_w15 ] {self.frame = 205;Zombie_Think();};//SWINGING +void() zombie_attack_through_w15 =[ $zatkw5, zombie_attack_through_w16 ] {self.frame = 206;zombie_attack_through_window();Zombie_Think();};//SWINGING +void() zombie_attack_through_w16 =[ $zatkw6, zombie_attack_through_w17 ] {self.frame = 207;Zombie_Think();};//SWINGING + +void() zombie_attack_through_w17 =[ $zatkw7, zombie_attack_through_w18 ] {self.frame = 208;Zombie_Think();};//ENDING +void() zombie_attack_through_w18 =[ $zatkw8, zombie_attack_through_w19 ] {self.frame = 209;Zombie_Think();};//ENDING +void() zombie_attack_through_w19 =[ $zatkw9, zombie_decide ] {self.frame = 210;Zombie_Think();};//ENDING + + +//////////////////////////////////////////////////////////////////////ZOMBIE DELAY ATTACKS +//zda = zombie delay attack +void() zombie_attackA1; +void() zombie_attackB1; + +void(float which) zombie_attack_choose = +{ + + self.angles_y = vectoyaw(self.enemy.origin - self.origin); + + if(which != 1) + { + if(random() > 0.2) + { + return; + } + } + + play_sound_z(1); + //Don't attack with an arm you don't have, silly + if(!self.larm.deadflag) + { + zombie_attackA1(); + return; + } + if(!self.rarm.deadflag) + { + zombie_attackB1(); + return; + } + + + if(random() < 0.5) + zombie_attackA1(); + else + zombie_attackB1(); +}; + +$frame zda1 zda2 zda3 zda4 zda5 zda6 zda7 zda8 zda9 zda10 zda11 zda12 zda13 zda14 +void() zombie_zda =[ $zda1, zombie_zda1 ] {self.frame = 0;zombie_attack_choose(0);SetZombieHitBox(IDLE_BBOX);Zombie_Think();}; +void() zombie_zda1 =[ $zda2, zombie_zda2 ] {self.frame = 1;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda2 =[ $zda3, zombie_zda3 ] {self.frame = 2;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda3 =[ $zda4, zombie_zda4 ] {self.frame = 3;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda4 =[ $zda5, zombie_zda5 ] {self.frame = 4;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda5 =[ $zda6, zombie_zda6 ] {self.frame = 5;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda6 =[ $zda7, zombie_zda7 ] {self.frame = 6;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda7 =[ $zda8, zombie_zda8 ] {self.frame = 7;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda8 =[ $zda9, zombie_zda9 ] {self.frame = 8;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda9 =[ $zda10, zombie_zda10 ] {self.frame = 9;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda10 =[ $zda12, zombie_zda11 ] {self.frame = 10;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda11 =[ $zda13, zombie_zda12 ] {self.frame = 11;zombie_attack_choose(0);Zombie_Think();}; +void() zombie_zda12 =[ $zda14, zombie_zda1 ] {self.frame = 12;zombie_attack_choose(1);Zombie_Think();}; +///////////////////////////////////////////////////////////////////// ZOMBIE ATTACK 1// Swipe with right arm +//86 - 90 +void() zombie_attack2; + +$frame attack1 attack2 attack3 attack4 attack5 + +void() zombie_attackA1 =[ $attack1, zombie_attackA2 ] {self.frame = 102;SetZombieHitBox(BASE_BBOX);Zombie_Think();self.solid = SOLID_BBOX;}; +void() zombie_attackA2 =[ $attack2, zombie_attackA3 ] {self.frame = 103;Zombie_Think();}; +void() zombie_attackA3 =[ $attack3, zombie_attackA4 ] {self.frame = 104;Zombie_Think();}; +void() zombie_attackA4 =[ $attack4, zombie_attackA5 ] {zombie_attack2();self.frame = 105;Zombie_Think();}; +void() zombie_attackA5 =[ $attack5, zombie_decide ] {self.frame = 106;Zombie_Think();self.solid = SOLID_CORPSE;}; + +///////////////////////////////////////////////////////////////////////// ZOMBIE ATTACK 2// Swipe with left arm +//91-96 +$frame attackb1 attackb2 attackb3 attackb4 attackb5 attackb6 + +void() zombie_attackB1 =[ $attackb1, zombie_attackB2 ] {self.frame = 107;SetZombieHitBox(BASE_BBOX);Zombie_Think();self.solid = SOLID_BBOX;}; +void() zombie_attackB2 =[ $attackb2, zombie_attackB3 ] {self.frame = 108;Zombie_Think();}; +void() zombie_attackB3 =[ $attackb3, zombie_attackB4 ] {self.frame = 109;Zombie_Think();}; +void() zombie_attackB4 =[ $attackb4, zombie_attackB5 ] {zombie_attack2(); self.frame = 110;Zombie_Think();}; +void() zombie_attackB5 =[ $attackb5, zombie_attackB6 ] {self.frame = 111;Zombie_Think();}; +void() zombie_attackB6 =[ $attackb6, zombie_decide ] {self.frame = 112;Zombie_Think();self.solid = SOLID_CORPSE;}; + + +void() zombie_attack = +{ + self.velocity = '0 0 0'; + + if (self.classname == "wunder") + return; + + if(self.outside == TRUE) + { + self.angles_y = self.goalentity.angles_y; + } + else + { + self.angles_y = vectoyaw(self.enemy.origin - self.origin); + } + + + if(self.crawling == TRUE) + { + if(self.outside == TRUE) + crawler_rip_board1(); + else + crawler_da1();//crawler delay attack + } + else + { + //zombie_attack_through_w1(); + + if(self.outside == TRUE) + { + if(random() < 0.30) + { + local entity who; + local float found; + found = 0; + who = findradius(self.origin,70); + while(who && found == 0 ) + { + if(who.classname == "player") + { + found = 1; + } + else + who = who.chain; + } + if(found == 1) + { + zombie_attack_through_w1(); + return; + } + } + + if(random() < 0.5) + zombie_ripboardA1(); + else + zombie_ripboardB1(); + } + else + { + zombie_zda();//Zombie Delay Attack + } + } + +}; + +//==========================Falling Code========================== +void CheckLand() = +{ + if ((self.flags & FL_ONGROUND)) + { + if (self.fall == TRUE) + { + self.fall = FALSE; + self.th_land(); + } + } +}; + +//Falling +//149-152 +$frame fall1 fall2 fall3 fall4 + +void() zombie_fall1 =[ $fall1, zombie_fall2 ] {self.frame = 149;SetZombieHitBox(BASE_BBOX);self.fall = TRUE;CheckLand();Zombie_Think();}; +void() zombie_fall2 =[ $fall2, zombie_fall3 ] {self.frame = 150;CheckLand();Zombie_Think();}; +void() zombie_fall3 =[ $fall3, zombie_fall4 ] {self.frame = 151;CheckLand();Zombie_Think();}; +void() zombie_fall4 =[ $fall4, zombie_fall3 ] {self.frame = 152;CheckLand();Zombie_Think();}; + +//Landing +//153-159 +$frame land1 land2 land3 land4 land5 land6 land7 +void() zombie_land1 =[ $land1, zombie_land2 ] {self.frame = 153;SetZombieHitBox(BASE_BBOX);Zombie_Think();zombie_footstep();zombie_footstep();}; +void() zombie_land2 =[ $land2, zombie_land3 ] {self.frame = 154;Zombie_Think();}; +void() zombie_land3 =[ $land3, zombie_land4 ] {self.frame = 155;Zombie_Think();}; +void() zombie_land4 =[ $land4, zombie_land5 ] {self.frame = 156;Zombie_Think();}; +void() zombie_land5 =[ $land5, zombie_land6 ] {self.frame = 157;Zombie_Think();}; +void() zombie_land6 =[ $land6, zombie_land7 ] {self.frame = 158;Zombie_Think();}; +void() zombie_land7 =[ $land7, Zombie_Walk_Setup ] {self.frame = 159;Zombie_Think();}; + +void() removeZombie = +{ + self.classname = "freeZombieEntity"; + self.aistatus = "0"; + setmodel(self,""); + + setmodel(self.larm,""); + setmodel(self.rarm,""); + setmodel(self.head,""); + + #ifdef PC + remove(self.larm); + remove(self.rarm); + remove(self.head); + #endif + + #ifndef PC + updateLimb (self, 0, world); + updateLimb (self, 1, world); + updateLimb (self, 2, world); + #endif + + setorigin(self,'0 0 0'); + setorigin (self.goaldummy, '0 0 0'); + + self.think = SUB_Null; + + self.goalentity = world; + self.enemy = world; + self.solid = 0; + self.movetype = 0; + + self.outside = TRUE; + self.hop_step = 0; + self.calc_time = 0; + self.tries = 0; + self.takedamage = DAMAGE_NO; + self.flags = 0; + self.crawling = 0; + self.bleedingtime = 0; + self.iszomb = 0; + self.owner = world; + + self.spawnflags = 0; + + + self.yaw_speed = 0; + + self.health = 0; + self.th_die = SUB_Null; + self.th_walk = SUB_Null; + self.th_melee = SUB_Null; + self.th_idle = SUB_Null; + self.th_windowhop = SUB_Null; + + self.th_fall = SUB_Null; + self.th_falling = SUB_Null; + self.th_land = SUB_Null; + + self.th_jump = SUB_Null; + self.th_grabledge = SUB_Null; + self.th_diewunder = SUB_Null; + //Disabling collision bboxes + SetZombieHitBox(EMPTY_BBOX); +}; + +///////////////////////////////////////////////////////////////////// ZOMBIE DEATH 1 +//123-133 +$frame death1 death2 death3 death4 death5 death6 death7 death8 death9 death10 death11 +void() zombie_deathA1 =[ $death1, zombie_death2 ] {self.frame = 123;}; +void() zombie_death2 =[ $death2, zombie_death3 ] {self.frame = 124;}; +void() zombie_death3 =[ $death3, zombie_death4 ] {self.frame = 125;}; +void() zombie_death4 =[ $death4, zombie_death5 ] {self.frame = 126;}; +void() zombie_death5 =[ $death5, zombie_death6 ] {self.frame = 127;}; +void() zombie_death6 =[ $death6, zombie_death7 ] {self.frame = 128;}; +void() zombie_death7 =[ $death7, zombie_death8 ] {self.frame = 129;}; +void() zombie_death8 =[ $death8, zombie_death9 ] {self.frame = 130;}; +void() zombie_death9 =[ $death9, zombie_death10 ] {self.frame = 131;}; +void() zombie_death10 =[ $death10, zombie_death11 ] {self.frame = 132;zombie_footstep();}; +void() zombie_death11 =[ $death11, SUB_Null ] {self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie; self.frame = 133;}; + +//////////////////////////////////////////////////////////////////////////////////// ZOMBIE DEATH 2 +//134-138 +$frame deathB1 deathB2 deathB3 deathB4 deathB5 + +void() zombie_deathB1 =[ $deathB1, zombie_deathB2 ] {self.frame = 134;}; +void() zombie_deathB2 =[ $deathB2, zombie_deathB3 ] {self.frame = 135;}; +void() zombie_deathB3 =[ $deathB3, zombie_deathB4 ] {self.frame = 136;}; +void() zombie_deathB4 =[ $deathB4, zombie_deathB5 ] {self.frame = 137;zombie_footstep();}; +void() zombie_deathB5 =[ $deathB5, SUB_Null ] {self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie; self.frame = 138;}; + + +//////////////////////////////////////////////////////////////////////////////////// ZOMBIE DEATH 3 +//139-148 + +$frame deathC1 deathC2 deathC3 deathC4 deathC5 deathC6 deathC7 deathC8 deathC9 deathC10 + +void() zombie_deathC1 =[ $deathC1, zombie_deathC2 ] {self.frame = 139;}; +void() zombie_deathC2 =[ $deathC2, zombie_deathC3 ] {self.frame = 140;}; +void() zombie_deathC3 =[ $deathC3, zombie_deathC4 ] {self.frame = 141;}; +void() zombie_deathC4 =[ $deathC4, zombie_deathC5 ] {self.frame = 142;}; +void() zombie_deathC5 =[ $deathC5, zombie_deathC6 ] {self.frame = 143;}; +void() zombie_deathC6 =[ $deathC6, zombie_deathC7 ] {self.frame = 144;}; +void() zombie_deathC7 =[ $deathC7, zombie_deathC8 ] {self.frame = 145;}; +void() zombie_deathC8 =[ $deathC8, zombie_deathC9 ] {self.frame = 146;zombie_footstep();}; +void() zombie_deathC9 =[ $deathC9, zombie_deathC10 ] {self.frame = 147;}; +void() zombie_deathC10 =[ $deathC10, SUB_Null ] {self.iszomb = 0; self.nextthink = time + 3; self.think = removeZombie; self.frame = 148;}; + +void() Zombie_Death = +{ + self.aistatus = "0"; + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NONE; + self.takedamage = DAMAGE_NO; + + self.head.solid = SOLID_NOT; + self.head.movetype = MOVETYPE_NONE; + self.head.takedamage = DAMAGE_NO; + self.larm.solid = SOLID_NOT; + self.larm.movetype = MOVETYPE_NONE; + self.larm.takedamage = DAMAGE_NO; + self.rarm.solid = SOLID_NOT; + self.rarm.movetype = MOVETYPE_NONE; + self.rarm.takedamage = DAMAGE_NO; + sound(self,CHAN_VOICE,"sounds/null.wav",1,ATTN_NORM); + + self.usedent = world; + + if(self.goalentity.classname == "window") + { + if(self.goalentity.box1owner == self) + self.goalentity.box1owner = world; + if(self.goalentity.box2owner == self) + self.goalentity.box2owner = world; + if(self.goalentity.box3owner == self) + self.goalentity.box3owner = world; + if(self.goalentity.usedent == self) + self.goalentity.usedent = world; + } + + + self.health = 0; + Remaining_Zombies = Remaining_Zombies - 1; + + play_sound_z(3); + //Gotta' make sure we set it back down instead of glitching it up, yo' + if(self.s_time > 0 && sounds_playing > 0) + { + sounds_playing --; + self.s_time = 0; + } + + if(self.outside == FALSE) + { + if (totalpowerups < 4) + { + if (total_powerup_points >= powerup_activate) + { + Spawn_Powerup(self.origin, 0); + total_powerup_points = total_powerup_points - powerup_activate; + } + else if (random () <= 0.02) + Spawn_Powerup(self.origin, 0); + } + } + + if(self.crawling == TRUE) + { + crawler_die1(); + } + else + { + if(random() < 0.3) + { + zombie_deathA1(); + } + else if(random() < 0.6) + { + zombie_deathB1(); + } + else + { + zombie_deathC1(); + } + } +}; + +/////////////////////ZOMBIE WAFFE STUFF///////////////////// + +void() Zombie_Lightning_Think = +{ + self.frame++; + + if (self.frame >= 3) + self.frame = 0; + + // suicide timer! + if(self.ltime < time) { + remove(self); + } + + self.nextthink = time + 0.05; +} + +void() Zombie_Tesla_Light = +{ + // lightning sprite + local entity lightning = spawn(); + setmodel(lightning, "models/sprites/lightning.spr"); + setorigin(lightning, self.origin + '0 0 13'); + lightning.think = Zombie_Lightning_Think; + lightning.nextthink = time + 0.05; + lightning.ltime = time + 1; +} + +void() Zombie_Find_Tesla_Target = +{ + local entity zombs = findradius(self.origin, 200); + local entity tempe; + local vector org; + local vector targetorg; + local float found_target; + + if (self.teslacount >= 10) + return; + + found_target = false; + while(zombs != world || found_target) { + if (zombs == world) { + found_target = true; + return; + } + // limb stuff + if (zombs.classname == "ai_zombie_head" || zombs.classname == "ai_zombie_larm" + || zombs.classname == "ai_zombie_rarm") { + zombs = zombs.owner; + } + + if (zombs.classname == "ai_zombie" || zombs.classname == "ai_dog") { + found_target = true; + + zombs.owner = self.owner; + + // remove libs + if (zombs.head) + setmodel(zombs.head, ""); + if (zombs.larm) + setmodel(zombs.larm, ""); + if (zombs.rarm) + setmodel(zombs.rarm, ""); + + // add 50pts + addmoney(self.owner, 50, true); + + // kill + self.owner.kills += 1; + + // add to tesla count + zombs.teslacount = self.teslacount + 1; + + // classname change + zombs.classname = "wunder"; + + org = self.origin + '0 0 13'; + targetorg = zombs.origin + '0 0 13'; + + // shoot lightning + #ifdef PC + te_lightning2(self, org, targetorg); + #endif + #ifdef PSP + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_LIGHTNING2); + WriteEntity (MSG_BROADCAST, self); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + WriteCoord (MSG_BROADCAST, targetorg_x); + WriteCoord (MSG_BROADCAST, targetorg_y); + WriteCoord (MSG_BROADCAST, targetorg_z); + #endif + + // anim play + tempe = self; + self = zombs; + self.th_diewunder(); + self = tempe; + } + zombs = zombs.chain; + } +} + +// Wunder'd +// 149-152 (FIXME - animate wunder'd zombies) +$frame wunder1 wunder2 wunder3 wunder4 wunder5 wunder6 wunder7 wunder8 wunder9 wunder10 + +void() zombie_wunder1 =[ $wunder1, zombie_wunder2 ] {play_sound_z(4);Zombie_Tesla_Light();Zombie_Walk(0);self.frame = 149;self.solid = SOLID_NOT;}; +void() zombie_wunder2 =[ $wunder2, zombie_wunder3 ] {Zombie_Walk(0);self.frame = 150;}; +void() zombie_wunder3 =[ $wunder3, zombie_wunder4 ] {Zombie_Walk(0);Zombie_Find_Tesla_Target();self.frame = 151;}; +void() zombie_wunder4 =[ $wunder4, zombie_wunder5 ] {Zombie_Walk(0);self.frame = 152;}; +void() zombie_wunder5 =[ $wunder5, zombie_wunder6 ] {Zombie_Walk(0);self.frame = 151;}; +void() zombie_wunder6 =[ $wunder6, zombie_wunder7 ] {Zombie_Walk(0);self.frame = 152;}; +void() zombie_wunder7 =[ $wunder7, zombie_wunder8 ] {Zombie_Walk(0);self.frame = 151;}; +void() zombie_wunder8 =[ $wunder8, zombie_wunder9 ] {play_sound_z(3);Zombie_Walk(0);self.frame = 152;}; +void() zombie_wunder9 =[ $wunder9, zombie_wunder10 ] {Zombie_Walk(0);self.frame = 151;}; +void() zombie_wunder10 =[ $wunder10, SUB_Null ] {self.health = 0; self.th_die();Zombie_Walk(0);self.frame = 152;}; + +/////////////////////ZOMBIE SPAWNING///////////////////// +void() zombieRise = +{ + //better not move wile rising, would happen sometimes + self.velocity_x = 0; + self.velocity_y = 0; + + if(self.frame < 13)//begin + { + self.frame = 12;//since we're going to add one anyways + self.state = 1;//don't make crawler, yo! + } + if(self.frame == 36) + { + self.state = 0; + if(self.crawling == 2) + { + makeCrawler(self); + return; + } + self.th_walk(); + return; + } + self.frame ++; + self.think = zombieRise; + self.nextthink = time + 0.1; +}; +void() spawn_zombie = +{ + setsize(self, '0 0 0', '0 0 0'); + if (self.spawnflags & INACTIVE) + { + if (cvar("developer")) + setmodel(self, "models/player.mdl"); + self.classname = "spawn_zombie_in"; + } + else + { + if (cvar("developer")) + setmodel(self, "models/ai/zfull.mdl"); + self.classname = "spawn_zombie"; + } + + //self.think = zombie_spawn_think; + //self.nextthink = time + 0.5; + + self.solid = SOLID_NOT; +}; + +void(entity where) spawn_a_zombieB; +void() set_z_health = +{ + if(rounds <= 10) + { + z_health = 50 + (rounds * 100); + } + else + { + local float i; + i = rounds - 10; + z_health = 1050; + while(i > 0) + { + z_health *= 1.1; + i--; + } + } +}; + +entity(entity me) getFreeZombieEnt = +{ + local entity who; + who = find(world,classname,"freeZombieEntity"); + + return who; +}; +float() spawn_a_zombieA = +{ + //dprint("Spawn_a_zombieA \n"); + local float pcount; + local entity thing, szombie; + local float FAIL; + + //warn + pcount = 0; + + FAIL = false; + szombie = getFreeZombieEnt(self); + if(szombie == world || zombie_spawn_timer > time) + { + //bprint(PRINT_HIGH, "STOPPED AT A\n"); + //dprint("No avaible zombie entity\n"); + return 0; + } + szombie.onfire = false; + lastspawn = find(lastspawn, classname, "spawn_zombie"); + while (random() < 0.4) + { + lastspawn = find(lastspawn, classname, "spawn_zombie"); + } + + while(lastspawn) + { + thing = findradius(lastspawn.origin, 60); + while (thing) + { + pcount = 0; + if (thing.classname == "ai_zombie") + { + pcount = 1; + break; + } + thing = thing.chain; + } + if (!pcount && random() < 0.6) + { + spawn_a_zombieB(lastspawn); + spawn_delay = time + delay_at_round; + return true; + } + lastspawn = find(lastspawn, classname, "spawn_zombie"); + } + return 0; //no free locations fround +}; + +void(float i) spawnSingleZombEnt = +{ + local entity zSpawn; + zSpawn = spawn(); + zSpawn.classname = "freeZombieEntity"; + zSpawn.aistatus = "0"; + zSpawn.goaldummy = spawn(); + zSpawn.goaldummy.classname = "waypoint_target"; + + zSpawn.head = spawn(); + zSpawn.head.classname = "in_ai_zombie_head"; + zSpawn.head.owner = zSpawn; + zSpawn.head.solid = SOLID_NOT; + zSpawn.head.movetype = MOVETYPE_STEP; + zSpawn.head.takedamage = DAMAGE_NO; + #ifndef PC + updateLimb (zSpawn, 0, zSpawn.head); + #endif + + zSpawn.larm = spawn(); + zSpawn.larm.classname = "in_ai_zombie_larm"; + zSpawn.larm.owner = zSpawn; + zSpawn.larm.solid = SOLID_NOT; + zSpawn.larm.movetype = MOVETYPE_STEP; + zSpawn.larm.takedamage = DAMAGE_NO; + #ifndef PC + updateLimb (zSpawn, 1, zSpawn.larm); + #endif + + zSpawn.rarm = spawn(); + zSpawn.rarm.classname = "in_ai_zombie_rarm"; + zSpawn.rarm.owner = zSpawn; + zSpawn.rarm.solid = SOLID_NOT; + zSpawn.rarm.movetype = MOVETYPE_STEP; + zSpawn.rarm.takedamage = DAMAGE_NO; + #ifndef PC + updateLimb (zSpawn, 2, zSpawn.rarm); + #endif +}; +void(entity szombie) SetUpHitBoxes = +{ + szombie.head.solid = SOLID_CORPSE; + szombie.head.movetype = MOVETYPE_STEP; + szombie.head.classname = "ai_zombie_head"; + szombie.head.takedamage = DAMAGE_YES; + + szombie.larm.solid = SOLID_CORPSE; + szombie.larm.movetype = MOVETYPE_STEP; + szombie.larm.classname = "ai_zombie_larm"; + szombie.larm.takedamage = DAMAGE_YES; + + szombie.rarm.solid = SOLID_CORPSE; + szombie.rarm.movetype = MOVETYPE_STEP; + szombie.rarm.classname = "ai_zombie_rarm"; + szombie.rarm.takedamage = DAMAGE_YES; + + local entity oldself = self; + self = szombie; + SetZombieHitBox(BASE_BBOX); + self = oldself; +}; + +void(entity szombie) SetZombieWalk = +{ + local float walk1Percent,walk2Percent,walk3Percent,jogPercent; + + walk1Percent = walk2Percent = walk3Percent = ((rounds < 3) ? 1 : 0);//1/x denotes walk types + walk1Percent += ((rounds >= 3 && rounds <= 7) ? (((-0.5 * rounds)/3) + 1.2) : 0); + walk2Percent = walk3Percent = walk1Percent; + + jogPercent = walk1Percent + ((rounds >= 3 && rounds < 6) ? (((0.5*rounds)/3) - 0.2) : 0); + jogPercent += ((rounds >= 6 && rounds <= 10) ? (0.6 + ((rounds - 7) / (-5))) : 0); + + //Dividing up the walks + walk1Percent *= 0.33; + walk2Percent *= 0.67; + //walk3Percent *= 1; just disabled becuase it doesn't do anything, but I wrote it to show how we're handling it + + local float whichwalk; + whichwalk = random(); + + if(whichwalk <= walk1Percent) + { + szombie.walktype = 1; + } + else if(whichwalk <= walk2Percent) + { + szombie.walktype = 2; + } + else if(whichwalk <= walk3Percent) + { + szombie.walktype =3; + } + else if(whichwalk <= jogPercent) + { + szombie.walktype = 4; + } + else szombie.walktype = 5; +}; + +float() getMaxZombies = +{ + if (dogWave == true) { + return 2; + } else { + return MAX_ZOMB; + } +} + +void() spawnAllZombEnts = +{ + local float i; + i = 0; + + while(i < getMaxZombies())//IMPORTANT VALUE, max zombies allowed in map + { + spawnSingleZombEnt(i); + i++; + } +}; + +void(entity where) spawn_a_zombieB = +{ + local entity szombie;//USED FOR WHAT TO SPAWN + //szombie = spawn(); + + szombie = getFreeZombieEnt(self); + if(szombie == world || zombie_spawn_timer > time) + { + //bprint(PRINT_HIGH, "STOPPED AT B\n"); + //dprint("Out of zombie entities \n"); + return; + } + + #ifdef PC + szombie.head = spawn(); + szombie.rarm = spawn(); + szombie.larm = spawn(); + + szombie.head.owner = szombie.rarm.owner = szombie.larm.owner = szombie; + #endif + + szombie.origin = where.origin; + + szombie.frame = 0; + + szombie.target = where.target; + //blubsvel + //szombie.movetype = MOVETYPE_STEP; + + szombie.head.deadflag = 1; + szombie.larm.deadflag = 1; + szombie.rarm.deadflag = 1; + + setmodel(szombie.head, "models/ai/zhead.mdl"); + setmodel(szombie.rarm, "models/ai/zrarm.mdl"); + setmodel(szombie.larm, "models/ai/zlarm.mdl"); + #ifndef PC + updateLimb (szombie, 0, szombie.head); + updateLimb (szombie, 1, szombie.larm); + updateLimb (szombie, 2, szombie.rarm); + #endif + + + szombie.solid = szombie.head.solid = SOLID_CORPSE; + szombie.movetype = MOVETYPE_WALK; + setmodel(szombie, "models/ai/zbod.mdl"); + szombie.hop_step = 0; + szombie.gravity = 1.0; + + szombie.mins = '-8 -8 -32';//-16 16 -32 + szombie.maxs = '8 8 30';//16 16 40 + setsize (szombie, szombie.mins, szombie.maxs);//-16,-16,-32,16,16,40 + //Setting Uniform skin================= + /*if(random() > 0.5) + { + szombie.skin = 1; + szombie.larm.skin = 1; + szombie.rarm.skin = 1; + } + //Setting Skin + if(random() < 0.5) + szombie.head.skin = 0; + else + szombie.head.skin = 1;*/ + + local float tex; + tex = random(); + + szombie.skin = 0; + + + //==================================== + + if(pointcontents(szombie.origin - '0 0 36') == -2) + { + while(pointcontents (szombie.origin - '0 0 36') == -2) + { + szombie.origin = szombie.origin + '0 0 1'; + setorigin(szombie,szombie.origin ); + } + } + + szombie.classname = "ai_zombie"; + szombie.aistatus = "1"; + setorigin (szombie.goaldummy, '0 0 0'); + szombie.origin_z = szombie.origin_z + 1; + szombie.outside = true; + szombie.takedamage = DAMAGE_YES; + setorigin(szombie, szombie.origin); + szombie.flags = szombie.flags | FL_PARTIALGROUND | FL_MONSTER; + + szombie.spawnflags = where.spawnflags; + //////////FIX FOR OUTSIDE + if(szombie.spawnflags & 4) + { + szombie.outside = FALSE; + } + + ///////// + + szombie.ideal_yaw = szombie.angles_y; + szombie.yaw_speed = 20; + szombie.iszomb = 1; + + // We used to set different limb health, too, but we now handle any limb damage differences in weapon stats. + szombie.health = szombie.head.health = szombie.larm.health = szombie.rarm.health = z_health; + + szombie.th_die = Zombie_Death; + szombie.th_walk = Zombie_Walk_Setup; + szombie.th_melee = zombie_attack; + szombie.th_idle = zombie_idle; + szombie.th_windowhop = zombie_hop1; + + szombie.th_fall = zombie_fall1; + szombie.th_land = zombie_land1; + + szombie.th_diewunder = zombie_wunder1; + + //szombie.th_jump = zombie_jump1; + //szombie.th_grabledge = zombie_grabledge1; + + + //================================================================================================== + SetZombieWalk(szombie); + //================================================================================================== + + SetUpHitBoxes(szombie); + //szombie.walktype = 5; + //////////////////////////////////////////////////// + szombie.reload_delay = 30 + time;//save floats, equals respawn time. + local entity old_self; + old_self = self; + self = szombie; + if (self.spawnflags & 2)//RISER + { + zombieRise(); + } + else + self.th_walk(); + self = old_self; + + // set up timer for next spawn + zombie_spawn_timer = zombie_spawn_delay + time; +}; + +void() zombie_dummy = +{ + self.frame = 0; + + self.solid = SOLID_SLIDEBOX; + self.movetype = MOVETYPE_STEP; + + + setmodel(self, "models/ai/zfull.mdl"); + + setsize (self, '-8 -8 -32', '8 8 30'); + setorigin(self, self.origin); + self.classname = "ai_zombie"; + self.takedamage = DAMAGE_YES; + self.flags = self.flags | FL_PARTIALGROUND | FL_MONSTER; + self.health = 999999; + SetUpHitBoxes(self); +}; \ No newline at end of file diff --git a/source/server/clientfuncs.qc b/source/server/clientfuncs.qc new file mode 100644 index 0000000..d43c0d3 --- /dev/null +++ b/source/server/clientfuncs.qc @@ -0,0 +1,542 @@ +/* + server/clientfuncs.qc + + used to communicate between server and client + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void SetUpdate(entity client, float type, float val1, float val2, float val3) +{ + #ifndef PSP + #ifndef NX + + if (type != 2) + { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_UPDATE); + WriteByte(MSG_MULTICAST, type); // UT_HUD + WriteByte(MSG_MULTICAST, val1); + WriteByte(MSG_MULTICAST, val2); + WriteByte(MSG_MULTICAST, val3); // misc flags/vars for later if needed + msg_entity = client; + multicast('0 0 0', MULTICAST_ONE); + } + else + { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_UPDATE); + WriteByte(MSG_MULTICAST, type); // UT_ROUNDS_CHANGE + WriteByte(MSG_MULTICAST, val1); + WriteByte(MSG_MULTICAST, val2); + WriteByte(MSG_MULTICAST, val3); // misc flags/vars for later if needed + multicast('0 0 0', MULTICAST_ALL); + } + #endif + #endif +} + +#ifndef PSP +#ifndef NX +void(entity to, float type, float cost, float weapon) useprint = { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_USEPRINT); + WriteByte(MSG_MULTICAST, type); + WriteShort(MSG_MULTICAST, cost); + WriteByte(MSG_MULTICAST, weapon); + msg_entity = to; + multicast('0 0 0', MULTICAST_ONE); +} +#endif +#endif + +void(vector org) CallExplosion = { + #ifndef PC + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + #else + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_EXPLOSION); + WriteCoord(MSG_MULTICAST, org_x); + WriteCoord(MSG_MULTICAST, org_y); + WriteCoord(MSG_MULTICAST, org_z); + multicast('0 0 0', MULTICAST_ALL); + #endif +} + +void NotifyNewRound(float to) { + #ifndef PSP + #ifndef NX + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_NEWROUND); + WriteByte(MSG_MULTICAST, to); + multicast('0 0 0', MULTICAST_ALL); + + #endif + #endif +} + +void SetRound(entity client, float to) { + #ifndef PSP + #ifndef NX + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_SETROUND); + WriteByte(MSG_MULTICAST, to); + msg_entity = client; + multicast('0 0 0', MULTICAST_ONE); + + #endif + #endif +} + +void SetPerk(entity client, float to) +{ + #ifndef PSP + #ifndef NX + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_PERK); + WriteLong(MSG_MULTICAST, to); + msg_entity = client; + multicast('0 0 0', MULTICAST_ONE); + + #endif + #endif +} + +void(float to) SwitchWeapon = +{ + #ifndef PSP + #ifndef NX + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_WEAPONCHANGE); + WriteByte(MSG_MULTICAST, to); + msg_entity = self; + multicast('0 0 0', MULTICAST_ONE); + + // hotfix for weapon2models not reseting + self.weapon2model = GetWeapon2Model(to); + + #endif + #endif +} + +void(string to, float skin) UpdateVmodel = +{ + #ifndef PSP + #ifndef NX + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_UPDATEVMODEL); + WriteString(MSG_MULTICAST, to); + WriteByte(MSG_MULTICAST, skin); + msg_entity = self; + multicast('0 0 0', MULTICAST_ONE); + + #endif + #endif +} + +void(string to, float skin) UpdateV2model = +{ + #ifndef PSP + #ifndef NX + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_UPDATEV2MODEL); + WriteString(MSG_MULTICAST, to); + WriteByte(MSG_MULTICAST, skin); + msg_entity = self; + multicast('0 0 0', MULTICAST_ONE); + #endif + #endif +} + +void(float broadcast_time, float type) BroadcastMessage = +{ + #ifndef PSP + #ifndef NX + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_BROADCAST); + WriteByte(MSG_MULTICAST, broadcast_time); + WriteByte(MSG_MULTICAST, type); + multicast('0 0 0', MULTICAST_ALL); + + #endif + #endif +} + +void(float playernum, float points, float am, float kills, string name, entity person) UpdatePlayerPoints = +{ + #ifndef PSP + #ifndef NX + if (player_count == 0) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_POINTUPDATE); + WriteByte(MSG_MULTICAST, playernum); + WriteLong(MSG_MULTICAST, points); + WriteLong(MSG_MULTICAST, am); + WriteLong(MSG_MULTICAST, kills); + WriteString(MSG_MULTICAST, name); + msg_entity = person; + multicast('0 0 0', MULTICAST_ONE); + } + else { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_POINTUPDATE); + WriteByte(MSG_MULTICAST, playernum); + WriteLong(MSG_MULTICAST, points); + WriteLong(MSG_MULTICAST, am); + WriteLong(MSG_MULTICAST, kills); + WriteString(MSG_MULTICAST, name); + multicast('0 0 0', MULTICAST_ALL); + } + + #endif + #endif +} + +void(float count) UpdatePlayerCount = { + #ifndef PSP + #ifndef NX + if (count == 0) + return; + else { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_PLAYERUPDATE); + WriteByte(MSG_MULTICAST, count); + multicast('0 0 0', MULTICAST_ALL); + } + + #endif + #endif +} + +void(float newtime, float newtype, entity change) PromptLevelChange = +{ + #ifndef PSP + #ifndef NX + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_BLACKOUT); + WriteByte(MSG_MULTICAST, newtime); + WriteByte(MSG_MULTICAST, newtype); + msg_entity = change; + multicast('0 0 0', MULTICAST_ONE); + + #endif + #endif +} + +void(entity who) UpdatePunchangle = +{ + + // naievil -- shit logic lol...but result looks clean as fuck... + #ifndef PSP + #ifndef NX + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_PUNCHANGLE); + WriteCoord(MSG_MULTICAST, who.punchangle_x); + WriteCoord(MSG_MULTICAST, who.punchangle_y); + WriteCoord(MSG_MULTICAST, who.punchangle_z); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); + + vector tempv = who.punchangle; + + if (fabs(who.punchangle_x) > 0.01) { + if (who.punchangle_x >= 0.05*tempv_x) + who.punchangle_x -= 0.05*tempv_x; + else if (who.punchangle_x <= -0.05*tempv_x) + who.punchangle_x -= 0.05*tempv_x; + else + who.punchangle_x = 0; + } else + who.punchangle_x = 0; + + if (fabs(who.punchangle_y) > 0.01) { + if (who.punchangle_y >= 0.05*tempv_y) + who.punchangle_y -= 0.05*tempv_y; + else if (who.punchangle_y <= -0.05*tempv_y) + who.punchangle_y -= 0.05*tempv_y; + else + who.punchangle_y = 0; + } else + who.punchangle_y = 0; + + if (fabs(who.punchangle_z) > 0.01) { + if (who.punchangle_z >= 0.05*tempv_z) + who.punchangle_z -= 0.05*tempv_z; + else if (who.punchangle_z <= -0.05*tempv_z) + who.punchangle_z -= 0.05*tempv_z; + else + who.punchangle_z = 0; + } else + who.punchangle_z = 0; + + #endif + #endif +} + +#ifndef PSP +#ifndef NX +void(string h, float h2, entity who) pushHUD = { + if (player_count == 0) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_HUDUPDATE); + WriteString(MSG_MULTICAST, h); + WriteByte(MSG_MULTICAST, h2); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); + } else { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_WEAPONUPDATE); + WriteString(MSG_MULTICAST, h); + WriteByte(MSG_MULTICAST, h2); + multicast('0 0 0', MULTICAST_ALL); + } +} + +void (float wepnum, string wepname, string wvmodel, float mag, float reserve, string ads, float min, float max, string flash, float flashsize, string v2, float isd, entity who) sendCustomWeapon = { + if (player_count == 0) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_WEAPONUPDATE); + WriteByte(MSG_MULTICAST, wepnum); + WriteString(MSG_MULTICAST, wepname); + WriteString(MSG_MULTICAST, wvmodel); + WriteByte(MSG_MULTICAST, mag); + WriteByte(MSG_MULTICAST, reserve); + WriteString(MSG_MULTICAST, ads); + WriteByte(MSG_MULTICAST, min); + WriteByte(MSG_MULTICAST, max); + WriteString(MSG_MULTICAST, flash); + WriteByte(MSG_MULTICAST, flashsize); + WriteString(MSG_MULTICAST, v2); + WriteByte(MSG_MULTICAST, isd); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); + } else { + WriteByte(MSG_ALL, SVC_CGAMEPACKET); + WriteByte(MSG_ALL, EVENT_WEAPONUPDATE); + WriteByte(MSG_ALL, wepnum); + WriteString(MSG_ALL, wepname); + WriteString(MSG_ALL, wvmodel); + WriteByte(MSG_ALL, mag); + WriteByte(MSG_ALL, reserve); + WriteString(MSG_ALL, ads); + WriteByte(MSG_ALL, min); + WriteByte(MSG_ALL, max); + WriteString(MSG_ALL, flash); + WriteByte(MSG_ALL, flashsize); + WriteString(MSG_ALL, v2); + WriteByte(MSG_ALL, isd); + } +} + +void(string msg, entity who) ScrollText = { + if (player_count == 0) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_SCROLLTEXT); + WriteString(MSG_MULTICAST, msg); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); + } else { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_SCROLLTEXT); + WriteString(MSG_MULTICAST, msg); + multicast('0 0 0', MULTICAST_ALL); + } +} + +void(string chaptertitle, string location, string date, string person, entity who) WorldText = { + if (player_count == 0) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_WORLDDATA); + WriteString(MSG_MULTICAST, chaptertitle); + WriteString(MSG_MULTICAST, location); + WriteString(MSG_MULTICAST, date); + WriteString(MSG_MULTICAST, person); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); + } else { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_WORLDDATA); + WriteString(MSG_MULTICAST, chaptertitle); + WriteString(MSG_MULTICAST, location); + WriteString(MSG_MULTICAST, date); + WriteString(MSG_MULTICAST, person); + multicast('0 0 0', MULTICAST_ALL); + } +} +#endif +#endif + +#ifndef NX +void (float achievement_id, optional entity who) GiveAchievement = +{ +#ifdef PSP + // temp + if (achievement_id > 4) + return; +#endif // PSP + + // this is an achievement specific to an individual + if ((who && who != world) || player_count == 0) { + if (player_count == 0) who = find(world, classname, "player"); +#ifdef PSP + achievement(who, achievement_id); +#else + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENT); + WriteByte(MSG_MULTICAST, achievement_id); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); +#endif // PSP + } else { +#ifdef PSP + entity players; + players = find(world, classname, "player"); + while(players != world) { + achievement(players, achievement_id); + players = find(players, classname, "player"); + } +#else + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENT); + WriteByte(MSG_MULTICAST, achievement_id); + multicast('0 0 0', MULTICAST_ALL); +#endif // PSP + } +} + +void (float achievement_id, float progress_value, optional entity who) UpdateAchievementProgress = +{ +#ifdef PSP + // temp + if (achievement_id > 4) + return; +#endif // PSP + // this is a progress update specific to an individual + if ((who && who != world) || player_count == 0) { + if (player_count == 0) who = find(world, classname, "player"); +#ifdef PSP + //achievement_progress(who, achievement_id, progress_value); +#else + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENTPROGRESS); + WriteByte(MSG_MULTICAST, achievement_id); + WriteFloat(MSG_MULTICAST, progress_value); + msg_entity = who; + multicast('0 0 0', MULTICAST_ONE); +#endif // PSP + } else { +#ifdef PSP + entity players; + players = find(world, classname, "player"); + while(players != world) { + //achievement_progress(players, achievement_id, progress_value); + players = find(players, classname, "player"); + } +#else + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_ACHIEVEMENTPROGRESS); + WriteByte(MSG_MULTICAST, achievement_id); + WriteFloat(MSG_MULTICAST, progress_value); + multicast('0 0 0', MULTICAST_ALL); +#endif // PSP + } +} +#endif // NX + +// ***************************************** +// Unrelated to engine, but custom functions +// ***************************************** + +float(entity who, entity target) isFacing = +{ + float who_angle = who.angles_y; + float target_angle = target.angles_y; + + float difference_angle = target_angle - who_angle; + + #ifdef PSP + if (difference_angle < -45) { + difference_angle += 360; + } + #endif + + // 180 = directly facing each other, 30 degree buffer. + if (difference_angle <= 210 && difference_angle >= 150) + return true; + + return false; +} + +float() crandom = +{ + return 2*(random() - 0.5); +} + +void WeaponSwitch(entity player) { + float wep, cmag, cmag2, cammo; + + wep = other.weapon; + other.weapon = other.secondaryweapon; + other.secondaryweapon = wep; + + cmag = other.currentmag; + other.currentmag = other.secondarymag; + other.secondarymag = cmag; + + cmag2 = other.currentmag2; + other.currentmag2 = other.secondarymag2; + other.secondarymag2 = cmag2; + + cammo = other.currentammo; + other.currentammo = other.secondaryammo; + other.secondaryammo = cammo; + + entity tempe = self; + self = player; + SwitchWeapon(other.weapon); + self = tempe; +} + +void(entity person, float expamt, float doublepoint) addmoney = +{ + if (person.classname != "player" || person.downed) + return; + + if (expamt > 0 && doublepoint == TRUE && x2_finished > time) { + expamt *= 2; + person.score += expamt; + } + + person.points += expamt; + + UpdatePlayerPoints(person.playernum, person.points, expamt, person.kills, person.netname, person); +}; diff --git a/source/server/damage.qc b/source/server/damage.qc new file mode 100644 index 0000000..fb8dcd1 --- /dev/null +++ b/source/server/damage.qc @@ -0,0 +1,570 @@ +/* + server/clientfuncs.qc + + used for any sort of down, hit, etc that the player or entity + experiences + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef NX +void (float achievement_id, optional entity who) GiveAchievement; +#endif // NX + +void() EndGame_Restart = +{ + localcmd("restart"); +} + +// Fade to black function, creates another think for restart +void() EndGame_FadePrompt = +{ + PromptLevelChange(time + 6, 2, self); + #ifdef PC + self.think = EndGame_Restart; + #else + self.think = Soft_Restart; + #endif + self.nextthink = time + 5; +} + +//Actual endgame function, all zombies die, music plays +void() EndGame = +{ + local entity oldself; + local entity who; + + self.health = 0; + self.origin = '0 0 0'; + setorigin (self, self.origin); + self.velocity = '0 0 0'; + sound (self, CHAN_AUTO, "sounds/music/end.wav", 1, ATTN_NORM); + + oldself = self; + + who = find(world,classname,"ai_zombie"); + while(who != world) + { + if(who.health) + { + self = who; + self.th_die(); + self = oldself; + } + + who = find(who,classname,"ai_zombie"); + } + + self.think = EndGame_FadePrompt; + self.nextthink = time + 33; +} + +// removes revive icon from downed player heads, used as a recursive think function +void() remove_revive = +{ + if (self.owner.beingrevived) + setmodel (self, "models/sprites/revive_white.spr"); + else + setmodel (self, "models/sprites/revive.spr"); + + if (!self.owner.downed || self.owner.isspec) + SUB_Remove (); + else { + self.think = remove_revive; + self.nextthink = time + 0.1; + } +} + +// when dead and other players exist and are alive, throw user into spectate mode +void() startspectate = +{ + if (!self.downed) + return; + + if (self.beingrevived) + { + self.think = startspectate; + self.nextthink = time + 0.1; + return; + } + + self.downedloop = 0; + self.beingrevived = false; + self.model = ""; + setmodel(self, self.model); + self.health = 100; + self.weaponmodel = ""; + self.weapon2model = ""; + self.downed = 0; + self.frame = 0; + + UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); + UpdateV2model(self.weapon2model, GetWepSkin(self.weapon)); + + SpectatorSpawn(); +} + +// searches for players that are alive given which clients have which playernumbers +// Returns 1 if there IS someone in the world that's not downed +float() PollPlayersAlive = +{ + float i, gotalive; + entity playerent; + + gotalive = 0; + + for (i = 1; i <= 4; i++) + { + playerent = findfloat(world, playernum, i); + + if (playerent) { + if (!playerent.downed && !playerent.isspec) { + gotalive = 1; + break; + } + } + } + + return gotalive; +} + +// Endgamesetup -- think function for setting up the death of everyone +void() EndGameSetup = +{ + game_over = true; + self.health = 10; + self.think = EndGame; + self.nextthink = time + 5; + self.weapon = 0; + self.currentammo = 0; + self.currentmag = 0; + self.weaponmodel = ""; + self.weapon2model = ""; + self.animend = SUB_Null; + self.perks = 0; + self.isspec = true; + SetPerk(self, self.perks); + SwitchWeapon(0); + addmoney(self, -self.points, 0); + addmoney(self, self.score, 0); + return; +} + +// rec_downed is used as a recursive loop where we consistently check to see if ALL players are downed +// if they aren't dead, we keep looping until our OWN death (45 seconds, so 450 loops) +void() rec_downed = +{ + self.downedloop++; + if (self.downedloop == 450) { + startspectate(); + return; + } + + float gotalive = PollPlayersAlive(); + + if (!gotalive && !self.progress_bar) { + EndGameSetup(); + return; + } + + self.think = rec_downed; + self.nextthink = time + 0.1; +} + +void() GetDown = +{ + float startframe; + float endframe; + local string modelname; + + if (rounds <= 1 && self.currentmag == 0 && self.currentmag2 == 0 && self.currentammo == 0 && self.secondarymag == 0 && + self.secondarymag2 == 0 && self.secondaryammo == 0) { + GiveAchievement(9, self); + } + + playdown(); + + switch(self.stance) { + case 2: + self.new_ofs_z = self.view_ofs_z - 42; + self.stance = 0; + break; + case 1: + self.new_ofs_z = self.view_ofs_z - 24; + self.stance = 0; + break; + default: break; + } + + // remove third weapon + self.thirdweapon = 0; + + self.velocity = '-80 0 -80'; // Stop any old movement + self.zoom = 0; + self.downed = true; + self.dive_delay = 0; + self.movetype = MOVETYPE_NONE; + + float gotalive = PollPlayersAlive(); + + if ((coop && !gotalive) || (!coop && !(self.perks & P_REVIVE))) { + EndGameSetup(); + return; + } else { + self.health = 19; + } + + if ((self.perks & P_REVIVE) && !coop) { + self.progress_bar = 10 + time; + self.progress_bar_time = 10; + self.progress_bar_percent = 1; + self.downed = true; + } + + self.points = 10*rint((self.points*0.95)/10); + addmoney(self, 0, true); // used to call a broadcast + BroadcastMessage(time + 3, 2); + + self.perks = 0; + self.weaponbk = self.weapon; + self.currentammobk = self.currentammo; + self.currentmagbk = self.currentmag; + self.currentmagbk2 = self.currentmag2; + + if (self.weapon == W_BIATCH || self.secondaryweapon == W_BIATCH || self.progress_bar_percent > 0) { + self.weapon = W_BIATCH; + self.currentammo = 12; + self.currentmag = 6; + self.currentmag2 = 6; + } else { + self.weapon = W_COLT; + self.currentammo = 16; + self.currentmag = 8; + } + + modelname = GetWeaponModel(self.weapon, 0); + self.weaponmodel = modelname; + SwitchWeapon(self.weapon); + + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH); + + local entity revive; + + revive = spawn (); + revive.owner = self; + revive.movetype = MOVETYPE_NONE; + revive.solid = SOLID_NOT; + revive.think = remove_revive; + revive.nextthink = time + 0.1; + + setmodel (revive, "models/sprites/revive.spr"); + revive.origin = self.origin + VEC_VIEW_OFS; + setorigin (revive, revive.origin); + + SetPerk(self, 0); + + self.think = rec_downed; + self.nextthink = time + 0.1; +} + +void () GetUp = +{ + local string modelname; + float startframe; + float endframe; + + playgetup(); // animation + + self.new_ofs_z = self.view_ofs_z + 42; + self.stance = 2; + self.health = 100; + self.downedloop = 0; // used for death timing vs endgame + self.downed = 0; + self.classname = "player"; + if (self.weaponbk) + { + self.weapon = self.weaponbk; + self.currentammo = self.currentammobk; + self.currentmag = self.currentmagbk; + self.currentmag2 = self.currentmagbk2; + } + modelname = GetWeaponModel(self.weapon, 0); + self.weaponmodel = modelname; + SwitchWeapon(self.weapon); + self.weapon2model = GetWeapon2Model(self.weapon); + self.movetype = MOVETYPE_WALK; + + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH); + +}; + +// poll checking whether to see if our revive invoke is active +void(entity ent) CheckRevive = +{ + if (self.invoke_revive) { + GetUp(); + self.invoke_revive = 0; + } +} + +void(entity attacker, float d_style) DieHandler = +{ + float t; + + t = random(); + + if (self.classname == "ai_zombie" || self.classname == "ai_dog") { + self.th_die(); + } + + if (attacker.classname == "player") { + attacker.kills++; + if (d_style == S_HEADSHOT) { + addmoney(attacker, 100, true); + total_powerup_points = total_powerup_points + 100; + attacker.headshots++; + } else if (d_style == S_NORMAL) { + addmoney(attacker, 60, true); + total_powerup_points = total_powerup_points + 60; + } + else if (d_style == S_KNIFE){ + addmoney(attacker, 130, true); + total_powerup_points = total_powerup_points + 130; + } else if (d_style == S_TESLA) { + addmoney(attacker, 50, true); + total_powerup_points = total_powerup_points + 50; + } + } +} + +void(entity victim,entity attacker, float damage, float d_style) DamageHandler = { + // don't do any attacking during nuke delay + if (d_style == S_ZOMBIE && nuke_powerup_active > time) + return; + + // Abstinence Program + victim.ach_tracker_abst = 1; + + entity old_self; + if (victim.classname == "ai_zombie" || victim.classname == "ai_dog") { + + if (attacker.classname == "player" && (victim.health - damage)> 0) { + addmoney(attacker, 10, 1); + } + + victim.health = victim.health - damage; + + if (d_style == S_EXPLOSIVE) { + if (victim.health < z_health*0.5) + { + if (victim.crawling != TRUE && !(victim.health <= 0) && crawler_num < 5 && victim.classname != "ai_dog") + { + makeCrawler(victim); + #ifndef NX + GiveAchievement(3, attacker); + #endif + } + else + { + if (attacker.classname == "player" && (victim.health - damage) > 0) + addmoney(attacker, 10, 1); + } + } + // MOTO - explosives seem to work fine without, but with will cause counter and QC errors.. + //else + // victim.th_die(); + + if (victim.health <= 0) + addmoney(attacker, 60, 1); + } + + if (victim.health <= 0 || instakill_finished) { + old_self = self; + self = victim; + DieHandler(attacker, d_style); + self = old_self; + } + } else if (victim.classname == "player" && !victim.downed) { + + if (victim.flags & FL_GODMODE) { + return; + } + + if (victim.perks & P_JUG) + damage = ceil(damage*0.5); + + victim.health = victim.health - damage; + victim.health_delay = time + 2; + + // shake the camera on impact + local vector distance; + distance = attacker.angles - victim.angles; + + // just to prevent radical punchangles + while(distance_x > 10 || distance_x < -10) { + distance_x /= 2; + } + while(distance_y > 10 || distance_y < -10) { + distance_y /= 2; + } + + // apply + victim.punchangle_x = distance_x; + victim.punchangle_y = distance_y; + + // play hurt sound + sound (self, CHAN_AUTO, "sounds/player/pain4.wav", 1, ATTN_NORM); + + if (victim.health <= 20) + { + old_self = self; + self = victim; + GetDown(); + self = old_self; + } + } +} + +/* +============ +CanDamage + +Returns true if the inflictor can directly damage the target. Used for +explosions and melee attacks. +============ +*/ +float(entity targ, entity inflictor) CanDamage = +{ + if (targ.flags == FL_GODMODE) + return FALSE; +// 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; +}; + +void(entity inflictor, entity attacker, float damage2, float mindamage, float radius) DamgageExplode = +{ + float final_damage; + entity ent; + + float multi, r; + ent = findradius(inflictor.origin, radius); + + while (ent != world) + { + if(ent.classname == "player") + { + if (ent.perks & P_FLOP) + final_damage = 0; + else + { + final_damage = radius - vlen(inflictor.origin - ent.origin); + + if(final_damage < 0) + continue; + + if (final_damage > radius * 0.6) + final_damage = 100; + + if (final_damage < other.health) + { + addmoney(self, 10, 0); + } + else if (final_damage > other.health) + { + addmoney(self, 60, 0); + } + else + { + final_damage /= radius; + final_damage *= 60; + } + DamageHandler (attacker, attacker, final_damage, S_EXPLOSIVE); + } + } + else if (ent.takedamage && ent.classname != "ai_zombie_head" && ent.classname != "ai_zombie_larm" && ent.classname != "ai_zombie_rarm") + { + // verify we aren't doin anything with a bmodel + if (ent.solid == SOLID_BSP || ent.movetype == MOVETYPE_PUSH) + return; + + if (mapname == "ndu" && ent.classname == "ai_zombie" && inflictor.classname == "explosive_barrel") { + ach_tracker_barr++; + + if (ach_tracker_barr >= 15) { + GiveAchievement(13); + } + } + + r = rounds; + multi = 1.07; + while(r > 0) + { + multi *= 1.05; + r --; + } + + if (mindamage == 75) + final_damage = (200 * multi) + 185; + else + final_damage = (mindamage + damage2)/2; + + if (final_damage > 0) + { + /* ndaekill = true; */ + if (CanDamage (ent, inflictor)) + DamageHandler (ent, attacker, final_damage, S_EXPLOSIVE); + /* kill = false; */ + } + } + ent = ent.chain; + } +}; \ No newline at end of file diff --git a/source/server/defs/custom.qc b/source/server/defs/custom.qc new file mode 100644 index 0000000..66909ff --- /dev/null +++ b/source/server/defs/custom.qc @@ -0,0 +1,501 @@ +/* + server/defs/custom.qc + + put custom server-only globals and fields here + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef PSP +#ifndef NX +#pragma target FTE +#endif +#endif + +#define true 1 +#define false 0 + +#define FL_JUMPRELEASED 4096 + +// achievement tracking +.float ach_tracker_npnp; +.float ach_tracker_abst; +float ach_tracker_spin; +float ach_tracker_luck; +.float ach_tracker_coll; +float ach_tracker_col2; +float ach_tracker_barr; + +float framecount; +float deathmatch; +float coop; +.vector oldvelocity; +.float lastsound_time; +.float isspec; +string mappath; +.float playernum; +.float ads_toggle; +float player_count; +entity pl1; +.string fog; // used for hacking in changing fog from world.fog for legacy maps + +entity local_client; +.float stance; +.float stancereset; +.float changestance; +.vector new_ofs; + +//money +.float points; +.float cost; +.float cost2; +void(entity person, float expamt , float doublepoint) addmoney; + +//stats +.float score; +.float kills; +.float headshots; +#ifdef PC +.float facingenemy; +#endif + +//doors +.float state; +#define STATE_TOP 0 +#define STATE_BOTTOM 1 +#define STATE_UP 2 +#define STATE_DOWN 3 +.float /*worldtype,*/ delay, wait, lip, /*light_lev,*/ speed, style/*, skill*/; + +entity activator; +.float requirespower; + +//player funcs +#ifdef PC +.float zoom; +#endif + +float sprint_max_time = 4.0; +.float sprinting; +.float weaponskin; +.float secondaryweaponskin; +.float thirdweaponskin; +.float stamina; +.float sprint_timer; +.float sprint_duration; +.float sprint_timer_stopped; +.float sprint_start_time; +.float sprint_stop_time; +.float sprint_rest_time; +void() W_SprintStop; +.float into_sprint; +.float dive; +.float dive_delay; +.float damagetimer; +.vector movement; + +//Weaponsystem defines +void SwitchWeapon(float to); +void GetUp(); +void Weapon_Logic(); +.float downed; +.float fire_delay; +.float fire_delay2; +.float reload_delay; +.float reload_delay2; +.float switch_delay; +.float health_delay; +.float progress_bar; +.float progress_bar_time; +.float progress_bar_percent; +.float weaponbk; +.float currentmag; +.float currentmag2; +.float currentmagbk; +.float currentmagbk2; +.float currentammobk; +.float secondaryammo; +.float thirdammo; +.float semi; +.float semi2; +.float semiuse; +.float semiswitch; +.float semireload; +.float secondarymag; +.float secondarymag2; +.float secondaryweapon; +.float thirdmag; +.float thirdmag2; +.float thirdweapon; +.float NeedLoad; +.string weapon2model; +.float weapon2frame; +.float reloadinterupted; +.float hitcount; +.float weaponnum; // 0 for weapon one, 1 for second weapon...we invert value for easy comparison, a third gun would need to be hardwired + +//Reviving +.float invoke_revive; +.float reviving; +.float revived; +.float beingrevived; +.float downedloop; + +#define S_LEFT 0 +#define S_RIGHT 1 +#define S_BOTH 2 + +//Knife +.float semiknife; +.float knife_delay; +.float bowie; + +//Grenades +.float grenades; +.float pri_grenade_state; +.float bk_nade; +.float grenade_delay; +.float secondary_grenades; +.float primary_grenades; +.float throw_delay; + +//weapon frames +void Set_W_Frame (float startframe, float endframe, float duration, float funccalledin, float animtype, void(optional float t) endanimfunc, string set_model, float dontstartnew, float side); +.float weapon_animduration; +.float weapon2_animduration; +.float weapon_anim_type; +.float weapon2_anim_type; +.float anim_weapon_time; +.float anim_weapon2_time; +.float weaponframe_end; +.float weapon2frame_end; +.float callfuncat; +.float callfuncat2; +.float new_anim_stop; +.float new_anim2_stop; +.float anim_reversed; +.float anim2_reversed; +.void() animend; +.void(optional float t) animend2; + +//Null functions +void() SUB_Null = {}; +void() SUB_Null2 = {}; + +#define VEC_HULL_MIN '-16 -16 -32' +#define VEC_HULL_MAX '16 16 40' +#define VEC_HULL2_MIN '-32 -32 -24' +#define VEC_HULL2_MAX '32 32 64' + +#define VEC_VIEW_OFS '0 0 32' + +#ifdef PSP +#define MAX_ZOMB 12 +#else +#define MAX_ZOMB 24 +#endif + +vector trace_plane_normal; + + +// AI definitions +//Used for global one-zombie-at-a-time type ai +void Do_Zombie_AI(); +.string aistatus; +entity lastzombie; +float zombie_spawn_delay; // time before spawning, in seconds. +float zombie_spawn_timer; // the actual timer for spawn delay +//Other AI definitions +.vector box1, box2, box3;//used for windows and zombies +.vector idlebox; +.vector hop_spot;//used for windows (zombies hop to these) +.vector goalorigin; +.float teslacount; +.float iszomb; +.float onfire; +.entity firer; +float crawler_num; +//==== Reference Vars ==== +#define WWINDOW 1 +#define WBOX1 2 +#define WBOX2 4 +#define WBOX3 8 +#define WIDLEBOX 16 +//======================== + +//we're using usedent for who is currently hopping the window +//Used for windows to keep track of what zombies are at windows +.entity box1owner, box2owner, box3owner; + +.entity usedent; +//.float used;//used for the window boxes//not anymore +.float outside;//used for knowing if a zomibe has hopped window yet +.float chase_enemy_time; +.float chase_time; +.float enemy_timeout; +//.float pathing; +.float calc_time; //used as a delay thing (so zombie ai doesn't run 100% of the time +.string target2; +.string target3; +.string target4; +.string target5; +.string target6; +.string target7; +.string target8; +.string wayTarget; +.entity active_door;//Set in waypoint mode +.string targetname;//the name of an entitys +entity lastspawn;//last spawn point used by spawning code +.entity goaldummy; //Used to store the origin of the zombies target +.float goalway; //Used to store the origin of the zombies target +.float walktype;//decides animations and moving speeds for zombies +.void() th_walk; +//.void() th_run; +.void() th_die; +.void() th_melee; +.void() th_idle; +.void() th_windowhop; +.void() th_diewunder; + +.void() th_fall; +.void() th_falling; +.void() th_land; + +.void() th_jump; +.void() th_grabledge; +.float tries; +.float hop_step;//KEEPS TRACK OF WHERE WE ARE ON THE HOPPING PART + +float INACTIVE = 1; + +float tracemove(vector start, vector min, vector max, vector end, float nomonsters, entity forent); + +.float way_path[40]; +.float way_cur; + +.float sound_time; +.float s_time; +float sounds_playing; + +.float fall; +//.vector lastOrg;//Zombie's last origin, for checking stuckness +//.float OrgStuckCount; +.float crawling; + +.float washit; +.float hitamount; + +.float laststep; +void(entity who) makeCrawler; +.float state;//used to delay making a crawler, ex) when zombie is rising from ground or climbing over barrier, turn zombie into a crawler afterwards + +void() spawnAllZombEnts; +void() set_z_health; +float() spawn_a_zombieA; +float gotdog; +float dogRound; +float dogWave; +float z_health; +.float bleedingtime; +.float time_to_die; + + +float crandom(); + + +// Door +.string killtarget; +.void() think1; +.vector finaldest; +.vector pos1, pos2/*, mangle*/; +.vector finalangle; +.float distance; +.float sequence; +.entity active_door; +.string door_model_target; +.string door_model_name; + +//Perk and Power system +float isPowerOn; +.float isBuying; // naievil -- used for checking if a perk is being consumed, limits glitching +.float perks; +.float perk_delay; +.float revivesoda; +.float collected; + +.float boxstatus; +.entity boxweapon; +.float spins; +.float papState; +float BoxWeapons[25]; +entity boxLocations[32]; +float boxCount; +vector boxOrigin; + +#ifndef PSP +//powerups +.float x2_icon; +.float insta_icon; +#endif +.string powerup_vo; +float instakill_finished; +float insta_blink; +float x2_finished; +float x2_blink; +float total_windows_down; +float total_powerup_points; +float powerup_activate; +float nuke_powerup_active; +float nuke_powerups_activated; +float nuke_powerup_spawndelay; + +//rounds +float roundinit; +float roundtype; +float Current_Zombies; +float Total_Zombies; +float Remaining_Zombies; +float Delay_Time; +float spawn_time; +float round_changetime; +float game_over; +float blink_return; +float delay_at_round; +float spawn_delay; +float maxreward; +float totalreward; +float totalpowerups; +float sounds_playing; + +float rounds; +float rounds_change; + +//Waypoints +void () Waypoint_Logic; +entity current_way; +float waypoint_mode; +entity active_way; + +#define MAX_WAY_TARGETS 10 +.string waynum; +.string targets[MAX_WAY_TARGETS]; + + +//pathfinds +#define MAX_WAYPOINTS 256 //max waypoints +void LoadWaypointData(); +typedef struct +{ + vector org; + float id; + float g, f; + float next, prev; + float step; + float target_id [MAX_WAY_TARGETS]; // Targets array number + string targetdoor; //special tag is required for the closed waypoints + float dist [MAX_WAY_TARGETS]; // Distance to the next waypoints + float set; +} waypoint_ai; + +#define SET_NONE 0 +#define SET_OPEN 1 +#define SET_CLOSED 2 + +#ifdef PC +waypoint_ai waypoints[MAX_WAYPOINTS]; + +// fog +string world_fog; + +// lights +#define EF_PURPLELIGHT 256 // fte already has some effect styles defined... +#endif + +#define UT_HUD 1 +#define UT_ROUNDS_CHANGE 2 +#define UT_HM 3 +#define UT_ZOOM2 4 +#define UT_CROSSHAIR 5 + +//Misc patch definitions +.string teddyremovetarget; + +.float oldz; // used for fall damage that does not truly work correctly + +.float sprint_delay; + +//soft restart stuff for doors +.string oldmodel; +.vector oldorigin; +.float oldstate; +.float state; +.float isopen; + +//world +.string chaptertitle; +.string location; +.string date; +.string person; +.string song; + +//altered game elements +float G_STARTPOINTS; +float G_STARTROUND; +float G_PRONEPOINTS; +float G_STARTWEAPON[3]; +float G_WORLDTEXT; +float G_PERKS; +float G_PERKPOWER; + +//song easter egg +float sndTriggerCnt; +float sndActivCnt; +.float activated; + +//teleporter +.entity tele_target; +.float mode; +.float cooldown; +.float isLinked; +.float waitLink; +.float tpTimer; +.float isTimed; +.entity host; + +// GIBBING +#ifdef PC +.entity larm; +.entity rarm; +.entity head; +.vector bbmins, bbmaxs; +.float currentHitBoxSetup; +#endif + +// PC Fog force +.float PC_fog; + +// hl stuff +#ifdef PC +.float rendermode; +.float renderamt; +.vector rendercolor; +#endif \ No newline at end of file diff --git a/source/server/defs/fte.qc b/source/server/defs/fte.qc new file mode 100644 index 0000000..5479fa7 --- /dev/null +++ b/source/server/defs/fte.qc @@ -0,0 +1,1667 @@ +/* +This file was automatically generated by FTE Quake v1.06 +This file can be regenerated by issuing the following command: +pr_dumpplatform -O ssdefs -Tqw +Available options: +-Ffte - target only FTE (optimations and additional extensions) +-Tnq - dump specifically NQ fields +-Tqw - dump specifically QW fields +-Tcs - dump specifically CSQC fields +-Tmenu - dump specifically menuqc fields +-Fdefines - generate #defines instead of constants +-Faccessors - use accessors instead of basic types via defines +-O - write to a different qc file +*/ +#pragma noref 1 +//#pragma flag enable logicops +#pragma warning error Q101 /*too many parms*/ +#pragma warning error Q105 /*too few parms*/ +#pragma warning error Q106 /*assignment to constant/lvalue*/ +#pragma warning error Q208 /*system crc unknown*/ +#pragma warning disable F211 /*system crc outdated (eg: dp's csqc)*/ +#pragma warning enable F301 /*non-utf-8 strings*/ +#pragma warning enable F302 /*uninitialised locals*/ +#ifndef QUAKEWORLD +#define QUAKEWORLD +#endif +#ifndef QWSSQC +#define QWSSQC +#endif +#ifndef SSQC +#define SSQC +#endif +#define FTE_PEXT_SETVIEW /* NQ's svc_setview works correctly even in quakeworld */ +#define DP_ENT_SCALE +#define FTE_PEXT_LIGHTSTYLECOL +#define DP_ENT_ALPHA +#define FTE_PEXT_VIEW2 +#define FTE_PEXT_ACURATETIMINGS +#define FTE_PEXT_SOUNDDBL +#define FTE_PEXT_FATNESS +#define DP_HALFLIFE_MAP +#define FTE_PEXT_TE_BULLET +#define FTE_PEXT_HULLSIZE +#define FTE_PEXT_MODELDBL +#define FTE_PEXT_ENTITYDBL +#define FTE_PEXT_ENTITYDBL2 +#define FTE_PEXT_FLOATCOORDS +#define FTE_PEXT_VWEAP +#define FTE_PEXT_Q2BSP +#define FTE_PEXT_Q3BSP +#define DP_ENT_COLORMOD +#define FTE_HEXEN2 +#define FTE_PEXT_SPAWNSTATIC +#define FTE_PEXT_CUSTOMTENTS +#define FTE_PEXT_256PACKETENTITIES +#define TEI_SHOWLMP2 +#define DP_GFX_QUAKE3MODELTAGS +#define FTE_PK3DOWNLOADS +#define PEXT_CHUNKEDDOWNLOADS +#define EXT_CSQC_SHARED +#define PEXT_DPFLAGS +#define EXT_CSQC +#define BX_COLOREDTEXT +#define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */ +#define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */ +#define DP_EF_ADDITIVE +#define DP_EF_BLUE +#define DP_EF_FULLBRIGHT +#define DP_EF_NODEPTHTEST +#define DP_EF_NODRAW +#define DP_EF_NOGUNBOB +#define DP_EF_NOSHADOW +#define DP_EF_RED +#define DP_ENT_CUSTOMCOLORMAP +#define DP_ENT_EXTERIORMODELTOCLIENT +#define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum("myeffectname"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */ +#define DP_ENT_VIEWMODEL +#define DP_GECKO_SUPPORT +#define DP_GFX_SKINFILES +#define DP_GFX_SKYBOX +#define DP_HALFLIFE_MAP_CVAR +#define DP_INPUTBUTTONS +#define DP_LIGHTSTYLE_STATICVALUE +#define DP_LITSUPPORT +#define DP_MD3_TAGSINFO +#define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */ +#define DP_MOVETYPEBOUNCEMISSILE +#define DP_MOVETYPEFOLLOW +#define DP_QC_ASINACOSATANATAN2TAN +#define DP_QC_CHANGEPITCH +#define DP_QC_COPYENTITY +#define DP_QC_CRC16 +#define DP_QC_CVAR_DEFSTRING +#define DP_QC_CVAR_STRING +#define DP_QC_CVAR_TYPE +#define DP_QC_EDICT_NUM +#define DP_QC_ENTITYDATA +#define DP_QC_ETOS +#define DP_QC_FINDCHAIN +#define DP_QC_FINDCHAINFLOAT +#define DP_QC_FINDFLAGS +#define DP_QC_FINDCHAINFLAGS +#define DP_QC_FINDFLOAT +#define DP_QC_FS_SEARCH +#define DP_QC_GETSURFACE +#define DP_QC_GETSURFACEPOINTATTRIBUTE +#define DP_QC_MINMAXBOUND +#define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */ +#define DP_SV_PRINT /* Says that the print builtin can be used from nqssqc (as well as just csqc), bypassing the developer cvar issues. */ +#define DP_QC_RANDOMVEC +#define DP_QC_RENDER_SCENE /* clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it. */ +#define DP_QC_SINCOSSQRTPOW +#define DP_QC_SPRINTF /* Provides the sprintf builtin, which allows for rich formatting along the lines of C's function with the same name. Not to be confused with QC's sprint builtin. */ +#define DP_QC_STRFTIME +#define DP_QC_STRING_CASE_FUNCTIONS +#define DP_QC_STRINGBUFFERS +#define DP_QC_STRINGCOLORFUNCTIONS +#define DP_QC_STRREPLACE +#define DP_QC_TOKENIZEBYSEPARATOR +#define DP_QC_TRACEBOX +#define DP_QC_TRACETOSS +#define DP_QC_TRACE_MOVETYPE_HITMODEL +#define DP_QC_TRACE_MOVETYPE_WORLDONLY +#define DP_QC_TRACE_MOVETYPES +#define DP_QC_UNLIMITEDTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS, superseded by FTE_QC_PERSISTENTTEMPSTRINGS. Specifies that all temp strings will be valid at least until the QCVM returns. */ +#define DP_QC_URI_ESCAPE +#define DP_QC_URI_GET +#define DP_QC_URI_POST +#define DP_QC_VECTOANGLES_WITH_ROLL +#define DP_QC_VECTORVECTORS +#define DP_QC_WHICHPACK +#define DP_QUAKE2_MODEL +#define DP_QUAKE2_SPRITE +#define DP_QUAKE3_MODEL +#define DP_REGISTERCVAR +#define DP_SND_SOUND7_WIP2 +#define DP_SND_STEREOWAV +#define DP_SND_OGGVORBIS +#define DP_SOLIDCORPSE +#define DP_SPRITE32 +#define DP_SV_BOTCLIENT +#define DP_SV_CLIENTCOLORS /* Provided only for compatibility with DP. */ +#define DP_SV_CLIENTNAME /* Provided only for compatibility with DP. */ +#define DP_SV_DRAWONLYTOCLIENT +#define DP_SV_DROPCLIENT /* Equivelent to quakeworld's stuffcmd(self,"disconnect\n"); hack */ +#define DP_SV_EFFECT +#define DP_SV_EXTERIORMODELFORCLIENT +#define DP_SV_NODRAWTOCLIENT +#define DP_SV_PLAYERPHYSICS /* Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction. */ +#define DP_SV_POINTSOUND +#define DP_SV_PRECACHEANYTIME /* Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...) */ +#define DP_SV_SETCOLOR +#define DP_SV_SPAWNFUNC_PREFIX +#define DP_SV_WRITEPICTURE +#define DP_SV_WRITEUNTERMINATEDSTRING +#define DP_TE_BLOOD +#define DP_TE_CUSTOMFLASH +#define DP_TE_EXPLOSIONRGB +#define DP_TE_PARTICLECUBE +#define DP_TE_PARTICLERAIN +#define DP_TE_PARTICLESNOW +#define DP_TE_SMALLFLASH +#define DP_TE_SPARK +#define DP_TE_STANDARDEFFECTBUILTINS +#define DP_VIEWZOOM +#define EXT_BITSHIFT +#define EXT_DIMENSION_VISIBILITY +#define EXT_DIMENSION_PHYSICS +#define EXT_DIMENSION_GHOST +#define FRIK_FILE +#define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */ +#define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */ +#define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ +#define FTE_CSQC_HALFLIFE_MODELS +#define FTE_CSQC_SERVERBROWSER /* Provides builtins to query the engine's serverbrowser servers list from ssqc. Note that these builtins are always available in menuqc. */ +#define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */ +#define FTE_CSQC_RAWIMAGES /* Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer. */ +#define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */ +#define FTE_CSQC_REVERB /* Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver. */ +#define FTE_CSQC_WINDOWCAPTION /* Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out. */ +#define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */ +#define FTE_ENT_UNIQUESPAWNID +#define FTE_EXTENDEDTEXTCODES +#define FTE_FORCESHADER /* Allows csqc to override shaders on models with an explicitly named replacement. Also allows you to define shaders with a fallback if it does not exist on disk. */ +#define FTE_FORCEINFOKEY /* Provides an easy way to change a user's userinfo from the server. */ +#define FTE_GFX_QUAKE3SHADERS /* specifies that the engine has full support for vanilla quake3 shaders */ +#define FTE_GFX_REMAPSHADER /* With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface. */ +#define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */ +#define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */ +#define FTE_MEDIA_AVI /* playfilm command supports avi files. */ +#define FTE_MEDIA_CIN /* playfilm command supports q2 cin files. */ +#define FTE_MEDIA_ROQ /* playfilm command supports q3 roq files. */ +#define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand. */ +#define FTE_MULTITHREADED /* Faux multithreading, allowing multiple contexts to run in sequence. */ +#define FTE_MVD_PLAYERSTATS /* In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case. */ +#define FTE_QC_NPCCHAT +#define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/*.cfg, the format of which is documented elsewhere. */ +#define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */ +#define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */ +#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */ +#define FTE_QC_FILE_BINARY /* Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers. */ +#define FTE_QC_CHANGELEVEL_HUB /* Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data. */ +#define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */ +#define FTE_QC_CHECKPVS +#define FTE_QC_CROSSPRODUCT +#define FTE_QC_FS_SEARCH_SIZEMTIME +#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed. */ +#define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */ +#define FTE_QC_INFOKEY /* QuakeWorld's infokey builtin works, and reports at least name+topcolor+bottomcolor+ping(in ms)+ip(unmasked, but not always ipv4)+team(aka bottomcolor in nq). Does not require actual localinfo/serverinfo/userinfo, but they're _highly_ recommended to any engines with csqc */ +#define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */ +#define FTE_QC_MATCHCLIENTNAME +#define FTE_QC_MULTICAST /* QuakeWorld's multicast builtin works along with MSG_MULTICAST, but also with unicast support. */ +#define FTE_QC_PAUSED +#define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */ +#define FTE_QC_RAGDOLL_WIP +#define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */ +#define FTE_QC_STUFFCMDFLAGS /* Variation on regular stuffcmd that gives control over how spectators/mvds should be treated. */ +#define FTE_QC_TRACETRIGGER +#define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */ +#define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */ +#define FTE_QUAKE3_CLIENT /* This engine is able to act as a quake3 client */ +#define FTE_QUAKE3_SERVER /* This engine is able to act as a quake3 server */ +#define FTE_SOLID_LADDER /* Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS */ +#define FTE_SQL /* Provides sql* builtins which can be used for sql database access */ +#define FTE_SQL_SQLITE /* SQL functionality is able to utilise sqlite databases */ +#define FTE_STRINGS /* Extra builtins (and additional behaviour) to make string manipulation easier */ +#define FTE_SV_POINTPARTICLES /* Specifies that particleeffectnum, pointparticles, and trailparticles exist in ssqc as well as csqc. particleeffectnum acts as a precache, allowing ssqc values to be networked up with csqc for use. Use in combination with FTE_PART_SCRIPT+FTE_PART_NAMESPACES to use custom effects. This extension is functionally identical to the DP version, but avoids any misplaced assumptions about the format of the client's particle descriptions. */ +#define FTE_SV_REENTER +#define FTE_TE_STANDARDEFFECTBUILTINS /* Provides builtins to replace writebytes, with a QW compatible twist. */ +#define FTE_TERRAIN_MAP /* This engine supports .hmp files, as well as terrain embedded within bsp files. */ +#define FTE_RAW_MAP /* This engine supports directly loading .map files, as well as realtime editing of the various brushes. */ +#define KRIMZON_SV_PARSECLIENTCOMMAND /* SSQC's SV_ParseClientCommand function is able to handle client 'cmd' commands. The tokenizing parts also work in csqc. */ +#define NEH_CMD_PLAY2 +#define NEH_RESTOREGAME +#define QSG_CVARSTRING +#define QW_ENGINE +#define QWE_MVD_RECORD /* You can use the easyrecord command to record MVD demos serverside. */ +#define TEI_MD3_MODEL +#define TENEBRAE_GFX_DLIGHTS /* Allows ssqc to attach rtlights to entities with various special properties. */ +#define ZQ_MOVETYPE_FLY /* MOVETYPE_FLY works on players. */ +#define ZQ_MOVETYPE_NOCLIP /* MOVETYPE_NOCLIP works on players. */ +#define ZQ_MOVETYPE_NONE /* MOVETYPE_NONE works on players. */ +#define ZQ_VWEP +#define ZQ_QC_STRINGS /* The strings-only subset of FRIK_FILE is supported. */ + +#ifdef _ACCESSORS +accessor strbuf : float; +accessor searchhandle : float; +accessor hashtable : float; +accessor infostring : string; +accessor filestream : float; +accessor filestream : float; +#else +#define strbuf float +#define searchhandle float +#define hashtable float +#define infostring string +#define filestream float +#endif + +entity self; /* The magic me */ +entity other; /* Valid in touch functions, this is the entity that we touched. */ +entity world; /* The null entity. Hurrah. Readonly after map spawn time. */ +float time; /* The current game time. Stops when paused. */ +float frametime; /* The time since the last physics/render/input frame. */ +entity newmis; /* A named entity that should be run soon, to reduce the effects of latency. */ +float force_retouch; /* If positive, causes all entities to check for triggers. */ +string mapname; /* The short name of the map. */ +float serverflags; +float total_secrets; +float total_monsters; +float found_secrets; +float killed_monsters; +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +vector v_forward, v_up, v_right; +float trace_allsolid, trace_startsolid, trace_fraction; +vector trace_endpos, trace_plane_normal; +float trace_plane_dist; +entity trace_ent; +float trace_inopen; +float trace_inwater; +entity msg_entity; +void() main; +void() StartFrame; +void() PlayerPreThink; +void() PlayerPostThink; +void() ClientKill; +void() ClientConnect; +void() PutClientInServer; +void() ClientDisconnect; +void() SetNewParms; +void() SetChangeParms; +void end_sys_globals; +.float modelindex; /* This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it. */ +.vector absmin; /* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */ +.vector absmax; /* Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates. */ +.float ltime; /* On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly. */ +.float lastruntime; /* This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons. */ +.float movetype; /* Describes how the entity moves. One of the MOVETYPE_ constants. */ +.float solid; /* Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants */ +.vector origin; /* The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction. */ +.vector oldorigin; /* This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck. */ +.vector velocity; /* The direction and speed that the entity is moving in world space. */ +.vector angles; /* The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN. */ +.vector avelocity; /* The amount the entity's angles change by per second. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy if you're changing more than one angle at a time. */ +.string classname; /* Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit. */ +.string model; /* The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible. */ +.float frame; /* The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc. */ +.float skin; /* The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water. */ +.float effects; /* Lots of random flags that change random effects. See EF_* constants. */ +.vector mins; /* The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel. */ +.vector maxs; /* like mins, but in the other direction. */ +.vector size; /* maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel) */ +.void() touch; +.void() use; +.void() think; +.void() blocked; +.float nextthink; /* The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal. */ +.entity groundentity; +.float health; +.float frags; +.float weapon; +.string weaponmodel; +.float weaponframe; +.float currentammo; +.float ammo_shells; +.float ammo_nails; +.float ammo_rockets; +.float ammo_cells; +.float items; +.float takedamage; +.entity chain; +.float deadflag; +.vector view_ofs; +.float button0; +.float button1; +.float button2; +.float impulse; +.float fixangle; /* Forces the clientside view angles to change to the value of .angles (has some lag). If set to 1/TRUE, the server will guess whether to send a delta or an explicit angle. If 2, will always send a delta (due to lag between transmission and acknowledgement, this cannot be spammed reliably). If 3, will always send an explicit angle. */ +.vector v_angle; /* The angles a player is viewing. +x is DOWN (pitch, yaw, roll) */ +.string netname; +.entity enemy; +.float flags; +.float colormap; +.float team; +.float max_health; +.float teleport_time; /* While active, prevents the player from using the +back command, also blocks waterjumping. */ +.float armortype; +.float armorvalue; +.float waterlevel; +.float watertype; +.float ideal_yaw; +.float yaw_speed; +.entity aiment; +.entity goalentity; +.float spawnflags; +.string target; +.string targetname; +.float dmg_take; +.float dmg_save; +.entity dmg_inflictor; +.entity owner; +.vector movedir; +.string message; +.float sounds; +.string noise; +.string noise1; +.string noise2; +.string noise3; +void end_sys_fields; +float input_timelength; +vector input_angles; /* +x=DOWN */ +vector input_movevalues; +float input_buttons; +float input_impulse; +int trace_endcontents; +int trace_surfaceflags; +int trace_brush_id; +int trace_brush_faceid; +int trace_surface_id; /* 1-based. 0 if not known. */ +int trace_bone_id; /* 1-based. 0 if not known. typically needs MOVE_HITMODEL. */ +int trace_triangle_id; /* 1-based. 0 if not known. */ +vector global_gravitydir = '0 0 -1'; /* The direction gravity should act in if not otherwise specified per entity. */ +int serverid; /* The unique id of this server within the server cluster. */ +.vector punchangle; +.float gravity; +.float hull; /* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */ +.entity movechain; /* This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity. */ +.void() chainmoved; /* Called when the entity is moved as a result of being part of another entity's .movechain */ +.void(float old, float new) contentstransition; /* This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own. */ +.float dimension_solid; /* This is the bitmask of dimensions which the entity is solid within. */ +.float dimension_hit; /* This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through. */ +.int hitcontentsmaski; /* Traces performed for this entity will impact against surfaces that match this contents mask. */ +.float dphitcontentsmask; /* Some crappy field that inefficiently requires translating to the native contents flags. Ditch the 'dp', do it properly. */ +.float scale; /* Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16. */ +.float fatness; /* How many QuakeUnits to push the entity's verticies along their normals by. */ +.float alpha; /* The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility. */ +.float modelflags; /* Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile. */ +.float frame1time; /* This controls the time into the framegroup/animation named by .frame, you should increment this value according to frametime or to distance moved, depending on the sort of animation you're attempting. You may wish to avoid incrementing this while lerpfrac is still changing, to avoid wasting parts of the animation. */ +.float basebone; /* The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects. */ +.float baseframe; /* See basebone */ +.void() customphysics; /* Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate. */ +.entity tag_entity; +.float tag_index; +.float skeletonindex; /* This object serves as a container for the skeletal bone states used to override the animation data. */ +.vector colormod; /* Provides a colour tint for the entity. */ +.vector glowmod; +.vector gravitydir; /* Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings. */ +.vector(vector org, vector ang) camera_transform; /* Provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */ +.float pmove_flags; +.float geomtype; +.float friction; +.float erp; +.float jointtype; +.float mass; +.float bouncefactor; +.float bouncestop; +.float idealpitch; +.float pitch_speed; +.float drawflags; /* Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE. */ +.float abslight; /* Allows overriding light levels. Use drawflags to state that this field should actually be used. */ +.vector color; /* This affects the colour of realtime lights that were enabled via the pflags field. */ +.float light_lev; /* This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field). */ +.float style; /* Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights. */ +.float pflags; /* Realtime lighting flags */ +.float maxspeed; +.entity view2; /* defines a second viewpoint, typically displayed in a corner of the screen (also punches open pvs). */ +.vector movement; /* These are the directions that the player is currently trying to move in (ie: which +forward/+moveright/+moveup etc buttons they have held), expressed relative to that player's angles. Order is forward, right, up. */ +.float vw_index; /* This acts as a second modelindex, using the same frames etc. */ +.entity nodrawtoclient; /* This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */ +.entity drawonlytoclient; /* This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */ +.entity viewmodelforclient; /* This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model. */ +.entity exteriormodeltoclient; /* This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity. */ +.float button3; /* DP_INPUTBUTTONS (note in qw, we set 1 to equal 3, to match zquake/fuhquake/mvdsv) */ +.float button4; +.float button5; +.float button6; +.float button7; +.float button8; +.float glow_size; +.float glow_color; +.float glow_trail; +.float traileffectnum; /* This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves. */ +.float emiteffectnum; /* This should be set to the result of particleeffectnum, in order to continually spawn particles in the direction that this entity faces. */ +.float dimension_see; /* This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible. */ +.float dimension_seen; /* This is the dimension mask (bitfield) that the client is visible within. Clients that cannot see this dimension mask will not see this entity. */ +.float dimension_ghost; /* If this entity is visible only within these dimensions, it will become transparent, as if a ghost. */ +.float dimension_ghost_alpha; /* If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead. */ +.float(entity playerent, float changedflags) SendEntity; /* Called by the engine whenever an entity needs to be (re)sent to a client's csprogs, either because SendFlags was set or because data was lost. Must write its data to the MSG_ENTITY buffer. Will be called at the engine's leasure. */ +.float SendFlags; /* Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method. */ +.float Version; /* Obsolete, set a SendFlags bit instead. */ +.float clientcolors; +.float viewzoom; +.float items2; +.float playerclass; +.float hasted; +.float light_level; /* Used by hexen2 to indicate the light level where the player is standing. */ +.float pvsflags; /* Reconfigures when the entity is visible to clients */ +.float uniquespawnid; /* Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient. */ +.float() customizeentityforclient; /* Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see. */ +void(float reqid, float responsecode, string resourcebody) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ +void() SpectatorConnect; /* Called when a spectator joins the game. */ +void() SpectatorDisconnect; /* Called when a spectator disconnects from the game. */ +void() SpectatorThink; /* Called each frame for each spectator. */ +void(string cmd) SV_ParseClientCommand; /* Provides QC with a way to intercept 'cmd foo' commands from the client. Very handy. Self will be set to the sending client, while the 'cmd' argument can be tokenize()d and each element retrieved via argv(argno). Unrecognised cmds MUST be passed on to the clientcommand builtin. */ +void(string dest, string from, string cmd, string info) SV_ParseClusterEvent; /* Part of cluster mode. Handles cross-node events that were sent via clusterevent, on behalf of the named client. */ +float(string sender, string body) SV_ParseConnectionlessPacket; /* Provides QC with a way to communicate between servers, or with client server browsers. Sender is the sender's ip. Body is the body of the message. You'll need to add your own password/etc support as required. Self is not valid. */ +void(float pauseduration) SV_PausedTic; /* For each frame that the server is paused, this function will be called to give the gamecode a chance to unpause the server again. the pauseduration argument says how long the server has been paused for (the time global is frozen and will not increment while paused). Self is not valid. */ +float(float newstatus) SV_ShouldPause; /* Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event. */ +void() SV_RunClientCommand; /* Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs. */ +void() SV_AddDebugPolygons; /* Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server. */ +void() SV_PlayerPhysics; /* Legacy method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful. */ +void() EndFrame; /* Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame. */ +string(string addr, string uinfo, string features) SV_CheckRejectConnection; /* Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments. */ +string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) Cef_GeneratePage; /* Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are +-separated key/value pairs (use tokenizebyseparator). */ +void(float prevprogs) init; /* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */ +void() initents; /* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */ +float parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32; +float parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48; +float parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64; +var float dimension_send; /* Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero. */ +//var float dimension_default = 255; +/* Default dimension bitmask */ +__used var float physics_mode = 2; /* 0: original csqc - physics are not run +1: DP-compat. Thinks occur, but not true movetypes. +2: movetypes occur just as they do in ssqc. */ +const float TRUE = 1; +const float FALSE = 0; /* File not found... */ +const float M_PI = 3.14159; +const float MOVETYPE_NONE = 0; +const float MOVETYPE_WALK = 3; +const float MOVETYPE_STEP = 4; +const float MOVETYPE_FLY = 5; +const float MOVETYPE_TOSS = 6; +const float MOVETYPE_PUSH = 7; +const float MOVETYPE_NOCLIP = 8; +const float MOVETYPE_FLYMISSILE = 9; +const float MOVETYPE_BOUNCE = 10; +const float MOVETYPE_BOUNCEMISSILE = 11; +const float MOVETYPE_FOLLOW = 12; +const float MOVETYPE_6DOF = 30; /* A glorified MOVETYPE_FLY. Players using this movetype will get some flightsim-like physics, with fully independant rotations (order-dependant transforms). */ +const float MOVETYPE_WALLWALK = 31; /* Players using this movetype will be able to orient themselves to walls, and then run up them. */ +const float MOVETYPE_PHYSICS = 32; /* Enable the use of ODE physics upon this entity. */ +const float SOLID_NOT = 0; +const float SOLID_TRIGGER = 1; +const float SOLID_BBOX = 2; +const float SOLID_SLIDEBOX = 3; +const float SOLID_BSP = 4; /* Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH. */ +const float SOLID_CORPSE = 5; /* Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .solid value to SOLID_BBOX or so, perform the traceline, then revert the player's .solid value. */ +const float SOLID_LADDER = 20; /* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */ +const float SOLID_PORTAL = 21; /* CSG subtraction volume combined with entity transformations on impact. */ +const float SOLID_PHYSICS_BOX = 32; +const float SOLID_PHYSICS_SPHERE = 33; +const float SOLID_PHYSICS_CAPSULE = 34; +const float SOLID_PHYSICS_TRIMESH = 35; +const float SOLID_PHYSICS_CYLINDER = 36; +const float GEOMTYPE_NONE = -1; +const float GEOMTYPE_SOLID = 0; +const float GEOMTYPE_BOX = 1; +const float GEOMTYPE_SPHERE = 2; +const float GEOMTYPE_CAPSULE = 3; +const float GEOMTYPE_TRIMESH = 4; +const float GEOMTYPE_CYLINDER = 5; +const float GEOMTYPE_CAPSULE_X = 6; +const float GEOMTYPE_CAPSULE_Y = 7; +const float GEOMTYPE_CAPSULE_Z = 8; +const float GEOMTYPE_CYLINDER_X = 9; +const float GEOMTYPE_CYLINDER_Y = 10; +const float GEOMTYPE_CYLINDER_Z = 11; +const float JOINTTYPE_FIXED = -1; +const float JOINTTYPE_POINT = 1; +const float JOINTTYPE_HINGE = 2; +const float JOINTTYPE_SLIDER = 3; +const float JOINTTYPE_UNIVERSAL = 4; +const float JOINTTYPE_HINGE2 = 5; +const float DAMAGE_NO = 0; +const float DAMAGE_YES = 1; +const float DAMAGE_AIM = 2; +const float CONTENT_EMPTY = -1; +const float CONTENT_SOLID = -2; +const float CONTENT_WATER = -3; +const float CONTENT_SLIME = -4; +const float CONTENT_LAVA = -5; +const float CONTENT_SKY = -6; +const float CONTENT_LADDER = -16; /* If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume. */ +const int CONTENTBIT_NONE = 0x00000000i; +const int CONTENTBIT_SOLID = 0x00000001i; +const int CONTENTBIT_LAVA = 0x00000008i; +const int CONTENTBIT_SLIME = 0x00000010i; +const int CONTENTBIT_WATER = 0x00000020i; +const int CONTENTBIT_FTELADDER = 0x00004000i; +const int CONTENTBIT_PLAYERCLIP = 0x00010000i; +const int CONTENTBIT_MONSTERCLIP = 0x00020000i; +const int CONTENTBIT_BODY = 0x02000000i; +const int CONTENTBIT_CORPSE = 0x04000000i; +const int CONTENTBIT_Q2LADDER = 0x20000000i; /* Content bit specific to q2bsp */ +const int CONTENTBIT_SKY = 0x80000000i; +const int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY; /* Bits that traceline would normally consider solid */ +const int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP; /* Bits that tracebox would normally consider solid */ +const int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY; +const int SPA_POSITION; /* These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin */ +const int SPA_S_AXIS = 1; +const int SPA_T_AXIS = 2; +const int SPA_R_AXIS = 3; /* aka: SPA_NORMAL */ +const int SPA_TEXCOORDS0 = 4; +const int SPA_LIGHTMAP0_TEXCOORDS = 5; +const int SPA_LIGHTMAP0_COLOR = 6; +const float CHAN_AUTO = 0; /* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */ +const float CHAN_WEAPON = 1; +const float CHAN_VOICE = 2; +const float CHAN_ITEM = 3; +const float CHAN_BODY = 4; +const float CHANF_RELIABLE = 8; /* Only valid if the flags argument is not specified. The sound will be sent reliably, which is important if it is intended to replace looping sounds on doors etc. */ +const float SOUNDFLAG_RELIABLE = 1; /* The sound will be sent reliably, and without regard to phs. */ +const float SOUNDFLAG_FORCELOOP = 2; /* The sound will restart once it reaches the end of the sample. */ +const float SOUNDFLAG_NOREVERB = 32; /* Disables the use of underwater/reverb effects on this sound effect. */ +const float SOUNDFLAG_FOLLOW = 64; /* The sound's origin will updated to follow the emitting entity. */ +const float SOUNDFLAG_UNICAST = 256; /* The sound will be heard only by the player specified by msg_entity. */ +const float SOUNDFLAG_SENDVELOCITY = 512; /* The entity's current velocity will be sent to the client, only useful if doppler is enabled. */ +const float ATTN_NONE = 0; /* Sounds with this attenuation can be heard throughout the map */ +const float ATTN_NORM = 1; /* Standard attenuation */ +const float ATTN_IDLE = 2; /* Extra attenuation so that sounds don't travel too far. */ +const float ATTN_STATIC = 3; /* Even more attenuation to avoid torches drowing out everything else throughout the map. */ +const float SVC_CGAMEPACKET = 83; /* Direct ssqc->csqc message. Must only be multicast. The data triggers a CSQC_Parse_Event call in the csqc for the csqc to read the contents. The server *may* insert length information for clients connected via proxies which are not able to cope with custom csqc payloads. This should only ever be used in conjunction with the MSG_MULTICAST destination. */ +const float MSG_BROADCAST = 0; /* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */ +const float MSG_ONE = 1; /* The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client. */ +const float MSG_ALL = 2; /* The byte(s) will be reliably sent to all players. */ +const float MSG_INIT = 3; /* The byte(s) will be written into the signon buffer. Clients will see these messages when they connect later. This buffer is only flushed on map changes, so spamming it _WILL_ result in overflows. */ +const float MSG_MULTICAST = 4; /* The byte(s) will be written into the multicast buffer for more selective sending. Messages sent this way will never be split across packets, and using this for csqc-only messages will not break protocol translation. */ +const float MSG_ENTITY = 5; /* The byte(s) will be written into the entity buffer. This is a special value used only inside 'SendEntity' functions. */ +const float MULTICAST_ALL = 0; /* The multicast message is unreliably sent to all players. MULTICAST_ constants are valid arguments for the multicast builtin, which ignores the specified origin when given this constant. */ +const float MULTICAST_PHS = 1; /* The multicast message is unreliably sent to only players that can potentially hear the specified origin. Its quite loose. */ +const float MULTICAST_PVS = 2; /* The multicast message is unreliably sent to only players that can potentially see the specified origin. */ +const float MULTICAST_ONE = 6; /* The multicast message is unreliably sent to the player specified in the msg_entity global. The specified origin is ignored. */ +const float MULTICAST_ALL_R = 3; /* The multicast message is reliably sent to all players. The specified origin is ignored. */ +const float MULTICAST_PHS_R = 4; /* The multicast message is reliably sent to only players that can potentially hear the specified origin. Players might still not receive it if they are out of range. */ +const float MULTICAST_PVS_R = 5; /* The multicast message is reliably sent to only players that can potentially see the specified origin. Players might still not receive it if they cannot see the event. */ +const float MULTICAST_ONE_R = 7; /* The multicast message is reliably sent to the player specified in the msg_entity global. The specified origin is ignored */ +const float PRINT_LOW = 0; +const float PRINT_MEDIUM = 1; +const float PRINT_HIGH = 2; +const float PRINT_CHAT = 3; +const float PVSF_NORMALPVS = 0; /* Filter first by PVS, then filter this entity using tracelines if sv_cullentities is enabled. */ +const float PVSF_NOTRACECHECK = 1; /* Filter strictly by PVS. */ +const float PVSF_USEPHS = 2; /* Send if we're close enough to be able to hear this entity. */ +const float PVSF_IGNOREPVS = 3; /* Ignores pvs. This entity is visible whereever you are on the map. */ +const float PVSF_NOREMOVE = 128; /* Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses. */ +const string INFOKEY_P_IP = "ip"; /* The apparent ip address of the client. This may be a proxy's ip address. */ +const string INFOKEY_P_REALIP = "realip"; /* If sv_getrealip is set, this gives the ip as determine using that algorithm. */ +const string INFOKEY_P_CSQCACTIVE = "csqcactive"; /* Client has csqc enabled. CSQC ents etc will be sent to this player. */ +const string INFOKEY_P_SVPING = "svping"; +const string INFOKEY_P_GUID = "guid"; /* Some hash string which should be reasonably unique to this player's quake installation. */ +const string INFOKEY_P_CHALLENGE = "challenge"; +const string INFOKEY_P_USERID = "*userid"; +const string INFOKEY_P_DOWNLOADPCT = "download"; /* The client's download percentage for the current file. Additional files are not known. */ +const string INFOKEY_P_TRUSTLEVEL = "trustlevel"; +const string INFOKEY_P_PROTOCOL = "protocol"; /* The network protocol the client is using to connect to the server. */ +const string INFOKEY_P_VIP = "*VIP"; /* 1 if the player has the VIP 'penalty'. */ +const string INFOKEY_P_ISMUTED = "*ismuted"; /* 1 if the player has the 'mute' penalty and is not allowed to use the say/say_team commands. */ +const string INFOKEY_P_ISDEAF = "*isdeaf"; /* 1 if the player has the 'deaf' penalty and cannot see other people's say/say_team commands. */ +const string INFOKEY_P_ISCRIPPLED = "*ismuted"; /* 1 if the player has the cripple penalty, and their movement values are ignored (.movement is locked to 0). */ +const string INFOKEY_P_ISCUFFED = "*ismuted"; /* 1 if the player has the cuff penalty, and is unable to attack or use impulses(.button0 and .impulse fields are locked to 0). */ +const string INFOKEY_P_ISLAGGED = "*ismuted"; /* 1 if the player has the fakelag penalty and has an extra 200ms of lag. */ +const string INFOKEY_P_PING = "ping"; /* The player's ping time, in milliseconds. */ +const string INFOKEY_P_NAME = "name"; /* The player's name. */ +const string INFOKEY_P_SPECTATOR = "*spectator"; /* Whether the player is a spectator or not. */ +const string INFOKEY_P_TOPCOLOR = "topcolor"; /* The player's upper/shirt colour (palette index). */ +const string INFOKEY_P_BOTTOMCOLOR = "bottomcolor"; /* The player's lower/pants/trouser colour (palette index). */ +const float STUFFCMD_IGNOREINDEMO = 1; /* This stuffcmd will NOT be written to mvds/qtv. */ +const float STUFFCMD_DEMOONLY = 2; /* This stuffcmd will ONLY be written into mvds/qtv streams. */ +const float STUFFCMD_BROADCAST = 4; /* The stuffcmd will be broadcast server-wide (according to the mvd filters). */ +const float STUFFCMD_UNRELIABLE = 8; /* The stuffcmd might not arrive. It might also get there faster than ones sent over the reliable channel. */ +const float FL_FLY = 1; +const float FL_SWIM = 2; +const float FL_CLIENT = 8; +const float FL_INWATER = 16; +const float FL_MONSTER = 32; +const float FL_GODMODE = 64; +const float FL_NOTARGET = 128; +const float FL_ITEM = 256; +const float FL_ONGROUND = 512; +const float FL_PARTIALGROUND = 1024; +const float FL_WATERJUMP = 2048; +const float FL_FINDABLE_NONSOLID = 16384; /* Allows this entity to be found with findradius */ +const float FL_LAGGEDMOVE = 65536; /* Enables anti-lag on rockets etc. */ +const float MOVE_NORMAL = 0; +const float MOVE_NOMONSTERS = 1; /* The trace will ignore all non-solid_bsp entities. */ +const float MOVE_MISSILE = 2; /* The trace will use a bbox size of +/- 15 against entities with FL_MONSTER set. */ +const float MOVE_HITMODEL = 4; /* Traces will impact the actual mesh of the model instead of merely their bounding box. Should generally only be used for tracelines. Note that this flag is unreliable as an object can animate through projectiles. The bounding box MUST be set to completely encompass the entity or those extra areas will be non-solid (leaving a hole for things to go through). */ +const float MOVE_TRIGGERS = 16; /* This trace type will impact only triggers. It will ignore non-solid entities. */ +const float MOVE_EVERYTHING = 32; /* This type of trace will hit solids and triggers alike. Even non-solid entities. */ +const float MOVE_LAGGED = 64; /* Will use antilag based upon the player's latency. Traces will be performed against old positions for entities instead of their current origin. */ +const float MOVE_ENTCHAIN = 128; /* Returns a list of entities impacted via the trace_ent.chain field */ +const float MOVE_OTHERONLY = 256; /* Traces that use this trace type will collide against *only* the entity specified via the 'other' global, and will ignore all owner/solid_not/dimension etc rules, they will still adhere to contents and bsp/bbox rules though. */ +const float RESTYPE_MODEL = 0; /* RESTYPE_* constants are used as arguments with the resourcestatus builtin. */ +const float RESTYPE_SOUND = 1; /* precache_sound */ +const float RESTYPE_PARTICLE = 2; /* particleeffectnum */ +const float RESSTATE_NOTKNOWN = 0; /* RESSTATE_* constants are return values from the resourcestatus builtin. The engine doesn't know about the resource if it is in this state. This means you will need to precache it. Attempting to use it anyway may result in warnings, errors, or silently succeed, depending on engine version and resource type. */ +const float RESSTATE_NOTLOADED = 1; /* The resource was precached, but has been flushed and there has not been an attempt to reload it. If you use the resource normally, chances are it'll be loaded but at the cost of a stall. */ +const float RESSTATE_LOADING = 2; /* Resources in this this state are queued for loading, and will be loaded at the engine's convienience. If you attempt to query the resource now, the engine will stall until the result is available. sounds in this state may be delayed, while models/pics/shaders may be invisible. */ +const float RESSTATE_FAILED = 3; /* Resources in this state are unusable/could not be loaded. You will get placeholders or dummy results. Queries will not stall the engine. The engine may display placeholder content. */ +const float RESSTATE_LOADED = 4; /* Resources in this state are finally usable, everything will work okay. Hurrah. Queries will not stall the engine. */ +const float EF_BRIGHTFIELD = 1; +const float EF_BRIGHTLIGHT = 4; +const float EF_DIMLIGHT = 8; +const float EF_FLAG1 = 16; +const float EF_FLAG2 = 32; +const float EF_BLUE = 64; /* A blue glow */ +const float EF_RED = 128; /* A red glow */ +const float EF_GREEN = 262144; /* A green glow */ +const float EF_ORANGE = 524288; /* A orange glow */ +const float EF_FULLBRIGHT = 512; /* This entity will ignore lighting */ +const float EF_NOSHADOW = 4096; /* This entity will not cast shadows */ +const float EF_NODEPTHTEST = 8192; /* This entity will be drawn over the top of other things that are closer. */ +const float EF_NOMODELFLAGS = 8388608; /* Surpresses the normal flags specified in the model. */ +const float MF_ROCKET = 1; +const float MF_GRENADE = 2; +const float MF_GIB = 4; /* Regular blood trail */ +const float MF_ROTATE = 8; +const float MF_TRACER = 16; /* AKA: green scrag trail */ +const float MF_ZOMGIB = 32; /* Dark blood trail */ +const float MF_TRACER2 = 64; /* AKA: hellknight projectile trail */ +const float MF_TRACER3 = 128; /* AKA: purple vore trail */ +const float SL_ORG_TL = 20; /* Used with showpic etc, specifies that the x+y values are relative to the top-left of the screen */ +const float SL_ORG_TR = 21; +const float SL_ORG_BL = 22; +const float SL_ORG_BR = 23; +const float SL_ORG_MM = 24; +const float SL_ORG_TM = 25; +const float SL_ORG_BM = 26; +const float SL_ORG_ML = 27; +const float SL_ORG_MR = 28; +const float PFLAGS_NOSHADOW = 1; /* Associated RT lights attached will not cast shadows, making them significantly faster to draw. */ +const float PFLAGS_CORONA = 2; /* Enables support of coronas on the associated rtlights. */ +const float PFLAGS_FULLDYNAMIC = 128; /* When set in self.pflags, enables fully-customised dynamic lights. Custom rtlight information is not otherwise used. */ +const float EV_STRING = 1; +const float EV_FLOAT = 2; +const float EV_VECTOR = 3; +const float EV_ENTITY = 4; +const float EV_FIELD = 5; +const float EV_FUNCTION = 6; +const float EV_POINTER = 7; +const float EV_INTEGER = 8; +const float EV_VARIANT = 9; +hashtable gamestate; /* Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted). */ +const float HASH_REPLACE = 256; /* Used with hash_add. Attempts to remove the old value instead of adding two values for a single key. */ +const float HASH_ADD = 512; /* Used with hash_add. The new entry will be inserted in addition to the existing entry. */ +const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */ +const float CLIENTTYPE_DISCONNECTED = 0; /* Return value from clienttype() builtin. This entity is a player slot that is currently empty. */ +const float CLIENTTYPE_REAL = 1; /* This is a real player, and not a bot. */ +const float CLIENTTYPE_BOT = 2; /* This player slot does not correlate to a real player, any messages sent to this client will be ignored. */ +const float CLIENTTYPE_NOTACLIENT = 3; /* This entity is not even a player slot. This is typically an error condition. */ +const float FILE_READ = 0; /* The file may be read via fgets to read a single line at a time. */ +const float FILE_APPEND = 1; /* Like FILE_WRITE, but writing starts at the end of the file. */ +const float FILE_WRITE = 2; /* fputs will be used to write to the file. */ +const float FILE_READNL = 4; /* Like FILE_READ, except newlines are not special. fgets reads the entire file into a tempstring. */ +const float FILE_MMAP_READ = 5; /* The file will be loaded into memory. fgets returns a pointer to the first byte (and will always return the same value for this file). Cast this to your datatype. */ +const float FILE_MMAP_RW = 6; /* Like FILE_MMAP_READ, except any changes to the data will be written back to disk once the file is closed. */ +void(vector vang) makevectors = #1; /* + Takes an angle vector (pitch,yaw,roll) (+x=DOWN). Writes its results into v_forward, v_right, v_up vectors. */ + +void(entity e, vector o) setorigin = #2; /* + Changes e's origin to be equal to o. Also relinks collision state (as well as setting absmin+absmax), which is required after changing .solid */ + +void(entity e, string m) setmodel = #3; /* + Looks up m in the model precache list, and sets both e.model and e.modelindex to match. BSP models will set e.mins and e.maxs accordingly, other models depend upon the value of sv_gameplayfix_setmodelrealbox - for compatibility you should always call setsize after all pickups or non-bsp models. Also relinks collision state. */ + +void(entity e, vector min, vector max) setsize = #4; /* + Sets the e's mins and maxs fields. Also relinks collision state, which sets absmin and absmax too. */ + +void() breakpoint = #6; /* + Trigger a debugging event. FTE will break into the qc debugger. Other engines may crash with a debug execption. */ + +float() random = #7; /* + Returns a random value between 0 and 1. Be warned, this builtin can return 1 in most engines, which can break arrays. */ + +void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8; /* + Starts a sound centered upon the given entity. + chan is the entity sound channel to use, channel 0 will allow you to mix many samples at once, others will replace the old sample + 'samp' must have been precached first + if specified, 'speedpct' should normally be around 100 (or =0), 200 for double speed or 50 for half speed. + If flags is specified, the reliable flag in the channels argument is used for additional channels. Flags should be made from SOUNDFLAG_* constants + timeofs should be negative in order to provide a delay before the sound actually starts. */ + +vector(vector v) normalize = #9; /* + Shorten or lengthen a direction vector such that it is only one quake unit long. */ + +void(string e) error = #10; /* + Ends the game with an easily readable error message. */ + +void(string e) objerror = #11; /* + Displays a non-fatal easily readable error message concerning the self entity, including a field dump. self will be removed! */ + +float(vector v) vlen = #12; /* + Returns the square root of the dotproduct of a vector with itself. Or in other words the length of a distance vector, in quake units. */ + +float(vector v, optional entity reference) vectoyaw = #13; /* + Given a direction vector, returns the yaw angle in which that direction vector points. If an entity is passed, the yaw angle will be relative to that entity's gravity direction. */ + +entity() spawn = #14; /* + Adds a brand new entity into the world! Hurrah, you're now a parent! */ + +void(entity e) remove = #15; /* + Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */ + +void(vector v1, vector v2, float flags, entity ent) traceline = #16; /* + Traces a thin line through the world from v1 towards v2. + Will not collide with ent, ent.owner, or any entity who's owner field refers to ent. + The passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace. + There are no side effects beyond the trace_* globals being written. + flags&MOVE_NOMONSTERS will not impact on non-bsp entities. + flags&MOVE_MISSILE will impact with increased size. + flags&MOVE_HITMODEL will impact upon model meshes, instead of their bounding boxes. + flags&MOVE_TRIGGERS will also stop on triggers + flags&MOVE_EVERYTHING will stop if it hits anything, even non-solid entities. + flags&MOVE_LAGGED will backdate entity positions for the purposes of this builtin according to the indicated player ent's latency, to provide lag compensation. */ + +entity() checkclient = #17; /* + Returns one of the player entities. The returned player will change periodically. */ + +entity(entity start, .string fld, string match) find = #18; /* + Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more. + If you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */ + +string(string s) precache_sound = #19; /* + Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard. */ + +string(string s) precache_model = #20; /* + Precaches a model, making it known to clients and loading it from disk if it has a .bsp extension. This builtin (strongly) should be called during spawn functions. This must be called for each model name before setmodel may use that model name. + Modelindicies precached in SSQC will always be positive. CSQC precaches will be negative if they are not also on the server. */ + +void(entity client, string s) stuffcmd = #21; /* + Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \n. + This builtin is generally considered evil. */ + +void(entity client, float flags, string s) stuffcmdflags = #0:stuffcmdflags; /* Part of FTE_QC_STUFFCMDFLAGS + Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \n. + This (just as evil) variant allows specifying some flags too. See the STUFFCMD_* constants. */ + +entity(vector org, float rad, optional .entity chainfield) findradius = #22; /* + Finds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. */ + +void(float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) bprint = #23; /* + QW: Concatenates all string arguments, and prints the messsage on the console of only all clients who's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument. */ + +void(entity client, float msglvl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6) sprint = #24; /* + QW: Concatenates all string arguments, and prints the messsage on the named client's console, but only if that client's 'msg' infokey is set lower or equal to the supplied 'msglvl' argument. */ + +void(string s, ...) dprint = #25; /* + QW: Unconditionally prints the given message on the server's console. Arguments will be concatenated into a single message. */ + +string(float val) ftos = #26; /* + Returns a tempstring containing a representation of the given float. Precision depends upon engine. */ + +string(vector val) vtos = #27; /* + Returns a tempstring containing a representation of the given vector. Precision depends upon engine. */ + +void() coredump = #28; /* + Writes out a coredump. This contains stack, globals, and field info for all ents. This can be handy for debugging. */ + +void() traceon = #29; /* + Enables tracing. This may be spammy, slow, and stuff. Set debugger 1 in order to use fte's qc debugger. */ + +void() traceoff = #30; /* + Disables tracing again. */ + +void(entity e) eprint = #31; /* + Debugging builtin that prints all fields of the given entity to the console. */ + +float(float yaw, float dist, optional float settraceglobals) walkmove = #32; /* + Attempt to walk the entity at a given angle for a given distance. + if settraceglobals is set, the trace_* globals will be set, showing the results of the movement. + This function will trigger touch events. */ + +float() droptofloor = #34; /* + Instantly moves the entity downwards until it hits the ground. If the entity is in solid or would need to drop more than 'pr_droptofloorunits' quake units, its position will be considered invalid and the builtin will abort, returning FALSE, otherwise TRUE. */ + +void(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35; /* + Specifies an auto-animating string that specifies the light intensity for entities using that lightstyle. + a is off, z is fully lit. Should be lower case only. + rgb will recolour all lights using that lightstyle. */ + +float(float) rint = #36; /* + Rounds the given float up or down to the closest integeral value. X.5 rounds away from 0 */ + +float(float) floor = #37; /* + Rounds the given float downwards, even when negative. */ + +float(float) ceil = #38; /* + Rounds the given float upwards, even when negative. */ + +float(entity ent) checkbottom = #40; /* + Expensive checks to ensure that the entity is actually sitting on something solid, returns true if it is. */ + +float(vector pos) pointcontents = #41; /* + Checks the given point to see what is there. Returns one of the SOLID_* constants. Just because a spot is empty does not mean that the player can stand there due to the size of the player - use tracebox for such tests. */ + +float(float) fabs = #43; /* + Removes the sign of the float, making it positive if it is negative. */ + +vector(entity player, float missilespeed) aim = #44; /* + Returns a direction vector (specifically v_forward on error). This builtin attempts to guess what pitch angle to fire projectiles at for people that don't know about mouselook. Does not affect yaw angles. */ + +float(string) cvar = #45; /* + Returns the numeric value of the named cvar */ + +void(string, ...) localcmd = #46; /* + Adds the string to the console command queue. Commands will not be executed immediately, but rather at the start of the following frame. */ + +entity(entity) nextent = #47; /* + Returns the following entity. Skips over removed entities. Returns world when passed the last valid entity. */ + +void(vector pos, vector dir, float colour, float count) particle = #48; /* + Spawn 'count' particles around 'pos' moving in the direction 'dir', with a palette colour index between 'colour' and 'colour+8'. */ + +#define ChangeYaw changeyaw +void() changeyaw = #49; /* + Changes the self.angles_y field towards self.ideal_yaw by up to self.yaw_speed. */ + +vector(vector fwd, optional vector up) vectoangles = #51; /* + Returns the angles (+x=UP) required to orient an entity to look in the given direction. The 'up' argument is required if you wish to set a roll angle, otherwise it will be limited to just monster-style turning. */ + +void(float to, float val) WriteByte = #52; /* + Writes a single byte into a network message buffer. Typically you will find a more correct alternative to writing arbitary data. 'to' should be one of the MSG_* constants. MSG_ONE must have msg_entity set first. */ + +void(float to, float val) WriteChar = #53; +void(float to, float val) WriteShort = #54; +void(float to, float val) WriteLong = #55; +void(float to, float val) WriteCoord = #56; +void(float to, float val) WriteAngle = #57; +void(float to, string val) WriteString = #58; +void(float to, entity val) WriteEntity = #59; +float(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW + Forgive me father, for I have trigonometry homework. */ + +float(float angle) cos = #61; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float value) sqrt = #62; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float a, float n) modulo = #0:modulo; +void(entity ent) changepitch = #63; /* Part of DP_QC_CHANGEPITCH*/ +void(entity ent, entity ignore) tracetoss = #64; +string(entity ent) etos = #65; /* Part of DP_QC_ETOS*/ +void(float step) movetogoal = #67; /* + Runs lots and lots of fancy logic in order to try to step the entity the specified distance towards its goalentity. */ + +string(string s) precache_file = #68; /* + This builtin does nothing. It was used only as a hint for pak generation. */ + +void(entity e) makestatic = #69; /* + Sends a copy of the entity's renderable fields to all clients, and REMOVES the entity, preventing further changes. This means it will be unmutable and non-solid. */ + +void(string mapname, optional string newmapstartspot) changelevel = #70; /* + Attempts to change the map to the named map. If 'newmapstartspot' is specified, the state of the current map will be preserved, and the argument will be passed to the next map in the 'startspot' global, and the next map will be loaded from archived state if it was previously visited. If not specified, all archived map states will be purged. */ + +void(string cvarname, string valuetoset) cvar_set = #72; /* + Instantly sets a cvar to the given string value. */ + +void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7) centerprint = #73; +void (vector pos, string samp, float vol, float atten) ambientsound = #74; +string(string str) precache_model2 = #75; +string(string str) precache_sound2 = #76; +string(string str) precache_file2 = #77; +void(entity player) setspawnparms = #78; +void(entity killer, entity killee) logfrag = #79; /* Part of QW_ENGINE*/ +string(entity e, string key) infokey = #80; /* Part of FTE_QC_INFOKEY, QW_ENGINE + If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */ + +float(entity e, string key) infokeyf = #0:infokeyf; /* + Identical to regular infokey, except returns a float. */ + +float(string) stof = #81; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ +#define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0) +void(vector where, float set) multicast = #82; /* Part of FTE_QC_MULTICAST + Once the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth. */ + +void(entity to, string str) redirectcmd = #101; /* Part of ??MVDSV_BUILTINS + Executes a single console command, and sends the text generated by it to the specified player. The command will be executed at the end of the frame once QC is no longer running - you may wish to pre/postfix it with 'echo'. */ + +string(float style, optional __out vector rgb) getlightstyle = #0:getlightstyle; /* + Obtains the light style string for the given style. */ + +vector(float style) getlightstylergb = #0:getlightstylergb; /* + Obtains the current rgb value of the specified light style. In csqc, this is correct with regard to the current frame, while ssqc gives no guarentees about time and ignores client cvars. Note: use getlight if you want the actual light value at a point. */ + +void(float style, float val, optional vector rgb) lightstylestatic = #5; /* + Sets the lightstyle to an explicit numerical level. From Hexen2. */ + +void(vector start, vector mins, vector maxs, vector end, float nomonsters, entity ent) tracebox = #90; /* Part of DP_QC_TRACEBOX + Exactly like traceline, but a box instead of a uselessly thin point. Acceptable sizes are limited by bsp format, q1bsp has strict acceptable size values. */ + +vector() randomvec = #91; /* Part of DP_QC_RANDOMVEC + Returns a vector with random values. Each axis is independantly a value between -1 and 1 inclusive. */ + +vector(vector org) getlight = #92; +float(string cvarname, string defaultvalue) registercvar = #93; /* Part of DP_REGISTERCVAR + Creates a new cvar on the fly. If it does not already exist, it will be given the specified value. If it does exist, this is a no-op. + This builtin has the limitation that it does not apply to configs or commandlines. Such configs will need to use the set or seta command causing this builtin to be a noop. + In engines that support it, you will generally find the autocvar feature easier and more efficient to use. */ + +float(float a, float b, ...) min = #94; /* Part of DP_QC_MINMAXBOUND + Returns the lowest value of its arguments. */ + +float(float a, float b, ...) max = #95; /* Part of DP_QC_MINMAXBOUND + Returns the highest value of its arguments. */ + +float(float minimum, float val, float maximum) bound = #96; /* Part of DP_QC_MINMAXBOUND + Returns val, unless minimum is higher, or maximum is less. */ + +float(float value, float exp) pow = #97; /* Part of DP_QC_SINCOSSQRTPOW*/ +float(float v, optional float base) logarithm = #0:logarithm; /* + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + +#define findentity findfloat +entity(entity start, .__variant fld, __variant match) findfloat = #98; /* Part of DP_QC_FINDFLOAT + Equivelent to the find builtin, but instead of comparing strings contents, this builtin compares the raw values. This builtin requires multiple calls in order to scan all entities - set start to the previous call's return value. + world is returned when there are no more entities. */ + +float(string extname) checkextension = #99; /* + Checks for an extension by its name (eg: checkextension("FRIK_FILE") says that its okay to go ahead and use strcat). + Use cvar("pr_checkextension") to see if this builtin exists. */ + +float(__variant funcref) checkbuiltin = #0:checkbuiltin; /* + Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available). */ + +float(string builtinname) builtin_find = #100; /* + Looks to see if the named builtin is valid, and returns the builtin number it exists at. */ + +float(float value) anglemod = #102; +void(string slot, string picname, float x, float y, float zone, optional entity player) showpic = #104; /* Part of TEI_SHOWLMP2*/ +void(string slot, optional entity player) hidepic = #105; /* Part of TEI_SHOWLMP2*/ +void(string slot, float x, float y, float zone, optional entity player) movepic = #106; /* Part of TEI_SHOWLMP2*/ +void(string slot, string picname, optional entity player) changepic = #107; /* Part of TEI_SHOWLMP2*/ +filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE + Opens a file, typically prefixed with "data/", for either read or write access. */ + +void(filestream fhandle) fclose = #111; /* Part of FRIK_FILE*/ +string(filestream fhandle) fgets = #112; /* Part of FRIK_FILE + Reads a single line out of the file. The new line character is not returned as part of the string. Returns the null string on EOF (use if not(string) to easily test for this, which distinguishes it from the empty string which is returned if the line being read is blank */ + +void(filestream fhandle, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) fputs = #113; /* Part of FRIK_FILE + Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */ + +int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* Part of FTE_QC_FILE_BINARY + Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */ + +int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* Part of FTE_QC_FILE_BINARY + Writes binary data out of the file. */ + +#define ftell fseek //c compat +int(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* Part of FTE_QC_FILE_BINARY + Changes the current position of the file, if specified. Returns prior position, in bytes. */ + +int(filestream fhandle, optional int newsize) fsize = #0:fsize; /* Part of FTE_QC_FILE_BINARY + Reports the total size of the file, in bytes. Can also be used to truncate/extend the file */ + +float(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +vector(string s) stov = #117; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ +string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS + Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods. */ + +void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS + Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */ + +void(string cvar, float val) cvar_setf = #176; +void(string soundname, optional float channel, optional float volume) localsound = #177; /* + Plays a sound... locally... probably best not to call this from ssqc. Also disables reverb. */ + +float(string modelname, optional float queryonly) getmodelindex = #200; /* + Acts as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex; + If queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */ + +__variant(float prnum, string funcname, ...) externcall = #201; /* Part of FTE_MULTIPROGS + Directly call a function in a different/same progs by its name. + prnum=0 is the 'default' or 'main' progs. + prnum=-1 means current progs. + prnum=-2 will scan through the active progs and will use the first it finds. */ + +float(string progsname) addprogs = #202; /* Part of FTE_MULTIPROGS + Loads an additional .dat file into the current qcvm. The returned handle can be used with any of the externcall/externset/externvalue builtins. + There are cvars that allow progs to be loaded automatically. */ + +__variant(float prnum, string varname) externvalue = #203; /* Part of FTE_MULTIPROGS + Reads a global in the named progs by the name of that global. + prnum=0 is the 'default' or 'main' progs. + prnum=-1 means current progs. + prnum=-2 will scan through the active progs and will use the first it finds. */ + +void(float prnum, __variant newval, string varname) externset = #204; /* Part of FTE_MULTIPROGS + Sets a global in the named progs by name. + prnum=0 is the 'default' or 'main' progs. + prnum=-1 means current progs. + prnum=-2 will scan through the active progs and will use the first it finds. */ + +void(entity portal, float state) openportal = #207; /* + Opens or closes the portals associated with a door or some such on q2 or q3 maps. On Q2BSPs, the entity should be the 'func_areaportal' entity - its style field will say which portal to open. On Q3BSPs, the entity is the door itself, the portal will be determined by the two areas found from a preceding setorigin call. */ + +float(float attributes, string effectname, ...) RegisterTempEnt = #208; /* Part of FTE_PEXT_CUSTOMTENTS*/ +void(float type, vector pos, ...) CustomTempEnt = #209; /* Part of FTE_PEXT_CUSTOMTENTS*/ +float(optional float sleeptime) fork = #210; /* Part of FTE_MULTITHREADED + When called, this builtin simply returns. Twice. + The current 'thread' will return instantly with a return value of 0. The new 'thread' will return after sleeptime seconds with a return value of 1. See documentation for the 'sleep' builtin for limitations/requirements concerning the new thread. Note that QC should probably call abort in the new thread, as otherwise the function will return to the calling qc function twice also. */ + +void(optional __variant ret) abort = #211; /* Part of FTE_MULTITHREADED + QC execution is aborted. Parent QC functions on the stack will be skipped, effectively this forces all QC functions to 'return ret' until execution returns to the engine. If ret is ommited, it is assumed to be 0. */ + +void(float sleeptime) sleep = #212; /* Part of FTE_MULTITHREADED + Suspends the current QC execution thread for 'sleeptime' seconds. + Other QC functions can and will be executed in the interim, including changing globals and field state (but not simultaneously). + The self and other globals will be restored when the thread wakes up (or set to world if they were removed since the thread started sleeping). Locals will be preserved, but will not be protected from remove calls. + If the engine is expecting the QC to return a value (even in the parent/root function), the value 0 shall be used instead of waiting for the qc to resume. */ + +void(entity player, string key, string value) forceinfokey = #213; /* Part of FTE_FORCEINFOKEY + Directly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */ + +void(string filename, float starttag, entity edict) chat = #214; /* Part of FTE_QC_NPCCHAT*/ +void(vector org, vector dmin, vector dmax, float colour, float effect, float count) particle2 = #215; /* Part of FTE_HEXEN2*/ +void(vector org, vector box, float colour, float effect, float count) particle3 = #216; /* Part of FTE_HEXEN2*/ +void(vector org, float radius, float colour, float effect, float count) particle4 = #217; /* Part of FTE_HEXEN2*/ +float(float number, float quantity) bitshift = #218; /* Part of EXT_BITSHIFT*/ +void(vector pos) te_lightningblood = #219; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/ +float(string s1, string sub, optional float startidx) strstrofs = #221; /* Part of FTE_STRINGS + Returns the 0-based offset of sub within the s1 string, or -1 if sub is not in s1. + If startidx is set, this builtin will ignore matches before that 0-based offset. */ + +float(string str, float index) str2chr = #222; /* Part of FTE_STRINGS + Retrieves the character value at offset 'index'. */ + +string(float chr, ...) chr2str = #223; /* Part of FTE_STRINGS + The input floats are considered character values, and are concatenated. */ + +string(float ccase, float redalpha, float redchars, string str, ...) strconv = #224; /* Part of FTE_STRINGS + Converts quake chars in the input string amongst different representations. + ccase specifies the new case for letters. + 0: not changed. + 1: forced to lower case. + 2: forced to upper case. + redalpha and redchars switch between colour ranges. + 0: no change. + 1: Forced white. + 2: Forced red. + 3: Forced gold(low) (numbers only). + 4: Forced gold (high) (numbers only). + 5+6: Forced to white and red alternately. + You should not use this builtin in combination with UTF-8. */ + +string(float pad, string str1, ...) strpad = #225; /* Part of FTE_STRINGS + Pads the string with spaces, to ensure its a specific length (so long as a fixed-width font is used, anyway). If pad is negative, the spaces are added on the left. If positive the padding is on the right. */ + +string(infostring old, string key, string value) infoadd = #226; /* Part of FTE_STRINGS + Returns a new tempstring infostring with the named value changed (or added if it was previously unspecified). Key and value may not contain the \ character. */ + +string(infostring info, string key) infoget = #227; /* Part of FTE_STRINGS + Reads a named value from an infostring. The returned value is a tempstring */ + +#define strcmp strncmp +float(string s1, string s2, optional float len, optional float s1ofs, optional float s2ofs) strncmp = #228; /* Part of FTE_STRINGS + Compares up to 'len' chars in the two strings. s1ofs allows you to treat s2 as a substring to compare against, or should be 0. + Returns 0 if the two strings are equal, a negative value if s1 appears numerically lower, and positive if s1 appears numerically higher. */ + +float(string s1, string s2) strcasecmp = #229; /* Part of FTE_STRINGS + Compares the two strings without case sensitivity. + Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */ + +float(string s1, string s2, float len, optional float s1ofs, optional float s2ofs) strncasecmp = #230; /* Part of FTE_STRINGS + Compares up to 'len' chars in the two strings without case sensitivity. s1ofs allows you to treat s2 as a substring to compare against, or should be 0. + Returns 0 if they are equal. The sign of the return value may be significant, but should not be depended upon. */ + +string(string s) strtrim = #0:strtrim; /* + Trims the whitespace from the start+end of the string. */ + +void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY + Asks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv. + timeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue) + The strftime builtin is more versatile and less weird. */ + +void(float num, float type, .__variant fld) clientstat = #232; /* + Specifies what data to use in order to send various stats, in a client-specific way. + 'num' should be a value between 32 and 127, other values are reserved. + 'type' must be set to one of the EV_* constants, one of EV_FLOAT, EV_STRING, EV_INTEGER, EV_ENTITY. + fld must be a reference to the field used, each player will be sent only their own copy of these fields. */ + +void(float num, float type, string name) globalstat = #233; /* + Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, name however, is the name of the global to read in the form of a string (pass "foo"). */ + +void(float num, float type, __variant *address) pointerstat = #0:pointerstat; /* + Specifies what data to use in order to send various stats, in a non-client-specific way. num and type are as in clientstat, address however, is the address of the variable you would like to use (pass &foo). */ + +float(entity player) isbackbuffered = #234; /* Part of FTE_ISBACKBUFFERED + Returns if the given player's network buffer will take multiple network frames in order to clear. If this builtin returns non-zero, you should delay or reduce the amount of reliable (and also unreliable) data that you are sending to that client. */ + +void(vector angle) rotatevectorsbyangle = #235; /* + rotates the v_forward,v_right,v_up matrix by the specified angles. */ + +void(vector fwd, vector right, vector up) rotatevectorsbyvectors = #236; +float(float mdlindex, string skinname) skinforname = #237; +void(vector org, optional float count) te_bloodqw = #239; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity ent) te_muzzleflash = #0:te_muzzleflash; +float(vector viewpos, entity entity) checkpvs = #240; /* Part of FTE_QC_CHECKPVS*/ +entity(string match, optional float matchnum) matchclientname = #241; /* Part of FTE_QC_MATCHCLIENTNAME*/ +void(string destaddress, string content) sendpacket = #242; /* Part of FTE_QC_SENDPACKET*/ +float(float dividend, float divisor) mod = #245; +float(optional string host, optional string user, optional string pass, optional string defaultdb, optional string driver) sqlconnect = #250; /* Part of FTE_SQL*/ +void(float serveridx) sqldisconnect = #251; /* Part of FTE_SQL*/ +float(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof, float firstrow) callback, float querytype, string query) sqlopenquery = #252; /* Part of FTE_SQL*/ +void(float serveridx, float queryidx) sqlclosequery = #253; /* Part of FTE_SQL*/ +string(float serveridx, float queryidx, float row, float column) sqlreadfield = #254; /* Part of FTE_SQL*/ +string(float serveridx, optional float queryidx) sqlerror = #255; /* Part of FTE_SQL*/ +string(float serveridx, string data) sqlescape = #256; /* Part of FTE_SQL*/ +string(float serveridx) sqlversion = #257; /* Part of FTE_SQL*/ +float(float serveridx, float queryidx, float row, float column) sqlreadfloat = #258; /* Part of FTE_SQL*/ +int(float serveridx, float queryidx, float row, float column, __variant *ptr, int maxsize) sqlreadblob = #0:sqlreadblob; +string(float serveridx, __variant *ptr, int maxsize) sqlescapeblob = #0:sqlescapeblob; +int(string) stoi = #259; /* Part of FTE_QC_INTCONV + Converts the given string into a true integer. Base 8, 10, or 16 is determined based upon the format of the string. */ + +string(int) itos = #260; /* Part of FTE_QC_INTCONV + Converts the passed true integer into a base10 string. */ + +int(string) stoh = #261; /* Part of FTE_QC_INTCONV + Reads a base-16 string (with or without 0x prefix) as an integer. Bugs out if given a base 8 or base 10 string. :P */ + +string(int) htos = #262; /* Part of FTE_QC_INTCONV + Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */ + +int(float) ftoi = #0:ftoi; /* Part of FTE_QC_INTCONV + Converts the given float into a true integer without depending on extended qcvm instructions. */ + +float(int) itof = #0:itof; /* Part of FTE_QC_INTCONV + Converts the given true integer into a float without depending on extended qcvm instructions. */ + +float(float modlindex, optional float useabstransforms) skel_create = #263; /* Part of FTE_CSQC_SKELETONOBJECTS + Allocates a new uninitiaised skeletal object, with enough bone info to animate the given model. + eg: self.skeletonobject = skel_create(self.modelindex); */ + +float(float skel, entity ent, float modelindex, float retainfrac, float firstbone, float lastbone, optional float addfrac) skel_build = #264; /* Part of FTE_CSQC_SKELETONOBJECTS + Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object. + If retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based. */ + +float(float skel) skel_get_numbones = #265; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrives the number of bones in the model. The valid range is 1<=bone<=numbones. */ + +string(float skel, float bonenum) skel_get_bonename = #266; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrieves the name of the specified bone. Mostly only for debugging. */ + +float(float skel, float bonenum) skel_get_boneparent = #267; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrieves which bone this bone's position is relative to. Bone 0 refers to the entity's position rather than an actual bone */ + +float(float skel, string tagname) skel_find_bone = #268; /* Part of FTE_CSQC_SKELETONOBJECTS + Finds a bone by its name, from the model that was used to create the skeletal object. */ + +vector(float skel, float bonenum) skel_get_bonerel = #269; /* Part of FTE_CSQC_SKELETONOBJECTS + Gets the bone position and orientation relative to the bone's parent. Return value is the offset, and v_forward, v_right, v_up contain the orientation. */ + +vector(float skel, float bonenum) skel_get_boneabs = #270; /* Part of FTE_CSQC_SKELETONOBJECTS + Gets the bone position and orientation relative to the entity. Return value is the offset, and v_forward, v_right, v_up contain the orientation. + Use gettaginfo for world coord+orientation. */ + +void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_set_bone = #271; /* Part of FTE_CSQC_SKELETONOBJECTS + Sets a bone position relative to its parent. If the orientation arguments are not specified, v_forward+v_right+v_up are used instead. */ + +void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bone = #272; /* Part of FTE_CSQC_SKELETONOBJECTS + Transforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */ + +void(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bones = #273; /* Part of FTE_CSQC_SKELETONOBJECTS + Transforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones. */ + +void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; /* Part of FTE_CSQC_SKELETONOBJECTS + Copy bone data from one skeleton directly into another. */ + +void(float skel) skel_delete = #275; /* Part of FTE_CSQC_SKELETONOBJECTS + Deletes a skeletal object. The actual delete is delayed, allowing the skeletal object to be deleted in an entity's predraw function yet still be valid by the time the addentity+renderscene builtins need it. Also uninstanciates any ragdoll currently in effect on the skeletal object. */ + +float(float modidx, string framename) frameforname = #276; /* Part of FTE_CSQC_SKELETONOBJECTS + Looks up a framegroup from a model by name, avoiding the need for hardcoding. Returns -1 on error. */ + +float(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SKELETONOBJECTS + Retrieves the duration (in seconds) of the specified framegroup. */ + +void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback) processmodelevents = #0:processmodelevents; /* + Calls a callback for each event that has been reached. Basetime is set to targettime. */ + +float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data) getnextmodelevent = #0:getnextmodelevent; /* + Reports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime). + WARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported). */ + +float(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data) getmodeleventidx = #0:getmodeleventidx; /* + Reports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp. */ + +#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2)) +vector(vector v1, vector v2) crossproduct = #0:crossproduct; /* Part of FTE_QC_CROSSPRODUCT + Small helper function to calculate the crossproduct of two vectors. */ + +float(entity pusher, vector move, vector amove) pushmove = #0:pushmove; +void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Part of FTE_TERRAIN_MAP + Realtime terrain editing. Actions are the TEREDIT_ constants. */ + +typedef struct +{ + string shadername; + vector planenormal; + float planedist; + vector sdir; + float sbias; + vector tdir; + float tbias; +} brushface_t; +int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Part of FTE_RAW_MAP + Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */ + +int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Part of FTE_RAW_MAP + Inserts a new brush into the model. Return value is the new brush's id. */ + +void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Part of FTE_RAW_MAP + Destroys the specified brush. */ + +float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Part of FTE_RAW_MAP + Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */ + +int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Part of FTE_RAW_MAP + Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */ + +int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Part of FTE_RAW_MAP + Determines the points of the specified face, if the specified brush were to actually be created. */ + +int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Part of FTE_RAW_MAP + Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */ + +void(optional entity ent, optional vector neworigin) touchtriggers = #279; /* + Triggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity. */ + +void(float buf, float fl) WriteFloat = #280; +float(entity skelent, string dollcmd, float animskel) skel_ragupdate = #281; /* + Updates the skeletal object attached to the entity according to its origin and other properties. + if animskel is non-zero, the ragdoll will animate towards the bone state in the animskel skeletal object, otherwise they will pick up the model's base pose which may not give nice results. + If dollcmd is not set, the ragdoll will update (this should be done each frame). + If the doll is updated without having a valid doll, the model's default .doll will be instanciated. + commands: + doll foo.doll : sets up the entity to use the named doll file + dollstring TEXT : uses the doll file directly embedded within qc, with that extra prefix. + cleardoll : uninstanciates the doll without destroying the skeletal object. + animate 0.5 : specifies the strength of the ragdoll as a whole + animatebody somebody 0.5 : specifies the strength of the ragdoll on a specific body (0 will disable ragdoll animations on that body). + enablejoint somejoint 1 : enables (or disables) a joint. Disabling joints will allow the doll to shatter. */ + +float*(float skel) skel_mmap = #282; /* + Map the bones in VM memory. They can then be accessed via pointers. Each bone is 12 floats, the four vectors interleaved (sadly). */ + +void(entity ent, float bonenum, vector org, optional vector angorfwd, optional vector right, optional vector up) skel_set_bone_world = #283; /* + Sets the world position of a bone within the given entity's attached skeletal object. The world position is dependant upon the owning entity's position. If no orientation argument is specified, v_forward+v_right+v_up are used for the orientation instead. If 1 is specified, it is understood as angles. If 3 are specified, they are the forawrd/right/up vectors to use. */ + +string(float modidx, float framenum) frametoname = #284; +string(float modidx, float skin) skintoname = #285; +float(float resourcetype, float tryload, string resourcename) resourcestatus = #286; /* + resourcetype must be one of the RESTYPE_ constants. Returns one of the RESSTATE_ constants. Tryload 0 is a query only. Tryload 1 will attempt to reload the content if it was flushed. */ + +hashtable(float tabsize, optional float defaulttype) hash_createtab = #287; /* Part of FTE_QC_HASHTABLES + Creates a hash table object with at least 'tabsize' slots. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return). */ + +void(hashtable table) hash_destroytab = #288; /* Part of FTE_QC_HASHTABLES + Destroys a hash table object. */ + +void(hashtable table, string name, __variant value, optional float typeandflags) hash_add = #289; /* Part of FTE_QC_HASHTABLES + Adds the given key with the given value to the table. + If flags&HASH_REPLACE, the old value will be removed, if not set then multiple values may be added for a single key, they won't overwrite. + The type argument describes how the value should be stored and saved to files. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games. */ + +__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index) hash_get = #290; /* Part of FTE_QC_HASHTABLES + looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was. If requiretype is specified, then values not of the specified type will be ignored. Hurrah for multiple types with the same name. */ + +__variant(hashtable table, string name) hash_delete = #291; /* Part of FTE_QC_HASHTABLES + removes the named key. returns the value of the object that was destroyed, or 0 on error. */ + +string(hashtable table, float idx) hash_getkey = #292; /* Part of FTE_QC_HASHTABLES + gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all. */ + +float(string name) checkcommand = #294; /* Part of FTE_QC_CHECKCOMMAND + Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist. */ + +string(string s) argescape = #295; /* + Marks up a string so that it can be reliably tokenized as a single argument later. */ + +void(string dest, string from, string cmd, string info) clusterevent = #0:clusterevent; /* + Only functions in mapcluster mode. Sends an event to whichever server the named player is on. The destination server can then dispatch the event to the client or handle it itself via the SV_ParseClusterEvent entrypoint. If dest is empty, the event is broadcast to ALL servers. If the named player can't be found, the event will be returned to this server with the cmd prefixed with 'error:'. */ + +string(entity player, optional string newnode) clustertransfer = #0:clustertransfer; /* + Only functions in mapcluster mode. Initiate transfer of the player to a different node. Can take some time. If dest is specified, returns null on error. Otherwise returns the current/new target node (or null if not transferring). */ + +float(float mdlidx) modelframecount = #0:modelframecount; /* + Retrieves the number of frames in the specified model. */ + +void(string texturename, optional float flags, optional float is2d) R_BeginPolygon = #306; /* + Specifies the shader to use for the following polygons, along with optional flags. + If is2d, the polygon will be drawn as soon as the EndPolygon call is made, rather than waiting for renderscene. This allows complex 2d effects. */ + +void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #307; /* + Specifies a polygon vertex with its various properties. */ + +void() R_EndPolygon = #308; /* + Ends the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon if you wish to draw another polygon with the same shader. */ + +float(string effectname) particleeffectnum = #335; /* Part of DP_ENT_TRAILEFFECTNUM, FTE_SV_POINTPARTICLES + Precaches the named particle effect. If your effect name is of the form 'foo.bar' then particles/foo.cfg will be loaded by the client if foo.bar was not already defined. + Different engines will have different particle systems, this specifies the QC API only. */ + +void(float effectnum, entity ent, vector start, vector end) trailparticles = #336; /* Part of FTE_SV_POINTPARTICLES + Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */ + +void(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* Part of FTE_SV_POINTPARTICLES + Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument. + For regular particles, the dir vector is multiplied by the 'veladd' property (while orgadd will push the particles along it). Decals will use it as a hint to align to the correct surface. In both cases, it should normally be a unit vector, but other lengths will still work. If it has length 0 then FTE will assume downwards. */ + +void(string s, ...) print = #339; /* Part of DP_SV_PRINT + Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar). */ + +void(entity ent) runstandardplayerphysics = #347; /* + Perform the engine's standard player movement prediction upon the given entity using the input_* globals to describe movement. */ + +float(entity ent) wasfreed = #353; /* + Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust. */ + +string(string key) serverkey = #354; /* + Look up a key in the server's public serverinfo string */ + +float(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat; /* + Version of serverkey that returns the value as a float (which avoids tempstrings). */ + +__variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC + Allocate an arbitary block of memory */ + +void(__variant *ptr) memfree = #385; /* Part of FTE_MEMALLOC + Frees a block of memory that was allocated with memfree */ + +void(__variant *dst, __variant *src, int size) memcpy = #386; /* Part of FTE_MEMALLOC + Copys memory from one location to another */ + +void(__variant *dst, int val, int size) memfill8 = #387; /* Part of FTE_MEMALLOC + Sets an entire block of memory to a specified value. Pretty much always 0. */ + +__variant(__variant *dst, float ofs) memgetval = #388; /* + Looks up the 32bit value stored at a pointer-with-offset. */ + +void(__variant *dst, float ofs, __variant val) memsetval = #389; /* + Changes the 32bit value stored at the specified pointer-with-offset. */ + +__variant*(__variant *base, float ofs) memptradd = #390; /* + Perform some pointer maths. Woo. */ + +float(string s) memstrsize = #0:memstrsize; /* + strlen, except ignores utf-8 */ + +float(entity e, float nowreadonly) entityprotection = #0:entityprotection; /* + Changes the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea). */ + +entity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY + Copies all fields from one entity to another. */ + +void(entity ent, float colours) setcolors = #401; /* + Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */ + +entity(.string field, string match, optional .entity chainfield) findchain = #402; /* Part of DP_QC_FINDCHAIN*/ +entity(.float fld, float match, optional .entity chainfield) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/ +void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; /* Part of DP_SV_EFFECT + Spawns a self-animating sprite */ + +void(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOOD*/ +void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/ +void(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of DP_TE_PARTICLERAIN*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of DP_TE_PARTICLESNOW*/ +void(vector org, vector vel, float howmany) te_spark = #411; /* Part of DP_TE_SPARK*/ +void(vector org) te_gunshotquad = #412; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_spikequad = #413; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_superspikequad = #414; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_explosionquad = #415; /* Part of _DP_TE_QUADEFFECTS1*/ +void(vector org) te_smallflash = #416; /* Part of DP_TE_SMALLFLASH*/ +void(vector org, float radius, float lifetime, vector color) te_customflash = #417; /* Part of DP_TE_CUSTOMFLASH*/ +void(vector org, optional float count) te_gunshot = #418; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_spike = #419; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_superspike = #420; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_explosion = #421; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_tarexplosion = #422; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_wizspike = #423; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_knightspike = #424; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_lavasplash = #425; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org) te_teleport = #426; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(vector org, float color, float colorlength) te_explosion2 = #427; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_lightning1 = #428; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_lightning2 = #429; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_lightning3 = #430; /* Part of DP_TE_STANDARDEFFECTBUILTINS, FTE_TE_STANDARDEFFECTBUILTINS*/ +void(entity own, vector start, vector end) te_beam = #431; /* Part of DP_TE_STANDARDEFFECTBUILTINS*/ +void(vector dir) vectorvectors = #432; /* Part of DP_QC_VECTORVECTORS*/ +void(vector org) te_plasmaburn = #433; /* Part of _DP_TE_PLASMABURN*/ +float(entity e, float s) getsurfacenumpoints = #434; /* Part of DP_QC_GETSURFACE*/ +vector(entity e, float s, float n) getsurfacepoint = #435; /* Part of DP_QC_GETSURFACE*/ +vector(entity e, float s) getsurfacenormal = #436; /* Part of DP_QC_GETSURFACE*/ +string(entity e, float s) getsurfacetexture = #437; /* Part of DP_QC_GETSURFACE*/ +float(entity e, vector p) getsurfacenearpoint = #438; /* Part of DP_QC_GETSURFACE*/ +vector(entity e, float s, vector p) getsurfaceclippedpoint = #439; /* Part of DP_QC_GETSURFACE*/ +void(entity e, string s) clientcommand = #440; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ +float(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ +string(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ +void(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/ +searchhandle(string pattern, float caseinsensitive, float quiet) search_begin = #444; /* Part of DP_QC_FS_SEARCH + initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. */ + +void(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH*/ +float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH + Retrieves the number of files that were found. */ + +string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH + Retrieves name of one of the files that was found by the initial search. */ + +float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME + Retrieves the size of one of the files that was found by the initial search. */ + +string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME + Retrieves modification time of one of the files. */ + +string(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/ +entity(entity start, .float fld, float match) findflags = #449; /* Part of DP_QC_FINDFLAGS*/ +entity(.float fld, float match, optional .entity chainfield) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/ +float(entity ent, string tagname) gettagindex = #451; /* Part of DP_MD3_TAGSINFO*/ +vector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_MD3_TAGSINFO + Obtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation. */ + +void(entity player) dropclient = #453; /* Part of DP_SV_DROPCLIENT*/ +entity() spawnclient = #454; /* Part of DP_SV_BOTCLIENT*/ +float(entity client) clienttype = #455; /* Part of DP_SV_BOTCLIENT*/ +void(float target, string str) WriteUnterminatedString = #456; /* Part of DP_SV_WRITEUNTERMINATEDSTRING*/ +void(vector org, vector vel, float howmany) te_flamejet = #457; /* Part of _DP_TE_FLAMEJET*/ +entity(float entnum) edict_num = #459; /* Part of DP_QC_EDICT_NUM*/ +strbuf() buf_create = #460; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle) buf_del = #461; /* Part of DP_QC_STRINGBUFFERS*/ +float(strbuf bufhandle) buf_getsize = #462; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle_from, strbuf bufhandle_to) buf_copy = #463; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #464; /* Part of DP_QC_STRINGBUFFERS*/ +string(strbuf bufhandle, string glue) buf_implode = #465; /* Part of DP_QC_STRINGBUFFERS*/ +string(strbuf bufhandle, float string_index) bufstr_get = #466; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle, float string_index, string str) bufstr_set = #467; /* Part of DP_QC_STRINGBUFFERS*/ +float(strbuf bufhandle, string str, float ordered) bufstr_add = #468; /* Part of DP_QC_STRINGBUFFERS*/ +void(strbuf bufhandle, float string_index) bufstr_free = #469; /* Part of DP_QC_STRINGBUFFERS*/ +float(float s) asin = #471; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float c) acos = #472; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float t) atan = #473; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float c, float s) atan2 = #474; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ +float(float a) tan = #475; /* Part of DP_QC_ASINACOSATANATAN2TAN + Forgive me father, for I have a sunbed and I'm not afraid to use it. */ + +float(string s) strlennocol = #476; /* Part of DP_QC_STRINGCOLORFUNCTIONS + Returns the number of characters in the string after any colour codes or other markup has been parsed. */ + +string(string s) strdecolorize = #477; /* Part of DP_QC_STRINGCOLORFUNCTIONS + Flattens any markup/colours, removing them from the string. */ + +string(float uselocaltime, string format, ...) strftime = #478; /* Part of DP_QC_STRFTIME*/ +float(string s, string separator1, ...) tokenizebyseparator = #479; /* Part of DP_QC_TOKENIZEBYSEPARATOR*/ +string(string s) strtolower = #480; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ +string(string s) strtoupper = #481; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ +string(string s) cvar_defstring = #482; /* Part of DP_QC_CVAR_DEFSTRING*/ +void(vector origin, string sample, float volume, float attenuation) pointsound = #483; /* Part of DP_SV_POINTSOUND*/ +string(string search, string replace, string subject) strreplace = #484; /* Part of DP_QC_STRREPLACE*/ +string(string search, string replace, string subject) strireplace = #485; /* Part of DP_QC_STRREPLACE*/ +vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/ +float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/ +float(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/ +float() numentityfields = #496; /* Part of DP_QC_ENTITYDATA + Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */ + +float(string fieldname) findentityfield = #0:findentityfield; /* + Find a field index by name. */ + +typedef .__variant field_t; +field_t(float fieldnum) entityfieldref = #0:entityfieldref; /* + Returns a field value that can be directly used to read entity fields. Be sure to validate the type with entityfieldtype before using. */ + +string(float fieldnum) entityfieldname = #497; /* Part of DP_QC_ENTITYDATA + Retrieves the name of the given entity field. */ + +float(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA + Provides information about the type of the field specified by the field num. Returns one of the EV_ values. */ + +string(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/ +float(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/ +void(float to, string s, float sz) WritePicture = #501; /* Part of DP_SV_WRITEPICTURE + Encodes the named image across the network as-is adhering to some size limit. In FTE, this simply writes the string and is equivelent to writestring and sz is ignored. WritePicture should be paired with ReadPicture in csqc. */ + +string(string filename, optional float makereferenced) whichpack = #503; /* Part of DP_QC_WHICHPACK + Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */ + +string(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE*/ +string(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE*/ +float(entity ent) num_for_edict = #512; +#define uri_post uri_get +float(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_URI_GET, DP_QC_URI_POST + uri_get() gets content from an URL and calls a callback "uri_get_callback" with it set as string; an unique ID of the transfer is returned + returns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string + For a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded. + For a GET request, omit the mime+data entirely. + Consult your webserver/php/etc documentation for best-practise. */ + +float(string str) tokenize_console = #514; /* + Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches. */ + +float(float idx) argv_start_index = #515; /* + Returns the character index that the tokenized arg started at. */ + +float(float idx) argv_end_index = #516; /* + Returns the character index that the tokenized arg stopped at. */ + +void(strbuf strbuf, string pattern, string antipattern) buf_cvarlist = #517; +string(string cvarname) cvar_description = #518; /* + Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful. */ + +float(optional float timetype) gettime = #519; +void(string s) loadfromdata = #529; /* + Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ + +void(string s) loadfromfile = #530; /* + Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ + +void(float pause) setpause = #531; /* + Sets whether the server should or should not be paused. This does not affect auto-paused things like when the console is down. */ + +float(string mname) precache_vwep_model = #532; /* Part of ZQ_VWEP*/ +float(float v, optional float base) log = #532; /* Part of ??MVDSV_BUILTINS + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + +float(string filename, strbuf bufhandle) buf_loadfile = #535; /* + Appends the named file into a string buffer (which must have been created in advance). The return value merely says whether the file was readable. */ + +float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /* + Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */ + +float(optional float force) physics_supported = #0:physics_supported; /* + Queries whether rigid body physics is enabled or not. CSQC and SSQC may report different values. If the force argument is used then the engine will try to activate or release physics (returning the new state, which may fail if plugins or dlls are missing). Note that restarting the physics engine is likely to result in hitches when collision trees get generated. The state may change if a plugin is disabled mid-map. */ + +void(entity e, float physics_enabled) physics_enable = #540; /* + Enable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time. */ + +void(entity e, vector force, vector relative_ofs) physics_addforce = #541; /* + Apply some impulse directional force upon a MOVETYPE_PHYSICS entity. */ + +void(entity e, vector torque) physics_addtorque = #542; /* + Apply some impulse rotational force upon a MOVETYPE_PHYSICS entity. */ + +void(.../*, string funcname*/) callfunction = #605; /* + Invokes the named function. The function name is always passed as the last parameter and must always be present. The others are passed to the named function as-is */ + +void(filestream fh, entity e) writetofile = #606; /* + Writes an entity's fields to the named frik_file file handle. */ + +float(string s) isfunction = #607; /* + Returns true if the named function exists and can be called with the callfunction builtin. */ + +float(entity e, string s, optional float offset) parseentitydata = #613; /* + Reads a single entity's fields into an already-spawned entity. s should contain field pairs like in a saved game: {"foo1" "bar" "foo2" "5"}. Returns <=0 on failure, otherwise returns the offset in the string that was read to. */ + +string(entity e) generateentitydata = #0:generateentitydata; /* + Dumps the entities fields into a string which can later be parsed with parseentitydata. */ + +string(string dnsname, optional float defport) netaddress_resolve = #625; +string(string fmt, ...) sprintf = #627; /* Part of DP_QC_SPRINTF*/ +float(entity e, float s) getsurfacenumtriangles = #628; +vector(entity e, float s, float n) getsurfacetriangle = #629; +string(string digest, string data, ...) digest_hex = #639; +#ifdef _ACCESSORS +accessor strbuf : float +{ + inline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));}; + inline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));}; + get string[float] = bufstr_get; + set string[float] = bufstr_set; + get float length = buf_getsize; +}; +accessor searchhandle : float +{ + get string[float] = search_getfilename; + get float length = search_getsize; +}; +accessor hashtable : float +{ + inline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);}; + inline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);}; + inline get string s[string key] = {return hash_get(this, key, "", EV_STRING);}; + inline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);}; + inline get float f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);}; + inline set float f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);}; + inline get __variant[string key] = {return hash_get(this, key, __NULL__);}; + inline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);}; +}; +accessor infostring : string +{ + get string[string] = infoget; + inline set* string[string fld] = {(*this) = infoadd(*this, fld, value);}; +}; +accessor filestream : float +{ + get string = fgets; + inline set string = {fputs(this,value);}; +}; +#endif +#pragma noref 0 + +#define QUAKESTATS diff --git a/source/server/defs/nx.qc b/source/server/defs/nx.qc new file mode 100644 index 0000000..ed7f519 --- /dev/null +++ b/source/server/defs/nx.qc @@ -0,0 +1,666 @@ +/* + defs.qc + + global definitions + + 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 General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/* +============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + +============================================================================== +*/ + +#define NX + +// +// system globals +// +entity self; +entity other; +entity world; +float time; +float frametime; +float force_retouch; +string mapname; +float deathmatch; +float coop; +float teamplay; +float serverflags; +float rounds; +float rounds_change; +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +vector v_forward, v_up, v_right; +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 +void() main; // only for testing +void() StartFrame; +void() EndFrame; +void() PlayerPreThink; +void() PlayerPostThink; +void() ClientKill; +void() ClientConnect; +void() PutClientInServer; +void() ClientDisconnect; +void() SetNewParms; +void() SetChangeParms; +void end_sys_globals; + + +.float modelindex; +.vector absmin, absmax; +.float ltime; +.float movetype; +.float solid; +.vector origin; +.vector oldorigin; +.vector velocity; +.vector angles; +.vector avelocity; +.vector punchangle; +.string classname; +.string model; +.float frame; +.float skin; +.float effects; +.vector mins, maxs; +.vector size; +.void() touch; +.void() use; +.void() think; +.void() blocked; +.float nextthink; +.entity groundentity; +.float health; +.float points; +.float kills; +.float weapon; +.string weaponmodel; +.string weapon2model; +.float weaponframe; +.float weapon2frame; +.float currentammo; +.float currentmag; +.float zoom; +.float weaponskin; +.float weapon2skin; +.float primary_grenades; +.float secondary_grenades; +.float grenades; +.float perks; +.float takedamage; +.entity chain; +.float deadflag; +.vector view_ofs; +.float button0; +.float button1; +.float button2; +.float button3; +.float button4; +.float button5; +.float button6; +.float button7; +.float button8; +.float impulse; +.float fixangle; +.vector v_angle; +.float idealpitch; // Naievil -- new +.string netname; +.entity enemy; +.float flags; +.float colormap; +.float team; +.float max_health; +.float teleport_time; +.float armortype; +.float armorvalue; +.float waterlevel; +.float watertype; +.float ideal_yaw; +.float yaw_speed; +.entity aiment; +.entity head; +.entity larm; +.entity rarm; +.entity goalentity; +.float spawnflags; +.string target; +.string targetname; +.float dmg_take; +.float dmg_save; +.float progress_bar; +.entity dmg_inflictor; +.entity owner; +.vector movedir; +.string message; +.float sounds; +.string noise, noise1, noise2, noise3; +.float x2_icon; +.float insta_icon; +.string Weapon_Name_Touch; +.vector ADS_Offset; +.vector Flash_Offset; +.float Flash_Size; +.float currentmag2; +.float maxspeed; +.float renderGrayscale; +void end_sys_fields; + +/* +============================================================================== + + 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 + +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; + +// 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'; + +// 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; + +// 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; +float ATTN_NORM = 1; +float ATTN_IDLE = 2; +float ATTN_STATIC = 3; + +// entity effects +#define EF_BLUE 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_RED 8 +#define EF_ORANGELIGHT 16 +#define EF_GREEN 32 +#define EF_LIGHT 64 +#define EF_NODRAW 128 +#define EF_BRIGHTFIELD 256 +#define EF_FULLBRIGHT 512 +#define EF_DARKLIGHT 1024 +#define EF_DARKFIELD 2048 +#define EF_PURPLELIGHT 4096 +#define EF_RAYRED 8196 +#define EF_RAYGREEN 16384 + +// 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 +// +string string_null; // null string, nothing should be held here +entity activator; // the entity that activated a trigger or brush +float framecount; + +// +// cvars checked each frame +// +float teamplay; +float deathmatch; + +//================================================ + +// +// 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 +// +.float voided; +.float walkframe; + +// Zoid Additions +.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 +.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; + + +// +// 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; + +.float sprintflag; + +// +// 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; + +void() break = #6; +float() random = #7; // returns 0 - 1 +void(entity e, float chan, string samp, float vol, float atten) sound = #8; +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; +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; +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; +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 +float(entity zombie, float which, entity limb) updateLimb = #33; +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 +float(entity e) checkbottom = #40; // true if self is on ground +float(vector v) pointcontents = #41; // returns a CONTENT_* +float(float f) fabs = #43; +vector(entity e, float speed) aim = #44; // 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() ChangeYaw = #49; // turn towards self.ideal_yaw +vector(vector v) vectoangles = #51; +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; +string(string s) precache_file = #68; // no effect except for -copy +void(entity e) makestatic = #69; +void(string s) changelevel = #70; +void(string var, string val) cvar_set = #72; // sets cvar.value +void(entity client, string s) centerprint = #73; // sprint, but in middle +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 +void(entity e) setspawnparms = #78; // set parm1... to the +void(entity killer, entity killee) logfrag = #79; // add to stats +string(entity e, string key) infokey = #80; // get a key value (world = serverinfo) +float(string s) stof = #81; // convert string to float +void(vector where, float set) multicast = #82; +vector(entity what) Get_Waypoint_Near = #83; +float(entity zombie, entity target) Do_Pathfind_psp = #84; +void(string s) Open_Waypoint = #85; +vector(entity zombie,vector start,vector min, vector max) Get_Next_Waypoint = #86; +void(entity client, float type, float cost, float weapon) useprint = #87; +vector(entity zombie,vector start,vector min, vector max) Get_First_Waypoint = #88; +entity (entity start, .float field, float match) findfloat = #98; + +// New NZP custom ones +float(string filename, float mode) fopen = #110; +void(float fhandle) fclose = #111; +string(float fhandle) fgets = #112; +void(float fhandle, string s) fputs = #113; +float(string s) strlen = #114; +string(string s1, string s2) strcat = #115; +string(string s, float start, float length) substring = #116; +vector(string s) stov = #117; +string(string s) strzone = #118; +string(string s) strunzone = #119; +string(string s) strtrim = #120; +float(string s) tokenize = #130; // Was #441 +string(float num) argv = #131; // Was #442 + +float(entity targ, entity inflictor) CanDamage; + + +#define FILE_READ 0 +#define FILE_APPEND 1 +#define FILE_WRITE 2 +#define SVC_WEAPONFIRE 35 +#define SVC_HITMARK 36 +#define SVC_LIMBUPDATE 51 // naievil -- keep me +#define SVC_BSPDECAL 50 // naievil -- keep me +#define SVC_ACHIEVEMENT 52 + +#define VEC_ORIGIN '0 0 0' + +//Standard Quake View Offset +//vector VEC_VIEW_OFS = '0 0 22'; + +//Half Life View Offset is 64 +//64 units above ground, for our purposes it's, 64 - (how low bounding box goes) +vector VEC_VIEW_OFS = '0 0 32'; + +//Standard Quake Hull +//vector VEC_HULL_MIN = '-16 -16 -24'; +//vector VEC_HULL_MAX = '16 16 32'; + +//Half Life 1 Hull Sizes ADDED BY BLUBS, COMMENTED ORIGINAL QUAKE BBOX SIZS OUT +#define VEC_HULL_MIN '-16 -16 -32' +#define VEC_HULL_MAX '16 16 40' + +#define VEC_HULL2_MIN '-32 -32 -24' +#define VEC_HULL2_MAX '32 32 64' + +.string fog; +//string world_fog; + +.float button3; +.float button4; +.float button5; +.float button6; +.float button7; +.float button8; + +.float x2_icon; // double points icon +.float insta_icon; +.vector Flash_Offset; +.float Flash_Size; +.string Weapon_Name; + +.float currentHitBoxSetup; +.vector bbmins, bbmaxs; // Used for zombie hitboxes + +.entity head; +.entity larm; +.entity rarm; + +#define SOLID_CORPSE 5 // bsp clip, touch on edge, block +#define TE_EXPLOSION 3 +#define TE_GUNSHOT 2 +entity windows[32]; +float wincnt; + +.float recoil_delay; + +float pow(float base, float n) = { + float temp = base; + if (n == 0) { + return 1; + } else { + while (n > 0) { + temp = temp * base; + n = n - 1; + } + return temp; + } + return 0; +}; + +void enableGrayscale(void) = { + self.renderGrayscale = 1; +} + +void disableGrayscale(void) = { + self.renderGrayscale = 0; +} \ No newline at end of file diff --git a/source/server/defs/psp.qc b/source/server/defs/psp.qc new file mode 100644 index 0000000..f6f6575 --- /dev/null +++ b/source/server/defs/psp.qc @@ -0,0 +1,541 @@ +/* ++----+ +|Defs| ++----+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| Scratch Http://www.admdev.com/scratch | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +| This contains necessary definitions from the original V1.06 defs.qc file. | +| This includes some basic constants, the built in function definitions, and | +| some variable's used by the Quake Engine internally. | +| Certain lines in this file are hardcoded into Quake engine, and -must- be | +| present and unchanged, in the order they are shown. Otherwise Quake will | +| refuse to run. | ++=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ +*/ + +#define PSP + +// These lines CANNOT be altered/moved +entity self; +entity other; +entity world; +float time; +float frametime; +float force_retouch; // force all entities to touch triggers +string mapname; +float deathmatch; +float coop; +float teamplay; +float serverflags; // propagated from level to level, used to +float rounds; +float rounds_change; +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +vector v_forward, v_up, v_right; // set by makevectors() +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 +void() main; // only for testing +void() StartFrame; +void() EndFrame; +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 +void() SetChangeParms; // call to set parms for self so they can +void() ParseClientCommand; // special command calls +string CMD_STRING; +void() Soft_Restart; +void end_sys_globals; // flag for structure dumping + +.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 iframetime; +.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; +.float health; +.float points; +.float kills; +.float weapon; // one of the W_COLT, etc flags +.string weaponmodel; +.string weapon2model; +.float weaponframe; +.float weapon2frame; +.float currentammo; +.float currentmag; +.float zoom; +.float weaponskin; +.float weapon2skin; +.float primary_grenades; +.float secondary_grenades; +.float grenades; +.float perks; // bit flags +.float takedamage; +.entity chain; +.float deadflag; +.vector view_ofs; // add to origin to get eye point +.float button0; // +.float button1; // +.float button2; // +.float button3; // +.float button4; // +.float button5; // +.float button6; // +.float button7; // +.float button8; // +.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 waterlevel; // 0 = not in, 1 = feet, 2 = wast, 3 = eyes +.float watertype; // a contents value +.float ideal_yaw; +.float yaw_speed; +.entity aiment; +.entity head; +.entity larm; +.entity rarm; +.entity goalentity; // a movetarget or an enemy +//.entity goalorigin; // a movetarget location +.float spawnflags; +.string target; +.string targetname; +.float bleed_out; +.float progress_bar; +.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 +.float x2_icon; // double points icon +.float insta_icon; +.vector ADS_Offset; +.vector Flash_Offset; +.float Flash_Size; +.string Weapon_Name; +.string Weapon_Name_Touch; +.float currentmag2; +.float maxspeed; +.float facingenemy; +void end_sys_fields; // flag for structure dumping +// End. Lines below this MAY be altered, to some extent + +// Built In 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; +void() break = #6; +float() random = #7; // returns 0 - 1 +void(entity e, float chan, string samp, float vol, float atten) sound = #8; +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; +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; +entity(vector org, float rad) findradius = #22; +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 +float(entity zombie, float which, entity limb) updateLimb = #33; +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 +float(entity e) checkbottom = #40; // true if self is on ground +float(vector v) pointcontents = #41; // returns a CONTENT_* +float(float f) fabs = #43; +vector(entity e, float speed) aim = #44; // 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() ChangeYaw = #49; // turn towards self.ideal_yaw +float(string name) getSoundLen = #50; +vector(vector v) vectoangles = #51; +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; +string(entity s) etos = #65; +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; +void(float step, vector origin) movetoorigin = #71; +void(string var, string val) cvar_set = #72; // sets cvar.value +void(entity client, string s) centerprint = #73; // sprint, but in middle +void(entity client, string s, string s) centerprint2 = #73; +void(entity client, string s, string s, string s) centerprint3 = #73; +void(entity client, string s, string s, string s, string s) centerprint4 = #73; +void(entity client, string s, string s, string s, string s, string s) centerprint5 = #73; +void(entity client, string s, string s, string s, string s, string s, string s) centerprint6 = #73; +void(entity client, string s, string s, string s, string s, string s, string s, string s) centerprint7 = #73; +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 +void(entity e) setspawnparms = #78; // set parm1... to the +void(vector start, vector min, vector max, vector end, float nomonsters, entity forent) tracebox = #90; +float(vector start, vector min, vector max, vector end, float nomonsters, entity forent) tracemove = #99; +entity (entity start, .float field, float match) findfloat = #98; +float(string s) stof = #81; // 2001-09-20 QuakeC string manipulation by FrikaC +vector(entity what) Get_Waypoint_Near = #83; +float(entity zombie, entity target) Do_Pathfind_psp = #84; +void(string s) Open_Waypoint = #85; +vector(entity zombie,vector start,vector min, vector max) Get_Next_Waypoint = #86; +void(entity client, float type, float cost, float weapon) useprint = #87; +vector(entity zombie,vector start,vector min, vector max) Get_First_Waypoint = #88; +void(string s) Close_Waypoint = #89; + +void(entity plr, float achievement) achievement = #79; + +void(string trackname) songegg = #500; +void() nzp_maxammo = #501; +/* +type 0 = clear +type 1 = buy door +type 2 = buy debris +type 3 = Buy ammo +type 4 = buy weapon +type 5 = repair window +type 6 = use box +type 7 = use trap +type 8 = require power +*/ + + +// 2001-09-20 QuakeC file access by FrikaC start +float(string filename, float mode) fopen = #110; +void(float fhandle) fclose = #111; +string(float fhandle) fgets = #112; +void(float fhandle, string s) fputs = #113; +// 2001-09-20 QuakeC file access by FrikaC end + +// 2001-09-20 QuakeC string manipulation by FrikaC start +float(string s) strlen = #114; +string(string s1, string s2) strcat = #115; +string(string s, float start, float length) substring = #116; +vector(string s) stov = #117; +string(string s) strzone = #118; +string(string s) strunzone = #119; +string(string s) strtrim = #120; +// 2001-09-20 QuakeC string manipulation by FrikaC end + +// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start +float(string s) tokenize = #441; +string(float num) argv = #442; +// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end + +// +// constants +// + +#define FALSE 0 +#define TRUE 1 + +// edict.flags +#define FL_FLY 1 +#define FL_SWIM 2 +#define FL_CLIENT 8 // set for all client edicts +#define FL_INWATER 16 // for enter / leave water splash +#define FL_MONSTER 32 +#define FL_GODMODE 64 // player cheat +#define FL_NOTARGET 128 // player cheat +#define FL_ITEM 256 // extra wide size for bonus items +#define FL_ONGROUND 512 // standing on something +#define FL_PARTIALGROUND 1024 // not all corners are valid +#define FL_WATERJUMP 2048 // player jumping out of water +//#define FL_JUMPRELEASED 4096 // for jump debouncing + +// edict.movetype values +#define MOVETYPE_NONE 0 // never moves +#define MOVETYPE_ANGLENOCLIP 1 +#define MOVETYPE_ANGLECLIP 2 +#define MOVETYPE_WALK 3 // players only +#define MOVETYPE_STEP 4 // discrete, not real time unless fall +#define MOVETYPE_FLY 5 +#define MOVETYPE_TOSS 6 // gravity +#define MOVETYPE_PUSH 7 // no clip to world, push and crush +#define MOVETYPE_NOCLIP 8 +#define MOVETYPE_FLYMISSILE 9 // fly with extra size against monsters +#define MOVETYPE_BOUNCE 10 +#define MOVETYPE_BOUNCEMISSILE 11 // bounce with extra size +#define MOVETYPE_FOLLOW 12 + +// edict.solid values +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block +#define SOLID_CORPSE 5 // bsp clip, touch on edge, block + +// range values +#define RANGE_MELEE 0 +#define RANGE_NEAR 1 +#define RANGE_MID 2 +#define RANGE_FAR 3 + +// deadflag values + +#define DEAD_NO 0 +#define DEAD_DYING 1 +#define DEAD_DEAD 2 +#define DEAD_RESPAWNABLE 3 + +// takedamage values + +#define DAMAGE_NO 0 +#define DAMAGE_YES 1 +#define DAMAGE_AIM 2 +#define DAMAGE_NOMARKER 3 + +/*.void() th_stand; +.void() th_walk; +.void() th_run; +.void(entity attacker, float damage) th_pain;*/ +.void() th_die; +//.void() th_missile; +.void() th_melee; + +// 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 + + +#define VEC_ORIGIN '0 0 0' + +//Standard Quake View Offset +//vector VEC_VIEW_OFS = '0 0 22'; + +//Half Life View Offset is 64 +//64 units above ground, for our purposes it's, 64 - (how low bounding box goes) +//vector VEC_VIEW_OFS = '0 0 32'; + +//Standard Quake Hull +//vector VEC_HULL_MIN = '-16 -16 -24'; +//vector VEC_HULL_MAX = '16 16 32'; + +//Half Life 1 Hull Sizes ADDED BY BLUBS, COMMENTED ORIGINAL QUAKE BBOX SIZS OUT +#define VEC_HULL_MIN '-16 -16 -32' +#define VEC_HULL_MAX '16 16 40' + +#define VEC_HULL2_MIN '-32 -32 -24' +#define VEC_HULL2_MAX '32 32 64' + +// protocol bytes +#define SVC_BAD 0 +#define SVC_NOP 1 +#define SVC_DISCONNECT 2 +#define SVC_UPDATESTAT 3 +#define SVC_VERSION 4 +#define SVC_SETVIEW 5 +#define SVC_SOUND 6 +#define SVC_TIME 7 +#define SVC_PRINT 8 +#define SVC_STUFFTEXT 9 +#define SVC_SETANGLE 10 +#define SVC_SERVERINFO 11 +#define SVC_LIGHTSTYLE 12 +#define SVC_UPDATENAME 13 +#define SVC_UPDATEPOINTS 14 +#define SVC_CLIENTDATA 15 +#define SVC_STOPSOUND 16 +#define SVC_PARTICLE 18 +#define SVC_DAMAGE 19 +#define SVC_SPAWNSTATIC 20 +#define SVC_SPAWNBINARY 21 +#define SVC_SPAWNBASELINE 22 +#define SVC_TEMPENTITY 23 +#define SVC_SETPAUSE 24 +#define SVC_SIGNONNUM 25 +#define SVC_CENTERPRINT 26 +#define SVC_SPAWNSTATICSOUND 29 // 1998-08-08 Complete SVC list by Zhenga +#define SVC_INTERMISSION 30 +#define SVC_FINALE 31 +#define SVC_CDTRACK 32 +#define SVC_SELLSCREEN 33 +#define SVC_CUTSCENE 34 // 1998-08-08 Complete SVC list by Zhenga +#define SVC_WEAPONFIRE 35 +#define SVC_HITMARK 36 +#define SVC_USEPRINT 38 + +#define TE_SPIKE 0 +#define TE_SUPERSPIKE 1 +#define TE_GUNSHOT 2 +#define TE_EXPLOSION 3 +#define TE_TAREXPLOSION 4 +#define TE_LIGHTNING1 5 +#define TE_LIGHTNING2 6 +#define TE_WIZSPIKE 7 +#define TE_KNIGHTSPIKE 8 +#define TE_LIGHTNING3 9 +#define TE_LAVASPLASH 10 +#define TE_TELEPORT 11 +#define TE_RAYSPLASHGREEN 14 +#define TE_RAYSPLASHRED 15 + +// sound channels +// channel 0 never willingly overrides +// other channels (1-7) allways override a playing sound on that channel +#define CHAN_AUTO 0 +#define CHAN_WEAPON 1 +#define CHAN_VOICE 2 +#define CHAN_ITEM 3 +#define CHAN_BODY 4 +//Player uses channel 5 for all weapon reload sfx + +#define ATTN_NONE 0 +#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 + +#define EF_BLUE 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_RED 8 +#define EF_ORANGELIGHT 16 +#define EF_GREEN 32 +#define EF_LIGHT 64 +#define EF_NODRAW 128 +#define EF_BRIGHTFIELD 256 +#define EF_FULLBRIGHT 512 +#define EF_DARKLIGHT 1024 +#define EF_DARKFIELD 2048 +#define EF_PURPLELIGHT 4096 +#define EF_RAYRED 8196 +#define EF_RAYGREEN 16384 + +// messages +#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 AS_STRAIGHT 1 +#define AS_SLIDING 2 +#define AS_MELEE 3 +#define AS_MISSILE 4 + +// Quake assumes these are defined. + +//.string wad, map; +.float /*worldtype,*/ delay, wait, lip, /*light_lev,*/ speed, style/*, skill*/; +.string killtarget; +.void() think1; +.vector finaldest; +.vector pos1, pos2/*, mangle*/; + +void(vector o, vector d, float color, float count) particle = #48;// start a particle effect +void(string s) bprint_psp = #23; +void(entity client, string s) sprint_psp = #24; +// End + +void(string s) println = {bprint_psp(s);} //just when brain decided to think this is c +void(float ignore, string s) bprint = {bprint_psp(s);}; +void(entity client, float type, string s) sprint = {sprint_psp(client, s);}; +void(string s) print = {bprint_psp(s);}; + +//doors +.float state; +#define STATE_TOP 0 +#define STATE_BOTTOM 1 +#define STATE_UP 2 +#define STATE_DOWN 3 + +string mappath; +#define PRINT_HIGH 0 + +.float isspec; + +#define FILE_READ 0 +#define FILE_APPEND 1 +#define FILE_WRITE 2 + +#define MOVE_HITMODEL 0 // must be different for this engine! + +.float recoil_delay; +.float gravity; + +.float renderamt; +.float rendermode; +.vector rendercolor; +.string mapversion; +.float ammo; + +void(string com) SV_ParseClientCommand; + +.float currentHitBoxSetup; +.vector bbmins, bbmaxs; // Used for zombie hitboxes + +.float achievements; diff --git a/source/server/defs/vita.qc b/source/server/defs/vita.qc new file mode 100644 index 0000000..637adfd --- /dev/null +++ b/source/server/defs/vita.qc @@ -0,0 +1,668 @@ +/* + defs.qc + + global definitions + + 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 General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/* +============================================================================== + + SOURCE FOR GLOBALVARS_T C STRUCTURE + +============================================================================== +*/ + +#define NX +#define VITA + +// +// system globals +// +entity self; +entity other; +entity world; +float time; +float frametime; +float force_retouch; +string mapname; +float deathmatch; +float coop; +float teamplay; +float serverflags; +float rounds; +float rounds_change; +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +vector v_forward, v_up, v_right; +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 +void() main; // only for testing +void() StartFrame; +void() EndFrame; +void() PlayerPreThink; +void() PlayerPostThink; +void() ClientKill; +void() ClientConnect; +void() PutClientInServer; +void() ClientDisconnect; +void() SetNewParms; +void() SetChangeParms; +void end_sys_globals; + + +.float modelindex; +.vector absmin, absmax; +.float ltime; +.float movetype; +.float solid; +.vector origin; +.vector oldorigin; +.vector velocity; +.vector angles; +.vector avelocity; +.vector punchangle; +.string classname; +.string model; +.float frame; +.float skin; +.float effects; +.vector mins, maxs; +.vector size; +.void() touch; +.void() use; +.void() think; +.void() blocked; +.float nextthink; +.entity groundentity; +.float health; +.float points; +.float kills; +.float weapon; +.string weaponmodel; +.string weapon2model; +.float weaponframe; +.float weapon2frame; +.float currentammo; +.float currentmag; +.float zoom; +.float weaponskin; +.float weapon2skin; +.float primary_grenades; +.float secondary_grenades; +.float grenades; +.float perks; +.float takedamage; +.entity chain; +.float deadflag; +.vector view_ofs; +.float button0; +.float button1; +.float button2; +.float button3; +.float button4; +.float button5; +.float button6; +.float button7; +.float button8; +.float impulse; +.float fixangle; +.vector v_angle; +.float idealpitch; // Naievil -- new +.string netname; +.entity enemy; +.float flags; +.float colormap; +.float team; +.float max_health; +.float teleport_time; +.float armortype; +.float armorvalue; +.float waterlevel; +.float watertype; +.float ideal_yaw; +.float yaw_speed; +.entity aiment; +.entity head; +.entity larm; +.entity rarm; +.entity goalentity; +.float spawnflags; +.string target; +.string targetname; +.float dmg_take; +.float dmg_save; +.float progress_bar; +.entity dmg_inflictor; +.entity owner; +.vector movedir; +.string message; +.float sounds; +.string noise, noise1, noise2, noise3; +.float x2_icon; +.float insta_icon; +.string Weapon_Name_Touch; +.vector ADS_Offset; +.vector Flash_Offset; +.float Flash_Size; +.float currentmag2; +.float maxspeed; +.float renderGrayscale; +void end_sys_fields; + +/* +============================================================================== + + 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 + +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; + +// 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'; + +// 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; + +// 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; +float ATTN_NORM = 1; +float ATTN_IDLE = 2; +float ATTN_STATIC = 3; + +// entity effects +#define EF_BLUE 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_RED 8 +#define EF_ORANGELIGHT 16 +#define EF_GREEN 32 +#define EF_LIGHT 64 +#define EF_NODRAW 128 +#define EF_BRIGHTFIELD 256 +#define EF_FULLBRIGHT 512 +#define EF_DARKLIGHT 1024 +#define EF_DARKFIELD 2048 +#define EF_PURPLELIGHT 4096 +#define EF_RAYRED 8196 +#define EF_RAYGREEN 16384 + +// 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 +// +string string_null; // null string, nothing should be held here +entity activator; // the entity that activated a trigger or brush +float framecount; + +// +// cvars checked each frame +// +float teamplay; +float deathmatch; + +//================================================ + +// +// 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 +// +.float voided; +.float walkframe; + +// Zoid Additions +.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 +.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; + + +// +// 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; + +.float sprintflag; + +// +// 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; + +void() break = #6; +float() random = #7; // returns 0 - 1 +void(entity e, float chan, string samp, float vol, float atten) sound = #8; +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; +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; +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; +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 +float(entity zombie, float which, entity limb) updateLimb = #33; +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 +float(entity e) checkbottom = #40; // true if self is on ground +float(vector v) pointcontents = #41; // returns a CONTENT_* +float(float f) fabs = #43; +vector(entity e, float speed) aim = #44; // 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() ChangeYaw = #49; // turn towards self.ideal_yaw +vector(vector v) vectoangles = #51; +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; +string(string s) precache_file = #68; // no effect except for -copy +void(entity e) makestatic = #69; +void(string s) changelevel = #70; +void(string var, string val) cvar_set = #72; // sets cvar.value +void(entity client, string s) centerprint = #73; // sprint, but in middle +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 +void(entity e) setspawnparms = #78; // set parm1... to the +void(entity killer, entity killee) logfrag = #79; // add to stats +string(entity e, string key) infokey = #80; // get a key value (world = serverinfo) +float(string s) stof = #81; // convert string to float +void(vector where, float set) multicast = #82; +vector(entity what) Get_Waypoint_Near = #83; +float(entity zombie, entity target) Do_Pathfind_psp = #84; +void(string s) Open_Waypoint = #85; +vector(entity zombie,vector start,vector min, vector max) Get_Next_Waypoint = #86; +void(entity client, float type, float cost, float weapon) useprint = #87; +vector(entity zombie,vector start,vector min, vector max) Get_First_Waypoint = #88; +entity (entity start, .float field, float match) findfloat = #98; + +// New NZP custom ones +float(string filename, float mode) fopen = #110; +void(float fhandle) fclose = #111; +string(float fhandle) fgets = #112; +void(float fhandle, string s) fputs = #113; +float(string s) strlen = #114; +string(string s1, string s2) strcat = #115; +string(string s, float start, float length) substring = #116; +vector(string s) stov = #117; +string(string s) strzone = #118; +string(string s) strunzone = #119; +string(string s) strtrim = #120; +float(string s) tokenize = #130; // Was #441 +string(float num) argv = #131; // Was #442 +void(float intensity_small, float intensity_large, float duration) rumble = #132; + +float(entity targ, entity inflictor) CanDamage; + + +#define FILE_READ 0 +#define FILE_APPEND 1 +#define FILE_WRITE 2 +#define SVC_WEAPONFIRE 35 +#define SVC_HITMARK 36 +#define SVC_LIMBUPDATE 51 // naievil -- keep me +#define SVC_BSPDECAL 50 // naievil -- keep me +#define SVC_ACHIEVEMENT 52 + +#define VEC_ORIGIN '0 0 0' + +//Standard Quake View Offset +//vector VEC_VIEW_OFS = '0 0 22'; + +//Half Life View Offset is 64 +//64 units above ground, for our purposes it's, 64 - (how low bounding box goes) +vector VEC_VIEW_OFS = '0 0 32'; + +//Standard Quake Hull +//vector VEC_HULL_MIN = '-16 -16 -24'; +//vector VEC_HULL_MAX = '16 16 32'; + +//Half Life 1 Hull Sizes ADDED BY BLUBS, COMMENTED ORIGINAL QUAKE BBOX SIZS OUT +#define VEC_HULL_MIN '-16 -16 -32' +#define VEC_HULL_MAX '16 16 40' + +#define VEC_HULL2_MIN '-32 -32 -24' +#define VEC_HULL2_MAX '32 32 64' + +.string fog; +//string world_fog; + +.float button3; +.float button4; +.float button5; +.float button6; +.float button7; +.float button8; + +.float x2_icon; // double points icon +.float insta_icon; +.vector Flash_Offset; +.float Flash_Size; +.string Weapon_Name; + +.float currentHitBoxSetup; +.vector bbmins, bbmaxs; // Used for zombie hitboxes + +.entity head; +.entity larm; +.entity rarm; + +#define SOLID_CORPSE 5 // bsp clip, touch on edge, block +#define TE_EXPLOSION 3 +#define TE_GUNSHOT 2 +entity windows[32]; +float wincnt; + +.float recoil_delay; + +float pow(float base, float n) = { + float temp = base; + if (n == 0) { + return 1; + } else { + while (n > 0) { + temp = temp * base; + n = n - 1; + } + return temp; + } + return 0; +}; + +void enableGrayscale(void) = { + self.renderGrayscale = 1; +} + +void disableGrayscale(void) = { + self.renderGrayscale = 0; +} \ No newline at end of file diff --git a/source/server/dummies/generic.qc b/source/server/dummies/generic.qc new file mode 100644 index 0000000..578700c --- /dev/null +++ b/source/server/dummies/generic.qc @@ -0,0 +1,89 @@ +/* + server/dummies/generic.qc + + generic (all platform) dummys + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +// Triggers +void() trigger_relay = {remove(self);}; +void() trigger_changelevel = {remove(self);}; +void() trigger_counter = {remove(self);}; +void() trigger_secret = {remove(self);}; +void() trigger_setskill = {remove(self);}; +void() trigger_monsterjump = {remove(self);}; +void() trigger_onlyregistered = {remove(self);}; +void() trigger_push = {remove(self);}; +void() trigger_hurt = {remove(self);}; +// Player Starts +void() info_player_start = {}; +void() info_player_start2 = {}; +void() info_player_deathmatch = {}; +void() info_player_coop = {}; +void() info_player_tank = {}; +void() info_player_nikolai = {}; +void() info_player_doctor = {}; +void() info_player_takeo = {}; + +void() zapper1 = {remove(self);}; +void() zapper2 = {remove(self);}; +void() item_switch = {remove(self);}; +void() zap_light = {remove(self);}; +void() info_end = {remove(self);}; + +//// beta removal +void() monster_dog = {remove(self);}; +void() item_pap = {remove(self);}; +//void() item_juggernog = {remove(self);}; +//void() item_flopper = {remove(self);}; +//void() item_douple = {remove(self);}; +//void() item_speed = {remove(self);}; +//void() item_revive = {remove(self);}; +void() palm_tree_closed = {remove(self);}; +void() func_model = {remove(self);}; +void() wooden_crate = {remove(self);}; + +void() change_frame; +void() change_frame2 = +{ + self.frame++; + self.think = change_frame; + self.nextthink = time + 1; +} +void() change_frame = +{ + self.frame++; + self.think = change_frame2; + self.nextthink = time + 1; +} + +#ifdef PSP +void LoadWaypointData() = {}; +#endif + +// Old Demo defs +.float fogdogs; +.float MaxRange; +void() trigger_teleport = {remove(self);}; +void() tigger_mainframe = {remove(self);}; //lol this typo is in their old fgd too, apparently. +void() trigger_teleport_area = {remove(self);}; \ No newline at end of file diff --git a/source/server/dummies/vitanx.qc b/source/server/dummies/vitanx.qc new file mode 100644 index 0000000..d96482e --- /dev/null +++ b/source/server/dummies/vitanx.qc @@ -0,0 +1,157 @@ +/* + server/dummies/generic.qc + + dummys for vita and nx + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +// Dummys that should be here +void GiveAchievement(float achievement_id, optional entity who) = {}; +void print(string s) = {}; + +float tracemove(vector start, vector min, vector max, vector end, float nomonsters, entity forent) = {return 0;}; + +void(vector o, vector d, float color, float count) particle = {}; + +#define MOVE_HITMODEL 0 + +void() LoadWaypointData = {}; + +void() Soft_Restart = { + /* + entity who, oldself, doors, box, revive, endgame; + self = find(world,classname,"player"); + oldself = self; + + //remove all zombies + who = find(world,classname,"ai_zombie"); + while(who != world) + { + if(who.health) + { + self = who; + self.th_die(); + remove(self); //so dead bodies don't remain + self = oldself; + } + + who = find(who,classname,"ai_zombie"); + } + + //repair all windows + for(float i = 0; i < wincnt; i++) { + if (windows[i].health != -10) { + windows[i].health = 6; + windows[i].frame = 0; + } + } + + //close doors + doors = findfloat(world, isopen, 1); + while (doors) { + if (doors.isopen) + reclose_door(doors); + doors = findfloat(world, isopen, 1); + } + + //revert mystery box + box = find(world, classname, "mystery"); + if (box) { + box.boxstatus = 0; + box.frame = 0; + box.goaldummy.frame = 0; + boxCount = 0; + box.origin = boxOrigin; + //self = box; + + if (box.boxweapon) + remove(box.boxweapon); + + //mystery_box(); + //self = oldself; + } + + //reset quick revive + revive = find(world, classname, "perk_revive"); + if (revive) { + setmodel(revive, "models/machines/quick_revive.mdl"); + oldself.revivesoda = 0; + } + + //reset buyable ending + endgame = find(world, classname, "func_ending"); + if (endgame) { + endgame.activated = false; + } + + //reset teleporters + local entity tp; + tp = find(world, classname, "func_teleporter_entrance"); + + if (tp) { + tp.activated = false; + tp.isLinked = false; + tp.cooldown = false; + tp.waitLink = false; + tp.think = SUB_Null; + } + + local entity power; + power = find(world, classname, "power_switch"); + if(power) { + isPowerOn = false; + power.frame = 0; + } + + + + self = oldself; + self.downed = 0; + game_over = false; + rounds = 0; + self.score = 0; + self.points = 0; + self.secondaryweapon = 0; + InitRounds(); + self.isspec = false; + PutClientInServer(); + */ +} + +void () CL_SendWeaponFire = +{ + float return_time; + vector Wep_Recoil; + + Wep_Recoil = GetWeaponRecoil(self.weapon); + + msg_entity = self; + WriteByte(MSG_ONE, SVC_WEAPONFIRE); + return_time = getWeaponRecoilReturn(self.weapon); + WriteLong(MSG_ONE, return_time); + WriteCoord (MSG_ONE, Wep_Recoil_x); + WriteCoord (MSG_ONE, Wep_Recoil_y); + WriteCoord (MSG_ONE, Wep_Recoil_z); + //self.punchangle = Wep_Recoil; + self.recoil_delay = 60/return_time + time; +} diff --git a/source/server/entities/doors.qc b/source/server/entities/doors.qc new file mode 100644 index 0000000..93a2aa1 --- /dev/null +++ b/source/server/entities/doors.qc @@ -0,0 +1,696 @@ +/* + server/entities/doors.qc + + 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. + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/* +code for show that you need money EDITASAP +*/ +float DOOR_START_OPEN = 1; +float DOOR_DONT_LINK = 4; +float DOOR_DEBRIS = 8; +float DOOR_SILVER_KEY = 16; +float DOOR_TOGGLE = 32; +float DOOR_POWER = 64; + +entity activator; +.float delay; +.string killtarget; +.void() think1; +.vector finaldest; +.float dmg; +.entity trigger_field; +.float wait; +.float speed; +.vector pos1; +.vector pos2; +string string_null; +.float cost; +.float attack_delay; +.float lip; + +void() print_need_power = +{ + if(other.classname == "player" && !other.downed) + { + /*if (self.owner.message) + centerprint(other, self.owner.message); + else + centerprint(other, "Power must be activated first");*/ + + useprint (other, 8, 0, 0); + } + +}; +/* +============================================================================= + +THINK FUNCTIONS + +============================================================================= +*/ + +void() door_go_down; +void() door_go_up; + +void() door_blocked = +{ + DamageHandler (other, self, self.dmg, S_NORMAL); + +// 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 = +{ + self.state = STATE_TOP; + if ((self.classname == "door_nzp_cost" || self.classname == "door_nzp" || self.classname == "door_open")) + { + //remove (self.owner.trigger_field); //moto - what does this do lol + if (!(self.spawnflags & 128)) + setmodel(self, ""); + + self.isopen = 1; + return;//so we dont have to reopen doors + } + 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 = +{ + self.state = STATE_BOTTOM; +}; + +void() door_go_down = +{ + if ((self.classname == "door_nzp_cost" || self.classname == "door_nzp" || self.classname == "door_open")) + { + if (!(self.spawnflags & 128)) + setmodel(self, ""); + self.isopen = 1; + return;//so we dont have to reopen doors + } + if (self.max_health) + { + self.takedamage = DAMAGE_YES; + self.health = self.max_health; + } + + self.state = STATE_DOWN; + + if (self.spawnflags & 256) + SUB_CalcAngleMove(self.pos1, self.speed, door_hit_bottom); + else + 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; + } + + self.state = STATE_UP; + + if (self.spawnflags & 256) + SUB_CalcAngleMove(self.pos2, self.speed, door_hit_top); + else + SUB_CalcMove (self.pos2, self.speed, door_hit_top); + + #ifndef PC + Open_Waypoint(self.wayTarget); + #endif + SUB_UseTargets(); +}; + + +/* +============================================================================= + +ACTIVATION FUNCTIONS + +============================================================================= +*/ + +void() door_fire = +{ + local entity oself; + local entity starte; + + if (isPowerOn == FALSE) + { + if (self.spawnflags & DOOR_POWER ) + { + if(other.classname == "player" && !other.downed) + { + /*if (self.message) + centerprint(other, self.message); + else + centerprint(other, "Power must be activated first");*/ + useprint (other, 8, 0, 0); + return; + } + } + } + self.message = string_null; // no more message + oself = self; + + if (self.door_model_target) + { + entity tempe = find(world, classname, "func_door_model"); + if (tempe != world) { + ///door_model_name, self.door_model_target + if (tempe.door_model_name == self.door_model_target) { + setmodel(tempe, ""); + remove(tempe); + } else { + bprint(PRINT_HIGH, "Could not find door_model_name: "); + bprint(PRINT_HIGH, self.door_model_target); + bprint(PRINT_HIGH, "\n"); + } + } + } + + 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.isopen = true; + self = self.enemy; + } while ( (self != starte) && (self != world) ); + self = oself; + + SUB_UseTargets(); +}; + +void() cost_door = +{ +if (self.state == STATE_TOP || self.state == STATE_UP) + return; + + if (isPowerOn == FALSE) + { + if (self.spawnflags & DOOR_POWER ) + { + if(other.classname == "player" && !other.downed) + { + /*if (self.message) + centerprint(other, self.message); + else + centerprint(other, "Power must be activated first");*/ + useprint (other, 8, 0, 0); + return; + } + } + } + + if (other.button7) + { + if (other.points >= self.cost) + { + door_fire(); + sound(self, 0,"sounds/misc/ching.wav", 1, 1); + sound(self, CHAN_ITEM,"sounds/misc/buy.wav", 1, 1); + switch(self.sounds) + { + case 1: + sound(self, CHAN_ITEM,"sounds/misc/wood_door.wav", 1, 1); + break; + case 2: + sound(self, CHAN_ITEM,"sounds/misc/debris.wav", 1, 1); + break; + default: + break; + } + addmoney(other, self.cost*-1, 0); + self.solid = SOLID_NOT; + //centerprint (other, ""); + //useprint (other, 0, 0, 0); + //other.total_bought = other.total_bought + 1; + } + else + { + if(other.classname == "player" && !other.downed) + { + centerprint (other, "You do not have enough points\n"); + other.semiuse = 1; + } + } + } + else if (!other.button7) + { + if(other.classname == "player" && !other.downed) + { + /*if (!self.message) + centerprint3(other, "press use to open door for ", b = ftos (self.cost), " points\n"); + else + centerprint (other, self.message);*/ + if (self.spawnflags & DOOR_DEBRIS) + useprint (other, 2, self.cost, 0); + else + useprint (other, 1, self.cost, 0); + return; + } + } +}; +void() door_use = +{ + local entity oself; + + oself = self; + self = self.owner; + if (self.cost) + cost_door(); + else + door_fire (); + self = oself; +}; + + +void() door_trigger_touch = +{ + if(other.classname != "player") + { + return; + } + + if(cvar("waypoint_mode")) + { + if(other.active_door != self.owner) + { + bprint(PRINT_HIGH, "Current Door for special waypoints set to "); + bprint(PRINT_HIGH, self.owner.wayTarget); + bprint(PRINT_HIGH, "\n"); + other.active_door = self.owner; + } + return; + } + + if (other.health <= 20) + return; + + 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; // will 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 (other.button7) + { + return; + } + if (self.owner.message != "") + { + centerprint (other, self.owner.message); + } + + if (isPowerOn == FALSE) + { + if (self.owner.spawnflags & DOOR_POWER) + { + self.touch = print_need_power; + return; + } + } + 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; + setorigin(trigger, self.origin); + + t1 = fmins; + t2 = fmaxs; + setsize (trigger, t1/* - '15 15 8'*/, t2/* + '15 15 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; + + 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 ); +}; + +/* +===================== +SPECIAL DOORS +thease doors can be opened by money or when power is on +===================== +*/ + +void() func_door_model = +{ + place_model(); +} + +void() func_door = + +{ + + 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.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.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; +}; + +void() func_door_nzp = +{ + #ifdef PSP + if (!self.renderamt) + self.renderamt = 255; + + if (!self.rendermode) + self.rendermode = 4; + + if (!self.rendercolor) + self.rendercolor = '0 0 0'; + + if (!self.mapversion) + self.mapversion = 0; + + if (!self.ammo) + self.ammo = 0; //thease are just here because they can be sp there is no error message. serve no real purpose + #endif + + // naievil (FIXME) ^^^^ hl rendermodes, mapversion + makevectors(self.angles); + SetMovedir (); + + self.target2 = self.target; + self.target3 = self.target; + self.target4 = self.target; + self.target5 = self.target; + self.target6 = self.target; + self.target7 = self.target; + self.target8 = self.target; + + self.max_health = self.health; + self.solid = SOLID_BSP; + self.movetype = MOVETYPE_PUSH; + self.oldmodel = self.model; + self.oldorigin = self.origin; + self.oldstate = self.state; + setorigin (self, self.origin); + setmodel (self, self.model); + + self.blocked = door_blocked; + self.use = door_use; + + if (self.cost) + { + self.classname = "door_nzp_cost"; + // total_buy = total_buy +1; + } + else + self.classname = "door_nzp"; + + if (!self.speed) + self.speed = 100; + if (!self.wait) + self.wait = 3; + if (!self.lip) + self.lip = 8; + if (!self.dmg) + self.dmg = 2; + + // only rotate on the Y axis.. potentially change? + if (self.spawnflags & 256) { + self.pos1 = self.angles; + self.pos2 = self.pos1; + self.pos2_y = self.pos1_y + self.distance; + } else { + 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.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; +}; \ No newline at end of file diff --git a/source/server/entities/lights.qc b/source/server/entities/lights.qc new file mode 100644 index 0000000..0e59ce6 --- /dev/null +++ b/source/server/entities/lights.qc @@ -0,0 +1,118 @@ +/* + server/entities/lights.qc + + Spawns and handles Quake's lights and torches + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +float START_OFF = 1; // Light on/off spawnflag +void() Light_setup; // Definition from Lights.qc + +void(string modelname) Precache_Set = // Precache model, and set myself to it +{ + precache_model(modelname); + setmodel(self, modelname); +}; + + +void() light = // Basic Light +{ + Light_setup(); // Setup Light +} + +void() light_fluoro = // Light with hum ambient +{ + Light_setup(); // Setup Light +}; + +void() light_environment = // Basic Light +{ + makestatic(self); // Static entity. Never changes. +} + +void() light_fluorospark = // Light with buzz ambient +{ + Light_setup(); // Setup Light +}; + +void() light_globe = // Light with visible globe +{ + Precache_Set("progs/s_light.spr"); // Set model + makestatic(self); // Static entity. Never changes. +} + +void() light_torch_small_walltorch = // Light with visible wall torch +{ + Precache_Set("progs/flame.mdl"); // Set model + makestatic(self); // Static entity. Never changes. +}; + +void() light_flame_small_yellow = // Light with small flame & fire sound +{ + Precache_Set("progs/flame2.mdl"); // Set model + makestatic(self); // Static entity. Never changes. +}; + +void() light_flame_large_yellow = // Light with larger flame & fire sound +{ + Precache_Set("progs/flame2.mdl"); // Set model + self.frame = 1; // Switch to second frame (large) + makestatic(self); // Static entity. Never changes. +}; + +void() light_flame_small_white = // Light with small flame & fire sound +{ + Precache_Set("progs/flame2.mdl"); // Set model + makestatic(self); // Static entity. Never changes. +}; + +void() Light_setup = // Set light on or off, as per spawnflags +{ + if (self.style < 32) { + return; + } // Dont switch other styles. + + if (self.spawnflags & START_OFF) + lightstyle(self.style, "a"); // If light starts off, set it off. + else + lightstyle(self.style, "m"); // If light starts ON, turn in ON. Simple :) +} + +void() LightStyles_setup = +{ + // Setup light animation tables. 'a' is total darkness, 'z' is maxbright. + + lightstyle(0,"m"); // Style 0: Normal + lightstyle(1,"mmnmmommommnonmmonqnmmo"); // Style 1: Flicker + lightstyle(2,"abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); // Style 2: Slow Strong Pulse + lightstyle(3,"mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); // Style 3: Candle + lightstyle(4,"mamamamamama"); // Style 4: Fast Strobe + lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj"); // Style 5: Gentle Pulse + lightstyle(6,"nmonqnmomnmomomno"); // Style 6: Flicker 2 + lightstyle(7,"mmmaaaabcdefgmmmmaaaammmaamm"); // Style 7: Candle 2 + lightstyle(8,"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); // Style 8: Candle 3 + lightstyle(9,"aaaaaaaazzzzzzzz"); // Style 9: Slow Strobe + lightstyle(10,"mmamammmmammamamaaamammma"); // Style 10: Fluro + lightstyle(11,"abcdefghijklmnopqrrqponmlkjihgfedcba"); // Style 11: Slow Pulse (no black) +}; + \ No newline at end of file diff --git a/source/server/entities/machines.qc b/source/server/entities/machines.qc new file mode 100644 index 0000000..b989485 --- /dev/null +++ b/source/server/entities/machines.qc @@ -0,0 +1,2068 @@ +/* + server/entities/machines.qc + + mbox, perks, pap + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +float backupWepSkin; +float sound_perk_delay; +void() W_Switch; +void() W_TakeOut; +void() mystery_touch; + +// +// -------------------- +// Lights +// -------------------- +// + +// +// Light_Red(e) +// RGB: 255, 56, 56 +// +void(entity e) Light_Red = +{ + e.effects = e.effects | EF_FULLBRIGHT; + #ifndef PC + e.effects = e.effects | EF_RED; + #else + { + e.pflags = PFLAGS_FULLDYNAMIC; + e.light_lev = 100; + e.color_x = 2; + e.color_y = 0.25; + e.color_z = 0.25; + } + #endif +} + +// +// Light_Green(e) +// RGB: 56, 255, 56 +// +void(entity e) Light_Green = +{ + e.effects = e.effects | EF_FULLBRIGHT; + #ifndef PC + e.effects = e.effects | EF_GREEN; + #else + { + e.pflags = PFLAGS_FULLDYNAMIC; + e.light_lev = 100; + e.color_x = 0.25; + e.color_y = 2; + e.color_z = 0.25; + } + #endif +} + +// +// Light_Blue(e) +// RGB: 56, 56, 255 +// +void(entity e) Light_Blue = +{ + e.effects = e.effects | EF_FULLBRIGHT; + #ifndef PC + e.effects = e.effects | EF_BLUE; + #else + { + e.pflags = PFLAGS_FULLDYNAMIC; + e.light_lev = 100; + e.color_x = 0.25; + e.color_y = 0.25; + e.color_z = 2; + } + #endif +} + +// +// Light_Orange(e) +// RGB: 255, 128, 0 +// +void(entity e) Light_Orange = +{ + e.effects = e.effects | EF_FULLBRIGHT; + #ifndef PC + e.effects = e.effects | EF_ORANGELIGHT; + #else + { + e.pflags = PFLAGS_FULLDYNAMIC; + e.light_lev = 100; + e.color_x = 2; + e.color_y = 1; + e.color_z = 0; + } + #endif +} + +// +// Light_Purple(e) +// RGB: 255, 28, 255 +// +void(entity e) Light_Purple = +{ + e.effects = e.effects | EF_FULLBRIGHT; + #ifndef PC + e.effects = e.effects | EF_PURPLELIGHT; + #else + { + e.pflags = PFLAGS_FULLDYNAMIC; + e.light_lev = 100; + e.color_x = 2; + e.color_y = 0.25; + e.color_z = 2; + } + #endif +} + +// +// Light_None(e) +// Resets all Perk Light Effects. +// +void(entity e) Light_None = +{ + e.effects = 0; + #ifdef PC + { + e.pflags = 0; + e.light_lev = 0; + e.color_x = 0; + e.color_y = 0; + e.color_z = 0; + } + #endif +} + +// +// Light_Custom(e, r, g, b) +// For custom Perk Light Effects, generally not recommended +// unless you're testing a cool selection of colors and do not +// want to constantly recompile the client, or the client is +// inaccessible. Only functional on PC/FTE atm (TODO?) +// +void(entity e, float r, float g, float b) Light_Custom = +{ + e.effects = e.effects | EF_FULLBRIGHT; + #ifdef PC + { + e.pflags = PFLAGS_FULLDYNAMIC; + e.light_lev = 100; + e.color_x = r/255; + e.color_y = g/255; + e.color_z = b/255; + } + #endif +} + +// +// -------------------- +// Core Perk System +// -------------------- +// + +// +// GivePerk(p) +// Restores View Model and tells the Client to draw the Perk. +// +void GivePerk(optional float p) { + local float perk; + + // First of, check if our Client is holding anything, this holds + // priority to prevent interruption. + if (self.style != 0) { + perk = self.style; + self.style = 0; + } + // Next, check if a perk was explicitly set + else if (p) { + perk = p; + } + // Executed without context.. Nothing to do. + else { + return; + } + + // No Perks? No Problem tracker + self.ach_tracker_npnp++; + + switch(perk) { + case P_REVIVE: + self.perks = self.perks | 8; + break; + case P_FLOP: + self.perks = self.perks | 16; + break; + case P_JUG: + self.perks = self.perks | 1; + break; + case P_STAMIN: + self.perks = self.perks | 32; + break; + case P_SPEED: + self.perks = self.perks | 4; + break; + case P_DOUBLE: + self.perks = self.perks | 2; + break; + case P_DEAD: + self.perks = self.perks | 64; + break; + case P_MULE: + self.perks = self.perks | 128; + break; + default: + break; + } + + if (self.perks == 255) + GiveAchievement(6, self); + + sound_perk_delay = time + 4.5; + self.isBuying = false; + string modelname = GetWeaponModel(self.weapon, 0); + + local float startframe = GetFrame(self.weapon,TAKE_OUT_START); + local float endframe = GetFrame(self.weapon,TAKE_OUT_END); + + self.weaponskin = backupWepSkin; + self.perk_delay = self.fire_delay; + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH); + + SetPerk(self, self.perks); +} + +// +// DrinkPerk(perk) +// Gives Perk Bottle and jumps to GivePerk(p) +// +void DrinkPerk(float perk) { + // Set the Perk Skin based on the Perk sequence. + // NOTE: If you add perks, you will either need to append + // the MDL with a tool like QuArK or add an edge-case + // after this instruction to have a valid skin. + other.weaponskin = self.sequence - 1; + + other.isBuying = true; + entity tempe = self; + self = other; + + if (self.sprinting) + self.sprinting = 0; + + self.maxspeed *= GetWeaponWalkSpeed(self.perks, self.weapon); + + self.knife_delay = self.reload_delay2 = self.fire_delay2 = self.fire_delay = self.reload_delay = 4.5 + time; + Set_W_Frame (tempe.weapon_animduration, tempe.weapon2_animduration, 0, 0, PERK, GivePerk, tempe.weapon2model, true, S_RIGHT); + self = tempe; + sound(other, CHAN_ITEM, self.oldmodel, 1, ATTN_NORM); + + // Play Perk Sting if we can and it exists + if (self.aistatus && self.ltime < time) { + sound(self, CHAN_AUTO, self.aistatus, 0.25, ATTN_IDLE); + self.ltime = time + self.anim_weapon_time; + } +} + +// +// SpawnSpark(where, time_alive) +// Technically a utilty function, spawns a spark/elec effect +// at the given origin and lasts until the time given is up. +// +void() SparkThink = +{ + self.frame++; + + if (self.frame >= 3) + self.frame = 0; + + // suicide timer! + if(self.ltime < time) { + remove(self); + } + + self.nextthink = time + 0.05; +} + +void(vector where, float time_alive) SpawnSpark = +{ + entity spark = spawn(); + setmodel(spark, "models/sprites/lightning.spr"); + setorigin(spark, where); + spark.think = SparkThink; + spark.nextthink = time + 0.05; + spark.ltime = time + time_alive; +} + +// +// ReviveGoAway() +// Called when the max amount of solo revives have been used. +// Does a little animation and then 'teleports' away. +// +void() StopReviveAnimation = +{ + self.velocity = 0; + + setmodel(self, ""); // We don't want to remove Revive in the event of a Soft_Restart.. + SpawnSpark(self.origin, 0.65); // Spawn a Spark at the position + self.origin = self.oldvelocity; // Restore old position + Light_None(self); // Remove light effect + + sound(self,CHAN_ITEM,"sounds/pu/drop.wav",1,ATTN_NONE); // Play a fitting sound +} + +void() ReviveAnimation = +{ + if (self.score == 0) + self.angles_z += 6; + else + self.angles_z -= 6; + + if (self.angles_z - self.movement_z >= 20) + self.score = 1; + else if (self.angles_z - self.movement_z <= -20) + self.score = 0; + + if(self.ltime < time) { + StopReviveAnimation(); + } else { + self.nextthink = time + 0.05; + } +} + +void() ReviveGoAwayForReal = +{ + makevectors(self.angles); + self.movetype = MOVETYPE_NOCLIP; + self.velocity = v_up * 7; + + sound(self, CHAN_ITEM, "sounds/pu/byebye.wav", 1, ATTN_NORM); + + // Do our silly little animation for 6 seconds + self.ltime = time + 6; + self.think = ReviveAnimation; + self.nextthink = time + 0.05; +} + +void() ReviveGoAway = +{ + // First start a timer, we want to wait until the player finishes drinking + // before starting the animation. + self.think = ReviveGoAwayForReal; + self.nextthink = time + 4; + + // Save our original position to restore on restart + self.oldvelocity = self.origin; + + // Same with angle + self.movement = self.angles; +} + +// +// touch_perk() +// Executed on touch; prompt purchase and drink if requirements met. +// ----- +// MotoLegacy (10-28-20) - Made modular using some existing entity fields we had, +// as the memory is allocated to all entities on load anyway. This comment applies +// to all other relevant jumps as well. +// +void() touch_perk = +{ + if (other.classname != "player" || other.downed || other.isBuying == true || !isFacing(other, self)) + return; + + // If we're proned and the map permits, collect some spare change + if (other.stance == 0 && self.collected == false && G_PRONEPOINTS != 0) { + self.collected = true; + addmoney(other, G_PRONEPOINTS, 0); + sound(self, 0,"sounds/misc/ching.wav", 1, 1); + } + + // Do this after the perk change check + if (other.stance == 0) + return; + + // Power's off! Nothing to do here. + if (self.requirespower == true && !isPowerOn) { + useprint (other, 8, 0, 0); + return; + } + + // Back up weapon skin + backupWepSkin = other.weaponskin; + + // Don't prompt if our hands or busy or we already have the perk + if (!(other.perks & self.style) && other.fire_delay < time && self.revivesoda < 3) { + // sequence = perk ID in client + useprint(other, 9, self.cost, self.sequence); + + if (other.points >= self.cost && other.button7) { + addmoney(other, -self.cost, 0); + + // Pass along the Perk information to the Player to avoid complications later on, then Drink! + other.style = self.style; + DrinkPerk(self.style); + + // Do Self-Revive checks + if (self.classname == "perk_revive" && !coop && !deathmatch) { + // Increment self-revive count + self.revivesoda++; + + if (self.revivesoda >= 3) { + ReviveGoAway(); + } + } + } else if (other.button7) { + // We tried to use, but we don't have the cash.. + centerprint(other, "Not enough points\n"); + } + } +} + +// +// Turn_PerkLight_On(who) +// Turns on the Perk Lights +// +void(entity who) Turn_PerkLight_On = +{ + if (who.spawnflags & 1) { + Light_None(who); + } + if (who.spawnflags & 2) { + Light_Red(who); + } + if (who.spawnflags & 4) { + Light_Green(who); + } + if (who.spawnflags & 8) { + Light_Blue(who); + } + if (who.spawnflags & 16) { + Light_Orange(who); + } + if (who.spawnflags & 32) { + Light_Purple(who); + } +} + +// +// Perk_Jingle() +// The Think loop for Perk Jingles +// +void() Perk_Jingle = +{ + // If Revive is gone, don't bother to check anymore + if (self.revivesoda >= 3) + return; + + local float jinglewaittime; + jinglewaittime = rint((random() * 30)) + 30; + + // Just try again later if the Power isn't Activated + if ((self.requirespower == true && !isPowerOn) || self.ltime > time) { + self.nextthink = time + jinglewaittime; + } else { + // 15% Chance to Play Jingle + local float chance = random(); + if (chance <= 0.15) { + sound(self, CHAN_AUTO, self.powerup_vo, 0.25, ATTN_IDLE); + self.ltime = time + self.anim_weapon2_time; + jinglewaittime += self.anim_weapon2_time; + } + + self.nextthink = time + jinglewaittime; + } +} + +// +// setup_perk() +// Readies up misc. Perk aesthetics +// +void() setup_perk = +{ + local entity power = find(world, classname, "power_switch"); + + // Check for Revive before Power + if (self.classname == "perk_revive" && !coop && !deathmatch) { + Turn_PerkLight_On(self); + } else { + // Check for Power + if (power != world) { + self.requirespower = true; + } else { + // There's no Power Switch, so turn the rest On. + Turn_PerkLight_On(self); + } + } + + // Perk Jingle Timer + if (self.powerup_vo) { + self.think = Perk_Jingle; + self.nextthink = time + rint((random() * 30)) + 30; + } +} + +// +// -------------------- +// Perk Entity Spawn Functions +// -------------------- +// + +// Quick Revive +void() perk_revive = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/quick_revive.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 500; + self.cost2 = 1500; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 8; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + precache_sound("sounds/weapons/papfire.wav"); + precache_extra(W_BIATCH); + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_revive"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 1; + self.style = P_REVIVE; +}; + +// PhD Flopper +void() perk_flopper = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/flopper.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 2000; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 16; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_flopper"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 6; + self.style = P_FLOP; +}; + +// Jugger-Nog +void() perk_juggernog = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/juggernog.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 2500; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 2; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_juggernog"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 2; + self.style = P_JUG; +}; + +// Stamin-Up +void() perk_staminup = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/staminup.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 2000; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 16; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_staminup"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 5; + self.style = P_STAMIN; +}; + +// Speed Cola +void() perk_speed = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/speed_cola.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 3000; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 4; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_speed"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 3; + self.style = P_SPEED; +}; + +// Double Tap Door Beer +void() perk_double = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/double_tap.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 2000; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 16; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_double"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 4; + self.style = P_DOUBLE; +}; + +// Deadshot Daiquiri +void() perk_deadshot = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/deadshot.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 1500; + self.cost2 = 1000; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 16; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + setorigin(self, self.origin); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_deadshot"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Different price on PC, for lack of Aim Assist + #ifdef PC + self.cost = self.cost2; + #endif + + // Drink Information + self.sequence = 7; + self.style = P_DEAD; +}; +// naievil -- older maps compatability +void() perk_dead = { perk_deadshot();} + +// Mule Kick +void() perk_mule = +{ + // + // Set Default Stats for Compatibility + // + + // Model + if (!self.model) { + self.model = "models/machines/mulekick.mdl"; + } + + // Perk Cost + if (!self.cost) { + self.cost = 4000; + } + + // Player Trigger Sound + if (!self.oldmodel) { + self.oldmodel = "sounds/machines/perk_drink.wav"; + } + + // View Model + if (!self.weapon2model) { + self.weapon2model = "models/machines/v_perk.mdl"; + } + + // View Model Start Frame + if (!self.weapon_animduration) { + self.weapon_animduration = 0; + } + + // View Model End Frame + if (!self.weapon2_animduration) { + self.weapon2_animduration = 22; + } + + // Light Effect + if (!self.spawnflags) { + self.spawnflags = self.spawnflags | 4; + } + + // Set up the Perk Entity + self.solid = SOLID_TRIGGER; + precache_model(self.model); + precache_model(self.weapon2model); + precache_sound(self.oldmodel); + if (self.aistatus) { precache_sound(self.aistatus); } + if (self.powerup_vo) { precache_sound(self.powerup_vo); } + setorigin(self, self.origin); + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_mule"; + self.touch = touch_perk; + self.think = setup_perk; + self.nextthink = time + 0.1; + + // Drink Information + self.sequence = 8; + self.style = P_MULE; +} + +// +// -------------------- +// Power Switch +// -------------------- +// + +void() turnpower1 =[ 1, turnpower2 ] {self.frame = 1;}; +void() turnpower2 =[ 2, turnpower3 ] {self.frame = 2;}; +void() turnpower3 =[ 3, turnpower4 ] {self.frame = 3;}; +void() turnpower4 =[ 4, SUB_Null ] {self.frame = 4;}; + +// +// Turn_Lights() +// Activates Perk Light Effects +// +void() Turn_Lights = +{ + local entity tempe = findfloat(world, requirespower, 1); + while (tempe) + { + Turn_PerkLight_On(tempe); + tempe = findfloat (tempe, requirespower, 1); + } +} + +// +// touch_power_Switch() +// Power Switch touch function; play Animation and do Power things.. +// +void() touch_power_Switch = +{ + if (other.classname != "player" || other.downed) + return; + + if (isPowerOn == true) + return; + + local entity tempe; + local entity old_self; + + useprint (other, 10, 0, 0); + if (other.button7 && !isPowerOn) { + turnpower1(); + isPowerOn = true; + sound(self, CHAN_ITEM, "sounds/machines/power.wav", 1, ATTN_NONE); + + tempe = find (world, classname, "door_nzp");// trigger all paired doors + while (tempe) { + local entity starte; + + if (!tempe.targetname) { + if (!tempe.cost) { + old_self = self; + self = tempe; + starte = self; + door_go_up (); + tempe.classname = "door_open"; + tempe = find (tempe, classname, "door_nzp");// trigger all paired doors + self = old_self; + } + } + if (tempe.targetname) { + old_self = self; + self = tempe; + remove(self); + self = old_self; + tempe = find (tempe, classname, "door_nzp");// trigger all paired doors + } + } + Turn_Lights(); + } +} + +void() power_switch = +{ + self.solid = SOLID_TRIGGER; + precache_model ("models/machines/power_switch.mdl"); + precache_sound ("sounds/machines/power.wav"); + setorigin (self, self.origin); + setmodel (self, "models/machines/power_switch.mdl"); + setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "power_switch"; + isPowerOn = false; + self.touch = touch_power_Switch; +} + +// +// -------------------- +// Mystery Box +// -------------------- +// + +void() updateBoxGlow +{ + if(self.goaldummy) + { + self.goaldummy.frame = self.frame; + } +}; + + +void() box_open1 =[ 1, box_open2 ] { self.frame = 1;updateBoxGlow(); Light_Orange(self);}; +void() box_open2 =[ 2, box_open3 ] { self.frame = 2;updateBoxGlow();}; +void() box_open3 =[ 3, box_open4 ] { self.frame = 3;updateBoxGlow();}; +void() box_open4 =[ 4, box_open5 ] { self.frame = 4;updateBoxGlow();}; +void() box_open5 =[ 5, box_open6 ] { self.frame = 5;updateBoxGlow();}; +void() box_open6 =[ 6, box_open7 ] { self.frame = 6;updateBoxGlow();}; +void() box_open7 =[ 7, box_open8 ] { self.frame = 7;updateBoxGlow();}; +void() box_open8 =[ 8, SUB_Null ] { self.frame = 8;updateBoxGlow();}; + +void() resetbox = +{ + self.frame = 0; + self.boxstatus = 0; +}; + + +void() box_close1 =[ 9, box_close2 ] { self.frame = 9;updateBoxGlow();sound (self, CHAN_ITEM, "sounds/machines/mbox_close.wav", 1, ATTN_NORM); Light_None(self);}; +void() box_close2 =[ 10, box_close3 ] { self.frame = 10;updateBoxGlow();}; +void() box_close3 =[ 11, box_close4 ] { self.frame = 11;updateBoxGlow();}; +void() box_close4 =[ 12, resetbox ] { self.frame = 12;updateBoxGlow();}; + +// +// Getweaponid() +// Returns weapon ID from Mbox ID, as they are slightly different +// because.. reasons.. +// +float(float r) Getweaponid = +{ + switch(r) { + case 0: + return W_COLT; + break; + case 1: + return W_KAR; + break; + case 2: + return W_DB; + break; + case 3: + return W_MG; + break; + case 4: + return W_RAY; + break; + case 5: + return W_THOMPSON; + break; + case 6: + return W_M2; + break; + case 7: + return W_PPSH; + break; + case 8: + return W_SAWNOFF; + break; + case 9: + return W_TESLA; + break; + case 10: + return W_M1A1; + break; + case 11: + return W_GEWEHR; + break; + case 12: + return W_FG; + break; + case 13: + return W_BROWNING; + break; + case 14: + return W_KAR_SCOPE; + break; + case 15: + return W_357; + break; + case 16: + return W_STG; + break; + case 17: + return W_PANZER; + break; + case 18: + return W_BK; + break; + case 19: + return W_PTRS; + break; + case 20: + return W_MP40; + break; + case 21: + return W_TRENCH; + break; + case 22: + return W_BAR; + break; + case 23: + return W_M1; + break; + case 24: + return W_TYPE; + break; + } + + return r; +} + +// +// randomweapon() +// Returns a Weapon if permitted by the map's MBOX data file. +// +float() randomweapon = +{ + local float r; + r = rint((random() * 24)); + + // If this weapon is in our Box Array, we can return it. + if (BoxWeapons[r] == 1) { + return r; + } else { // It's not in the Array, try again until we find one. + return randomweapon(); + } +}; + +// +// CheckWeapon(w, user) +// Checks all 3 weapon slots to see if the Player is holding specified +// Weapon, ensures we do not give Duplicates. +// +float CheckWeapon (float w, entity user) = +{ + // Non-PaP Weapons + if (user.weapon == w || user.secondaryweapon == w || user.thirdweapon == w) + return 0; + + // PaP Weapons + if (EqualNonPapWeapon(user.weapon) == w || EqualNonPapWeapon(user.secondaryweapon) == w || EqualNonPapWeapon(user.thirdweapon) == w) + return 0; + + // We passed both, this weapon is okay + return 1; +}; + +// +// Reset_MBox() +// Resets the Mystery Box to it's inital State. +// +void() Reset_MBox = +{ + entity tempe; + self.velocity = '0 0 0'; + tempe = self; + self = self.owner; + box_close1(); + self = tempe; + self.owner.owner = world; + self.owner.boxstatus = 0; + remove (self); +} + +// +// Float_Decreate() +// Make the Gun in the Box slowly descend, eventually +// resetting the Box. +// +void() Float_Decrease = +{ + makevectors(self.angles); + self.velocity = v_up*-5; + self.nextthink = time + 7; + self.think = Reset_MBox; +} + +// +// findboxspot() +// Locate a new MBox spot and turn this spot into +// a tp_spot. +// +void() findboxspot = +{ + local entity newspot; + local float box = rint(random(boxCount)); + newspot = boxLocations[box]; + + // Ensure the spot we choose is valid. + while(newspot == world || newspot == self.owner) { + box = rint(random(boxCount)); + newspot = boxLocations[box]; + } + + // Make our current spot a tp_spot + self.owner.model = "models/props/teddy.mdl"; + self.owner.frame = 2; + setmodel(self.owner, self.owner.model); + self.owner.classname = "mystery_box_tp_spot"; + self.owner.touch = SUB_Null; + self.owner.angles_y -= 90; + Light_None(self.owner); + + newspot.angles_y += 90; + + // Spawn the Box Glow if permitted + #ifndef PC + if (!(self.owner.spawnflags & 2)) + { + local entity g; + g = spawn(); + newspot.goaldummy = g; + setmodel(g,"models/machines/mglow$.mdl"); + setorigin(g,newspot.origin); + g.angles = newspot.angles; + } + #endif + + // Remove teddy + remove(self); + + // Set some values and change the found Spot to an MBox + newspot.spins = 0; + newspot.boxstatus = 0; + newspot.touch = mystery_touch; + newspot.solid=SOLID_TRIGGER; + newspot.classname = "mystery"; + setorigin(newspot, newspot.origin); + setmodel (newspot, "models/machines/mystery.mdl"); + newspot.frame = 0; + setsize (newspot, VEC_HULL2_MIN, VEC_HULL2_MAX); +} + +void() remove_box = +{ + //setmodel(self.owner.goaldummy, ""); + self.owner.frame = 0; // set box frame + makevectors(self.angles); + self.velocity = v_up*100; + self.think = findboxspot; + self.nextthink = time + 2; +} + +void() Float_Change = +{ + entity tpspot; + float r, tempf, teddygen; + string temps; + + tempf = randomweapon(); + r = Getweaponid(tempf); + while (!CheckWeapon (r, self.owner.owner)) + { + tempf = randomweapon(); + r = Getweaponid(tempf); + } + temps = GetWeaponModel(r, 1); + setmodel (self, temps); + + self.boxstatus = self.boxstatus + 0.01; + if (self.wait <= time) + { + tpspot = find(world, classname, "mystery_box_tp_spot"); + if (tpspot && self.owner.spins > 3) { + teddygen = random(); + } else { + teddygen = 0; + //bprint(PRINT_HIGH, "no spots\n"); + } + + self.velocity = '0 0 0'; + + if (!teddygen || teddygen < 0.7) { //teddy gen threshold, high means less chance + self.owner.boxstatus = 2; + self.weapon = r; + self.nextthink = time + 5; + self.think = Float_Decrease; + return; + } + else { + addmoney(self.owner.owner, 950, 0); + self.model = "models/props/teddy.mdl"; + setmodel(self, self.model); + self.angles_y = self.angles_y - 90; + self.nextthink = time + 5; + self.think = remove_box; + sound (self, CHAN_ITEM, "sounds/pu/byebye.wav", 1, ATTN_NORM); + ach_tracker_luck++; + + if (ach_tracker_luck >= 10) + GiveAchievement(11); + return; + } + } + if (self.ltime <= time) + { + self.velocity_z = self.velocity_z*0.5; + self.ltime = 0.5; + } + self.nextthink = time + self.boxstatus; + self.think = Float_Change; +} + +void() Create_Floating_Weapon = +{ + entity gun; + float r, tempf; + string temps; + + tempf = randomweapon(); + r = Getweaponid(tempf); + while (!CheckWeapon (r, self.owner.owner)) + { + tempf = randomweapon(); + r = Getweaponid(tempf); + } + temps = GetWeaponModel(r, 1); + + gun = spawn(); + + setorigin (gun, self.origin); + setmodel (gun, temps); + setsize (gun, '0 0 0', '0 0 0'); + gun.angles = self.angles; + gun.effects = EF_FULLBRIGHT; + + gun.movetype = MOVETYPE_NOCLIP; + gun.solid = SOLID_NOT; + makevectors(self.angles); + gun.velocity = v_up*15; + + gun.owner = self; + self.boxweapon = gun; + + gun.ltime = time+2; + gun.boxstatus = 0.01; + gun.wait = time + 5; + gun.nextthink = time + 0.01; + gun.think = Float_Change; +} + +void() mystery_box_tp_spot = +{ + precache_model ("models/props/teddy.mdl"); + + self.solid=SOLID_TRIGGER; + self.classname = "mystery_box_tp_spot"; + setorigin(self, self.origin); + setmodel (self, "models/props/teddy.mdl"); + setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + if (self.model == "progs/Misc/teddy.mdl") { + self.frame = 2; + self.angles_y -= 90; + } + + boxLocations[boxCount] = self; + boxCount++; +}; + +void() mystery_touch = +{ + entity tempe; + float startframe, endframe; + string modelname; + + if (other.classname != "player" || other.downed) + return; + + if (!self.boxstatus) { + useprint (other, 6, 950, 0); + } + if (self.boxstatus == 2 && self.owner == other) { + useprint (other, 7, 0, 0); + } + + if (other.button7 && !other.semiuse) + { + if (!self.boxstatus) + { + if (other.points >= 950) + { + sound (self, CHAN_ITEM, "sounds/machines/mbox_open.wav", 1, ATTN_NORM); + addmoney(other, -950, FALSE); + self.boxstatus = 1; + self.owner = other; + box_open1 (); + Create_Floating_Weapon(); + self.spins++; + } + else { + centerprint (other, "Not enough points\n"); + } + } + if (self.boxstatus == 2) + { + other.semiuse = 1; + if (self.owner == other) + { + other.reload_delay = 0; + self.boxstatus = 0; + self.owner = world; + if (other.weapon != self.boxweapon.weapon && other.secondaryweapon != self.boxweapon.weapon && other.secondaryweapon && other.thirdweapon != self.boxweapon.weapon) + { + if ((other.perks & P_MULE) && !other.thirdweapon) { + // store secondary weapon + local float tempf = other.secondaryweapon; + local float tempf1 = other.secondarymag; + local float tempf2 = other.secondaryammo; + local float tempf3 = other.secondarymag2; + // move primary to secondary + other.secondaryweapon = other.weapon; + other.secondarymag = other.currentmag; + other.secondarymag2 = other.currentmag2; + other.secondaryammo = other.currentammo; + // move secondary to tertiary + other.thirdweapon = tempf; + other.thirdmag = tempf1; + other.thirdammo = tempf2; + other.thirdmag2 = tempf3; + } + + // give boxweapon + other.weapon = self.boxweapon.weapon; + other.currentammo = getWeaponAmmo(self.boxweapon.weapon); + other.currentmag = getWeaponMag(self.boxweapon.weapon); + other.weaponskin = 0; + + if (other.weapon != W_KAR_SCOPE && other.weapon != W_HEADCRACKER && !IsDualWeapon(other.weapon)) { + other.weapon2model = ""; + } + + #ifndef PC + other.Flash_Offset = GetWeaponFlash_Offset(self.boxweapon.weapon); + other.Flash_Size = GetWeaponFlash_Size(self.boxweapon.weapon); + other.Weapon_Name = GetWeaponName(self.boxweapon.weapon); + #endif + } + else if (other.weapon == self.boxweapon.weapon) + { + other.currentammo = getWeaponAmmo(self.boxweapon.weapon); + other.currentmag = getWeaponMag(self.boxweapon.weapon); + } + else if (other.secondaryweapon == self.boxweapon.weapon) + { + other.secondaryammo = getWeaponAmmo(self.boxweapon.weapon); + other.secondarymag = getWeaponMag(self.boxweapon.weapon); + } + else if (other.thirdweapon == self.boxweapon.weapon) + { + other.thirdammo = getWeaponAmmo(self.boxweapon.weapon); + other.thirdmag = getWeaponMag(self.boxweapon.weapon); + } + else if (!other.secondaryweapon) + { + WeaponSwitch(other); + other.weapon = self.boxweapon.weapon; + other.currentammo = getWeaponAmmo(self.boxweapon.weapon); + other.currentmag = getWeaponMag(self.boxweapon.weapon); + other.weaponskin = GetWepSkin(self.boxweapon.weapon); + #ifndef PC + other.Flash_Offset = GetWeaponFlash_Offset(self.boxweapon.weapon); + other.Flash_Size = GetWeaponFlash_Size(self.boxweapon.weapon); + other.Weapon_Name = GetWeaponName(self.boxweapon.weapon); + #endif + } + else if (!other.thirdweapon && (other.perks & P_MULE)) + { + + } + sound(self, 0,"sounds/misc/ching.wav", 1, 1); + tempe = self; + self = other; + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(self.weapon, 0); + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH);//FIXME + SwitchWeapon(self.weapon); + self = tempe; + remove (self.boxweapon); + box_close1(); + } + } + } +} + + +// +// Load_Mbox_Data() +// Opens the map's MBOX Data File and adds parsed data +// into the Box's weapon Array. +// ---------- +// TODO: Possibly investigate making this a little better and more modular +// so adding MBOX Weapons can be easier? +// +void() Load_Mbox_Data = +{ + local float file; + local string h; + local int weapons_all_disabled = 1; + + // Attempt to Open the File + h = strcat(mapname, ".mbox"); + h = strcat("maps/", h); + file = fopen (h, FILE_READ); + + // There was no MBOX Data, enable all Weapons! + // R.I.P. PSP Memory 90% of the time in this case.. + if (file == -1) { + for (float i = 0; i < 25; i++) { + BoxWeapons[i] = 1; + } + } else { + // Parse each Line and write the Data into our Array. + for (float i = 0; i < 25; i++) { + h = strtrim((fgets(file))); + BoxWeapons[i] = stof(h); + + if (stof(h) == 1) + weapons_all_disabled = 0; + } + } + + if (weapons_all_disabled) { + for (float i = 0; i < 25; i++) { + BoxWeapons[i] = 1; + } + } + + for(float i = 0; i < 25; i++) { + // Precache Weapon Data if enabled + if (BoxWeapons[i]) { + precache_model(GetWeaponModel(Getweaponid(i), 0)); + precache_model(GetWeaponModel(Getweaponid(i), 1)); + precache_extra(Getweaponid(i)); + } + } +} + +void() mystery_box = +{ + Load_Mbox_Data(); + + precache_model ("models/machines/mystery.mdl"); + precache_model ("models/machines/mglow$.mdl"); + precache_model ("models/props/teddy.mdl"); + precache_sound ("sounds/machines/mbox_open.wav"); + precache_sound ("sounds/machines/mbox_close.wav"); + + self.solid = SOLID_TRIGGER; + self.classname = "mystery"; + setorigin(self, self.origin); + setmodel (self, "models/machines/mystery.mdl"); + setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + #ifndef PC + if (!(self.spawnflags & 2)) + { + local entity g; + g = spawn(); + self.goaldummy = g; + setmodel(g,"models/machines/mglow$.mdl"); + setorigin(g,self.origin); + g.angles = self.angles; + } + #endif + + self.touch = mystery_touch; + boxOrigin = self.origin; + + boxLocations[boxCount] = self; + boxCount++; +} + +// +// -------------------- +// Pack-a-Punch +// -------------------- +// + +void removeinpap() { + //remove(self); +}; + +//Self = floating gun +void velocity_reduce_output() { + makevectors(self.owner.angles); + self.velocity = [0,0,0]; + setorigin(self, self.owner.origin + v_forward*25); + + //start floating back into pap + self.velocity = v_forward*-1.5; +} + +//Self = floating gun +void velocity_reduce_output_in() { + self.velocity = [0,0,0]; + setorigin(self, self.owner.origin); +} + +void() reset_pap = +{ + self.weapon = 0; + self.owner = 0; + + + if (self.goaldummy) { + remove(self.goaldummy); + self.goaldummy = 0; + } + + + self.papState = 0; +} + +//Self = pap +void PapSendOut() { + makevectors(self.angles); + setorigin(self.goaldummy, self.origin); + self.goaldummy.velocity = v_forward*25; + + self.goaldummy.think = velocity_reduce_output; + self.goaldummy.nextthink = time + 1; + + self.papState = 2; + self.nextthink = time + 15; + self.think = reset_pap; +} + +void PapUpgrade(entity pap, entity buyer) { + entity tempe; + string modelname; + + tempe = self; + self = buyer; + addmoney (self, -5000, 0); + + //////////WepModel IN Floating test//////// + + entity floatmodelin; + + modelname = GetWeaponModel(self.weapon, 1); + + floatmodelin = spawn (); + + + //model goes in + makevectors(pap.angles); + setorigin (floatmodelin, pap.origin + v_forward*25); + setmodel (floatmodelin, modelname); + setsize (floatmodelin, '0 0 0', '0 0 0'); + floatmodelin.angles = pap.angles; + floatmodelin.angles_y = 90 + pap.angles_y; + + floatmodelin.movetype = MOVETYPE_NOCLIP; + floatmodelin.solid = SOLID_NOT; + + floatmodelin.owner = pap; + floatmodelin.velocity = v_forward*-25; + + floatmodelin.think = velocity_reduce_output_in; + floatmodelin.nextthink = time + 1; + + //model goes velocity_reduce_output + pap.owner = self; + pap.goaldummy = floatmodelin; + pap.think = PapSendOut; + pap.nextthink = time + 3.5; + + + //////////////////////////////////////// + + self.fire_delay = self.reload_delay = 4.0 + time; + sound(self,CHAN_WEAPON,"sounds/machines/papsound.wav",1,ATTN_NONE); + + pap.weapon = self.weapon; + + self.weapon = self.secondaryweapon; + self.currentammo = self.secondaryammo; + self.currentmag = self.secondarymag; + self.currentmag2 = self.secondarymag2; + self.secondaryweapon = 0; + self.secondaryammo = 0; + self.secondarymag = 0; + self.secondarymag2 = 0; + + self.isBuying = true; + + Set_W_Frame (0, 39, 0, 0, 0, W_TakeOut, "models/machines/v_pap.mdl", true, S_BOTH); + self.weapon2model = ""; + UpdateV2model(self.weapon2model, 0); + + #ifndef PC + self.Weapon_Name = ""; + #endif + + self = tempe; + + self.papState = 1; + +} + +void touch_pap() { + + if (other.classname != "player" || other.downed || !isFacing(other, self)) { + return; + } + + if (self.requirespower == true && !isPowerOn) { + useprint (other, 8, 0, 0); + return; + } + + if (!self.papState) { + if (IsPapWeapon(other.weapon) == 1) { + return; + } + useprint (other, 12, 5000, 5); + if (other.points >= 5000 && other.button7 && other.weapon) + { + PapUpgrade(self, other); + + } + else if (other.button7 && other.weapon) { + centerprint (other, "Not enough points\n"); + } + } else if (self.papState == 2) { + float startframe; + float endframe; + string modelname; + + useprint (other, 7, 0, 0); + + if (other.button7) { + if (!other.secondaryweapon) { + WeaponSwitch(other); + } + + other.weapon = EqualPapWeapon(self.weapon); + other.currentammo = getWeaponAmmo(other.weapon); + other.currentmag = getWeaponMag(other.weapon); + + if (IsDualWeapon(other.weapon)) { + other.currentmag2 = other.currentmag; + } + + entity tempe = self; + startframe = GetFrame(other.weapon,TAKE_OUT_START); + endframe = GetFrame(other.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(other.weapon, 0); + other.weaponmodel = GetWeaponModel(other.weapon, 0);// Give weapon model + + // Kar Scope + if (other.weapon == W_HEADCRACKER) + other.weapon2model = "models/weapons/kar/v_karscope.mdl"; + + self = other; + SwitchWeapon(self.weapon); + UpdateV2model(other.weapon2model, 0); + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, true, S_BOTH);//FIXME + + #ifndef PC + self.Flash_Offset = GetWeaponFlash_Offset(self.weapon); + self.Flash_Size = GetWeaponFlash_Size(self.weapon); + self.Weapon_Name = GetWeaponName(self.weapon); + self.weaponskin = GetWepSkin(self.weapon); + #endif + + self = tempe; + + // because PaP broke itself + self.think = reset_pap; + self.nextthink = time + 0.1; + } + } +} + +void() perk_pap = +{ + entity tempe; + + tempe = find (world, classname, "power_switch");// trigger all paired doors + + self.solid = SOLID_TRIGGER; + + if (tempe) { + self.requirespower = true; + } else { + self.requirespower = false; + } + + precache_model ("models/machines/pap.mdl"); + precache_model ("models/machines/v_pap.mdl"); + precache_sound ("sounds/machines/papsound.wav"); + precache_sound ("sounds/machines/papsound.wav"); + setorigin(self, self.origin); + setmodel (self, "models/machines/pap.mdl"); + setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.classname = "perk_pap"; + self.touch = touch_pap; +} + +// Sorta ugly function but whatever +void() perk_random = +{ + float i; + float foundperk; + + // remove perks from our selection if they already exist + // ugly ugly + if ((find(world,classname,"perk_revive") && (self.spawnflags & 1))) { + self.spawnflags -= self.spawnflags & 1; + } + if ((find(world,classname,"perk_juggernog") && (self.spawnflags & 2))) { + self.spawnflags -= self.spawnflags & 2; + } + if ((find(world,classname,"perk_speed") && (self.spawnflags & 4))) { + self.spawnflags -= self.spawnflags & 4; + } + if ((find(world,classname,"perk_double") && (self.spawnflags & 8))) { + self.spawnflags -= self.spawnflags & 8; + } + if ((find(world,classname,"perk_flopper") && (self.spawnflags & 16))) { + self.spawnflags -= self.spawnflags & 16; + } + if ((find(world,classname,"perk_staminup") && (self.spawnflags & 32))) { + self.spawnflags -= self.spawnflags & 32; + } + if ((find(world,classname,"perk_deadshot") && (self.spawnflags & 64))) { + self.spawnflags -= self.spawnflags & 64; + } + if ((find(world,classname,"perk_mule") && (self.spawnflags & 128))) { + self.spawnflags -= self.spawnflags & 128; + } + + // obj error because there are no possible perks for us to obtain + if (self.spawnflags == 0) { + objerror("No available Perks to choose from!"); + } + + foundperk = false; + while (foundperk == false) { + i = random(); + // Quick Revive + if (i < (1/8) && (self.spawnflags & 1)) { + if !(find(world, classname, "perk_revive")) { + perk_revive(); + foundperk = true; + } + } + // Jugger-Nog + else if (i < (2/8) && (self.spawnflags & 2)) { + if !(find(world, classname, "perk_juggernog")) { + perk_juggernog(); + foundperk = true; + } + } + // Speed Cola + else if (i < (3/8) && (self.spawnflags & 4)) { + if !(find(world, classname, "perk_speed")) { + perk_speed(); + foundperk = true; + } + } + // Double Tap + else if (i < (4/8) && (self.spawnflags & 8)) { + if !(find(world, classname, "perk_double")) { + perk_double(); + foundperk = true; + } + } + // PhD Flopper + else if (i < (5/8) && (self.spawnflags & 16)) { + if !(find(world, classname, "perk_flopper")) { + perk_flopper(); + foundperk = true; + } + } + // Stamin-Up + else if (i < (6/8) && (self.spawnflags & 32)) { + if !(find(world, classname, "perk_staminup")) { + perk_staminup(); + foundperk = true; + } + } + // Deadshot Daiquiri + else if (i < (7/8) && (self.spawnflags & 64)) { + if (!find(world, classname, "perk_deadshot")) { + perk_deadshot(); + foundperk = true; + } + } + // Mule Kick + else if (i < 1 && (self.spawnflags & 128)) { + if (!find(world, classname, "perk_mule")) { + perk_mule(); + foundperk = true; + } + } + } +} + +void() item_revive = +{ + perk_revive(); + self.origin_z += 8; +} + +void() item_juggernog = +{ + perk_juggernog(); + self.origin_z += 8; +} + +void() item_speed = +{ + perk_speed(); + self.origin_z += 8; +} + +void() item_douple = +{ + perk_double(); + self.origin_z += 8; +} + +void() item_flopper = +{ + perk_flopper(); + self.origin_z += 8; +} diff --git a/source/server/entities/map_entities.qc b/source/server/entities/map_entities.qc new file mode 100644 index 0000000..62cf532 --- /dev/null +++ b/source/server/entities/map_entities.qc @@ -0,0 +1,1151 @@ +/* + server/entities/map_entities.qc + + misc map entity spawn and logic + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +string(float wep, float gorvmodel) GetWeaponModel; +void() ReturnWeaponModel; + +void() func_wall = +{ + self.angles = '0 0 0'; + self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything + self.solid = SOLID_BSP; + setmodel (self, self.model); +}; + +void() func_illusionary = +{ + self.angles = '0 0 0'; + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_NOT; + setmodel (self, self.model); + + #ifdef PC + HalfLife_DoRender(); + #endif + // makestatic (self); // dr_mabuse1981: dont use makestatic, it doesnt show the HL rendermodes then. +}; + +// 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 ome 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; + + if(self.cost) + { + if(self.state == STATE_BOTTOM||self.state == STATE_DOWN) + { + centerprint(other,"Press use to buy [cost:"); + centerprint(other,ftos(self.cost)); + centerprint(other,"]\n"); + if (other.button7) + { + if(other.points >= self.cost) + { + self.enemy = other; + addmoney(other, 0 - self.cost, FALSE); + button_fire(); + return; + } + else + { + centerprint(other,"You do not have enough points\n"); + + } + } + } + } + else + { + self.enemy = other; + button_fire (); + } +}; + +void() button_killed = +{ + 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 = +{ + 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; + } + 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); +}; + +void() flame_update = { + #ifndef PC + particle (self.origin, v_up, 111, 0); + #else + te_flamejet(self.origin, v_up, 10); + #endif + self.think = flame_update; + self.nextthink = time + random()+0.1; +} +void() place_fire = +{ + #ifndef PC + particle (self.origin, v_up*8, self.frame, 0); + #else + te_flamejet(self.origin, v_up*8, 10); + #endif + self.think = flame_update; + self.nextthink = time + random()+0.1; +}; + +void() place_model = +{ + #ifdef PSP + if (self.spawnflags & 2) + remove(self); + #endif + + self.model = convert_old_asset_path(self.model); + + precache_model (self.model); + setmodel (self, self.model); + + self.skin = 0; + setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); + self.angles = self.angles; + self.solid = SOLID_NOT; + self.frame = self.sequence; + + if (self.spawnflags & 1) + self.effects = self.effects | EF_FULLBRIGHT; +}; + + + + +void () buy_weapon_touch = +{ + entity oldent; + float tempf, tempf1, tempf2, tempf3; + float startframe,endframe; + string modelname; + + if (other.classname != "player" || other.downed || other.isBuying/* || !isFacing(other, self)*/) { + return; + } + + #ifndef PC + other.Weapon_Name_Touch = GetWeaponName(self.weapon); + #endif + + if (self.weapon == other.weapon || self.weapon == other.secondaryweapon + || self.weapon == EqualNonPapWeapon(other.weapon) + || self.weapon == EqualNonPapWeapon(other.secondaryweapon)) { + + if (self.weapon == EqualNonPapWeapon(other.secondaryweapon) + || self.weapon == EqualNonPapWeapon(other.weapon)) + useprint (other, 3, 4500, other.weapon); + else + useprint (other, 3, self.cost2, self.weapon); + + if (!other.button7 || other.semiuse || other.isBuying) { + return; + } + if ((self.weapon == other.weapon && other.currentammo >= getWeaponAmmo(self.weapon)) + ||(self.weapon == other.secondaryweapon && other.secondaryammo >= getWeaponAmmo(self.weapon))) { + return; + } + + other.semiuse = true; + if ((other.points < self.cost2 && !IsPapWeapon(other.weapon)) || (other.points < 4500 && IsPapWeapon(other.weapon))) { + centerprint(other, "You do not have enough points\n"); + return; + } + + other.ach_tracker_coll++; + + if (self.weapon == other.weapon || (self.weapon == EqualNonPapWeapon(other.weapon))) { + other.currentammo = getWeaponAmmo(self.weapon); + } else if (self.weapon == other.secondaryweapon || (self.weapon == EqualNonPapWeapon(other.secondaryweapon)) ) { + other.secondaryammo = getWeaponAmmo(self.weapon); + } + + + sound(other, 0,"sounds/misc/ching.wav", 1, 1); + other.reload_delay = 0; + + if (!IsPapWeapon(other.weapon)) + addmoney(other, -1*self.cost2, 0); + else + addmoney(other, -1*4500, 0); + + if (self.enemy) { + oldent = self; + self = self.enemy; + self.use(); + self = oldent; + } + } + if (self.weapon == W_BETTY) + { + if (other.secondary_grenades < 2) + useprint (other, 3, self.cost2, self.weapon); + else + return; + if (!other.button7 || other.semiuse) + return; + if (other.points < self.cost2) + { + centerprint(other, "You do not have enough points\n"); + sound(other, 0,"sounds/misc/denybuy.wav", 1, 1); + return; + } + else + { + other.ach_tracker_coll++; + other.reload_delay = 0; + sound(other, 0,"sounds/misc/ching.wav", 1, 1); + //other.boughtweapon = true; + addmoney(other, 0 - self.cost2, FALSE); + other.grenades = other.grenades | 2; + other.secondary_grenades = 2; + if (self.enemy) + { + oldent = self; + self = self.enemy; + self.use(); + self = oldent; + } + } + other.semiuse = true; + return; + } + else if (self.weapon == W_GRENADE) + { + if (other.primary_grenades < 4) + useprint (other, 3, self.cost2, self.weapon); + else + return; + if (!other.button7 || other.semiuse) + return; + if (other.points < self.cost) + { + centerprint(other, "You do not have enough points\n"); + sound(other, 0,"sounds/misc/denybuy.wav", 1, 1); + + return; + } + else + { + other.ach_tracker_coll++; + other.reload_delay = 0; + sound(other, 0,"sounds/misc/ching.wav", 1, 1); + //other.boughtweapon = true; + addmoney(other, 0 - self.cost, FALSE); + other.primary_grenades = 4; + if (self.enemy) + { + oldent = self; + self = self.enemy; + self.use(); + self = oldent; + } + } + other.semiuse = true; + return; + } + else if (self.weapon == W_BOWIE) + { + if (!other.bowie) { + useprint(other, 3, self.cost2, self.weapon); + if (other.button7) + { + other.ach_tracker_coll++; + entity tempz; + tempz = self; + self = other; + Set_W_Frame(15, 30, 0, 0, 0, ReturnWeaponModel, "progs/VModels/v_bowie.mdl", false, S_BOTH); + self.bowie = 1; + } + } + } + else + { + entity tempe; + + //centerprint(other, self.message); + useprint (other, 4, self.cost, self.weapon); + + if (!other.button7 || other.semiuse) { + return; + } + + other.semiuse = 1; + if (other.points < self.cost) { + centerprint (other, "Not enough points\n"); + return; + } + + other.ach_tracker_coll++; + + if (other.weapon && !other.secondaryweapon) { + tempf = other.currentammo; + other.currentammo = other.secondaryammo; + other.secondaryammo = tempf; + + tempf1 = other.currentmag; + other.currentmag = other.secondarymag; + other.secondarymag = tempf1; + + tempf2 = other.weapon; + other.weapon = other.secondaryweapon; + other.secondaryweapon = tempf2; + + tempf3 = other.currentmag2; + other.currentmag2 = other.secondarymag2; + other.secondarymag2 = tempf3; + } else if (other.weapon && other.secondaryweapon) { + if ((other.perks & P_MULE) && !other.thirdweapon) { + // store secondary weapon + tempf = other.secondaryweapon; + tempf1 = other.secondarymag; + tempf2 = other.secondaryammo; + tempf3 = other.secondarymag2; + // move primary to secondary + other.secondaryweapon = other.weapon; + other.secondarymag = other.currentmag; + other.secondarymag2 = other.currentmag2; + other.secondaryammo = other.currentammo; + // move secondary to tertiary + other.thirdweapon = tempf; + other.thirdmag = tempf1; + other.thirdammo = tempf2; + other.thirdmag2 = tempf3; + } + + // free current slot + other.currentammo = 0; + other.currentmag = 0; + other.weapon = 0; + } + + sound(other, 0,"sounds/misc/ching.wav", 1, 1); + other.reload_delay = 0; + addmoney(other, -1*self.cost, 0); + if (self.enemy) { + oldent = self; + self = self.enemy; + self.use(); + self = oldent; + } + other.weapon = self.weapon; + + other.currentammo = getWeaponAmmo(self.weapon); + other.currentmag = getWeaponMag(self.weapon); + tempe = self; + self = other; + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(self.weapon, 0); + + if (self.weapon != W_KAR_SCOPE && self.weapon != W_HEADCRACKER && !IsDualWeapon(self.weapon)) { + self.weapon2model = ""; + } + + SwitchWeapon(self.weapon); + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH);//FIXME + + #ifndef PC + self.Flash_Offset = GetWeaponFlash_Offset(self.weapon); + self.Flash_Size = GetWeaponFlash_Size(self.weapon); + self.Weapon_Name = GetWeaponName(self.weapon); + #endif + + self = tempe; + } + + if (other.ach_tracker_coll == ach_tracker_col2) { + GiveAchievement(12, other); + } +}; + +void() buy_weapon_link_target = +{ + entity ent; + + ent = find (world, targetname, self.target); + + if (ent.classname == "weapon_wall") + self.enemy = ent; +} + +void() buy_weapon = +{ + local string weaponname; + InitTrigger (); + weaponname = GetWeaponModel (self.weapon, 0); + if (weaponname != "") + precache_model (weaponname); + weaponname = GetWeaponModel(self.weapon, 1); + if (weaponname != "") + precache_model (weaponname); + + + precache_extra (self.weapon); + self.touch = buy_weapon_touch; + + self.think = buy_weapon_link_target; + self.nextthink = time + 0.2; + + ach_tracker_col2++; + + precache_sound("sounds/misc/ching.wav"); +}; + + + +void() BarrelExplode = +{ + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/explode.wav", 1, ATTN_NORM); + DamgageExplode (self, self.enemy, 500, 650, 200); + + #ifdef PC + te_customflash(self.origin, 128, 300, '1 1 1'); + #endif + + CallExplosion(self.origin); + + SUB_Remove (); +}; + +void() barrel_hit = +{ + if (self.health <= 0) { + BarrelExplode(); + } else if (self.health <= 200) { + makevectors(self.angles); + self.think = barrel_hit; + self.nextthink = time + 0.1; + self.health = self.health - 1; + if (self.health > 100) + particle (self.origin + '0 0 30', v_up*8, 111, 0); + else + particle (self.origin + '0 0 30', v_up*8, 112, 0); + + } +} + +void() explosive_barrel = +{ + precache_model ("models/props/barrel_m.mdl"); + + self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + self.solid=SOLID_BBOX; + self.classname = "explosive_barrel"; + setmodel (self, "models/props/barrel_m.mdl"); + setsize (self, '-10 -10 ', '10 10 38'); + + self.takedamage = DAMAGE_YES; + self.health = 300; +}; + +.float radioState; +.float length; +.string tune; +void() radioPlay = +{ + self.health = 1; + + if (self.radioState == 1) { + sound (self, CHAN_ITEM, self.tune, 1, ATTN_NORM); + self.nextthink = time + self.length; + self.think = radioPlay; + } else if (self.radioState == 0) { + sound (self, CHAN_ITEM, "sounds/null.wav", 1, ATTN_NONE); + self.nextthink = 0; + self.think = SUB_Null; + } +} + +void() radio_hit = +{ + self.health = 1; + + sound (self, CHAN_ITEM, "sounds/misc/radio.wav", 1, ATTN_NORM); + self.nextthink = time + 1;//getSoundLen("sounds/music/tune1.wav"); + self.think = radioPlay; + + if (self.radioState == 0) + self.radioState = 1; + else if (self.radioState == 1) + self.radioState = 0; +} + +void() item_radio = +{ + precache_model ("models/props/radio.mdl"); + precache_sound ("sounds/misc/radio.wav"); + + if(self.tune) { + precache_sound (self.tune); + } + + self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + self.solid=SOLID_BBOX; + self.classname = "item_radio"; + setmodel (self, "models/props/radio.mdl"); + setsize (self, '-8 -8 -4', '8 8 4'); + + self.takedamage = DAMAGE_YES; + self.health = 1; + self.radioState = 0; + self.th_die = radio_hit; +}; + +.string zappername; +void() turnswitch1 =[ 1, turnswitch2 ] {self.frame = 0;}; +void() turnswitch2 =[ 2, turnswitch3 ] {self.frame = 1;}; +void() turnswitch3 =[ 3, turnswitch4 ] {self.frame = 2;}; +void() turnswitch4 =[ 4, SUB_Null ] {self.frame = 2;}; + +void() offswitch2 =[ 2, offswitch3 ] {self.frame = 2;}; +void() offswitch3 =[ 3, offswitch4 ] {self.frame = 1;}; +void() offswitch4 =[ 4, SUB_Null ] {self.frame = 0;}; + + +void zap_zombie () { + entity tempe; + + if (other.classname == "ai_zombie") { + tempe = self; + self = other; + self.th_die(); + self = tempe; + } else if (other.classname == "player") { + if (other.damagetimer < time) { + DamageHandler (other, self, 50, S_ZAPPER); + other.damagetimer = time + 1.5; + } + } +} + + +void zapper_play () { + entity zents = find(world, targetname, self.target); + local vector org = self.origin; + local vector targetorg = zents.origin; + + if (zents) { + #ifdef PC + te_lightning2(self, self.origin, zents.origin); + #endif + #ifdef PSP + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_LIGHTNING2); + WriteEntity (MSG_BROADCAST, self); + WriteCoord (MSG_BROADCAST, org_x); + WriteCoord (MSG_BROADCAST, org_y); + WriteCoord (MSG_BROADCAST, org_z); + WriteCoord (MSG_BROADCAST, targetorg_x); + WriteCoord (MSG_BROADCAST, targetorg_y); + WriteCoord (MSG_BROADCAST, targetorg_z); + #endif + } + + zents.touch = zap_zombie; + + self.think = zapper_play; + self.nextthink = time + (0.5*random()); +} + +void() zapper_touch; +void() zapper_cooldown = +{ + offswitch2(); + self.touch = zapper_touch; +} + +void zapper_stop() { + entity zents; + entity tempe; + + zents = find(world, zappername, self.zappername); + while (zents) + { + if (zents.classname == "zapper_light") { + zents.skin = 0; + } else if (zents.classname == "zapper_switch") { + tempe = self; + self = zents; + self.state = 0; + self.think = zapper_cooldown; + self.nextthink = time + 30; + self = tempe; + } else if (zents.classname == "zapper_node" && zents.target) { + zents.think = SUB_Null; + zents.nextthink = 0; + } + zents.touch = SUB_Null; + zents = find(zents, zappername, self.zappername); + } + + remove(self); +} + +void zapper_start(string zapper) { + entity zents; + entity tempe; + + zents = find(world, zappername, zapper); + while (zents) + { + if (zents.classname == "zapper_light") { + zents.skin = 1; + } else if (zents.classname == "zapper_switch") { + tempe = self; + self = zents; + self.state = 1; + turnswitch1(); + self = tempe; + } else if (zents.classname == "zapper_node" && zents.target) { + zents.think = zapper_play; + zents.nextthink = time + 0.1; + } + zents = find(zents, zappername, zapper); + } + + tempe = spawn(); + tempe.think = zapper_stop; + tempe.nextthink = time + 30; + tempe.zappername = zapper; +} + +void zapper_touch () { + if (other.classname != "player") { + return; + } + + if (self.requirespower == true && !isPowerOn) { + useprint (other, 8, 0, 0); + return; + } + + if (self.state == 0) { + useprint (other, 11, self.cost, self.weapon); + + if (!other.button7) { + return; + } + + if (other.points < self.cost) { + centerprint(other, "You do not have enough points\n"); + return; + } + + zapper_start(self.zappername); + addmoney(other, -1*self.cost, false); + } +} + +void() zapper_switch = +{ + precache_model ("models/machines/zapper/z_switch.mdl"); + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_TRIGGER; + self.classname = "zapper_switch"; + setmodel(self, "models/machines/zapper/z_switch.mdl"); + setsize (self, '-4 -4 -4', '4 4 4'); + + self.state = 0; + self.touch = zapper_touch; +}; + +void() zapper_node = +{ + precache_model ("models/machines/zapper/z_zap.mdl"); + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_TRIGGER; + self.classname = "zapper_node"; + setmodel(self, "models/machines/zapper/z_zap.mdl"); + setsize (self, '-4 -4 -4', '4 4 4'); +}; + +void() zapper_light = +{ + precache_model ("models/machines/zapper/z_light.mdl"); + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_TRIGGER; + //self.touch = switch_touch; + self.classname = "zapper_light"; + setmodel(self, "models/machines/zapper/z_light.mdl"); + setsize (self, '-4 -4 -4', '4 4 4'); +}; + + +void() trigger_electro = +{ + self.classname = "zapper_field"; + InitTrigger (); + //self.touch = electro_touch; + self.solid = SOLID_TRIGGER; +}; + +/* ================ + Custom Teddy Code + ================*/ + +void() teddy_spawn = +{ + precache_model ("models/props/teddy.mdl"); + + self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + self.solid=SOLID_BBOX; + self.classname = "teddy_spawn"; + setmodel (self, "models/props/teddy.mdl"); + setsize (self, '-8 -8 -4', '8 8 4'); + + self.takedamage = DAMAGE_YES; + self.health = 0; + self.th_die = teddy_react; +}; + +/* ================== + Custom Song Code + ================== */ + +void() touch_songtrigger = { + if (other.classname != "player" || self.activated) + return; + + if (other.button7) { + sound(self, CHAN_VOICE, self.tune, 1, ATTN_NORM); + self.activated = true; + sndActivCnt++; + + if (sndTriggerCnt == sndActivCnt) + playSoundAtPlayers(world.song); + } + +} + +void() trigger_song = { + #ifndef PC + remove(self); + #endif + + precache_model(self.model); + precache_sound(self.tune); + + self.movetype = MOVETYPE_NONE; + self.solid = SOLID_TRIGGER; + self.classname = "trigger_song"; + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + self.touch = touch_songtrigger; + + sndTriggerCnt++; +} + +/* + ============ + Teleporter + ============ + + Teleporters have 3 different modes + + 0: Default Patch 1.0.4 Mode + - Interact with teleporter and go to pad (credit: Naievil and PerikiyoXD) + + 1: Linked Mode + - One-Time-Link teleporter with pad, teleport to pad + + 2: Timed Mode + - Link teleporter with pad, teleport to other destination + for a set amount of time, then return to pad. Link every time. + + TODO: + - Sounds + - Keep track of whether player is touching instead of locking movement +*/ + +void() teleporter_cooldown = +{ + self.activated = false; + + if (self.mode == 2) { + self.isLinked = false; + } + + self.cooldown = false; +} + +void() start_cooldown = +{ + self.cooldown = true; + self.think = teleporter_cooldown; + self.nextthink = time + 5; +} + +void() teleport_entity = +{ + local entity who, en; + + who = find(world, classname ,"player"); + en = find(world, targetname, self.target2); + + + SUB_UseTargets (); + + setorigin (who, who.tele_target.origin); + + if (self.isTimed || en.classname == "func_teleporter_pad") { + setorigin(who, who.origin + '0 0 40'); + } + + who.fire_delay = who.reload_delay = 3.0 + time; + who.zoom = 0; + who.weaponmodel = ""; + who.weapon2model = ""; + who.movetype = MOVETYPE_WALK; + + if (who.classname == "player") { + if (who.flags & FL_ONGROUND) + who.flags = who.flags - FL_ONGROUND; + who.velocity = v_forward * 0; + + } + + who.flags = who.flags - who.flags & FL_ONGROUND; + + if (self.mode == 0) { + self.activated = false; + } else if (self.mode == 2 && !self.isTimed) { + who.tele_target = find(world, targetname, self.target2); + + self.nextthink = time + self.tpTimer; + self.think = teleport_entity; + self.isTimed = true; + } else { + start_cooldown(); + self.isTimed = false; + } +} + +void() teleport_pad_touch = +{ + if (other.classname != "player" || self.host.isLinked) + return; + + if (!isPowerOn) { + useprint(other, 8, 0, 0); + return; + } + + if (self.host.waitLink) { + useprint(other, 19, 0, 0); + + if (other.button7) { + self.host.isLinked = true; + self.host.waitLink = false; + } + } else { + if (self.host.mode == 2) { + useprint(other, 18, 0, 0); + } + } +} + +void() teleporter_link_touch = +{ + + if (!self.waitLink) + useprint(other, 17, 0, 0); + + if (other.button7) { + local entity en; + + en = find(world, targetname, self.target2); + self.waitLink = true; + en.host = self; + } +} + +void() teleport_touch = +{ + if (self.cooldown) { + useprint(other, 16, 0, 0); + return; + } + + if (other.classname != "player" || self.activated) + return; + + if (!isPowerOn) { + useprint(other, 8, 0, 0); + return; + } + + if (self.mode != 0 && !self.isLinked) { + teleporter_link_touch(); + return; + } + + if (!self.cost) + useprint(other, 14, 0, 0); + else + useprint(other, 15, self.cost, 0); + + + if (other.button7) { + + if (other.points < self.cost) { + centerprint(other, "Not enough points"); + return; + } + + addmoney(other, -self.cost, 0); + + SUB_UseTargets(); + other.tele_target = find(world, targetname, self.target); + + if (!other.tele_target) + objerror("Couldn't find target!"); + + self.activated = true; + + other.movetype = MOVETYPE_NONE; + + self.think = teleport_entity; + self.nextthink = time + 3; + } + + if (other.button1) { + if (self.targetname) { + if (self.nextthink < time) + return; // not fired yet + } + } +} + +void() teleport_use = +{ + self.nextthink = time + 2; + force_retouch = 2; // make sure even still objects get hit + self.think = SUB_Null; +} + +void() func_teleporter_entrance = +{ + precache_model ("models/props/teleporter.mdl"); + + self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + self.solid = SOLID_TRIGGER; + self.classname = "func_teleporter_entrance"; + setmodel(self, "models/props/teleporter.mdl"); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + self.touch = teleport_touch; + + // find the destination + if (!self.target) + objerror("No target!"); + + if (self.mode != 0) { + if (!self.target2) + objerror("No mainframe!"); + + if (self.mode == 2) { + local entity tempe; + tempe = find(world, targetname, self.target2); + tempe.host = self; + } + } + + self.use = teleport_use; +} + +void() func_teleporter_destination = +{ + // this does nothing, just serves as a target spot + self.origin = self.origin + '0 0 40'; + self.classname = "func_teleporter_destination"; +} + +void() func_teleporter_timed = +{ + // this does nothing, just serves as a target spot + self.origin = self.origin + '0 0 40'; + self.classname = "func_teleporter_timed"; +} + + +void() func_teleporter_pad = +{ + precache_model ("models/props/mainframe_pad.mdl"); + + self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + self.solid = SOLID_TRIGGER; + self.classname = "func_teleporter_pad"; + setmodel(self, "models/props/mainframe_pad.mdl"); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + self.touch = teleport_pad_touch; +} + +/* ================ + Buyable Ending + ================ */ +void() touch_ending = +{ + if (other.classname != "player" || self.activated) + return; + + useprint(other, 20, self.cost, 0); + + if (other.button7) { + + if (other.points < self.cost) + return; + + addmoney(other, -self.cost, 0); + + local entity tempe; + tempe = self; + self = other; + EndGameSetup(); + self = tempe; + + self.activated = true; + } +} + +void() func_ending = +{ + precache_model (self.model); + + self.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + self.solid = SOLID_TRIGGER; + self.classname = "func_ending"; + setmodel(self, self.model); + setsize(self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + self.touch = touch_ending; +}; diff --git a/source/server/entities/powerups.qc b/source/server/entities/powerups.qc new file mode 100644 index 0000000..a988f4b --- /dev/null +++ b/source/server/entities/powerups.qc @@ -0,0 +1,576 @@ +/* + server/entities/powerups.qc + + powerup logic + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +.float zombie_drop_id; +.float flashtime; +.float flash_step; + +// the powerup's model +string(float type) getPowerupModel = +{ + switch(type) { + case 1: return "models/pu/maxammo!.mdl"; break; + case 2: return "models/pu/x2!.mdl"; break; + case 3: return "models/pu/instakill!.mdl"; break; + case 4: return "models/pu/nuke!.mdl"; break; + case 5: return "models/pu/carpenter!.mdl"; break; + case 6: return "models/pu/perkbottle!.mdl"; break; + default: return ""; break; + } +} + +// the powerup's pick up voice clip +string(float type) getPowerupVO = +{ + switch(type) { + case 1: return "sounds/pu/maxammo.wav"; break; + case 2: return "sounds/pu/double_points.wav"; break; + case 3: return "sounds/pu/insta_kill.wav"; break; + case 4: return "sounds/pu/nuke.wav"; break; + case 5: return "sounds/pu/carpenter.wav"; break; + default: return ""; break; + } + +} + +void() maxammo = +{ + entity tempe, temp; + + tempe = find (world, classname, "player"); + while (tempe) + { + if (!tempe.downed) + { + if (tempe.weapon) + { + tempe.currentammo = getWeaponAmmo(tempe.weapon); + if (!tempe.currentmag) + { + temp = self; + self = tempe; + W_Reload(S_BOTH); + self = temp; + } + } + if (tempe.secondaryweapon) + tempe.secondaryammo = getWeaponAmmo(tempe.secondaryweapon); + + tempe.primary_grenades = 4; + + if (tempe.grenades & 2) + tempe.secondary_grenades = 2; + } + #ifdef PC + ScrollText("MAX AMMO!", tempe); + #endif + #ifdef PSP + nzp_maxammo(); + #endif + + tempe = find (tempe, classname, "player"); + } +} + +void() nuke_finalize = +{ + entity players; + + // give 'The F Bomb' + #ifndef NX + if (self.kills == 1) { + GiveAchievement(4); + } + #endif + + // award points + players = find(world,classname,"player"); + while(players) + { + addmoney(players, 400*nuke_powerups_activated, 1); + players = find(players,classname,"player"); + } + + nuke_powerup_active = false; +} + +void() do_nuke_kill = +{ + // back up ourselves + entity oldself; + oldself = self; + + // switch to goaldummy, is goaldummy world? + if (self.goaldummy == world) { + nuke_finalize(); + remove(self); + return; + } else { + self = self.goaldummy; + } + + // kill a target + self.th_die(); + + // restore self + self = oldself; + + // increment kills + self.kills++; + + // find new target + self.goaldummy = findfloat(self.goaldummy, iszomb, 1); + + self.nextthink = (rint((random() * 6) + 1)/10) + time; // random number from 0.1 to 0.7 +} + +void() nuke = +{ + // if there's already one active, just increment the point multiplier + if (nuke_powerup_active == true) { + nuke_powerups_activated++; + return; + } + + // mark nuke as being active, to prevent zombie damage and spawning. + nuke_powerup_active = true; + nuke_powerup_spawndelay = time + 3; + nuke_powerups_activated = 1; + + // create our watcher entity + entity nuke_watcher; + nuke_watcher = spawn(); + nuke_watcher.goaldummy = findfloat(world, iszomb, 1); + + nuke_watcher.think = do_nuke_kill; + nuke_watcher.nextthink = (rint((random() * 6) + 1)/10) + time; // random number from 0.1 to 0.7 +} + +void() carpenter = +{ + local entity oldself; + local entity who; + oldself = self; + + who = find(world,classname,"window"); + while(who != world) + { + if(who.health < 6 && who.health != -10)//-10 is for boardless windows + { + self = who; + window_carpenter_1 (); + who.health = 6; + self = oldself; + } + + who = find(who,classname,"window"); + } + + who = find(world,classname,"player"); + while(who) + { + addmoney(who, 200, 1); + + who = find(who,classname,"player"); + } + total_windows_down = 0; +} + +void(entity who) give_perkdrop_logic = +{ + // Return here if we already have all of the Perks + if ((who.perks & P_REVIVE) && (who.perks & P_JUG) && (who.perks & P_SPEED) && (who.perks & P_DOUBLE) && + (who.perks & P_FLOP) && (who.perks & P_STAMIN) && (who.perks & P_DEAD) && (who.perks & P_MULE)) { + return; + } + + local float perk; + perk = 0; + + while(perk == 0) { + local float num; + num = rint((random() * 7)) + 1; + + switch(num) { + case 1: + if (!(who.perks & P_JUG)) { + perk = 1; + } + break; + case 2: + if (!(who.perks & P_DOUBLE)) { + perk = 2; + } + break; + case 3: + if (!(who.perks & P_SPEED)) { + perk = 4; + } + break; + case 4: + if (!(who.perks & P_REVIVE)) { + perk = 8; + } + break; + case 5: + if (!(who.perks & P_FLOP)) { + perk = 16; + } + break; + case 6: + if (!(who.perks & P_STAMIN)) { + perk = 32; + } + break; + case 7: + if (!(who.perks & P_DEAD)) { + perk = 64; + } + break; + case 8: + if (!(who.perks & P_MULE)) { + perk = 128; + } + break; + default: break; + } + + who.perks = who.perks | perk; + } + SetPerk(who, who.perks); +} + +void() give_perk = +{ + // Individual Power-Up + if (self.style == 1) { + give_perkdrop_logic(other); + } + // OUR Power-Up + else { + local entity who; + who = find(world, classname, "player"); + + while(who) { + give_perkdrop_logic(who); + who = find(who, classname, "player"); + } + } +} + + + + +void() powerup_flash = +{ + if(self.flash_step == 0) + { + self.flash_step = 1; + self.flashtime = time + 3; + self.nextthink = time + 0.6; + } + else if(self.flash_step == 1) + { + self.nextthink = time + 0.6; + + if(self.flashtime < time) + { + self.flash_step = 2; + self.nextthink = time + 0.3; + self.flashtime = time + 3; + } + + } + else if(self.flash_step == 2) + { + self.nextthink = time + 0.3; + + if(self.flashtime < time) + { + self.flash_step = 3; + self.nextthink = time + 0.15; + self.flashtime = time + 3; + } + + } + else if(self.flash_step == 3) + { + self.nextthink = time + 0.15; + if(self.flashtime < time) + { + // moto - we used to setmodel blank here too, but it caused errors with the sprite. + remove(self.owner); + remove(self); + return; + } + } + + if(self.model == "") { + setmodel(self, getPowerupModel(self.zombie_drop_id)); + } else { + setmodel(self,""); + } +}; + +void() powerup_play_sound = +{ + // play the VO clip if its not the perk bottle + if (self.zombie_drop_id != 6) + sound(self, CHAN_VOICE, self.powerup_vo, 1, ATTN_NONE); + + // finally, remove the (invisible/inactive) powerup. + remove(self); +} + +void() powerup_touch = +{ + local float t; + + t = random(); + + if(other.classname == "player") + { + // pickup sound + sound(self.owner,CHAN_VOICE,"sounds/pu/pickup.wav",1,ATTN_NONE); + + // add a slight delay before VO play + self.think = powerup_play_sound; + self.nextthink = time + 1; + + // hide powerup until we remove (after sound) + setmodel(self, ""); + self.effects = 0; + self.touch = SUB_Null; + remove(self.owner); + + // slight screen flash + stuffcmd(other, "bf\n"); + + // powerup effects + switch(self.zombie_drop_id) { + // max ammo + case 1: + maxammo(); + break; + // double points + case 2: + x2_finished = time + 30; + other.x2_icon = true; + break; + // insta kill + case 3: + instakill_finished = time + 30; + other.insta_icon = true; + break; + // nuke + case 4: + nuke(); + break; + // carpenter + case 5: + carpenter(); + break; + // free perk + case 6: + give_perk(); + break; + // broken! + default: + centerprint(other, strcat("INVALID POWER-UP ID: ", ftos(self.zombie_drop_id))); + break; + } + } +}; + +void() sparkle_think = +{ + local float f; + f = self.frame; + local float r; + while(f == self.frame) + { + r = random(); + r = ((r > 0.2) ? 1 : 0) + ((r > 0.4) ? 1 : 0) + ((r > 0.6) ? 1 : 0) + ((r > 0.8) ? 1 : 0); + f = r; + } + self.frame = f; + + self.think = sparkle_think; + self.nextthink = time + 0.1; + + if(self.calc_time <= time) + { + sound(self,CHAN_VOICE,"sounds/pu/powerup.wav",0.6,ATTN_NORM); + self.calc_time = time + 2.998; + } + +}; + +// +// GetPowerupID() +// Returns a powerup id, checks if the game allows for +// one and if requirements for said powerup are met. +// +float() GetPowerupID = +{ + float found; + float carpenter_able; + float perk_able; + float id; + + id = carpenter_able = perk_able = 0; + + // Check if we can get a carpenter or a free perk drop + if (total_windows_down >= 5) + carpenter_able = true; + if (rounds >= 15) + perk_able = true; + + float total_powerups = 5; // nuke, insta, 2x, maxammo, carpenter, free perk + + // Start ID loop + found = false; + while(found == false) { + float t = random(); + + // loop through all IDs + for (float i = 0; i < total_powerups + 1; i++) { + // check if the ID we got was viable + if (t > (i/total_powerups)) { + switch(i) { + case 1: + found = true; + id = i; + break; + case 2: + found = true; + id = i; + break; + case 3: + found = true; + id = i; + break; + case 4: + found = true; + id = i; + break; + case 5: + if (carpenter_able) { + found = true; + id = i; + } + break; + case 6: + /*if (perk_able) { + found = true; + id = i; + }*/ + break; + default: + break; + } + } + } + } + + return id; +} + +float last_pu; +void(vector where, float type) Spawn_Powerup = +{ + entity new_powerup; + float id; + + new_powerup = spawn(); + new_powerup.origin = where; + setorigin(new_powerup, new_powerup.origin); + new_powerup.solid = SOLID_TRIGGER; + new_powerup.classname = "item_powerup"; + + + setsize (new_powerup, VEC_HULL_MIN, VEC_HULL_MAX); + new_powerup.movetype = MOVETYPE_NONE; + Light_Green(new_powerup); + + //=================== Sparkle Effects ===================== + + entity twlt_Sparkle; + twlt_Sparkle = spawn(); + new_powerup.owner = twlt_Sparkle; //just a reference so power up can delete the sparkle later on + + setorigin(twlt_Sparkle,where); + #ifndef PC + setmodel(twlt_Sparkle,"models/sprites/sprkle.spr"); + #endif + twlt_Sparkle.think = sparkle_think; + twlt_Sparkle.nextthink = time + 0.1; + sound(twlt_Sparkle,CHAN_VOICE,"sounds/pu/powerup.wav",0.6,ATTN_NORM); + + sound(new_powerup,CHAN_AUTO,"sounds/pu/drop.wav",1,ATTN_NONE); + + //======================================================== + + // Specific Power-Ups (for dogs) + if (type) { + setmodel(new_powerup, getPowerupModel(type)); + new_powerup.zombie_drop_id = type; + new_powerup.powerup_vo = getPowerupVO(type); + } else { + // Grab a powerup ID + id = GetPowerupID(); + + // Should perk drops be individual? + if (id == 6) { + // Yes! + if (random() > (1/2)) { + // Set Style and make light Blue to symbolize it is an individual drop + // TODO: Make a sprite too?? + new_powerup.style = 1; + Light_None(new_powerup); + Light_Blue(new_powerup); + } + // No! + else { + // failsafe + new_powerup.style = 0; + } + } + + // finally, assign the id and model to our id. + setmodel(new_powerup, getPowerupModel(id)); + new_powerup.zombie_drop_id = id; + new_powerup.powerup_vo = getPowerupVO(id); + } + + last_pu = new_powerup.zombie_drop_id; + new_powerup.touch = powerup_touch; + + new_powerup.think = powerup_flash; + new_powerup.nextthink = time + 21; + totalpowerups++; +} \ No newline at end of file diff --git a/source/server/entities/sounds.qc b/source/server/entities/sounds.qc new file mode 100644 index 0000000..89b7f14 --- /dev/null +++ b/source/server/entities/sounds.qc @@ -0,0 +1,131 @@ +/* + server/entities/sounds.qc + + Sound Entities for Maps + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#define AMBIENT_SOUND_STATIC 0 // medium radius attenuation +#define AMBIENT_SOUND_EVERYWHERE 1 +#define AMBIENT_SOUND_SMALLRADIUS 2 +#define AMBIENT_SOUND_MEDIUMRADIUS 4 +#define AMBIENT_SOUND_LARGERADIUS 8 +#define AMBIENT_SOUND_START_SILENT 16 +#define AMBIENT_SOUND_NOT_LOOPING 32 + +void() ambient_use = +{ + // volume can only be between 0 - 1 in quake (maybe FTE supports higher.. not sure) + if (self.health > 1) + self.health = 1; + + if (self.team == 1) { + if (self.spawnflags & AMBIENT_SOUND_NOT_LOOPING) { + sound(self, CHAN_VOICE, self.message, self.health, self.stance); + + } + else { + sound(self, CHAN_VOICE, self.message, self.health, self.stance); + ambientsound(self.origin, self.message, self.health, self.stance); + } + self.team = 0; + } + else + { + if (self.spawnflags & AMBIENT_SOUND_NOT_LOOPING) { + sound (self, CHAN_VOICE, self.message, 0, self.stance); + } + else { + sound (self, CHAN_VOICE, self.message, 0, self.stance); + ambientsound (self.origin, self.message, 0, self.stance); + } + self.team = 1; + } + + self.use = SUB_Null; +}; + +void () ambient_generic = +{ + local string link; + + // moto - FIXME: typically this is set to upper-case but NX and PSP lack strtoupper(); assume lower-case! + link = self.message; + + if (link == "nolink") { + if (substring(self.message, 0, 1) == "*") + self.message = substring(self.message , 1, strlen(self.message)); + + + if (substring(self.message , 0, 1) == "!" || substring(self.message , 0, 1) == "*") { + remove(self); + return; + } + else + precache_sound(self.message); + } + else { + // append sounds/ if absent + if (substring(self.message, 0, 7) != "sounds/") + self.message = strcat("sounds/", self.message); + + // always assume .wav extension.. + precache_sound(self.message); + } + + if (self.spawnflags & AMBIENT_SOUND_SMALLRADIUS) + self.stance = ATTN_IDLE; + else if (self.spawnflags & AMBIENT_SOUND_MEDIUMRADIUS) + self.stance = ATTN_STATIC; + else if (self.spawnflags & AMBIENT_SOUND_LARGERADIUS) + self.stance = ATTN_NORM; + else if (self.spawnflags & AMBIENT_SOUND_EVERYWHERE) + self.stance = ATTN_NONE; + else + self.stance = ATTN_STATIC; + + + if (self.health == 0) // final vol + self.health = 1; + + if (self.spawnflags & AMBIENT_SOUND_START_SILENT) + self.team = 1; + else + ambientsound(self.origin, self.message, self.health, self.stance); + + self.use = ambient_use; +}; + + +// +// ambient_bgm() conversion +// +void() ambient_bgm = +{ + // play everywhere + self.spawnflags = 1; + // sound to loop + self.message = self.noise2; + // call ambient_generic() + ambient_generic(); +} \ No newline at end of file diff --git a/source/server/entities/sub_functions.qc b/source/server/entities/sub_functions.qc new file mode 100644 index 0000000..1273a41 --- /dev/null +++ b/source/server/entities/sub_functions.qc @@ -0,0 +1,367 @@ +/* + server/entities/sub_functions.qc + + entity/server utility subroutines + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/* +============================== +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; +void(vector tdest, float tspeed, void() func) SUB_CalcMove; +void() SUB_CalcMoveDone; +void() DelayThink = +{ + activator = self.enemy; + SUB_UseTargets (); + remove(self); +}; + +void() SUB_UseTargets = +{ + local entity t, stemp, otemp, act; + float tempcount, temptotal, breakthis; + string tempstring; + + tempstring = ""; + temptotal = 0; + tempcount = 0; + breakthis = false; + if (self.target2) + tempcount = tempcount + 1; + if (self.target3) + tempcount = tempcount + 1; + if (self.target4) + tempcount = tempcount + 1; + if (self.target5) + tempcount = tempcount + 1; + if (self.target6) + tempcount = tempcount + 1; + if (self.target7) + tempcount = tempcount + 1; + if (self.target8) + tempcount = tempcount + 1; + + while(tempcount > temptotal) + { + temptotal = temptotal + 1; + if (temptotal == 1) + tempstring = self.target; + if (temptotal == 2) + tempstring = self.target2; + if (temptotal == 3) + tempstring = self.target3; + if (temptotal == 4) + tempstring = self.target4; + if (temptotal == 5) + tempstring = self.target5; + if (temptotal == 6) + tempstring = self.target6; + if (temptotal == 7) + tempstring = self.target7; + if (temptotal == 8) + tempstring = self.target8; + // + // 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; + t.target2 = self.target2; + t.target3 = self.target3; + t.target4 = self.target4; + t.target5 = self.target5; + t.target6 = self.target6; + t.target7 = self.target7; + t.target8 = self.target8; + return; + } + + + // + // print the message + // + if (activator.classname == "player" && self.message != "") + { + centerprint (activator, self.message); + } + + // + // kill the killtagets + // + if (self.killtarget) + { + t = world; + do + { + t = find (t, targetname, self.killtarget); + if (!t) + breakthis = true; + remove (t); + } while (!breakthis); + } + + // + // fire targets + // + if (tempstring) + { + act = activator; + t = find (world, targetname, tempstring); + breakthis = 0; + while (!breakthis) + { + if (!t) + { + breakthis = true; + } + stemp = self; + otemp = other; + self = t; + other = stemp; + if (t.classname == "spawn_zombie_in") + { + t.classname = "spawn_zombie"; + } + if (t.classname == "waypoint_s") + { + t.classname = "waypoint_s"; + } + if (t.classname == "spawn_dog_in") + t.classname = "spawn_dog"; + if (self.use != SUB_Null) + { + if (self.use) + self.use(); + } + self = stemp; + other = otemp; + activator = act; + t = find (t, targetname, tempstring); + } + } + } + + +}; + +/* +============= +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 SUB_CalcAngleMove (vector destangle, float tspeed, void() func); +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 SUB_CalcAngleMoveDone (void) +{ + // After rotating, set angle to exact final angle + self.angles = self.finalangle; + self.avelocity = '0 0 0'; + self.nextthink = -1; + if (self.think1) + self.think1 (); +} + +void SUB_CalcAngleMove (vector destangle, float tspeed, void() func) +{ + vector delta; + float traveltime; + + if (!tspeed) + objerror ("No speed is defined!"); + + delta = destangle - self.angles; + traveltime = vlen (delta) / tspeed; + + self.avelocity = delta * (1 / traveltime); + + self.think1 = func; + self.finalangle = destangle; + + self.think = SUB_CalcAngleMoveDone; + self.nextthink = self.ltime + traveltime; +} + +/* +============= +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(); +}; + + +#ifdef PC +// +// HalfLife_DoRender() +// Adds some support for HL rendermodes to FTE, made by +// autonomous1 for 'quakelife' +// + +// Do *some* of the half-life render stuff. +void () HalfLife_DoRender = +{ + local vector osize, omins, omaxs, oabsmin, oabsmax; + + if (self.rendercolor != '0 0 0') {} // ADDME + + if (self.rendermode > 0) + if (self.renderamt > 0) { + self.alpha = self.renderamt/255; + if (self.alpha == 0) + self.alpha = .01; + } + else { + self.rendermode = 2; + } + + if (self.rendermode == 2) { + self.alpha = self.renderamt/255; + if (self.alpha == 0) { + //self.alpha = 0.01; + osize = self.size; + omins = self.mins; + omaxs = self.maxs; + oabsmin = self.absmin; + oabsmax = self.absmax; + + if (self.solid == SOLID_BSP) { + + self.solid = SOLID_BBOX; + } + + //setmodel(self, string_null); + self.size = osize; + self.mins = omins; + self.maxs = omaxs; + self.absmin = oabsmin; + self.absmax = oabsmax; + //setorigin(self, self.origin); + } + } +}; +#endif \ No newline at end of file diff --git a/source/server/entities/triggers.qc b/source/server/entities/triggers.qc new file mode 100644 index 0000000..7c5835f --- /dev/null +++ b/source/server/entities/triggers.qc @@ -0,0 +1,344 @@ +/* + server/entities/triggers.qc + + Misc. Trigger functions + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#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; // already been triggered + } + + 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 while C code is looping through area links... + self.touch = SUB_Null; + self.nextthink = time + 0.1; + self.think = SUB_Remove; + } +}; + +void() multi_killed = +{ + // motolegacy - FIXME + //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(); +}; + +void() 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'; +}; + +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 = ""; +}; + +#ifndef PSP +entity last_act_trigger; +void() trigger_activator_touch = +{ + other.cost = other.cost +1; //hack, we can only touch one of thease at the time + if (other.classname != "player" || last_act_trigger == self || other.cost > 1) + return; + last_act_trigger = self; + + entity t; + float tempcount, temptotal,breakthis; + string tempstring; + temptotal = 0; + breakthis = 0; + tempcount = 1; + tempstring = ""; + t = world; + do + { + t = find (t, classname, "spawn_zombie"); + if (!t) + { + breakthis = 1; + } + if (t.classname == "spawn_zombie") + { + t.classname = "spawn_zombie_away"; + /*if (cvar("developer")) + setmodel(t, "progs/player.mdl");*/ + } + } while (!breakthis); + + if (self.target2) + tempcount =+ 1; + if (self.target3) + tempcount =+ 1; + if (self.target4) + tempcount =+ 1; + if (self.target5) + tempcount =+ 1; + if (self.target6) + tempcount =+ 1; + if (self.target7) + tempcount =+ 1; + if (self.target8) + tempcount =+ 1; + if (self.target2) + tempcount = tempcount + 1; + if (self.target3) + tempcount = tempcount + 1; + if (self.target4) + tempcount = tempcount + 1; + if (self.target5) + tempcount = tempcount + 1; + if (self.target6) + tempcount = tempcount + 1; + if (self.target7) + tempcount = tempcount + 1; + if (self.target8) + tempcount = tempcount + 1; + + while(tempcount > temptotal) + { + temptotal = temptotal + 1; + if (temptotal == 1) + tempstring = self.target; + if (temptotal == 2) + tempstring = self.target2; + if (temptotal == 3) + tempstring = self.target3; + if (temptotal == 4) + tempstring = self.target4; + if (temptotal == 5) + tempstring = self.target5; + if (temptotal == 6) + tempstring = self.target6; + if (temptotal == 7) + tempstring = self.target7; + if (temptotal == 8) + tempstring = self.target8; + if (tempstring) + { + t = find (world, targetname, tempstring); + breakthis = 0; + while (!breakthis) + { + if (!t) + { + breakthis = true; + } + if (t.classname == "spawn_zombie_away") + { + t.classname = "spawn_zombie"; + /*if (cvar("developer")) + setmodel(t, "progs/ai/zfull.mdl");*/ + } + t = find (t, targetname, tempstring); + } + } + } +} + +void() trigger_activator = +{ + InitTrigger (); + self.touch = trigger_activator_touch; +} +#endif + +void() use_wall_weapon = +{ + if (self.enemy) + return; + entity newent; + + newent = spawn(); + + newent.movetype = MOVETYPE_NONE; // so it doesn't get pushed by anything + newent.solid=SOLID_NOT; + newent.classname = "wall_weapon"; + setorigin(newent, self.origin); + setmodel (newent, GetWeaponModel(self.sequence + 1, 1)); + setsize (newent, VEC_HULL2_MIN, VEC_HULL2_MAX); + newent.angles = self.angles; + + self.enemy = newent; +} + +void() weapon_wall = +{ + precache_model ("models/misc/chalk.mdl"); + + setmodel (self, "models/misc/chalk.mdl"); + self.skin = 0; + setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX); + + self.frame = self.sequence; + self.use = use_wall_weapon; + self.classname = "weapon_wall"; + //self.effects = EF_WEPLIGHT; +} + +/* =================== + Custom Teddy Triggers + ===================*/ + +void() teddy_react = +{ + local entity t; + if (self.spawnflags & 1) { + t = find (world, teddyremovetarget, self.target); + + if (t) + remove(t); + } + + remove(self); //this was present in the patch, i like it. +} + +/* + 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! + set "message" to text string +*/ +void() trigger_multiple = +{ + 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 + } + else + { + if (!(self.spawnflags & SPAWNFLAG_NOTOUCH)) + { + self.touch = multi_touch; + } + } +}; + +/* + 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. + set "message" to text string +*/ +void() trigger_once = +{ + self.wait = -1; + + trigger_multiple(); +} \ No newline at end of file diff --git a/source/server/entities/window.qc b/source/server/entities/window.qc new file mode 100644 index 0000000..045bdaf --- /dev/null +++ b/source/server/entities/window.qc @@ -0,0 +1,379 @@ +/* + server/entities/window.qc + + barricades + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +.float rebuild_time; + +void() spawn_boxes = +{ + makevectors(self.angles); + + self.box1 = self.origin + (v_forward * -30) + (v_up * -32); + self.box2 = self.box1 + (v_right * 30); + self.box3 = self.box1 + (v_right * -30); + self.idlebox = self.box1 + (v_forward * -30); + + self.hop_spot = self.origin + v_forward * 40; + self.hop_spot_z -= 30;//We want this to be a little bit above the ground still +}; + +void() screen_shake = +{ + local float r,s,zoom_factor; + r = crandom(); + s = random(); + if (self.enemy.zoom == 2 || self.enemy.zoom == 1) + zoom_factor = 0.5; + else + zoom_factor = 1; + + #ifdef PSP + self.enemy.punchangle_y = r * 8 * zoom_factor; + self.enemy.punchangle_x = (4 + (s * 4)) * zoom_factor; + #else + self.enemy.punchangle_y = r * 4 * zoom_factor; + self.enemy.punchangle_x = (4 + (s * 2)) * zoom_factor; + #endif +}; + +void() Window_repaired = +{ + if (maxreward > totalreward) + { + sound(self, 0,"sounds/misc/ching.wav", 1, 0); + addmoney(self.enemy, 10, 1); + totalreward = totalreward + 10; + } + screen_shake(); + self.enemy = world; +}; + +void() Barricade_hit_window = +{ + sound(self, 0, self.oldmodel, 1, 0); +} + +void(void() next) checkForSpeed = { + if (!(other.perks & P_SPEED)) + return; + + next(); +} + +//////////////////////////////////////////////////////////////////////////////////////////// ANIMATIONS +//0-6 +//7-13 +//14-20 +//21-27 +//28-34 +//35-41 +void() window_anim1_1 =[ 1, window_anim1_2 ] {self.frame = 0;self.nextthink = time + 0.05;}; +void() window_anim1_2 =[ 2, window_anim1_3 ] {self.frame = 1;self.nextthink = time + 0.05;}; +void() window_anim1_3 =[ 3, window_anim1_4 ] {self.frame = 2;self.nextthink = time + 0.05;}; +void() window_anim1_4 =[ 4, window_anim1_5 ] {self.frame = 3;self.nextthink = time + 0.05;}; +void() window_anim1_5 =[ 5, window_anim1_6 ] {self.frame = 4;self.nextthink = time + 0.05;}; +void() window_anim1_6 =[ 6, window_anim1_7 ] {self.frame = 5;self.nextthink = time + 0.05;}; +void() window_anim1_7 =[ 7, SUB_Null ] {self.frame = 6;self.nextthink = time + 0.05;}; + +void() window_anim2_1 =[ 1, window_anim2_2 ] {self.frame = 7;self.nextthink = time + 0.05;}; +void() window_anim2_2 =[ 2, window_anim2_3 ] {self.frame = 8;self.nextthink = time + 0.05;}; +void() window_anim2_3 =[ 3, window_anim2_4 ] {self.frame = 9;self.nextthink = time + 0.05;}; +void() window_anim2_4 =[ 4, window_anim2_5 ] {self.frame = 10;self.nextthink = time + 0.05;}; +void() window_anim2_5 =[ 5, window_anim2_6 ] {self.frame = 11;self.nextthink = time + 0.05;}; +void() window_anim2_6 =[ 6, window_anim2_7 ] {self.frame = 12;self.nextthink = time + 0.05;}; +void() window_anim2_7 =[ 7, SUB_Null ] {self.frame = 13;self.nextthink = time + 0.05;}; + +void() window_anim3_1 =[ 1, window_anim3_2 ] {self.frame = 14;self.nextthink = time + 0.05;}; +void() window_anim3_2 =[ 2, window_anim3_3 ] {self.frame = 15;self.nextthink = time + 0.05;}; +void() window_anim3_3 =[ 3, window_anim3_4 ] {self.frame = 16;self.nextthink = time + 0.05;}; +void() window_anim3_4 =[ 4, window_anim3_5 ] {self.frame = 17;self.nextthink = time + 0.05;}; +void() window_anim3_5 =[ 5, window_anim3_6 ] {self.frame = 18;self.nextthink = time + 0.05;}; +void() window_anim3_6 =[ 6, window_anim3_7 ] {self.frame = 19;self.nextthink = time + 0.05;}; +void() window_anim3_7 =[ 7, SUB_Null ] {self.frame = 20;self.nextthink = time + 0.05;}; + +void() window_anim4_1 =[ 1, window_anim4_2 ] {self.frame = 21;self.nextthink = time + 0.05;}; +void() window_anim4_2 =[ 2, window_anim4_3 ] {self.frame = 22;self.nextthink = time + 0.05;}; +void() window_anim4_3 =[ 3, window_anim4_4 ] {self.frame = 23;self.nextthink = time + 0.05;}; +void() window_anim4_4 =[ 4, window_anim4_5 ] {self.frame = 24;self.nextthink = time + 0.05;}; +void() window_anim4_5 =[ 5, window_anim4_6 ] {self.frame = 25;self.nextthink = time + 0.05;}; +void() window_anim4_6 =[ 6, window_anim4_7 ] {self.frame = 26;self.nextthink = time + 0.05;}; +void() window_anim4_7 =[ 7, SUB_Null ] {self.frame = 27;self.nextthink = time + 0.05;}; + +void() window_anim5_1 =[ 1, window_anim5_2 ] {self.frame = 28;self.nextthink = time + 0.05;}; +void() window_anim5_2 =[ 2, window_anim5_3 ] {self.frame = 29;self.nextthink = time + 0.05;}; +void() window_anim5_3 =[ 3, window_anim5_4 ] {self.frame = 30;self.nextthink = time + 0.05;}; +void() window_anim5_4 =[ 4, window_anim5_5 ] {self.frame = 31;self.nextthink = time + 0.05;}; +void() window_anim5_5 =[ 5, window_anim5_6 ] {self.frame = 32;self.nextthink = time + 0.05;}; +void() window_anim5_6 =[ 6, window_anim5_7 ] {self.frame = 33;self.nextthink = time + 0.05;}; +void() window_anim5_7 =[ 7, SUB_Null ] {self.frame = 34;self.nextthink = time + 0.05;}; + +void() window_anim6_1 =[ 1, window_anim6_2 ] {self.frame = 35;self.nextthink = time + 0.05;}; +void() window_anim6_2 =[ 2, window_anim6_3 ] {self.frame = 36;self.nextthink = time + 0.05;}; +void() window_anim6_3 =[ 3, window_anim6_4 ] {self.frame = 37;self.nextthink = time + 0.05;}; +void() window_anim6_4 =[ 4, window_anim6_5 ] {self.frame = 38;self.nextthink = time + 0.05;}; +void() window_anim6_5 =[ 5, window_anim6_6 ] {self.frame = 39;self.nextthink = time + 0.05;}; +void() window_anim6_6 =[ 6, window_anim6_7 ] {self.frame = 40;self.nextthink = time + 0.05;}; +void() window_anim6_7 =[ 7, SUB_Null ] {self.frame = 41;self.nextthink = time + 0.05;}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//speed cola repair +//81-88 +void() window_srepair1_1 =[ 1, window_srepair1_2 ] {self.frame = 81;}; +void() window_srepair1_2 =[ 2, window_srepair1_4 ] {self.frame = 84;}; +void() window_srepair1_4 =[ 3, window_srepair1_5 ] {self.frame = 87;Barricade_hit_window();}; +void() window_srepair1_5 =[ 4, SUB_Null ] {self.frame = 88; Window_repaired();}; +//73-80 +void() window_srepair2_1 =[ 1, window_srepair2_2 ] {self.frame = 73;}; +void() window_srepair2_2 =[ 2, window_srepair2_4 ] {self.frame = 75;}; +void() window_srepair2_4 =[ 3, window_srepair2_5 ] {self.frame = 79;Barricade_hit_window();}; +void() window_srepair2_5 =[ 4, SUB_Null ] {self.frame = 80; Window_repaired();}; +//65-72 +void() window_srepair3_1 =[ 1, window_srepair3_2 ] {self.frame = 65;}; +void() window_srepair3_2 =[ 2, window_srepair3_4 ] {self.frame = 67;}; +void() window_srepair3_4 =[ 3, window_srepair3_5 ] {self.frame = 71;Barricade_hit_window();}; +void() window_srepair3_5 =[ 4, SUB_Null ] {self.frame = 72; Window_repaired();}; +//57-64 +void() window_srepair4_1 =[ 1, window_srepair4_2 ] {self.frame = 57;}; +void() window_srepair4_2 =[ 2, window_srepair4_4 ] {self.frame = 69;}; +void() window_srepair4_4 =[ 3, window_srepair4_5 ] {self.frame = 63;Barricade_hit_window();}; +void() window_srepair4_5 =[ 4, SUB_Null ] {self.frame = 64; Window_repaired();}; +//49-56 +void() window_srepair5_1 =[ 1, window_srepair5_2 ] {self.frame = 49;}; +void() window_srepair5_2 =[ 2, window_srepair5_4 ] {self.frame = 51;}; +void() window_srepair5_4 =[ 3, window_srepair5_5 ] {self.frame = 55;Barricade_hit_window();}; +void() window_srepair5_5 =[ 4, SUB_Null ] {self.frame = 56; Window_repaired();}; +//41-48 +void() window_srepair6_1 =[ 1, window_srepair6_2 ] {self.frame = 41;}; +void() window_srepair6_2 =[ 2, window_srepair6_4 ] {self.frame = 43;}; +void() window_srepair6_4 =[ 3, window_srepair6_5 ] {self.frame = 47;Barricade_hit_window();}; +void() window_srepair6_5 =[ 4, SUB_Null ] {self.frame = 48; Window_repaired();}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void() window_repair1_1 =[ 1, window_repair1_2 ] {self.frame = 81;}; +void() window_repair1_2 =[ 2, window_repair1_3 ] {self.frame = 82;}; +void() window_repair1_3 =[ 3, window_repair1_4 ] {self.frame = 83;}; +void() window_repair1_4 =[ 4, window_repair1_5 ] {self.frame = 84;}; +void() window_repair1_5 =[ 5, window_repair1_6 ] {self.frame = 85;}; +void() window_repair1_6 =[ 6, window_repair1_7 ] {self.frame = 86;}; +void() window_repair1_7 =[ 7, window_repair1_8 ] {self.frame = 87;Barricade_hit_window();}; +void() window_repair1_8 =[ 8, SUB_Null ] {self.frame = 88; Window_repaired();}; + +void() window_repair2_1 =[ 1, window_repair2_2 ] {self.frame = 73;}; +void() window_repair2_2 =[ 2, window_repair2_3 ] {self.frame = 74;}; +void() window_repair2_3 =[ 3, window_repair2_4 ] {self.frame = 75;}; +void() window_repair2_4 =[ 4, window_repair2_5 ] {self.frame = 76;}; +void() window_repair2_5 =[ 5, window_repair2_6 ] {self.frame = 77;}; +void() window_repair2_6 =[ 6, window_repair2_7 ] {self.frame = 78;}; +void() window_repair2_7 =[ 7, window_repair2_8 ] {self.frame = 79;Barricade_hit_window();}; +void() window_repair2_8 =[ 8, SUB_Null ] {self.frame = 80; Window_repaired();}; + +void() window_repair3_1 =[ 1, window_repair3_2 ] {self.frame = 65;}; +void() window_repair3_2 =[ 2, window_repair3_3 ] {self.frame = 66;}; +void() window_repair3_3 =[ 3, window_repair3_4 ] {self.frame = 67;}; +void() window_repair3_4 =[ 4, window_repair3_5 ] {self.frame = 68;}; +void() window_repair3_5 =[ 5, window_repair3_6 ] {self.frame = 69;}; +void() window_repair3_6 =[ 6, window_repair3_7 ] {self.frame = 70;}; +void() window_repair3_7 =[ 7, window_repair3_8 ] {self.frame = 71;Barricade_hit_window();}; +void() window_repair3_8 =[ 8, SUB_Null ] {self.frame = 72; Window_repaired();}; + +void() window_repair4_1 =[ 1, window_repair4_2 ] {self.frame = 57;}; +void() window_repair4_2 =[ 2, window_repair4_3 ] {self.frame = 58;}; +void() window_repair4_3 =[ 3, window_repair4_4 ] {self.frame = 59;}; +void() window_repair4_4 =[ 4, window_repair4_5 ] {self.frame = 60;}; +void() window_repair4_5 =[ 5, window_repair4_6 ] {self.frame = 61;}; +void() window_repair4_6 =[ 6, window_repair4_7 ] {self.frame = 62;}; +void() window_repair4_7 =[ 7, window_repair4_8 ] {self.frame = 63;Barricade_hit_window();}; +void() window_repair4_8 =[ 8, SUB_Null ] {self.frame = 64; Window_repaired();}; + +void() window_repair5_1 =[ 1, window_repair5_2 ] {self.frame = 49;}; +void() window_repair5_2 =[ 2, window_repair5_3 ] {self.frame = 50;}; +void() window_repair5_3 =[ 3, window_repair5_4 ] {self.frame = 51;}; +void() window_repair5_4 =[ 4, window_repair5_5 ] {self.frame = 52;}; +void() window_repair5_5 =[ 5, window_repair5_6 ] {self.frame = 53;}; +void() window_repair5_6 =[ 6, window_repair5_7 ] {self.frame = 54;}; +void() window_repair5_7 =[ 7, window_repair5_8 ] {self.frame = 55;Barricade_hit_window();}; +void() window_repair5_8 =[ 8, SUB_Null ] {self.frame = 56; Window_repaired();}; + +void() window_repair6_1 =[ 1, window_repair6_2 ] {self.frame = 41;}; +void() window_repair6_2 =[ 2, window_repair6_3 ] {self.frame = 42;}; +void() window_repair6_3 =[ 3, window_repair6_4 ] {self.frame = 43;}; +void() window_repair6_4 =[ 4, window_repair6_5 ] {self.frame = 44;}; +void() window_repair6_5 =[ 5, window_repair6_6 ] {self.frame = 45;}; +void() window_repair6_6 =[ 6, window_repair6_7 ] {self.frame = 46;}; +void() window_repair6_7 =[ 7, window_repair6_8 ] {self.frame = 47;Barricade_hit_window();}; +void() window_repair6_8 =[ 8, SUB_Null ] {self.frame = 48; Window_repaired();}; + +void() window_carpenter_1 =[ 1, window_carpenter_2 ] {self.frame = 47;sound(self, 0, self.oldmodel, 1, 1);}; +void() window_carpenter_2 =[ 2, window_carpenter_3 ] {self.frame = 48;}; +void() window_carpenter_3 =[ 3, window_carpenter_4 ] {self.frame = 55;sound(self, 1, self.oldmodel, 1, 1);}; +void() window_carpenter_4 =[ 4, window_carpenter_5 ] {self.frame = 56;}; +void() window_carpenter_5 =[ 5, window_carpenter_6 ] {self.frame = 63;sound(self, 2, self.oldmodel, 1, 1);}; +void() window_carpenter_6 =[ 6, window_carpenter_7 ] {self.frame = 64;}; +void() window_carpenter_7 =[ 7, window_carpenter_8 ] {self.frame = 71;sound(self, 0, self.oldmodel, 1, 1);}; +void() window_carpenter_8 =[ 8, window_carpenter_9 ] {self.frame = 72;}; +void() window_carpenter_9 =[ 7, window_carpenter_10 ] {self.frame = 79;sound(self, 1, self.oldmodel, 1, 1);}; +void() window_carpenter_10 =[ 8, window_carpenter_11 ] {self.frame = 80;}; +void() window_carpenter_11 =[ 7, window_carpenter_12 ] {self.frame = 87;sound(self, 2, self.oldmodel, 1, 1);}; +void() window_carpenter_12 =[ 8, SUB_Null ] {self.frame = 88;}; + + +void() Window_Damage = +{ + if(self.health == 0) + return; + + sound(self, CHAN_VOICE, self.aistatus, 1, 0); + + self.health = self.health - 1; + + if(self.health == 5) + { + window_anim1_1(); + total_windows_down = total_windows_down + 1; + } + else if(self.health == 4) + window_anim2_1(); + else if(self.health == 3) + window_anim3_1(); + else if(self.health == 2) + window_anim4_1(); + else if(self.health == 1) + window_anim5_1(); + else if(self.health == 0) + window_anim6_1(); + + ach_tracker_spin = 1; +}; + +void() Rebuild_Anims = +{ + if(self.health == 5) + { + if (other.perks & P_SPEED) + window_srepair1_1(); + else + window_repair1_1(); + + total_windows_down = total_windows_down - 1; + } + else if(self.health == 4) + { + if (other.perks & P_SPEED) + window_srepair2_1(); + else + window_repair2_1(); + } + else if(self.health == 3) + { + if (other.perks & P_SPEED) + window_srepair3_1(); + else + window_repair3_1(); + } + else if(self.health == 2) + { + if (other.perks & P_SPEED) + window_srepair4_1(); + else + window_repair4_1(); + } + else if(self.health == 1) + { + if (other.perks & P_SPEED) + window_srepair5_1(); + else + window_repair5_1(); + } + else if(self.health == 0) + { + if (other.perks & P_SPEED) + window_srepair6_1(); + else + window_repair6_1(); + } +}; +void() window_touch = +{ + if(other.classname == "player" && !other.downed && self.health != -10) + { + if(self.health < 6) + { + useprint (other, 5, 0, 0); + + if(other.button7) + { + if(self.rebuild_time < time) + { + self.enemy = other; + Rebuild_Anims(); + self.health = self.health + 1; + + self.rebuild_time = time + 0.75; + } + } + } + } +}; + +void() item_barricade = +{ + if (self.spawnflags & 1) { + self.health = -10; // Window is deactivated, to only hop over it + } else { + if (!self.model) + self.model = "models/misc/window.mdl"; + if (!self.oldmodel) + self.oldmodel = "sounds/misc/barricade.wav"; + if (!self.aistatus) + self.aistatus = "sounds/misc/barricade_destroy.wav"; + + precache_model(self.model); + precache_sound(self.oldmodel); + precache_sound(self.aistatus); + } + + self.classname = "window"; + self.touch = window_touch; + self.solid = SOLID_TRIGGER; + + if (self.health != -10) { + self.health = 6; + setmodel(self, self.model); + } + + setsize(self, '-20 -20 -64', '20 20 16'); + setorigin(self, self.origin); + spawn_boxes(); + + #ifndef PC + windows[wincnt] = self; + wincnt++; + #endif +}; + +void() item_cover = {item_barricade();}; \ No newline at end of file diff --git a/source/server/items.qc b/source/server/items.qc new file mode 100644 index 0000000..e574381 --- /dev/null +++ b/source/server/items.qc @@ -0,0 +1,3 @@ +// +// Items.qc - all the definitions for item spawn functions as well as other functions related to them +// diff --git a/source/server/main.qc b/source/server/main.qc new file mode 100644 index 0000000..1988898 --- /dev/null +++ b/source/server/main.qc @@ -0,0 +1,521 @@ +/* + server/main.qc + + mostly functions that will be called from the engine and are + expected to exist + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() LightStyles_setup; +void() SUB_Remove = {remove(self);} + +//called when starting server/loading the map +void() main = +{ + localcmd("echo Server starting...\n"); +} + +//called for each frame that QC runs +float zombie_cleaned_w; +void() StartFrame = +{ + deathmatch = cvar("deathmatch"); + //coop = cvar("coop"); + framecount = framecount + 1; + + if (waypoint_mode) { + if (!zombie_cleaned_w) { + entity zent; + zent = find (world, classname, "ai_zombie"); + while (zent) + { + /* + if (zent.head) + remove (zent.head); + if (zent.larm) + remove (zent.larm); + if (zent.rarm) + remove (zent.rarm); + */ + remove (zent); + zent = find (zent, classname, "ai_zombie"); + } + zombie_cleaned_w = true; + + zent = find (world, classname, "waypoint"); + while (zent) + { + if (zent.targetname) + setmodel(zent, "models/way/normal_way_door.spr"); + else + setmodel(zent, "models/way/normal_way.spr"); + zent = find (zent, classname, "waypoint"); + } + zent = find (world, classname, "door_nzp_cost"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent = find (zent, classname, "door_nzp_cost"); + } + zent = find (world, classname, "door_nzp"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent.solid = SOLID_NOT; + zent = find (zent, classname, "door_nzp"); + } + zent = find (world, classname, "window"); + while (zent) + { + zent.solid = SOLID_NOT; + zent.touch = SUB_Null; + zent = find (zent, classname, "window"); + } + } + return; + } + + + if (roundinit) { + Round_Core(); + Do_Zombie_AI (); + } else { + + entity SpawnedIn; + SpawnedIn = find(world, classname, "player"); + + if (SpawnedIn) { + entity getdog = find(world, classname, "spawn_dog"); + if (getdog) + gotdog = 1; + else + gotdog = 0; + + updateDogRound(); + InitRounds(); + } + } +} +string(string s) precache_model = #20; + +void() precaches = +{ + precache_model ("models/player.mdl"); + + #ifndef PSP + #ifndef NX + if (world.song != "") + precache_sound (world.song); + #endif + #endif + + // + // Models + // + + // sprites + precache_model ("models/sprites/revive.spr"); + precache_model ("models/sprites/revive_white.spr"); + precache_model ("models/sprites/sprkle.spr"); + precache_model ("models/sprites/lightning.spr"); + precache_model ("models/way/current_way.spr"); + precache_model ("models/way/current_way_door.spr"); + precache_model ("models/way/last_way.spr"); + precache_model ("models/way/last_way_door.spr"); + precache_model ("models/way/normal_way.spr"); + precache_model ("models/way/normal_way_door.spr"); + precache_model ("models/way/way_jump.spr"); + precache_model ("models/way/way_land.spr"); + + // zombie + precache_model ("models/ai/zfull.mdl"); + precache_model ("models/ai/zlarm.mdl"); + precache_model ("models/ai/zrarm.mdl"); + precache_model ("models/ai/zbod.mdl"); + precache_model ("models/ai/zhead.mdl"); + + // zombie crawler + precache_model ("models/ai/zcfull.mdl"); + precache_model ("models/ai/zcbod.mdl"); + precache_model ("models/ai/zclarm.mdl"); + precache_model ("models/ai/zcrarm.mdl"); + precache_model ("models/ai/zchead.mdl"); + + // powerups + precache_model ("models/pu/maxammo!.mdl"); + precache_model ("models/pu/x2!.mdl"); + precache_model ("models/pu/instakill!.mdl"); + precache_model ("models/pu/nuke!.mdl"); + precache_model ("models/pu/carpenter!.mdl"); + precache_model ("models/pu/perkbottle!.mdl"); + + // start weapons + precache_model ("models/weapons/m1911/v_colt.mdl"); + precache_model ("models/weapons/m1911/g_colt.mdl"); + precache_model ("models/weapons/knife/v_knife.mdl"); + precache_model ("models/weapons/grenade/v_grenade.mdl"); + precache_model ("models/weapons/grenade/g_grenade.mdl"); + precache_model ("models/weapons/grenade/g_betty.mdl"); + precache_model ("models/weapons/grenade/v_betty.mdl"); + precache_model ("models/weapons/morphine/v_morphine.mdl"); + + // + // Sounds + // + + // player-made + precache_sound("sounds/player/footstep1.wav"); + precache_sound("sounds/player/footstep2.wav"); + precache_sound("sounds/player/footstep3.wav"); + precache_sound("sounds/player/footstep4.wav"); + precache_sound("sounds/player/footstep5.wav"); + precache_sound("sounds/player/jump.wav"); + precache_sound("sounds/player/land.wav"); + precache_sound("sounds/player/pain4.wav"); + + // weapons + precache_sound("sounds/weapons/colt/magin.wav"); + precache_sound("sounds/weapons/colt/magout.wav"); + precache_sound("sounds/weapons/colt/shoot.wav"); + precache_sound("sounds/weapons/colt/slide.wav"); + precache_sound("sounds/weapons/papfire.wav"); + + // grenade + precache_sound("sounds/weapons/grenade/prime.wav"); + precache_sound("sounds/weapons/grenade/throw.wav"); + precache_sound("sounds/weapons/grenade/explode.wav"); + + // melee + precache_sound("sounds/weapons/knife/knife_hitbod.wav"); + precache_sound("sounds/weapons/knife/knife.wav"); + precache_sound("sounds/weapons/knife/knife_hit.wav"); + + // tunes + precache_sound("sounds/rounds/eround.wav"); + precache_sound("sounds/rounds/nround.wav"); + precache_sound("sounds/rounds/splash.wav"); + precache_sound("sounds/music/end.wav"); + + // misc + precache_sound("sounds/misc/buy.wav"); + precache_sound("sounds/misc/wood_door.wav"); + precache_sound("sounds/misc/debris.wav"); + precache_sound("sounds/misc/denybuy.wav"); + precache_sound("sounds/misc/what.wav"); + + // power-ups + precache_sound ("sounds/pu/pickup.wav"); + precache_sound ("sounds/pu/carpenter.wav"); + precache_sound ("sounds/pu/maxammo.wav"); + precache_sound ("sounds/pu/double_points.wav"); + precache_sound ("sounds/pu/insta_kill.wav"); + precache_sound ("sounds/pu/nuke.wav"); + precache_sound ("sounds/pu/byebye.wav"); + precache_sound ("sounds/pu/powerup.wav"); + precache_sound ("sounds/pu/drop.wav"); + + // zombie walk + precache_sound ("sounds/zombie/w0.wav"); + precache_sound ("sounds/zombie/w1.wav"); + precache_sound ("sounds/zombie/w2.wav"); + precache_sound ("sounds/zombie/w3.wav"); + precache_sound ("sounds/zombie/w4.wav"); + precache_sound ("sounds/zombie/w5.wav"); + precache_sound ("sounds/zombie/w6.wav"); + precache_sound ("sounds/zombie/w7.wav"); + precache_sound ("sounds/zombie/w8.wav"); + precache_sound ("sounds/zombie/w9.wav"); + + // zombie run + precache_sound ("sounds/zombie/r0.wav"); + precache_sound ("sounds/zombie/r1.wav"); + precache_sound ("sounds/zombie/r2.wav"); + precache_sound ("sounds/zombie/r3.wav"); + precache_sound ("sounds/zombie/r4.wav"); + precache_sound ("sounds/zombie/r5.wav"); + precache_sound ("sounds/zombie/r6.wav"); + precache_sound ("sounds/zombie/r7.wav"); + precache_sound ("sounds/zombie/r8.wav"); + precache_sound ("sounds/zombie/r9.wav"); + + // zombie swipe + precache_sound ("sounds/zombie/a0.wav"); + precache_sound ("sounds/zombie/a1.wav"); + precache_sound ("sounds/zombie/a2.wav"); + precache_sound ("sounds/zombie/a3.wav"); + precache_sound ("sounds/zombie/a4.wav"); + precache_sound ("sounds/zombie/a5.wav"); + precache_sound ("sounds/zombie/a6.wav"); + precache_sound ("sounds/zombie/a7.wav"); + + // zombie death + precache_sound ("sounds/zombie/d0.wav"); + precache_sound ("sounds/zombie/d1.wav"); + precache_sound ("sounds/zombie/d2.wav"); + precache_sound ("sounds/zombie/d3.wav"); + precache_sound ("sounds/zombie/d4.wav"); + precache_sound ("sounds/zombie/d5.wav"); + precache_sound ("sounds/zombie/d6.wav"); + precache_sound ("sounds/zombie/d7.wav"); + + // zombie taunt + precache_sound ("sounds/zombie/t0.wav"); + precache_sound ("sounds/zombie/t1.wav"); + precache_sound ("sounds/zombie/t2.wav"); + precache_sound ("sounds/zombie/t3.wav"); + precache_sound ("sounds/zombie/t4.wav"); + + // zombie footsteps + precache_sound ("sounds/zombie/s0.wav"); + precache_sound ("sounds/zombie/s1.wav"); + precache_sound ("sounds/zombie/sc0.wav"); + precache_sound ("sounds/zombie/sc1.wav"); + + // null + precache_sound("sounds/null.wav"); + + #ifdef PC + // Moto -- FIXME: compile FTE to remove this engine sound request + precache_sound ("demon/dland2.wav"); + #endif +} + +//called when map loaded +void() worldspawn = +{ + precaches(); + LightStyles_setup(); + + #ifdef PC + clientstat(STAT_CURRENTMAG, EV_FLOAT, currentmag); + clientstat(STAT_CURRENTMAG2, EV_FLOAT, currentmag2); + clientstat(STAT_POINTS, EV_FLOAT, points); + clientstat(STAT_WEAPON2FRAME, EV_FLOAT, weapon2frame); + clientstat(STAT_WEAPON2MODEL, EV_STRING, weapon2model); + clientstat(STAT_GRENADES, EV_FLOAT, primary_grenades); + clientstat(STAT_SECGRENADES, EV_FLOAT, secondary_grenades); + clientstat(STAT_PROGRESSBAR, EV_FLOAT, progress_bar_percent); + clientstat(STAT_WEAPONDURATION, EV_FLOAT, weapon_animduration); + clientstat(STAT_WEAPON2DURATION, EV_FLOAT, weapon2_animduration); + clientstat(STAT_WEAPONZOOM, EV_FLOAT, zoom); + clientstat(STAT_INSTA, EV_FLOAT, insta_icon); + clientstat(STAT_X2, EV_FLOAT, x2_icon); + clientstat(STAT_SPECTATING, EV_FLOAT, isspec); + clientstat(STAT_PLAYERNUM, EV_FLOAT, playernum); // literally useless but will be kept in case + clientstat(STAT_PLAYERSTANCE, EV_FLOAT, stance); + clientstat(STAT_FACINGENEMY, EV_FLOAT, facingenemy); + clientstat(STAT_VIEWZOOM, EV_FLOAT, viewzoom); + #endif + + mappath = strcat("maps/", mapname); + mappath = strzone(mappath); + + LoadWaypointData(); + + //set game elements + G_STARTPOINTS = 500; + G_STARTROUND = 1; + G_PRONEPOINTS = 0; + G_STARTWEAPON[0] = W_COLT; + G_STARTWEAPON[1] = 8; + G_STARTWEAPON[2] = 32; + G_WORLDTEXT = 1; + G_PERKS = 0; + G_PERKPOWER = 0; + load_nzd(); + +} + +void() SpectatorConnect = +{ + bprint(PRINT_HIGH, self.netname); + bprint(PRINT_HIGH, " has joined the spectators.\n"); +} + +void() RelinkZombies = +{ + entity ent,ent2; + local float i; + local vector min, max; + + + //warn + ent = ent2 = world; + + while ((ent = find (ent, classname, "ai_zombie"))) + { + if(ent.currentHitBoxSetup == 0)//empty bbox, we don't care to update + continue; + + makevectors (ent.angles); + + for(i = 0; i < 3; i++) + { + if(i == 0) + ent2 = ent.head; + if(i == 1) + ent2 = ent.larm; + if(i == 2) + ent2 = ent.rarm; + + + if (ent2) + { + //setorigin (ent.head, ent.origin + v_right * ent.head.view_ofs_x + v_forward * ent.head.view_ofs_y + v_up * ent.head.view_ofs_z); + setorigin (ent2, ent.origin); + //fixme, move angles set and frame set to below the continue, we only want to update origin (maybe angles too?) + ent2.angles = ent.angles; + + if(ent2.deadflag) + ent2.frame = ent.frame; + + //if(OnlyOrigin) + // continue; + + min = ent2.bbmins + (v_right * ent2.view_ofs_x) + (v_forward * ent2.view_ofs_y) + (v_up * ent2.view_ofs_z); + max = ent2.bbmaxs + (v_right * ent2.view_ofs_x) + (v_forward * ent2.view_ofs_y) + (v_up * ent2.view_ofs_z); + + if(min_x > max_x) { min_x += max_x; max_x = min_x - max_x; min_x -= max_x; } + if(min_y > max_y) { min_y += max_y; max_y = min_y - max_y; min_y -= max_y; } + if(min_z > max_z) { min_z += max_z; max_z = min_z - max_z; min_z -= max_z; } + + setsize(ent2,min,max); + } + + } + } +} + +void() EndFrame = +{ + RelinkZombies(); +}; + +// +// a large switch statement to convert asset paths made for +// older versions of nzp to the new standard +// TODO: add strlower or similar to all platforms. +// +string(string asset) convert_old_asset_path = +{ + switch(asset) { + case "progs/Sprites/lamp_glow.spr": + return "models/sprites/lamp_glow.spr"; + case "progs/Sprites/lamp_glow2.spr": + return "models/sprites/lamp_glow2.spr"; + case "progs/Sprites/lamp_glow3.spr": + return "models/sprites/lamp_glow.spr"; + case "progs/Props/Kino_boxes2.mdl": + case "progs/props/kino_boxes2.mdl": + return "models/props/Kino_boxes2.mdl"; + case "progs/props/kino_boxes3.mdl": + return "models/props/Kino_boxes3.mdl"; + case "progs/Props/Kino_boxes4.mdl": + case "progs/props/kino_boxes4.mdl": + return "models/props/Kino_boxes4.mdl"; + case "progs/props/Kino_box.mdl": + case "progs/props/kino_box.mdl": + case "progs/Props/Kino_box.mdl": + return "models/props/Kino_box.mdl"; + case "progs/Props/table_dinner.mdl": + return "models/props/table_dinner.mdl"; + case "progs/Props/table_sq.mdl": + return "models/props/table_sq.mdl"; + case "progs/Props/Kino_table.mdl": + case "progs/props/kino_table.mdl": + return "models/props/Kino_table.mdl"; + case "progs/props/kino_couch.mdl": + return "models/props/kino_couch.mdl"; + case "progs/Props/metal_chair.mdl": + return "models/props/metal_chair.mdl"; + case "progs/Props/sandbags.mdl": + case "progs/props/sandbags.mdl": + return "models/props/sandbags.mdl"; + case "progs/Props/shelf.mdl": + case "progs/props/shelf.mdl": + return "models/props/shelf.mdl"; + case "progs/Props/dummy.mdl": + case "progs/props/dummy.mdl": + return "models/props/dummy.mdl"; + case "progs/props/stand.mdl": + return "models/props/stand.mdl"; + case "progs/Props/radio.mdl": + return "models/props/radio.mdl"; + case "progs/Props/radiator.mdl": + return "models/props/radiator.mdl"; + case "progs/Props/lamp_ndu.mdl": + case "progs/props/lamp_ndu.mdl": + return "models/props/lamp_ndu.mdl"; + case "progs/Props/piano.mdl": + return "models/props/piano.mdl"; + case "progs/props/teleporter.mdl": + return "models/props/teleporter.mdl"; + case "progs/props/chandelier.mdl": + return "models/props/chandelier.mdl"; + case "progs/props/vanity_table.mdl": + return "models/props/vanity_table.mdl"; + case "progs/props/trash_con.mdl": + return "models/props/trash_con.mdl"; + case "progs/props/kino_chairset.mdl": + return "models/props/kino_chairset.mdl"; + case "progs/props/Kino_lounge.mdl": + return "models/props/Kino_lounge.mdl"; + case "progs/props/kino_stageprop.mdl": + return "models/props/kino_stageprop.mdl"; + case "progs/props/mainframe_pad.mdl": + return "models/props/mainframe_pad.mdl"; + case "progs/Gmodels/g_mp40.mdl": + case "progs/gmodels/g_mp40.mdl": + return "models/weapons/mp40/g_mp40.mdl"; + case "progs/gmodels/g_thomp.mdl": + return "models/weapons/thomp/g_thomp.mdl"; + case "progs/Gmodels/g_betty.mdl": + case "progs/gmodels/g_betty.mdl": + return "models/weapons/grenade/g_betty.mdl"; + case "progs/GModels/g_fg.mdl": + return "models/weapons/fg/g_fg.mdl"; + case "progs/gmodels/g_m1.mdl": + return "models/weapons/garand/g_m1.mdl"; + case "progs/gmodels/g_trench.mdl": + return "models/weapons/trench/g_trench.mdl"; + case "progs/gmodels/g_bar.mdl": + return "models/weapons/bar/g_bar.mdl"; + case "progs/gmodels/g_grenade.mdl": + return "models/weapons/grenade/g_grenade.mdl"; + case "progs/gmodels/g_gewehr.mdl": + return "models/weapons/gewehr/g_gewehr.mdl"; + case "progs/gmodels/g_bowie.mdl": + return "models/weapons/knife/g_bowie.mdl"; + case "progs/gmodels/g_type.mdl": + return "models/weapons/type/g_type.mdl"; + case "progs/gmodels/g_stg.mdl": + return "models/weapons/stg/g_stg.mdl"; + case "models/misc/lightning.spr": + return "models/sprites/lightning.spr"; + default: return asset; + } + + return asset; +}; \ No newline at end of file diff --git a/source/server/nzdparser.qc b/source/server/nzdparser.qc new file mode 100644 index 0000000..916cfac --- /dev/null +++ b/source/server/nzdparser.qc @@ -0,0 +1,378 @@ +/* + server/nzdparser.qc + + parses NZData files for map variety + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +//custom weapon search +void(string weaponFile) nzd_defineweapon = { + float f, p, l, fnum, wepnum, framenum, fsnd; + string line; + + fnum = 0; + framenum = 0; + fsnd = 0; + + line = strcat(strcat("data/maps/", weaponFile), ".cw"); + f = fopen(line, FILE_READ); + + if (f == -1) { //no custom weapons + return; + } + wepnum = currentWeaponTracker - 1; + p = 0; + l = 1; + + while(l) { + string li; + li = fgets(f); + + if not (li) { + l = 0; + break; + } + + line = strtrim(li); + + if (line == "") + continue; + + switch(p) { + case 0: + wepnum += 1; + currentWeaponTracker = wepnum; + if (line == "weapon") + p = 1; + break; + case 1: + if (line == "{") + p = 2; + else + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(line, " expected {\n"))); + break; + case 2: + tokenize(line); + + string v, vari; + vari = strtrim(argv(0)); + v = strtrim(argv(2)); + + switch(vari) { + case "name": + CustomWeapons[wepnum].name = v; + break; + case "mag": + CustomWeapons[wepnum].magSize = stof(v); + break; + case "reserve": + CustomWeapons[wepnum].reserveSize = stof(v); + break; + case "vmodel": + CustomWeapons[wepnum].vmodel = v; + break; + case "vmodel2": + CustomWeapons[wepnum].vmodel2 = v; + break; + case "gmodel": + CustomWeapons[wepnum].gmodel = v; + break; + case "ads": + CustomWeapons[wepnum].adsofs = stov(v); + break; + case "firetype": + CustomWeapons[wepnum].firetype = stof(v); + break; + case "ispap": + CustomWeapons[wepnum].ispap = stof(v); + break; + case "nonpap": + CustomWeapons[wepnum].nonpap = stof(v); + break; + case "pap": + CustomWeapons[wepnum].papWpn = stof(v); + break; + case "dmg": + CustomWeapons[wepnum].damage = stof(v); + break; + case "shotcount": + CustomWeapons[wepnum].shotcount = stof(v); + break; + case "bodypen": + CustomWeapons[wepnum].bodypen = stof(v); + break; + case "penetration": + CustomWeapons[wepnum].penetration = stof(v); + break; + case "spread": + CustomWeapons[wepnum].spread = stof(v); + break; + case "fdelay": + CustomWeapons[wepnum].fdelay = stof(v); + break; + case "rdelay": + CustomWeapons[wepnum].rdelay = stof(v); + break; + case "walkspeed": + CustomWeapons[wepnum].walkspeed = stof(v); + break; + case "firesound": + CustomWeapons[wepnum].firesound = v; + break; + case "skin": + CustomWeapons[wepnum].skin = stof(v); + break; + case "recoil": + CustomWeapons[wepnum].recoil = stof(v); + break; + case "crossmin": + CustomWeapons[wepnum].crossmin = stof(v); + break; + case "crossmax": + CustomWeapons[wepnum].crossmax = stof(v); + break; + case "lowmag": + CustomWeapons[wepnum].lowmag = stof(v); + break; + case "lowreserve": + CustomWeapons[wepnum].lowreserve = stof(v); + break; + case "flash": + CustomWeapons[wepnum].flash = stov(v); + break; + case "flashsize": + CustomWeapons[wepnum].flashsize = stof(v); + } + + #ifdef PC + /*sendCustomWeapon(wepnum, CustomWeapons[wepnum].name, CustomWeapons[wepnum].magSize, + CustomWeapons[wepnum].reserveSize, CustomWeapons[wepnum].vmodel, CustomWeapons[wepnum].vmodel2, + CustomWeapons[wepnum].gmodel, CustomWeapons[wepnum].isDual, CustomWeapons[wepnum].firetype, + CustomWeapons[wepnum].nonpap, CustomWeapons[wepnum].pap, CustomWeapons[wepnum].ispap, + CustomWeapons[wepnum].damage, CustomWeapons[wepnum].shotcount, CustomWeapons[wepnum].bodypen, + CustomWeapons[wepnum].penetration, CustomWeapons[wepnum].spread, CustomWeapons[wepnum].fdelay, + CustomWeapons[wepnum].rdelay, CustomWeapons[wepnum].walkspeed, CustomWeapons[wepnum].firesound, + CustomWeapons[wepnum].skin, CustomWeapons[wepnum].recoil, CustomWeapons[wepnum].crossmin, + CustomWeapons[wepnum].crossmax, CustomWeapons[wepnum].lowmag, CustomWeapons[wepnum].lowreserve, + CustomWeapons[wepnum].flash, CustomWeapons[wepnum].flashsize, CustomWeapons[wepnum].papWpn, + CustomWeapons[wepnum].adsofs);*/ + #endif + + break; + case 3: + if (line == "[") { + p = 4; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(line, " expected [\n"))); + } + break; + case 4: + if (line == "]") { + p = 2; + } else { + switch(wepnum) { + /*case 0: W_C1FRAMES[fnum] = stof(line); break; + case 1: W_C2FRAMES[fnum] = stof(line); break; + case 2: W_C3FRAMES[fnum] = stof(line); break; + case 3: W_C4FRAMES[fnum] = stof(line); break;*/ + } + fnum++; + } + break; + case 5: + if (line == "[") { + p = 6; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(line, " expected [\n"))); + } + break; + case 6: + if (line == "]") { + p = 2; + } else { + switch(wepnum) { + /*case 0: W_C1XSNDFRAME[framenum] = stof(line); break; + case 1: W_C2XSNDFRAME[framenum] = stof(line); break; + case 2: W_C3XSNDFRAME[framenum] = stof(line); break; + case 3: W_C4XSNDFRAME[framenum] = stof(line); break;*/ + } + framenum++; + } + break; + case 7: + if (line == "[") { + p = 8; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(line, " expected [\n"))); + } + break; + case 8: + if (line == "]") { + p = 2; + } else { + switch(wepnum) { + /*case 0: W_C1XTRASND[fsnd] = line; break; + case 1: W_C2XTRASND[fsnd] = line; break; + case 2: W_C3XTRASND[fsnd] = line; break; + case 3: W_C4XTRASND[fsnd] = line; break;*/ + } + fsnd++; + } + break; + } + } + fclose(f); +} + +//basically copying the waypoint code.. yikes +void() load_nzd = { + + #ifndef NX + float file, point; + string h; + float loop; + float wepdata = 0; + + h = strcat(mappath, ".nzd"); + file = fopen (h, FILE_READ); + + if (file == -1) + { + if (cvar("developer")) + dprint(".NZD file not found, using default settings..\n"); + return; + } + + point = 0; + loop = 1; + + while(loop) { + string line; + line = fgets(file); + if not (line) { + bprint(PRINT_HIGH, "End of file\n"); + loop = 0; + break; + } + h = strtrim(line); + + //print(h, "\n"); + if (h == "") { + continue; + } + + switch (point) { + case 0: + //switch-ception! + switch(h) { + case "mech": point = 1; break; + default: bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, "\n"))); + } + break; + case 1: + if (h == "{") { + point = 2; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected {\n"))); + } + break; + case 2: + tokenize(h); + + string value, variable; + variable = strtrim(argv(0)); + value = strtrim(argv(2)); + switch (variable) { + case "proneperkpoints": + G_PRONEPOINTS = stof(value); + break; + case "spawnpoints": + G_STARTPOINTS = stof(value); + break; + case "round": + G_STARTROUND = stof(value); + break; + case "worldtext": + G_WORLDTEXT = stof(value); + break; + case "perkpower": + G_PERKPOWER = stof(value); + break; + case "includeweapon": + nzd_defineweapon(value); + break; + case "hud": + G_HUD = value; + break; + case "hudhor": + G_HUDHOR = stof(value); + break; + case "wep": + point = 3; + break; + case "perk": + point = 5; + break; + case "}": + loop = 0; + break; + default: + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected }\n"))); + break; + } + break; + case 3: + if (h == "[") { + point = 4; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected [\n"))); + } + break; + case 4: + if (h == "]") { + point = 2; + } else { + G_STARTWEAPON[wepdata] = stof(h); + wepdata++; + } + break; + case 5: + if (h == "[") { + point = 6; + } else { + bprint(PRINT_HIGH, strcat("Error: unknown variable ", strcat(h, " expected [\n"))); + } + break; + case 6: + if (h == "]") { + point = 2; + } else { + G_PERKS = G_PERKS | stof(h); + } + break; + } + } + + fclose(file); + #endif +} \ No newline at end of file diff --git a/source/server/player.qc b/source/server/player.qc new file mode 100644 index 0000000..d33d5f5 --- /dev/null +++ b/source/server/player.qc @@ -0,0 +1,810 @@ +/* + server/nzdparser.qc + + Various stuff done for the player, including per-frame functions + like PlayerPreThink and PlayerPostThink, also client specific + stuff like PutClientInServer etc. + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void(entity e) Light_None; + +// player animations + +void() playdown =[ 1, playdown1 ] {self.frame = 32;} +void() playdown1 =[ 2, playdown2 ] {self.frame = 33;} +void() playdown2 =[ 2, playdown3 ] {self.frame = 34;} +void() playdown3 =[ 2, playdown4 ] {self.frame = 35;} +void() playdown4 =[ 2, playdown5 ] {self.frame = 36;} +void() playdown5 =[ 2, playdown5 ] {self.frame = 37;} +// +void() playreload =[ 1, playreload1 ] {self.frame = 11;} +void() playreload1 =[ 2, playreload2 ] {self.frame = 12;} +void() playreload2 =[ 3, playreload3 ] {self.frame = 13;} +void() playreload3 =[ 4, playreload4 ] {self.frame = 14;} +void() playreload4 =[ 5, playreload5 ] {self.frame = 15;} +void() playreload5 =[ 6, playreload6 ] {self.frame = 16;} +void() playreload6 =[ 7, playreload7 ] {self.frame = 17;} +void() playreload7 =[ 8, playreload8 ] {self.frame = 18;} +void() playreload8 =[ 9, playreload9 ] {self.frame = 19;} +void() playreload9 =[ 10, playreload10 ] {self.frame = 20;} +void() playreload10 =[ 11, playreload11 ] {self.frame = 21;} +void() playreload11 =[ 12, playreload12 ] {self.frame = 22;} +void() playreload12 =[ 13, playreload13 ] {self.frame = 23;} +void() playreload13 =[ 14, playreload13 ] {self.frame = 24;} +// +void() playdownfire =[ 1, playdownfire1 ] {self.frame = 38;} +void() playdownfire1 =[ 2, playdownfire1 ] {self.frame = 39;} +// +void() playaim =[ 1, playaim1 ] {self.frame = 8;} // naievil -- player aimin anim +void() playaim1 =[ 2, playaim1 ] {self.frame = 9;} // naievil -- second player aimin anim +// +void() playout =[ 1, playout1 ] {self.frame = 10;} // naievil -- player aim out anim +void() playout1 =[ 2, playout1 ] {self.frame = 11;} // naievil -- second player aim out anim +// +void() playrun1 =[ 1, playrun2 ] {self.frame = 25;} +void() playrun2 =[ 2, playrun3 ] {self.frame = 26;} +void() playrun3 =[ 3, playrun4 ] {self.frame = 27;} +void() playrun4 =[ 4, playrun5 ] {self.frame = 28;} +void() playrun5 =[ 5, playrun6 ] {self.frame = 29;} +void() playrun6 =[ 6, playrun7 ] {self.frame = 30;} +void() playrun7 =[ 7, playrun8 ] {self.frame = 31;} +void() playrun8 =[ 8, playrun9 ] {self.frame = 25;} +void() playrun9 =[ 9, playrun10 ] {self.frame = 26;} +void() playrun10 =[ 10, playrun11 ] {self.frame = 27;} +void() playrun11 =[ 11, playrun12 ] {self.frame = 28;} +void() playrun12 =[ 12, playrun13 ] {self.frame = 29;} +void() playrun13 =[ 13, playrun14 ] {self.frame = 30;} +void() playrun14 =[ 14, playrun15 ] {self.frame = 25;} +void() playrun15 =[ 15, playrun16 ] {self.frame = 26;} +void() playrun16 =[ 16, playrun17 ] {self.frame = 27;} +void() playrun17 =[ 17, playrun18 ] {self.frame = 28;} +void() playrun18 =[ 18, playrun19 ] {self.frame = 29;} +void() playrun19 =[ 19, playrun20 ] {self.frame = 30;} +void() playrun20 =[ 20, playrun21 ] {self.frame = 31;} +void() playrun21 =[ 21, playrun22 ] {self.frame = 25;} +void() playrun22 =[ 22, playrun23 ] {self.frame = 26;} +void() playrun23 =[ 23, playrun24 ] {self.frame = 27;} +void() playrun24 =[ 24, playrun25 ] {self.frame = 28;} +void() playrun25 =[ 25, playrun26 ] {self.frame = 29;} +void() playrun26 =[ 26, playrun27 ] {self.frame = 30;} +void() playrun27 =[ 27, playrun28 ] {self.frame = 25;} +void() playrun28 =[ 28, playrun29 ] {self.frame = 26;} +void() playrun29 =[ 29, playrun30 ] {self.frame = 27;} +void() playrun30 =[ 30, playrun31 ] {self.frame = 28;} +void() playrun31 =[ 31, playrun32 ] {self.frame = 29;} +void() playrun32 =[ 32, playrun33 ] {self.frame = 30;} +void() playrun33 =[ 33, playrun33 ] {self.frame = 31;} +// +void() playwalk =[ 1, playwalk1 ] {if (self.velocity) { self.frame = 0; }} +void() playwalk1 =[ 2, playwalk2 ] {if (self.velocity) { self.frame = 1; }} +void() playwalk2 =[ 3, playwalk3 ] {if (self.velocity) { self.frame = 2; }} +void() playwalk3 =[ 4, playwalk4 ] {if (self.velocity) { self.frame = 3; }} +void() playwalk4 =[ 5, playwalk5 ] {if (self.velocity) { self.frame = 4; }} +void() playwalk5 =[ 6, playwalk6 ] {if (self.velocity) { self.frame = 5; }} +void() playwalk6 =[ 7, playwalk7 ] {if (self.velocity) { self.frame = 6; }} +void() playwalk7 =[ 8, playwalk8 ] {if (self.velocity) { self.frame = 7; }} +void() playwalk8 =[ 9, playwalk8 ] {if (self.velocity) { self.frame = 8; }} +// +void() playgetup =[ 1, playgetup1 ] {self.frame = 38;} +void() playgetup1 =[ 2, playgetup2 ] {self.frame = 39;} +void() playgetup2 =[ 3, playgetup3 ] {self.frame = 40;} +void() playgetup3 =[ 4, playgetup4 ] {self.frame = 41;} +void() playgetup4 =[ 5, playgetup5 ] {self.frame = 42;} +void() playgetup5 =[ 6, playgetup6 ] {self.frame = 43;} +void() playgetup6 =[ 7, playgetup7 ] {self.frame = 44;} +void() playgetup7 =[ 8, playgetup8 ] {self.frame = 45;} +void() playgetup8 =[ 9, playgetup9 ] {self.frame = 46;} +void() playgetup9 =[ 10, playgetup10 ] {self.frame = 47;} +void() playgetup10 =[ 11, playgetup10 ] {self.frame = 48;} + +#define forward 0 +#define backward 1 +#define left 2 +#define right 3 +#define all_move -1 +float(float dir) checkMovement = +{ + makevectors(self.movement); + string a = vtos(self.movement); + float x, y; + + tokenize(a); + x = stof(argv(0)); + y = stof(argv(1)); + + switch(dir) { + case forward: + if (x > 0) + return 1; + break; + case backward: + if (x < 0) + return 1; + break; + case right: + if (y > 0) + return 1; + break; + case left: + if (y < 0) + return 1; + break; + case all_move: + if (x || y) + return 1; + break; + default: + return 0; + } +} + +void() PlayerJump = +{ + if (!(self.flags & FL_ONGROUND) + || !(self.flags & FL_JUMPRELEASED) + || self.downed + || self.dive ) { + return; + } + + self.flags = self.flags - (self.flags & FL_JUMPRELEASED); + sound(self, CHAN_VOICE, "sounds/player/jump.wav", 1, 1.75); + + if (self.button2) + self.button2 = 0; + + self.oldz = self.origin_z; + + self.velocity_z = 230; +} + +void(float override) JumpCheck = +{ + #ifndef PC + override = 0; + #endif + + if(self.button2 || override) { + if (self.downed) + return; + + if (self.stance == 2) { + // naievil -- stop sprinting if we jump, which is a real mechanic from the game that we never implemented + if (self.sprinting) { + W_SprintStop(); + } + PlayerJump(); + } else if (self.view_ofs_z == self.new_ofs_z && (self.flags & FL_ONGROUND)) { + switch(self.stance) { + case 0: + self.new_ofs_z = self.view_ofs_z + 42; + self.stance = 2; + break; + case 1: + self.new_ofs_z = self.view_ofs_z + 24; + self.stance = 2; + break; + default: break; + } + } + } else + self.flags = self.flags | FL_JUMPRELEASED; +} + +void() PlayerPreThink = +{ + if (self.downed) { + self.maxspeed = 30; + } else { + self.maxspeed = 175; + if (self.sprinting) { + #ifdef PC + if (self.viewzoom > 0.9) + self.viewzoom -= 0.015; + else if (self.viewzoom < 0.9) + self.viewzoom = 0.9; + + // viewbob when running + self.punchangle_x = 1*sin(time*15); + #endif + playrun1(); + self.maxspeed *= 1.66; + } else if (!self.sprinting && !self.zoom) { + #ifdef PC + if (self.viewzoom < 1) + self.viewzoom += 0.015; + else + self.viewzoom = 1; + + if (checkMovement(-1)) + self.punchangle_x = 0.25*sin(time*15); + #endif + } else if (self.zoom != 3) { + self.maxspeed *= 0.5; + } if (self.damagetimer > time) { + self.maxspeed *= 0.5; + } + + switch(self.stance) { + case 1: + self.maxspeed *= 0.5; + break; + case 0: + self.maxspeed *= 0.25; + break; + } + + #ifdef PC + if (checkMovement(backward)) { + self.maxspeed *= 0.7; + } else if (checkMovement(left) || checkMovement(right)) { + self.maxspeed *= 0.8; + } + #endif + + self.maxspeed *= GetWeaponWalkSpeed(self.perks, self.weapon); + } + if(self.isspec != 0 && !self.downed) + { + if(self.button0) + { + self.aiment = find(self.aiment, classname, "player"); + if(self.aiment != world) + { + sprint(self, PRINT_HIGH, "Now spectating "); + sprint(self, PRINT_HIGH, self.aiment.netname); + sprint(self, PRINT_HIGH, "\n"); + + self.solid = SOLID_NOT; + self.movetype = MOVETYPE_NONE; + } + else + { + sprint(self, PRINT_HIGH, "Freefly spectate\n"); + self.movetype = MOVETYPE_FLY; + } + } + if(self.aiment != world) + { + self.origin = self.aiment.origin; + self.angles = self.aiment.v_angle; + self.velocity = self.aiment.velocity; + self.fixangle = TRUE; + } + return; + } + + if (cvar("waypoint_mode")) { + Waypoint_Logic(); + } else { + Weapon_Logic(); + } + + JumpCheck(0); + + // refuel/cool m2 + if (self.ltime < time) { + if (self.currentmag == 0 && !self.cooldown) { + self.cooldown = true; + } + + if (self.cooldown && self.currentmag > 20) + self.cooldown = false; + + if (self.weapon == W_M2 || self.weapon == W_FIW && self.currentmag < getWeaponMag(self.weapon)) + self.currentmag += 1; + + self.ltime = time + 0.1; + } +}; + +void() PlayerPostThink = +{ + if(self.isspec) + return; + + //landsound + if((self.oldvelocity_z < -10) && (self.flags & FL_ONGROUND)) + { + if(self.oldvelocity_z < -270) + sound(self, CHAN_BODY, "sounds/player/land.wav", 1, 1.75); + self.lastsound_time = time - 0.15; + } + + #ifdef PC + //footsteps + if((vlen(self.velocity) > 100) &&(( time - self.lastsound_time > 0.4) || (time - self.lastsound_time > 0.3 && self.sprinting)) && (self.flags & FL_ONGROUND)) + { + local float movelen = vlen(input_movevalues); + if(movelen > 300) + { + if (!self.sprinting) + playwalk(); + + local float ran = random(); + if(ran > 0.8) + sound(self, CHAN_BODY, "sounds/player/footstep1.wav", 0.8, 2.5); + else if(ran > 0.6) + sound(self, CHAN_BODY, "sounds/player/footstep2.wav", 0.8, 2.5); + else if(ran > 0.4) + sound(self, CHAN_BODY, "sounds/player/footstep3.wav", 0.8, 2.5); + else if(ran > 0.2) + sound(self, CHAN_BODY, "sounds/player/footstep4.wav", 0.8, 2.5); + else + sound(self, CHAN_BODY, "sounds/player/footstep5.wav", 0.8, 2.5); + self.lastsound_time = time; + } + } + #endif + + if (self.health_delay < time && self.health != self.max_health && !self.downed) + { + self.health = self.health + 5; + if (self.max_health < self.health) + self.health = self.max_health; + } + + if (self.progress_bar) { + if (self.progress_bar < time) { + if (self.downed) + GetUp(); + + if (self.reviving) + self.revived = 1; + + self.progress_bar = 0; + self.progress_bar_time = 0; + self.progress_bar_percent = 0; + + + } else { + float remaining = self.progress_bar - time; + self.progress_bar_percent = invertfloat((remaining / self.progress_bar_time)); + } + } + + if (self.sprinting) { + self.sprint_timer = self.sprint_duration + (time - self.sprint_start_time); + + #ifndef PC + if (!self.velocity || self.stance != 2) { + W_SprintStop(); + } + #else + if (!self.velocity || !checkMovement(0) || self.stance != 2) { //moto (FIXME) -- move me! + W_SprintStop(); + } + #endif + + if (self.perks & P_STAMIN) { + if (self.sprint_timer > sprint_max_time * 2) { + W_SprintStop(); + } + } + else + { + if (self.sprint_timer > sprint_max_time) { + W_SprintStop(); + } + } + + } else if (self.sprint_duration > 0.0) { + self.sprint_rest_time = (time - self.sprint_stop_time); + } + + self.oldvelocity = self.velocity; + + // Perform a traceline to keep track of entities directly + // in front of the player. + #ifndef NX + vector source; + + makevectors (self.v_angle); + source = self.origin + self.view_ofs; + + traceline (source, source + v_forward*800*1.2, 0, self); + + // use .head here to avoid expanding ent struct + self.head = trace_ent; + + // check whether we're looking at an entity separately to communicate + // with the client more reasonably + if (trace_ent.classname == "ai_zombie" || trace_ent.classname == "ai_zombie_head" + || trace_ent.classname == "ai_zombie_rarm" ||trace_ent.classname == "ai_zombie_larm" + || trace_ent.classname == "ai_dog") + self.facingenemy = true; + else + self.facingenemy = false; + #endif +}; + +void() ClientKill = {}; + +//called when a client connects to the server +void() ClientConnect = +{ + #ifdef PC + // Maintain old FGD values and just convert them to new ones + // + if (world.fog) { + // Force fog on PC + if (!world.PC_fog) { + // Don't execute more than once + if (!world_fog) { + string fog_start_end, fog_R, fog_B, fog_G; + + // Tokenize our fog + tokenize(world.fog); + + // Get values and transmute it a bit if necessary + // Originally: start | end | R | G | B + fog_start_end = ftos(((stof(argv(0))) / stof(argv(1))) / 3.5); + + fog_R = ftos(stof(argv(2))/255); + fog_G = ftos(stof(argv(3))/255); + fog_B = ftos(stof(argv(4))/255); + + // Restore into our world.fog + world_fog = strcat(fog_start_end, " ", fog_R, " ", fog_G, " ", fog_B); + } + localcmd(strcat("fog ", world_fog)); + } + } + #endif + + if(cvar("developer") || player_count > 1) { + bprint(PRINT_HIGH, self.netname); //print player name + bprint(PRINT_HIGH, " connected.\n"); + } +}; + +void() PollPlayerPoints = +{ + float i, breakpoint; + entity pollent; + breakpoint = 0; + + for (i = 1; i <= 4 && !breakpoint; i++) + { + pollent = findfloat(world, playernum, i); + + if (pollent == world) { + breakpoint = 1; + break; + } + UpdatePlayerPoints(i, pollent.points, pollent.kills, 0, pollent.netname, pollent); + } +} + +void() PlayerSpawn = +{ + local entity spawnpoint; + local_client = self; + + spawnpoint = find(world, classname, "info_player_start"); + + self.isspec = FALSE; + self.classname = "player"; + self.solid = SOLID_BBOX; + setmodel(self, "models/player.mdl"); + self.movetype = MOVETYPE_WALK; + self.max_health = self.health = 100; + + //custom weapon stuff (we want to put this here so we can send the info to the client with an entity) + // DISGUSTING. + //nzd_defineweapon(self); + + //centerprint(self, CustomWeapons[0].vmodel); + + entity who = find(world,classname,"player"); + while(who != self && !self.playernum) + { + if(who) + { + coop = 1; + player_count++; + break; + } + } + + switch(player_count) { + default: spawnpoint = find(world, classname, "info_player_start"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + case 1: spawnpoint = find(world, classname, "info_player_nikolai"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + case 2: spawnpoint = find(world, classname, "info_player_takeo"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + case 3: spawnpoint = find(world, classname, "info_player_doctor"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + } + + if (self.playernum) { + switch(self.playernum) { + default: spawnpoint = find(world, classname, "info_player_start"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + case 1: spawnpoint = find(world, classname, "info_player_nikolai"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + case 2: spawnpoint = find(world, classname, "info_player_takeo"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + case 3: spawnpoint = find(world, classname, "info_player_doctor"); if (!spawnpoint) {spawnpoint = find(world, classname, "info_player_start");} break; + } + } + + self.origin = spawnpoint.origin + [0,0,1]; + self.angles = spawnpoint.angles; + self.fixangle = TRUE; + setsize(self, [-16, -16, -32], [16, 16, 40]); + self.view_ofs = VEC_VIEW_OFS; // naievil -- set view_ofs to 32 to maintain half life (64) sizes + self.stance = 2; + self.new_ofs_z = self.view_ofs_z; + self.oldz = self.origin_z; + + self.currentammo = G_STARTWEAPON[2]; + self.currentmag = G_STARTWEAPON[1]; + self.weapon = G_STARTWEAPON[0]; + self.grenades = self.grenades | 1; // add frag grenades to player inventory + + if (rounds) + self.primary_grenades = 2; + else + self.primary_grenades = 0; // start off without grenades + + self.pri_grenade_state = 0; // defines that frag grenades are for player first, not betty + self.secondary_grenades = -1; // shows that we both don't have betties AND shouldn't draw the image onscreen + + if (!self.points) + addmoney(self, G_STARTPOINTS, 0); + + self.weaponmodel = GetWeaponModel(self.weapon, 0);// Give weapon model + self.weapon2model = GetWeapon2Model(self.weapon); + + SwitchWeapon(self.weapon); + + self.stamina = 3; + self.reviving = 0; + self.weaponnum = 0; + self.perks = G_PERKS; + SetPerk(self, self.perks); + + //self.zoom = 1; // This is to fix an aimin bug for the kar scope + + if (!self.playernum) { + self.playernum = player_count + 1; + if (self.playernum == 1) + pl1 = self; + } + + if (rounds < 1) { + sound(self, CHAN_AUTO, "sounds/rounds/splash.wav", 1, ATTN_NONE); + } + + PromptLevelChange(self.nextthink + 3, 1, self); + UpdatePlayerCount(player_count); + + #ifdef PC + PollPlayerPoints(); + UpdateV2model("", 0); + stuffcmd(self, "cl_gunx 8;cl_guny 16;cl_gunz 25\n"); + SetRound(self, G_STARTROUND); + self.viewzoom = 1; + self.weapon_animduration = getWeaponDelay(self.weapon, FIRE); + + if (G_WORLDTEXT) + WorldText(world.chaptertitle, world.location, world.date, world.person, self); + #else + self.Weapon_Name = GetWeaponName(self.weapon); + self.Flash_Offset = GetWeaponFlash_Offset(self.weapon); + self.Flash_Size = GetWeaponFlash_Size(self.weapon); + #endif + + if (G_STARTROUND != 1) { + rounds = G_STARTROUND - 1; + } + + #ifndef PSP + //pushHUD(G_HUD, G_HUDHOR, self); //FIXME - breaks mp! + #endif +}; + +void() SpectatorSpawn = +{ + local entity spawnpoint; + spawnpoint = find(world, classname, "info_player_start"); + + self.isspec = TRUE; + self.classname = "spectator"; + self.solid = SOLID_NOT; + setmodel(self, ""); + self.movetype = MOVETYPE_FLY; + self.origin = spawnpoint.origin + [0,0,1]; + self.fixangle = TRUE; + setsize(self, [-16, -16, -24], [16, 16, 32]); + self.view_ofs = '0 0 22'; + self.aiment = world; +}; + +//called when a client loads a map +void() PutClientInServer = +{ + if(cvar("developer") || player_count > 1) { + bprint(PRINT_HIGH, self.netname); + bprint(PRINT_HIGH, " has joined the game.\n"); + } + + if (spawn_time > time || !rounds) + PlayerSpawn(); + // TEMPORARY +#ifdef PC + else + SpectatorSpawn(); +#endif +}; + +//called when client disconnects from the server +void() ClientDisconnect = +{ + bprint(PRINT_HIGH, self.netname); + bprint(PRINT_HIGH, " has left the game.\n"); + player_count--; + UpdatePlayerCount(player_count); +}; + + +void() SetNewParms = +{ +}; + +void() SetChangeParms = +{ +}; + +void(string com) SV_ParseClientCommand = +{ + + if(com == "joingame") + { + if(self.isspec) + { + bprint(PRINT_HIGH, self.netname); + bprint(PRINT_HIGH, " has joined the game.\n"); + PlayerSpawn(); + } + else + sprint(self, PRINT_HIGH, "You're already in game!\n"); + } + else if(com == "specgame") + { + if(self.isspec) + return; + else + { + coop = 1; + + /*SpectatorSpawn(); + bprint(PRINT_HIGH, self.netname); //print player name + bprint(PRINT_HIGH, " has joined the spectators.\n");*/ + + PlayerSpawn(); + } + } + else if(com == "pause") + { + static float paused; + paused = !paused; + #ifdef PC + setpause(paused); + #endif + } + else if (com == "noclip") + { + #ifndef PC + entity benis = self; + other = find(world, classname, "player"); + self = other; + #endif + + if (self.movetype == MOVETYPE_WALK) + self.movetype = MOVETYPE_NOCLIP; + else + self.movetype = MOVETYPE_WALK; + + #ifndef PC + localcmd(com); + self = benis; + #endif + } + else + { + tokenize(com); + + switch(argv(0)) + { + case "give": + entity tempe = self; + other = find(world, classname, "player"); + self = other; + + float wep = stof(argv(1)); + + if (wep) { + if (!self.secondaryweapon) { + WeaponSwitch(self); + } + + float startframe, endframe; + string modelname; + + self.weapon = wep; + self.currentammo = getWeaponAmmo(wep); + self.currentmag = getWeaponMag(wep); + if (IsDualWeapon(wep)) { + self.currentmag2 = self.currentmag; + } + + self.weaponskin = GetWepSkin(self.weapon); + + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(wep, 0); + + SwitchWeapon(wep); + + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH); + + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; + + #ifndef PC + self.Weapon_Name = GetWeaponName(self.weapon); + #endif + } + break; + case "god": + if (!(self.flags & FL_GODMODE)) + self.flags = self.flags | FL_GODMODE; + else + self.flags = self.flags & (~FL_GODMODE); + break; + case "nextround": + rounds++; + break; + case "prevround": + rounds--; + break; + case "addmoney": + entity tempe1 = self; + other = find(world, classname, "player"); + self = other; + + addmoney(self, stof(argv(1)), 1); + + self = tempe1; + break; + default: + #ifndef PC + bprint(PRINT_HIGH, "Command not found in QC\n"); + #endif + break; + } + } +}; + +#ifdef PC +void() SV_RunClientCommand = +{ + runstandardplayerphysics(self); +} +#endif diff --git a/source/server/psp_specifics.qc b/source/server/psp_specifics.qc new file mode 100644 index 0000000..1059f53 --- /dev/null +++ b/source/server/psp_specifics.qc @@ -0,0 +1,183 @@ +/* + server/psp_specifics.qc + + stuff only for psp, remove later + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() Do_Zombie_A = {}; +void() func_door_nzp; +void() mystery_box; + +entity windows[32]; +float wincnt; + +void () CL_SendWeaponFire = +{ + float return_time; + vector Wep_Recoil; + + Wep_Recoil = GetWeaponRecoil(self.weapon); + + msg_entity = self; + WriteByte(MSG_ONE, SVC_WEAPONFIRE); + return_time = getWeaponRecoilReturn(self.weapon); + WriteLong(MSG_ONE, return_time); + WriteCoord (MSG_ONE, Wep_Recoil_x); + WriteCoord (MSG_ONE, Wep_Recoil_y); + WriteCoord (MSG_ONE, Wep_Recoil_z); + //self.punchangle = Wep_Recoil; + self.recoil_delay = 60/return_time + time; +} + +void() trigger_activator = {}; + +void() ParseClientCommand = {SV_ParseClientCommand(CMD_STRING);} + +void() PutClientInServer; +void() InitRounds; + +//moto -- put this here because it keeps soft_restart somewhat clean.. +void(entity door) reclose_door = { + entity oldself; + oldself = self; + + self = door; + setmodel(self, self.oldmodel); + self.solid = SOLID_BSP; + setorigin(self, self.oldorigin); + self.isopen = 0; + func_door_nzp(); + + //Close_Waypoint(self.wayTarget); + + self = oldself; +} + +void() Soft_Restart = { + entity who, oldself, doors, box, revive, endgame; + self = find(world,classname,"player"); + oldself = self; + + //remove all zombies + who = find(world,classname,"ai_zombie"); + while(who != world) + { + if(who.health) + { + self = who; + self.th_die(); + + // hide bodies + setmodel(self, ""); + if (self.head) + setmodel(self.head, ""); + if (self.larm) + setmodel(self.larm, ""); + if (self.rarm) + setmodel(self.rarm, ""); + + self = oldself; + } + + who = find(who,classname,"ai_zombie"); + } + + //repair all windows + for(float i = 0; i < wincnt; i++) { + if (windows[i].health != -10) { + windows[i].health = 6; + windows[i].frame = 0; + } + } + + //close doors + doors = findfloat(world, isopen, 1); + while (doors) { + if (doors.isopen) + reclose_door(doors); + doors = findfloat(world, isopen, 1); + } + + //revert mystery box + box = find(world, classname, "mystery"); + if (box) { + box.boxstatus = 0; + box.frame = 0; + box.goaldummy.frame = 0; + boxCount = 0; + box.origin = boxOrigin; + //self = box; + + if (box.boxweapon) + remove(box.boxweapon); + + //mystery_box(); + //self = oldself; + } + + //reset quick revive + revive = find(world, classname, "perk_revive"); + if (revive) { + setmodel(revive, revive.model); + oldself.revivesoda = 0; + } + + //reset buyable ending + endgame = find(world, classname, "func_ending"); + if (endgame) { + endgame.activated = false; + } + + //reset teleporters + local entity tp; + tp = find(world, classname, "func_teleporter_entrance"); + + if (tp) { + tp.activated = false; + tp.isLinked = false; + tp.cooldown = false; + tp.waitLink = false; + tp.think = SUB_Null; + } + + local entity power; + power = find(world, classname, "power_switch"); + if(power) { + isPowerOn = false; + power.frame = 0; + } + + + + self = oldself; + self.downed = 0; + game_over = false; + rounds = 0; + self.score = 0; + self.points = 0; + self.secondaryweapon = 0; + InitRounds(); + self.isspec = false; + PutClientInServer(); +} \ No newline at end of file diff --git a/source/server/rounds.qc b/source/server/rounds.qc new file mode 100644 index 0000000..5b50d15 --- /dev/null +++ b/source/server/rounds.qc @@ -0,0 +1,340 @@ +/* + server/rounds.qc + + wave logic + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +float() spawn_a_dogA; + +void() Spawn_Enemy = +{ + // temporarily prevent spawning + if (nuke_powerup_spawndelay > time) + return; + + if (roundtype == 1) + { + if (spawn_a_zombieA()) + { + Current_Zombies = Current_Zombies + 1; + } + } + else if (roundtype == 2) + { + if (spawn_a_dogA()) + { + Current_Zombies = Current_Zombies + 1; + } + } +} + +float(float a, float b) qc_max = +{ + if (a < b) + return b; + return a; +} + +float() getZombieTotal = { + if (roundtype == 1) { + float count, multiplier, plrcnt; + count = 24; + plrcnt = player_count + 1; + + multiplier = qc_max(rounds/5, 1); + + if (rounds >= 10) + multiplier *= rounds * 0.15; + + if (plrcnt == 1) + count += rint((0.5 * 6) * multiplier); + else + count += rint((plrcnt * 6) * multiplier); + + if (rounds < 2) + count = floor(count * 0.25); + else if (rounds < 3) + count = floor(count * 0.30); + else if (rounds < 4) + count = floor(count * 0.50); + else if (rounds < 5) + count = floor(count * 0.70); + else if (rounds < 6) + count = floor(count * 0.90); + + return count; + } else { //dogs + // 2 waves + if (rounds <= 14) + return 6 * (player_count + 1); + + return 8 * (player_count + 1); + } + return 0; +} + +void(string s) playSoundAtPlayers = +{ + local entity p; + p = find(world,classname,"player"); + while(p) + { + sound(p,CHAN_AUTO,s,1,ATTN_NONE); + p = find(p,classname,"player"); + } +} + +void() updateDogRound = +{ + float r = random(); + + if (r < 0.33) { + dogRound = rounds + 5; + } else if (r < 0.66) { + dogRound = rounds + 6; + } else { + dogRound = rounds + 7; + } +} + +void() EndRound = +{ + rounds_change = 4; + SetUpdate(self, UT_ROUNDS_CHANGE, rounds_change, 0, 0); + if (gotdog && rounds == dogRound) { + playSoundAtPlayers("sounds/rounds/droundend.wav"); + dogWave = false; + } else { + playSoundAtPlayers("sounds/rounds/eround.wav"); + } + round_changetime = time + 5; + + // No Perks? No Problem & Abstinence Program + if (rounds >= 10) { + entity players; + players = find(world, classname, "player"); + + while(players != world) { + if (players.ach_tracker_npnp == 0) { + GiveAchievement(5, players); + } + if (players.ach_tracker_abst == 0) { + GiveAchievement(8, players); + } + players.ach_tracker_npnp = 0; + players = find(players, classname, "player"); + } + } +} + +void() PlayerSpawn; + +void() NewRound = +{ + entity who = find(world,classname,"spectator"); + while(who != world) + { + if(who.isspec) + { + self = who; + PlayerSpawn(); + break; + } + + } + + entity tempe; + round_changetime = 0; + spawn_time = time + 5; + sounds_playing = 0;//just in case it ever somehow glitches + if (delay_at_round > 0.08) + { + delay_at_round = delay_at_round*0.95; + if (delay_at_round < 0.08) + delay_at_round = 0.08; + } + if (rounds != 0) + { + if (gotdog && rounds == (dogRound - 1)) { + playSoundAtPlayers("sounds/rounds/droundstart.wav"); + dogWave = true; + } else { + playSoundAtPlayers("sounds/rounds/nround.wav"); + } + rounds_change = 6; + SetUpdate(self, UT_ROUNDS_CHANGE, rounds_change, 0, 0); + blink_return = time + 2; + } + + // if we just had a dog round, set the next + if (gotdog && rounds == dogRound) { + updateDogRound(); + } + + rounds = rounds + 1; + + NotifyNewRound(rounds); + tempe = find(world, classname, "player"); + while (tempe) + { + if(tempe.grenades & 1) + { + tempe.primary_grenades = tempe.primary_grenades + 2; + if (tempe.primary_grenades > 4) + tempe.primary_grenades = 4; + } + + if(tempe.grenades & 2) + { + tempe.secondary_grenades = tempe.secondary_grenades + 2; + if (tempe.secondary_grenades > 2) + tempe.secondary_grenades = 2; + } + tempe = find(tempe, classname, "player"); + } + + set_z_health(); + + maxreward = 50 * rounds; + if (maxreward > 500) + maxreward = 500; + + totalreward = 0; + Current_Zombies = 0; + spawn_delay = 0; + totalpowerups = 0; + powerup_activate = powerup_activate*1.14; + + if (rounds == dogRound && gotdog) { + roundtype = 2; + localcmd("fog 0.25\n"); + } else { + roundtype = 1; + + if (world.fog) { + #ifdef PSP + localcmd(strcat("fog ", world.fog)); + #else + #ifdef PC + localcmd(strcat("fog ", world_fog)); + #endif + #endif + } + } + + Remaining_Zombies = Total_Zombies = getZombieTotal(); + +#ifndef NX + switch(rounds) { + case 5: GiveAchievement(0); break; + case 10: GiveAchievement(1); break; + case 15: GiveAchievement(2); break; + default: break; + } +#endif // NX + + // Set up delay for zombie spawning + local float spawndelay; + + spawndelay = zombie_spawn_delay; + + if (spawndelay > 0.08) { + zombie_spawn_delay *= 0.95; + } else { + zombie_spawn_delay = 0.08; + } + + // 2 seconds by default + if (rounds == 1) + zombie_spawn_delay = 2; + + // Actually start the timer + zombie_spawn_timer = 2 + time; + + if (rounds >= 10 && ach_tracker_spin == 0) { + GiveAchievement(10); + } +} +void() Round_Core = +{ + if (game_over) + return; + + if (round_changetime <= (time + 2) && !rounds && round_changetime) { + rounds_change = 2; + } + + if (round_changetime <= (time + 2) && rounds && round_changetime) { + rounds_change = 5; + } + + if (blink_return && blink_return < time && rounds_change == 6) + { + blink_return = time + 3; + rounds_change = 7; + } + if (blink_return && blink_return < time && rounds_change == 7) + { + blink_return = 0; + rounds_change = 0; + } + if (round_changetime >= time) + return; + if (round_changetime) + { + roundtype = 1; + + NewRound(); + return; + } + if (Total_Zombies > Current_Zombies && spawn_time < time) + { + Spawn_Enemy(); + } + if (Remaining_Zombies < 1 && !Delay_Time) + { + Delay_Time = time + 2; + rounds_change = 3; + } + else if (Delay_Time && Delay_Time < time) + { + Delay_Time = 0; + EndRound(); + } + + SetUpdate(self, UT_ROUNDS_CHANGE, rounds_change, 0, 0); +} + +void() InitRounds = +{ + roundtype = 1; + delay_at_round = 2/0.95; + totalpowerups = 0; + powerup_activate = 2000/1.14; + spawnAllZombEnts(); + round_changetime = time + 3.5; + rounds_change = 1; + roundinit = 1; + SetUpdate(self, UT_ROUNDS_CHANGE, rounds_change, 0, 0); +} \ No newline at end of file diff --git a/source/server/weapons/frames_core.qc b/source/server/weapons/frames_core.qc new file mode 100644 index 0000000..93b6dd1 --- /dev/null +++ b/source/server/weapons/frames_core.qc @@ -0,0 +1,233 @@ +/* + server/weapons/frames_core.qc + + advanced viewmodel frame iteration + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() ContinueRun = +{ + float startframe; + float endframe; + string modelname; + startframe = GetFrame(self.weapon,SPRINT_START); + endframe = GetFrame(self.weapon,SPRINT_END); + if(!startframe && !endframe) + { + startframe = GetFrame(self.weapon,SPRINT_IN_END); + endframe = GetFrame(self.weapon,SPRINT_IN_END); + } + if (!self.downed) { + modelname = GetWeaponModel(self.weapon, 0); + } else + modelname = ""; + Set_W_Frame (startframe, endframe, 0, 0, SPRINT, ContinueRun, modelname, false, S_BOTH); +} + + +void () W2_Frame_Update = +{ + local void temp(optional float t); + +// note: call whenever weapon frames are called! + + if (self.anim_weapon2_time > time) + return; //don't call every frame, if it is the animations will play too fast + + #ifndef PC + if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) { + self.weapon2frame = self.weaponframe; + return; + } + #endif + + self.anim_weapon2_time = time + self.weapon2_animduration; + + if (self.weapon2frame != self.weapon2frame_end && !self.anim2_reversed) + { // continue an animation + if (self.anim2_reversed) { + self.weapon2frame = self.weapon2frame - 1; + } else { + self.weapon2frame = self.weapon2frame + 1; + } + + if (self.callfuncat2) + { + if (self.weapon2frame == self.callfuncat2) + { + if (self.animend2) + { + temp = self.animend2; + self.animend2 = SUB_Null2; + if (temp) + temp(S_LEFT); + } + } + } + PlayWeaponSound(self.weapon, self.weapon2_anim_type, FALSE, self.weapon2frame); + return; + } + else + { + self.weapon2frame = self.weapon2frame_end = self.weapon2frame = GetFrame(self.weapon,BASE_FRAME); + self.new_anim2_stop = FALSE; + self.weapon2_anim_type = 0; + self.weapon2_animduration = 0; + self.callfuncat2 = 0; + temp = self.animend2; + self.animend2 = SUB_Null; + if (temp) + temp(S_LEFT); + } +}; +void () W_Frame_Update = +{ + local void temp(optional float t); + + // note: call whenever weapon frames are called! + if (self.anim_weapon_time > time) + return; //don't call every frame, if it is the animations will play too fast + + W2_Frame_Update(); + + self.anim_weapon_time = time + self.weapon_animduration; + + + + if (self.weaponframe != self.weaponframe_end && !self.anim_reversed) + { // continue an animation + + if (self.anim_reversed) { + self.weaponframe = self.weaponframe - 1; + } else { + self.weaponframe = self.weaponframe + 1; + } + + if (self.weaponmodel == "models/weapons/kar/v_kar.mdl" && (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER)) + { + self.weapon2model = "models/weapons/kar/v_karscope.mdl"; + self.weapon2frame = self.weaponframe; + //self.weapon2skin = self.weaponskin; + } + else if (self.weaponmodel == "progs/VModels/v_knife.mdl" || self.weaponmodel == "models/machines/v_perk.mdl") { + self.weapon2model = ""; + UpdateV2model(self.weapon2model, 0); + } + + if (self.callfuncat) + { + if (self.weaponframe == self.callfuncat) + { + if (self.animend) + { + temp = self.animend; + self.animend = SUB_Null; + if (temp) + temp(S_RIGHT); + } + } + } + PlayWeaponSound(self.weapon, self.weapon_anim_type, FALSE, self.weaponframe); + return; + } + else + { + self.weaponframe_end = self.weaponframe = GetFrame(self.weapon,BASE_FRAME); + self.new_anim_stop = FALSE; + self.weapon_anim_type = 0; + self.weapon_animduration = 0; + self.callfuncat = 0; + temp = self.animend; + self.animend = SUB_Null; + if (temp) + temp(S_RIGHT); + } +}; + +void Set_W_Frame (float startframe, float endframe, float duration, float funccalledin, float animtype, void(optional float t) endanimfunc, string set_model, float dontstartnew, float side) = +{ + float math, reversed; + + if (startframe >= endframe) { + reversed = true; + } else { + reversed = false; + } + + math = 0; + if (duration) { + math = duration / (fabs(endframe - startframe) + 1); + } else { + math = 0.1; + } + + if (side == S_RIGHT || side == S_BOTH) { + self.weaponframe = startframe; + self.weaponframe_end = endframe; + self.animend = endanimfunc; + self.callfuncat = funccalledin; + self.weapon_anim_type = animtype; + self.new_anim_stop = dontstartnew; + self.weapon_animduration = math; + self.anim_reversed = reversed; + } + if (side == S_LEFT || side == S_BOTH) { + self.weapon2frame = startframe; + self.weapon2frame_end = endframe; + self.weapon2_anim_type = animtype; + self.new_anim2_stop = dontstartnew; + self.weapon2_animduration = math; + self.anim2_reversed = reversed; + + if (side != S_BOTH) { + self.animend2 = endanimfunc; + self.callfuncat2 = funccalledin; + } + } + + if ((startframe != endframe) && !(self.zoom == 2)) { // naievil -- latter used for checkhold + if (side == S_LEFT) { + self.weapon2model = set_model; + UpdateV2model(self.weapon2model, GetWepSkin(self.weapon)); + } else { + self.weaponmodel = set_model; + + if (set_model != "models/machines/v_perk.mdl") + UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); + else + UpdateVmodel(self.weaponmodel, self.weaponskin); + + + if (set_model == "progs/VModels/v_nade.mdl" || set_model == "progs/VModels/v_betty.mdl") { + self.weapon2model = ""; + UpdateV2model(self.weapon2model, 0); + } else if (self.weapon == W_KAR_SCOPE) { + self.weapon2model = "models/weapons/kar/v_karscope.mdl"; + UpdateV2model(self.weapon2model, 0); + } else if (IsDualWeapon(self.weapon)) { + self.weapon2model = GetLeftWeaponModel(self.weapon); + UpdateV2model(self.weapon2model, 0); + } + } + } +} \ No newline at end of file diff --git a/source/server/weapons/weapon_core.qc b/source/server/weapons/weapon_core.qc new file mode 100644 index 0000000..df10d6a --- /dev/null +++ b/source/server/weapons/weapon_core.qc @@ -0,0 +1,2526 @@ +/* + server/weapons/weapon_core.qc + + weapon logic + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +void() W_PutOut; +void() GrenadeExplode; + +void() ReturnWeaponModel = +{ + self.weaponmodel = GetWeaponModel(self.weapon, 0); + + if (IsDualWeapon(self.weapon)) { + self.weapon2model = GetLeftWeaponModel(self.weapon); + } else if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) { + self.weapon2model = "models/weapons/kar/v_karscope.mdl"; + UpdateV2model(self.weapon2model, 0); + } + + UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); + UpdateV2model(self.weapon2model, GetWepSkin(self.weapon)); +} + +.float scopetime; + +void() W_AimIn = +{ + if (self.weapon == W_TESLA || self.weapon == W_DG3) + return; + + if (IsDualWeapon(self.weapon) || + self.zoom || + self.reload_delay > time || + self.sprinting || + self.new_anim_stop || + self.weapon == W_BK) { + return; + } + + #ifndef PC + self.ADS_Offset = GetWeaponADSOfs_PSP(self.weapon); + #endif + + if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || + self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { + SetUpdate(self, UT_ZOOM2, 0, 0, 0); + #ifdef NX + self.zoom = 1; + #else + self.zoom = 2; + #endif + self.scopetime = time + 0.3; + } else { + self.zoom = 1; + } + + if (!self.downed) + playaim(); + +} + +void() W_AimOut = +{ + if (!self.zoom || + self.new_anim_stop || + self.sprinting) { + return; + } + + if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || + self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { + ReturnWeaponModel(); + } + + #ifndef PC + self.zoom = 0; + #endif + + if (!self.downed) + playout(); +} + +void() W_SprintStop = +{ + if (self.sprinting) + { + self.sprint_stop_time = time; + self.sprint_duration = self.sprint_timer; + } + + float startframe = GetFrame(self.weapon,SPRINT_OUT_START); + float endframe = GetFrame(self.weapon,SPRINT_OUT_END); + string modelname = GetWeaponModel(self.weapon, 0); + + if (self.isBuying || self.damagetimer > time || !self.sprinting) + return; + + //TEMPORARY + #ifndef PC + self.zoom = 0; + #endif + + Set_W_Frame (startframe, endframe, 0, 0, SPRINT, SUB_Null, modelname, false, S_BOTH); // BUG IS HERE + + self.sprinting = 0; + self.into_sprint = 0; + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; + + if (self.velocity) + playwalk(); +} + + +void W_SprintStart () { + self.sprint_start_time = time; + + if (self.sprint_rest_time > sprint_max_time) + self.sprint_duration = 0.0; + else + self.sprint_duration -= self.sprint_rest_time; + +#ifdef NX + if (!self.sprintflag) { + return; + } +#endif + + if (self.fire_delay > time || + self.fire_delay2 > time || + self.new_anim_stop || + self.new_anim2_stop || + self.isBuying || + self.downed || + !(self.flags & FL_ONGROUND) || + self.sprint_delay > time) { + return; + } + + float startframe = GetFrame(self.weapon,SPRINT_IN_START); + float endframe = GetFrame(self.weapon,SPRINT_IN_END); + string modelname = GetWeaponModel(self.weapon, 0); + + #ifndef PC + self.zoom = 3; + #endif + + if (startframe || endframe) { + Set_W_Frame (startframe, endframe, 0, 0, SPRINT, ContinueRun, modelname, false, S_BOTH); + } + + self.sprint_delay = time + 1; + self.sprinting = true; + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; +} + +void() W_Switch = +{ + if (self.secondaryweapon && self.secondaryweapon != 0 && !self.new_anim_stop && !other.button7) + { + float tempf,tempf1,tempf2,tempf3,tempf4; + float startframe; + float endframe; + string modelname; + + // un-zoom camera + self.zoom = false; + + // fix fire rate exploit + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = true; + + // just do normal weapon swapping if we don't have mule.. + if (!(self.perks & P_MULE) || !self.thirdweapon) { + tempf = self.currentammo; + self.currentammo = self.secondaryammo; + self.secondaryammo = tempf; + + tempf1 = self.currentmag; + self.currentmag = self.secondarymag; + self.secondarymag = tempf1; + + tempf1 = self.currentmag2; + self.currentmag2 = self.secondarymag2; + self.secondarymag2 = tempf1; + + tempf2 = self.weapon; + self.weapon = self.secondaryweapon; + self.secondaryweapon = tempf2; + + tempf3 = self.weaponskin; + self.weaponskin = self.secondaryweaponskin; + self.secondaryweaponskin = tempf3; + } else { + // store primary weapon + tempf = self.weapon; + tempf1 = self.currentmag; + tempf2 = self.currentmag2; + tempf3 = self.currentammo; + tempf4 = self.weaponskin; + + // set primary weapon to third weapon + self.weapon = self.thirdweapon; + self.currentmag = self.thirdmag; + self.currentmag2 = self.thirdmag2; + self.currentammo = self.thirdammo; + self.weaponskin = self.thirdweaponskin; + + // set third weapon to secondary weapon + self.thirdweapon = self.secondaryweapon; + self.thirdmag = self.secondarymag; + self.thirdmag2 = self.secondarymag2; + self.thirdammo = self.secondaryammo; + self.thirdweaponskin = self.secondaryweaponskin; + + // set secondary weapon to primary weapon + self.secondaryweapon = tempf; + self.secondarymag = tempf1; + self.secondarymag2 = tempf2; + self.secondaryammo = tempf3; + self.secondaryweaponskin = tempf4; + } + + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(self.weapon, 0); + + SwitchWeapon(self.weapon); + + if (IsDualWeapon(self.weapon)) { + self.weapon2model = GetLeftWeaponModel(self.weapon); + } else if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) + self.weapon2model = "models/weapons/kar/v_karscope.mdl"; + else + self.weapon2model = ""; + + UpdateV2model(self.weapon2model, 0); + + // TEMP + if (self.weapon == W_M2 || self.weapon == W_FIW) + UpdateVmodel(modelname, GetWepSkin(self.weapon)); + + + //UpdateWeapons(1); + //if (self.weaponslot1.dualwield) + // Set_W2_Frame (self.weaponslot1.TakeWeaponStart, self.weaponslot1.TakeWeaponEnd, 0, 0, 0, SUB_Null, self.weaponslot1.v_model2, false);//FIXME + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH);//FIXME + + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; + + + #ifndef PC + self.Weapon_Name = GetWeaponName(self.weapon); + self.Flash_Offset = GetWeaponFlash_Offset(self.weapon); + self.Flash_Size = GetWeaponFlash_Size(self.weapon); + #endif + } +} + +void() W_PutOut = +{ + if (self.downed) { + return; + } + + W_AimOut(); + self.weaponnum = !self.weaponnum; + + if (self.secondaryweapon && !self.new_anim_stop) + { + float startframe; + float endframe; + string modelname; + startframe = GetFrame(self.weapon,PUT_OUT_START); + endframe = GetFrame(self.weapon,PUT_OUT_END); + modelname = GetWeaponModel(self.weapon, 0); + + Set_W_Frame (startframe, endframe, (endframe - startframe)/10, 0, SWITCHWEP, W_Switch, modelname, false, S_BOTH);//FIXME + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; + } +} + +void() W_TakeOut = +{ + W_AimOut(); + + float startframe; + float endframe; + string modelname; + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(self.weapon, 0); + + self.isBuying = false; + + Set_W_Frame (startframe, endframe, (endframe - startframe)/10, 0, SWITCHWEP, SUB_Null, modelname, false, S_BOTH);//FIXME + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = 0; +} + +float(entity who) hasWeapon = +{ + if (other.weapon || other.secondaryweapon || other.thirdweapon) + return true; + + return false; +} + + + +//RELOAD + +void(float side) W_Give_Ammo = +{ + float ammo_shot, max_mag, loadammo; + + max_mag = getWeaponMag(self.weapon); + + if (side == S_LEFT) { + ammo_shot = max_mag - self.currentmag2; + } else { + ammo_shot = max_mag - self.currentmag; + } + if (ammo_shot < self.currentammo) + { + self.currentammo = self.currentammo - ammo_shot; + + loadammo = max_mag; + } + else + { + loadammo = self.currentmag + self.currentammo; + self.currentammo = 0; + } + + if (side == S_LEFT) { + self.currentmag2 = loadammo; + } else { + self.currentmag = loadammo; + } +}; + +void () W_LoadAmmo; +void() ContinueReload = //Special reloads +{ + if (self.new_anim_stop) + return; + + if (self.weapon == W_GUT && self.currentmag == 10) + return; + + float delay = 0; + string modelname = GetWeaponModel(self.weapon, 0); + float startframe = 0; + float endframe = 0; + void(optional float t) endanimfunc = SUB_Null; + + if (self.currentmag >= getWeaponMag(self.weapon) || !self.currentammo || self.reloadinterupted) { + if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) + { + delay = 1; + startframe = 24; + endframe = 28; + } else if (self.weapon == W_TRENCH || self.weapon == W_GUT) { + delay = 0.5; + startframe = 24; + endframe = 26; + endanimfunc = W_LoadAmmo; + } + self.reloadinterupted = FALSE; + } else if (self.currentmag < getWeaponMag(self.weapon)) { + if (self.weapon == W_BROWNING || self.weapon == W_ACCELERATOR) { + delay = 4; + startframe = 45; + endframe = 77; + endanimfunc = W_Give_Ammo; + } else if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) { + self.currentmag++; + self.currentammo = self.currentammo - 1; + delay = 0.8; + startframe = 19; + endframe = 24; + endanimfunc = ContinueReload; + } else if (self.weapon == W_TRENCH || self.weapon == W_GUT) { + if (self.weapon == W_GUT && self.currentammo >= 2 && self.currentmag < 9) { + self.currentmag = self.currentmag + 2; + self.currentammo = self.currentammo - 2; + } else { + self.currentmag++; + self.currentammo = self.currentammo - 1; + } + delay = 0.5; + startframe = 18; + endframe = 23; + endanimfunc = ContinueReload; + } + } + + if (delay) { + if (self.perks & P_SPEED) { + delay *= 0.5; + } + self.reload_delay = time + delay; + Set_W_Frame (startframe, endframe, delay, 0, RELOAD, endanimfunc, modelname, false, S_RIGHT); + } +} + +void(float side) W_Reload = +{ + if (self.weapon == W_M2 || self.weapon == W_FIW) + return; + + if (side == S_BOTH) { + W_Reload(S_RIGHT); + if (IsDualWeapon(self.weapon)) { + W_Reload(S_LEFT); + } + return; + } + if (side == S_RIGHT && + (self.reload_delay > time || + self.new_anim_stop || + !self.currentammo || + self.currentmag >= getWeaponMag(self.weapon))){ + return; + } + if (side == S_LEFT && + (self.reload_delay2 > time || + self.new_anim2_stop || + !self.currentammo || + self.currentmag2 >= getWeaponMag(self.weapon))) { + return; + } + + //TEMPORARY + #ifdef PC + self.viewzoom = 1; + #endif + self.zoom = false; + + W_AimOut(); + W_SprintStop(); + + float endframe, startframe, reloadcancelframe, delay; + string modelname = ""; + + if (side == S_RIGHT) { + modelname = GetWeaponModel(self.weapon, 0); + } else { + if (IsDualWeapon(self.weapon)) { + modelname = GetLeftWeaponModel(self.weapon); + } + } + + if (self.currentammo) + { + playreload(); + + startframe = GetFrame(self.weapon,RELOAD_START); + endframe = GetFrame(self.weapon,RELOAD_END); + reloadcancelframe = GetFrame(self.weapon,RELOAD_CANCEL); + delay = getWeaponDelay(self.weapon,RELOAD); + void(optional float t) endanimfunc = SUB_Null; + + if (self.weapon == W_BROWNING || self.weapon == W_ACCELERATOR) { + if (!self.currentmag) { + startframe = 26; + endframe = 77; + reloadcancelframe = 45; + delay = 6; + endanimfunc = W_Give_Ammo; + } else if (self.currentmag > 0) { + startframe = 4; + endframe = 25; + reloadcancelframe = 0; + delay = 3.5; + endanimfunc = ContinueReload; + } + } else if (self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER ){ + startframe = 14; + endframe = 18; + reloadcancelframe = 0; + delay = 0.8; + endanimfunc = ContinueReload; + } else if (self.weapon == W_M1 || self.weapon == W_M1000) { + if (self.currentmag > 0) { + startframe = 4; + endframe = 44; + reloadcancelframe = 25; + delay = 3.9; + endanimfunc = W_Give_Ammo; + } else { + startframe = 8; + endframe = 44; + reloadcancelframe = 25; + delay = 3.6; + endanimfunc = W_Give_Ammo; + } + } else if (self.weapon == W_TRENCH || self.weapon == W_GUT) { + if (self.currentmag == 0) { + self.NeedLoad = true; + } + startframe = 14; + endframe = 17; + reloadcancelframe = 0; + delay = 0.8; + endanimfunc = ContinueReload; + } else { + endanimfunc = W_Give_Ammo; + } + + if (self.perks & P_SPEED) + delay = delay*0.5; + + if (side == S_RIGHT) { + self.reload_delay = delay + time; + } else { + self.reload_delay2 = delay + time; + } + + Set_W_Frame (startframe, endframe, delay, reloadcancelframe, RELOAD, endanimfunc, modelname, false, side); + } + + if (self.weapon != W_TRENCH) { + self.NeedLoad = false; + } +}; + +void () W_LoadAmmoDone = +{ + self.NeedLoad = false; +} +void () W_LoadAmmo = +{ + if (!self.NeedLoad) + return; + if (!self.currentmag) + { + W_Reload(S_BOTH); + return; + } + + string modelname; + float endframe, startframe, reloadcancelframe, delay; + + modelname = GetWeaponModel(self.weapon, 0); + startframe = GetFrame(self.weapon,RELOAD_START); + endframe = GetFrame(self.weapon,RELOAD_END); + reloadcancelframe = GetFrame(self.weapon,RELOAD_CANCEL); + delay = getWeaponDelay(self.weapon,RELOAD); + void(optional float t) endanimfunc = SUB_Null; + + if (self.weapon == W_TRENCH || self.weapon == W_GUT) + { + startframe = 4; + endframe = 14; + reloadcancelframe = 12; + delay = 1; + endanimfunc = W_LoadAmmoDone; + } + else if (self.weapon == W_KAR || self.weapon == W_ARMAGEDDON || self.weapon == W_KAR_SCOPE || self.weapon == W_HEADCRACKER) + { + startframe = 4; + endframe = 13; + reloadcancelframe = 9; + delay = 0.9; + endanimfunc = W_LoadAmmoDone; + } + + if (delay) { + + Set_W_Frame (startframe, endframe, delay, reloadcancelframe, FIRE, W_LoadAmmoDone, modelname, false, S_RIGHT); + self.fire_delay = delay + time; + } +} + +void () CheckReload = +{ + if (!self.currentmag2 && IsDualWeapon(self.weapon)) { + W_Reload(S_LEFT); + } + if (!self.currentmag && self.currentammo) { + W_Reload(S_RIGHT); + } +} + +/* +================ +SpawnBlood +================ +*/ +void(vector org, vector vel, float damage) SpawnBlood = +{ + local entity f; + + #ifdef PC + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_BLOOD); + WriteCoord(MSG_MULTICAST, org_x); + WriteCoord(MSG_MULTICAST, org_y); + WriteCoord(MSG_MULTICAST, org_z); + msg_entity = self; + multicast('0 0 0', MULTICAST_ONE); + #else + particle (org, vel*0.1, 73, damage*2); + #endif + + f = find (world, classname, "player"); + f.hitcount = f.hitcount + 1; +}; + +void(vector where) spawn_gibs = { + #ifndef PC + particle(where, where*0.1, 225, 1); + #else + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_BLOOD); + WriteCoord(MSG_MULTICAST, where_x); + WriteCoord(MSG_MULTICAST, where_y); + WriteCoord(MSG_MULTICAST, where_z); + msg_entity = self; + multicast('0 0 0', MULTICAST_ONE); + #endif +} + + +void Parse_Damage () = // DO NOT TOUCH +{ + entity ent; + float total_dmg; + + entity body_ent; + float head_hit; + + //warn + head_hit = 0; + + ent = findfloat (world, washit, 1); + + while (ent) { + if (ent.classname != "radio" && ent.classname != "explosive_barrel") { + + if (ent.classname == "ai_zombie_head") + head_hit = 1; + + if (ent.classname != "ai_zombie" && ent.classname != "ai_dog") //limb + body_ent = ent.owner; + else + body_ent = ent; + + total_dmg = body_ent.hitamount; + + if (body_ent.head.washit) { + total_dmg += body_ent.head.hitamount; + body_ent.head.health -= body_ent.head.hitamount; + head_hit = 1; + + // Head = Gone! + if (body_ent.head.health <= 0) { + // Head has already been blown off, bro + if (!body_ent.head.deadflag) + return; + + vector where; + + // If our weapon is capable, blow the head off + if (getWeaponDamage(self.weapon) >= 100) { + makevectors(body_ent.head.owner.angles); + where = body_ent.head.owner.origin + (body_ent.head.view_ofs_x * v_right) + (body_ent.head.view_ofs_y * v_forward) + (body_ent.head.view_ofs_z * v_up); + spawn_gibs(where); + + // Should the Zombie try and walk on? + if (random() <= 0.01) { + body_ent.bleedingtime = time + 2; + } else { + // Nah, he dead. + total_dmg *= 5000; + } + + body_ent.head.deadflag = false; + body_ent.head.solid = SOLID_NOT; + setmodel(body_ent.head,""); + body_ent.head.frame = 0; + + body_ent.usedent = self; + body_ent.bleedingtime = time + 2; + } + } + + body_ent.head.washit = 0; + body_ent.head.hitamount = 0; + } + if (body_ent.larm.washit) { + total_dmg = total_dmg + body_ent.larm.hitamount; + + body_ent.larm.health = body_ent.larm.health - body_ent.larm.hitamount; + if (body_ent.larm.health <= 0 && self.weapon != W_COLT && body_ent.larm.owner.rarm.deadflag && body_ent.larm.deadflag) + { + + makevectors(body_ent.larm.owner.angles); + where = body_ent.larm.owner.origin + (body_ent.larm.view_ofs_x * v_right) + (body_ent.larm.view_ofs_y * v_forward) + (body_ent.larm.view_ofs_z * v_up); + spawn_gibs(where); + + body_ent.larm.deadflag = false; + body_ent.larm.solid = SOLID_NOT; + setmodel(body_ent.larm,""); + body_ent.larm.frame = 0; + //updateLimb (body_ent.larm.owner, 1, world); + } + + body_ent.larm.washit = 0; + body_ent.larm.hitamount = 0; + } + if (body_ent.rarm.washit) { + total_dmg = total_dmg + body_ent.rarm.hitamount; + + body_ent.rarm.health = body_ent.rarm.health - body_ent.rarm.hitamount; + if (body_ent.rarm.health <= 0 && self.weapon != W_COLT && body_ent.rarm.owner.larm.deadflag && body_ent.rarm.deadflag) { + + makevectors(body_ent.rarm.owner.angles); + where = body_ent.rarm.owner.origin + (body_ent.rarm.view_ofs_x * v_right) + (body_ent.rarm.view_ofs_y * v_forward) + (body_ent.rarm.view_ofs_z * v_up); + spawn_gibs(where); + + body_ent.rarm.deadflag = false; + body_ent.rarm.solid = SOLID_NOT; + setmodel(body_ent.rarm,""); + body_ent.rarm.frame = 0; + //updateLimb (body_ent.rarm.owner, 2, world); + } + + body_ent.rarm.washit = 0; + body_ent.rarm.hitamount = 0; + } + + if (instakill_finished) { + if(body_ent.head.deadflag) { + body_ent.head.deadflag = false; + body_ent.head.solid = SOLID_NOT; + setmodel(body_ent.head,""); + body_ent.head.frame = 0; + } + } + + if (head_hit) + DamageHandler(body_ent,self, total_dmg, S_HEADSHOT); + else + DamageHandler(body_ent,self, total_dmg, S_NORMAL); + + #ifndef PC + body_ent.washit = 0; + body_ent.hitamount = 0; + #else + ent.washit = 0; + ent.hitamount = 0; + #endif + + } + ent = findfloat (ent, washit, 1); + } +} + +void() LungeKnifeHit = +{ + vector source,org; + float f_damage; + entity hit_ent; + + makevectors (self.v_angle); + source = self.origin + self.view_ofs; + + if (self.weapon == W_BK) + traceline (source, source + v_forward*80*1.2, 0, self); // naievil -- added some extra length + else + traceline (source, source + v_forward*64*1.2, 0, self); // naievil -- ^^ + + org = trace_endpos - v_forward*3; // naievil -- changed to factor of 3 from 4 + + + if (trace_ent.takedamage) + { + if(trace_ent.classname == "item_radio" || trace_ent.classname == "teddy_spawn") + { + entity oldself; + oldself = self; + self = trace_ent; + self.th_die(); + self = oldself; + return; + } + sound (self, CHAN_WEAPON, "sounds/weapons/knife/knife_hitbod.wav", 1, ATTN_NORM); + + if (self.bowie && self.weapon == W_BK) + f_damage = 1200; + else if (self.bowie) + f_damage = 1000; + else if (self.weapon == W_BK) + f_damage = 500; + else + f_damage = 150; + + if (trace_ent.classname == "ai_zombie_head") + { + hit_ent = trace_ent.owner; + f_damage = f_damage*1.5; + } + else if (trace_ent.classname == "ai_zombie_larm") + { + hit_ent = trace_ent.owner; + } + else if (trace_ent.classname == "ai_zombie_rarm") + { + hit_ent = trace_ent.owner; + } + else + { + hit_ent = trace_ent; + } + + if (hit_ent.takedamage) + { + org = source + (v_forward * 20); + SpawnBlood (org, org, 50); + //hit = 1; + } + + DamageHandler (hit_ent, self, f_damage, S_KNIFE); + } +} + +void(float damage, vector dir, vector org, vector plane, entity hit_ent, float side) TraceAttack = +{ + vector vel; + float f_damage = 0; + + vel = normalize(dir); + vel = vel + 2*plane; + vel = vel * 200; + + if (hit_ent.takedamage) { + if (trace_fraction >= 1) { + return; + } + if(hit_ent.classname == "item_radio" || hit_ent.classname == "teddy_spawn") + { + entity oldself; + oldself = self; + self = hit_ent; + self.th_die(); + self = oldself; + return; + } else if (hit_ent.classname == "explosive_barrel") { + hit_ent.enemy = self; + oldself = self; + self = hit_ent; + self.health = self.health - damage; + + barrel_hit (); + + self = oldself; + return; + } else if (hit_ent.classname == "door" || hit_ent.classname == "door_nzp") { + local entity old; + old = self; + self = hit_ent; + self.health -= damage; + + if (self.health < 0) + door_use(); + self = old; + return; + } + + SpawnBlood (org, vel, damage*10); + + switch(hit_ent.classname) { + case "ai_zombie_head": + f_damage = damage*getWeaponMultiplier(self.weapon, HEAD_X); + break; + case "ai_zombie_larm": + case "ai_zombie_rarm": + f_damage = damage*getWeaponMultiplier(self.weapon, LIMBS_X); + break; + case "ai_zombie": + if (trace_endpos_z < hit_ent.origin_z) + f_damage = damage*getWeaponMultiplier(self.weapon, LOWER_TORSO_X); + else + f_damage = damage*getWeaponMultiplier(self.weapon, UPPER_TORSO_X); + break; + default: + f_damage = damage; + break; + } + + hit_ent.washit = 1; + hit_ent.hitamount = hit_ent.hitamount + f_damage; + + SetUpdate(self, UT_HM, 0, 0, 0); + + #ifndef PC + msg_entity = self; + WriteByte(MSG_ONE, SVC_HITMARK); + #endif + } else { + + #ifndef PC + 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); + #endif + } + + #ifdef PC + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_PISTOLFIRE); + WriteEntity(MSG_MULTICAST, self); + WriteFloat(MSG_MULTICAST, side); + WriteCoord(MSG_MULTICAST, org_x); + WriteCoord(MSG_MULTICAST, org_y); + WriteCoord(MSG_MULTICAST, org_z); + WriteCoord(MSG_MULTICAST, plane_x); + WriteCoord(MSG_MULTICAST, plane_y); + WriteCoord(MSG_MULTICAST, plane_z); + WriteEntity(MSG_MULTICAST, hit_ent); + multicast(trace_endpos, MULTICAST_PHS); + #endif + +}; + +void (float shotcount, float sprd, float Damage, float side) FireTrace = +{ + float count, r, ru, penetration_times, do_break; + vector dir, src, trace_start_org; + entity hitent; + + count = 0; + + makevectors(self.v_angle); + + src = self.origin + self.view_ofs; + + #ifndef PC + CL_SendWeaponFire(); + #endif + + while (count < shotcount) + { + dir = aim (self,0); + r = random() * sprd*10; + if (random() < 0.5) + r = r*-1; + ru = random() * sprd*10; + if (random() < 0.5) + ru = ru*-1; + + dir = dir*2048; + dir = dir + v_right*r + v_up*ru; + trace_start_org = src; + trace_ent = self; + do_break = 0; + penetration_times = 0; + hitent = world; + while (!do_break && random() < getWeaponPenetration(self.weapon, penetration_times)) + { + while (1) + { + traceline (trace_start_org, trace_start_org + dir, MOVE_HITMODEL, trace_ent); + + if (hitent == world) + break; + /*if (hitent.larm == trace_ent || hitent.rarm == trace_ent || hitent.head == trace_ent) + { + hitent = trace_ent; + trace_start_org = trace_endpos + normalize(dir)*10; + //break; + }*/ + else + break; + } + + if (trace_fraction != 1.0) + TraceAttack (Damage, dir, trace_endpos, trace_plane_normal, trace_ent, side); + trace_start_org = trace_endpos; + penetration_times++; + if (!trace_ent.takedamage) + do_break = 1; + + if (trace_ent.classname == "ai_zombie_head" || trace_ent.classname == "ai_zombie_larm" || trace_ent.classname == "ai_zombie_rarm") + hitent = trace_ent.owner; + else + hitent = trace_ent; + + } + hitent = world; + + count++; + } + if (self.weapon != W_TESLA) + Parse_Damage(); + +} + +void(float side) W_FireGrenade = +{ + entity nade = spawn(); + nade.owner = self; + nade.movetype = MOVETYPE_BOUNCE; + nade.solid = SOLID_BBOX; + nade.classname = "explosiveNade"; + + makevectors (self.v_angle); + + nade.velocity = v_forward*1000; + nade.avelocity = '50 -50 50'; + + centerprint(self, vtos(nade.angles)); + + nade.touch = GrenadeExplode; + setsize(nade, '0 0 0', '0 0 0'); + nade.origin = self.origin + self.view_ofs; + + setorigin(nade, nade.origin); +} + +// +// RAY GUN - modified from patch <3 +// + +void() RayBulletExplode = +{ + DamgageExplode (self, self.owner, 1800, 1800, 64);//we want A SMALL radius of explosion + + // MOTO - FTE explosions are.. large, let's flash instead. + + #ifdef PC + te_smallflash(self.origin); + #else + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + #ifdef PSP + if (self.effects & EF_RAYGREEN) + WriteByte (MSG_BROADCAST, TE_RAYSPLASHGREEN); + else + WriteByte (MSG_BROADCAST, TE_RAYSPLASHRED); + #else + WriteByte (MSG_BROADCAST, TE_EXPLOSION); + #endif + WriteCoord (MSG_BROADCAST, self.origin_x); + WriteCoord (MSG_BROADCAST, self.origin_y); + WriteCoord (MSG_BROADCAST, self.origin_z); + #endif + + SUB_Remove (); +}; + +void() velocity_increase_ray = +{ + if (!other.solid || other.solid == SOLID_TRIGGER) + if (other != world) + return; + + if (other == self.owner) + return; + + if (other.solid == SOLID_NOT) { + RayBulletExplode(); + } + + self.velocity = self.velocity*1.5; + + RayBulletExplode(); +}; + +void() W_FireRay = +{ + local entity porter; + + porter = spawn(); + porter.owner = self; + porter.movetype = MOVETYPE_FLY; + porter.solid = SOLID_BBOX; + + makevectors(self.v_angle); + + porter.velocity = v_forward*2000; + porter.avelocity = '0 0 0'; + + porter.angles = vectoangles(porter.velocity); + porter.angles_z += (porter.angles_z + 180 < 360)? 180 : -180; + porter.angles = vectoangles(v_forward); + + porter.v_angle = '0 0 200'; + + porter.touch = velocity_increase_ray; + setmodel(porter, "models/misc/raybeam.mdl"); + setsize(porter, '0 0 0', '0 0 0'); + + + if (self.weapon == W_PORTER) { + #ifdef PSP + porter.effects = EF_RAYRED; + #else + porter.effects = EF_RED; + #endif + } else { + #ifdef PSP + porter.effects = EF_RAYGREEN; + #else + porter.effects = EF_GREEN; + #endif + } + + + porter.origin = self.origin + self.view_ofs; + porter.origin += v_forward * 0; + + setorigin(porter, porter.origin); + + self.animend = ReturnWeaponModel; + self.callfuncat = 0; +} + +// Remove zombie, add money, request clone spawn. +void(entity hit_ent) LightningHit = +{ + local entity tempe; + + // 50 points for waffe kills + addmoney(self, 50, true); + + // set lib models to null + // (if we remove them RelinkZombies will cry) + if (hit_ent.head) + setmodel(hit_ent.head, ""); + if (hit_ent.larm) + setmodel(hit_ent.larm, ""); + if (hit_ent.rarm) + setmodel(hit_ent.rarm, ""); + + // window hop fix + if (self.state == 1 || self.hop_step != 0) { + self.state = self.hop_step = 0; + } + + // change classname so it can't be re-targted + hit_ent.classname = "wunder"; + + // own it! + hit_ent.owner = self; + + // we're the start of a chain + hit_ent.teslacount = 1; + + // play our wunder-ful anim + tempe = self; + self = hit_ent; + self.th_diewunder(); + self = tempe; + + // kill + self.kills += 1; +}; + +// Initial Zombie impact, creds to Naievil/UP +void(vector p1, vector p2, entity from, float damage) LightningDamage = +{ + local entity e1, e2; + local vector f; + entity hit_ent; + + f = p2 - p1; + normalize (f); + f_x = 0 - f_y; + f_y = f_x; + f_z = 0; + f = f*16; + + e1 = e2 = world; + + //traceline (p1, p2, false, self); + + hit_ent = trace_ent; + + if (hit_ent == world) + return; + + if (hit_ent.classname == "ai_zombie_head" || hit_ent.classname == "ai_zombie_larm" || hit_ent.classname == "ai_zombie_rarm") + hit_ent = hit_ent.owner; + + if (trace_ent.takedamage) + { + LightningHit (hit_ent); + } + e1 = trace_ent; + + traceline (p1 + f, p2 + f, FALSE, self); + if (trace_ent != e1 && trace_ent.takedamage) + { + LightningHit (hit_ent); + } + e2 = trace_ent; + + + traceline (p1 - f, p2 - f, FALSE, self); + if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage) + { + LightningHit (hit_ent); + } +}; + +// Fire lightning out of the gun +void() W_FireTesla = +{ + local vector source; + + makevectors (self.v_angle); + source = self.origin + self.view_ofs; + + FireTrace(1, 0, 0, 0); + + #ifdef PC + te_lightning2(self, source, trace_endpos); + #endif + #ifdef PSP + WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte (MSG_BROADCAST, TE_LIGHTNING2); + WriteEntity (MSG_BROADCAST, self); + WriteCoord (MSG_BROADCAST, source_x); + WriteCoord (MSG_BROADCAST, source_y); + WriteCoord (MSG_BROADCAST, source_z); + WriteCoord (MSG_BROADCAST, trace_endpos_x); + WriteCoord (MSG_BROADCAST, trace_endpos_y); + WriteCoord (MSG_BROADCAST, trace_endpos_z); + #endif + + LightningDamage (self.origin, trace_endpos + v_forward*4, self, 30); +} + +void() Flame_LingerThink = +{ + #ifdef PC + te_flamejet(self.origin, v_up*8, 5); + #else + particle (self.origin, v_up*8, 0, 0); + #endif + + if (self.ltime < time) + remove(self); + + self.nextthink = time + 0.1; +} + +// spawn linger effect/entity +void(vector org, entity whodunit) Flame_Linger = +{ + local entity linger = spawn(); + + setorigin(linger, org); + + linger.owner = whodunit.owner; + linger.think = Flame_LingerThink; + linger.nextthink = time + 0.1; + linger.ltime = time + 5; +} + +void() Flame_Touch = +{ + if (other == world) { + Flame_Linger(self.origin, self); + remove(self); + } else { + if (other.onfire || other.owner.onfire) + return; + if (other.classname == "ai_zombie_head" || other.classname == "ai_zombie_larm" + || other.classname == "ai_zombie_rarm") { + other.owner.onfire = true; + other.owner.ltime = time + 2; + addmoney(self.owner, 10, true); + } else if (other.classname == "ai_zombie" || other.classname == "ai_dog") { + other.onfire = true; + other.firer = self.owner; + other.ltime = time + 2; + addmoney(self.owner, 10, true); + } + } +} + +// fallen flame touches something +void() Flame_DropCatch = +{ + if (other == world) { + Flame_Linger(self.origin + '0 0 5', self); + remove(self); + } +} + +// drop didn't make it to the ground in time to spread +void() Flame_DropDissapate = +{ + remove(self); +} + +// rare chance some falls +void() Flame_Drop = +{ + local entity drop = spawn(); + + drop.owner = self.owner; + + setmodel(drop, "models/sprites/flame.spr"); + setsize(drop, '0 0 0', '0 0 0'); + setorigin(drop, self.origin); + + drop.touch = Flame_DropCatch; + drop.think = Flame_DropDissapate; + drop.nextthink = time + 0.8; + drop.solid = SOLID_TRIGGER; + drop.movetype = MOVETYPE_BOUNCE; + + drop.velocity = v_up*-55; +} + +// increase frames, remove/drop +void() Flame_Buildup = +{ + self.frame++; + + if (self.frame > 11) { + if (random() > 0.75) + Flame_Drop(); + remove(self); + } + + self.nextthink = time + 0.05; +} + +void() W_FireM2 = +{ + + local entity flamespr; + local vector rand_angle; + + flamespr = spawn(); + flamespr.owner = self; + flamespr.movetype = MOVETYPE_FLYMISSILE; + flamespr.solid = SOLID_TRIGGER; + flamespr.classname = "flamethrower_sprite"; + flamespr.state = 0; + flamespr.finaldest = self.v_angle; + + // calc some randomness + rand_angle = self.v_angle; + rand_angle_x += random()*10; + rand_angle_y += random()*10; + + makevectors(rand_angle); + flamespr.velocity = aim(self, 1000); + flamespr.velocity *= 600; + + flamespr.owner = self; + + flamespr.think = Flame_Buildup; + flamespr.nextthink = time + 0.01; + flamespr.touch = Flame_Touch; + + setmodel(flamespr, "models/sprites/flame.spr"); + setorigin(flamespr, self.origin + self.view_ofs - '0 0 10'); + setsize(flamespr, '0 0 0', '0 0 0'); +} + +void(float side) W_Fire = +{ + if (self.weapon == W_M2 || self.weapon == W_FIW && self.cooldown) + return; + + //First check that we can actualy fire + if (side == S_RIGHT && + (time < self.fire_delay || + self.new_anim_stop || + self.reload_delay > time || (!self.currentammo && !self.currentmag))) { + return; + } + + if (side == S_LEFT && + (time < self.fire_delay2 || + self.new_anim2_stop || + self.reload_delay2 > time)) { + return; + } + + if (self.sprinting) { + W_SprintStop(); + return; + } + + float startframe; + float endframe; + float firetype; + float damage; + float shotcount; + string modelname = ""; + string soundname; + float spread; + float sprdstance = 0; + float delay; + + //Update the basic vectors + makevectors(self.v_angle); + + //make sure magazine is loading + if (!self.currentmag && side == S_RIGHT) + { + W_Reload(S_RIGHT); + return; + } else if (!self.currentmag2 && side == S_LEFT) + { + W_Reload(S_LEFT); + return; + } + + //Dont fire if the gun has to cycle + if (self.NeedLoad && (self.weapon == W_TRENCH || self.weapon == W_GUT || self.weapon == W_KAR_SCOPE || self.weapon == W_KAR || self.weapon == W_ARMAGEDDON || self.weapon == W_HEADCRACKER)) + { + W_LoadAmmo(); + return; + } + + //get some basic info + damage = getWeaponDamage(self.weapon); + firetype = GetFiretype (self.weapon); + if (side == S_RIGHT) { + modelname = GetWeaponModel(self.weapon, 0); + } else { + if (IsDualWeapon(self.weapon)) { + modelname = GetLeftWeaponModel(self.weapon); + } + } + shotcount = GetWeaponShotcount(self.weapon); + soundname = GetWeaponSound(self.weapon); + delay = getWeaponDelay(self.weapon, FIRE); + + // Double Tap 2.0 + if (self.perks & P_DOUBLE) + shotcount *= 2; + + switch(self.stance) { + case 2: + sprdstance = 0; + break; + case 1: + sprdstance = 2; + break; + case 0: + sprdstance = 4; + break; + default: + sprdstance = 0; + break; + } + + spread = (1.5*GetWeaponSpread(self.weapon))-sprdstance; + + if (self.perks & P_DEAD) { + spread *= 0.65; + } + + if (self.zoom == 1) { + if (self.weapon == W_TRENCH || + self.weapon == W_DB || + self.weapon == W_SAWNOFF || + self.weapon == W_GUT || + self.weapon == W_BORE) { + spread = spread * 0.75; + } else { + spread = spread * 0.1; + } + } else if (self.zoom == 2) { + spread = spread * 0.05; + } else if (self.velocity || !(self.flags & FL_ONGROUND)) + spread *= 2; + + if (self.downed) + playdownfire(); + + // MOTO - should probably reorganize this whole firetype chunk in the future.. + + //Fire weapons + if (firetype == 1 || firetype == 3) + { + if (side == S_RIGHT) { + if (self.semi) { + return; + } + self.semi = TRUE; + } else if (side == S_LEFT) { + if (self.semi2) { + return; + } + self.semi2 = TRUE; + } + + if (firetype == 3) { + W_FireGrenade(side); + } + } else if (firetype == 4) { + W_FireRay(); + } else if (firetype == 5) { + W_FireTesla(); + } else if (firetype == 6) { + W_FireM2(); + } + + // FIXME - clean this up lol + if (firetype == 1 || firetype == 0 || firetype == 3) { + FireTrace(shotcount, spread, damage, side); + } + + //Play weapon animation and sound + startframe = GetFrame(self.weapon,FIRE_START); + endframe = GetFrame(self.weapon,FIRE_END); + + + if (self.perks & P_DOUBLE) { + delay *= 0.66; + } + + if (self.weapon == W_GUT || self.weapon == W_KAR_SCOPE || self.weapon == W_TRENCH || self.weapon == W_KAR || self.weapon == W_ARMAGEDDON || self.weapon == W_HEADCRACKER) + { + Set_W_Frame (startframe, endframe, delay, 0, FIRE, W_LoadAmmo, modelname, FALSE, side); + self.NeedLoad = true; + } else { + Set_W_Frame (startframe, endframe, delay, 0, FIRE, CheckReload, modelname, FALSE, side); + } + + + sound (self, CHAN_WEAPON, soundname, 1, ATTN_NORM); + + if (side == S_RIGHT) { + self.currentmag = self.currentmag - 1; + self.fire_delay = getWeaponDelay(self.weapon, FIRE) + time; + } else { + self.currentmag2 = self.currentmag2 - 1; + self.fire_delay2 = getWeaponDelay(self.weapon, FIRE) + time; + } + + if (IsPapWeapon(self.weapon)) { + sound (self, 0, "sounds/weapons/papfire.wav", 1, ATTN_NORM); + } + + SetUpdate(self, UT_HUD, 6, 0, 0); + SetUpdate(self, UT_CROSSHAIR, 5, 0, 0); + + + // REFORMAT THIS + #ifndef PC + self.effects = self.effects | EF_MUZZLEFLASH; + #else + // psp takes care of this in engine + if (self.perks & P_DEAD) + return; + + vector Wep_Recoil; + Wep_Recoil = GetWeaponRecoil(self.weapon)*0.5; + + self.punchangle_x = Wep_Recoil_x; + self.punchangle_y = Wep_Recoil_y; + self.punchangle_z = Wep_Recoil_z; + #endif +} + + +/****************************** +* W_Knife * +******************************/ + +// FIXME - knife & nade vmodels change skin on PSP! +void () W_Knife = +{ + if (time < self.knife_delay || self.new_anim_stop || + self.zoom || self.health == 10 || + (!(self.flags & FL_ONGROUND))) + return; + + vector source; + entity hit_ent = world; + //entity oldself; + float r, backupSkin; + vector org; + float hit = false; + + if (self.sprinting) { + W_SprintStop(); + } + + backupSkin = self.weaponskin; + self.weaponskin = 0; + sound (self, CHAN_WEAPON, "sounds/weapons/knife/knife.wav", 1, ATTN_NORM); + makevectors (self.v_angle); + source = self.origin + self.view_ofs; + if (self.weapon == W_BK) + traceline (source, source + v_forward*128, 0, self); + else + traceline (source, source + v_forward*100, 0, self); + + org = trace_endpos - v_forward*4; + + r = fabs(vlen(source - org)); + + if (trace_ent.takedamage) + { + if (trace_ent.classname == "ai_zombie_head") + hit_ent = trace_ent.owner; + else if (trace_ent.classname == "ai_zombie_larm") + hit_ent = trace_ent.owner; + else if (trace_ent.classname == "ai_zombie_rarm") + hit_ent = trace_ent.owner; + else + hit_ent = trace_ent; + if (hit_ent.classname == "ai_zombie") + hit = true; + + // if zombie has a head and it's instakill, gib for a e s t h e t i c + if (hit_ent.head && instakill_finished >= time) { + spawn_gibs(hit_ent.head.origin); + setmodel(hit_ent.head, ""); + } + } + else if (trace_fraction < 1.0) + { // hit wall + sound (self, CHAN_WEAPON, "sounds/weapons/knife/knife_hit.wav", 1, ATTN_NORM); + + /* naievil (FIXME) add weapon decal here */ + + #ifdef PC + if (self.weapon != W_RAY && self.weapon != W_PORTER) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EVENT_PISTOLFIRE); + WriteEntity(MSG_MULTICAST, self); + WriteFloat(MSG_MULTICAST, 0); + WriteCoord(MSG_MULTICAST, org_x); + WriteCoord(MSG_MULTICAST, org_y); + WriteCoord(MSG_MULTICAST, org_z); + WriteCoord(MSG_MULTICAST, trace_plane_normal_x); + WriteCoord(MSG_MULTICAST, trace_plane_normal_y); + WriteCoord(MSG_MULTICAST, trace_plane_normal_z); + WriteEntity(MSG_MULTICAST, hit_ent); + multicast(trace_endpos, MULTICAST_PHS); + } + #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); + #endif + } + + if (r > 40 && hit)//lunge + { + if (self.weapon == W_BK) + { + Set_W_Frame (14, 24, 1.4, 0, KNIFE, SUB_Null, "models/weapons/bk/v_bk.mdl", false, S_RIGHT); + self.fire_delay = time + 1.5; + self.knife_delay = time + 1.75; + } + else if (self.bowie) + { + Set_W_Frame (4, 10, 0.7, 0, KNIFE, ReturnWeaponModel, "models/weapons/knife/v_bowie.mdl", false, S_RIGHT); + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.8; + self.knife_delay = time + 1.3; + } + else + { + self.punchangle_x = -5; + self.punchangle_y = -10; + Set_W_Frame (4, 12, 0.7, 0, KNIFE, ReturnWeaponModel, "models/weapons/knife/v_knife.mdl", false, S_RIGHT); + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.8; + self.knife_delay = time + 1; + } + self.velocity = v_forward * 200; + + } + else + { + if (self.weapon == W_BK)//slash + { + Set_W_Frame (8, 13, 0, 0.4, KNIFE, SUB_Null, "models/weapons/bk/v_bk.mdl", false, S_RIGHT); + self.fire_delay = time + 0.5; + self.knife_delay = time + 0.8; + } + else if (self.bowie) + { + Set_W_Frame (0, 3, 0.3, 0, KNIFE, ReturnWeaponModel, "models/weapons/knife/v_bowie.mdl", false, S_RIGHT); + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; + self.knife_delay = time + 0.9; + } + else + { + self.punchangle_x = -5; + self.punchangle_y = -10; + //self.punchangle_z = 5; + Set_W_Frame (0, 3, 0.3, 0, KNIFE, ReturnWeaponModel, "models/weapons/knife/v_knife.mdl", false, S_RIGHT); + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; + self.knife_delay = time + 0.7; + } + } + self.reload_delay2 = self.reload_delay = 0; + self.weaponskin = backupSkin; + + LungeKnifeHit(); + +}; + + + + +/****************************** +* W_Grenade * +******************************/ + +void() switch_nade = +{ + if (self.downed || self.isBuying || !(self.grenades & 2) || self.semiswitch) + return; + + if (self.pri_grenade_state == 0) + { + centerprint (self, "Bouncing Betties Selected"); + self.bk_nade = 1; + } + if (self.pri_grenade_state == 1) + { + centerprint (self, "Frag Grenades Selected"); + self.bk_nade = 0; + } + + self.pri_grenade_state = self.bk_nade; + +}; + +void() GrenadeExplode = +{ + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/explode.wav", 1, ATTN_NORM); + + if (self.classname == "grenade") + DamgageExplode (self, self.owner, 225, 75, 128);//was 256 inch radius, kicked it down to 128 because 21 feet exploding radius is... a bit overkill... + else + DamgageExplode (self, self.owner, 1200, 1000, 128); //explosive weapons are more powerful :) + + #ifdef PC + te_customflash(self.origin, 178, 300, '1 1 1'); + #endif + + CallExplosion(self.origin); + + SUB_Remove (); +}; +void() NadeStraighten = +{ + self.angles_x = (self.angles_x > 0) ? 90 : -90; + + if (self.pri_grenade_state == 1) + return; +}; +void() Velocity_reduce = +{ + if (!other.solid || other.solid == SOLID_TRIGGER) + if (other != world) + return; + + self.velocity = self.velocity*0.5; + + NadeStraighten(); +}; + +void() betty_touch = +{ + //centerprint (other, "wow\n"); <- THIS + + if (other.classname == "grenade") + return; + + if (other == self.owner || other.solid == SOLID_TRIGGER) + if (other != world) + return; + + GrenadeExplode(); +} + +void() W_ThrowBetty = +{ + local entity betty; + betty = spawn (); + betty.owner = self; + betty.grenade_delay = betty.owner.grenade_delay; + betty.owner.grenade_delay = 0; + betty.movetype = MOVETYPE_NONE; + betty.solid = SOLID_TRIGGER; + betty.classname = "betty"; + + // set betty speed + betty.velocity = v_forward*0; + + betty.think = GrenadeExplode; + betty.touch = betty_touch; + + setmodel (betty, "models/weapons/grenade/g_betty.mdl"); + setsize (betty, '-16 -16 -4', '16 16 1'); + + betty.origin = betty.owner.origin - self.view_ofs + '0 0 1'; + //if player isn't on ground, drop the betty to the floor. + if (!(self.flags & FL_ONGROUND)) { + local entity oldself; + oldself = self; + self = betty; + + #ifdef PC + droptofloor(); + #else + droptofloor(0, 0); + #endif + self = oldself; + } + betty.origin += v_forward * 0; + setorigin (betty, betty.origin); + + self.animend = ReturnWeaponModel; + self.callfuncat = 0; + self.isBuying = false; +} + +void() W_ThrowGrenade = +{ + string modelname; + float startframe; + float endframe; + local entity nade; + + if (self.pri_grenade_state == 0) { + nade = spawn (); + nade.owner = self; + nade.grenade_delay = nade.owner.grenade_delay; + nade.owner.grenade_delay = 0; + nade.movetype = MOVETYPE_BOUNCE; + nade.solid = SOLID_BBOX; + nade.classname = "grenade"; + + // set nade speed + + makevectors (self.v_angle); + + #ifndef PSP + nade.velocity = v_forward*500; + #else + nade.velocity = v_forward*800; // on PSP, we need more OOMFPH + #endif + + nade.avelocity = '400 -400 400'; + + //nade.angles = vectoangles(nade.velocity); + //nade.angles_z += (nade.angles_z + 180 < 360)? 180 : -180; + nade.angles = vectoangles(v_forward); + nade.angles_z -= 15; + // set nade duration + nade.nextthink = nade.grenade_delay; + nade.think = GrenadeExplode; + + nade.touch = Velocity_reduce; + setmodel (nade, "models/weapons/grenade/g_grenade.mdl"); + setsize (nade, '0 0 0', '0 0 0'); + nade.origin = self.origin + self.view_ofs; + nade.origin += v_forward * 12; + setorigin (nade, nade.origin); + + self.animend = ReturnWeaponModel; + self.callfuncat = 0; + self.isBuying = false; + } else if (self.pri_grenade_state == 1) { + W_ThrowBetty(); + } else { + centerprint (other, "No grenadetype defined...\n"); + } + + //if (!(self.flags & FL_ONGROUND)) { + // self.secondary_grenades = self.secondary_grenades + 1; + //} + + startframe = GetFrame(self.weapon,TAKE_OUT_START); + endframe = GetFrame(self.weapon,TAKE_OUT_END); + modelname = GetWeaponModel(self.weapon, 0); + Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH); + self.semiswitch = false; + + SetUpdate(self, UT_HUD, 6, 0, 0); +} + + +void() checkHoldB = +{ + if (!self.button3) + { + if(self.grenade_delay < time) + self.grenade_delay = time + 0.05; + self.isBuying = true; + Set_W_Frame (18, 23, 0, 5, GRENADE, W_ThrowBetty, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT); + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/throw.wav", 1, ATTN_NORM); + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; + self.throw_delay = time + 0.9; + } + else + { + self.isBuying = true; + Set_W_Frame (18, 18, 0, 0, GRENADE, checkHoldB, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT); + } +} + +void() checkHold = +{ + if (self.pri_grenade_state == 1) { + checkHoldB(); + return; + } + + if (!self.button3 || self.grenade_delay < time) + { + if(self.grenade_delay < time) + self.grenade_delay = time + 0.05; + + self.isBuying = true; + Set_W_Frame (3, 6, 0, 5, GRENADE, W_ThrowGrenade, "models/weapons/grenade/v_grenade.mdl", true, S_RIGHT); + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/throw.wav", 1, ATTN_NORM); + self.reload_delay2 = self.fire_delay2 = self.reload_delay = self.fire_delay = time + 0.4; + self.throw_delay = time + 0.9; + } + else + { + self.isBuying = true; + Set_W_Frame (2, 2, 0, 0, GRENADE, checkHold, "models/weapons/grenade/v_grenade.mdl", true, S_RIGHT); + } +} + + +void() W_Betty = { + if (self.throw_delay > time || self.zoom || self.downed || self.secondary_grenades < 1 || self.isBuying) + return; + + Set_W_Frame (0, 18, 0, 0, GRENADE, checkHoldB, "models/weapons/grenade/v_betty.mdl", true, S_RIGHT); + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/prime.wav", 1, ATTN_NORM); + self.secondary_grenades -= 1; + self.reload_delay2 = self.fire_delay2 = self.throw_delay = self.reload_delay = self.fire_delay = time + 6; + self.semiswitch = true; +} + +void() W_Grenade = +{ + if (self.throw_delay > time || self.zoom || self.downed || self.primary_grenades < 1 || self.isBuying) + return; + + Set_W_Frame (0, 2, 0, 0, GRENADE, checkHold, "models/weapons/grenade/v_grenade.mdl", true, S_RIGHT); + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/prime.wav", 1, ATTN_NORM); + self.primary_grenades -= 1; + self.reload_delay2 = self.fire_delay2 = self.throw_delay = self.reload_delay = self.fire_delay = time + 6; + self.grenade_delay = time + 5; + self.semiswitch = true; +} + +void(float wepnum) CheckButton = { + W_PutOut(); +} + +void() Change_Stance = { + + if (self.downed || !(self.flags & FL_ONGROUND) || self.changestance == true || self.new_ofs_z != self.view_ofs_z) + return; + + switch(self.stance) { + case 2: + self.new_ofs_z = self.view_ofs_z - 24; + self.stance = 1; + break; + case 1: + if (self.isBuying) { //don't want to prone while buying a perk.. + self.stance = 2; + self.new_ofs_z = self.view_ofs_z + 24; + } else if (!self.stancereset) { + self.new_ofs_z = self.view_ofs_z - 18; + self.stance = 0; + } else { + self.new_ofs_z = self.view_ofs_z + 24; + self.stance = 2; + self.stancereset = 0; + } + break; + case 0: + self.new_ofs_z = self.view_ofs_z + 18; + self.stance = self.stancereset = 1; + break; + default: break; + } + +} + +void() dolphin_dive = //naievil +{ + if (self.stance != 2) + return; + 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; + } + + 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 + makevectors (self.v_angle); + self.velocity = self.velocity * 1.2; + self.velocity_z = self.velocity_z + 270; + + if (self.button2) + self.button2 = 0; + + self.dive = 1; + W_SprintStop(); + + self.oldz = self.origin_z; + self.new_ofs_z = self.view_ofs_z - 42; + self.stance = 0; +} + + +void () Impulse_Functions = +{ + if (!self.impulse) + return; + + switch (self.impulse) + { + case 10: + #ifdef PC + // FTE handles jumping on PC, which we don't want.. so we use impulses to override. + JumpCheck(1); + #endif + break; + case 22: + addmoney(self, 10000, 0); + rounds += 4; + break; + case 23: + #ifdef PC + if (!checkMovement(forward)) + return; + #endif + if (self.dive || self.downed) + return; + switch(self.stance) { + case 0: + self.new_ofs_z = self.view_ofs_z + 42; + self.stance = 2; + break; + case 1: + self.new_ofs_z = self.view_ofs_z + 24; + self.stance = 2; + break; + default: break; + } + W_SprintStart(); + break; + case 24: + W_SprintStop(); + break; + case 25: + #ifdef PSP + switch_nade(); + #else + W_Betty(); + #endif + break; + case 26: + case 30: + if (self.sprinting) + dolphin_dive(); + else + Change_Stance(); + break; + #ifdef NX + case 31: + self.sprintflag = 0; + break; + case 32: + self.sprintflag = 1; + break; + #endif + /*case 110: + CheckButton(1); + break; + case 111: + CheckButton(0); + break; */ + case 110: + case 111: + W_PutOut(); + break; + case 100: + cvar_set("waypoint_mode", "1"); + + local entity d; + d = find(world,classname,"door_nzp_cost"); + while(d != world) + { + d.classname = "door_nzp"; + d = find(d,classname,"door_nzp_cost"); + } + d = find(world,classname,"door_open"); + while(d != world) + { + d.classname = "door_nzp"; + d = find(d,classname,"door_open"); + } + + waypoint_mode = 1; + break; + default: + #ifdef PC + bprint(PRINT_HIGH, "Unknown impulse: ", ftos(self.impulse)); + #endif + break; + } + + self.impulse = 0; +}; + +void() CheckImpulses = +{ + self.impulse = 0; +} + +void(entity ent) CheckPowerups = +{ + if (instakill_finished >= time) + { + if (instakill_finished < 4 + time) + { + if (insta_blink< time - 0.25) + insta_blink = time + 0.25; + } + else if (instakill_finished < 7 + time) + { + if (insta_blink< time - 0.5) + insta_blink = time + 0.5; + } + else if (instakill_finished < 10 + time) + { + if (insta_blink< time - 1) + insta_blink = time + 1; + } + + if (insta_blink < time) + ent.insta_icon = true; + else + ent.insta_icon = false; + } + else if(instakill_finished) + { + instakill_finished = 0; + + ent.insta_icon = false; + } + + if (x2_finished > time) + { + if (x2_finished < 4 + time) + { + if (x2_blink< time - 0.25) + x2_blink = time + 0.25; + } + else if (x2_finished < 7 + time) + { + if (x2_blink< time - 0.5) + x2_blink = time + 0.5; + } + else if (x2_finished < 10 + time) + { + if (x2_blink< time - 1) + x2_blink = time + 1; + } + + if (x2_blink < time) + ent.x2_icon = true; + else + ent.x2_icon = false; + } + else if(x2_finished) + { + x2_finished = 0; + + ent.x2_icon = false; + } + +} + +void() CheckPlayer = +{ + CheckPowerups(self); + CheckRevive(self); + +#ifdef NX + if (!self.sprintflag && self.sprinting) { + W_SprintStop(); + } +#endif + + // FIXME - can't do frame independent stance changing.. ofs starts spazzing.. + if (self.view_ofs_z > self.new_ofs_z) { + self.changestance = true; + self.view_ofs_z = self.view_ofs_z - 1.5; + } else if (self.view_ofs_z < self.new_ofs_z) { + self.changestance = true; + self.view_ofs_z = self.view_ofs_z + 1.5; + } else { + self.changestance = false; + } + + if (self.view_ofs_z > 32) { + self.view_ofs_z = 32; + self.changestance = false; + } + + /* Check player push start */ + entity ent; + ent = findradius(self.origin, 70); + + while(ent) + { + if(ent.classname == "player" && ent != self && ent.downed && !self.downed) + { + useprint (self, 13, 0, 0); + + if (self.button7 && !ent.invoke_revive) { + centerprint(ent, "Another player is reviving you..."); + ent.beingrevived = true; + + if (!self.progress_bar_percent) { + self.movetype = MOVETYPE_NONE; + Set_W_Frame (0, 21, 0, 0, SPRINT, SUB_Null, "models/v_morphine.mdl", false, S_BOTH); + + if !(self.perks & P_REVIVE) + self.progress_bar_time = 4; + else + self.progress_bar_time = 2; + + self.progress_bar = self.progress_bar_time + time; + self.progress_bar_percent = 1; + self.reviving = true; + } + + if (self.revived) { + self.movetype = MOVETYPE_WALK; + W_TakeOut(); + ent.invoke_revive = 1; + ent.beingrevived = false; + self.reviving = 0; + self.progress_bar = 0; + self.progress_bar_time = 0; + self.progress_bar_percent = 0; + self.revived = 0; + } + } + else if (!self.button7 && self.reviving) { + self.movetype = MOVETYPE_WALK; + ent.beingrevived = false; + W_TakeOut(); + self.progress_bar = 0; + self.progress_bar_time = 0; + self.progress_bar_percent = 0; + self.revived = 0; + self.reviving = 0; + } + } + else if(ent.classname == "player" && ent != self && !ent.downed) // small player push + { + vector push; + push = ent.origin - self.origin; + push_z = 0; + push = normalize(push) * 0.5; + + ent.velocity += push; + } + ent = ent.chain; + } + /* Check for player push end */ + + if (!self.button2 && self.flags & FL_ONGROUND) { + if (self.dive) { + if (self.perks & P_FLOP && fabs(self.oldz - self.origin_z) > 90) { + sound (self, CHAN_WEAPON, "sounds/weapons/grenade/explode.wav", 1, ATTN_NORM); + DamgageExplode (self, self, 225, 75, 128); + #ifdef PC + te_customflash(self.origin, 128, 300, '1 1 1'); + #endif + + CallExplosion(self.origin); + } + self.dive = 0; + } + + if (fabs(self.oldz - self.origin_z) > 90 && !(self.perks & P_FLOP)) { + DamageHandler (self, other, 50, S_ZOMBIE); + + if (self.health <= 10) + GiveAchievement(7, self); + } + + self.oldz = self.origin_z; + } +} + +void () Weapon_Logic = +{ + W_Frame_Update (); + Impulse_Functions(); + + #ifndef PC + // For PSP only to tell our engine to display the scope + if ((self.scopetime < time) && self.scopetime) { + self.scopetime = 0; + self.zoom = 2; + self.weapon2model = ""; + self.weaponmodel = ""; + } + #else + UpdatePunchangle(self); + #endif + + //FIXME - cleanup! + if (IsDualWeapon(self.weapon)) { + if (self.button0) { + #ifdef PC + W_Fire(S_LEFT); + #else + W_Fire(S_RIGHT); + #endif + } else { + #ifdef PC + self.semi2 = FALSE; + #else + self.semi = FALSE; + #endif + } + + if (self.button8) { + #ifdef PC + W_Fire(S_RIGHT); + #else + W_Fire(S_LEFT); + #endif + } else { + #ifdef PC + self.semi = FALSE; + #else + self.semi2 = FALSE; + #endif + } + } else { + if (self.button0) { + W_Fire(S_RIGHT); + } else { + self.semi = FALSE; + } + if (cvar("cl_adsmode")) { + if (self.button8) { + self.ads_toggle = !self.ads_toggle; + + if (self.ads_toggle == false) { + W_AimOut(); + self.zoom = 0; + + #ifdef PC + self.viewzoom = 1; + UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); + UpdateV2model(self.weapon2model, 0); + #endif + } else { + W_AimIn(); + self.zoom = 1; + + #ifdef PC + self.viewzoom = 0.9; + #endif + } + } + } else { + if (self.button8) { + if (!self.zoom) { + W_AimIn(); + } + #ifdef PC + else { + + if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || + self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { + if (self.viewzoom > 0.5) + self.viewzoom -= 0.05; + else + self.viewzoom = 0.5; + } else { + if (self.viewzoom > 0.9) + self.viewzoom -= 0.01; + else + self.viewzoom = 0.89999; + } + } + #endif + } else if (!self.button8 && self.zoom) { + + #ifdef PC + if (self.weapon == W_KAR_SCOPE || self.weapon == W_PTRS || + self.weapon == W_HEADCRACKER || self.weapon == W_PENETRATOR) { + if (self.viewzoom == 0.5) { + self.viewzoom += 0.05; + ReturnWeaponModel(); + } else { + UpdateVmodel(self.weaponmodel, GetWepSkin(self.weapon)); + UpdateV2model(self.weapon2model, 0); + self.viewzoom += 0.05; + } + } else { + if (self.viewzoom == 0.9) { + W_AimOut(); + } else { + self.viewzoom += 0.015; + } + } + + if (self.viewzoom >= 1) { + self.viewzoom = 1; + self.zoom = 0; + } + #else + W_AimOut(); + #endif + } + } + } + + if (!self.button7) { + self.semiuse = false; + } + + if (self.button4 && !self.semiswitch && self.secondaryweapon && self.secondaryweapon !=0 && !self.zoom) + { + W_PutOut(); + self.semiswitch = true; + } else if (!self.button4) { + self.semiswitch = false; + } + + if (self.button3 && self.isBuying != true && self.semiswitch != true) + { + if (self.pri_grenade_state == 0) + W_Grenade(); + else + W_Betty(); + self.semiswitch = true; + } + else if (!self.button3) + self.semiswitch = false; + + if (self.button5 && !self.semireload) + { + W_Reload(S_BOTH); + self.semireload = TRUE; + } + else if (!self.button5) { + self.semireload = FALSE; + } + + if (self.button6 && !self.semiknife) + { + #ifdef PSP + if (self.sprinting) { + dolphin_dive(); + return; + } + #endif + W_Knife(); + self.semiknife = true; + } + else if (!self.button6) + self.semiknife = false; + + CheckImpulses(); + CheckPlayer(); +} diff --git a/source/shared/defs/custom.qc b/source/shared/defs/custom.qc new file mode 100644 index 0000000..14dd10c --- /dev/null +++ b/source/shared/defs/custom.qc @@ -0,0 +1,221 @@ +/* + shared/defs/custom.qc + + shared definitions + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +const float EVENT_WEAPONCHANGE = 9; +const float EVENT_PISTOLFIRE = 10; +const float EVENT_USEPRINT = 11; +const float EVENT_NEWROUND = 12; +const float EVENT_SETROUND = 13; +const float EVENT_UPDATEVMODEL = 20; +const float EVENT_UPDATEV2MODEL = 21; +const float EVENT_PERK = 22; +const float EVENT_UPDATE = 23; +const float EVENT_BROADCAST = 24; +const float EVENT_POINTUPDATE = 25; +const float EVENT_BLACKOUT = 26; +const float EVENT_PUNCHANGLE = 27; +const float EVENT_SCROLLTEXT = 28; +const float EVENT_WORLDDATA = 29; +const float EVENT_ACHIEVEMENT = 30; +const float EVENT_PLAYERUPDATE = 31; +const float EVENT_WEAPONUPDATE = 32; +const float EVENT_HUDUPDATE = 33; +const float EVENT_EXPLOSION = 34; +const float EVENT_BLOOD = 35; +const float EVENT_ACHIEVEMENTPROGRESS = 36; + +// Define our PC version if we don't have NX or PSP +#ifndef NX +#ifndef PSP +#define PC +#endif +#endif + +//nzp weapon defines +//id list +#define W_NOWEP 0 +#define W_COLT 1 +#define W_KAR 2 +#define W_THOMPSON 3 +#define W_357 4 +#define W_BAR 5 +#define W_BK 6 +#define W_BROWNING 7 +#define W_DB 8 +#define W_FG 9 +#define W_GEWEHR 10 +#define W_KAR_SCOPE 11 +#define W_M1 12 +#define W_M1A1 13 +#define W_M2 14 +#define W_MP40 15 +#define W_MG 16 +#define W_PANZER 17 +#define W_PPSH 18 +#define W_PTRS 19 +#define W_RAY 20 +#define W_SAWNOFF 21 +#define W_STG 22 +#define W_TRENCH 23 +#define W_TYPE 24 +#define W_BOWIE 25 +#define W_GRENADE 26 +#define W_BETTY 27 +#define W_BIATCH 28 +#define W_KILLU 29 //357 +#define W_COMPRESSOR 30 // Gewehr +#define W_M1000 31 //garand +//#define W_KOLLIDER 32 +#define W_PORTER 33 // Ray +#define W_WIDDER 34 // M1A1 +#define W_FIW 35 //upgraded flamethrower +#define W_ARMAGEDDON 36 //Kar +#define W_WUNDER 37 +#define W_GIBS 38 // thompson +#define W_SAMURAI 39 //Type +#define W_AFTERBURNER 40 //mp40 +#define W_SPATZ 41 // stg +#define W_SNUFF 42 // sawn off +#define W_BORE 43 // double barrel +#define W_IMPELLER 44 //fg +#define W_BARRACUDA 45 //mg42 +#define W_ACCELERATOR 46 //M1919 browning +#define W_GUT 47 //trench +#define W_REAPER 48 //ppsh +#define W_HEADCRACKER 49 //scoped kar +#define W_LONGINUS 50 //panzer +#define W_PENETRATOR 51 //ptrs +#define W_WIDOW 52 //bar +//#define W_KRAUS 53 //ballistic +#define W_MP5 54 +#define W_M14 55 + +#define W_TESLA 56 +#define W_DG3 57 + +//Custom Weapons +//FIXME - use array? +#define W_CUSTOM1 70 +#define W_CUSTOM2 71 +#define W_CUSTOM3 72 +#define W_CUSTOM4 73 + + +#define BASE_FRAME 1 +#define FIRE_START 2 +#define FIRE_END 3 +#define RELOAD_START 4 +#define RELOAD_END 5 +#define SPRINT_IN_START 12 +#define SPRINT_IN_END 13 +#define SPRINT_START 10 +#define SPRINT_END 11 +#define SPRINT_OUT_START 14 +#define SPRINT_OUT_END 15 +#define TAKE_OUT_START 16 +#define TAKE_OUT_END 17 +#define PUT_OUT_START 18 +#define PUT_OUT_END 19 +#define RELOAD_CANCEL 20 +#define AIM_IN_START 30 +#define AIM_IN_END 31 +#define AIM_LOOP 33 +#define AIM_OUT_START 34 +#define AIM_OUT_END 35 +#define AIM_FIRE_START 36 +#define AIM_FIRE_END 37 + +//Animation types +#define RELOAD 1 +#define GRENADE 2 +#define FIRE 3 +#define SWITCHWEP 4 +#define KNIFE 5 +#define ZOOM 6 +#define SPRINT 7 +#define PERK 8 +#define KNIFE2 9 +#define REVIVE 10 + +#define S_HEADSHOT 1 +#define S_KNIFE 2 +#define S_NORMAL 3 +#define S_ZOMBIE 4 +#define S_EXPLOSIVE 5 +#define S_ZAPPER 6 +#define S_TESLA 7 + +//Perk types +#define P_JUG 1 +#define P_DOUBLE 2 +#define P_SPEED 4 +#define P_REVIVE 8 +#define P_FLOP 16 +#define P_STAMIN 32 +#define P_DEAD 64 +#define P_MULE 128 + +#define STAT_VIEWZOOM 21 + +#define STAT_CURRENTMAG 50 +#define STAT_CURRENTMAG2 51 +#define STAT_POINTS 52 +#define STAT_WEAPON2FRAME 53 +#define STAT_WEAPON2MODEL 54 +#define STAT_GRENADES 55 +#define STAT_SECGRENADES 56 +#define STAT_PROGRESSBAR 57 +#define STAT_WEAPONDURATION 58 +#define STAT_WEAPON2DURATION 59 +#define STAT_WEAPONZOOM 60 +#define STAT_INSTA 61 +#define STAT_X2 62 +#define STAT_SPECTATING 63 +#define STAT_PLAYERNUM 64 +#define STAT_PLAYERSTANCE 65 +#define STAT_FACINGENEMY 66 + +// +// invert float takes in float value between 0 and 1, inverts position +// eg: 0.1 returns 0.9, 0.34 returns 0.66 +float invertfloat(float input) { + + if (input <= 0 || input >= 1) + return input; // out of boundaries + + return (1 - input); +} + +//elements +#ifndef NX +string G_HUD; +float G_HUDHOR; +#endif + +// custom weapon defs +// now 90% slimmed down +float currentWeaponTracker; diff --git a/source/shared/sound_enhanced.qc b/source/shared/sound_enhanced.qc new file mode 100644 index 0000000..858ecdd --- /dev/null +++ b/source/shared/sound_enhanced.qc @@ -0,0 +1,52 @@ +/* + shared/sound_enhanced.qc + + mainly serves as a QC middleground for sound to adjust volume per + channel. + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#define CHAN_MUSIC 1 +#define CHAN_SFX 2 + +#ifndef PSP +#ifndef NX +void(string soundname, optional float channel, optional float volume) localsound_enhanced = +{ + if (!volume) + volume = 1; + + switch(channel) { + case 1: volume *= cvar("snd_channel1volume"); break; + case 2: volume *= cvar("snd_channel2volume"); break; + case 3: volume *= cvar("snd_channel3volume"); break; + case 4: volume *= cvar("snd_channel4volume"); break; + case 5: volume *= cvar("snd_channel5volume"); break; + case 6: volume *= cvar("snd_channel6volume"); break; + default: break; + } + + localsound(soundname, channel, volume); +} +#endif +#endif \ No newline at end of file diff --git a/source/shared/weapon_defines.qc b/source/shared/weapon_defines.qc new file mode 100644 index 0000000..9d20bcf --- /dev/null +++ b/source/shared/weapon_defines.qc @@ -0,0 +1,4219 @@ +/* + shared/weapon_defines.qc + + all weapon stats are stored here + + Copyright (C) 2021 NZ:P Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +/* + Structs are MAGICAL! +*/ +var struct { + string name; + float magSize; + float reserveSize; + string vmodel; + string vmodel2; + string gmodel; + float isDual; + float firetype; + float nonpap; + float pap; + float ispap; + float damage; + float shotcount; + float bodypen; + float penetration; + float spread; + float fdelay; + float rdelay; + float walkspeed; + string firesound; + float skin; + float recoil; + float crossmin; + float crossmax; + float lowmag; + float lowreserve; + vector flash; + float flashsize; + float papWpn; + vector adsofs; +} CustomWeapons[] = +{ + +}; + +// so we know whether to use normal values or pull from our struct! +float(float wep) isCustomWeapon = +{ + if (wep >= 70) + return TRUE; + + return FALSE; +} + +string(float wep) GetWeaponName = +{ + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].name; + } + + switch (wep) + { + case W_COLT: + return "Colt M1911"; + case W_BIATCH: + return "Mustang & Sally"; + case W_KAR: + return "Kar 98k"; + case W_ARMAGEDDON: + return "Armageddon"; + case W_THOMPSON: + return "Thompson"; + case W_GIBS: + return "Gibs-O-Matic"; + case W_357: + return ".357 Magnum"; + case W_KILLU: + return ".357 Plus 1 K1L-u"; + case W_BAR: + return "BAR"; + case W_WIDOW: + return "The Widow Maker"; + case W_BK: + return "Ballistic Knife"; + case W_BROWNING: + return "Browning M1919"; + case W_ACCELERATOR: + return "B115 Accelerator"; + case W_DB: + return "Double-Barreled Shotgun"; + case W_BORE: + return "24 Bore Long Range"; + case W_FG: + return "FG42"; + case W_IMPELLER: + return "420 Impeller"; + case W_GEWEHR: + return "Gewehr 43"; + case W_COMPRESSOR: + return "G115 Compressor"; + case W_KAR_SCOPE: + return "Scoped Kar 98k"; + case W_HEADCRACKER: + return "Headcracker"; + case W_M1: + return "M1 Garand"; + case W_M1000: + return "M1000"; + case W_M1A1: + return "M1A1 Carbine"; + case W_WIDDER: + return "Widdershins RC-1"; + case W_M2: + return "M2 Flamethrower"; + case W_FIW: + return "F1W Nitrogen Cooled"; + case W_MP40: + return "MP40"; + case W_AFTERBURNER: + return "The Afterburner"; + case W_MG: + return "MG42"; + case W_BARRACUDA: + return "Barracuda FU-A11"; + case W_PANZER: + return "Panzerschrek"; + case W_LONGINUS: + return "Longinus"; + case W_PPSH: + return "PPSh-41"; + case W_REAPER: + return "The Reaper"; + case W_PTRS: + return "PTRS-41"; + case W_PENETRATOR: + return "The Penetrator"; + case W_RAY: + return "Ray Gun"; + case W_PORTER: + return "Porter's X2 Ray Gun"; + case W_SAWNOFF: + return "Sawnoff shotgun"; + case W_SNUFF: + return "The Snuff Box"; + case W_STG: + return "STG-44"; + case W_SPATZ: + return "Spatz-447 +"; + case W_TRENCH: + return "M1897 Trench Gun"; + case W_GUT: + return "Gut Shot"; + case W_TYPE: + return "Type 100"; + case W_SAMURAI: + return "1001 Samurais"; + case W_BOWIE: + return "Bowie Knife"; + case W_GRENADE: + return "Grenades"; + case W_BETTY: + return "Bouncing Betty"; + case W_TESLA: + return "Wunderwaffe DG-2"; + case W_DG3: + return "Wunderwaffe DG-3 JZ"; + default: + return " "; + } +} + +string(float perk) GetPerkName = +{ + switch (perk) + { + case 1: + return "Quick Revive"; + case 2: + return "Juggernog"; + case 3: + return "Speed Cola"; + case 4: + return "Double Tap"; + case 5: + return "Stamin-Up"; + case 6: + return "PhD Flopper"; + case 7: + return "Deadshot Daiquiri"; + case 8: + return "Mule Kick"; + default: + return "NULL"; + } +} + +float(float wep) GetFiretype = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].firetype; + } + + switch (wep) + { + case W_COLT: + return 1; + case W_BIATCH: + return 3; + case W_KAR: + return 1; + case W_ARMAGEDDON: + return 1; + case W_THOMPSON: + case W_GIBS: + return 0; + case W_357: + case W_KILLU: + return 1; + case W_BAR: + case W_WIDOW: + return 0; + case W_BROWNING: + case W_ACCELERATOR: + return 0; + case W_DB: + case W_BORE: + return 1; + case W_FG: + case W_IMPELLER: + return 0; + case W_GEWEHR: + case W_COMPRESSOR: + return 1; + case W_KAR_SCOPE: + return 1; + case W_HEADCRACKER: + return 0; + case W_M1: + case W_M1000: + return 1; + case W_M1A1: + return 1; + case W_WIDDER: + return 0; + case W_M2: + case W_FIW: + return 6; + case W_MP40: + case W_AFTERBURNER: + return 0; + case W_MG: + case W_BARRACUDA: + return 0; + case W_PANZER: + case W_LONGINUS: + return 3; + case W_PPSH: + case W_REAPER: + return 0; + case W_PTRS: + case W_PENETRATOR: + return 0; + case W_RAY: + case W_PORTER: + return 4; + case W_SAWNOFF: + case W_SNUFF: + return 1; + case W_STG: + case W_SPATZ: + return 0; + case W_TRENCH: + case W_GUT: + return 1; + case W_TYPE: + case W_SAMURAI: + return 0; + case W_TESLA: + case W_DG3: + return 5; + } + + return 0; +} + +#define HEAD_X 0 +#define UPPER_TORSO_X 1 +#define LOWER_TORSO_X 2 +#define LIMBS_X 3 + +float(float wep, float type) getWeaponMultiplier = +{ + + switch (wep) + { + case W_COLT: + case W_BIATCH: + switch (type) + { + case HEAD_X: return 3; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_KAR: + case W_ARMAGEDDON: + switch (type) + { + case HEAD_X: return 3.5; + case UPPER_TORSO_X: return 1.8; + case LOWER_TORSO_X: return 1.7; + case LIMBS_X: return 0.7; + } + break; + case W_THOMPSON: + case W_GIBS: + switch (type) + { + case HEAD_X: return 4; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_357: + case W_KILLU: + switch (type) + { + case HEAD_X: return 1.4; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_BAR: + case W_WIDOW: + switch (type) + { + case HEAD_X: return 2.7; + case UPPER_TORSO_X: return 1.8; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1.5; + } + break; + case W_BK: + return 1; + break; + case W_BROWNING: + case W_ACCELERATOR: + switch (type) + { + case HEAD_X: return 3; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_DB: + case W_BORE: + switch (type) + { + case HEAD_X: return 1; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_FG: + case W_IMPELLER: + switch (type) + { + case HEAD_X: return 2.7; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1.5; + } + break; + case W_GEWEHR: + case W_COMPRESSOR: + switch (type) + { + case HEAD_X: return 2.7; + case UPPER_TORSO_X: return 1.25; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 0.8; + } + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + switch (type) + { + case HEAD_X: return 10; + case UPPER_TORSO_X: return 1.5; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_M1: + case W_M1000: + switch (type) + { + case HEAD_X: return 2.7; + case UPPER_TORSO_X: return 1.25; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 0.7; + } + break; + case W_M1A1: + case W_WIDDER: + switch (type) + { + case HEAD_X: return 2.7; + case UPPER_TORSO_X: return 1.25; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 0.7; + } + break; + case W_M2: + case W_FIW: + return 3; + break; + case W_MP40: + case W_AFTERBURNER: + switch (type) + { + case HEAD_X: return 4; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_MG: + case W_BARRACUDA: + switch (type) + { + case HEAD_X: return 3; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_PANZER: + case W_LONGINUS: + return 3; + break; + case W_PPSH: + case W_REAPER: + switch (type) + { + case HEAD_X: return 3; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_PTRS: + case W_PENETRATOR: + switch (type) + { + case HEAD_X: return 4; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_RAY: + case W_PORTER: + return 3; + break; + case W_SAWNOFF: + case W_SNUFF: + switch (type) + { + case HEAD_X: return 1; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_STG: + case W_SPATZ: + switch (type) + { + case HEAD_X: return 2.7; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 0.5; + } + break; + case W_TRENCH: + case W_GUT: + switch (type) + { + case HEAD_X: return 1; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + case W_TYPE: + case W_SAMURAI: + switch (type) + { + case HEAD_X: return 4; + case UPPER_TORSO_X: return 1; + case LOWER_TORSO_X: return 1; + case LIMBS_X: return 1; + } + break; + } + return 0; +} + +float(float wep) getWeaponMag = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].magSize; + } + + switch (wep) + { + case W_COLT: + return 8; + case W_BIATCH: + return 6; + case W_KAR: + return 5; + case W_ARMAGEDDON: + return 8; + case W_THOMPSON: + return 20; + case W_GIBS: + return 40; + case W_357: + case W_KILLU: + return 6; + case W_BAR: + return 20; + case W_WIDOW: + return 30; + case W_BROWNING: + return 125; + case W_ACCELERATOR: + return 125; + case W_DB: + case W_BORE: + return 2; + case W_FG: + return 32; + case W_IMPELLER: + return 64; + case W_GEWEHR: + return 12; + case W_COMPRESSOR: + return 10; + case W_KAR_SCOPE: + return 5; + case W_HEADCRACKER: + return 10; + case W_M1: + return 8; + case W_M1000: + return 12; + case W_M1A1: + return 15; + case W_WIDDER: + return 15; + case W_M2: + return 200; + case W_FIW: + return 400; + case W_MP40: + return 32; + case W_AFTERBURNER: + return 64; + case W_MG: + return 125; + case W_BARRACUDA: + return 125; + case W_PANZER: + return 1; + case W_LONGINUS: + return 3; + case W_PPSH: + return 71; + case W_REAPER: + return 115; + case W_PTRS: + return 5; + case W_PENETRATOR: + return 8; + case W_RAY: + return 20; + case W_PORTER: + return 40; + case W_SAWNOFF: + case W_SNUFF: + return 2; + case W_STG: + return 30; + case W_SPATZ: + return 60; + case W_TRENCH: + return 6; + case W_GUT: + return 10; + case W_TYPE: + return 30; + case W_SAMURAI: + return 60; + case W_TESLA: + return 3; + case W_DG3: + return 6; + } + return 0; +} + +float(float wep) getWeaponAmmo = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].reserveSize; + } + + switch (wep) + { + case W_COLT: + return 80; + case W_BIATCH: + return 80; + case W_KAR: + return 50; + case W_ARMAGEDDON: + return 60; + case W_THOMPSON: + return 200; + case W_GIBS: + return 250; + case W_357: + case W_KILLU: + return 80; + case W_BAR: + return 140; + case W_WIDOW: + return 180; + case W_BROWNING: + return 500; + case W_ACCELERATOR: + return 750; + case W_DB: + return 60; + case W_BORE: + return 60; + case W_FG: + return 192; + case W_IMPELLER: + return 400; + case W_GEWEHR: + return 120; + case W_COMPRESSOR: + return 170; + case W_KAR_SCOPE: + return 50; + case W_HEADCRACKER: + return 100; + case W_M1: + return 128; + case W_M1000: + return 150; + case W_M1A1: + return 120; + case W_WIDDER: + return 150; + case W_M2: + case W_FIW: + return 0; + case W_MP40: + return 192; + case W_AFTERBURNER: + return 192; + case W_MG: + return 500; + case W_BARRACUDA: + return 750; + case W_PANZER: + return 20; + case W_LONGINUS: + return 30; + case W_PPSH: + return 355; + case W_REAPER: + return 700; + case W_PTRS: + return 60; + case W_PENETRATOR: + return 60; + case W_RAY: + return 160; + case W_PORTER: + return 200; + case W_SAWNOFF: + return 60; + case W_SNUFF: + return 60; + case W_STG: + return 180; + case W_SPATZ: + return 300; + case W_TRENCH: + return 60; + case W_GUT: + return 60; + case W_TYPE: + return 160; + case W_SAMURAI: + return 220; + case W_TESLA: + return 15; + case W_DG3: + return 32; + } + return 0; +} + + +float(float wep) getWeaponDamage = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].damage; + } + + switch (wep) + { + case W_COLT: + return 20; + case W_BIATCH: + return 1000; + case W_KAR: + return 100; + case W_ARMAGEDDON: + return 200; + case W_THOMPSON: + return 120; + case W_GIBS: + return 200; + case W_357: + return 1000; + case W_KILLU: + return 1400; + case W_BAR: + return 100; + case W_WIDOW: + return 200; + case W_BROWNING: + return 130; + case W_ACCELERATOR: + return 130; + case W_DB: + return 160; + case W_BORE: + return 320; + case W_FG: + return 100; + case W_IMPELLER: + return 110; + case W_GEWEHR: + return 90; + case W_COMPRESSOR: + return 200; + case W_KAR_SCOPE: + return 800; + case W_HEADCRACKER: + return 1000; + case W_M1: + return 105; + case W_M1000: + return 200; + case W_M1A1: + return 120; + case W_WIDDER: + return 200; + case W_MP40: + return 100; + case W_AFTERBURNER: + return 200; + case W_MG: + return 130; + case W_BARRACUDA: + return 130; + case W_PANZER: + return 600; + case W_LONGINUS: + return 1000; + case W_PPSH: + return 100; + case W_REAPER: + return 150; + case W_PTRS: + return 1000; + case W_PENETRATOR: + return 1000; + case W_RAY: + return 1000; + case W_PORTER: + return 1000; + case W_SAWNOFF: + return 240; + case W_SNUFF: + return 240; + case W_STG: + return 100; + case W_SPATZ: + return 150; + case W_TRENCH: + return 120; + case W_GUT: + return 240; + case W_TYPE: + return 100; + case W_SAMURAI: + return 200; + } + + return 0; +} + +float(float wep) GetWeaponShotcount = +{ + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].shotcount; + } + + switch(wep) + { + case W_DB: + return 8; + case W_BORE: + return 10; + case W_SAWNOFF: + case W_SNUFF: + return 8; + case W_TRENCH: + return 8; + case W_GUT: + return 10; + } + + return 1; +} + +float(float wep, float penetration_times) getWeaponPenetration = +{ + if (!penetration_times) + return 2; + + if (isCustomWeapon(wep)) { + if (penetration_times >= CustomWeapons[wep - 70].bodypen) + return 0; + + return CustomWeapons[wep - 70].penetration; + } + + switch (wep) + { + case W_COLT: + case W_BIATCH: + if (penetration_times >= 2) + return 0; + return 0.75; + case W_KAR: + case W_ARMAGEDDON: + if (penetration_times >= 3) + return 0; + return 1; + case W_THOMPSON: + case W_GIBS: + if (penetration_times >= 2) + return 0; + return 0.8; + case W_357: + case W_KILLU: + if (penetration_times >= 3) + return 0; + case W_BAR: + case W_WIDOW: + if (penetration_times >= 3) + return 0; + return 1; + case W_BROWNING: + case W_ACCELERATOR: + if (penetration_times >= 3) + return 0; + return 1; + case W_DB: + case W_BORE: + if (penetration_times >= 1) + return 0; + return 0.5; + case W_FG: + case W_IMPELLER: + if (penetration_times >= 2) + return 0; + return 0.9; + case W_GEWEHR: + case W_COMPRESSOR: + if (penetration_times >= 2) + return 0; + return 0.9; + case W_KAR_SCOPE: + case W_HEADCRACKER: + if (penetration_times >= 3) + return 0; + return 1; + case W_M1: + case W_M1000: + if (penetration_times >= 2) + return 0; + return 0.9; + case W_M1A1: + case W_WIDDER: + if (penetration_times >= 2) + return 0; + return 0.9; + case W_MP40: + case W_AFTERBURNER: + if (penetration_times >= 2) + return 0; + return 0.8; + case W_MG: + case W_BARRACUDA: + if (penetration_times >= 3) + return 0; + return 1; + case W_PPSH: + case W_REAPER: + if (penetration_times >= 2) + return 0; + return 0.75; + case W_PTRS: + if (penetration_times >= 3) + return 0; + return 1; + case W_PENETRATOR: + if (penetration_times >= 5) + return 0; + return 1; + case W_SAWNOFF: + case W_SNUFF: + if (penetration_times >= 1) + return 0; + return 0.4; + case W_STG: + case W_SPATZ: + if (penetration_times >= 2) + return 0; + return 0.8; + case W_TRENCH: + case W_GUT: + if (penetration_times >= 1) + return 0; + return 0.7; + case W_TYPE: + case W_SAMURAI: + if (penetration_times >= 2) + return 0; + return 0.8; + default: + return 0; + } +} + +float(float wep) GetWeaponSpread = +{ + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].spread; + } + + switch (wep) + { + case W_COLT: + return 7.5; + case W_BIATCH: + return 7.5; + case W_KAR: + return 25; + case W_ARMAGEDDON: + return 25; + case W_THOMPSON: + return 5; + case W_GIBS: + return 5; + case W_RAY: + return 5; + case W_PORTER: + return 5; + case W_KAR_SCOPE: + return 25; + case W_HEADCRACKER: + return 25; + case W_BAR: + return 5; + case W_WIDOW: + return 5; + case W_M1A1: + return 5; + case W_WIDDER: + return 5; + case W_M2: + return 0; + case W_FIW: + return 0; + case W_TYPE: + return 5; + case W_SAMURAI: + return 5; + case W_SAWNOFF: + return 15; + case W_SNUFF: + return 15; + case W_PPSH: + return 5; + case W_REAPER: + return 5; + case W_DB: + return 12.5; + case W_BORE: + return 12.5; + case W_FG: + return 5; + case W_IMPELLER: + return 5; + case W_TRENCH: + return 12.5; + case W_GUT: + return 12.5; + case W_MG: + return 10; + case W_BARRACUDA: + return 10; + case W_GEWEHR: + return 5; + case W_COMPRESSOR: + return 5; + case W_BROWNING: + return 10; + case W_ACCELERATOR: + return 10; + case W_357: + return 5; + case W_KILLU: + return 5; + case W_M1: + return 5; + case W_M1000: + return 5; + case W_MP40: + return 5; + case W_AFTERBURNER: + return 5; + case W_PTRS: + return 15; + case W_PENETRATOR: + return 15; + case W_STG: + return 5; + case W_SPATZ: + return 5; + case W_MP5: + return 5; + case W_TESLA: + return 5; + case W_M14: + return 5; + } + + return 0; +} + +float(float wep, float delaytype) getWeaponDelay = +{ + + if (isCustomWeapon(wep)) { + if (delaytype == RELOAD) + return CustomWeapons[wep - 70].rdelay; + else if (delaytype == FIRE) + return CustomWeapons[wep - 70].fdelay; + } + + switch (wep) + { + case W_COLT: + if (delaytype == RELOAD) + return 1.9; + else if (delaytype == FIRE) + return 0.100; + case W_BIATCH: + if (delaytype == RELOAD) + return 1.9; + else if (delaytype == FIRE) + return 0.225; + case W_ARMAGEDDON: + case W_KAR: + if (delaytype == RELOAD) + return 1.8; + else if (delaytype == FIRE) + return 0.3; + case W_THOMPSON: + case W_GIBS: + if (delaytype == RELOAD) + return 2.1; + else if (delaytype == FIRE) { + if (wep == W_THOMPSON) + return 0.085; + return 0.07; + } + case W_357: + case W_KILLU: + if (delaytype == RELOAD) + return 3; + else if (delaytype == FIRE) + return 0.4; + case W_BAR: + if (delaytype == RELOAD) + return 2.75; + else if (delaytype == FIRE) + return 0.17; + case W_WIDOW: + if (delaytype == RELOAD) + return 3.5; + else if (delaytype == FIRE) + return 0.12; + case W_BROWNING: + if (delaytype == RELOAD) + return 7; + else if (delaytype == FIRE) + return 0.104; + case W_ACCELERATOR: + if (delaytype == RELOAD) + return 7; + else if (delaytype == FIRE) + return 0.088; + case W_DB: + if (delaytype == RELOAD) + return 3; + else if (delaytype == FIRE) + return 0.5; + case W_BORE: + if (delaytype == RELOAD) + return 2.5; + else if (delaytype == FIRE) + return 0.5; + case W_FG: + if (delaytype == RELOAD) + return 2.6; + else if (delaytype == FIRE) + return 0.075; + case W_IMPELLER: + if (delaytype == RELOAD) + return 2.6; + else if (delaytype == FIRE) + return 0.068; + case W_GEWEHR: + case W_COMPRESSOR: + if (delaytype == RELOAD) + return 3; + else if (delaytype == FIRE) + return 0.200; + case W_M1: + case W_M1000: + if (delaytype == RELOAD) + return 3.2; + else if (delaytype == FIRE) + return 0.275; + case W_M1A1: + if (delaytype == RELOAD) + return 2.9; + else if (delaytype == FIRE) + return 0.220; + case W_WIDDER: + if (delaytype == RELOAD) + return 2.9; + else if (delaytype == FIRE) + return 0.150; + case W_M2: + case W_FIW: + if (delaytype == RELOAD) + return 2.7; + else if (delaytype == FIRE) + return 0.02; + case W_MP40: + if (delaytype == RELOAD) + return 2.3; + else if (delaytype == FIRE) + return 0.120; + case W_AFTERBURNER: + if (delaytype == RELOAD) + return 2.3; + else if (delaytype == FIRE) + return 0.110; + case W_MG: + if (delaytype == RELOAD) + return 4; + else if (delaytype == FIRE) + return 0.065; + case W_BARRACUDA: + if (delaytype == RELOAD) + return 4; + else if (delaytype == FIRE) + return 0.05; + case W_PANZER: + case W_LONGINUS: + if (delaytype == RELOAD) + return 1.55; + else if (delaytype == FIRE) + return 0.331; + case W_PPSH: + if (delaytype == RELOAD) + return 2.1; + else if (delaytype == FIRE) + return 0.05; + case W_REAPER: + if (delaytype == RELOAD) + return 2.1; + else if (delaytype == FIRE) + return 0.04; + case W_PTRS: + case W_PENETRATOR: + if (delaytype == RELOAD) + return 3; + else if (delaytype == FIRE) + return 0.8; + case W_RAY: + case W_PORTER: + if (delaytype == RELOAD) + return 2.75; + else if (delaytype == FIRE) + return 0.35; + case W_SAWNOFF: + if (delaytype == RELOAD) + return 2.65; + else if (delaytype == FIRE) + return 0.283; + case W_SNUFF: + if (delaytype == RELOAD) + return 2; + else if (delaytype == FIRE) + return 0.283; + case W_STG: + if (delaytype == RELOAD) + return 2.15; + else if (delaytype == FIRE) + return 0.110; + case W_SPATZ: + if (delaytype == RELOAD) + return 2.15; + else if (delaytype == FIRE) + return 0.08; + case W_TRENCH: + case W_GUT: + if (delaytype == RELOAD) + return 2.2; + else if (delaytype == FIRE) + return 0.283; + case W_TYPE: + if (delaytype == RELOAD) + return 2.25; + else if (delaytype == FIRE) + return 0.09; + case W_SAMURAI: + if (delaytype == RELOAD) + return 2.25; + else if (delaytype == FIRE) + return 0.07; + case W_TESLA: + if (delaytype == RELOAD) + return 4; + else if (delaytype == FIRE) + return 1; + break; + case W_DG3: + if (delaytype == RELOAD) + return 3.3; + else if (delaytype == FIRE) + return 1; + break; + } + + return 0; +} + +float GetWeaponWalkSpeed(float perks, float weapontype) +{ + float spd; + + spd = 0; + + if (isCustomWeapon(weapontype)) { + spd = CustomWeapons[weapontype - 70].walkspeed; + } + + switch(weapontype) { + default: spd = 1; + case W_KAR: + case W_357: + case W_GEWEHR: + case W_M1: + case W_M1A1: + case W_KAR_SCOPE: + case W_KILLU: + case W_COMPRESSOR: + case W_M1000: + case W_WIDDER: + case W_ARMAGEDDON: + case W_HEADCRACKER: + spd = 0.9; + case W_M2: + spd = 0.67; + case W_MG: + case W_BROWNING: + case W_PANZER: + case W_PTRS: + case W_BARRACUDA: + case W_ACCELERATOR: + case W_PENETRATOR: + spd = 0.75; + case W_FIW: + spd = 0.85; + } + + if (perks & P_STAMIN) + spd *= 1.07; + + return spd; +} + +vector GetWeaponADSPos(float wep) { + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].adsofs; + } + + switch (wep) { + case W_COLT: + return [-2.99, 2.77, 0.92]; + case W_KAR: + case W_ARMAGEDDON: + return [-2.19, 2.77, 1.42]; + case W_THOMPSON: + case W_GIBS: + return [-2.9, 3.16, 1.71]; + case W_357: + case W_KILLU: + return [-2.6, 4.16, 0.5]; + case W_BAR: + case W_WIDOW: + return [-0.6, 2, 1.47]; + case W_BROWNING: + case W_ACCELERATOR: + return [-1.6, 7.7, 6.5]; + case W_DB: + case W_BORE: + case W_SAWNOFF: + return [-0.6, 2.9, 1.47]; + case W_FG: + case W_IMPELLER: + return [-6.5, 7.4, 3.27]; + case W_GEWEHR: + case W_COMPRESSOR: + return [-0.2, 3.28, 2.57]; + case W_KAR_SCOPE: + case W_HEADCRACKER: + return [-2.19, 2.67, 0.42]; + case W_M1: + case W_M1000: + return [-2.6, 1.46, 1.12]; + case W_M1A1: + case W_WIDDER: + return [-0.6, 2.7, 1.94]; + case W_MP40: + case W_AFTERBURNER: + return [-3.79, 6.1, 4.04]; + case W_MG: + case W_BARRACUDA: + return [-3.9, 3.5, 1.84]; + case W_PPSH: + case W_REAPER: + return [-9.79, 5.5, 2.94]; + case W_PTRS: + case W_PENETRATOR: + return [-9.79, 6.6, 2.44]; + case W_STG: + case W_SPATZ: + return [-9.79, 7.3, 4.94]; + case W_TRENCH: + case W_GUT: + return [0, 2.85, 1.4]; + case W_M2: + case W_FIW: + return [0, 0.89, 0.82]; + case W_RAY: + case W_PORTER: + return [-1.50, 6.40, 0.87]; + default: + return [0, 0, 0]; + } +} + +/* +=========================== +Weapon PSP ADS Declarations +=========================== +*/ + +vector GetWeaponADSOfs_PSP(float wep) = +{ + + switch(wep) + { + case W_COLT: + case W_BIATCH: + return [-5479.2, 1650, 0]; + case W_KAR: + case W_ARMAGEDDON: + return [-5495.9, 3186.9, 0]; + case W_KAR_SCOPE: + case W_HEADCRACKER: + return [-5286, 706.1, 0]; + case W_THOMPSON: + case W_GIBS: + return [-6069.3, 3007.6, 0]; + case W_TRENCH: + case W_GUT: + return [-5527.1, 2880.3, 0]; + case W_357: + case W_KILLU: + return [-8306.5, 879.2, 0]; + case W_MG: + case W_BARRACUDA: + return [-6643.7, 3509.2, 0]; + case W_DB: + case W_BORE: + case W_SAWNOFF: + return [-5801.7, 2912.1, 0]; + case W_M1A1: + case W_WIDDER: + return [-5387.8, 3671.9, 0]; + case W_BAR: + case W_WIDOW: + return [-3883.3, 2674.5, 0]; + case W_FG: + case W_IMPELLER: + return [-14800, 6000, 0]; + case W_RAY: + case W_PORTER: + return [-12700, 2086.9, 0]; + case W_GEWEHR: + case W_COMPRESSOR: + return [-6550, 4400, 0]; + case W_PPSH: + case W_REAPER: + return [-11000, 3700, 0]; + case W_MP40: + case W_AFTERBURNER: + return [-12320, 7600, 0]; + case W_TYPE: + case W_SAMURAI: + return [-400, -200, 0]; + case W_STG: + case W_SPATZ: + return [-14534, 8920, 0]; + case W_M1: + case W_M1000: + return [-2860, 2000, 0]; + case W_BROWNING: + case W_ACCELERATOR: + return [-15200, 11620, 0]; + case W_PTRS: + case W_PENETRATOR: + return [-14000, 5386.9, 0]; + case W_PANZER: + case W_LONGINUS: + return [-3479.2, -650, 0]; + default: + //Large values > 20ish cause weapon to flicker, scale model down if we encounter! + //Scale better be 4.3, or else viewbobbing is going to be inaccurate. + return [-5.4792, 1.6500, 0]; + } +}; + +float(float wep, float frametype, optional float z) GetFrame = +{ + switch (wep) + { + case W_COLT: + switch (frametype) + { + case FIRE_START: + #ifndef PC + return 1; + #else + return 0; + #endif + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 26; + case SPRINT_IN_START: + return 33; + case SPRINT_IN_END: + return 34; + case SPRINT_START: + return 35; + case SPRINT_END: + return 40; + case SPRINT_OUT_START: + return 41; + case SPRINT_OUT_END: + return 42; + case TAKE_OUT_START: + return 29; + case TAKE_OUT_END: + return 32; + case PUT_OUT_START: + return 26; + case PUT_OUT_END: + return 29; + case RELOAD_CANCEL: + return 13; + } + break; + case W_BIATCH: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 36; + case SPRINT_IN_START: + return 33; + case SPRINT_IN_END: + return 34; + case SPRINT_START: + return 44; + case SPRINT_END: + return 43; + case SPRINT_OUT_START: + return 50; + case SPRINT_OUT_END: + return 42; + case TAKE_OUT_START: + return 39; + case TAKE_OUT_END: + return 42; + case PUT_OUT_START: + return 36; + case PUT_OUT_END: + return 39; + case RELOAD_CANCEL: + return 13; + } + break; + case W_KAR: + case W_ARMAGEDDON: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case RELOAD_START: + return 14; + case RELOAD_END: + return 28; + case SPRINT_IN_START: + return 29; + case SPRINT_IN_END: + return 30; + case SPRINT_OUT_START: + return 30; + case SPRINT_OUT_END: + return 29; + case TAKE_OUT_START: + return 33; + case TAKE_OUT_END: + return 35; + case PUT_OUT_START: + return 32; + case PUT_OUT_END: + return 33; + case RELOAD_CANCEL: + return 21; + } + break; + case W_THOMPSON: + case W_GIBS: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 25; + case SPRINT_IN_START: + return 26; + case SPRINT_IN_END: + return 27; + case SPRINT_OUT_START: + return 28; + case SPRINT_OUT_END: + return 29; + case TAKE_OUT_START: + return 32; + case TAKE_OUT_END: + return 35; + case PUT_OUT_START: + return 29; + case PUT_OUT_END: + return 32; + case RELOAD_CANCEL: + return 15; + } + break; + case W_357: + case W_KILLU: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case RELOAD_START: + return 4; + case RELOAD_END: + return 25; + case SPRINT_IN_START: + return 32; + case SPRINT_IN_END: + return 33; + case SPRINT_START: + return 35; + case SPRINT_END: + return 40; + case SPRINT_OUT_START: + return 41; + case SPRINT_OUT_END: + return 42; + case TAKE_OUT_START: + return 29; + case TAKE_OUT_END: + return 31; + case PUT_OUT_START: + return 26; + case PUT_OUT_END: + return 28; + case RELOAD_CANCEL: + return 14; + } + break; + case W_BAR: + case W_WIDOW: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 36; + case SPRINT_IN_START: + return 37; + case SPRINT_IN_END: + return 38; + case SPRINT_OUT_START: + return 38; + case SPRINT_OUT_END: + return 37; + case TAKE_OUT_START: + return 41; + case TAKE_OUT_END: + return 39; + case PUT_OUT_START: + return 39; + case PUT_OUT_END: + return 41; + case RELOAD_CANCEL: + return 21; + } + break; + case W_BROWNING: + case W_ACCELERATOR: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case RELOAD_START: + return 4; + case RELOAD_END: + return 37; + case SPRINT_IN_START: + return 92; + case SPRINT_IN_END: + return 93; + case SPRINT_OUT_START: + return 95; + case SPRINT_OUT_END: + return 96; + case TAKE_OUT_START: + return 100; + case TAKE_OUT_END: + return 103; + case PUT_OUT_START: + return 97; + case PUT_OUT_END: + return 100; + case RELOAD_CANCEL: + return 16; + } + break; + case W_SAWNOFF: + case W_DB: + case W_BORE: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 4; + case RELOAD_START: + return 5; + case RELOAD_END: + return 31; + case SPRINT_IN_START: + return 32; + case SPRINT_IN_END: + return 33; + case SPRINT_OUT_START: + return 33; + case SPRINT_OUT_END: + return 31; + case TAKE_OUT_START: + return 38; + case TAKE_OUT_END: + return 41; + case PUT_OUT_START: + return 35; + case PUT_OUT_END: + return 37; + case RELOAD_CANCEL: + return 22; + } + break; + case W_SNUFF: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 4; + case RELOAD_START: + return 5; + case RELOAD_END: + return 45; + case SPRINT_IN_START: + return 53; + case SPRINT_IN_END: + return 58; + case SPRINT_OUT_START: + return 33; + case SPRINT_OUT_END: + return 31; + case TAKE_OUT_START: + return 46; + case TAKE_OUT_END: + return 49; + case PUT_OUT_START: + return 59; + case PUT_OUT_END: + return 52; + case RELOAD_CANCEL: + return 34; + } + break; + case W_FG: + case W_IMPELLER: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case RELOAD_START: + return 41; + case RELOAD_END: + return 88; + case SPRINT_IN_START: + return 29; + case SPRINT_IN_END: + return 33; + case SPRINT_OUT_START: + return 34; + case SPRINT_OUT_END: + return 37; + case TAKE_OUT_START: + return 8; + case TAKE_OUT_END: + return 15; + case PUT_OUT_START: + return 3; + case PUT_OUT_END: + return 7; + case RELOAD_CANCEL: + return 64; + } + break; + case W_GEWEHR: + case W_COMPRESSOR: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 38; + case SPRINT_IN_START: + return 49; + case SPRINT_IN_END: + return 49; + case SPRINT_OUT_START: + return 51; + case SPRINT_OUT_END: + return 52; + case TAKE_OUT_START: + return 55; + case TAKE_OUT_END: + return 59; + case PUT_OUT_START: + return 53; + case PUT_OUT_END: + return 56; + case RELOAD_CANCEL: + return 16; + } + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case SPRINT_IN_START: + return 36; + case SPRINT_IN_END: + return 37; + case SPRINT_OUT_START: + return 37; + case SPRINT_OUT_END: + return 36; + case TAKE_OUT_START: + return 33; + case TAKE_OUT_END: + return 35; + case PUT_OUT_START: + return 32; + case PUT_OUT_END: + return 33; + case RELOAD_CANCEL: + return 21; + } + break; + case W_M1: + case W_M1000: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case RELOAD_START: + return 8; + case RELOAD_END: + return 44; + case SPRINT_IN_START: + return 58; + case SPRINT_IN_END: + return 59; + case SPRINT_OUT_START: + return 60; + case SPRINT_OUT_END: + return 61; + case TAKE_OUT_START: + return 59; + case TAKE_OUT_END: + return 63; + case PUT_OUT_START: + return 56; + case PUT_OUT_END: + return 59; + case RELOAD_CANCEL: + return 16; + } + break; + case W_M1A1: + case W_WIDDER: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 29; + case SPRINT_IN_START: + return 37; + case SPRINT_IN_END: + return 38; + case SPRINT_OUT_START: + return 38; + case SPRINT_OUT_END: + return 37; + case TAKE_OUT_START: + return 33; + case TAKE_OUT_END: + return 36; + case PUT_OUT_START: + return 30; + case PUT_OUT_END: + return 33; + case RELOAD_CANCEL: + return 17; + } + break; + case W_FIW: + case W_M2: + switch (frametype) + { + case FIRE_START: + return 0; + case FIRE_END: + return 0; + case SPRINT_IN_START: + return 0; + case SPRINT_IN_END: + return 0; + case SPRINT_OUT_START: + return 8; + case SPRINT_OUT_END: + return 0; + case TAKE_OUT_START: + return 2; + case TAKE_OUT_END: + return 1; + case PUT_OUT_START: + return 1; + case PUT_OUT_END: + return 2; + case RELOAD_CANCEL: + return 0; + } + break; + case W_MP40: + case W_AFTERBURNER: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 21; + case SPRINT_IN_START: + return 37; + case SPRINT_IN_END: + return 38; + case SPRINT_OUT_START: + return 39; + case SPRINT_OUT_END: + return 40; + case TAKE_OUT_START: + return 32; + case TAKE_OUT_END: + return 35; + case PUT_OUT_START: + return 30; + case PUT_OUT_END: + return 32; + case RELOAD_CANCEL: + return 16; + } + break; + case W_MG: + case W_BARRACUDA: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 38; + case SPRINT_IN_START: + return 39; + case SPRINT_IN_END: + return 40; + case SPRINT_OUT_START: + return 40; + case SPRINT_OUT_END: + return 38; + case TAKE_OUT_START: + return 43; + case TAKE_OUT_END: + return 47; + case PUT_OUT_START: + return 42; + case PUT_OUT_END: + return 43; + case RELOAD_CANCEL: + return 21; + } + break; + case W_PANZER: + case W_LONGINUS: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 6; + case RELOAD_START: + return 7; + case RELOAD_END: + return 28; + case SPRINT_IN_START: + return 37; + case SPRINT_IN_END: + return 38; + case SPRINT_OUT_START: + return 45; + case SPRINT_OUT_END: + return 45; + case TAKE_OUT_START: + return 44; + case TAKE_OUT_END: + return 45; + case PUT_OUT_START: + return 39; + case PUT_OUT_END: + return 42; + case RELOAD_CANCEL: + return 16; + case AIM_IN_END: + return 33; + } + break; + case W_PPSH: + case W_REAPER: + switch (frametype) + { + case FIRE_START: + return 0; + case FIRE_END: + return 0; + case RELOAD_START: + return 4; + case RELOAD_END: + return 34; + case SPRINT_IN_START: + return 44; + case SPRINT_IN_END: + return 45; + case SPRINT_OUT_START: + return 47; + case SPRINT_OUT_END: + return 49; + case TAKE_OUT_START: + return 46; + case TAKE_OUT_END: + return 49; + case PUT_OUT_START: + return 43; + case PUT_OUT_END: + return 46; + case RELOAD_CANCEL: + return 16; + } + break; + case W_PTRS: + case W_PENETRATOR: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 4; + case RELOAD_START: + return 5; + case RELOAD_END: + return 32; + case SPRINT_IN_START: + return 48; + case SPRINT_IN_END: + return 50; + case SPRINT_OUT_START: + return 51; + case SPRINT_OUT_END: + return 54; + case TAKE_OUT_START: + return 44; + case TAKE_OUT_END: + return 47; + case PUT_OUT_START: + return 41; + case PUT_OUT_END: + return 44; + case RELOAD_CANCEL: + return 16; + } + break; + case W_RAY: + case W_PORTER: + switch (frametype) + { + case FIRE_START: + return 3; + case FIRE_END: + return 7; + case RELOAD_START: + return 9; + case RELOAD_END: + return 54; + case SPRINT_IN_START: + return 60; + case SPRINT_IN_END: + return 64; + case SPRINT_OUT_START: + return 70; + case SPRINT_OUT_END: + return 73; + case TAKE_OUT_START: + return 57; + case TAKE_OUT_END: + return 59; + case PUT_OUT_START: + return 54; + case PUT_OUT_END: + return 56; + case RELOAD_CANCEL: + return 36; + } + break; + case W_STG: + case W_SPATZ: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 2; + case RELOAD_START: + return 3; + case RELOAD_END: + return 24; + case SPRINT_IN_START: + return 40; + case SPRINT_IN_END: + return 42; + case SPRINT_OUT_START: + return 43; + case SPRINT_OUT_END: + return 45; + case TAKE_OUT_START: + return 42; + case TAKE_OUT_END: + return 46; + case PUT_OUT_START: + return 39; + case PUT_OUT_END: + return 42; + case RELOAD_CANCEL: + return 16; + } + break; + case W_TRENCH: + case W_GUT: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 3; + case SPRINT_IN_START: + return 27; + case SPRINT_IN_END: + return 28; + case SPRINT_OUT_START: + return 27; + case SPRINT_OUT_END: + return 26; + case TAKE_OUT_START: + return 32; + case TAKE_OUT_END: + return 36; + case PUT_OUT_START: + return 30; + case PUT_OUT_END: + return 32; + case RELOAD_CANCEL: + return 20; + } + break; + case W_TYPE: + case W_SAMURAI: + switch (frametype) + { + case FIRE_START: + return 0; + case FIRE_END: + return 1; + case RELOAD_START: + return 2; + case RELOAD_END: + return 24; + case SPRINT_IN_START: + return 35; + case SPRINT_IN_END: + return 37; + case SPRINT_OUT_START: + return 38; + case SPRINT_OUT_END: + return 40; + case TAKE_OUT_START: + return 38; + case TAKE_OUT_END: + return 41; + case PUT_OUT_START: + return 35; + case PUT_OUT_END: + return 38; + case RELOAD_CANCEL: + return 16; + case AIM_IN_END: + return 28; + case AIM_IN_START: + return 27; + } + break; + case W_TESLA: + case W_DG3: + switch (frametype) + { + case FIRE_START: + return 1; + case FIRE_END: + return 4; + case RELOAD_START: + return 9; + case RELOAD_END: + return 58; + case SPRINT_IN_START: + return 60; + case SPRINT_IN_END: + return 63; + case SPRINT_OUT_START: + return 63; + case SPRINT_OUT_END: + return 66; + case TAKE_OUT_START: + return 72; + case TAKE_OUT_END: + return 76; + case PUT_OUT_START: + return 66; + case PUT_OUT_END: + return 71; + case RELOAD_CANCEL: + return 16; + } + break; + } + + return 0; +} + +string(float wep, float gorvmodel) GetWeaponModel = +{ + + if (isCustomWeapon(wep)) { + if (gorvmodel) + return CustomWeapons[wep - 70].gmodel; + else + return CustomWeapons[wep - 70].vmodel; + } + + switch (wep) + { + case W_COLT: + if (gorvmodel) + return ("models/weapons/m1911/g_colt.mdl"); + else + return ("models/weapons/m1911/v_colt.mdl"); + case W_BIATCH: + if (gorvmodel) + return ("models/weapons/m1911/g_colt.mdl"); + else + return ("models/weapons/m1911/v_biatch_right.mdl"); + case W_KAR: + case W_ARMAGEDDON: + if (gorvmodel) + return ("models/weapons/kar/g_kar.mdl"); + else + return ("models/weapons/kar/v_kar.mdl"); + case W_THOMPSON: + case W_GIBS: + if (gorvmodel) + return ("models/weapons/thomp/g_thomp.mdl"); + else + return ("models/weapons/thomp/v_thomp.mdl"); + case W_357: + case W_KILLU: + if (gorvmodel) + return ("models/weapons/357/g_357.mdl"); + else + return ("models/weapons/357/v_357.mdl"); + case W_BAR: + case W_WIDOW: + if (gorvmodel) + return ("models/weapons/bar/g_bar.mdl"); + else + return ("models/weapons/bar/v_bar.mdl"); + case W_BROWNING: + case W_ACCELERATOR: + if (gorvmodel) + return ("models/weapons/browning/g_browning.mdl"); + else + return ("models/weapons/browning/v_browning.mdl"); + case W_DB: + case W_BORE: + if (gorvmodel) + return ("models/weapons/db/g_db.mdl"); + else + return ("models/weapons/db/v_db.mdl"); + case W_FG: + case W_IMPELLER: + if (gorvmodel) + return ("models/weapons/fg42/g_fg.mdl"); + else + return ("models/weapons/fg42/v_fg.mdl"); + case W_GEWEHR: + case W_COMPRESSOR: + if (gorvmodel) + return ("models/weapons/gewehr/g_gewehr.mdl"); + else + return ("models/weapons/gewehr/v_gewehr.mdl"); + case W_KAR_SCOPE: + case W_HEADCRACKER: + if (gorvmodel) + return ("models/weapons/kar/g_kars.mdl"); + else + return ("models/weapons/kar/v_kar.mdl"); + case W_M1: + case W_M1000: + if (gorvmodel) + return ("models/weapons/garand/g_m1.mdl"); + else + return ("models/weapons/garand/v_m1.mdl"); + case W_M1A1: + case W_WIDDER: + if (gorvmodel) + return ("models/weapons/m1carbine/g_m1a1.mdl"); + else + return ("models/weapons/m1carbine/v_m1a1.mdl"); + case W_M2: + case W_FIW: + if (gorvmodel) + return ("models/weapons/m2flame/g_m2flame.mdl"); + else + return ("models/weapons/m2flame/v_m2flame.mdl"); + case W_MP40: + case W_AFTERBURNER: + if (gorvmodel) + return ("models/weapons/mp40/g_mp40.mdl"); + else + return ("models/weapons/mp40/v_mp40.mdl"); + case W_MG: + case W_BARRACUDA: + if (gorvmodel) + return ("models/weapons/mg/g_mg.mdl"); + else + return ("models/weapons/mg/v_mg.mdl"); + case W_PANZER: + case W_LONGINUS: + if (gorvmodel) + return ("models/weapons/panzer/g_panzer.mdl"); + else + return ("models/weapons/panzer/v_panzer.mdl"); + case W_PPSH: + case W_REAPER: + if (gorvmodel) + return ("models/weapons/ppsh/g_ppsh.mdl"); + else + return ("models/weapons/ppsh/v_ppsh.mdl"); + case W_PTRS: + case W_PENETRATOR: + if (gorvmodel) + return ("models/weapons/ptrs/g_ptrs.mdl"); + else + return ("models/weapons/ptrs/v_ptrs.mdl"); + case W_RAY: + case W_PORTER: + if (gorvmodel) + return ("models/weapons/ray/g_ray.mdl"); + else + return ("models/weapons/ray/v_ray.mdl"); + case W_SAWNOFF: + if (gorvmodel) + return ("models/weapons/sawnoff/g_sawnoff.mdl"); + else + return ("models/weapons/sawnoff/v_sawnoff.mdl"); + case W_SNUFF: + if (gorvmodel) + return ("models/weapons/sawnoff/g_sawnoff.mdl"); + else + return ("models/weapons/sawnoff/v_snuff_right.mdl"); + case W_STG: + case W_SPATZ: + if (gorvmodel) + return ("models/weapons/stg/g_stg.mdl"); + else + return ("models/weapons/stg/v_stg.mdl"); + case W_TRENCH: + case W_GUT: + if (gorvmodel) + return ("models/weapons/trench/g_trench.mdl"); + else + return ("models/weapons/trench/v_trench.mdl"); + case W_TYPE: + case W_SAMURAI: + if (gorvmodel) + return ("models/weapons/type/g_type.mdl"); + else + return ("models/weapons/type/v_type.mdl"); + case W_GRENADE: + return ("models/weapons/grenade/g_grenade.mdl"); + case W_BETTY: + return ("models/weapons/grenade/g_betty.mdl"); + case W_DG3: + case W_TESLA: + if (gorvmodel) + return ("models/weapons/tesla/g_tesla.mdl"); + else + return ("models/weapons/tesla/v_tesla.mdl"); + case W_BK: + if (gorvmodel) + return ("models/weapons/bk/g_bk.mdl"); + else + return ("models/weapons/bk/v_bk.mdl"); + } + + return (""); +} + +string(float wep) GetWeapon2Model = +{ + + // naievil -- fixme, add support for custom weapon2models + /* + if (isCustomWeapon(wep)) { + if (gorvmodel) + return CustomWeapons[wep - 70].gmodel; + else + return CustomWeapons[wep - 70].vmodel; + } + */ + + switch (wep) + { + case W_BIATCH: + return ("models/weapons/m1911/v_biatch_left.mdl"); + case W_KAR_SCOPE: + case W_HEADCRACKER: + return ("models/weapons/kar/v_karscope.mdl"); + case W_SNUFF: + return ("models/weapons/sawnoff/v_snuff_left.mdl"); + default: + return ""; + } + + return (""); +} + +string(float wep) GetWeaponSound = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].firesound; + } + + switch (wep) + { + case W_BIATCH: + case W_COLT: + return "sounds/weapons/colt/shoot.wav"; + case W_KAR: + case W_ARMAGEDDON: + return "sounds/weapons/kar/shoot.wav"; + case W_THOMPSON: + case W_GIBS: + return "sounds/weapons/thomp/shoot.wav"; + case W_357: + case W_KILLU: + return "sounds/weapons/357/shoot.wav"; + case W_BAR: + case W_WIDOW: + return "sounds/weapons/bar/shoot.wav"; + case W_BROWNING: + case W_ACCELERATOR: + return "sounds/weapons/browning/shoot.wav"; + case W_DB: + case W_BORE: + return "sounds/weapons/shotgun/shoot.wav"; + case W_FG: + case W_IMPELLER: + return "sounds/weapons/fg42/shoot.wav"; + case W_GEWEHR: + case W_COMPRESSOR: + return "sounds/weapons/gewehr/shoot.wav"; + case W_KAR_SCOPE: + case W_HEADCRACKER: + return "sounds/weapons/kar/shoot.wav"; + case W_M1: + case W_M1000: + return "sounds/weapons/garand/shoot.wav"; + case W_M1A1: + case W_WIDDER: + return "sounds/weapons/m1carbine/shoot.wav"; + case W_M2: + return "sounds/weapons/m2flame/flamethrower.wav"; + case W_MP40: + case W_AFTERBURNER: + return "sounds/weapons/mp40/shoot.wav"; + case W_MG: + case W_BARRACUDA: + return "sounds/weapons/mg42/shoot.wav"; + case W_PANZER: + case W_LONGINUS: + return "sounds/weapons/panzer/shoot.wav"; + case W_PPSH: + case W_REAPER: + return "sounds/weapons/ppsh/shoot.wav"; + case W_PTRS: + case W_PENETRATOR: + return "sounds/weapons/ptrs/shoot.wav"; + case W_RAY: + case W_PORTER: + return "sounds/weapons/raygun/shoot.wav"; + case W_SAWNOFF: + case W_SNUFF: + return "sounds/weapons/shotgun/shoot.wav"; + case W_STG: + case W_SPATZ: + return "sounds/weapons/stg/shoot.wav"; + case W_TRENCH: + case W_GUT: + return "sounds/weapons/shotgun/shoot.wav"; + case W_TYPE: + case W_SAMURAI: + return "sounds/weapons/type100/shoot.wav"; + case W_TESLA: + case W_DG3: + return "sounds/weapons/tesla/shoot.wav"; + } + + return ""; +} + +float(float wep) IsDualWeapon = { + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].isDual; + } + + switch(wep) { + case W_BIATCH: + return 1; + case W_SNUFF: + return 1; + default: + return 0; + } +} + +string(float wep) GetLeftWeaponModel = { + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].vmodel2; + } + + switch(wep) { + case W_BIATCH: + return "models/weapons/m1911/v_biatch_left.mdl"; + case W_SNUFF: + return "models/weapons/sawnoff/v_snuff_left.mdl"; + default: + return ""; + } +} + +float(float wep) IsPapWeapon = { + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].ispap; + } + + switch(wep) { + case W_BIATCH: + case W_ARMAGEDDON: + case W_GIBS: + case W_KILLU: + case W_WIDOW: + case W_ACCELERATOR: + case W_BORE: + case W_IMPELLER: + case W_COMPRESSOR: + case W_HEADCRACKER: + case W_M1000: + case W_WIDDER: + case W_AFTERBURNER: + case W_BARRACUDA: + case W_LONGINUS: + case W_REAPER: + case W_PENETRATOR: + case W_PORTER: + case W_SNUFF: + case W_SPATZ: + case W_GUT: + case W_SAMURAI: + case W_DG3: + return 1; + default: + return 0; + } +} + +float(float wep) GetWepSkin = { + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].skin; + } + + switch(wep) { + case W_BIATCH: + return 0; + case W_SNUFF: + return 0; + default: + if (IsPapWeapon(wep)) { + return 1; + } else { + return 0; + } + } +} + + +float(float wep) EqualNonPapWeapon = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].nonpap; + } + + switch (wep) { + case W_BIATCH: + return W_COLT; + case W_ARMAGEDDON: + return W_KAR; + case W_GIBS: + return W_THOMPSON; + case W_KILLU: + return W_357; + case W_WIDOW: + return W_BAR; + case W_ACCELERATOR: + return W_BROWNING; + case W_BORE: + return W_DB; + case W_IMPELLER: + return W_FG; + case W_COMPRESSOR: + return W_GEWEHR; + case W_HEADCRACKER: + return W_KAR_SCOPE; + case W_M1000: + return W_M1; + case W_WIDDER: + return W_M1A1; + case W_AFTERBURNER: + return W_MP40; + case W_BARRACUDA: + return W_MG; + case W_LONGINUS: + return W_PANZER; + case W_REAPER: + return W_PPSH; + case W_PENETRATOR: + return W_PTRS; + case W_PORTER: + return W_RAY; + case W_SNUFF: + return W_SAWNOFF; + case W_SPATZ: + return W_STG; + case W_GUT: + return W_TRENCH; + case W_SAMURAI: + return W_TYPE; + case W_MP5: + return W_MP5; + case W_M14: + return W_M14; + case W_CUSTOM2: + return W_CUSTOM1; + case W_CUSTOM4: + return W_CUSTOM3; + case W_DG3: + return W_TESLA; + case W_FIW: + return W_M2; + default: + return 0; + } +} +float(float wep) EqualPapWeapon = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].pap; + } + + switch (wep) { + case W_COLT: + return W_BIATCH; + case W_KAR: + return W_ARMAGEDDON; + case W_THOMPSON: + return W_GIBS; + case W_357: + return W_KILLU; + case W_BAR: + return W_WIDOW; + case W_BROWNING: + return W_ACCELERATOR; + case W_DB: + return W_BORE; + case W_FG: + return W_IMPELLER; + case W_GEWEHR: + return W_COMPRESSOR; + case W_KAR_SCOPE: + return W_HEADCRACKER; + case W_M1: + return W_M1000; + case W_M1A1: + return W_WIDDER; + case W_MP40: + return W_AFTERBURNER; + case W_MG: + return W_BARRACUDA; + case W_PANZER: + return W_LONGINUS; + case W_PPSH: + return W_REAPER; + case W_PTRS: + return W_PENETRATOR; + case W_RAY: + return W_PORTER; + case W_SAWNOFF: + return W_SNUFF; + case W_STG: + return W_SPATZ; + case W_TRENCH: + return W_GUT; + case W_TYPE: + return W_SAMURAI; + case W_MP5: + return W_MP5; + case W_TESLA: + return W_DG3; + case W_M14: + return W_M14; + case W_M2: + return W_FIW; + default: + return wep; + } +} + +void (float wep, float anim_style, float dualwep, float curweaponframe) PlayWeaponSound = +{ + if (anim_style == KNIFE) { + if (curweaponframe == 6) { + self.punchangle_x = -2; + self.punchangle_y = -5; + } else if (curweaponframe == 7) { + self.punchangle_x = 5; + self.punchangle_y = 10; + } + } + + if (anim_style == FIRE) + { + if (wep == W_KAR || wep == W_ARMAGEDDON || wep == W_KAR_SCOPE || wep == W_HEADCRACKER) + { + if (curweaponframe == 6) + sound (self ,5, "sounds/weapons/kar/boltup.wav", 1, ATTN_NORM); + else if (curweaponframe == 7) + sound (self ,5, "sounds/weapons/kar/boltback.wav", 1, ATTN_NORM); + else if (curweaponframe == 9) + sound (self ,5, "sounds/weapons/kar/boltforward.wav", 1, ATTN_NORM); + else if (curweaponframe == 10) + sound (self ,5, "sounds/weapons/kar/boltdown.wav", 1, ATTN_NORM); + } + else if (wep == W_TRENCH || wep == W_GUT) + { + if (curweaponframe == 7) + sound (self ,5, "sounds/weapons/shotgun/pump.wav", 1, ATTN_NORM); + } + } else if (anim_style != RELOAD) { + return; + } + + + switch (wep) { + case W_COLT: + if (curweaponframe == 5) { + sound (self ,5, "sounds/weapons/colt/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 12) { + sound (self ,5, "sounds/weapons/colt/magin.wav", 1, ATTN_NORM); + } else if(curweaponframe == 18) { + sound (self ,5, "sounds/weapons/colt/slide.wav", 1, ATTN_NORM); + } + break; + case W_KAR: + case W_ARMAGEDDON: + if (curweaponframe == 15) { + sound (self ,5, "sounds/weapons/kar/boltup.wav", 1, ATTN_NORM); + } else if (curweaponframe == 16) { + sound (self ,5, "sounds/weapons/kar/boltback.wav", 1, ATTN_NORM); + } else if (curweaponframe == 20) { + sound (self ,5, "sounds/weapons/kar/clipin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 25) { + sound (self ,5, "sounds/weapons/kar/boltforward.wav", 1, ATTN_NORM); + } else if (curweaponframe == 26) { + sound (self ,5, "sounds/weapons/kar/clipoff.wav", 1, ATTN_NORM); + } else if (curweaponframe == 27) { + sound (self ,5, "sounds/weapons/kar/boltdown.wav", 1, ATTN_NORM); + } + break; + case W_THOMPSON: + case W_GIBS: + if (curweaponframe == 6) { + sound (self ,5, "sounds/weapons/thomp/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 12) { + sound (self ,5, "sounds/weapons/thomp/magin.wav", 1, ATTN_NORM);} + else if (curweaponframe == 19) { + sound (self ,5, "sounds/weapons/thomp/boltback.wav", 1, ATTN_NORM); + } + break; + case W_357: + case W_KILLU: + if (curweaponframe == 5) { + sound (self ,5, "sounds/weapons/357/open.wav", 1, ATTN_NORM); + } else if (curweaponframe == 9) { + sound (self ,5, "sounds/weapons/357/out.wav", 1, ATTN_NORM); + } else if (curweaponframe == 15) { + sound (self ,5, "sounds/weapons/357/in.wav", 1, ATTN_NORM); + } else if (curweaponframe == 20) { + sound (self ,5, "sounds/weapons/357/close.wav", 1, ATTN_NORM); + } + break; + case W_BAR: + case W_WIDOW: + if (curweaponframe == 6 || curweaponframe == 31) { + sound (self ,5, "sounds/weapons/bar/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 19 || curweaponframe == 29) { + sound (self ,5, "sounds/weapons/bar/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 23) { + sound (self ,5, "sounds/weapons/bar/maghit.wav", 1, ATTN_NORM); + } + break; + case W_BROWNING: + case W_ACCELERATOR: + if (curweaponframe == 9) { + sound (self ,5, "sounds/weapons/browning/boltback.wav", 1, ATTN_NORM); + } else if (curweaponframe == 16) { + sound (self ,5, "sounds/weapons/browning/topopen.wav", 1, ATTN_NORM); + } else if (curweaponframe == 22) { + sound (self ,5, "sounds/weapons/browning/chainoff.wav", 1, ATTN_NORM); + } else if (curweaponframe == 32) { + sound (self ,5, "sounds/weapons/browning/boltback.wav", 1, ATTN_NORM); + } else if (curweaponframe == 39) { + sound (self ,5, "sounds/weapons/browning/topopen.wav", 1, ATTN_NORM); + } else if (curweaponframe == 49) { + sound (self ,5, "sounds/weapons/browning/chainon.wav", 1, ATTN_NORM); + } else if (curweaponframe == 53) { + sound (self ,5, "sounds/weapons/browning/chainplace.wav", 1, ATTN_NORM); + } else if (curweaponframe == 58) { + sound (self ,5, "sounds/weapons/browning/topclose.wav", 1, ATTN_NORM); + } else if (curweaponframe == 61) { + sound (self ,5, "sounds/weapons/browning/tophit.wav", 1, ATTN_NORM); + } else if (curweaponframe == 70) { + sound (self ,5, "sounds/weapons/browning/boltforward.wav", 1, ATTN_NORM); + } + break; + case W_DB: + case W_SAWNOFF: + case W_BORE: + if (curweaponframe == 6) { + sound (self ,5, "sounds/weapons/shotgun/open.wav", 1, ATTN_NORM); + } else if (curweaponframe == 12) { + sound (self ,5, "sounds/weapons/shotgun/out.wav", 1, ATTN_NORM); + } else if (curweaponframe == 20) { + sound (self ,5, "sounds/weapons/shotgun/in.wav", 1, ATTN_NORM); + } else if (curweaponframe == 26) { + sound (self ,5, "sounds/weapons/shotgun/close.wav", 1, ATTN_NORM); + } + break; + case W_SNUFF: + if (curweaponframe == 15) { + sound (self ,5, "sounds/weapons/shotgun/open.wav", 1, ATTN_NORM); + } else if (curweaponframe == 23) { + sound (self ,5, "sounds/weapons/shotgun/out.wav", 1, ATTN_NORM); + } else if (curweaponframe == 33) { + sound (self ,5, "sounds/weapons/shotgun/in.wav", 1, ATTN_NORM); + } else if (curweaponframe == 40) { + sound (self ,5, "sounds/weapons/shotgun/close.wav", 1, ATTN_NORM); + } + break; + case W_FG: + case W_IMPELLER: + if (curweaponframe == 43) { + sound (self ,5, "sounds/weapons/fg42/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 60) { + sound (self ,5, "sounds/weapons/fg42/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 72) { + sound (self ,5, "sounds/weapons/fg42/boltback.wav", 1, ATTN_NORM); + } else if (curweaponframe == 77) { + sound (self ,5, "sounds/weapons/fg42/boltforward.wav", 1, ATTN_NORM); + } + break; + case W_GEWEHR: + case W_COMPRESSOR: + if (curweaponframe == 10) { + sound (self ,5, "sounds/weapons/gewehr/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 24) { + sound (self ,5, "sounds/weapons/gewehr/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 27) { + sound (self ,5, "sounds/weapons/gewehr/maghit.wav", 1, ATTN_NORM); + } else if (curweaponframe == 33) { + sound (self ,5, "sounds/weapons/gewehr/boltrelease.wav", 1, ATTN_NORM); + } + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + if (curweaponframe == 15) { + sound (self ,5, "sounds/weapons/kar/boltup.wav", 1, ATTN_NORM); + } else if (curweaponframe == 16) { + sound (self ,5, "sounds/weapons/kar/boltback.wav", 1, ATTN_NORM); + } else if (curweaponframe == 20) { + sound (self ,5, "sounds/weapons/kar/insert.wav", 1, ATTN_NORM); + } else if (curweaponframe == 25) { + sound (self ,5, "sounds/weapons/kar/boltforward.wav", 1, ATTN_NORM); + } else if (curweaponframe == 27) { + sound (self ,5, "sounds/weapons/kar/boltdown.wav", 1, ATTN_NORM); + } + break; + case W_M1: + case W_M1000: + if (curweaponframe == 6) { + sound (self ,5, "sounds/weapons/garand/clipout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 20) { + sound (self ,5, "sounds/weapons/garand/clipin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 26) { + sound (self ,5, "sounds/weapons/garand/clippush.wav", 1, ATTN_NORM); + } else if (curweaponframe == 37) { + sound (self ,5, "sounds/weapons/garand/boltrelease.wav", 1, ATTN_NORM); + } + break; + case W_M1A1: + case W_WIDDER: + if (curweaponframe == 6) { + sound (self ,5, "sounds/weapons/m1carbine/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 14) { + sound (self ,5, "sounds/weapons/m1carbine/magin.wav", 1, ATTN_NORM); + } else if(curweaponframe == 16) { + sound (self ,5, "sounds/weapons/m1carbine/maghit.wav", 1, ATTN_NORM); + } else if (curweaponframe == 24) { + sound (self ,5, "sounds/weapons/m1carbine/bolt.wav", 1, ATTN_NORM); + } + break; + case W_MP40: + case W_AFTERBURNER: + if (curweaponframe == 10) { + sound (self ,5, "sounds/weapons/mp40/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 17) { + sound (self ,5, "sounds/weapons/mp40/magin.wav", 1, ATTN_NORM); + } + break; + case W_PANZER: + case W_LONGINUS: + if (curweaponframe == 12) { + sound (self ,5, "sounds/weapons/panzer/move.wav", 1, ATTN_NORM); + } else if (curweaponframe == 16) { + sound (self ,5, "sounds/weapons/panzer/insert.wav", 1, ATTN_NORM); + } + break; + case W_MG: + case W_BARRACUDA: + if (curweaponframe == 8) { + sound (self ,5, "sounds/weapons/mg42/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 19) { + sound (self ,5, "sounds/weapons/mg42/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 29) { + sound (self ,5, "sounds/weapons/mg42/charge.wav", 1, ATTN_NORM); + } + break; + case W_PPSH: + case W_REAPER: + if (curweaponframe == 7) { + sound (self ,5, "sounds/weapons/ppsh/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 15) { + sound (self ,5, "sounds/weapons/ppsh/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 19) { + sound (self ,5, "sounds/weapons/ppsh/maghit.wav", 1, ATTN_NORM); + } else if (curweaponframe == 26) { + sound (self ,5, "sounds/weapons/ppsh/boltback.wav", 1, ATTN_NORM); + } else if (curweaponframe == 28) { + sound (self ,5, "sounds/weapons/ppsh/boltrelease.wav", 1, ATTN_NORM); + } + break; + case W_PTRS: + case W_PENETRATOR: + if (curweaponframe == 6) { + sound (self ,5, "sounds/weapons/ptrs/open.wav", 1, ATTN_NORM); + } else if (curweaponframe == 18) { + sound (self ,5, "sounds/weapons/ptrs/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 21) { + sound (self ,5, "sounds/weapons/ptrs/maghit.wav", 1, ATTN_NORM); + } else if (curweaponframe == 27) { + sound (self ,5, "sounds/weapons/ptrs/close.wav", 1, ATTN_NORM); + } + break; + case W_RAY: + case W_PORTER: + if (curweaponframe == 14) { + sound (self ,5, "sounds/weapons/raygun/open.wav", 1, ATTN_NORM); + } else if (curweaponframe == 23) { + sound (self ,5, "sounds/weapons/raygun/out.wav", 1, ATTN_NORM); + } else if (curweaponframe == 36) { + sound (self ,5, "sounds/weapons/raygun/in.wav", 1, ATTN_NORM); + } else if (curweaponframe == 45) { + sound (self ,5, "sounds/weapons/raygun/close.wav", 1, ATTN_NORM); + } + break; + case W_STG: + case W_SPATZ: + if (curweaponframe == 11) { + sound (self ,5, "sounds/weapons/stg/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 21) { + sound (self ,5, "sounds/weapons/stg/magin.wav", 1, ATTN_NORM); + } + break; + case W_TRENCH: + case W_GUT: + if (curweaponframe == 19) { + sound (self ,5, "sounds/weapons/shotgun/insert.wav", 1, ATTN_NORM); + } + break; + case W_TYPE: + case W_SAMURAI: + if (curweaponframe == 6) { + sound (self ,5, "sounds/weapons/type100/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 15) { + sound (self ,5, "sounds/weapons/type100/magin.wav", 1, ATTN_NORM); + } else if (curweaponframe == 20) { + sound (self ,5, "sounds/weapons/type100/boltpull.wav", 1, ATTN_NORM); + } + break; + case W_BIATCH: + if (curweaponframe == 10) { + sound (self ,5, "sounds/weapons/colt/magout.wav", 1, ATTN_NORM); + } else if (curweaponframe == 19) { + sound (self ,5, "sounds/weapons/colt/magin.wav", 1, ATTN_NORM); + } else if(curweaponframe == 26) { + sound (self ,5, "sounds/weapons/colt/slide.wav", 1, ATTN_NORM); + } + break; + case W_TESLA: + case W_DG3: + if (curweaponframe == 20) + sound (self ,5, "sounds/weapons/tesla/switchoff.wav", 1, ATTN_NORM); + else if (curweaponframe == 25) + sound (self ,5, "sounds/weapons/tesla/pulllever.wav", 1, ATTN_NORM); + else if (curweaponframe == 31) + sound (self ,5, "sounds/weapons/tesla/glassbreak.wav", 1, ATTN_NORM); + else if (curweaponframe == 38) + sound (self ,5, "sounds/weapons/tesla/clipin.wav", 1, ATTN_NORM); + else if (curweaponframe == 43) + sound (self ,5, "sounds/weapons/tesla/clipoff.wav", 1, ATTN_NORM); + else if (curweaponframe == 51) + sound (self ,5, "sounds/weapons/tesla/switchon.wav", 1, ATTN_NORM); + } +} + + +void(float weptype) precache_extra = +{ + + if (isCustomWeapon(weptype)) { + precache_model(CustomWeapons[weptype - 70].vmodel); + } + + switch (weptype) { + case W_COLT: + case W_BIATCH: + precache_sound("sounds/weapons/colt/shoot.wav"); + precache_sound("sounds/weapons/colt/magout.wav"); + precache_sound("sounds/weapons/colt/magin.wav"); + precache_sound("sounds/weapons/colt/slide.wav"); + precache_model ("models/weapons/m1911/v_biatch_right.mdl"); // sprite explosion + precache_model ("models/weapons/m1911/v_biatch_left.mdl"); + break; + case W_KAR_SCOPE: + case W_KAR: + case W_ARMAGEDDON: + precache_sound ("sounds/weapons/kar/boltup.wav"); + precache_sound ("sounds/weapons/kar/boltback.wav"); + precache_sound ("sounds/weapons/kar/boltforward.wav"); + precache_sound ("sounds/weapons/kar/boltdown.wav"); + precache_sound ("sounds/weapons/kar/clipin.wav"); + precache_sound ("sounds/weapons/kar/clipoff.wav"); + precache_sound ("sounds/weapons/kar/shoot.wav"); + precache_sound ("sounds/weapons/kar/insert.wav"); + precache_model ("models/weapons/kar/v_karscope.mdl"); + break; + case W_THOMPSON: + case W_GIBS: + precache_sound("sounds/weapons/thomp/magout.wav"); + precache_sound("sounds/weapons/thomp/magin.wav"); + precache_sound("sounds/weapons/thomp/boltback.wav"); + precache_sound("sounds/weapons/thomp/shoot.wav"); + break; + case W_357: + case W_KILLU: + precache_sound("sounds/weapons/357/open.wav"); + precache_sound("sounds/weapons/357/out.wav"); + precache_sound("sounds/weapons/357/in.wav"); + precache_sound("sounds/weapons/357/close.wav"); + precache_sound("sounds/weapons/357/shoot.wav"); + break; + case W_BAR: + case W_WIDOW: + precache_sound("sounds/weapons/bar/shoot.wav"); + precache_sound("sounds/weapons/bar/magout.wav"); + precache_sound("sounds/weapons/bar/magin.wav"); + precache_sound("sounds/weapons/bar/maghit.wav"); + break; + case W_BROWNING: + case W_ACCELERATOR: + precache_sound ("sounds/weapons/browning/boltback.wav"); + precache_sound ("sounds/weapons/browning/topopen.wav"); + precache_sound ("sounds/weapons/browning/chainoff.wav"); + precache_sound ("sounds/weapons/browning/boltback.wav"); + precache_sound ("sounds/weapons/browning/topopen.wav"); + precache_sound ("sounds/weapons/browning/chainon.wav"); + precache_sound ("sounds/weapons/browning/chainplace.wav"); + precache_sound ("sounds/weapons/browning/topclose.wav"); + precache_sound ("sounds/weapons/browning/tophit.wav"); + precache_sound ("sounds/weapons/browning/boltforward.wav"); + precache_sound ("sounds/weapons/browning/shoot.wav"); + break; + case W_SAWNOFF: + case W_SNUFF: + case W_DB: + case W_BORE: + precache_sound("sounds/weapons/shotgun/open.wav"); + precache_sound("sounds/weapons/shotgun/out.wav"); + precache_sound("sounds/weapons/shotgun/in.wav"); + precache_sound("sounds/weapons/shotgun/close.wav"); + precache_sound("sounds/weapons/shotgun/shoot.wav"); + + //lotta stuff here, so let's make sure we don't add unless ABSOLUTELY necessary.. + if (weptype == W_SAWNOFF || weptype == W_SNUFF) + precache_model("models/weapons/sawnoff/v_snuff_right.mdl"); + precache_model("models/weapons/sawnoff/v_snuff_left.mdl"); + break; + case W_FG: + case W_IMPELLER: + precache_sound("sounds/weapons/fg42/magout.wav"); + precache_sound("sounds/weapons/fg42/magin.wav"); + precache_sound("sounds/weapons/fg42/boltback.wav"); + precache_sound("sounds/weapons/fg42/boltforward.wav"); + precache_sound("sounds/weapons/fg42/shoot.wav"); + break; + case W_GEWEHR: + case W_COMPRESSOR: + precache_sound("sounds/weapons/gewehr/magout.wav"); + precache_sound("sounds/weapons/gewehr/magin.wav"); + precache_sound("sounds/weapons/gewehr/maghit.wav"); + precache_sound("sounds/weapons/gewehr/boltrelease.wav"); + precache_sound("sounds/weapons/gewehr/shoot.wav"); + break; + case W_M1: + case W_M1000: + precache_sound ("sounds/weapons/garand/clipout.wav"); + precache_sound ("sounds/weapons/garand/clipin.wav"); + precache_sound ("sounds/weapons/garand/clippush.wav"); + precache_sound ("sounds/weapons/garand/boltrelease.wav"); + precache_sound ("sounds/weapons/garand/shoot.wav"); + break; + case W_M1A1: + case W_WIDDER: + precache_sound("sounds/weapons/m1carbine/shoot.wav"); + precache_sound("sounds/weapons/m1carbine/magout.wav"); + precache_sound("sounds/weapons/m1carbine/magin.wav"); + precache_sound("sounds/weapons/m1carbine/maghit.wav"); + precache_sound("sounds/weapons/m1carbine/bolt.wav"); + break; + case W_M2: + precache_sound("sounds/weapons/m2flame/flamethrower.wav"); + precache_model("models/sprites/flame.spr"); + break; + case W_MP40: + case W_AFTERBURNER: + precache_sound ("sounds/weapons/mp40/shoot.wav"); + precache_sound ("sounds/weapons/mp40/magout.wav"); + precache_sound ("sounds/weapons/mp40/magin.wav"); + break; + case W_MG: + case W_BARRACUDA: + precache_sound("sounds/weapons/mg42/magout.wav"); + precache_sound("sounds/weapons/mg42/magin.wav"); + precache_sound("sounds/weapons/mg42/charge.wav"); + precache_sound("sounds/weapons/mg42/shoot.wav"); + break; + case W_PANZER: + case W_LONGINUS: + precache_sound ("sounds/weapons/panzer/move.wav"); + precache_sound ("sounds/weapons/panzer/insert.wav"); + precache_sound ("sounds/weapons/panzer/shoot.wav"); + //precache_model ("progs/shark.mdl"); + break; + case W_PPSH: + case W_REAPER: + precache_sound("sounds/weapons/ppsh/magout.wav"); + precache_sound("sounds/weapons/ppsh/magin.wav"); + precache_sound("sounds/weapons/ppsh/maghit.wav"); + precache_sound("sounds/weapons/ppsh/boltback.wav"); + precache_sound("sounds/weapons/ppsh/boltrelease.wav"); + precache_sound("sounds/weapons/ppsh/shoot.wav"); + break; + case W_PTRS: + case W_PENETRATOR: + precache_sound ("sounds/weapons/ptrs/open.wav"); + precache_sound ("sounds/weapons/ptrs/maghit.wav"); + precache_sound ("sounds/weapons/ptrs/magin.wav"); + precache_sound ("sounds/weapons/ptrs/close.wav"); + precache_sound ("sounds/weapons/ptrs/shoot.wav"); + break; + case W_RAY: + case W_PORTER: + precache_sound("sounds/weapons/raygun/open.wav"); + precache_sound("sounds/weapons/raygun/out.wav"); + precache_sound("sounds/weapons/raygun/in.wav"); + precache_sound("sounds/weapons/raygun/close.wav"); + precache_sound("sounds/weapons/raygun/shoot.wav"); + precache_model("models/misc/raybeam.mdl"); + break; + case W_STG: + case W_SPATZ: + precache_sound ("sounds/weapons/stg/magout.wav"); + precache_sound ("sounds/weapons/stg/magin.wav"); + precache_sound ("sounds/weapons/stg/shoot.wav"); + break; + case W_TRENCH: + case W_GUT: + precache_sound ("sounds/weapons/shotgun/pump.wav"); + precache_sound ("sounds/weapons/shotgun/insert.wav"); + precache_sound ("sounds/weapons/shotgun/shoot.wav"); + break; + case W_TYPE: + case W_SAMURAI: + precache_sound("sounds/weapons/type100/magout.wav"); + precache_sound("sounds/weapons/type100/magin.wav"); + precache_sound("sounds/weapons/type100/boltpull.wav"); + precache_sound("sounds/weapons/type100/shoot.wav"); + break; + case W_TESLA: + case W_DG3: + precache_model("models/misc/lightning.spr"); + precache_sound("sounds/weapons/tesla/switchoff.wav"); + precache_sound("sounds/weapons/tesla/pulllever.wav"); + precache_sound("sounds/weapons/tesla/glassbreak.wav"); + precache_sound("sounds/weapons/tesla/clipin.wav"); + precache_sound("sounds/weapons/tesla/clipoff.wav"); + precache_sound("sounds/weapons/tesla/switchon.wav"); + precache_sound("sounds/weapons/tesla/shoot.wav"); + break; + } +} + +float(float wep) getWeaponRecoilReturn = +{ + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].recoil; + } + + switch (wep) + { + case W_COLT: + return 750; + break; + case W_BIATCH: + return 750; + break; + case W_KAR: + return 750; + break; + case W_ARMAGEDDON: + return 750; + break; + case W_THOMPSON: + return 950; + break; + case W_GIBS: + return 950; + break; + case W_357: + return 1500; + break; + case W_KILLU: + return 1500; + break; + case W_BAR: + return 460; + break; + case W_WIDOW: + return 460; + break; + case W_BROWNING: + return 950; + break; + case W_ACCELERATOR: + return 950; + break; + case W_DB: + return 1000; + break; + case W_BORE: + return 1000; + break; + case W_FG: + return 1129; + break; + case W_IMPELLER: + return 1129; + break; + case W_GEWEHR: + return 1500; + break; + case W_COMPRESSOR: + return 1500; + break; + case W_KAR_SCOPE: + return 750; + break; + case W_HEADCRACKER: + return 750; + break; + case W_M1: + return 1500; + break; + case W_M1000: + return 1500; + break; + case W_M1A1: + return 1500; + break; + case W_WIDDER: + return 1500; + break; + case W_MP40: + return 2538; + break; + case W_AFTERBURNER: + return 2538; + break; + case W_MG: + return 1778; + break; + case W_BARRACUDA: + return 1778; + break; + case W_PANZER: + return 0; + break; + case W_LONGINUS: + return 0; + break; + case W_PPSH: + return 1255; + break; + case W_REAPER: + return 1255; + break; + case W_PTRS: + return 1500; + break; + case W_PENETRATOR: + return 1500; + break; + case W_RAY: + return 0; + break; + case W_PORTER: + return 0; + break; + case W_SAWNOFF: + return 1000; + break; + case W_SNUFF: + return 1000; + break; + case W_STG: + return 2400; + break; + case W_SPATZ: + return 2400; + break; + case W_TRENCH: + return 500; + break; + case W_GUT: + return 500; + break; + case W_TYPE: + return 1813; + break; + case W_SAMURAI: + return 1813; + break; + case W_MP5: + return 1255; + break; + case W_TESLA: + case W_DG3: + return 1813; + break; + } + return 0; +} + +float(float weapon, float stance) CrossHairWeapon = +{ + + float sprd; + + sprd = 0; + + if (isCustomWeapon(weapon)) { + sprd = CustomWeapons[weapon - 70].crossmin; + } + + switch(weapon) + { + case W_COLT: + case W_BIATCH: + sprd = 15; + break; + case W_KAR: + case W_ARMAGEDDON: + sprd = 50; + break; + case W_THOMPSON: + case W_GIBS: + sprd = 10; + break; + case W_357: + case W_KILLU: + sprd = 10; + break; + case W_BAR: + case W_WIDOW: + sprd = 10; + break; + case W_BROWNING: + case W_ACCELERATOR: + sprd = 20; + break; + case W_DB: + case W_BORE: + sprd = 25; + break; + case W_FG: + case W_IMPELLER: + sprd = 10; + break; + case W_GEWEHR: + case W_COMPRESSOR: + sprd = 10; + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + sprd = 50; + break; + case W_M1: + case W_M1000: + sprd = 10; + break; + case W_M1A1: + case W_WIDDER: + sprd = 10; + break; + case W_MP40: + case W_AFTERBURNER: + sprd = 10; + break; + case W_MG: + case W_BARRACUDA: + sprd = 20; + break; + case W_PANZER: + case W_LONGINUS: + sprd = 0; + break; + case W_PPSH: + case W_REAPER: + sprd = 10; + break; + case W_PTRS: + case W_PENETRATOR: + sprd = 50; + break; + case W_RAY: + case W_PORTER: + sprd = 10; + break; + case W_SAWNOFF: + case W_SNUFF: + sprd = 30; + break; + case W_STG: + case W_SPATZ: + sprd = 10; + break; + case W_TRENCH: + case W_GUT: + sprd = 25; + break; + case W_TYPE: + case W_SAMURAI: + sprd = 10; + break; + case W_MP5: + sprd = 10; + break; + case W_TESLA: + sprd = 0; + break; + default: + sprd = 0; + break; + } + + if (sprd == 0) + return 0; + else { + switch(stance) { + case 2: + return sprd; + case 1: + return sprd - 2; + case 0: + return sprd - 4; + default: return 0; + } + } +} + +float(float weapon, float stance) CrossHairMaxSpread = +{ + float sprd; + + sprd = 0; + + if (isCustomWeapon(weapon)) { + return CustomWeapons[weapon - 70].crossmax; + } + + switch(weapon) + { + case W_COLT: + case W_BIATCH: + sprd = 30; + break; + case W_KAR: + case W_ARMAGEDDON: + sprd = 75; + break; + case W_THOMPSON: + case W_GIBS: + sprd = 25; + break; + case W_357: + case W_KILLU: + sprd = 20; + break; + case W_BAR: + case W_WIDOW: + sprd = 35; + break; + case W_BROWNING: + case W_ACCELERATOR: + sprd = 50; + break; + case W_DB: + case W_BORE: + sprd = 25; + break; + case W_FG: + case W_IMPELLER: + sprd = 40; + break; + case W_GEWEHR: + case W_COMPRESSOR: + sprd = 35; + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + sprd = 75; + break; + case W_M1: + case W_M1000: + sprd = 35; + break; + case W_M1A1: + case W_WIDDER: + sprd = 35; + break; + case W_MP40: + case W_AFTERBURNER: + sprd = 25; + break; + case W_MG: + case W_BARRACUDA: + sprd = 50; + break; + case W_PANZER: + case W_LONGINUS: + sprd = 0; + break; + case W_PPSH: + case W_REAPER: + sprd = 25; + break; + case W_PTRS: + case W_PENETRATOR: + sprd = 75; + break; + case W_RAY: + case W_PORTER: + sprd = 20; + break; + case W_SAWNOFF: + case W_SNUFF: + sprd = 30; + break; + case W_STG: + case W_SPATZ: + sprd = 35; + break; + case W_TRENCH: + case W_GUT: + sprd = 25; + break; + case W_TYPE: + case W_SAMURAI: + sprd = 25; + break; + case W_MP5: + sprd = 25; + break; + case W_TESLA: + sprd = 0; + break; + default: + sprd = 0; + break; + } + + if (sprd == 0) + return 0; + else { + switch(stance) { + case 2: + return sprd; + case 1: + return sprd - 2; + case 0: + return sprd - 4; + default: + return 0; + } + } +} + +float(float weapon, float type) GetLowAmmo = // determine what ammo value you should have that turns shells red +{ + + if (isCustomWeapon(weapon)) { + if (type) return CustomWeapons[weapon - 70].lowmag; else return CustomWeapons[weapon - 70].lowreserve; + } + + switch (weapon) + { + case W_COLT: if (type) return 2; else return 16; + case W_KAR: if (type) return 1; else return 10; + case W_KAR_SCOPE: if (type) return 1; else return 10; + case W_M1A1: if (type) return 4; else return 24; + case W_SAWNOFF: if (type) return 1; else return 12; + case W_DB: if (type) return 1; else return 12; + case W_THOMPSON: if (type) return 6; else return 40; + case W_BAR: if (type) return 6; else return 28; + case W_BIATCH: if (type) return 2; else return 16; + case W_FG: if (type) return 8; else return 32; + case W_357: if (type) return 2; else return 12; + case W_BROWNING: if (type) return 25; else return 125; + case W_GEWEHR: if (type) return 3; else return 20; + case W_M1: if (type) return 2; else return 24; + case W_MP40: if (type) return 8; else return 64; + case W_MG: if (type) return 25; else return 125; + case W_PANZER: if (type) return 0; else return 4; + case W_PPSH: if (type) return 25; else return 71; + case W_PTRS: if (type) return 2; else return 12; + case W_RAY: if (type) return 5; else return 40; + case W_STG: if (type) return 12; else return 60; + case W_TRENCH: if (type) return 2; else return 12; + case W_TYPE: if (type) return 12; else return 60; + case W_COMPRESSOR: if (type) return 3; else return 36; + case W_M1000: if (type) return 3; else return 36; + case W_GIBS: if (type) return 8; else return 80; + case W_SAMURAI: if (type) return 12; else return 60; + case W_AFTERBURNER: if (type) return 12; else return 64; + case W_SPATZ: if (type) return 12; else return 120; + case W_SNUFF: if (type) return 1; else return 14; + case W_BORE: if (type) return 1; else return 14; + case W_IMPELLER: if (type) return 12; else return 128; + case W_BARRACUDA: if (type) return 25; else return 125; + case W_ACCELERATOR: if (type) return 25; else return 125; + case W_GUT: if (type) return 3; else return 15; + case W_REAPER: if (type) return 23; else return 115; + case W_HEADCRACKER: if (type) return 2; else return 16; + case W_LONGINUS: if (type) return 1; else return 12; + case W_PENETRATOR: if (type) return 2; else return 16; + case W_WIDOW: if (type) return 6; else return 60; + case W_PORTER: if (type) return 8; else return 40; + case W_ARMAGEDDON: if (type) return 2; else return 15; + case W_WIDDER: if (type) return 4; else return 30; + case W_KILLU: if (type) return 2; else return 20; + case W_TESLA: if (type) return 1; else return 6; + case 0: if (type) return -1; else return -1; + default: return 0; + } +} + +// PSP Specific!! +vector(float wep) GetWeaponRecoil = +{ + vector guaranteed = '0 0 0'; + vector kick_change = '0 0 0'; + vector final_kick = '0 0 0'; + float change_1, change_2, change_3,total_recoil, r, temp1, temp2; + switch (wep) + { + case W_COLT: + guaranteed = '0 35 0'; + kick_change = '25 15 25'; + break; + case W_BIATCH: + guaranteed = '0 35 0'; + kick_change = '25 15 25'; + break; + case W_357: + guaranteed = '0 80 50'; + kick_change = '0 60 40'; + break; + case W_KILLU: + guaranteed = '0 80 50'; + kick_change = '0 60 40'; + break; + case W_THOMPSON: + kick_change = '74 74 85'; + break; + case W_GIBS: + kick_change = '74 74 85'; + break; + case W_MP40: + kick_change = '44 44 44'; + break; + case W_AFTERBURNER: + kick_change = '44 44 44'; + break; + case W_TYPE: + kick_change = '44 44 44'; + break; + case W_SAMURAI: + kick_change = '24 74 24'; + break; + case W_PPSH: + kick_change = '4 22 4'; + break; + case W_REAPER: + kick_change = '4 22 4'; + break; + case W_GEWEHR: + kick_change = '50 60 50'; + break; + case W_COMPRESSOR: + kick_change = '50 60 50'; + break; + case W_M1: + guaranteed = '0 35 0'; + kick_change = '30 30 30'; + break; + case W_M1000: + guaranteed = '0 35 0'; + kick_change = '30 30 30'; + break; + case W_STG: + kick_change = '15 44 15'; + break; + case W_SPATZ: + kick_change = '15 44 15'; + break; + case W_M1A1: + kick_change = '50 60 50'; + break; + case W_WIDDER: + kick_change = '50 60 50'; + break; + case W_KAR: + guaranteed = '0 50 0'; + kick_change = '25 30 25'; + break; + case W_ARMAGEDDON: + guaranteed = '0 50 0'; + kick_change = '25 30 25'; + break; + case W_KAR_SCOPE: + guaranteed = '0 50 0'; + kick_change = '25 30 25'; + break; + case W_HEADCRACKER: + guaranteed = '0 50 0'; + kick_change = '25 30 25'; + break; + case W_PTRS: + guaranteed = '0 100 85'; + kick_change = '0 95 75'; + break; + case W_PENETRATOR: + guaranteed = '0 100 85'; + kick_change = '0 95 75'; + break; + case W_TRENCH: + guaranteed = '0 100 85'; + kick_change = '0 95 75'; + break; + case W_GUT: + guaranteed = '0 100 85'; + kick_change = '0 95 75'; + break; + case W_DB: + guaranteed = '0 95 0'; + kick_change = '45 85 45'; + break; + case W_BORE: + guaranteed = '0 95 0'; + kick_change = '45 85 45'; + break; + case W_SAWNOFF: + guaranteed = '0 95 0'; + kick_change = '45 85 45'; + break; + case W_SNUFF: + guaranteed = '0 95 0'; + kick_change = '45 85 45'; + break; + case W_BAR: + kick_change = '0 50 0'; + break; + case W_WIDOW: + kick_change = '0 50 0'; + break; + case W_MG: + kick_change = '40 30 40'; + break; + case W_BARRACUDA: + kick_change = '40 30 40'; + break; + case W_FG: + kick_change = '30 45 0'; + break; + case W_IMPELLER: + kick_change = '30 45 0'; + break; + case W_BROWNING: + kick_change = '50 66 50'; + break; + case W_ACCELERATOR: + kick_change = '50 66 50'; + break; + case W_TESLA: + case W_DG3: + kick_change = '74 74 85'; + break; + } + total_recoil = kick_change_x + kick_change_y + kick_change_z; + + change_1 = (kick_change_x)/total_recoil; + change_2 = (kick_change_y)/total_recoil; + change_3 = (kick_change_z)/total_recoil; + + r = random(); + temp1 = change_1 + change_2; + temp2 = change_1 + change_2 + change_3; + + #ifndef PSP + if (r < change_1) + final_kick_y = kick_change_x*0.25; + else if (r < temp1) + final_kick_x = kick_change_y*-1*.25; + else if (r < temp2) + final_kick_y = kick_change_z*-1*0.25; + #else + if (r < change_1) + final_kick_y = kick_change_x; + else if (r < temp1) + final_kick_x = kick_change_y*-1; + else if (r < temp2) + final_kick_y = kick_change_z*-1; + #endif + + final_kick_y = final_kick_y + guaranteed_x; + final_kick_x = final_kick_x - guaranteed_y; + final_kick_y = final_kick_y - guaranteed_z; + + final_kick_x = final_kick_x / 10; + final_kick_y = final_kick_y / 10; + final_kick_z = final_kick_z / 10; + //bprint (vtos(final_kick)); + //bprint ("\n"); + return final_kick; +} + +// Flash offset is multiplied because vectors have limited precision within qc +vector (float wep) GetWeaponFlash_Offset = +{ + switch(wep) { + #ifndef NX + case W_COLT: + return [5488, -2742, 35300]; + case W_BIATCH: + return [8588, -3842, 35300]; + case W_KAR: + case W_KAR_SCOPE: + case W_HEADCRACKER: + case W_ARMAGEDDON: + return [5560, -4582, 75833]; + case W_THOMPSON: + case W_GIBS: + return [6203, -5740, 57670]; + case W_TRENCH: + case W_GUT: + return [5550, -4118, 62364]; + case W_357: + case W_KILLU: + return [8193, -2353, 50527]; + case W_MG: + case W_BARRACUDA: + return [6806, -9375, 81713]; + case W_DB: + case W_BORE: + return [5801, -4300, 83174]; + case W_SAWNOFF: + return [5801, -4414, 44820]; + case W_M1A1: + case W_WIDDER: + return [5317, -5331, 70583]; + case W_BAR: + case W_WIDOW: + return [3899, -5021, 78199]; + case W_FG: + case W_IMPELLER: + return [14000, -12000, 110000]; + case W_GEWEHR: + case W_COMPRESSOR: + return [6500, -8500, 65000]; + case W_PPSH: + case W_REAPER: + return [11100, -9000, 86300]; + case W_MP40: + case W_AFTERBURNER: + return [11300, -11300, 80000]; + case W_STG: + case W_SPATZ: + return [14300, -16230, 100000]; + case W_M1: + case W_M1000: + return [2896, -4142, 28300]; + case W_BROWNING: + case W_ACCELERATOR: + return [15726, -23375, 170713]; + case W_PTRS: + case W_PENETRATOR: + return [10988, -14242, 180300]; + case W_TYPE: + case W_SAMURAI: + return [488, -2300, 25300]; + default: + return [5488, -2742, 35300]; + #endif + + #ifdef NX + case W_COLT: + case W_BIATCH: + return [1950, -6250, 35300]; + case W_KAR: + case W_KAR_SCOPE: + case W_HEADCRACKER: + case W_ARMAGEDDON: + return [-5120, -13582, 75833]; + case W_THOMPSON: + case W_GIBS: + return [770, -12740, 57670]; + case W_TRENCH: + case W_GUT: + return [-7450, -17918, 62364]; + case W_357: + case W_KILLU: + return [4093, -7353, 50527]; + case W_MG: + case W_BARRACUDA: + return [-4006, -19375, 81713]; + case W_DB: + case W_BORE: + return [-6700, -18300, 83174]; + case W_SAWNOFF: + return [-8501, -19414, 44820]; + case W_M1A1: + case W_WIDDER: + return [-4717, -15331, 70583]; + case W_BAR: + case W_WIDOW: + return [-7599, -17321, 78199]; + case W_FG: + case W_IMPELLER: + return [1800, -25500, 110000]; + case W_GEWEHR: + case W_COMPRESSOR: + return [-2200, -17800, 65000]; + case W_PPSH: + case W_REAPER: + return [300, -18500, 86300]; + case W_MP40: + case W_AFTERBURNER: + return [1570, -21300, 80000]; + case W_STG: + case W_SPATZ: + return [2500, -30230, 100000]; + case W_M1: + case W_M1000: + return [-750, -7842, 28300]; + case W_BROWNING: + case W_ACCELERATOR: + return [-1926, -51375, 170713]; + case W_PTRS: + case W_PENETRATOR: + return [-7888, -37242, 180300]; + + case W_TYPE: + case W_SAMURAI: + return [-4488, -7800, 25300]; + default: + return [5488, -2742, 35300]; + #endif + } + return [0, 0, 0]; +} + +float (float wep) GetWeaponFlash_Size = { + + if (isCustomWeapon(wep)) { + return CustomWeapons[wep - 70].flashsize; + } + + switch(wep) { + case W_COLT: + return 5; + case W_BIATCH: + return 5; + case W_KAR: + case W_KAR_SCOPE: + case W_HEADCRACKER: + case W_ARMAGEDDON: + return 15; + case W_THOMPSON: + case W_GIBS: + return 8; + case W_TRENCH: + case W_GUT: + return 19; + case W_357: + case W_KILLU: + return 7; + case W_MG: + case W_BARRACUDA: + return 16; + case W_DB: + case W_BORE: + return 18; + case W_SAWNOFF: + return 20; + case W_M1A1: + case W_WIDDER: + return 14; + case W_BAR: + case W_WIDOW: + return 16; + case W_FG: + case W_IMPELLER: + return 18; + case W_GEWEHR: + case W_COMPRESSOR: + return 12; + case W_PPSH: + return 14; + case W_MP40: + case W_AFTERBURNER: + return 14; + case W_STG: + case W_SPATZ: + return 16; + case W_M1: + case W_M1000: + return 5; + case W_BROWNING: + case W_ACCELERATOR: + return 27; + case W_PTRS: + case W_PENETRATOR: + return 30; + case W_TYPE: + case W_SAMURAI: + return 5; + default: + return 5; + } +} diff --git a/tools/qc-compiler-lin.sh b/tools/qc-compiler-lin.sh new file mode 100644 index 0000000..4570ab8 --- /dev/null +++ b/tools/qc-compiler-lin.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +cd ../ +# create build directories +mkdir -p build/{pc,psp,nx,vita} +cd bin/ +echo "" +echo "====================" +echo " compiling FTE CCQC " +echo "====================" +echo "" +./fteqcc-cli-lin -srcfile ../progs/fte-client.src +echo "" +echo "====================" +echo " compiling FTE SSQC " +echo "====================" +echo "" +./fteqcc-cli-lin -srcfile ../progs/fte-server.src +echo "" +echo "====================" +echo " compiling PSP QC " +echo "====================" +echo "" +./fteqcc-cli-lin -srcfile ../progs/psp.src +echo "" +echo "====================" +echo " compiling NX-QS QC " +echo "====================" +echo "" +./fteqcc-cli-lin -srcfile ../progs/nx.src +echo "" +echo "====================" +echo " compiling VITA QC " +echo "====================" +echo "" +./fteqcc-cli-lin -srcfile ../progs/vita.src \ No newline at end of file diff --git a/tools/qc-compiler-win.bat b/tools/qc-compiler-win.bat new file mode 100644 index 0000000..8ae6eef --- /dev/null +++ b/tools/qc-compiler-win.bat @@ -0,0 +1,39 @@ +@ECHO OFF +CD ../ +REM ****** create build directories ****** +MKDIR build\pc\ 2>nul +MKDIR build\psp\ 2>nul +MKDIR build\nx\ 2>nul +MKDIR build\vita\ 2>nul +CD bin/ +echo. +echo ==================== +echo compiling FTE CCQC +echo ==================== +echo. +fteqcc-cli-win.exe -srcfile ../progs/fte-client.src +echo. +echo ==================== +echo compiling FTE SSQC +echo ==================== +echo. +fteqcc-cli-win.exe -srcfile ../progs/fte-server.src +echo. +echo ==================== +echo compiling PSP QC +echo ==================== +echo. +fteqcc-cli-win.exe -srcfile ../progs/psp.src +echo. +echo ==================== +echo compiling NX-QS QC +echo ==================== +echo. +fteqcc-cli-win.exe -srcfile ../progs/nx.src +echo. +echo ==================== +echo compiling VITA QC +echo ==================== +echo. +fteqcc-cli-win.exe -srcfile ../progs/vita.src +pause