Repository: Rinnegatamante/vitaQuake Branch: master Commit: fbe7c5e7fe77 Files: 176 Total size: 1.4 MB Directory structure: gitextract_5ojh867z/ ├── .gitattributes ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── build/ │ ├── sce_sys/ │ │ └── livearea/ │ │ └── contents/ │ │ └── template.xml │ └── shaders/ │ ├── modulate_alpha_f.gxp │ ├── modulate_alpha_fog_f.gxp │ ├── modulate_f.gxp │ ├── modulate_fog_f.gxp │ ├── modulate_rgba_alpha_f.gxp │ ├── modulate_rgba_alpha_fog_f.gxp │ ├── modulate_rgba_f.gxp │ ├── modulate_rgba_fog_f.gxp │ ├── replace_alpha_f.gxp │ ├── replace_alpha_fog_f.gxp │ ├── replace_f.gxp │ ├── replace_fog_f.gxp │ ├── rgba_alpha_f.gxp │ ├── rgba_f.gxp │ ├── rgba_v.gxp │ ├── texture2d_fog_v.gxp │ ├── texture2d_rgba_fog_v.gxp │ ├── texture2d_rgba_v.gxp │ ├── texture2d_v.gxp │ ├── vertex_f.gxp │ └── vertex_v.gxp ├── shaders/ │ ├── modulate_alpha_f.cg │ ├── modulate_alpha_fog_f.cg │ ├── modulate_f.cg │ ├── modulate_fog_f.cg │ ├── modulate_rgba_alpha_f.cg │ ├── modulate_rgba_alpha_fog_f.cg │ ├── modulate_rgba_f.cg │ ├── modulate_rgba_fog_f.cg │ ├── replace_alpha_f.cg │ ├── replace_alpha_fog_f.cg │ ├── replace_f.cg │ ├── replace_fog_f.cg │ ├── rgba_alpha_f.cg │ ├── rgba_f.cg │ ├── rgba_v.cg │ ├── texture2d_fog_v.cg │ ├── texture2d_rgba_fog_v.cg │ ├── texture2d_rgba_v.cg │ ├── texture2d_v.cg │ ├── vertex_f.cg │ └── vertex_v.cg └── source/ ├── adivtab.h ├── anorm_dots.h ├── anorms.h ├── asm_draw.h ├── asm_i386.h ├── audiodec/ │ ├── audio_decoder.cpp │ ├── audio_decoder.h │ ├── audio_resampler.cpp │ ├── audio_resampler.h │ ├── cd_psp2.cpp │ ├── decoder_mpg123.cpp │ ├── decoder_mpg123.h │ ├── decoder_oggvorbis.cpp │ └── decoder_oggvorbis.h ├── block16.h ├── block8.h ├── bspfile.h ├── cdaudio.h ├── chase.c ├── cl_demo.c ├── cl_input.c ├── cl_main.c ├── cl_parse.c ├── cl_tent.c ├── client.h ├── cmd.c ├── cmd.h ├── common.c ├── common.h ├── console.c ├── console.h ├── crc.c ├── crc.h ├── cvar.c ├── cvar.h ├── draw.h ├── gl_draw.cpp ├── gl_fullbright.c ├── gl_fullbright.h ├── gl_mesh.c ├── gl_model.c ├── gl_model.h ├── gl_refrag.c ├── gl_rlight.c ├── gl_rmain.c ├── gl_rmisc.c ├── gl_rsurf.c ├── gl_screen.c ├── gl_vidpsp2.c ├── gl_warp.c ├── gl_warp_sin.h ├── glquake.h ├── glquake2.h ├── host.c ├── host_cmd.c ├── image.c ├── image.h ├── in_psp2.c ├── input.h ├── keys.c ├── keys.h ├── mathlib.c ├── mathlib.h ├── menu.c ├── menu.h ├── model.h ├── modelgen.h ├── mpdosock.h ├── neon_mathfun.c ├── neon_mathfun.h ├── net.h ├── net_adhoc.h ├── net_adhoc_psp2.c ├── net_dgrm.c ├── net_dgrm.h ├── net_loop.c ├── net_loop.h ├── net_main.c ├── net_none.c ├── net_psp2.c ├── net_udp.h ├── net_udp_psp2.c ├── net_vcr.c ├── net_vcr.h ├── nonintel.c ├── pr_cmds.c ├── pr_comp.h ├── pr_edict.c ├── pr_exec.c ├── progdefs.h ├── progdefs.q1 ├── progdefs.q2 ├── progs.h ├── protocol.h ├── quakeasm.h ├── quakedef.h ├── r_local.h ├── r_part.c ├── r_shared.h ├── render.h ├── sbar.c ├── sbar.h ├── screen.h ├── server.h ├── snd_dma.c ├── snd_mem.c ├── snd_mix.c ├── snd_psp2.c ├── sound.h ├── spritegn.h ├── sv_main.c ├── sv_move.c ├── sv_phys.c ├── sv_user.c ├── sys.h ├── sys_psp2.c ├── vid.h ├── view.c ├── view.h ├── wad.c ├── wad.h ├── webdownload.c ├── webdownload.h ├── world.c ├── world.h ├── zone.c └── zone.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .github/FUNDING.yml ================================================ patreon: Rinnegatamante ================================================ FILE: .gitignore ================================================ # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msm *.msp # Windows shortcuts *.lnk # ========================= # Operating System Files # ========================= # OSX # ========================= .DS_Store .AppleDouble .LSOverride # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # c object files and other build files *.o *.elf build/eboot.bin build/sce_sys/param.sfo buildeboot.bin param.sfo *.velf *.vpk ================================================ FILE: LICENSE.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: Makefile ================================================ TARGET := vitaQuake TITLE := QUAK00001 GIT_VERSION := $(shell git describe --abbrev=6 --dirty --always --tags) SHADERS := shaders LIBS = -lvitaGL -lvitashark -lSceShaccCgExt -ltaihen_stub -lvorbisfile -lvorbis -logg \ -lspeexdsp -lmpg123 -lScePspnetAdhoc_stub -lSceShaccCg_stub -lSceKernelDmacMgr_stub \ -lc -lSceCommonDialog_stub -lSceAudio_stub -lSceLibKernel_stub -lmathneon \ -lSceNet_stub -lSceNetCtl_stub -lpng -lSceDisplay_stub -lSceGxm_stub \ -Wl,--whole-archive -lSceSysmodule_stub -Wl,--no-whole-archive \ -lSceCtrl_stub -lSceTouch_stub -lSceMotion_stub -lm -lSceAppMgr_stub \ -lSceAppUtil_stub -lScePgf_stub -ljpeg -lSceRtc_stub -lScePower_stub -lcurl -lssl -lcrypto -lz COMMON_OBJS = source/chase.o \ source/cl_demo.o \ source/cl_input.o \ source/cl_main.o \ source/cl_parse.o \ source/cl_tent.o \ source/chase.o \ source/cmd.o \ source/common.o \ source/console.o \ source/crc.o \ source/cvar.o \ source/host.o \ source/host_cmd.o \ source/image.o \ source/keys.o \ source/mathlib.o \ source/menu.o \ source/net_dgrm.o \ source/net_loop.o \ source/net_main.o \ source/net_vcr.o \ source/pr_cmds.o \ source/pr_edict.o \ source/pr_exec.o \ source/r_part.o \ source/sbar.o \ source/sv_main.o \ source/sv_move.o \ source/sv_phys.o \ source/sv_user.o \ source/view.o \ source/wad.o \ source/world.o \ source/zone.o \ source/sys_psp2.o \ source/gl_draw.o \ source/gl_mesh.o \ source/gl_model.o \ source/gl_refrag.o \ source/gl_rlight.o \ source/gl_rmain.o \ source/gl_rmisc.o \ source/gl_rsurf.o \ source/gl_screen.o \ source/gl_warp.o \ source/gl_fullbright.o \ source/r_part.o \ source/snd_dma.o \ source/snd_mix.o \ source/snd_mem.o \ source/snd_psp2.o \ source/net_psp2.o \ source/net_adhoc_psp2.o \ source/net_udp_psp2.o \ source/in_psp2.o \ source/gl_vidpsp2.o \ source/neon_mathfun.o \ source/webdownload.o CPPSOURCES := source/audiodec CFILES := $(COMMON_OBJS) CPPFILES := $(foreach dir,$(CPPSOURCES), $(wildcard $(dir)/*.cpp)) CGFILES := $(foreach dir,$(SHADERS), $(wildcard $(dir)/*.cg)) CGSHADERS := $(CGFILES:.cg=.h) OBJS := $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) PREFIX = arm-vita-eabi CC = $(PREFIX)-gcc CXX = $(PREFIX)-g++ CFLAGS = -fsigned-char -Wl,-q -O3 -g -fno-optimize-sibling-calls \ -ffast-math -mtune=cortex-a9 -mfpu=neon \ -DGLQUAKE -DHAVE_OGGVORBIS -DHAVE_MPG123 -DHAVE_LIBSPEEXDSP \ -DUSE_AUDIO_RESAMPLER -DGIT_VERSION=\"$(GIT_VERSION)\" CXXFLAGS = $(CFLAGS) -fno-exceptions -std=gnu++11 ASFLAGS = $(CFLAGS) all: $(TARGET).vpk $(TARGET).vpk: $(TARGET).velf vita-make-fself -c -s $< build/eboot.bin vita-mksfoex -s TITLE_ID=$(TITLE) -d ATTRIBUTE2=12 "$(TARGET)" param.sfo cp -f param.sfo build/sce_sys/param.sfo vita-pack-vpk -s param.sfo -b build/eboot.bin $(TARGET).vpk \ -a build/shaders=shaders \ -a build/sce_sys=sce_sys %_f.h: psp2cgc -profile sce_fp_psp2 $(@:_f.h=_f.cg) -Wperf -fastprecision -O3 -o build/$(@:_f.h=_f.gxp) %_v.h: psp2cgc -profile sce_vp_psp2 $(@:_v.h=_v.cg) -Wperf -fastprecision -O3 -o build/$(@:_v.h=_v.gxp) shaders: $(CGSHADERS) %.velf: %.elf cp $< $<.unstripped.elf $(PREFIX)-strip -g $< vita-elf-create $< $@ $(TARGET).elf: $(OBJS) $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ clean: @rm -rf $(TARGET).velf $(TARGET).elf $(OBJS) $(TARGET).elf.unstripped.elf $(TARGET).vpk build/eboot.bin build/sce_sys/param.sfo ./param.sfo ================================================ FILE: README.md ================================================ # Introduction vitaQuake is a Quake engine source port for PSVITA. An official channel to discuss the development of this source port can be found on [Vita Nuova discord server](https://discord.gg/PyCaBx9). # Features - Hardware accelerated GPU rendering - Native 960x544 resolution - Rendering resolution up to 1920x1080 on the PSTV with [Sharpscale](https://git.shotatoshounenwachigau.moe/vita/sharpscale) - MSAA 2x and MSAA 4x support - Dual analogs support - Native IME for inputing commands/text - Sounds and Musics (CDTracks) support in OGG, MP3, WAV formats - Gyroscope and touchscreen support for camera movement - Custom arguments support and mods support - Support for both official missionpacks - Support for transparent surfaces (.alpha and .renderamt) - Increased Quake Engine limits (max vertices, max entities, max static entities, etc...) - LAN Multiplayer support (locale and online) - AdHoc Multiplayer support - ProQuake net protocol support - Savegames fully working - Support for colored lights with .lit files support - Support for Half-Life BSP - Supprt for BSP2 and 2BSP formats - Smooth animations thanks to interpolation techniques - Crosshair and custom crosshairs support - Mirrors support - Specular mode support - Fog support - Cel Shading support - Bilinear filtering support - Dynamic shadows support - Several different improvements in the renderer quality - Several different miscellaneous features (eg: transparent statusbar, benchmark feature, working gamma, etc...) - Map downloader support if you try to join an online server and you don't own the running map # Supported DarkPlaces extensions - DP_CON_SET - DP_CON_SETA - DP_EF_BLUE - DP_EF_NODRAW - DP_EF_RED - DP_ENT_ALPHA - DP_GFX_EXTERNALTEXTURES - DP_GFX_EXTERNALTEXTURES_PERMAPTEXTURES - DP_HALFLIFE_MAP - DP_LITSUPPORT - DP_QC_ASINACOSATANATAN2TAN - DP_QC_COPYENTITY - DP_QC_CVAR_STRING - DP_QC_EDICT_NUM - DP_QC_ETOS - DP_QC_FINDCHAIN - DP_QC_FINDCHAINFLOAT - DP_QC_MINMAXBOUND - DP_QC_NUM_FOR_EDICT - DP_QC_RANDOMVEC - DP_QC_SINCOSSQRTPOW - DP_QC_TRACEBOX - DP_SND_FAKETRACKS - DP_SV_MODELFLAGS_AS_EFFECTS - DP_SV_NODRAWTOCLIENT - DP_SV_DRAWONLYTOCLIENT - EXT_BITSHIFT - FRIK_FILE # CDAudio Support vitaQuake supports all soundtrack packs for Quake and its two official mission packs, "Scourge of Armagon" and "Dissolution of Eternity." In order for the soundtrack to work, files must be placed in a folder named /cdtracks/ in each campaign folder (main game for example will be ux0:data/Quake/id1/cdtracks). By default, the music folder has tracks named as track02, track03, etc. For vitaQuake, add an extra "0" after "track" in order for them to be loaded properly and in order. **Ex.: track02 -> track002** You can find the official soundtrack for the main campaign in .ogg format [here](https://www.quaddicted.com/files/music/quake_music.zip). # Loading Expansions and Mods vitaQuake supports the official Quake expansions, "Scourge of Armagon" and "Dissolution of Eternity." These were offical expansions, so they can be found usually wherever the full base game is sold (GOG, Steam). In order to get them to load properly, place them in the "ux0:/data/quake/" folder alongside "id1". Both official mission packs support their own soundtracks as long as they are placed properly in their respective "/cdtracks/" folder. Mod compatibility is varied, but as a general rule of thumb, mods compatible with winQuake will be compatible with vitaQuake. Here's a list of some popular mods and their actual working state: Expansion/Mod | Link | Status ---|---|---| Dissolution of Eternity | Official Expansion | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Fully Working` dopa | [Free](https://twitter.com/machinegames/status/746363189768650752?lang=en) | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Fully Working` Halo Revamped | [Free](https://wololo.net/downloads/index.php/download/1376) | ![#d0d000](https://placehold.it/15/d0d000/000000?text=+) `Boots, lots of bugs` Kurok | [Free](http://www.bladebattles.com/kurok/) | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Playable with glitches` Nazi Zombies Portable | [Free](https://www.moddb.com/games/nazi-zombies-portable/news/nazi-zombies-portable-ps-vita-info) | ![#ff0000](https://placehold.it/15/ff0000/000000?text=+) `Not Working` Quake Rally | [Free](https://www.moddb.com/mods/quake-rally) | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Fully Working` Scourge of Armagon | Official Expansion | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Fully Working` Slayer's Testament | [Free](https://www.youtube.com/watch?v=abn9aCiJ3pY) | ![#ff0000](https://placehold.it/15/ff0000/000000?text=+) `Crash on models loading` SUPERHOT Quake | [Free](https://www.moddb.com/mods/superhot-quake) | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Working without monochromatic graphics` SUPERQOT | [Free](https://superhotgame.com/SUPERQOT/) | ![#ff0000](https://placehold.it/15/ff0000/000000?text=+) `Not Working` YPOD | [Free](https://www.quakewiki.net/archives/doom/) | ![#007f00](https://placehold.it/15/007f00/000000?text=+) `Fully Working` # Credits - idSoftware for winQuake sourcecode - MasterFeizz for ctrQuake sourcecode i studied to understand how winQuake works - EasyRPG Team for the audio decoder used for CDAudio support - Ch0wW for various improvements and code cleanup - JPG for ProQuake and some various fixes. - Cuevavirus for 1920x1080 rendering ================================================ FILE: build/sce_sys/livearea/contents/template.xml ================================================ bg.png startup.png psla:-hipnotic hipnotic.png psla:-rogue rogue.png psla:-custom  Click here to launch with custom args ================================================ FILE: shaders/modulate_alpha_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, uniform float4 vColor, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord) * vColor; if (c.a > 0.666) return c; else discard; } ================================================ FILE: shaders/modulate_alpha_fog_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float vFog : FOG, uniform float4 vColor, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord) * vColor; c.rgb = c.rgb * vFog; if (c.a > 0.666) return c; else discard; } ================================================ FILE: shaders/modulate_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, uniform float4 vColor, uniform sampler2D tex ) { return tex2D(tex, vTexcoord) * vColor; } ================================================ FILE: shaders/modulate_fog_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float vFog : FOG, uniform float4 vColor, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord) * vColor; c.rgb = c.rgb * vFog; return c; } ================================================ FILE: shaders/modulate_rgba_alpha_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float4 vColor : COLOR, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord) * vColor; if (c.a > 0.666) return c; else discard; } ================================================ FILE: shaders/modulate_rgba_alpha_fog_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float vFog : FOG, float4 vColor : COLOR, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord) * vColor; c.rgb = c.rgb * vFog; if (c.a > 0.666) return c; else discard; } ================================================ FILE: shaders/modulate_rgba_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float4 vColor : COLOR, uniform sampler2D tex ) { return tex2D(tex, vTexcoord) * vColor; } ================================================ FILE: shaders/modulate_rgba_fog_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float vFog : FOG, float4 vColor : COLOR, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord) * vColor; c.rgb = c.rgb * vFog; return c; } ================================================ FILE: shaders/replace_alpha_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord); if (c.a > 0.666) return c; else discard; } ================================================ FILE: shaders/replace_alpha_fog_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float vFog : FOG, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord); c.rgb = c.rgb * vFog; if (c.a > 0.666) return c; else discard; } ================================================ FILE: shaders/replace_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, uniform sampler2D tex ) { return tex2D(tex, vTexcoord); } ================================================ FILE: shaders/replace_fog_f.cg ================================================ float4 main( float2 vTexcoord : TEXCOORD0, float vFog : FOG, uniform sampler2D tex ) { float4 c = tex2D(tex, vTexcoord); c.rgb = c.rgb * vFog; return c; } ================================================ FILE: shaders/rgba_alpha_f.cg ================================================ float4 main(float4 vColor : COLOR) : COLOR { if (vColor.a > 0.666) return vColor; else discard; } ================================================ FILE: shaders/rgba_f.cg ================================================ float4 main(float4 vColor : COLOR) : COLOR { return vColor; } ================================================ FILE: shaders/rgba_v.cg ================================================ void main( float3 aPosition, float4 aColor, uniform float4x4 gl_ModelViewProjectionMatrix, float4 out vPosition: POSITION, float4 out vColor: COLOR) { vPosition = mul(gl_ModelViewProjectionMatrix, float4(aPosition, 1.f)); vColor = aColor; } ================================================ FILE: shaders/texture2d_fog_v.cg ================================================ void main( float3 position, float2 texcoord, uniform float4x4 gl_ModelViewProjectionMatrix, float4 out vPosition : POSITION, float out vFog : FOG, float2 out vTexcoord : TEXCOORD0) { vPosition = mul(gl_ModelViewProjectionMatrix, float4(position, 1.f)); float dist = distance(vPosition,float4(0.0,0.0,0.0,1.0)); vFog = ((512.0 - dist) / (512.0)); vTexcoord = texcoord; } ================================================ FILE: shaders/texture2d_rgba_fog_v.cg ================================================ void main( float3 position, float2 texcoord, float4 color, uniform float4x4 gl_ModelViewProjectionMatrix, float4 out vPosition : POSITION, float out vFog : FOG, float2 out vTexcoord : TEXCOORD0, float4 out vColor : COLOR) { vPosition = mul(gl_ModelViewProjectionMatrix, float4(position, 1.f)); float dist = distance(vPosition,float4(0.0,0.0,0.0,1.0)); vFog = ((512.0 - dist) / (512.0)); vTexcoord = texcoord; vColor = color; } ================================================ FILE: shaders/texture2d_rgba_v.cg ================================================ void main( float3 position, float2 texcoord, float4 color, uniform float4x4 gl_ModelViewProjectionMatrix, float4 out vPosition : POSITION, float2 out vTexcoord : TEXCOORD0, float4 out vColor : COLOR) { vPosition = mul(gl_ModelViewProjectionMatrix, float4(position, 1.f)); vTexcoord = texcoord; vColor = color; } ================================================ FILE: shaders/texture2d_v.cg ================================================ void main( float3 position, float2 texcoord, uniform float4x4 gl_ModelViewProjectionMatrix, float4 out vPosition : POSITION, float2 out vTexcoord : TEXCOORD0) { vPosition = mul(gl_ModelViewProjectionMatrix, float4(position, 1.f)); vTexcoord = texcoord; } ================================================ FILE: shaders/vertex_f.cg ================================================ float4 main(uniform float4 color) : COLOR { return color; } ================================================ FILE: shaders/vertex_v.cg ================================================ void main( float3 aPosition, uniform float4x4 gl_ModelViewProjectionMatrix, float4 out vPosition: POSITION) { vPosition = mul(gl_ModelViewProjectionMatrix, float4(aPosition, 1.f)); } ================================================ FILE: source/adivtab.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // table of quotients and remainders for [-15...16] / [-15...16] // numerator = -15 {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {1, -6}, {1, -7}, {2, -1}, {2, -3}, {3, 0}, {3, -3}, {5, 0}, {7, -1}, {15, 0}, {0, 0}, {-15, 0}, {-8, 1}, {-5, 0}, {-4, 1}, {-3, 0}, {-3, 3}, {-3, 6}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-2, 9}, {-2, 11}, {-2, 13}, {-1, 0}, {-1, 1}, // numerator = -14 {0, -14}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {1, -6}, {2, 0}, {2, -2}, {2, -4}, {3, -2}, {4, -2}, {7, 0}, {14, 0}, {0, 0}, {-14, 0}, {-7, 0}, {-5, 1}, {-4, 2}, {-3, 1}, {-3, 4}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-2, 8}, {-2, 10}, {-2, 12}, {-1, 0}, {-1, 1}, {-1, 2}, // numerator = -13 {0, -13}, {0, -13}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {1, -6}, {2, -1}, {2, -3}, {3, -1}, {4, -1}, {6, -1}, {13, 0}, {0, 0}, {-13, 0}, {-7, 1}, {-5, 2}, {-4, 3}, {-3, 2}, {-3, 5}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-2, 9}, {-2, 11}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, // numerator = -12 {0, -12}, {0, -12}, {0, -12}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {2, 0}, {2, -2}, {3, 0}, {4, 0}, {6, 0}, {12, 0}, {0, 0}, {-12, 0}, {-6, 0}, {-4, 0}, {-3, 0}, {-3, 3}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-2, 8}, {-2, 10}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, // numerator = -11 {0, -11}, {0, -11}, {0, -11}, {0, -11}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {1, -5}, {2, -1}, {2, -3}, {3, -2}, {5, -1}, {11, 0}, {0, 0}, {-11, 0}, {-6, 1}, {-4, 1}, {-3, 1}, {-3, 4}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-2, 9}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, // numerator = -10 {0, -10}, {0, -10}, {0, -10}, {0, -10}, {0, -10}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {2, 0}, {2, -2}, {3, -1}, {5, 0}, {10, 0}, {0, 0}, {-10, 0}, {-5, 0}, {-4, 2}, {-3, 2}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-2, 8}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, // numerator = -9 {0, -9}, {0, -9}, {0, -9}, {0, -9}, {0, -9}, {0, -9}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {1, -4}, {2, -1}, {3, 0}, {4, -1}, {9, 0}, {0, 0}, {-9, 0}, {-5, 1}, {-3, 0}, {-3, 3}, {-2, 1}, {-2, 3}, {-2, 5}, {-2, 7}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, // numerator = -8 {0, -8}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {0, -8}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {2, 0}, {2, -2}, {4, 0}, {8, 0}, {0, 0}, {-8, 0}, {-4, 0}, {-3, 1}, {-2, 0}, {-2, 2}, {-2, 4}, {-2, 6}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, // numerator = -7 {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {0, -7}, {1, 0}, {1, -1}, {1, -2}, {1, -3}, {2, -1}, {3, -1}, {7, 0}, {0, 0}, {-7, 0}, {-4, 1}, {-3, 2}, {-2, 1}, {-2, 3}, {-2, 5}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, // numerator = -6 {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {0, -6}, {1, 0}, {1, -1}, {1, -2}, {2, 0}, {3, 0}, {6, 0}, {0, 0}, {-6, 0}, {-3, 0}, {-2, 0}, {-2, 2}, {-2, 4}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, // numerator = -5 {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {0, -5}, {1, 0}, {1, -1}, {1, -2}, {2, -1}, {5, 0}, {0, 0}, {-5, 0}, {-3, 1}, {-2, 1}, {-2, 3}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, // numerator = -4 {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {0, -4}, {1, 0}, {1, -1}, {2, 0}, {4, 0}, {0, 0}, {-4, 0}, {-2, 0}, {-2, 2}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, // numerator = -3 {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {0, -3}, {1, 0}, {1, -1}, {3, 0}, {0, 0}, {-3, 0}, {-2, 1}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {-1, 13}, // numerator = -2 {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {0, -2}, {1, 0}, {2, 0}, {0, 0}, {-2, 0}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {-1, 13}, {-1, 14}, // numerator = -1 {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {1, 0}, {0, 0}, {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3}, {-1, 4}, {-1, 5}, {-1, 6}, {-1, 7}, {-1, 8}, {-1, 9}, {-1, 10}, {-1, 11}, {-1, 12}, {-1, 13}, {-1, 14}, {-1, 15}, // numerator = 0 {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, // numerator = 1 {-1, -14}, {-1, -13}, {-1, -12}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {0, 0}, {1, 0}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, // numerator = 2 {-1, -13}, {-1, -12}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, 0}, {0, 0}, {2, 0}, {1, 0}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, {0, 2}, // numerator = 3 {-1, -12}, {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -1}, {-3, 0}, {0, 0}, {3, 0}, {1, 1}, {1, 0}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, {0, 3}, // numerator = 4 {-1, -11}, {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -2}, {-2, 0}, {-4, 0}, {0, 0}, {4, 0}, {2, 0}, {1, 1}, {1, 0}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, {0, 4}, // numerator = 5 {-1, -10}, {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -3}, {-2, -1}, {-3, -1}, {-5, 0}, {0, 0}, {5, 0}, {2, 1}, {1, 2}, {1, 1}, {1, 0}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, {0, 5}, // numerator = 6 {-1, -9}, {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, 0}, {-6, 0}, {0, 0}, {6, 0}, {3, 0}, {2, 0}, {1, 2}, {1, 1}, {1, 0}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, {0, 6}, // numerator = 7 {-1, -8}, {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -2}, {-4, -1}, {-7, 0}, {0, 0}, {7, 0}, {3, 1}, {2, 1}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, {0, 7}, // numerator = 8 {-1, -7}, {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -1}, {-4, 0}, {-8, 0}, {0, 0}, {8, 0}, {4, 0}, {2, 2}, {2, 0}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, {0, 8}, // numerator = 9 {-1, -6}, {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -3}, {-3, 0}, {-5, -1}, {-9, 0}, {0, 0}, {9, 0}, {4, 1}, {3, 0}, {2, 1}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, {0, 9}, // numerator = 10 {-1, -5}, {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -2}, {-4, -2}, {-5, 0}, {-10, 0}, {0, 0}, {10, 0}, {5, 0}, {3, 1}, {2, 2}, {2, 0}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 10}, {0, 10}, {0, 10}, {0, 10}, {0, 10}, {0, 10}, // numerator = 11 {-1, -4}, {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -9}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -4}, {-3, -1}, {-4, -1}, {-6, -1}, {-11, 0}, {0, 0}, {11, 0}, {5, 1}, {3, 2}, {2, 3}, {2, 1}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 11}, {0, 11}, {0, 11}, {0, 11}, {0, 11}, // numerator = 12 {-1, -3}, {-1, -2}, {-1, -1}, {-1, 0}, {-2, -10}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -3}, {-3, 0}, {-4, 0}, {-6, 0}, {-12, 0}, {0, 0}, {12, 0}, {6, 0}, {4, 0}, {3, 0}, {2, 2}, {2, 0}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 12}, {0, 12}, {0, 12}, {0, 12}, // numerator = 13 {-1, -2}, {-1, -1}, {-1, 0}, {-2, -11}, {-2, -9}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -5}, {-3, -2}, {-4, -3}, {-5, -2}, {-7, -1}, {-13, 0}, {0, 0}, {13, 0}, {6, 1}, {4, 1}, {3, 1}, {2, 3}, {2, 1}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 13}, {0, 13}, {0, 13}, // numerator = 14 {-1, -1}, {-1, 0}, {-2, -12}, {-2, -10}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -4}, {-3, -1}, {-4, -2}, {-5, -1}, {-7, 0}, {-14, 0}, {0, 0}, {14, 0}, {7, 0}, {4, 2}, {3, 2}, {2, 4}, {2, 2}, {2, 0}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 14}, {0, 14}, // numerator = 15 {-1, 0}, {-2, -13}, {-2, -11}, {-2, -9}, {-2, -7}, {-2, -5}, {-2, -3}, {-2, -1}, {-3, -6}, {-3, -3}, {-3, 0}, {-4, -1}, {-5, 0}, {-8, -1}, {-15, 0}, {0, 0}, {15, 0}, {7, 1}, {5, 0}, {3, 3}, {3, 0}, {2, 3}, {2, 1}, {1, 7}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, {0, 15}, // numerator = 16 {-2, -14}, {-2, -12}, {-2, -10}, {-2, -8}, {-2, -6}, {-2, -4}, {-2, -2}, {-2, 0}, {-3, -5}, {-3, -2}, {-4, -4}, {-4, 0}, {-6, -2}, {-8, 0}, {-16, 0}, {0, 0}, {16, 0}, {8, 0}, {5, 1}, {4, 0}, {3, 1}, {2, 4}, {2, 2}, {2, 0}, {1, 7}, {1, 6}, {1, 5}, {1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}, ================================================ FILE: source/anorm_dots.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ { {1.23,1.30,1.47,1.35,1.56,1.71,1.37,1.38,1.59,1.60,1.79,1.97,1.88,1.92,1.79,1.02,0.93,1.07,0.82,0.87,0.88,0.94,0.96,1.14,1.11,0.82,0.83,0.89,0.89,0.86,0.94,0.91,1.00,1.21,0.98,1.48,1.30,1.57,0.96,1.07,1.14,1.60,1.61,1.40,1.37,1.72,1.78,1.79,1.93,1.99,1.90,1.68,1.71,1.86,1.60,1.68,1.78,1.86,1.93,1.99,1.97,1.44,1.22,1.49,0.93,0.99,0.99,1.23,1.22,1.44,1.49,0.89,0.89,0.97,0.91,0.98,1.19,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.19,0.98,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.87,0.93,0.94,1.02,1.30,1.07,1.35,1.38,1.11,1.56,1.92,1.79,1.79,1.59,1.60,1.72,1.90,1.79,0.80,0.85,0.79,0.93,0.80,0.85,0.77,0.74,0.72,0.77,0.74,0.72,0.70,0.70,0.71,0.76,0.73,0.79,0.79,0.73,0.76,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.26,1.26,1.48,1.23,1.50,1.71,1.14,1.19,1.38,1.46,1.64,1.94,1.87,1.84,1.71,1.02,0.92,1.00,0.79,0.85,0.84,0.91,0.90,0.98,0.99,0.77,0.77,0.83,0.82,0.79,0.86,0.84,0.92,0.99,0.91,1.24,1.03,1.33,0.88,0.94,0.97,1.41,1.39,1.18,1.11,1.51,1.61,1.59,1.80,1.91,1.76,1.54,1.65,1.76,1.70,1.70,1.85,1.85,1.97,1.99,1.93,1.28,1.09,1.39,0.92,0.97,0.99,1.18,1.26,1.52,1.48,0.83,0.85,0.90,0.88,0.93,1.00,0.77,0.73,0.78,0.72,0.71,0.74,0.75,0.79,0.86,0.81,0.75,0.81,0.79,0.96,0.88,0.94,0.86,0.93,0.92,0.85,1.08,1.33,1.05,1.55,1.31,1.01,1.05,1.27,1.31,1.60,1.47,1.70,1.54,1.76,1.76,1.57,0.93,0.90,0.99,0.88,0.88,0.95,0.97,1.11,1.39,1.20,0.92,0.97,1.01,1.10,1.39,1.22,1.51,1.58,1.32,1.64,1.97,1.85,1.91,1.77,1.74,1.88,1.99,1.91,0.79,0.86,0.80,0.94,0.84,0.88,0.74,0.74,0.71,0.82,0.77,0.76,0.70,0.73,0.72,0.73,0.70,0.74,0.85,0.77,0.82,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.34,1.27,1.53,1.17,1.46,1.71,0.98,1.05,1.20,1.34,1.48,1.86,1.82,1.71,1.62,1.09,0.94,0.99,0.79,0.85,0.82,0.90,0.87,0.93,0.96,0.76,0.74,0.79,0.76,0.74,0.79,0.78,0.85,0.92,0.85,1.00,0.93,1.06,0.81,0.86,0.89,1.16,1.12,0.97,0.95,1.28,1.38,1.35,1.60,1.77,1.57,1.33,1.50,1.58,1.69,1.63,1.82,1.74,1.91,1.92,1.80,1.04,0.97,1.21,0.90,0.93,0.97,1.05,1.21,1.48,1.37,0.77,0.80,0.84,0.85,0.88,0.92,0.73,0.71,0.74,0.74,0.71,0.75,0.73,0.79,0.84,0.78,0.79,0.86,0.81,1.05,0.94,0.99,0.90,0.95,0.92,0.86,1.24,1.44,1.14,1.59,1.34,1.02,1.27,1.50,1.49,1.80,1.69,1.86,1.72,1.87,1.80,1.69,1.00,0.98,1.23,0.95,0.96,1.09,1.16,1.37,1.63,1.46,0.99,1.10,1.25,1.24,1.51,1.41,1.67,1.77,1.55,1.72,1.95,1.89,1.98,1.91,1.86,1.97,1.99,1.94,0.81,0.89,0.85,0.98,0.90,0.94,0.75,0.78,0.73,0.89,0.83,0.82,0.72,0.77,0.76,0.72,0.70,0.71,0.91,0.83,0.89,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.46,1.34,1.60,1.16,1.46,1.71,0.94,0.99,1.05,1.26,1.33,1.74,1.76,1.57,1.54,1.23,0.98,1.05,0.83,0.89,0.84,0.92,0.87,0.91,0.96,0.78,0.74,0.79,0.72,0.72,0.75,0.76,0.80,0.88,0.83,0.94,0.87,0.95,0.76,0.80,0.82,0.97,0.96,0.89,0.88,1.08,1.11,1.10,1.37,1.59,1.37,1.07,1.27,1.34,1.57,1.45,1.69,1.55,1.77,1.79,1.60,0.93,0.90,0.99,0.86,0.87,0.93,0.96,1.07,1.35,1.18,0.73,0.76,0.77,0.81,0.82,0.85,0.70,0.71,0.72,0.78,0.73,0.77,0.73,0.79,0.82,0.76,0.83,0.90,0.84,1.18,0.98,1.03,0.92,0.95,0.90,0.86,1.32,1.45,1.15,1.53,1.27,0.99,1.42,1.65,1.58,1.93,1.83,1.94,1.81,1.88,1.74,1.70,1.19,1.17,1.44,1.11,1.15,1.36,1.41,1.61,1.81,1.67,1.22,1.34,1.50,1.42,1.65,1.61,1.82,1.91,1.75,1.80,1.89,1.89,1.98,1.99,1.94,1.98,1.92,1.87,0.86,0.95,0.92,1.14,0.98,1.03,0.79,0.84,0.77,0.97,0.90,0.89,0.76,0.82,0.82,0.74,0.72,0.71,0.98,0.89,0.97,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.60,1.44,1.68,1.22,1.49,1.71,0.93,0.99,0.99,1.23,1.22,1.60,1.68,1.44,1.49,1.40,1.14,1.19,0.89,0.96,0.89,0.97,0.89,0.91,0.98,0.82,0.76,0.82,0.71,0.72,0.73,0.76,0.79,0.86,0.83,0.91,0.83,0.89,0.72,0.76,0.76,0.89,0.89,0.82,0.82,0.98,0.96,0.97,1.14,1.40,1.19,0.94,1.00,1.07,1.37,1.21,1.48,1.30,1.57,1.61,1.37,0.86,0.83,0.91,0.82,0.82,0.88,0.89,0.96,1.14,0.98,0.70,0.72,0.73,0.77,0.76,0.79,0.70,0.72,0.71,0.82,0.77,0.80,0.74,0.79,0.80,0.74,0.87,0.93,0.85,1.23,1.02,1.02,0.93,0.93,0.87,0.85,1.30,1.35,1.07,1.38,1.11,0.94,1.47,1.71,1.56,1.97,1.88,1.92,1.79,1.79,1.59,1.60,1.30,1.35,1.56,1.37,1.38,1.59,1.60,1.79,1.92,1.79,1.48,1.57,1.72,1.61,1.78,1.79,1.93,1.99,1.90,1.86,1.78,1.86,1.93,1.99,1.97,1.90,1.79,1.72,0.94,1.07,1.00,1.37,1.21,1.30,0.86,0.91,0.83,1.14,0.98,0.96,0.82,0.88,0.89,0.79,0.76,0.73,1.07,0.94,1.11,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.74,1.57,1.76,1.33,1.54,1.71,0.94,1.05,0.99,1.26,1.16,1.46,1.60,1.34,1.46,1.59,1.37,1.37,0.97,1.11,0.96,1.10,0.95,0.94,1.08,0.89,0.82,0.88,0.72,0.76,0.75,0.80,0.80,0.88,0.87,0.91,0.83,0.87,0.72,0.76,0.74,0.83,0.84,0.78,0.79,0.96,0.89,0.92,0.98,1.23,1.05,0.86,0.92,0.95,1.11,0.98,1.22,1.03,1.34,1.42,1.14,0.79,0.77,0.84,0.78,0.76,0.82,0.82,0.89,0.97,0.90,0.70,0.71,0.71,0.73,0.72,0.74,0.73,0.76,0.72,0.86,0.81,0.82,0.76,0.79,0.77,0.73,0.90,0.95,0.86,1.18,1.03,0.98,0.92,0.90,0.83,0.84,1.19,1.17,0.98,1.15,0.97,0.89,1.42,1.65,1.44,1.93,1.83,1.81,1.67,1.61,1.36,1.41,1.32,1.45,1.58,1.57,1.53,1.74,1.70,1.88,1.94,1.81,1.69,1.77,1.87,1.79,1.89,1.92,1.98,1.99,1.98,1.89,1.65,1.80,1.82,1.91,1.94,1.75,1.61,1.50,1.07,1.34,1.27,1.60,1.45,1.55,0.93,0.99,0.90,1.35,1.18,1.07,0.87,0.93,0.96,0.85,0.82,0.77,1.15,0.99,1.27,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.86,1.71,1.82,1.48,1.62,1.71,0.98,1.20,1.05,1.34,1.17,1.34,1.53,1.27,1.46,1.77,1.60,1.57,1.16,1.38,1.12,1.35,1.06,1.00,1.28,0.97,0.89,0.95,0.76,0.81,0.79,0.86,0.85,0.92,0.93,0.93,0.85,0.87,0.74,0.78,0.74,0.79,0.82,0.76,0.79,0.96,0.85,0.90,0.94,1.09,0.99,0.81,0.85,0.89,0.95,0.90,0.99,0.94,1.10,1.24,0.98,0.75,0.73,0.78,0.74,0.72,0.77,0.76,0.82,0.89,0.83,0.73,0.71,0.71,0.71,0.70,0.72,0.77,0.80,0.74,0.90,0.85,0.84,0.78,0.79,0.75,0.73,0.92,0.95,0.86,1.05,0.99,0.94,0.90,0.86,0.79,0.81,1.00,0.98,0.91,0.96,0.89,0.83,1.27,1.50,1.23,1.80,1.69,1.63,1.46,1.37,1.09,1.16,1.24,1.44,1.49,1.69,1.59,1.80,1.69,1.87,1.86,1.72,1.82,1.91,1.94,1.92,1.95,1.99,1.98,1.91,1.97,1.89,1.51,1.72,1.67,1.77,1.86,1.55,1.41,1.25,1.33,1.58,1.50,1.80,1.63,1.74,1.04,1.21,0.97,1.48,1.37,1.21,0.93,0.97,1.05,0.92,0.88,0.84,1.14,1.02,1.34,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.94,1.84,1.87,1.64,1.71,1.71,1.14,1.38,1.19,1.46,1.23,1.26,1.48,1.26,1.50,1.91,1.80,1.76,1.41,1.61,1.39,1.59,1.33,1.24,1.51,1.18,0.97,1.11,0.82,0.88,0.86,0.94,0.92,0.99,1.03,0.98,0.91,0.90,0.79,0.84,0.77,0.79,0.84,0.77,0.83,0.99,0.85,0.91,0.92,1.02,1.00,0.79,0.80,0.86,0.88,0.84,0.92,0.88,0.97,1.10,0.94,0.74,0.71,0.74,0.72,0.70,0.73,0.72,0.76,0.82,0.77,0.77,0.73,0.74,0.71,0.70,0.73,0.83,0.85,0.78,0.92,0.88,0.86,0.81,0.79,0.74,0.75,0.92,0.93,0.85,0.96,0.94,0.88,0.86,0.81,0.75,0.79,0.93,0.90,0.85,0.88,0.82,0.77,1.05,1.27,0.99,1.60,1.47,1.39,1.20,1.11,0.95,0.97,1.08,1.33,1.31,1.70,1.55,1.76,1.57,1.76,1.70,1.54,1.85,1.97,1.91,1.99,1.97,1.99,1.91,1.77,1.88,1.85,1.39,1.64,1.51,1.58,1.74,1.32,1.22,1.01,1.54,1.76,1.65,1.93,1.70,1.85,1.28,1.39,1.09,1.52,1.48,1.26,0.97,0.99,1.18,1.00,0.93,0.90,1.05,1.01,1.31,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.97,1.92,1.88,1.79,1.79,1.71,1.37,1.59,1.38,1.60,1.35,1.23,1.47,1.30,1.56,1.99,1.93,1.90,1.60,1.78,1.61,1.79,1.57,1.48,1.72,1.40,1.14,1.37,0.89,0.96,0.94,1.07,1.00,1.21,1.30,1.14,0.98,0.96,0.86,0.91,0.83,0.82,0.88,0.82,0.89,1.11,0.87,0.94,0.93,1.02,1.07,0.80,0.79,0.85,0.82,0.80,0.87,0.85,0.93,1.02,0.93,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.82,0.76,0.79,0.72,0.73,0.76,0.89,0.89,0.82,0.93,0.91,0.86,0.83,0.79,0.73,0.76,0.91,0.89,0.83,0.89,0.89,0.82,0.82,0.76,0.72,0.76,0.86,0.83,0.79,0.82,0.76,0.73,0.94,1.00,0.91,1.37,1.21,1.14,0.98,0.96,0.88,0.89,0.96,1.14,1.07,1.60,1.40,1.61,1.37,1.57,1.48,1.30,1.78,1.93,1.79,1.99,1.92,1.90,1.79,1.59,1.72,1.79,1.30,1.56,1.35,1.38,1.60,1.11,1.07,0.94,1.68,1.86,1.71,1.97,1.68,1.86,1.44,1.49,1.22,1.44,1.49,1.22,0.99,0.99,1.23,1.19,0.98,0.97,0.97,0.98,1.19,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.94,1.97,1.87,1.91,1.85,1.71,1.60,1.77,1.58,1.74,1.51,1.26,1.48,1.39,1.64,1.99,1.97,1.99,1.70,1.85,1.76,1.91,1.76,1.70,1.88,1.55,1.33,1.57,0.96,1.08,1.05,1.31,1.27,1.47,1.54,1.39,1.20,1.11,0.93,0.99,0.90,0.88,0.95,0.88,0.97,1.32,0.92,1.01,0.97,1.10,1.22,0.84,0.80,0.88,0.79,0.79,0.85,0.86,0.92,1.02,0.94,0.82,0.76,0.77,0.72,0.73,0.70,0.72,0.71,0.74,0.74,0.88,0.81,0.85,0.75,0.77,0.82,0.94,0.93,0.86,0.92,0.92,0.86,0.85,0.79,0.74,0.79,0.88,0.85,0.81,0.82,0.83,0.77,0.78,0.73,0.71,0.75,0.79,0.77,0.74,0.77,0.73,0.70,0.86,0.92,0.84,1.14,0.99,0.98,0.91,0.90,0.84,0.83,0.88,0.97,0.94,1.41,1.18,1.39,1.11,1.33,1.24,1.03,1.61,1.80,1.59,1.91,1.84,1.76,1.64,1.38,1.51,1.71,1.26,1.50,1.23,1.19,1.46,0.99,1.00,0.91,1.70,1.85,1.65,1.93,1.54,1.76,1.52,1.48,1.26,1.28,1.39,1.09,0.99,0.97,1.18,1.31,1.01,1.05,0.90,0.93,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.86,1.95,1.82,1.98,1.89,1.71,1.80,1.91,1.77,1.86,1.67,1.34,1.53,1.51,1.72,1.92,1.91,1.99,1.69,1.82,1.80,1.94,1.87,1.86,1.97,1.59,1.44,1.69,1.05,1.24,1.27,1.49,1.50,1.69,1.72,1.63,1.46,1.37,1.00,1.23,0.98,0.95,1.09,0.96,1.16,1.55,0.99,1.25,1.10,1.24,1.41,0.90,0.85,0.94,0.79,0.81,0.85,0.89,0.94,1.09,0.98,0.89,0.82,0.83,0.74,0.77,0.72,0.76,0.73,0.75,0.78,0.94,0.86,0.91,0.79,0.83,0.89,0.99,0.95,0.90,0.90,0.92,0.84,0.86,0.79,0.75,0.81,0.85,0.80,0.78,0.76,0.77,0.73,0.74,0.71,0.71,0.73,0.74,0.74,0.71,0.76,0.72,0.70,0.79,0.85,0.78,0.98,0.92,0.93,0.85,0.87,0.82,0.79,0.81,0.89,0.86,1.16,0.97,1.12,0.95,1.06,1.00,0.93,1.38,1.60,1.35,1.77,1.71,1.57,1.48,1.20,1.28,1.62,1.27,1.46,1.17,1.05,1.34,0.96,0.99,0.90,1.63,1.74,1.50,1.80,1.33,1.58,1.48,1.37,1.21,1.04,1.21,0.97,0.97,0.93,1.05,1.34,1.02,1.14,0.84,0.88,0.92,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.74,1.89,1.76,1.98,1.89,1.71,1.93,1.99,1.91,1.94,1.82,1.46,1.60,1.65,1.80,1.79,1.77,1.92,1.57,1.69,1.74,1.87,1.88,1.94,1.98,1.53,1.45,1.70,1.18,1.32,1.42,1.58,1.65,1.83,1.81,1.81,1.67,1.61,1.19,1.44,1.17,1.11,1.36,1.15,1.41,1.75,1.22,1.50,1.34,1.42,1.61,0.98,0.92,1.03,0.83,0.86,0.89,0.95,0.98,1.23,1.14,0.97,0.89,0.90,0.78,0.82,0.76,0.82,0.77,0.79,0.84,0.98,0.90,0.98,0.83,0.89,0.97,1.03,0.95,0.92,0.86,0.90,0.82,0.86,0.79,0.77,0.84,0.81,0.76,0.76,0.72,0.73,0.70,0.72,0.71,0.73,0.73,0.72,0.74,0.71,0.78,0.74,0.72,0.75,0.80,0.76,0.94,0.88,0.91,0.83,0.87,0.84,0.79,0.76,0.82,0.80,0.97,0.89,0.96,0.88,0.95,0.94,0.87,1.11,1.37,1.10,1.59,1.57,1.37,1.33,1.05,1.08,1.54,1.34,1.46,1.16,0.99,1.26,0.96,1.05,0.92,1.45,1.55,1.27,1.60,1.07,1.34,1.35,1.18,1.07,0.93,0.99,0.90,0.93,0.87,0.96,1.27,0.99,1.15,0.77,0.82,0.85,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.60,1.78,1.68,1.93,1.86,1.71,1.97,1.99,1.99,1.97,1.93,1.60,1.68,1.78,1.86,1.61,1.57,1.79,1.37,1.48,1.59,1.72,1.79,1.92,1.90,1.38,1.35,1.60,1.23,1.30,1.47,1.56,1.71,1.88,1.79,1.92,1.79,1.79,1.30,1.56,1.35,1.37,1.59,1.38,1.60,1.90,1.48,1.72,1.57,1.61,1.79,1.21,1.00,1.30,0.89,0.94,0.96,1.07,1.14,1.40,1.37,1.14,0.96,0.98,0.82,0.88,0.82,0.89,0.83,0.86,0.91,1.02,0.93,1.07,0.87,0.94,1.11,1.02,0.93,0.93,0.82,0.87,0.80,0.85,0.79,0.80,0.85,0.77,0.72,0.74,0.71,0.70,0.70,0.71,0.72,0.77,0.74,0.72,0.76,0.73,0.82,0.79,0.76,0.73,0.79,0.76,0.93,0.86,0.91,0.83,0.89,0.89,0.82,0.72,0.76,0.76,0.89,0.82,0.89,0.82,0.89,0.91,0.83,0.96,1.14,0.97,1.40,1.44,1.19,1.22,0.99,0.98,1.49,1.44,1.49,1.22,0.99,1.23,0.98,1.19,0.97,1.21,1.30,1.00,1.37,0.94,1.07,1.14,0.98,0.96,0.86,0.91,0.83,0.88,0.82,0.89,1.11,0.94,1.07,0.73,0.76,0.79,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.46,1.65,1.60,1.82,1.80,1.71,1.93,1.91,1.99,1.94,1.98,1.74,1.76,1.89,1.89,1.42,1.34,1.61,1.11,1.22,1.36,1.50,1.61,1.81,1.75,1.15,1.17,1.41,1.18,1.19,1.42,1.44,1.65,1.83,1.67,1.94,1.81,1.88,1.32,1.58,1.45,1.57,1.74,1.53,1.70,1.98,1.69,1.87,1.77,1.79,1.92,1.45,1.27,1.55,0.97,1.07,1.11,1.34,1.37,1.59,1.60,1.35,1.07,1.18,0.86,0.93,0.87,0.96,0.90,0.93,0.99,1.03,0.95,1.15,0.90,0.99,1.27,0.98,0.90,0.92,0.78,0.83,0.77,0.84,0.79,0.82,0.86,0.73,0.71,0.73,0.72,0.70,0.73,0.72,0.76,0.81,0.76,0.76,0.82,0.77,0.89,0.85,0.82,0.75,0.80,0.80,0.94,0.88,0.94,0.87,0.95,0.96,0.88,0.72,0.74,0.76,0.83,0.78,0.84,0.79,0.87,0.91,0.83,0.89,0.98,0.92,1.23,1.34,1.05,1.16,0.99,0.96,1.46,1.57,1.54,1.33,1.05,1.26,1.08,1.37,1.10,0.98,1.03,0.92,1.14,0.86,0.95,0.97,0.90,0.89,0.79,0.84,0.77,0.82,0.76,0.82,0.97,0.89,0.98,0.71,0.72,0.74,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.34,1.51,1.53,1.67,1.72,1.71,1.80,1.77,1.91,1.86,1.98,1.86,1.82,1.95,1.89,1.24,1.10,1.41,0.95,0.99,1.09,1.25,1.37,1.63,1.55,0.96,0.98,1.16,1.05,1.00,1.27,1.23,1.50,1.69,1.46,1.86,1.72,1.87,1.24,1.49,1.44,1.69,1.80,1.59,1.69,1.97,1.82,1.94,1.91,1.92,1.99,1.63,1.50,1.74,1.16,1.33,1.38,1.58,1.60,1.77,1.80,1.48,1.21,1.37,0.90,0.97,0.93,1.05,0.97,1.04,1.21,0.99,0.95,1.14,0.92,1.02,1.34,0.94,0.86,0.90,0.74,0.79,0.75,0.81,0.79,0.84,0.86,0.71,0.71,0.73,0.76,0.73,0.77,0.74,0.80,0.85,0.78,0.81,0.89,0.84,0.97,0.92,0.88,0.79,0.85,0.86,0.98,0.92,1.00,0.93,1.06,1.12,0.95,0.74,0.74,0.78,0.79,0.76,0.82,0.79,0.87,0.93,0.85,0.85,0.94,0.90,1.09,1.27,0.99,1.17,1.05,0.96,1.46,1.71,1.62,1.48,1.20,1.34,1.28,1.57,1.35,0.90,0.94,0.85,0.98,0.81,0.89,0.89,0.83,0.82,0.75,0.78,0.73,0.77,0.72,0.76,0.89,0.83,0.91,0.71,0.70,0.72,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00}, {1.26,1.39,1.48,1.51,1.64,1.71,1.60,1.58,1.77,1.74,1.91,1.94,1.87,1.97,1.85,1.10,0.97,1.22,0.88,0.92,0.95,1.01,1.11,1.39,1.32,0.88,0.90,0.97,0.96,0.93,1.05,0.99,1.27,1.47,1.20,1.70,1.54,1.76,1.08,1.31,1.33,1.70,1.76,1.55,1.57,1.88,1.85,1.91,1.97,1.99,1.99,1.70,1.65,1.85,1.41,1.54,1.61,1.76,1.80,1.91,1.93,1.52,1.26,1.48,0.92,0.99,0.97,1.18,1.09,1.28,1.39,0.94,0.93,1.05,0.92,1.01,1.31,0.88,0.81,0.86,0.72,0.75,0.74,0.79,0.79,0.86,0.85,0.71,0.73,0.75,0.82,0.77,0.83,0.78,0.85,0.88,0.81,0.88,0.97,0.90,1.18,1.00,0.93,0.86,0.92,0.94,1.14,0.99,1.24,1.03,1.33,1.39,1.11,0.79,0.77,0.84,0.79,0.77,0.84,0.83,0.90,0.98,0.91,0.85,0.92,0.91,1.02,1.26,1.00,1.23,1.19,0.99,1.50,1.84,1.71,1.64,1.38,1.46,1.51,1.76,1.59,0.84,0.88,0.80,0.94,0.79,0.86,0.82,0.77,0.76,0.74,0.74,0.71,0.73,0.70,0.72,0.82,0.77,0.85,0.74,0.70,0.73,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00,1.00} } ================================================ FILE: source/anorms.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ {-0.525731, 0.000000, 0.850651}, {-0.442863, 0.238856, 0.864188}, {-0.295242, 0.000000, 0.955423}, {-0.309017, 0.500000, 0.809017}, {-0.162460, 0.262866, 0.951056}, {0.000000, 0.000000, 1.000000}, {0.000000, 0.850651, 0.525731}, {-0.147621, 0.716567, 0.681718}, {0.147621, 0.716567, 0.681718}, {0.000000, 0.525731, 0.850651}, {0.309017, 0.500000, 0.809017}, {0.525731, 0.000000, 0.850651}, {0.295242, 0.000000, 0.955423}, {0.442863, 0.238856, 0.864188}, {0.162460, 0.262866, 0.951056}, {-0.681718, 0.147621, 0.716567}, {-0.809017, 0.309017, 0.500000}, {-0.587785, 0.425325, 0.688191}, {-0.850651, 0.525731, 0.000000}, {-0.864188, 0.442863, 0.238856}, {-0.716567, 0.681718, 0.147621}, {-0.688191, 0.587785, 0.425325}, {-0.500000, 0.809017, 0.309017}, {-0.238856, 0.864188, 0.442863}, {-0.425325, 0.688191, 0.587785}, {-0.716567, 0.681718, -0.147621}, {-0.500000, 0.809017, -0.309017}, {-0.525731, 0.850651, 0.000000}, {0.000000, 0.850651, -0.525731}, {-0.238856, 0.864188, -0.442863}, {0.000000, 0.955423, -0.295242}, {-0.262866, 0.951056, -0.162460}, {0.000000, 1.000000, 0.000000}, {0.000000, 0.955423, 0.295242}, {-0.262866, 0.951056, 0.162460}, {0.238856, 0.864188, 0.442863}, {0.262866, 0.951056, 0.162460}, {0.500000, 0.809017, 0.309017}, {0.238856, 0.864188, -0.442863}, {0.262866, 0.951056, -0.162460}, {0.500000, 0.809017, -0.309017}, {0.850651, 0.525731, 0.000000}, {0.716567, 0.681718, 0.147621}, {0.716567, 0.681718, -0.147621}, {0.525731, 0.850651, 0.000000}, {0.425325, 0.688191, 0.587785}, {0.864188, 0.442863, 0.238856}, {0.688191, 0.587785, 0.425325}, {0.809017, 0.309017, 0.500000}, {0.681718, 0.147621, 0.716567}, {0.587785, 0.425325, 0.688191}, {0.955423, 0.295242, 0.000000}, {1.000000, 0.000000, 0.000000}, {0.951056, 0.162460, 0.262866}, {0.850651, -0.525731, 0.000000}, {0.955423, -0.295242, 0.000000}, {0.864188, -0.442863, 0.238856}, {0.951056, -0.162460, 0.262866}, {0.809017, -0.309017, 0.500000}, {0.681718, -0.147621, 0.716567}, {0.850651, 0.000000, 0.525731}, {0.864188, 0.442863, -0.238856}, {0.809017, 0.309017, -0.500000}, {0.951056, 0.162460, -0.262866}, {0.525731, 0.000000, -0.850651}, {0.681718, 0.147621, -0.716567}, {0.681718, -0.147621, -0.716567}, {0.850651, 0.000000, -0.525731}, {0.809017, -0.309017, -0.500000}, {0.864188, -0.442863, -0.238856}, {0.951056, -0.162460, -0.262866}, {0.147621, 0.716567, -0.681718}, {0.309017, 0.500000, -0.809017}, {0.425325, 0.688191, -0.587785}, {0.442863, 0.238856, -0.864188}, {0.587785, 0.425325, -0.688191}, {0.688191, 0.587785, -0.425325}, {-0.147621, 0.716567, -0.681718}, {-0.309017, 0.500000, -0.809017}, {0.000000, 0.525731, -0.850651}, {-0.525731, 0.000000, -0.850651}, {-0.442863, 0.238856, -0.864188}, {-0.295242, 0.000000, -0.955423}, {-0.162460, 0.262866, -0.951056}, {0.000000, 0.000000, -1.000000}, {0.295242, 0.000000, -0.955423}, {0.162460, 0.262866, -0.951056}, {-0.442863, -0.238856, -0.864188}, {-0.309017, -0.500000, -0.809017}, {-0.162460, -0.262866, -0.951056}, {0.000000, -0.850651, -0.525731}, {-0.147621, -0.716567, -0.681718}, {0.147621, -0.716567, -0.681718}, {0.000000, -0.525731, -0.850651}, {0.309017, -0.500000, -0.809017}, {0.442863, -0.238856, -0.864188}, {0.162460, -0.262866, -0.951056}, {0.238856, -0.864188, -0.442863}, {0.500000, -0.809017, -0.309017}, {0.425325, -0.688191, -0.587785}, {0.716567, -0.681718, -0.147621}, {0.688191, -0.587785, -0.425325}, {0.587785, -0.425325, -0.688191}, {0.000000, -0.955423, -0.295242}, {0.000000, -1.000000, 0.000000}, {0.262866, -0.951056, -0.162460}, {0.000000, -0.850651, 0.525731}, {0.000000, -0.955423, 0.295242}, {0.238856, -0.864188, 0.442863}, {0.262866, -0.951056, 0.162460}, {0.500000, -0.809017, 0.309017}, {0.716567, -0.681718, 0.147621}, {0.525731, -0.850651, 0.000000}, {-0.238856, -0.864188, -0.442863}, {-0.500000, -0.809017, -0.309017}, {-0.262866, -0.951056, -0.162460}, {-0.850651, -0.525731, 0.000000}, {-0.716567, -0.681718, -0.147621}, {-0.716567, -0.681718, 0.147621}, {-0.525731, -0.850651, 0.000000}, {-0.500000, -0.809017, 0.309017}, {-0.238856, -0.864188, 0.442863}, {-0.262866, -0.951056, 0.162460}, {-0.864188, -0.442863, 0.238856}, {-0.809017, -0.309017, 0.500000}, {-0.688191, -0.587785, 0.425325}, {-0.681718, -0.147621, 0.716567}, {-0.442863, -0.238856, 0.864188}, {-0.587785, -0.425325, 0.688191}, {-0.309017, -0.500000, 0.809017}, {-0.147621, -0.716567, 0.681718}, {-0.425325, -0.688191, 0.587785}, {-0.162460, -0.262866, 0.951056}, {0.442863, -0.238856, 0.864188}, {0.162460, -0.262866, 0.951056}, {0.309017, -0.500000, 0.809017}, {0.147621, -0.716567, 0.681718}, {0.000000, -0.525731, 0.850651}, {0.425325, -0.688191, 0.587785}, {0.587785, -0.425325, 0.688191}, {0.688191, -0.587785, 0.425325}, {-0.955423, 0.295242, 0.000000}, {-0.951056, 0.162460, 0.262866}, {-1.000000, 0.000000, 0.000000}, {-0.850651, 0.000000, 0.525731}, {-0.955423, -0.295242, 0.000000}, {-0.951056, -0.162460, 0.262866}, {-0.864188, 0.442863, -0.238856}, {-0.951056, 0.162460, -0.262866}, {-0.809017, 0.309017, -0.500000}, {-0.864188, -0.442863, -0.238856}, {-0.951056, -0.162460, -0.262866}, {-0.809017, -0.309017, -0.500000}, {-0.681718, 0.147621, -0.716567}, {-0.681718, -0.147621, -0.716567}, {-0.850651, 0.000000, -0.525731}, {-0.688191, 0.587785, -0.425325}, {-0.587785, 0.425325, -0.688191}, {-0.425325, 0.688191, -0.587785}, {-0.425325, -0.688191, -0.587785}, {-0.587785, -0.425325, -0.688191}, {-0.688191, -0.587785, -0.425325}, ================================================ FILE: source/asm_draw.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // asm_draw.h // // Include file for asm drawing routines. // // // !!! note that this file must match the corresponding C structures at all // times !!! // // !!! if this is changed, it must be changed in r_local.h too !!! #define NEAR_CLIP 0.01 // !!! if this is changed, it must be changed in r_local.h too !!! #define CYCLE 128 // espan_t structure // !!! if this is changed, it must be changed in r_shared.h too !!! #define espan_t_u 0 #define espan_t_v 4 #define espan_t_count 8 #define espan_t_pnext 12 #define espan_t_size 16 // sspan_t structure // !!! if this is changed, it must be changed in d_local.h too !!! #define sspan_t_u 0 #define sspan_t_v 4 #define sspan_t_count 8 #define sspan_t_size 12 // spanpackage_t structure // !!! if this is changed, it must be changed in d_polyset.c too !!! #define spanpackage_t_pdest 0 #define spanpackage_t_pz 4 #define spanpackage_t_count 8 #define spanpackage_t_ptex 12 #define spanpackage_t_sfrac 16 #define spanpackage_t_tfrac 20 #define spanpackage_t_light 24 #define spanpackage_t_zi 28 #define spanpackage_t_size 32 // edge_t structure // !!! if this is changed, it must be changed in r_shared.h too !!! #define et_u 0 #define et_u_step 4 #define et_prev 8 #define et_next 12 #define et_surfs 16 #define et_nextremove 20 #define et_nearzi 24 #define et_owner 28 #define et_size 32 // surf_t structure // !!! if this is changed, it must be changed in r_shared.h too !!! #define SURF_T_SHIFT 6 #define st_next 0 #define st_prev 4 #define st_spans 8 #define st_key 12 #define st_last_u 16 #define st_spanstate 20 #define st_flags 24 #define st_data 28 #define st_entity 32 #define st_nearzi 36 #define st_insubmodel 40 #define st_d_ziorigin 44 #define st_d_zistepu 48 #define st_d_zistepv 52 #define st_pad 56 #define st_size 64 // clipplane_t structure // !!! if this is changed, it must be changed in r_local.h too !!! #define cp_normal 0 #define cp_dist 12 #define cp_next 16 #define cp_leftedge 20 #define cp_rightedge 21 #define cp_reserved 22 #define cp_size 24 // medge_t structure // !!! if this is changed, it must be changed in model.h too !!! #define me_v 0 #define me_cachededgeoffset 4 #define me_size 8 // mvertex_t structure // !!! if this is changed, it must be changed in model.h too !!! #define mv_position 0 #define mv_size 12 // refdef_t structure // !!! if this is changed, it must be changed in render.h too !!! #define rd_vrect 0 #define rd_aliasvrect 20 #define rd_vrectright 40 #define rd_vrectbottom 44 #define rd_aliasvrectright 48 #define rd_aliasvrectbottom 52 #define rd_vrectrightedge 56 #define rd_fvrectx 60 #define rd_fvrecty 64 #define rd_fvrectx_adj 68 #define rd_fvrecty_adj 72 #define rd_vrect_x_adj_shift20 76 #define rd_vrectright_adj_shift20 80 #define rd_fvrectright_adj 84 #define rd_fvrectbottom_adj 88 #define rd_fvrectright 92 #define rd_fvrectbottom 96 #define rd_horizontalFieldOfView 100 #define rd_xOrigin 104 #define rd_yOrigin 108 #define rd_vieworg 112 #define rd_viewangles 124 #define rd_ambientlight 136 #define rd_size 140 // mtriangle_t structure // !!! if this is changed, it must be changed in model.h too !!! #define mtri_facesfront 0 #define mtri_vertindex 4 #define mtri_size 16 // !!! if this changes, array indexing in !!! // !!! d_polysa.s must be changed to match !!! #define mtri_shift 4 ================================================ FILE: source/asm_i386.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __ASM_I386__ #define __ASM_I386__ #ifdef ELF #define C(label) label #else #define C(label) _##label #endif // // !!! note that this file must match the corresponding C structures at all // times !!! // // plane_t structure // !!! if this is changed, it must be changed in model.h too !!! // !!! if the size of this is changed, the array lookup in SV_HullPointContents // must be changed too !!! #define pl_normal 0 #define pl_dist 12 #define pl_type 16 #define pl_signbits 17 #define pl_pad 18 #define pl_size 20 // hull_t structure // !!! if this is changed, it must be changed in model.h too !!! #define hu_clipnodes 0 #define hu_planes 4 #define hu_firstclipnode 8 #define hu_lastclipnode 12 #define hu_clip_mins 16 #define hu_clip_maxs 28 #define hu_size 40 // dnode_t structure // !!! if this is changed, it must be changed in bspfile.h too !!! #define nd_planenum 0 #define nd_children 4 #define nd_mins 8 #define nd_maxs 20 #define nd_firstface 32 #define nd_numfaces 36 #define nd_size 40 // sfxcache_t structure // !!! if this is changed, it much be changed in sound.h too !!! #define sfxc_length 0 #define sfxc_loopstart 4 #define sfxc_speed 8 #define sfxc_width 12 #define sfxc_stereo 16 #define sfxc_data 20 // channel_t structure // !!! if this is changed, it much be changed in sound.h too !!! #define ch_sfx 0 #define ch_leftvol 4 #define ch_rightvol 8 #define ch_end 12 #define ch_pos 16 #define ch_looping 20 #define ch_entnum 24 #define ch_entchannel 28 #define ch_origin 32 #define ch_dist_mult 44 #define ch_master_vol 48 #define ch_size 52 // portable_samplepair_t structure // !!! if this is changed, it much be changed in sound.h too !!! #define psp_left 0 #define psp_right 4 #define psp_size 8 #endif ================================================ FILE: source/audiodec/audio_decoder.cpp ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ // Headers #include #include #include "audio_decoder.h" #include "decoder_mpg123.h" #include "decoder_oggvorbis.h" #include "audio_resampler.h" void AudioDecoder::Pause() { paused = true; } void AudioDecoder::Resume() { paused = false; } int AudioDecoder::Decode(uint8_t* buffer, int length) { if (paused) { memset(buffer, '\0', length); return length; } int res = FillBuffer(buffer, length); if (res < 0) { memset(buffer, '\0', length); } else if (res < length) { memset(&buffer[res], '\0', length - res); } if (IsFinished() && looping) { ++loop_count; Rewind(); if (length - res > 0) { int res2 = Decode(&buffer[res], length - res); if (res2 <= 0) { return res; } return res + res2; } } return res; } int AudioDecoder::DecodeAsMono(uint8_t* left, uint8_t* right, int size) { int freq; Format format; int channels; GetFormat(freq, format, channels); if (channels == 1) { return Decode(left, size); } if ((int)mono_buffer.size() < size * 2) { mono_buffer.resize(size * 2); } int read = Decode(mono_buffer.data(), size * 2); if (read < 0) { memset(left, '\0', size); memset(right, '\0', size); return -1; } int sample_size = GetSamplesizeForFormat(format); for (int i = 0; i <= read / 2; i += sample_size) { memcpy(&left[i], &mono_buffer.data()[i * channels], sample_size); memcpy(&right[i], &mono_buffer.data()[i * channels + sample_size], sample_size); } if (read < size / 2) { memset(&left[read / 2], '\0', size); memset(&right[read / 2], '\0', size); } return read / 2; } class WMAUnsupportedFormatDecoder : public AudioDecoder { public: WMAUnsupportedFormatDecoder() { error_message = std::string("WMA audio files are not supported. Reinstall the\n") + "game and don't convert them when asked by Windows!\n"; } bool Open(FILE*) override { return false; } bool IsFinished() const override { return true; } void GetFormat(int&, Format&, int&) const override {} private: int FillBuffer(uint8_t*, int) override { return -1; }; }; const char wma_magic[] = { (char)0x30, (char)0x26, (char)0xB2, (char)0x75 }; std::unique_ptr AudioDecoder::Create(FILE* file, const std::string& filename) { char magic[4] = { 0 }; fread(magic, 4, 1, file); fseek(file, 0, SEEK_SET); // Try to use MIDI decoder, use fallback(s) if available if (!strncmp(magic, "MThd", 4)) { #ifdef HAVE_WILDMIDI static bool wildmidi_works = true; if (wildmidi_works) { AudioDecoder *mididec = nullptr; # ifdef USE_AUDIO_RESAMPLER mididec = new AudioResampler(new WildMidiDecoder(filename)); # else mididec = new WildMidiDecoder(filename); # endif if (mididec) { if (mididec->WasInited()) return std::unique_ptr(mididec); delete mididec; } wildmidi_works = false; } #endif #if WANT_FMMIDI == 1 # ifdef USE_AUDIO_RESAMPLER return std::unique_ptr(new AudioResampler(new FmMidiDecoder(), true, AudioResampler::Quality::Low)); # else return std::unique_ptr(new FmMidiDecoder()); # endif #endif // No MIDI decoder available return nullptr; } // Try to use internal OGG decoder if (!strncmp(magic, "OggS", 4)) { // OGG #if defined(HAVE_TREMOR) || defined(HAVE_OGGVORBIS) # ifdef USE_AUDIO_RESAMPLER return std::unique_ptr(new AudioResampler(new OggVorbisDecoder())); # else return std::unique_ptr(new OggVorbisDecoder()); # endif #endif } #ifdef HAVE_SLOW_CPU // Try to use a basic decoder for faster wav decoding if not ADPCM if (!strncmp(magic, "RIFF", 4)){ fseek(file, 20, SEEK_SET); uint16_t raw_enc; fread(&raw_enc, 2, 1, file); fseek(file, 0, SEEK_SET); if (raw_enc == 0x01){ // Codec is normal PCM # ifdef USE_AUDIO_RESAMPLER return std::unique_ptr(new AudioResampler(new WavDecoder())); # else return std::unique_ptr(new WavDecoder()); # endif } } #endif // Try to use libsndfile for common formats if (!strncmp(magic, "RIFF", 4) || // WAV !strncmp(magic, "FORM", 4) || // WAV AIFF !strncmp(magic, "OggS", 4) || // OGG !strncmp(magic, "fLaC", 4)) { // FLAC #ifdef HAVE_LIBSNDFILE # ifdef USE_AUDIO_RESAMPLER return std::unique_ptr(new AudioResampler(new LibsndfileDecoder())); # else return std::unique_ptr(new LibsndfileDecoder()); # endif #endif return nullptr; } // Inform about WMA issue if (!memcmp(magic, wma_magic, 4)) { return std::unique_ptr(new WMAUnsupportedFormatDecoder()); } // False positive MP3s should be prevented before by checking for common headers #ifdef HAVE_MPG123 static bool mpg123_works = true; if (mpg123_works) { AudioDecoder *mp3dec = nullptr; if (strncmp(magic, "ID3", 3) == 0) { # ifdef USE_AUDIO_RESAMPLER mp3dec = new AudioResampler(new Mpg123Decoder()); # else mp3dec = new Mpg123Decoder(); # endif if (mp3dec) { if (mp3dec->WasInited()) return std::unique_ptr(mp3dec); delete mp3dec; } mpg123_works = false; return nullptr; } // Parsing MP3s seems to be the only reliable way to detect them if (Mpg123Decoder::IsMp3(file)) { fseek(file, 0, SEEK_SET); # ifdef USE_AUDIO_RESAMPLER mp3dec = new AudioResampler(new Mpg123Decoder()); # else mp3dec = new Mpg123Decoder(); # endif if (mp3dec) { if(mp3dec->WasInited()) return std::unique_ptr(mp3dec); delete mp3dec; } mpg123_works = false; return nullptr; } } #endif fseek(file, 0, SEEK_SET); return nullptr; } void AudioDecoder::SetFade(int begin, int end, int duration) { fade_time = 0.0; if (duration <= 0.0) { volume = end; return; } if (begin == end) { volume = end; return; } volume = (double)begin; fade_end = (double)end; fade_time = (double)duration; delta_step = (fade_end - volume) / fade_time; } void AudioDecoder::Update(int delta) { if (fade_time <= 0.0) { return; } fade_time -= delta; volume += delta * delta_step; volume = volume > 100.0 ? 100.0 : volume < 0.0 ? 0.0 : volume; } void AudioDecoder::SetVolume(int volume) { this->volume = (double)volume; } int AudioDecoder::GetVolume() const { return (int)volume; } void AudioDecoder::Rewind() { if (!Seek(0, Origin::Begin)) { // The libs guarantee that Rewind works assert(false && "Rewind"); } } bool AudioDecoder::GetLooping() const { return looping; } void AudioDecoder::SetLooping(bool enable) { looping = enable; } int AudioDecoder::GetLoopCount() const { return loop_count; } bool AudioDecoder::WasInited() const { return true; } std::string AudioDecoder::GetError() const { return error_message; } std::string AudioDecoder::GetType() const { return music_type; } bool AudioDecoder::SetFormat(int, Format, int) { return false; } int AudioDecoder::GetPitch() const { return 0; } bool AudioDecoder::SetPitch(int) { return false; } bool AudioDecoder::Seek(size_t, Origin) { return false; } size_t AudioDecoder::Tell() const { return -1; } int AudioDecoder::GetTicks() const { return 0; } int AudioDecoder::GetSamplesizeForFormat(AudioDecoder::Format format) { switch (format) { case Format::S8: case Format::U8: return 1; case Format::S16: case Format::U16: return 2; case Format::S32: case Format::U32: case Format::F32: return 4; } assert(false && "Bad format"); return -1; } ================================================ FILE: source/audiodec/audio_decoder.h ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #ifndef EASYRPG_AUDIO_DECODER_H #define EASYRPG_AUDIO_DECODER_H // Headers #include #include #include #include /** * The AudioDecoder class provides an abstraction over the decoding of * common audio formats. * Create will instantitate a proper audio decoder and calling Decode will * fill a buffer with audio data which must be passed to the audio hardware. */ class AudioDecoder { public: virtual ~AudioDecoder() {} /** Sample format */ enum class Format { S8, U8, S16, U16, S32, U32, F32 }; /** Seek origin for Seek command */ enum class Origin { Begin = 0, Current = 1, End = 2 }; /** * Writes 'size' bytes in the specified buffer. The data matches the format * reported by GetFormat. * When size is is smaller then the amount of written bytes or an error occurs * the remaining buffer space is cleared. * * @param buffer Output buffer * @param size Size of the buffer in bytes * @return Number of bytes written to the buffer or -1 on error */ int Decode(uint8_t* buffer, int size); /** * Splits stereo into mono and Writes 'size' bytes in each of the buffers. * The data matches the format reported by GetFormat, except that both * buffers will contain Mono audio. When the source format was already mono * the 'right' buffer is ignored (and not cleared) * When size is is smaller then the amount of written bytes or an error occurs * the remaining buffer space is cleared. * * @param left Output buffer of the left channel * @param right Output buffer of the right channel (or nothing if source is mono) * @param size Size of each of the buffers * @return Number of bytes written in one of the buffers or -1 on error */ int DecodeAsMono(uint8_t* left, uint8_t* right, int size); /** * Parses the specified file handle and open a proper audio decoder to handle * the audio file. * Upon success the file handle is owned by the audio decoder and further * operations on it will be undefined! Upon failure the file handle points at * the beginning. * The filename is used for debug purposes but should match the FILE handle. * Upon failure the FILE handle is valid and points at the beginning. * * @param file File handle to parse * @param filename Path to the file handle * @return An audio decoder instance when the format was detected, otherwise null */ static std::unique_ptr Create(FILE* file, const std::string& filename); /** * Updates the volume for the fade in/out effect. * Volume changes will not really modify the volume but are only helper * functions for retrieving the volume information for the audio hardware. * * @param delta Time in ms since the last call of this function. */ void Update(int delta); /** * Prepares a volume fade in/out effect. * To do a fade out begin must be larger then end. * Call Update to do the fade. * Volume changes will not really modify the volume but are only helper * functions for retrieving the volume information for the audio hardware. * * @param begin Begin volume (from 0-100) * @param end End volume (from 0-100) * @param duration Fade duration in ms */ void SetFade(int begin, int end, int duration); /** * Sets the volume of the audio decoder. * Volume changes will not really modify the volume but are only helper * functions for retrieving the volume information for the audio hardware. * * @param volume (from 0-100) */ void SetVolume(int volume); /** * Gets the volume of the audio decoder. * Volume changes will not really modify the volume but are only helper * functions for retrieving the volume information for the audio hardware. */ int GetVolume() const; /** * Pauses the audio decoding. * Calling any Decode function will return a 0-buffer. */ void Pause(); /** * Resumes the audio decoding. * The decode function will continue behaving as expected. */ void Resume(); /** * Rewinds the audio stream to the beginning. */ void Rewind(); /** * Gets if the audio stream will loop when the stream finishes. * * @return if looping */ bool GetLooping() const; /** * Enables/Disables audio stream looping. * When looping is enabled IsFinished will never return true and the stream * auto-rewinds (assuming Rewind is supported) * * @param enable Enable/Disable looping */ void SetLooping(bool enable); /** * Gets the number of loops * * @return loop count */ int GetLoopCount() const; /** * Gets the status of the newly created audio decoder. * Used to make sure the underlying library is properly initialized. * * @return true if initializing was succesful, false otherwise */ virtual bool WasInited() const; /** * Provides an error message when Open or a Decode function fail. * * @return Human readable error message */ virtual std::string GetError() const; /** * Returns the name of the format the current audio decoder decodes in * lower case. * * @return Format name */ virtual std::string GetType() const; // Functions to be implemented by the audio decoder /** * Assigns a file handle to the audio decoder. * Open should be only called once per audio decoder instance. * Use GetError to get the error reason on failure. * * @return true if inititalizing was succesful, false otherwise */ virtual bool Open(FILE* file) = 0; /** * Determines whether the stream is finished. * * @return true stream ended */ virtual bool IsFinished() const = 0; /** * Retrieves the format of the audio decoder. * It is guaranteed that these settings will stay constant the whole time. * * @param frequency Filled with the audio frequency * @param format Filled with the audio format * @param channel Filled with the amount of channels */ virtual void GetFormat(int& frequency, Format& format, int& channels) const = 0; /** * Requests a prefered format from the audio decoder. Not all decoders * support everything and it's recommended to use the audio hardware * for audio processing. * When false is returned use GetFormat to get the real format of the * output data. * * @param frequency Audio frequency * @param format Audio format * @param channel Number of channels * @return true when all settings were set, otherwise false (use GetFormat) */ virtual bool SetFormat(int frequency, Format format, int channels); /** * Gets the pitch multiplier. * * @return pitch multiplier */ virtual int GetPitch() const; /** * Sets the pitch multiplier. * 100 = normal speed * 200 = double speed and so on * Not all audio decoders support this. Using the audio hardware is * recommended. * * @param pitch Pitch multiplier to use * @return true if pitch was set, false otherwise */ virtual bool SetPitch(int pitch); /** * Seeks in the audio stream. The value of offset is implementation * defined but is guaranteed to match the result of Tell. * Only Rewinding is guaranteed to work. * * @param offset Offset to seek to * @param origin Position to seek from * @return Whether seek was successful */ virtual bool Seek(size_t offset, Origin origin); /** * Tells the current stream position. The value is implementation * defined. * * @return Position in the stream */ virtual size_t Tell() const; /** * Returns amount of executed ticks. Only useful for MIDI format. * * @return Amount of MIDI ticks. */ virtual int GetTicks() const; /** * Returns the amount of bytes per sample. * * @param Sample format * @return Bytes per sample */ static int GetSamplesizeForFormat(AudioDecoder::Format format); protected: /** * Called by the Decode functions to fill the buffer. * * @param buffer Buffer to fill * @param size Buffer size * @return number of bytes read or -1 on error */ virtual int FillBuffer(uint8_t* buffer, int size) = 0; std::string error_message; std::string music_type; private: bool paused = false; double volume = 0; double fade_end = 0; double fade_time = -1; double delta_step = 0; bool looping = false; int loop_count = 0; std::vector mono_buffer; }; #endif ================================================ FILE: source/audiodec/audio_resampler.cpp ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #if defined(HAVE_LIBSPEEXDSP) || defined(HAVE_LIBSAMPLERATE) #include #include "audio_resampler.h" #define ERROR -1 #define STANDARD_PITCH 100 /** * Utility function used to convert a buffer of a arbitrary AudioDecoder::Format to a float buffer * * @param[in] wrapped_decoder The decoder from which audio samples are read * @param[inout] buffer The buffer which will receive the converted samples, * has to be at least amount_of_samples_to_read*sizeof(float) bytes big. * @param[in] amount_of_samples_to_read The number of samples to read. * @param[in] input_samplesize The size of one sample of the decoder in it's original format - given in bytes * @param[in] input_format The original format of the samples * * @return The number of converted samples - if this number is smaller than amount_of_samples_to_read the wrapped decoder has reaches it's end. * If the returned value has a negative value an error occured. */ inline static int DecodeAndConvertFloat(AudioDecoder * wrapped_decoder, uint8_t * buffer, int amount_of_samples_to_read, const int input_samplesize, const AudioDecoder::Format input_format){ float* bufferAsFloat = (float*)buffer; if (wrapped_decoder->IsFinished()) return 0; //Workaround for decoders which don't detect there own end int amount_of_samples_read = wrapped_decoder->Decode(buffer, amount_of_samples_to_read*input_samplesize); if (amount_of_samples_read <= 0) { return amount_of_samples_read; //error occured - or nothing read } else { amount_of_samples_read /= input_samplesize; } //Convert the read data (amount_of_data_read is at least one at this moment) switch (input_format) { case AudioDecoder::Format::S8: //Convert inplace (the last frames are unused if smaller) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsFloat[i] = ((int8_t*)bufferAsFloat)[i] / 128.0; } break; case AudioDecoder::Format::U8: //Convert inplace (the last frames are unused if smaller) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsFloat[i] = ((uint8_t*)bufferAsFloat)[i] / 128.0 - 1.0; } break; case AudioDecoder::Format::S16: //Convert inplace (the last frames are unused if smaller) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsFloat[i] = ((int16_t*)bufferAsFloat)[i] / 32768.0; } break; case AudioDecoder::Format::U16: //Convert inplace (the last frames are unused if smaller) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsFloat[i] = ((uint16_t*)bufferAsFloat)[i] / 32768.0 - 1.0; } break; case AudioDecoder::Format::S32: //Convert inplace (same size) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsFloat[i] = ((int32_t*)bufferAsFloat)[i] / 2147483648.0; } break; case AudioDecoder::Format::U32: //Convert inplace (same size) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsFloat[i] = ((uint32_t*)bufferAsFloat)[i] / 2147483648.0 - 1.0; } break; case AudioDecoder::Format::F32: //Nothing to convert break; } return amount_of_samples_read; } #if defined(HAVE_LIBSPEEXDSP) /** * Utility function used to convert a buffer of a arbitrary AudioDecoder::Format to a int16 buffer * * @param[in] wrapped_decoder The decoder from which audio samples are read * @param[inout] buffer The buffer which will receive the converted samples, * has to be at least amount_of_samples_to_read*max(sizeof(int16_t),input_samplesize) bytes big. * @param[in] amount_of_samples_to_read The number of samples to read. * @param[in] input_samplesize The size of one sample of the decoder in it's original format - given in bytes * @param[in] input_format The original format of the samples * * @return The number of converted samples - if this number is smaller than amount_of_samples_to_read the wrapped decoder has reaches it's end. * If the returned value has a negative value an error occured. */ inline static int DecodeAndConvertInt16(AudioDecoder * wrapped_decoder, uint8_t * buffer, int amount_of_samples_to_read, const int input_samplesize, const AudioDecoder::Format input_format){ int16_t* bufferAsInt16 = (int16_t*)buffer; if (wrapped_decoder->IsFinished()) return 0; //Workaround for decoders which don't detect there own end int amount_of_samples_read = wrapped_decoder->Decode(buffer, amount_of_samples_to_read*input_samplesize); if (amount_of_samples_read <= 0) { return amount_of_samples_read; //error occured - or nothing read } else { //Convert the number of bytes to the number of samples amount_of_samples_read /= input_samplesize; } //Convert the read data (amount_of_data_read is at least one at this moment) switch (input_format) { case AudioDecoder::Format::S8: //Convert inplace (the last frames are unused if smaller) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsInt16[i] = ((int8_t*)bufferAsInt16)[i] << 8; } break; case AudioDecoder::Format::U8: //Convert inplace (the last frames are unused if smaller) for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsInt16[i] = (((int16_t)(((uint8_t*)bufferAsInt16)[i])) - 128) << 8; } break; case AudioDecoder::Format::S16: //Nothing to convert break; case AudioDecoder::Format::U16: //Convert unsigned to signed for (int i = amount_of_samples_read - 1; i >= 0; i--) { bufferAsInt16[i] = (int16_t)(((int32_t)(((uint16_t*)bufferAsInt16)[i])) - 32768); } break; case AudioDecoder::Format::S32: //Convert inplace (from front to back to prevent overwriting the buffer) for (int i = 0; i < amount_of_samples_read; i++) { bufferAsInt16[i] = (int16_t)((((int32_t*)bufferAsInt16)[i]) >> 16); } break; case AudioDecoder::Format::U32: //Convert inplace (from front to back to prevent overwriting the buffer) for (int i = 0; i < amount_of_samples_read; i++) { bufferAsInt16[i] = (int16_t)(((int32_t)((((uint32_t*)bufferAsInt16)[i]) >> 16)) - 32768); } break; case AudioDecoder::Format::F32: //Convert inplace (from front to back to prevent overwriting the buffer) for (int i = 0; i < amount_of_samples_read; i++) { float number = ((((float*)bufferAsInt16)[i])*32768.0); bufferAsInt16[i] = (number <= 32767.0) ? ((number >= -32768.0) ? number : -32768) : 32767; } break; } return amount_of_samples_read; } #endif AudioResampler::AudioResampler(AudioDecoder * wrapped, bool pitch_handled, AudioResampler::Quality quality) { //There is no need for a standalone resampler decoder assert(wrapped != 0); wrapped_decoder = wrapped; music_type = wrapped->GetType(); lasterror = 0; pitch_handled_by_decoder = pitch_handled; #if defined(HAVE_LIBSPEEXDSP) switch (quality) { case Quality::Low: sampling_quality = 1; break; case Quality::Medium: sampling_quality = 3; break; case Quality::High: sampling_quality = 5; break; } #elif defined(HAVE_LIBSAMPLERATE) switch (quality) { case Quality::Low: sampling_quality = SRC_SINC_FASTEST; break; case Quality::Medium: sampling_quality = SRC_SINC_MEDIUM_QUALITY; break; case Quality::High: sampling_quality = SRC_SINC_BEST_QUALITY; break; } #endif finished = false; pitch = 100; } AudioResampler::~AudioResampler() { if (conversion_state != 0) { #if defined(HAVE_LIBSPEEXDSP) speex_resampler_destroy(conversion_state); #elif defined(HAVE_LIBSAMPLERATE) src_delete(conversion_state); #endif } if (wrapped_decoder != 0) { delete wrapped_decoder; } } bool AudioResampler::WasInited() const { return wrapped_decoder->WasInited(); } bool AudioResampler::Open(FILE* file) { if (wrapped_decoder->Open(file)) { wrapped_decoder->GetFormat(input_rate, input_format, nr_of_channels); //determine if the input format is supported by the resampler switch (input_format) { case Format::F32: output_format = input_format; break; #ifdef HAVE_LIBSPEEXDSP case Format::S16: output_format = input_format; break; #endif default: output_format = Format::F32; break; } //Set input format to output_format if possible wrapped_decoder->SetFormat(input_rate, output_format, nr_of_channels); //Reread format to get new values wrapped_decoder->GetFormat(input_rate, input_format, nr_of_channels); output_rate = input_rate; #if defined(HAVE_LIBSPEEXDSP) conversion_state = speex_resampler_init(nr_of_channels, input_rate, output_rate, sampling_quality, &lasterror); conversion_data.ratio_num = input_rate; conversion_data.ratio_denom = output_rate; speex_resampler_skip_zeros(conversion_state); #elif defined(HAVE_LIBSAMPLERATE) conversion_state = src_new(sampling_quality, nr_of_channels, &lasterror); #endif //Init the conversion data structure conversion_data.input_frames = 0; conversion_data.input_frames_used = 0; finished = false; return conversion_state != 0; } else { return false; } } bool AudioResampler::Seek(size_t offset, Origin origin) { if (wrapped_decoder->Seek(offset, origin)) { //reset conversio data conversion_data.input_frames = 0; conversion_data.input_frames_used = 0; finished = wrapped_decoder->IsFinished(); #if defined(HAVE_LIBSPEEXDSP) speex_resampler_reset_mem(conversion_state); #elif defined(HAVE_LIBSAMPLERATE) src_reset(conversion_state); #endif return true; } return false; } size_t AudioResampler::Tell() const { return wrapped_decoder->Tell(); } int AudioResampler::GetTicks() const { return wrapped_decoder->GetTicks(); } bool AudioResampler::IsFinished() const { return finished; } void AudioResampler::GetFormat(int& frequency, AudioDecoder::Format& format, int& channels) const { frequency = output_rate; format = output_format; channels = nr_of_channels; } bool AudioResampler::SetFormat(int freq, AudioDecoder::Format fmt, int channels) { //Check whether requested format is supported by the resampler switch (fmt) { case Format::F32: output_format = fmt; break; #ifdef HAVE_LIBSPEEXDSP case Format::S16: output_format = fmt; break; #endif default: break; } wrapped_decoder->SetFormat(input_rate, output_format, channels); wrapped_decoder->GetFormat(input_rate, input_format, nr_of_channels); output_rate = freq; return (nr_of_channels == channels&&output_format == fmt); } int AudioResampler::GetPitch() const { if (pitch_handled_by_decoder) { return wrapped_decoder->GetPitch(); } else { return pitch; } } bool AudioResampler::SetPitch(int pitch_) { if (pitch_handled_by_decoder) { return wrapped_decoder->SetPitch(pitch_); } else { pitch = pitch_; return true; } } int AudioResampler::FillBuffer(uint8_t* buffer, int length) { int amount_filled = 0; if((input_rate == output_rate) && ((pitch == STANDARD_PITCH) || pitch_handled_by_decoder)) { //Do only format conversion amount_filled = FillBufferSameRate(buffer, length); } else { if (conversion_state == 0) { error_message = "internal error: state pointer is a nullptr"; amount_filled = ERROR; } else { //Do samplerate conversion amount_filled = FillBufferDifferentRate(buffer, length); } } //Clear the remaining buffer as specified in audio_decoder.h for (int i = (amount_filled > 0) ? amount_filled : 0; i < length; i++) { buffer[i] = 0; } return amount_filled; } int AudioResampler::FillBufferSameRate(uint8_t* buffer, int length) { const int input_samplesize = GetSamplesizeForFormat(input_format); const int output_samplesize = GetSamplesizeForFormat(output_format); //The buffer size has to be a multiple of a frame const int buffer_size=sizeof(internal_buffer) - sizeof(internal_buffer)%(nr_of_channels*input_samplesize); int total_output_frames = length / (output_samplesize*nr_of_channels); int amount_of_data_to_read = 0; int amount_of_data_read = total_output_frames*nr_of_channels; int decoded = 0; if (input_samplesize > output_samplesize) { //It is necessary to use the internal_buffer to convert the samples. while (total_output_frames > 0) { amount_of_data_to_read = buffer_size / input_samplesize; //limit amount_of_data_to_read in the last loop amount_of_data_to_read = (amount_of_data_to_read > total_output_frames) ? total_output_frames : amount_of_data_to_read; switch (output_format) { case AudioDecoder::Format::F32: amount_of_data_read = DecodeAndConvertFloat(wrapped_decoder, internal_buffer, amount_of_data_to_read, input_samplesize, input_format); break; #ifdef HAVE_LIBSPEEXDSP case AudioDecoder::Format::S16: amount_of_data_read = DecodeAndConvertInt16(wrapped_decoder, internal_buffer, amount_of_data_to_read, input_samplesize, input_format); break; #endif default: error_message = "internal error: output_format is not convertable"; return ERROR; } if (amount_of_data_read < 0) { error_message = wrapped_decoder->GetError(); return amount_of_data_read; //error occured } //Copy the converted samples for (int i = 0; i < amount_of_data_read*output_samplesize; i++) { buffer[i] = internal_buffer[i]; } //Prepare next loop total_output_frames -= amount_of_data_read; decoded += amount_of_data_read; buffer += amount_of_data_read*output_samplesize; //If the end of the decoder is reached (it has finished) if (amount_of_data_read < amount_of_data_to_read) { break; } } } else { //It is possible to work inplace as length is specified for the output samplesize. switch (output_format) { case AudioDecoder::Format::F32: decoded = DecodeAndConvertFloat(wrapped_decoder, buffer, amount_of_data_read, input_samplesize, input_format); break; #ifdef HAVE_LIBSPEEXDSP case AudioDecoder::Format::S16: decoded = DecodeAndConvertInt16(wrapped_decoder, buffer, amount_of_data_read, input_samplesize, input_format); break; #endif default: error_message = "internal error: output_format is not convertable"; return ERROR; } } finished = wrapped_decoder->IsFinished(); if (decoded < 0) { error_message = wrapped_decoder->GetError(); return decoded; } else { return decoded*output_samplesize; } } int AudioResampler::FillBufferDifferentRate(uint8_t* buffer, int length) { const int input_samplesize = GetSamplesizeForFormat(input_format); const int output_samplesize = GetSamplesizeForFormat(output_format); //The buffer size has to be a multiple of a frame const int buffer_size=sizeof(internal_buffer) - sizeof(internal_buffer)%(nr_of_channels*((input_samplesize>output_samplesize) ? input_samplesize : output_samplesize)); int total_output_frames = length / (output_samplesize*nr_of_channels); int amount_of_samples_to_read = 0; int amount_of_samples_read = 0; uint8_t * advanced_input_buffer = internal_buffer; int unused_frames = 0; int empty_buffer_space = 0; int error = 0; #ifdef HAVE_LIBSPEEXDSP spx_uint32_t numerator = 0; spx_uint32_t denominator = 0; #endif while (total_output_frames > 0) { //Calculate how much frames of the last cycle are unused - to reuse them unused_frames = conversion_data.input_frames - conversion_data.input_frames_used; empty_buffer_space = buffer_size / output_samplesize - unused_frames*nr_of_channels; advanced_input_buffer = internal_buffer; //If there is still unused data in the input_buffer order it to the front for (int i = 0; i < unused_frames*nr_of_channels*output_samplesize; i++) { *advanced_input_buffer = *(advanced_input_buffer + empty_buffer_space*output_samplesize); advanced_input_buffer++; } //advanced_input_buffer is now offset to the first frame of new data! //ensure that the input buffer is not able to overrun amount_of_samples_to_read = (input_samplesize > output_samplesize) ? (empty_buffer_space*output_samplesize) / input_samplesize : empty_buffer_space; //Read as many frames as needed to refill the buffer (filled after the conversion to float) if (amount_of_samples_to_read != 0) { switch (output_format) { case AudioDecoder::Format::F32: amount_of_samples_read = DecodeAndConvertFloat(wrapped_decoder, advanced_input_buffer, amount_of_samples_to_read, input_samplesize, input_format); break; #ifdef HAVE_LIBSPEEXDSP case AudioDecoder::Format::S16: amount_of_samples_read = DecodeAndConvertInt16(wrapped_decoder, advanced_input_buffer, amount_of_samples_to_read, input_samplesize, input_format); break; #endif default: error_message = "internal error: output_format is not convertable"; return ERROR; } if (amount_of_samples_read < 0) { error_message = wrapped_decoder->GetError(); return amount_of_samples_read; //error occured } } //Now we have a prepared full buffer of converted values //Prepare the source data conversion_data.input_frames = amount_of_samples_read / nr_of_channels + unused_frames; conversion_data.output_frames = total_output_frames; #if defined(HAVE_LIBSPEEXDSP) conversion_data.input_frames_used = conversion_data.input_frames; conversion_data.output_frames_gen = conversion_data.output_frames; //libspeexdsp defines a sample rate conversion with a fraction (input/output) numerator = input_rate*pitch; denominator = output_rate * STANDARD_PITCH; if (pitch_handled_by_decoder) { numerator = input_rate; denominator = output_rate; } if (conversion_data.ratio_num != numerator || conversion_data.ratio_denom != denominator) { int err=speex_resampler_set_rate_frac(conversion_state, numerator, denominator, input_rate, output_rate); conversion_data.ratio_num = numerator; conversion_data.ratio_denom = denominator; } //A pitfall from libspeexdsp if the output buffer is defined to big - everything stutters -achieved good values with the same size as the input buffer for a maximum conversion_data.output_frames_gen=(conversion_data.input_framesIsFinished()) ? 1 : 0; //Now let libsamplerate filter the data error = src_process(conversion_state, &conversion_data); if (error != 0) { error_message = src_strerror(error); return ERROR; } #endif total_output_frames -= conversion_data.output_frames_gen; buffer += conversion_data.output_frames_gen*nr_of_channels*output_samplesize; if ((conversion_data.input_frames == 0 && conversion_data.output_frames_gen <= conversion_data.output_frames) || conversion_data.output_frames_gen == 0) { finished = true; //There is nothing left to convert - return how much samples (in bytes) are converted! return length - total_output_frames*(output_samplesize*nr_of_channels); } } return length; } #endif ================================================ FILE: source/audiodec/audio_resampler.h ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #ifndef EASYRPG_AUDIO_RESAMPLER_H #define EASYRPG_AUDIO_RESAMPLER_H // Headers #include "audio_decoder.h" #include #include #if defined(HAVE_LIBSPEEXDSP) #include #elif defined(HAVE_LIBSAMPLERATE) #include #endif /** * Audio resampler powered by Libspeexdsp or Libsamplerate * Wraps another decoder and provides resampling. */ class AudioResampler : public AudioDecoder { public: /** Resampling quality */ enum class Quality { High, Medium, Low }; /** * Constructs a resampler * * @param[in] decoder The decoder which provides samples to the resampler - will be owned by the resampler * @param[in] pitch_handled Defines whether the decoder handles pitch changes by itself or not. * @param[in] quality Sets the quality rting of the resampler - higher quality implies slower filtering */ AudioResampler(AudioDecoder * decoder, bool pitch_handled=false, Quality quality=Quality::Medium); /** * Destroys the resampler as well as its owned ressources */ ~AudioResampler(); /** * Wraps the status querying of the contained decoder. * Used to make sure the underlying library is properly initialized. * * @return true if initializing was succesful, false otherwise */ bool WasInited() const; /** * Wraps the opening function of the contained decoder * * @param[in] file Filepointer to a file readable by the wrapped decoder * * @return Whether the operation was successful or not */ bool Open(FILE* file) override; /** * Wraps the seek function of the contained decoder * @note If the seek function of the wrapped decoder is * somewhat corelated to time the offset is not influenced by the resampling ratio * * @param offset Offset to seek to * @param origin Position to seek from * * @return Whether seek was successful */ bool Seek(size_t offset, Origin origin) override; /** * Wraps the tell function of the contained decoder * * @return Position in the stream */ size_t Tell() const override; /** * Wraps the GetTicks Function of the contained decoder * * @return Amount of MIDI ticks. */ int GetTicks() const override; /** * Returns wheter the resampled audio stream is finished * * @return true if the stream has reached it's end */ bool IsFinished() const override; /** * Retrieves the format of the audio decoder. * * @param frequency Filled with the audio frequency * @param format Filled with the audio format * @param channel Filled with the amount of channels */ void GetFormat(int& frequency, AudioDecoder::Format& format, int& channels) const override; /** * Requests a certain frame format from the resampler. * Supported formats are: * * float,int16_t for libspeexdsp * * float for libsamplerate * The channel setting is redirected to the wrapped decoder. * The frequency setting controls the resampler. * * @param frequency Sample rate the resampler should output * @param format Audio format the resampler should output * @param channel Number of channels * @return true when all settings were set, otherwise false (use GetFormat) */ bool SetFormat(int frequency, AudioDecoder::Format format, int channels) override; /** * Gets the pitch multiplier. * * @return pitch multiplier */ int GetPitch() const override; /** * Sets the pitch multiplier. * 100 = normal speed * 200 = double speed and so on * If the pitch is handled by the resampler this setting controls the resampling in conjunction with the frequency. * * @param pitch Pitch multiplier to use * @return true if pitch was set, false otherwise */ bool SetPitch(int pitch) override; private: /** * Called by the Decode functions to fill the buffer. * * @param buffer Buffer to fill * @param size Buffer size * @return number of bytes read or -1 on error */ int FillBuffer(uint8_t* buffer, int length) override; /** * Internally used by the FillBuffer function if the output rate equals the input rate */ int FillBufferSameRate(uint8_t* buffer, int length); /** * Internally used by the FillBuffer function if resampling is necessary */ int FillBufferDifferentRate(uint8_t* buffer, int length); AudioDecoder * wrapped_decoder; bool pitch_handled_by_decoder; int pitch; int sampling_quality; int lasterror; bool finished; int nr_of_channels; Format input_format; int input_rate; Format output_format; int output_rate; #if defined(HAVE_LIBSPEEXDSP) struct { spx_uint32_t input_frames, output_frames; spx_uint32_t input_frames_used, output_frames_gen; spx_uint32_t ratio_num, ratio_denom; } conversion_data; SpeexResamplerState * conversion_state; #elif defined(HAVE_LIBSAMPLERATE) SRC_DATA conversion_data; SRC_STATE * conversion_state; #endif /** * A buffer needed for operations which can't be performed in place (e.g resampling) * The size of the buffer defines the number of calls to the resampling algorithmn * (In the cpp file sizeof is used therefore it can be adjusted to fit the available memory) */ uint8_t internal_buffer[256*sizeof(float)]; }; #endif ================================================ FILE: source/audiodec/cd_psp2.cpp ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #define min #define max extern "C"{ #include "../quakedef.h" } #undef min #undef max #include "audio_decoder.h" extern char* mod_path; #define BUFSIZE 8192 // Max dimension of audio buffer size #define NSAMPLES 2048 // Number of samples for output // Music block struct struct DecodedMusic{ uint8_t* audiobuf; uint8_t* audiobuf2; uint8_t* cur_audiobuf; FILE* handle; bool isPlaying; bool loop; volatile bool pauseTrigger; volatile bool closeTrigger; volatile bool changeVol; }; // Internal stuffs DecodedMusic* BGM = NULL; std::unique_ptr audio_decoder; SceUID thread, Audio_Mutex, Talk_Mutex; volatile bool mustExit = false; float old_vol = 1.0; // Audio thread code static int bgmThread(unsigned int args, void* arg){ // Initializing audio port int ch = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, NSAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO); sceAudioOutSetConfig(ch, -1, -1, (SceAudioOutMode)-1); old_vol = bgmvolume.value; int vol = 32767 * bgmvolume.value; int vol_stereo[] = {vol, vol}; sceAudioOutSetVolume(ch, (SceAudioOutChannelFlag)(SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH), vol_stereo); DecodedMusic* mus; for (;;){ // Waiting for an audio output request sceKernelWaitSema(Audio_Mutex, 1, NULL); // Fetching track mus = BGM; // Checking if a new track is available if (mus == NULL){ //If we enter here, we probably are in the exiting procedure if (mustExit){ sceAudioOutReleasePort(ch); mustExit = false; sceKernelExitDeleteThread(0); } } // Initializing audio decoder audio_decoder = AudioDecoder::Create(mus->handle, "Track"); audio_decoder->Open(mus->handle); audio_decoder->SetLooping(mus->loop); audio_decoder->SetFormat(48000, AudioDecoder::Format::S16, 2); // Initializing audio buffers mus->audiobuf = (uint8_t*)malloc(BUFSIZE); mus->audiobuf2 = (uint8_t*)malloc(BUFSIZE); mus->cur_audiobuf = mus->audiobuf; // Audio playback loop for (;;){ // Check if the music must be paused if (mus->pauseTrigger || mustExit){ // Check if the music must be closed if (mus->closeTrigger){ audio_decoder.reset(); free(mus->audiobuf); free(mus->audiobuf2); free(mus); BGM = NULL; mus = NULL; if (!mustExit){ sceKernelSignalSema(Talk_Mutex, 1); break; } } // Check if the thread must be closed if (mustExit){ // Check if the audio stream has already been closed if (mus != NULL){ mus->closeTrigger = true; continue; } // Recursively closing all the threads sceAudioOutReleasePort(ch); mustExit = false; sceKernelExitDeleteThread(0); } mus->isPlaying = !mus->isPlaying; mus->pauseTrigger = false; } // Check if a volume change is required if (mus->changeVol){ old_vol = bgmvolume.value; int vol = 32767 * bgmvolume.value; int vol_stereo[] = {vol, vol}; sceAudioOutSetVolume(ch, (SceAudioOutChannelFlag)(SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH), vol_stereo); mus->changeVol = false; } if (mus->isPlaying){ // Check if audio playback finished if ((!mus->loop) && audio_decoder->IsFinished()) mus->isPlaying = false; // Update audio output if (mus->cur_audiobuf == mus->audiobuf) mus->cur_audiobuf = mus->audiobuf2; else mus->cur_audiobuf = mus->audiobuf; audio_decoder->Decode(mus->cur_audiobuf, BUFSIZE); sceAudioOutOutput(ch, mus->cur_audiobuf); } } } } void CDAudio_Play(byte track, bool looping) { CDAudio_Stop(); char fname[256]; sprintf (fname, "%s/%s/cdtracks/track", host_parms.basedir, (mod_path == NULL) ? GAMENAME_DIR : mod_path); if (track < 100){ sprintf(fname, "%s0", fname); if (track < 10){ sprintf(fname, "%s0", fname); } } sprintf(fname,"%s%d",fname,track); char tmp[256]; sprintf(tmp,"%s.ogg",fname); FILE* fd = fopen(tmp,"rb"); if (fd == NULL){ sprintf(tmp,"%s.mp3",fname); fd = fopen(tmp,"rb"); } if (fd == NULL) return; DecodedMusic* memblock = (DecodedMusic*)malloc(sizeof(DecodedMusic)); memblock->handle = fd; memblock->pauseTrigger = false; memblock->closeTrigger = false; memblock->isPlaying = true; memblock->loop = looping; BGM = memblock; sceKernelSignalSema(Audio_Mutex, 1); } void CDAudio_Stop(void) { if (BGM != NULL){ BGM->closeTrigger = true; BGM->pauseTrigger = true; sceKernelWaitSema(Talk_Mutex, 1, NULL); } } void CDAudio_Pause(void) { if (BGM != NULL) BGM->pauseTrigger = true; } void CDAudio_Resume(void) { if (BGM != NULL) BGM->pauseTrigger = true; } void CDAudio_Update(void) { if (BGM != NULL){ if (old_vol != bgmvolume.value) BGM->changeVol = true; } } int CDAudio_Init(void) { int res; // Creating audio mutex Audio_Mutex = sceKernelCreateSema("Audio Mutex", 0, 0, 1, NULL); Talk_Mutex = sceKernelCreateSema("Talk Mutex", 0, 0, 1, NULL); // Creating audio thread thread = sceKernelCreateThread("Audio Thread", &bgmThread, 0x10000100, 0x10000, 0, 0, NULL); sceKernelStartThread(thread, sizeof(thread), &thread); return 0; } void CDAudio_Shutdown(void) { mustExit = true; sceKernelSignalSema(Audio_Mutex, 1); sceKernelWaitThreadEnd(thread, NULL, NULL); sceKernelDeleteSema(Audio_Mutex); sceKernelDeleteSema(Talk_Mutex); } ================================================ FILE: source/audiodec/decoder_mpg123.cpp ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #ifdef HAVE_MPG123 // Headers #include #include "decoder_mpg123.h" static bool init = false; static void Mpg123Decoder_deinit(void) { mpg123_exit(); } static ssize_t custom_read(void* io, void* buffer, size_t nbyte) { FILE* f = reinterpret_cast(io); return fread(buffer, 1, nbyte, f); } static off_t custom_seek(void* io, off_t offset, int seek_type) { FILE* f = reinterpret_cast(io); fseek(f, offset, seek_type); return ftell(f); } static void custom_close(void* io) { FILE* f = reinterpret_cast(io); fclose(f); } static void noop_close(void*) {} Mpg123Decoder::Mpg123Decoder() : handle(nullptr, mpg123_delete) { music_type = "mp3"; // only initialize library once if (!init) { err = mpg123_init(); if (err != MPG123_OK) { error_message = "mpg123: " + std::string(mpg123_plain_strerror(err)); return; } // setup deinitialization atexit(Mpg123Decoder_deinit); } handle.reset(mpg123_new(nullptr, &err)); mpg123_replace_reader_handle(handle.get(), custom_read, custom_seek, custom_close); if (!handle) { error_message = "mpg123: " + std::string(mpg123_plain_strerror(err)); return; } init = true; } Mpg123Decoder::~Mpg123Decoder() { } bool Mpg123Decoder::WasInited() const { return init; } bool Mpg123Decoder::Open(FILE* file) { if (!init) { return false; } finished = false; err = mpg123_open_handle(handle.get(), file); if (err != MPG123_OK) { error_message = "mpg123: " + std::string(mpg123_plain_strerror(err)); return false; } return true; } bool Mpg123Decoder::Seek(size_t offset, Origin origin) { finished = false; mpg123_seek_frame(handle.get(), offset, (int)origin); return true; } bool Mpg123Decoder::IsFinished() const { return finished; } static int format_to_mpg123_format(AudioDecoder::Format format) { switch (format) { case AudioDecoder::Format::U8: return MPG123_ENC_UNSIGNED_8; case AudioDecoder::Format::S8: return MPG123_ENC_SIGNED_8; case AudioDecoder::Format::U16: return MPG123_ENC_UNSIGNED_16; case AudioDecoder::Format::S16: return MPG123_ENC_SIGNED_16; case AudioDecoder::Format::U32: return MPG123_ENC_UNSIGNED_32; case AudioDecoder::Format::S32: return MPG123_ENC_SIGNED_32; case AudioDecoder::Format::F32: return MPG123_ENC_FLOAT_32; default: assert(false); } return -1; } static AudioDecoder::Format mpg123_format_to_format(int format) { switch (format) { case MPG123_ENC_UNSIGNED_8: return AudioDecoder::Format::U8; case MPG123_ENC_SIGNED_8: return AudioDecoder::Format::S8; case MPG123_ENC_UNSIGNED_16: return AudioDecoder::Format::U16; case MPG123_ENC_SIGNED_16: return AudioDecoder::Format::S16; case MPG123_ENC_UNSIGNED_32: return AudioDecoder::Format::U32; case MPG123_ENC_SIGNED_32: return AudioDecoder::Format::S32; case MPG123_ENC_FLOAT_32: return AudioDecoder::Format::F32; default: assert(false); } return (AudioDecoder::Format)-1; } void Mpg123Decoder::GetFormat(int& frequency, AudioDecoder::Format& format, int& channels) const { long freq; int ch; int fmt; mpg123_getformat(handle.get(), &freq, &ch, &fmt); frequency = (int)freq; channels = ch; format = mpg123_format_to_format(fmt); } bool Mpg123Decoder::SetFormat(int freq, AudioDecoder::Format fmt, int channels) { // mpg123 has a built-in pseudo-resampler, not needing SDL_ConvertAudio later // Remove all available conversion formats // Add just one format to force mpg123 pseudo-resampler work mpg123_format_none(handle.get()); err = mpg123_format(handle.get(), (long)freq, (int)channels, (int)format_to_mpg123_format(fmt)); if (err != MPG123_OK) { err = mpg123_format(handle.get(), (long)44100, (int)channels, (int)format_to_mpg123_format(fmt)); if (err != MPG123_OK) { mpg123_format(handle.get(), (long)44100, (int)2, (int)MPG123_ENC_SIGNED_16); } return false; } return err == MPG123_OK; } bool Mpg123Decoder::IsMp3(FILE* stream) { Mpg123Decoder decoder; // Prevent stream handle destruction mpg123_replace_reader_handle(decoder.handle.get(), custom_read, custom_seek, noop_close); // Prevent skipping of too many garbage, breaks heuristic mpg123_param(decoder.handle.get(), MPG123_RESYNC_LIMIT, 64, 0.0); if (!decoder.Open(stream)) { return false; } unsigned char buffer[1024]; int err = 0; size_t done = 0; int err_count = 0; // Read beginning of assumed MP3 file and count errors as an heuristic to detect MP3 for (int i = 0; i < 10; ++i) { err = mpg123_read(decoder.handle.get(), buffer, 1024, &done); if (err != MPG123_OK) { err_count += 1; } if (err_count >= 3) { break; } } return err_count < 3; } int Mpg123Decoder::FillBuffer(uint8_t* buffer, int length) { int err; size_t done = 0; size_t decoded = 0; // Skip invalid frames until getting a valid one do { err = mpg123_read(handle.get(), reinterpret_cast(buffer), length, &done); decoded += done; } while (done && err != MPG123_OK); if (err == MPG123_DONE) { finished = true; } return (int)decoded; } #endif ================================================ FILE: source/audiodec/decoder_mpg123.h ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #ifndef EASYRPG_AUDIO_DECODER_MPG123_H #define EASYRPG_AUDIO_DECODER_MPG123_H // Headers #include "audio_decoder.h" #include #ifdef HAVE_MPG123 #include #endif #include /** * Audio decoder for MP3 powered by mpg123 */ class Mpg123Decoder : public AudioDecoder { public: Mpg123Decoder(); ~Mpg123Decoder(); bool WasInited() const override; bool Open(FILE* file) override; bool Seek(size_t offset, Origin origin) override; bool IsFinished() const override; void GetFormat(int& frequency, AudioDecoder::Format& format, int& channels) const override; bool SetFormat(int frequency, AudioDecoder::Format format, int channels) override; static bool IsMp3(FILE* stream); private: int FillBuffer(uint8_t* buffer, int length) override; #ifdef HAVE_MPG123 std::unique_ptr handle; #endif FILE* file_handle; int err = 0; bool finished = false; int frequency = 44100; }; #endif ================================================ FILE: source/audiodec/decoder_oggvorbis.cpp ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #if defined(HAVE_TREMOR) || defined(HAVE_OGGVORBIS) // Headers #include #ifdef HAVE_TREMOR #include #include #else #include #include #endif #include "audio_decoder.h" #include "decoder_oggvorbis.h" OggVorbisDecoder::OggVorbisDecoder() { music_type = "ogg"; } OggVorbisDecoder::~OggVorbisDecoder() { if (ovf) { ov_clear(ovf); delete ovf; } } bool OggVorbisDecoder::Open(FILE* file) { finished = false; if (ovf) { ov_clear(ovf); delete ovf; } ovf = new OggVorbis_File; int res = ov_open(file, ovf, NULL, 0); if (res < 0) { error_message = "OggVorbis: Error reading file"; delete ovf; fclose(file); return false; } vorbis_info *vi = ov_info(ovf, -1); if (!vi) { error_message = "OggVorbis: Error getting file information"; ov_clear(ovf); delete ovf; return false; } // (long)ov_pcm_total(ovf, -1)) -> decoded length in samples, maybe useful for ticks later? frequency = vi->rate; channels = vi->channels; return true; } bool OggVorbisDecoder::Seek(size_t offset, Origin origin) { if (offset == 0 && origin == Origin::Begin) { if (ovf) { ov_raw_seek(ovf, 0); } finished = false; return true; } return false; } bool OggVorbisDecoder::IsFinished() const { if (!ovf) return false; return finished; } void OggVorbisDecoder::GetFormat(int& freq, AudioDecoder::Format& format, int& chans) const { freq = frequency; format = Format::S16; chans = channels; } bool OggVorbisDecoder::SetFormat(int freq, AudioDecoder::Format format, int chans) { if (freq != frequency || chans != channels || format != Format::S16) return false; return true; } bool OggVorbisDecoder::SetPitch(int pitch) { if (pitch != 100) { return false; } return true; } int OggVorbisDecoder::FillBuffer(uint8_t* buffer, int length) { if (!ovf) return -1; static int section; int read = 0; int to_read = length; do { #ifdef HAVE_TREMOR read = ov_read(ovf, reinterpret_cast(buffer + length - to_read), to_read, §ion); #else read = ov_read(ovf, reinterpret_cast(buffer + length - to_read), to_read, 0/*LE*/, 2/*16bit*/, 1/*signed*/, §ion); #endif // stop decoding when error or end of file if (read <= 0) break; to_read -= read; } while(to_read > 0); // end of file if (read == 0) finished = true; // error if (read < 0) return -1; return length - to_read; } #endif ================================================ FILE: source/audiodec/decoder_oggvorbis.h ================================================ /* * This file is part of EasyRPG Player. * * EasyRPG Player is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EasyRPG Player 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 EasyRPG Player. If not, see . */ #ifndef EASYRPG_AUDIO_DECODER_OGGVORBIS_H #define EASYRPG_AUDIO_DECODER_OGGVORBIS_H // Headers #include #include #ifdef HAVE_TREMOR #include #include #elif HAVE_OGGVORBIS #include #include #endif #include "audio_decoder.h" /** * Audio decoder for Ogg Vorbis powered by libTremor/libOgg+libVorbis */ class OggVorbisDecoder : public AudioDecoder { public: OggVorbisDecoder(); ~OggVorbisDecoder(); // Audio Decoder interface bool Open(FILE* file) override; bool Seek(size_t offset, Origin origin) override; bool IsFinished() const override; void GetFormat(int& frequency, AudioDecoder::Format& format, int& channels) const override; bool SetFormat(int frequency, AudioDecoder::Format format, int channels) override; bool SetPitch(int pitch) override; private: int FillBuffer(uint8_t* buffer, int length) override; #if defined(HAVE_TREMOR) || defined(HAVE_OGGVORBIS) OggVorbis_File *ovf = NULL; #endif bool finished = false; int frequency = 44100; int channels = 2; }; #endif ================================================ FILE: source/block16.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ LEnter16_16: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch0: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch1: movw %cx,2(%edi) addl $0x4,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch2: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch3: movw %cx,2(%edi) addl $0x4,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch4: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch5: movw %cx,2(%edi) addl $0x4,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch6: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch7: movw %cx,2(%edi) addl $0x4,%edi LEnter8_16: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch8: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch9: movw %cx,2(%edi) addl $0x4,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch10: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch11: movw %cx,2(%edi) addl $0x4,%edi LEnter4_16: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch12: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch13: movw %cx,2(%edi) addl $0x4,%edi LEnter2_16: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movw 0x12345678(,%eax,2),%ax LBPatch14: addl %ebp,%edx movw %ax,(%edi) movw 0x12345678(,%ecx,2),%cx LBPatch15: movw %cx,2(%edi) addl $0x4,%edi ================================================ FILE: source/block8.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ LEnter16_8: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch0: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch1: movb %cl,1(%edi) addl $0x2,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch2: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch3: movb %cl,1(%edi) addl $0x2,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch4: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch5: movb %cl,1(%edi) addl $0x2,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch6: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch7: movb %cl,1(%edi) addl $0x2,%edi LEnter8_8: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch8: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch9: movb %cl,1(%edi) addl $0x2,%edi movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch10: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch11: movb %cl,1(%edi) addl $0x2,%edi LEnter4_8: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch12: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch13: movb %cl,1(%edi) addl $0x2,%edi LEnter2_8: movb (%esi),%al movb (%esi,%ebx,),%cl movb %dh,%ah addl %ebp,%edx movb %dh,%ch leal (%esi,%ebx,2),%esi movb 0x12345678(%eax),%al LBPatch14: addl %ebp,%edx movb %al,(%edi) movb 0x12345678(%ecx),%cl LBPatch15: movb %cl,1(%edi) addl $0x2,%edi ================================================ FILE: source/bspfile.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // upper design bounds #define MAX_MAP_HULLS 4 #define MAX_MAP_MODELS 256 #define MAX_MAP_BRUSHES 4096 #define MAX_MAP_ENTITIES 1024 #define MAX_MAP_ENTSTRING 65536 #define MAX_MAP_PLANES 32767 #define MAX_MAP_NODES 32767 // because negative shorts are contents #define MAX_MAP_CLIPNODES 32767 #define MAX_MAP_LEAFS 65535 #define MAX_MAP_VERTS 65535 #define MAX_MAP_FACES 65535 #define MAX_MAP_MARKSURFACES 65535 #define MAX_MAP_TEXINFO 4096 #define MAX_MAP_EDGES 256000 #define MAX_MAP_SURFEDGES 512000 #define MAX_MAP_TEXTURES 512 #define MAX_MAP_MIPTEX 0x200000 #define MAX_MAP_LIGHTING 0x100000 #define MAX_MAP_VISIBILITY 0x100000 #define MAX_MAP_PORTALS 65536 // key / value pair sizes #define MAX_KEY 32 #define MAX_VALUE 1024 //============================================================================= #define Q1_BSPVERSION 29 #define HL_BSPVERSION 30 // RMQ support (2PSB). 32bits instead of shorts for all but bbox sizes (which still use shorts) #define BSP2VERSION_2PSB (('B' << 24) | ('S' << 16) | ('P' << 8) | '2') // BSP2 support. 32bits instead of shorts for everything (bboxes use floats) #define BSP2VERSION_BSP2 (('B' << 0) | ('S' << 8) | ('P' << 16) | ('2'<<24)) #define TOOLVERSION 2 typedef struct { int fileofs, filelen; } lump_t; #define LUMP_ENTITIES 0 #define LUMP_PLANES 1 #define LUMP_TEXTURES 2 #define LUMP_VERTEXES 3 #define LUMP_VISIBILITY 4 #define LUMP_NODES 5 #define LUMP_TEXINFO 6 #define LUMP_FACES 7 #define LUMP_LIGHTING 8 #define LUMP_CLIPNODES 9 #define LUMP_LEAFS 10 #define LUMP_MARKSURFACES 11 #define LUMP_EDGES 12 #define LUMP_SURFEDGES 13 #define LUMP_MODELS 14 #define HEADER_LUMPS 15 typedef struct { float mins[3], maxs[3]; float origin[3]; int headnode[MAX_MAP_HULLS]; int visleafs; // not including the solid leaf 0 int firstface, numfaces; } dmodel_t; typedef struct { int version; lump_t lumps[HEADER_LUMPS]; } dheader_t; typedef struct { int nummiptex; int dataofs[4]; // [nummiptex] } dmiptexlump_t; #define MIPLEVELS 4 typedef struct miptex_s { char name[16]; unsigned width, height; unsigned offsets[MIPLEVELS]; // four mip maps stored } miptex_t; typedef struct { float point[3]; } dvertex_t; // 0-2 are axial planes #define PLANE_X 0 #define PLANE_Y 1 #define PLANE_Z 2 // 3-5 are non-axial planes snapped to the nearest #define PLANE_ANYX 3 #define PLANE_ANYY 4 #define PLANE_ANYZ 5 typedef struct { float normal[3]; float dist; int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate } dplane_t; #define CONTENTS_EMPTY -1 #define CONTENTS_SOLID -2 #define CONTENTS_WATER -3 #define CONTENTS_SLIME -4 #define CONTENTS_LAVA -5 #define CONTENTS_SKY -6 #define CONTENTS_ORIGIN -7 // removed at csg time #define CONTENTS_CLIP -8 // changed to contents_solid #define CONTENTS_CURRENT_0 -9 #define CONTENTS_CURRENT_90 -10 #define CONTENTS_CURRENT_180 -11 #define CONTENTS_CURRENT_270 -12 #define CONTENTS_CURRENT_UP -13 #define CONTENTS_CURRENT_DOWN -14 // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { int planenum; short children[2]; // negative numbers are -(leafs+1), not nodes short mins[3]; // for sphere culling short maxs[3]; unsigned short firstface; unsigned short numfaces; // counting both sides } dsnode_t; typedef struct { int planenum; int children[2]; // negative numbers are -(leafs+1), not nodes short mins[3]; // for sphere culling short maxs[3]; unsigned int firstface; unsigned int numfaces; // counting both sides } dl1node_t; typedef struct { int planenum; int children[2]; // negative numbers are -(leafs+1), not nodes float mins[3]; // for sphere culling float maxs[3]; unsigned int firstface; unsigned int numfaces; // counting both sides } dl2node_t; typedef struct { int planenum; short children[2]; // negative numbers are contents } dsclipnode_t; typedef struct { int planenum; int children[2]; // negative numbers are contents } dlclipnode_t; typedef struct texinfo_s { float vecs[2][4]; // [s/t][xyz offset] int miptex; int flags; } texinfo_t; #define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision // note that edge 0 is never used, because negative edge nums are used for // counterclockwise use of the edge in a face typedef struct { unsigned short v[2]; // vertex numbers } dsedge_t; typedef struct { unsigned int v[2]; // vertex numbers } dledge_t; #define MAXLIGHTMAPS 4 typedef struct { short planenum; short side; int firstedge; // we must support > 64k edges short numedges; short texinfo; // lighting info byte styles[MAXLIGHTMAPS]; int lightofs; // start of [numstyles*surfsize] samples } dsface_t; typedef struct { int planenum; int side; int firstedge; // we must support > 64k edges int numedges; int texinfo; // lighting info byte styles[MAXLIGHTMAPS]; int lightofs; // start of [numstyles*surfsize] samples } dlface_t; #define AMBIENT_WATER 0 #define AMBIENT_SKY 1 #define AMBIENT_SLIME 2 #define AMBIENT_LAVA 3 #define NUM_AMBIENTS 4 // automatic ambient sounds // leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas // all other leafs need visibility info typedef struct { int contents; int visofs; // -1 = no visibility info short mins[3]; // for frustum culling short maxs[3]; unsigned short firstmarksurface; unsigned short nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dsleaf_t; typedef struct { int contents; int visofs; // -1 = no visibility info short mins[3]; // for frustum culling short maxs[3]; unsigned int firstmarksurface; unsigned int nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dl1leaf_t; typedef struct { int contents; int visofs; // -1 = no visibility info float mins[3]; // for frustum culling float maxs[3]; unsigned int firstmarksurface; unsigned int nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; } dl2leaf_t; //============================================================================ #ifndef QUAKE_GAME #define ANGLE_UP -1 #define ANGLE_DOWN -2 // the utilities get to be lazy and just use large static arrays extern int nummodels; extern dmodel_t dmodels[MAX_MAP_MODELS]; extern int visdatasize; extern byte dvisdata[MAX_MAP_VISIBILITY]; extern int lightdatasize; extern byte dlightdata[MAX_MAP_LIGHTING]; extern int texdatasize; extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) extern int entdatasize; extern char dentdata[MAX_MAP_ENTSTRING]; extern int numleafs; extern dleaf_t dleafs[MAX_MAP_LEAFS]; extern int numplanes; extern dplane_t dplanes[MAX_MAP_PLANES]; extern int numvertexes; extern dvertex_t dvertexes[MAX_MAP_VERTS]; extern int numnodes; extern dnode_t dnodes[MAX_MAP_NODES]; extern int numtexinfo; extern texinfo_t texinfo[MAX_MAP_TEXINFO]; extern int numfaces; extern dface_t dfaces[MAX_MAP_FACES]; extern int numclipnodes; extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; extern int numedges; extern dedge_t dedges[MAX_MAP_EDGES]; extern int nummarksurfaces; extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; extern int numsurfedges; extern int dsurfedges[MAX_MAP_SURFEDGES]; void DecompressVis (byte *in, byte *decompressed); int CompressVis (byte *vis, byte *dest); void LoadBSPFile (char *filename); void WriteBSPFile (char *filename); void PrintBSPFileSizes (void); //=============== typedef struct epair_s { struct epair_s *next; char *key; char *value; } epair_t; typedef struct { vec3_t origin; int firstbrush; int numbrushes; epair_t *epairs; } entity_t; extern int num_entities; extern entity_t entities[MAX_MAP_ENTITIES]; void ParseEntities (void); void UnparseEntities (void); void SetKeyValue (entity_t *ent, signed char *key, signed char *value); signed char *ValueForKey (entity_t *ent, signed char *key); // will return "" if not present vec_t FloatForKey (entity_t *ent, signed char *key); void GetVectorForKey (entity_t *ent, signed char *key, vec3_t vec); epair_t *ParseEpair (void); #endif ================================================ FILE: source/cdaudio.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ int CDAudio_Init(void); void CDAudio_Play(byte track, bool looping); void CDAudio_Stop(void); void CDAudio_Pause(void); void CDAudio_Resume(void); void CDAudio_Shutdown(void); void CDAudio_Update(void); ================================================ FILE: source/chase.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // chase.c -- chase camera code #include "quakedef.h" CVAR(chase_active, 0, CVAR_NONE) // CVAR enabling thirdperson CVAR(chase_back, 100, CVAR_NONE) CVAR(chase_up, 16, CVAR_NONE) CVAR(chase_right, 0, CVAR_NONE) //---------------------------------------------- vec3_t chase_pos; vec3_t chase_angles; vec3_t chase_dest; vec3_t chase_dest_angles; void Chase_Init (void) { Cvar_RegisterVariable (&chase_back); Cvar_RegisterVariable (&chase_up); Cvar_RegisterVariable (&chase_right); Cvar_RegisterVariable (&chase_active); } void Chase_Reset (void) { // for respawning and teleporting // start position 12 units behind head } void TraceLine (vec3_t start, vec3_t end, vec3_t impact) { trace_t trace; memset (&trace, 0, sizeof(trace)); SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &trace); VectorCopy (trace.endpos, impact); } void Chase_Update (void) { int i; float dist; vec3_t forward, up, right; vec3_t dest, stop; // if can't see player, reset AngleVectors (cl.viewangles, forward, right, up); // calc exact destination for (i=0 ; i<3 ; i++) chase_dest[i] = r_refdef.vieworg[i] - forward[i]*chase_back.value - right[i]*chase_right.value; chase_dest[2] = r_refdef.vieworg[2] + chase_up.value; // find the spot the player is looking at VectorMA (r_refdef.vieworg, 4096, forward, dest); TraceLine (r_refdef.vieworg, dest, stop); // calculate pitch to look at the same spot from camera VectorSubtract (stop, r_refdef.vieworg, stop); dist = DotProduct (stop, forward); if (dist < 1) dist = 1; r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180; // move towards destination VectorCopy (chase_dest, r_refdef.vieworg); } ================================================ FILE: source/cl_demo.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" bool benchmark; void CL_FinishTimeDemo (void); /* ============================================================================== DEMO CODE When a demo is playing back, all NET_SendMessages are skipped, and NET_GetMessages are read from the demo file. Whenever cl.time gets past the last received message, another message is read from the demo file. ============================================================================== */ /* ============== CL_StopPlayback Called when a demo file runs out, or the user starts a game ============== */ void CL_StopPlayback (void) { if (!cls.demoplayback) return; fclose (cls.demofile); cls.demoplayback = false; cls.demofile = NULL; cls.state = ca_disconnected; if (cls.timedemo || benchmark) CL_FinishTimeDemo (); } /* ==================== CL_WriteDemoMessage Dumps the current net message, prefixed by the length and view angles ==================== */ void CL_WriteDemoMessage (void) { int len; int i; float f; len = LittleLong (net_message.cursize); fwrite (&len, 4, 1, cls.demofile); for (i=0 ; i<3 ; i++) { f = LittleFloat (cl.viewangles[i]); fwrite (&f, 4, 1, cls.demofile); } fwrite (net_message.data, net_message.cursize, 1, cls.demofile); fflush (cls.demofile); } /* ==================== CL_GetMessage Handles recording and playback of demos, on top of NET_ code ==================== */ bool bBenchmarkStarted; int CL_GetMessage (void) { int r, i; float f; if (cls.demoplayback) { // decide if it is time to grab the next message if (cls.signon == SIGNONS) // always grab until fully connected { if (cls.timedemo && !benchmark) { if (host_framecount == cls.td_lastframe) return 0; // already read this frame's message cls.td_lastframe = host_framecount; // if this is the second frame, grab the real td_starttime // so the bogus time on the first frame doesn't count if (host_framecount == cls.td_startframe + 1 && !benchmark){ cls.td_starttime = realtime; } else if (host_framecount == cls.td_startframe + 30 && key_dest == key_benchmark) { bBenchmarkStarted = true; benchmark = true; }// Ignore first sec for the benchmark } else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0]) { return 0; // don't need another message yet } } // get the next message fread (&net_message.cursize, 4, 1, cls.demofile); VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); for (i=0 ; i<3 ; i++) { r = fread (&f, 4, 1, cls.demofile); cl.mviewangles[0][i] = LittleFloat (f); } net_message.cursize = LittleLong (net_message.cursize); if (net_message.cursize > MAX_MSGLEN) Sys_Error ("Demo message > MAX_MSGLEN"); r = fread (net_message.data, net_message.cursize, 1, cls.demofile); if (r != 1) { CL_StopPlayback (); return 0; } return 1; } while (1) { r = NET_GetMessage (cls.netcon); if (r != 1 && r != 2) return r; // discard nop keepalive message if (net_message.cursize == 1 && net_message.data[0] == svc_nop) Con_Printf ("<-- server to client keepalive\n"); else break; } if (cls.demorecording) CL_WriteDemoMessage (); return r; } /* ==================== CL_Stop_f stop recording a demo ==================== */ void CL_Stop_f (void) { if (cmd_source != src_command) return; if (!cls.demorecording) { Con_Printf ("Not recording a demo.\n"); return; } // write a disconnect message to the demo file SZ_Clear (&net_message); MSG_WriteByte (&net_message, svc_disconnect); CL_WriteDemoMessage (); // finish up fclose (cls.demofile); cls.demofile = NULL; cls.demorecording = false; Con_Printf ("Completed demo\n"); } /* ==================== CL_Record_f record [cd track] ==================== */ void CL_Record_f (void) { int c; char name[MAX_OSPATH]; int track; if (cmd_source != src_command) return; c = Cmd_Argc(); if (c != 2 && c != 3 && c != 4) { Con_Printf ("record [ [cd track]]\n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } if (c == 2 && cls.state == ca_connected) { Con_Printf("Can not record - already connected to server\nClient demo recording must be started before connecting\n"); return; } // write the forced cd track number, or -1 if (c == 4) { track = atoi(Cmd_Argv(3)); Con_Printf ("Forcing CD track to %i\n", cls.forcetrack); } else track = -1; sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); // // start the map up // if (c > 2) Cmd_ExecuteString ( va("map %s", Cmd_Argv(2)), src_command); // // open the demo file // COM_DefaultExtension (name, ".dem"); Con_Printf ("recording to %s.\n", name); cls.demofile = fopen (name, "wb"); if (!cls.demofile) { Con_Printf ("ERROR: couldn't open.\n"); return; } cls.forcetrack = track; fprintf (cls.demofile, "%i\n", cls.forcetrack); cls.demorecording = true; } /* ==================== CL_PlayDemo_f play [demoname] ==================== */ void CL_PlayDemo_f (void) { char name[256]; int c; bool neg = false; if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("play : plays a demo\n"); return; } // // disconnect from server // CL_Disconnect (); // // open the demo file // strcpy (name, Cmd_Argv(1)); COM_DefaultExtension (name, ".dem"); Con_Printf ("Playing demo from %s.\n", name); COM_FOpenFile (name, &cls.demofile, NULL); if (!cls.demofile) { Con_Printf ("ERROR: couldn't open.\n"); cls.demonum = -1; // stop demo loop return; } cls.demoplayback = true; cls.state = ca_connected; cls.forcetrack = 0; while ((c = getc(cls.demofile)) != '\n') if (c == '-') neg = true; else cls.forcetrack = cls.forcetrack * 10 + (c - '0'); if (neg) cls.forcetrack = -cls.forcetrack; // ZOID, fscanf is evil // fscanf (cls.demofile, "%i\n", &cls.forcetrack); } /* ==================== CL_FinishTimeDemo ==================== */ extern int average_fps; void CL_FinishTimeDemo (void) { int frames; float time; cls.timedemo = false; // the first frame didn't count frames = (host_framecount - cls.td_startframe) - 1; time = realtime - cls.td_starttime; if (!time) time = 1; if (benchmark){ average_fps = frames/time; key_dest = key_menu; m_state = m_benchmark; benchmark = false; }else Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time); } /* ==================== CL_TimeDemo_f timedemo [demoname] ==================== */ void CL_TimeDemo_f (void) { if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("timedemo : gets demo speeds\n"); return; } CL_PlayDemo_f (); // cls.td_starttime will be grabbed at the second frame of the demo, so // all the loading time doesn't get counted cls.timedemo = true; cls.td_startframe = host_framecount; cls.td_lastframe = -1; // get a new message this frame } /* ==================== CL_Benchmark_f benchmark [demoname] ==================== */ extern int max_fps; extern int min_fps; void CL_Benchmark_f (void) { if (cmd_source != src_command) return; if (Cmd_Argc() != 2) { Con_Printf ("benchmark : starts a demo benchmark\n"); return; } CL_PlayDemo_f (); // cls.td_starttime will be grabbed at the second frame of the demo, so // all the loading time doesn't get counted // resetting fps counters max_fps = 0; min_fps = 999; //benchmark = true; cls.timedemo = true; cls.td_startframe = host_framecount; cls.td_lastframe = -1; // get a new message this frame } ================================================ FILE: source/cl_input.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl.input.c -- builds an intended movement command to send to the server // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All // rights reserved. #include "quakedef.h" /* =============================================================================== KEY BUTTONS Continuous button event tracking is complicated by the fact that two different input sources (say, mouse button 1 and the control key) can both press the same button, but the button should only be released when both of the pressing key have been released. When a key event issues a button command (+forward, +attack, etc), it appends its key number as a parameter to the command so it can be matched up with the release. state bit 0 is the current state of the key state bit 1 is edge triggered on the up to down transition state bit 2 is edge triggered on the down to up transition =============================================================================== */ CVAR(cl_upspeed, 200, CVAR_NONE) CVAR(cl_forwardspeed, 200, CVAR_ARCHIVE) CVAR(cl_backspeed, 200, CVAR_ARCHIVE) CVAR(cl_sidespeed, 350, CVAR_NONE) CVAR(cl_movespeedkey, 2.0, CVAR_NONE) CVAR(cl_yawspeed, 140, CVAR_NONE) CVAR(cl_pitchspeed, 150, CVAR_NONE) CVAR(cl_anglespeedkey, 1.5, CVAR_NONE) CVAR(cl_fullpitch, 0, CVAR_NONE) // ProQuake - get rid of the "unknown command" messages //---------------------------------------------- kbutton_t in_mlook, in_klook; kbutton_t in_left, in_right, in_forward, in_back; kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; kbutton_t in_strafe, in_speed, in_use, in_jump, in_attack; kbutton_t in_up, in_down; int in_impulse; void KeyDown (kbutton_t *b) { int k; char *c; c = Cmd_Argv(1); if (c[0]) k = atoi(c); else k = -1; // typed manually at the console for continuous down if (k == b->down[0] || k == b->down[1]) return; // repeating key if (!b->down[0]) b->down[0] = k; else if (!b->down[1]) b->down[1] = k; else { Con_Printf ("Three keys down for a button!\n"); return; } if (b->state & 1) return; // still down b->state |= 1 + 2; // down + impulse down } void KeyUp (kbutton_t *b) { int k; char *c; c = Cmd_Argv(1); if (c[0]) k = atoi(c); else { // typed manually at the console, assume for unsticking, so clear all b->down[0] = b->down[1] = 0; b->state = 4; // impulse up return; } if (b->down[0] == k) b->down[0] = 0; else if (b->down[1] == k) b->down[1] = 0; else return; // key up without coresponding down (menu pass through) if (b->down[0] || b->down[1]) return; // some other key is still holding it down if (!(b->state & 1)) return; // still up (this should not happen) b->state &= ~1; // now up b->state |= 4; // impulse up } void IN_KLookDown (void) {KeyDown(&in_klook);} void IN_KLookUp (void) {KeyUp(&in_klook);} void IN_MLookDown (void) {KeyDown(&in_mlook);} void IN_MLookUp (void) { KeyUp(&in_mlook); if ( !(in_mlook.state&1) && lookspring.value) V_StartPitchDrift(); } void IN_UpDown(void) {KeyDown(&in_up);} void IN_UpUp(void) {KeyUp(&in_up);} void IN_DownDown(void) {KeyDown(&in_down);} void IN_DownUp(void) {KeyUp(&in_down);} void IN_LeftDown(void) {KeyDown(&in_left);} void IN_LeftUp(void) {KeyUp(&in_left);} void IN_RightDown(void) {KeyDown(&in_right);} void IN_RightUp(void) {KeyUp(&in_right);} void IN_ForwardDown(void) {KeyDown(&in_forward);} void IN_ForwardUp(void) {KeyUp(&in_forward);} void IN_BackDown(void) {KeyDown(&in_back);} void IN_BackUp(void) {KeyUp(&in_back);} void IN_LookupDown(void) {KeyDown(&in_lookup);} void IN_LookupUp(void) {KeyUp(&in_lookup);} void IN_LookdownDown(void) {KeyDown(&in_lookdown);} void IN_LookdownUp(void) {KeyUp(&in_lookdown);} void IN_MoveleftDown(void) {KeyDown(&in_moveleft);} void IN_MoveleftUp(void) {KeyUp(&in_moveleft);} void IN_MoverightDown(void) {KeyDown(&in_moveright);} void IN_MoverightUp(void) {KeyUp(&in_moveright);} void IN_SpeedDown(void) {KeyDown(&in_speed);} void IN_SpeedUp(void) {KeyUp(&in_speed);} void IN_StrafeDown(void) {KeyDown(&in_strafe);} void IN_StrafeUp(void) {KeyUp(&in_strafe);} void IN_AttackDown(void) {KeyDown(&in_attack);} void IN_AttackUp(void) {KeyUp(&in_attack);} void IN_UseDown (void) {KeyDown(&in_use);} void IN_UseUp (void) {KeyUp(&in_use);} void IN_JumpDown (void) {KeyDown(&in_jump);} void IN_JumpUp (void) {KeyUp(&in_jump);} void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));} /* =============== CL_KeyState Returns 0.25 if a key was pressed and released during the frame, 0.5 if it was pressed and held 0 if held then released, and 1.0 if held for the entire time =============== */ float CL_KeyState (kbutton_t *key) { float val; bool impulsedown, impulseup, down; impulsedown = key->state & 2; impulseup = key->state & 4; down = key->state & 1; val = 0; if (impulsedown && !impulseup) if (down) val = 0.5; // pressed and held this frame else val = 0; // I_Error (); if (impulseup && !impulsedown) if (down) val = 0; // I_Error (); else val = 0; // released this frame if (!impulsedown && !impulseup) if (down) val = 1.0; // held the entire frame else val = 0; // up the entire frame if (impulsedown && impulseup) if (down) val = 0.75; // released and re-pressed this frame else val = 0.25; // pressed and released this frame key->state &= 1; // clear impulses return val; } //========================================================================== /* ================ CL_AdjustAngles Moves the local angle positions ================ */ void CL_AdjustAngles (void) { float speed; float up, down; if (in_speed.state & 1) speed = host_frametime * cl_anglespeedkey.value; else speed = host_frametime; if (gl_xflip.value) cl.viewangles[YAW] *= -1; if (!(in_strafe.state & 1)) { cl.viewangles[YAW] -= speed*cl_yawspeed.value*CL_KeyState (&in_right); cl.viewangles[YAW] += speed*cl_yawspeed.value*CL_KeyState (&in_left); cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]); } if (gl_xflip.value) cl.viewangles[YAW] *= -1; if (in_klook.state & 1) { V_StopPitchDrift (); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * CL_KeyState (&in_forward); cl.viewangles[PITCH] += speed*cl_pitchspeed.value * CL_KeyState (&in_back); } up = CL_KeyState (&in_lookup); down = CL_KeyState(&in_lookdown); cl.viewangles[PITCH] -= speed*cl_pitchspeed.value * up; cl.viewangles[PITCH] += speed*cl_pitchspeed.value * down; if (up || down) V_StopPitchDrift (); // ProQuake aiming compatibility if (pq_fullpitch.value) cl.viewangles[PITCH] = COM_Clamp(cl.viewangles[PITCH], -90, 90); else cl.viewangles[PITCH] = COM_Clamp(cl.viewangles[PITCH], -70, 80); cl.viewangles[ROLL] = COM_Clamp(cl.viewangles[ROLL], -50, 50); } /* ================ CL_BaseMove Send the intended movement message to the server ================ */ void CL_BaseMove (usercmd_t *cmd) { if (cls.signon != SIGNONS) return; CL_AdjustAngles (); memset (cmd, 0, sizeof(*cmd)); if (in_strafe.state & 1) { cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_right); cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_left); } cmd->sidemove += cl_sidespeed.value * CL_KeyState (&in_moveright); cmd->sidemove -= cl_sidespeed.value * CL_KeyState (&in_moveleft); if(gl_xflip.value) cmd->sidemove *= -1; cmd->upmove += cl_upspeed.value * CL_KeyState (&in_up); cmd->upmove -= cl_upspeed.value * CL_KeyState (&in_down); if (! (in_klook.state & 1) ) { cmd->forwardmove += cl_forwardspeed.value * CL_KeyState (&in_forward); cmd->forwardmove -= cl_backspeed.value * CL_KeyState (&in_back); } // // adjust for speed key // if (in_speed.state & 1) { cmd->forwardmove *= cl_movespeedkey.value; cmd->sidemove *= cl_movespeedkey.value; cmd->upmove *= cl_movespeedkey.value; } #ifdef QUAKE2 cmd->lightlevel = cl.light_level; #endif } /* ============== CL_SendMove ============== */ void CL_SendMove(usercmd_t *cmd) { int i; int bits; sizebuf_t buf; byte data[128]; buf.maxsize = 128; buf.cursize = 0; buf.data = data; cl.cmd = *cmd; // // send the movement message // MSG_WriteByte(&buf, clc_move); MSG_WriteFloat(&buf, cl.mtime[0]); // so server can get ping times if (!cls.demoplayback && (cls.netcon->proquake_connection == MOD_PROQUAKE)) { for (i = 0; i < 3; i++) MSG_WritePreciseAngle(&buf, cl.viewangles[i]); } else { for (i = 0; i < 3; i++) MSG_WriteAngle(&buf, cl.viewangles[i]); } MSG_WriteShort (&buf, cmd->forwardmove); MSG_WriteShort (&buf, cmd->sidemove); MSG_WriteShort (&buf, cmd->upmove); // // send button bits // bits = 0; if ( in_attack.state & 3 ) bits |= 1; in_attack.state &= ~2; if (in_jump.state & 3) bits |= 2; in_jump.state &= ~2; MSG_WriteByte (&buf, bits); MSG_WriteByte (&buf, in_impulse); in_impulse = 0; #ifdef QUAKE2 // // light level // MSG_WriteByte (&buf, cmd->lightlevel); #endif // // deliver the message // if (cls.demoplayback) return; // // always dump the first two message, because it may contain leftover inputs // from the last level // if (++cl.movemessages <= 2) return; if (NET_SendUnreliableMessage (cls.netcon, &buf) == -1) { Con_Printf ("CL_SendMove: lost server connection\n"); CL_Disconnect (); } } /* ============ CL_InitInput ============ */ void CL_InitInput (void) { Cmd_AddCommand ("+moveup",IN_UpDown); Cmd_AddCommand ("-moveup",IN_UpUp); Cmd_AddCommand ("+movedown",IN_DownDown); Cmd_AddCommand ("-movedown",IN_DownUp); Cmd_AddCommand ("+left",IN_LeftDown); Cmd_AddCommand ("-left",IN_LeftUp); Cmd_AddCommand ("+right",IN_RightDown); Cmd_AddCommand ("-right",IN_RightUp); Cmd_AddCommand ("+forward",IN_ForwardDown); Cmd_AddCommand ("-forward",IN_ForwardUp); Cmd_AddCommand ("+back",IN_BackDown); Cmd_AddCommand ("-back",IN_BackUp); Cmd_AddCommand ("+lookup", IN_LookupDown); Cmd_AddCommand ("-lookup", IN_LookupUp); Cmd_AddCommand ("+lookdown", IN_LookdownDown); Cmd_AddCommand ("-lookdown", IN_LookdownUp); Cmd_AddCommand ("+strafe", IN_StrafeDown); Cmd_AddCommand ("-strafe", IN_StrafeUp); Cmd_AddCommand ("+moveleft", IN_MoveleftDown); Cmd_AddCommand ("-moveleft", IN_MoveleftUp); Cmd_AddCommand ("+moveright", IN_MoverightDown); Cmd_AddCommand ("-moveright", IN_MoverightUp); Cmd_AddCommand ("+speed", IN_SpeedDown); Cmd_AddCommand ("-speed", IN_SpeedUp); Cmd_AddCommand ("+attack", IN_AttackDown); Cmd_AddCommand ("-attack", IN_AttackUp); Cmd_AddCommand ("+use", IN_UseDown); Cmd_AddCommand ("-use", IN_UseUp); Cmd_AddCommand ("+jump", IN_JumpDown); Cmd_AddCommand ("-jump", IN_JumpUp); Cmd_AddCommand ("impulse", IN_Impulse); Cmd_AddCommand ("+klook", IN_KLookDown); Cmd_AddCommand ("-klook", IN_KLookUp); Cmd_AddCommand ("+mlook", IN_MLookDown); Cmd_AddCommand ("-mlook", IN_MLookUp); Cvar_RegisterVariable (&cl_fullpitch); // ProQuake CVAR } ================================================ FILE: source/cl_main.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_main.c -- client main loop #include "quakedef.h" #include // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. // these two are not intended to be set directly cvar_t cl_name = {"_cl_name", "player", CVAR_ARCHIVE}; cvar_t cl_color = {"_cl_color", "0", CVAR_ARCHIVE}; CVAR (cl_shownet, 0, CVAR_DEBUG) // can be 0, 1, or 2 CVAR (cl_nolerp, 0, CVAR_NONE) CVAR (always_run, 1, CVAR_ARCHIVE) CVAR (lookspring, 0, CVAR_ARCHIVE) CVAR (lookstrafe, 0, CVAR_ARCHIVE) // ToDo: Redirect them to the mouse input file. CVAR (sensitivity, 3, CVAR_ARCHIVE) CVAR (m_pitch, 0.022, CVAR_ARCHIVE) CVAR (m_yaw, 0.022, CVAR_ARCHIVE) CVAR (m_forward, 1, CVAR_ARCHIVE) CVAR (m_side, 0.8, CVAR_ARCHIVE) cvar_t cl_web_download = {"cl_web_download", "1", CVAR_ARCHIVE}; cvar_t cl_web_download_url = {"cl_web_download_url", "http://bigfoot.servequake.com/", CVAR_ARCHIVE}; // PSVITA extra Cvars (ToDo: redirect them to in_psp2.c, since it has nothing to do here) CVAR (invert_camera, 0, CVAR_ARCHIVE) //---------------------------------------------- client_static_t cls; client_state_t cl; efrag_t* cl_efrags; entity_t* cl_entities; entity_t* cl_static_entities; lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; dlight_t cl_dlights[MAX_DLIGHTS]; int cl_numvisedicts; entity_t *cl_visedicts[MAX_VISEDICTS]; /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { int i; if (!sv.active) Host_ClearMemory (); // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); SZ_Clear (&cls.message); // clear other arrays memset (cl_efrags, 0, sizeof(cl_efrags)); memset (cl_entities, 0, sizeof(cl_entities)); memset (cl_dlights, 0, sizeof(cl_dlights)); memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); memset (cl_temp_entities, 0, sizeof(cl_temp_entities)); memset (cl_beams, 0, sizeof(cl_beams)); // // allocate the efrags and chain together into a free list // cl.free_efrags = cl_efrags; for (i=0 ; i>4, ((int)cl_color.value)&15)); MSG_WriteByte (&cls.message, clc_stringcmd); sprintf (str, "spawn %s", cls.spawnparms); MSG_WriteString (&cls.message, str); break; case 3: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "begin"); Cache_Report (); // print remaining memory break; case 4: SCR_EndLoadingPlaque (); // allow normal screen updates cshift_empty.percent = 0; // Ch0wW: Hacky fix for the TF screen fading to black. break; } } /* ===================== CL_NextDemo Called to play the next demo in the demo loop ===================== */ void CL_NextDemo (void) { char str[1024]; if (cls.demonum == -1) return; // don't play demos SCR_BeginLoadingPlaque (); if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) { cls.demonum = 0; if (!cls.demos[cls.demonum][0]) { Con_Printf ("No demos listed with startdemos\n"); cls.demonum = -1; return; } } sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]); Cbuf_InsertText (str); cls.demonum++; } /* ============== CL_PrintEntities_f ============== */ void CL_PrintEntities_f (void) { entity_t *ent; int i; for (i=0,ent=cl_entities ; imodel) { Con_Printf ("EMPTY\n"); continue; } Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n" ,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]); } } /* =============== SetPal Debugging tool, just flashes the screen =============== */ void SetPal (int i) { } /* =============== CL_AllocDlight =============== */ dlight_t *CL_AllocDlight (int key) { int i; dlight_t *dl; // first look for an exact key match if (key) { dl = cl_dlights; for (i=0 ; ikey == key) { memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc return dl; } } } // then look for anything else dl = cl_dlights; for (i=0 ; idie < cl.time) { memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc return dl; } } dl = &cl_dlights[0]; memset (dl, 0, sizeof(*dl)); dl->key = key; dl->color[0] = dl->color[1] = dl->color[2] = 1; //johnfitz -- lit support via lordhavoc return dl; } /* =============== CL_DecayLights =============== */ void CL_DecayLights (void) { int i; dlight_t *dl; float time; time = cl.time - cl.oldtime; dl = cl_dlights; for (i=0 ; idie < cl.time || !dl->radius) continue; dl->radius -= time*dl->decay; if (dl->radius < 0) dl->radius = 0; } } /* =============== CL_LerpPoint Determines the fraction between the last two messages that the objects should be put at. =============== */ extern bool benchmark; float CL_LerpPoint (void) { float f, frac; f = cl.mtime[0] - cl.mtime[1]; if (!f || cl_nolerp.value || (cls.timedemo && !benchmark) || sv.active) { cl.time = cl.mtime[0]; return 1; } if (f > 0.1) { // dropped packet, or start of demo cl.mtime[1] = cl.mtime[0] - 0.1; f = 0.1; } frac = (cl.time - cl.mtime[1]) / f; //Con_Printf ("frac: %f\n",frac); if (frac < 0) { if (frac < -0.01) { SetPal(1); cl.time = cl.mtime[1]; // Con_Printf ("low frac\n"); } frac = 0; } else if (frac > 1) { if (frac > 1.01) { SetPal(2); cl.time = cl.mtime[0]; // Con_Printf ("high frac\n"); } frac = 1; } else SetPal(0); return frac; } /* =============== CL_RelinkEntities =============== */ void CL_RelinkEntities (void) { entity_t *ent; int i, j; float frac, f, d; vec3_t delta; float bobjrotate; vec3_t oldorg; dlight_t *dl; // determine partial update time frac = CL_LerpPoint (); cl_numvisedicts = 0; // // interpolate player info // for (i=0 ; i<3 ; i++) cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]); if (cls.demoplayback) { // interpolate the angles for (j=0 ; j<3 ; j++) { d = cl.mviewangles[0][j] - cl.mviewangles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; cl.viewangles[j] = cl.mviewangles[1][j] + frac*d; } } bobjrotate = anglemod(100*cl.time); // start on the entity after the world for (i=1,ent=cl_entities+1 ; imodel) { // empty slot if (ent->forcelink) R_RemoveEfrags (ent); // just became empty continue; } // if the object wasn't included in the last packet, remove it if (ent->msgtime != cl.mtime[0]) { ent->model = NULL; // fenix@io.com: model transform interpolation ent->frame_start_time = 0; ent->translate_start_time = 0; ent->rotate_start_time = 0; continue; } VectorCopy (ent->origin, oldorg); if (ent->forcelink) { // the entity was not updated in the last message // so move to the final spot VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->angles); } else { // if the delta is large, assume a teleport and don't lerp f = frac; for (j=0 ; j<3 ; j++) { delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j]; if (delta[j] > 100 || delta[j] < -100) f = 1; // assume a teleportation, not a motion } // fenix@io.com: model transform interpolation // interpolation should be reset in the event of a large delta if (f >= 1){ ent->frame_start_time = 0; ent->translate_start_time = 0; ent->rotate_start_time = 0; } // interpolate the origin and angles for (j=0 ; j<3 ; j++) { ent->origin[j] = ent->msg_origins[1][j] + f*delta[j]; d = ent->msg_angles[0][j] - ent->msg_angles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; ent->angles[j] = ent->msg_angles[1][j] + f*d; } } if (!(ent->effects & 0xFF800000)) //qbism based on DP model flags ent->effects |= ent->model->flags; // rotate binary objects locally if (ent->effects & EF_ROTATE) { ent->angles[1] = bobjrotate; // MUFF - makes them bob as well as rotate ;) ent->origin[2] += (( sin(bobjrotate/90*M_PI) * 5) + 5 ); } if (ent->effects & EF_BRIGHTFIELD) R_EntityParticles (ent); #ifdef QUAKE2 if (ent->effects & EF_DARKFIELD) R_DarkFieldParticles (ent); #endif if (ent->effects & EF_MUZZLEFLASH) { vec3_t fv, rv, uv; dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; AngleVectors (ent->angles, fv, rv, uv); VectorMA (dl->origin, 18, fv, dl->origin); dl->radius = 200 + (rand()&31); dl->minlight = 32; dl->die = cl.time + 0.1f; dl->color[0] = 0.2f; dl->color[1] = 0.1f; dl->color[2] = 0.05f; dl->alpha = 0.7f; } if (ent->effects & EF_BRIGHTLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; dl->radius = 400 + (rand()&31); dl->die = cl.time + 0.001f; dl->color[0] = 0.2f; dl->color[1] = 0.1f; dl->color[2] = 0.05f; dl->alpha = 0.7f; } if (ent->effects & EF_DIMLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand()&31); dl->die = cl.time + 0.001f; dl->color[0] = 0.2f; dl->color[1] = 0.1f; dl->color[2] = 0.05f; dl->alpha = 0.7f; } if (ent->effects & EF_BLUE) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand()&31); dl->die = cl.time + 0.001f; dl->color[0] = 0.05f; dl->color[1] = 0.05f; dl->color[2] = 0.3f; dl->alpha = 0.7f; } if (ent->effects & EF_RED) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand()&31); dl->die = cl.time + 0.001f; dl->color[0] = 0.5f; dl->color[1] = 0.05f; dl->color[2] = 0.05f; dl->alpha = 0.7f; } if ((ent->effects & (EF_BLUE | EF_RED)) == (EF_BLUE | EF_RED)) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand()&31); dl->die = cl.time + 0.001f; dl->color[0] = 0.5f; dl->color[1] = 0.05f; dl->color[2] = 0.4f; dl->alpha = 0.7f; } #ifdef QUAKE2 if (ent->effects & EF_DARKLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200.0 + (rand()&31); dl->die = cl.time + 0.001f; dl->dark = true; } if (ent->effects & EF_LIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.001f; } #endif if (ent->effects & EF_GIB) R_RocketTrail (oldorg, ent->origin, 2); else if (ent->effects & EF_ZOMGIB) R_RocketTrail (oldorg, ent->origin, 4); else if (ent->effects & EF_TRACER) R_RocketTrail (oldorg, ent->origin, 3); else if (ent->effects & EF_TRACER2) R_RocketTrail (oldorg, ent->origin, 5); else if (ent->effects & EF_ROCKET) { R_RocketTrail (oldorg, ent->origin, 0); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.01; dl->color[0] = 0.2f; dl->color[1] = 0.1f; dl->color[2] = 0.05f; dl->color[3] = 0.7f; } else if (ent->effects & EF_GRENADE) R_RocketTrail (oldorg, ent->origin, 1); else if (ent->effects & EF_TRACER3) R_RocketTrail (oldorg, ent->origin, 6); ent->forcelink = false; if (i == cl.viewentity && !chase_active.value) continue; if ( ent->effects & EF_NODRAW ) continue; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } if (!ent->alpha) ent->alpha = 1.0f; } } /* =============== CL_ReadFromServer Read all incoming data from the server =============== */ int CL_ReadFromServer (void) { int ret; cl.oldtime = cl.time; cl.time += host_frametime; do { ret = CL_GetMessage (); if (ret == -1) Host_Error ("CL_ReadFromServer: lost server connection"); if (!ret) break; cl.last_received_message = realtime; CL_ParseServerMessage (); } while (ret && cls.state == ca_connected); if (cl_shownet.value) Con_Printf ("\n"); CL_RelinkEntities (); CL_UpdateTEnts (); // // bring the links up to date // return 0; } /* ================= CL_SendCmd ================= */ void CL_SendCmd (void) { usercmd_t cmd; if (cls.state != ca_connected) return; if (cls.signon == SIGNONS) { // get basic movement from keyboard CL_BaseMove (&cmd); // allow mice or other external controllers to add to the move IN_Move (&cmd); // send the unreliable message CL_SendMove (&cmd); } if (cls.demoplayback) { SZ_Clear (&cls.message); return; } // send the reliable message if (!cls.message.cursize) return; // no message at all if (!NET_CanSendMessage (cls.netcon)) { Con_DPrintf ("CL_WriteToServer: can't send\n"); return; } if (NET_SendMessage (cls.netcon, &cls.message) == -1) Host_Error ("CL_WriteToServer: lost server connection"); SZ_Clear (&cls.message); } /* ================= CL_Init ================= */ void CL_Init (void) { SZ_Alloc (&cls.message, 1024); CL_InitInput (); CL_InitTEnts (); // // register our commands // Cvar_RegisterVariable (&cl_name); Cvar_RegisterVariable (&cl_color); Cvar_RegisterVariable (&cl_upspeed); Cvar_RegisterVariable (&cl_forwardspeed); Cvar_RegisterVariable (&cl_backspeed); Cvar_RegisterVariable (&cl_sidespeed); Cvar_RegisterVariable (&cl_movespeedkey); Cvar_RegisterVariable (&cl_yawspeed); Cvar_RegisterVariable (&cl_pitchspeed); Cvar_RegisterVariable (&cl_anglespeedkey); Cvar_RegisterVariable (&cl_shownet); Cvar_RegisterVariable (&cl_nolerp); Cvar_RegisterVariable (&lookspring); Cvar_RegisterVariable (&lookstrafe); Cvar_RegisterVariable (&sensitivity); Cvar_RegisterVariable (&cl_web_download); Cvar_RegisterVariable (&cl_web_download_url); Cvar_RegisterVariable (&m_pitch); Cvar_RegisterVariable (&m_yaw); Cvar_RegisterVariable (&m_forward); Cvar_RegisterVariable (&m_side); // Cvar_RegisterVariable (&cl_autofire); Cmd_AddCommand ("entities", CL_PrintEntities_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("stop", CL_Stop_f); Cmd_AddCommand ("playdemo", CL_PlayDemo_f); Cmd_AddCommand ("timedemo", CL_TimeDemo_f); Cmd_AddCommand ("benchmark", CL_Benchmark_f); } ================================================ FILE: source/cl_parse.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_parse.c -- parse a message received from the server #include #include "quakedef.h" #include "webdownload.h" char *svc_strings[] = { "svc_bad", "svc_nop", "svc_disconnect", "svc_updatestat", "svc_version", // [long] server version "svc_setview", // [short] entity number "svc_sound", // "svc_time", // [float] server time "svc_print", // [string] null terminated string "svc_stufftext", // [string] stuffed into client's console buffer // the string should be \n terminated "svc_setangle", // [vec3] set the view angle to this absolute value "svc_serverinfo", // [long] version // [string] signon string // [string]..[0]model cache [string]...[0]sounds cache // [string]..[0]item cache "svc_lightstyle", // [byte] [string] "svc_updatename", // [byte] [string] "svc_updatefrags", // [byte] [short] "svc_clientdata", // "svc_stopsound", // "svc_updatecolors", // [byte] [byte] "svc_particle", // [vec3] "svc_damage", // [byte] impact [byte] blood [vec3] from "svc_spawnstatic", "OBSOLETE svc_spawnbinary", "svc_spawnbaseline", "svc_temp_entity", // "svc_setpause", "svc_signonnum", "svc_centerprint", "svc_killedmonster", "svc_foundsecret", "svc_spawnstaticsound", "svc_intermission", "svc_finale", // [string] music [string] text "svc_cdtrack", // [byte] track [byte] looptrack "svc_sellscreen", "svc_cutscene" }; //============================================================================= /* =============== CL_EntityNum This error checks and tracks the total number of entities =============== */ entity_t *CL_EntityNum (int num) { if (num >= cl.num_entities) { if (num >= MAX_EDICTS) Host_Error ("CL_EntityNum: %i is an invalid number",num); while (cl.num_entities<=num) { cl_entities[cl.num_entities].colormap = vid.colormap; cl.num_entities++; } } return &cl_entities[num]; } /* ================== CL_ParseStartSoundPacket ================== */ void CL_ParseStartSoundPacket(void) { vec3_t pos; int channel, ent; int sound_num; int volume; int field_mask; float attenuation; int i; field_mask = MSG_ReadByte(); if (field_mask & SND_VOLUME) volume = MSG_ReadByte (); else volume = DEFAULT_SOUND_PACKET_VOLUME; if (field_mask & SND_ATTENUATION) attenuation = MSG_ReadByte () / 64.0; else attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; channel = MSG_ReadShort (); sound_num = MSG_ReadByte (); ent = channel >> 3; channel &= 7; if (ent > MAX_EDICTS) Host_Error ("CL_ParseStartSoundPacket: ent = %i", ent); for (i=0 ; i<3 ; i++) pos[i] = MSG_ReadCoord (); S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation); } /* ================== CL_KeepaliveMessage When the client is taking a long time to load stuff, send keepalive messages so the server doesn't disconnect. ================== */ void CL_KeepaliveMessage (void) { float time; static float lastmsg; int ret; sizebuf_t old; byte* olddata; if (sv.active) return; // no need if server is local if (cls.demoplayback) return; // read messages from server, should just be nops old = net_message; olddata = Sys_BigStackAlloc(8192 * sizeof(byte), "CL_KeepaliveMessage"); memcpy (olddata, net_message.data, net_message.cursize); do { ret = CL_GetMessage (); switch (ret) { default: Host_Error ("CL_KeepaliveMessage: CL_GetMessage failed"); case 0: break; // nothing waiting case 1: Host_Error ("CL_KeepaliveMessage: received a message"); break; case 2: if (MSG_ReadByte() != svc_nop) Host_Error ("CL_KeepaliveMessage: datagram wasn't a nop"); break; } } while (ret); net_message = old; memcpy (net_message.data, olddata, net_message.cursize); // check time time = Sys_FloatTime (); if (time - lastmsg < 5) { Sys_BigStackFree(8192 * sizeof(byte), "CL_KeepaliveMessage"); return; } lastmsg = time; // write out a nop Con_Printf ("--> client to server keepalive\n"); MSG_WriteByte (&cls.message, clc_nop); NET_SendMessage (cls.netcon, &cls.message); SZ_Clear (&cls.message); Sys_BigStackFree(8192 * sizeof(byte), "CL_KeepaliveMessage"); } /* ===================== CL_WebDownloadProgress Callback function for webdownloads. Since Web_Get only returns once it's done, we have to do various things here: Update download percent, handle input, redraw UI and send net packets. ===================== */ static int CL_WebDownloadProgress( double percent ) { static double time, oldtime, newtime; cls.download.percent = percent; CL_KeepaliveMessage(); newtime = Sys_FloatTime (); time = newtime - oldtime; Host_Frame (time); oldtime = newtime; return cls.download.disconnect; // abort if disconnect received } /* ================== CL_ParseServerInfo ================== */ void CL_ParseServerInfo (void) { char *str; int i, maxlen; int nummodels, numsounds; char** sound_precache; char** model_precache; char tempname[MAX_QPATH]; Con_DPrintf ("Serverinfo packet received.\n"); extern cvar_t cl_web_download; extern cvar_t cl_web_download_url; // // wipe the client_state_t struct // CL_ClearState (); // parse protocol version number i = MSG_ReadLong (); if (i != PROTOCOL_NETQUAKE && i != PROTOCOL_FITZQUAKE) { Host_Error("Server returned version %i, not %i (Net/ProQuake) or %i (FitzQuake)", i, PROTOCOL_NETQUAKE, PROTOCOL_FITZQUAKE); Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_NETQUAKE); return; } // parse maxclients cl.maxclients = MSG_ReadByte (); if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD) { Con_Printf("Bad maxclients (%u) from server\n", cl.maxclients); return; } cl.scores = Hunk_AllocName (cl.maxclients*sizeof(*cl.scores), "scores"); // parse gametype cl.gametype = MSG_ReadByte (); // parse signon message str = MSG_ReadString (); strncpy (cl.levelname, str, sizeof(cl.levelname)-1); // seperate the printfs so the server message can have a color Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); Con_Printf ("%c%s\n", 2, str); // // first we go through and touch all of the precache data that still // happens to be in the cache, so precaching something else doesn't // needlessly purge it // model_precache = Sys_BigStackAlloc(MAX_MODELS * sizeof(char*), "CL_ParseServerInfo"); for (i = 0; i < MAX_MODELS; i++) { model_precache[i] = Sys_BigStackAlloc(MAX_QPATH, "CL_ParseServerInfo"); }; // precache models memset (cl.model_precache, 0, sizeof(cl.model_precache)); for (nummodels=1 ; ; nummodels++) { str = MSG_ReadString (); if (!str[0]) break; if (nummodels == MAX_MODELS) { Con_Printf("Server sent too many model precaches\n"); for (i = MAX_MODELS - 1; i >= 0; i--) { free(model_precache[i]); }; free(model_precache); return; } strcpy (model_precache[nummodels], str); Mod_TouchModel (str); } // precache sounds sound_precache = Sys_BigStackAlloc(MAX_SOUNDS * sizeof(char*), "CL_ParseServerInfo"); for (i = 0; i < MAX_SOUNDS; i++) { sound_precache[i] = Sys_BigStackAlloc(MAX_QPATH, "CL_ParseServerInfo"); }; memset (cl.sound_precache, 0, sizeof(cl.sound_precache)); for (numsounds=1 ; ; numsounds++) { str = MSG_ReadString (); if (!str[0]) break; if (numsounds == MAX_SOUNDS) { Con_Printf("Server sent too many sound precaches\n"); Sys_BigStackFree(MAX_MODELS * sizeof(char*) + MAX_MODELS * MAX_QPATH + MAX_SOUNDS * sizeof(char*) + MAX_SOUNDS * MAX_QPATH, "CL_ParseServerInfo"); return; } strcpy (sound_precache[numsounds], str); S_TouchSound (str); } // // now we try to load everything else until a cache allocation fails // for (i=1 ; imsgtime != cl.mtime[1]) forcelink = true; // no previous frame to lerp from else forcelink = false; ent->msgtime = cl.mtime[0]; if (bits & U_MODEL) { modnum = MSG_ReadByte (); if (modnum >= MAX_MODELS) Host_Error ("CL_ParseModel: bad modnum"); } else modnum = ent->baseline.modelindex; model = cl.model_precache[modnum]; if (model != ent->model) { ent->model = model; // automatic animation (torches, etc) can be either all together // or randomized if (model) { if (model->synctype == ST_RAND) ent->syncbase = (float)(rand()&0x7fff) / 0x7fff; else ent->syncbase = 0.0; } else forcelink = true; // hack to make null model players work #ifdef GLQUAKE if (num > 0 && num <= cl.maxclients) R_TranslatePlayerSkin (num - 1); #endif } if (bits & U_FRAME) ent->frame = MSG_ReadByte (); else ent->frame = ent->baseline.frame; if (bits & U_COLORMAP) i = MSG_ReadByte(); else i = ent->baseline.colormap; if (!i) ent->colormap = vid.colormap; else { if (i > cl.maxclients) Sys_Error ("i >= cl.maxclients"); ent->colormap = cl.scores[i-1].translations; } if (bits & U_SKIN) skin = MSG_ReadByte(); else skin = ent->baseline.skin; if (skin != ent->skinnum) { ent->skinnum = skin; if (num > 0 && num <= cl.maxclients) R_TranslatePlayerSkin (num - 1); } if (bits & U_EFFECTS) ent->effects = MSG_ReadByte(); else ent->effects = ent->baseline.effects; // shift the known values for interpolation VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); if (bits & U_ORIGIN1) ent->msg_origins[0][0] = MSG_ReadCoord (); else ent->msg_origins[0][0] = ent->baseline.origin[0]; if (bits & U_ANGLE1) ent->msg_angles[0][0] = MSG_ReadAngle(); else ent->msg_angles[0][0] = ent->baseline.angles[0]; if (bits & U_ORIGIN2) ent->msg_origins[0][1] = MSG_ReadCoord (); else ent->msg_origins[0][1] = ent->baseline.origin[1]; if (bits & U_ANGLE2) ent->msg_angles[0][1] = MSG_ReadAngle(); else ent->msg_angles[0][1] = ent->baseline.angles[1]; if (bits & U_ORIGIN3) ent->msg_origins[0][2] = MSG_ReadCoord (); else ent->msg_origins[0][2] = ent->baseline.origin[2]; if (bits & U_ANGLE3) ent->msg_angles[0][2] = MSG_ReadAngle(); else ent->msg_angles[0][2] = ent->baseline.angles[2]; if (bits & U_ALPHA) ent->alpha = (float)(MSG_ReadByte()) / 255.0f; if (bits & U_RENDERAMT) ent->alpha = (float)(MSG_ReadByte()) / 255.0f; if ( bits & U_NOLERP ) ent->forcelink = true; if ( forcelink ) { // didn't have an update last message VectorCopy (ent->msg_origins[0], ent->msg_origins[1]); VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->msg_angles[1]); VectorCopy (ent->msg_angles[0], ent->angles); ent->forcelink = true; } } /* ================== CL_ParseBaseline ================== */ void CL_ParseBaseline (entity_t *ent) { int i; ent->baseline.modelindex = MSG_ReadByte (); ent->baseline.frame = MSG_ReadByte (); ent->baseline.colormap = MSG_ReadByte(); ent->baseline.skin = MSG_ReadByte(); for (i=0 ; i<3 ; i++) { ent->baseline.origin[i] = MSG_ReadCoord (); ent->baseline.angles[i] = MSG_ReadAngle (); } } /* ================== CL_ParseClientdata Server information pertaining to this client only ================== */ void CL_ParseClientdata (int bits) { int i, j; if (bits & SU_VIEWHEIGHT) cl.viewheight = MSG_ReadChar (); else cl.viewheight = DEFAULT_VIEWHEIGHT; if (bits & SU_IDEALPITCH) cl.idealpitch = MSG_ReadChar (); else cl.idealpitch = 0; VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); for (i=0 ; i<3 ; i++) { if (bits & (SU_PUNCH1< cl.maxclients) Sys_Error ("CL_NewTranslation: slot > cl.maxclients"); dest = cl.scores[slot].translations; source = vid.colormap; memcpy (dest, vid.colormap, sizeof(cl.scores[slot].translations)); top = cl.scores[slot].colors & 0xf0; bottom = (cl.scores[slot].colors &15)<<4; #ifdef GLQUAKE R_TranslatePlayerSkin (slot); #endif for (i=0 ; imodel = cl.model_precache[ent->baseline.modelindex]; ent->frame = ent->baseline.frame; ent->colormap = vid.colormap; ent->skinnum = ent->baseline.skin; ent->effects = ent->baseline.effects; VectorCopy (ent->baseline.origin, ent->origin); VectorCopy (ent->baseline.angles, ent->angles); R_AddEfrags (ent); } /* =================== CL_ParseStaticSound =================== */ void CL_ParseStaticSound (void) { vec3_t org; int sound_num, vol, atten; int i; for (i=0 ; i<3 ; i++) org[i] = MSG_ReadCoord (); sound_num = MSG_ReadByte (); vol = MSG_ReadByte (); atten = MSG_ReadByte (); S_StaticSound (cl.sound_precache[sound_num], org, vol, atten); } #define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x); /* ===================== CL_ParseServerMessage ===================== */ void CL_ParseServerMessage (void) { int cmd; int i; // // if recording demos, copy the message out // if (cl_shownet.value == 1) Con_Printf ("%i ",net_message.cursize); else if (cl_shownet.value == 2) Con_Printf ("------------------\n"); cl.onground = false; // unless the server says otherwise // // parse the message // MSG_BeginReading (); while (1) { if (msg_badread) Host_Error ("CL_ParseServerMessage: Bad server message"); cmd = MSG_ReadByte (); if (cmd == -1) { SHOWNET("END OF MESSAGE"); return; // end of message } // if the high bit of the command byte is set, it is a fast update if (cmd & 128) { SHOWNET("fast update"); CL_ParseUpdate (cmd&127); continue; } SHOWNET(svc_strings[cmd]); // other commands switch (cmd) { default: Host_Error ("CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: // Con_Printf ("svc_nop\n"); break; case svc_time: cl.mtime[1] = cl.mtime[0]; cl.mtime[0] = MSG_ReadFloat (); break; case svc_clientdata: i = MSG_ReadShort (); CL_ParseClientdata (i); break; case svc_version: i = MSG_ReadLong (); if (i != PROTOCOL_NETQUAKE) Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_NETQUAKE); break; case svc_disconnect: Host_EndGame ("Server disconnected\n"); case svc_print: Con_Printf ("%s", MSG_ReadString ()); break; case svc_centerprint: SCR_CenterPrint (MSG_ReadString ()); break; case svc_stufftext: Cbuf_AddText (MSG_ReadString ()); break; case svc_damage: V_ParseDamage (); break; case svc_serverinfo: CL_ParseServerInfo (); vid.recalc_refdef = true; // leave intermission full screen break; case svc_setangle: for (i=0 ; i<3 ; i++) cl.viewangles[i] = MSG_ReadAngle (); break; case svc_setview: cl.viewentity = MSG_ReadShort (); break; case svc_lightstyle: i = MSG_ReadByte (); if (i >= MAX_LIGHTSTYLES) Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES"); strcpy (cl_lightstyle[i].map, MSG_ReadString()); cl_lightstyle[i].length = strlen(cl_lightstyle[i].map); break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_stopsound: i = MSG_ReadShort(); S_StopSound(i>>3, i&7); break; case svc_updatename: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatename > MAX_SCOREBOARD"); strcpy (cl.scores[i].name, MSG_ReadString ()); break; case svc_updatefrags: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD"); cl.scores[i].frags = MSG_ReadShort (); break; case svc_updatecolors: Sbar_Changed (); i = MSG_ReadByte (); if (i >= cl.maxclients) Host_Error ("CL_ParseServerMessage: svc_updatecolors > MAX_SCOREBOARD"); cl.scores[i].colors = MSG_ReadByte (); CL_NewTranslation (i); break; case svc_particle: R_ParseParticleEffect (); break; case svc_spawnbaseline: i = MSG_ReadShort (); // must use CL_EntityNum() to force cl.num_entities up CL_ParseBaseline (CL_EntityNum(i)); break; case svc_spawnstatic: CL_ParseStatic (); break; case svc_temp_entity: CL_ParseTEnt (); break; case svc_setpause: { cl.paused = MSG_ReadByte (); if (cl.paused) { CDAudio_Pause (); #ifdef _WIN32 VID_HandlePause (true); #endif } else { CDAudio_Resume (); #ifdef _WIN32 VID_HandlePause (false); #endif } } break; case svc_signonnum: i = MSG_ReadByte (); if (i <= cls.signon) Host_Error ("Received signon %i when at %i", i, cls.signon); cls.signon = i; CL_SignonReply (); break; case svc_killedmonster: cl.stats[STAT_MONSTERS]++; break; case svc_foundsecret: cl.stats[STAT_SECRETS]++; break; case svc_updatestat: i = MSG_ReadByte (); if (i < 0 || i >= MAX_CL_STATS) Sys_Error ("svc_updatestat: %i is invalid", i); cl.stats[i] = MSG_ReadLong ();; break; case svc_spawnstaticsound: CL_ParseStaticSound (); break; case svc_cdtrack: cl.cdtrack = MSG_ReadByte (); cl.looptrack = MSG_ReadByte (); if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) ) CDAudio_Play ((byte)cls.forcetrack, true); else CDAudio_Play ((byte)cl.cdtrack, true); break; case svc_intermission: cl.intermission = 1; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen break; case svc_finale: cl.intermission = 2; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (MSG_ReadString ()); break; case svc_cutscene: cl.intermission = 3; cl.completed_time = cl.time; vid.recalc_refdef = true; // go to full screen SCR_CenterPrint (MSG_ReadString ()); break; case svc_sellscreen: Cmd_ExecuteString ("help", src_command); break; } } } ================================================ FILE: source/cl_tent.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_tent.c -- client side temporary entities #include "quakedef.h" int num_temp_entities; entity_t* cl_temp_entities; beam_t cl_beams[MAX_BEAMS]; sfx_t *cl_sfx_wizhit; sfx_t *cl_sfx_knighthit; sfx_t *cl_sfx_tink1; sfx_t *cl_sfx_ric1; sfx_t *cl_sfx_ric2; sfx_t *cl_sfx_ric3; sfx_t *cl_sfx_r_exp3; /* ================= CL_ParseTEnt ================= */ void CL_InitTEnts (void) { cl_sfx_wizhit = S_PrecacheSound ("wizard/hit.wav"); cl_sfx_knighthit = S_PrecacheSound ("hknight/hit.wav"); cl_sfx_tink1 = S_PrecacheSound ("weapons/tink1.wav"); cl_sfx_ric1 = S_PrecacheSound ("weapons/ric1.wav"); cl_sfx_ric2 = S_PrecacheSound ("weapons/ric2.wav"); cl_sfx_ric3 = S_PrecacheSound ("weapons/ric3.wav"); cl_sfx_r_exp3 = S_PrecacheSound ("weapons/r_exp3.wav"); } /* ================= CL_ParseBeam ================= */ void CL_ParseBeam (model_t *m) { int ent; vec3_t start, end; beam_t *b; int i; ent = MSG_ReadShort (); start[0] = MSG_ReadCoord (); start[1] = MSG_ReadCoord (); start[2] = MSG_ReadCoord (); end[0] = MSG_ReadCoord (); end[1] = MSG_ReadCoord (); end[2] = MSG_ReadCoord (); // override any beam with the same entity for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) if (b->entity == ent) { b->entity = ent; b->model = m; b->endtime = cl.time + 0.2; VectorCopy (start, b->start); VectorCopy (end, b->end); return; } // find a free beam for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) { if (!b->model || b->endtime < cl.time) { b->entity = ent; b->model = m; b->endtime = cl.time + 0.2; VectorCopy (start, b->start); VectorCopy (end, b->end); return; } } Con_Printf ("beam list overflow!\n"); } /* ================= CL_ParseTEnt ================= */ void CL_ParseTEnt (void) { int type; vec3_t pos; #ifdef QUAKE2 vec3_t endpos; #endif dlight_t *dl; int rnd; int colorStart, colorLength; type = MSG_ReadByte (); switch (type) { case TE_WIZSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 20, 30); S_StartSound (-1, 0, cl_sfx_wizhit, pos, 1, 1); break; case TE_KNIGHTSPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 226, 20); S_StartSound (-1, 0, cl_sfx_knighthit, pos, 1, 1); break; case TE_SPIKE: // spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); #ifdef GLTEST Test_Spawn (pos); #else R_RunParticleEffect (pos, vec3_origin, 0, 10); #endif if ( rand() % 5 ) S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_SUPERSPIKE: // super spike hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20); if ( rand() % 5 ) S_StartSound (-1, 0, cl_sfx_tink1, pos, 1, 1); else { rnd = rand() & 3; if (rnd == 1) S_StartSound (-1, 0, cl_sfx_ric1, pos, 1, 1); else if (rnd == 2) S_StartSound (-1, 0, cl_sfx_ric2, pos, 1, 1); else S_StartSound (-1, 0, cl_sfx_ric3, pos, 1, 1); } break; case TE_GUNSHOT: // bullet hitting wall pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_RunParticleEffect (pos, vec3_origin, 0, 20); break; case TE_EXPLOSION: // rocket explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_ParticleExplosion (pos); dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_TAREXPLOSION: // tarbaby explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_BlobExplosion (pos); S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; case TE_LIGHTNING1: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt.mdl", true)); break; case TE_LIGHTNING2: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt2.mdl", true)); break; case TE_LIGHTNING3: // lightning bolts CL_ParseBeam (Mod_ForName("progs/bolt3.mdl", true)); break; // PGM 01/21/97 case TE_BEAM: // grappling hook beam CL_ParseBeam (Mod_ForName("progs/beam.mdl", true)); break; // PGM 01/21/97 case TE_LAVASPLASH: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_LavaSplash (pos); break; case TE_TELEPORT: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); R_TeleportSplash (pos); break; case TE_EXPLOSION2: // color mapped explosion pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); colorStart = MSG_ReadByte (); colorLength = MSG_ReadByte (); R_ParticleExplosion2 (pos, colorStart, colorLength); dl = CL_AllocDlight (0); VectorCopy (pos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 1); break; #ifdef QUAKE2 case TE_IMPLOSION: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); S_StartSound (-1, 0, cl_sfx_imp, pos, 1, 1); break; case TE_RAILTRAIL: pos[0] = MSG_ReadCoord (); pos[1] = MSG_ReadCoord (); pos[2] = MSG_ReadCoord (); endpos[0] = MSG_ReadCoord (); endpos[1] = MSG_ReadCoord (); endpos[2] = MSG_ReadCoord (); S_StartSound (-1, 0, cl_sfx_rail, pos, 1, 1); S_StartSound (-1, 1, cl_sfx_r_exp3, endpos, 1, 1); R_RocketTrail (pos, endpos, 0+128); R_ParticleExplosion (endpos); dl = CL_AllocDlight (-1); VectorCopy (endpos, dl->origin); dl->radius = 350; dl->die = cl.time + 0.5; dl->decay = 300; break; #endif default: Sys_Error ("CL_ParseTEnt: bad type"); } } /* ================= CL_NewTempEntity ================= */ entity_t *CL_NewTempEntity (void) { entity_t *ent; if (cl_numvisedicts == MAX_VISEDICTS) return NULL; if (num_temp_entities == MAX_TEMP_ENTITIES) return NULL; ent = &cl_temp_entities[num_temp_entities]; memset (ent, 0, sizeof(*ent)); num_temp_entities++; cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; ent->colormap = vid.colormap; return ent; } /* ================= CL_UpdateTEnts ================= */ void CL_UpdateTEnts (void) { int i; beam_t *b; vec3_t dist, org; float d; entity_t *ent; float yaw, pitch; float forward; num_temp_entities = 0; // update lightning for (i=0, b=cl_beams ; i< MAX_BEAMS ; i++, b++) { if (!b->model || b->endtime < cl.time) continue; // if coming from the player, update the start position if (b->entity == cl.viewentity) { VectorCopy (cl_entities[cl.viewentity].origin, b->start); } // calculate pitch and yaw VectorSubtract (b->end, b->start, dist); if (dist[1] == 0 && dist[0] == 0) { yaw = 0; if (dist[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int) (atan2(dist[1], dist[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt (dist[0]*dist[0] + dist[1]*dist[1]); pitch = (int) (atan2(dist[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } // add new entities for the lightning VectorCopy (b->start, org); d = VectorNormalize(dist); while (d > 0) { ent = CL_NewTempEntity (); if (!ent) return; VectorCopy (org, ent->origin); ent->model = b->model; ent->angles[0] = pitch; ent->angles[1] = yaw; ent->angles[2] = rand()%360; for (i=0 ; i<3 ; i++) org[i] += dist[i]*30; d -= 30; } } } ================================================ FILE: source/client.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // client.h typedef struct { vec3_t viewangles; // intended velocities float forwardmove; float sidemove; float upmove; #ifdef QUAKE2 byte lightlevel; #endif } usercmd_t; typedef struct { int length; char map[MAX_STYLESTRING]; } lightstyle_t; typedef struct { char name[MAX_SCOREBOARDNAME]; float entertime; int frags; int colors; // two 4 bit fields byte translations[VID_GRADES*256]; } scoreboard_t; typedef struct { int destcolor[3]; int percent; // 0-256 } cshift_t; #define CSHIFT_CONTENTS 0 #define CSHIFT_DAMAGE 1 #define CSHIFT_BONUS 2 #define CSHIFT_POWERUP 3 #define NUM_CSHIFTS 4 #define NAME_LENGTH 64 // // client_state_t should hold all pieces of the client state // #define SIGNONS 4 // signon messages to receive before connected #define MAX_DLIGHTS 128 typedef struct { vec3_t origin; float radius; float die; // stop lighting after this time float decay; // drop this each second float minlight; // don't add when contributing less int key; #ifdef QUAKE2 bool dark; // subtracts light instead of adding #endif vec3_t color; //LordHavoc Lit. Support float alpha; } dlight_t; #define MAX_BEAMS 24 typedef struct { int entity; struct model_s *model; float endtime; vec3_t start, end; } beam_t; #define MAX_EFRAGS 640 #define MAX_MAPSTRING 2048 #define MAX_DEMOS 8 #define MAX_DEMONAME 16 typedef enum { ca_dedicated, // a dedicated server with no ability to start a client ca_disconnected, // full screen console with no connection ca_connected // valid netcon, talking to a server } cactive_t; typedef struct { bool web; char *name; double percent; bool disconnect; // set when user tries to disconnect, to allow cleaning up webdownload } download_t; // // the client_static_t structure is persistant through an arbitrary number // of server connections // typedef struct { cactive_t state; // personalization data sent to server char mapstring[MAX_QPATH]; char spawnparms[MAX_MAPSTRING]; // to restart a level // demo loop control int demonum; // -1 = don't play demos char demos[MAX_DEMOS][MAX_DEMONAME]; // when not playing // demo recording info must be here, because record is started before // entering a map (and clearing client_state_t) bool demorecording; bool demoplayback; bool timedemo; int forcetrack; // -1 = use normal cd track FILE *demofile; int td_lastframe; // to meter out one message a frame int td_startframe; // host_framecount at start float td_starttime; // realtime at second frame of timedemo // connection information int signon; // 0 to SIGNONS struct qsocket_s *netcon; sizebuf_t message; // writing buffer to send to server download_t download; } client_static_t; extern client_static_t cls; // // the client_state_t structure is wiped completely at every // server signon // typedef struct { int movemessages; // since connecting to this server // throw out the first couple, so the player // doesn't accidentally do something the // first frame usercmd_t cmd; // last command sent to the server // information for local display int stats[MAX_CL_STATS]; // health, etc int items; // inventory bit flags float item_gettime[32]; // cl.time of aquiring item, for blinking float faceanimtime; // use anim frame if cl.time < this cshift_t cshifts[NUM_CSHIFTS]; // color shifts for damage, powerups cshift_t prev_cshifts[NUM_CSHIFTS]; // and content types // the client maintains its own idea of view angles, which are // sent to the server each frame. The server sets punchangle when // the view is temporarliy offset, and an angle reset commands at the start // of each level and after teleporting. vec3_t mviewangles[2]; // during demo playback viewangles is lerped // between these vec3_t viewangles; vec3_t mvelocity[2]; // update by server, used for lean+bob // (0 is newest) vec3_t velocity; // lerped between mvelocity[0] and [1] vec3_t punchangle; // temporary offset // pitch drifting vars float idealpitch; float pitchvel; bool nodrift; float driftmove; double laststop; float viewheight; float crouch; // local amount for smoothing stepups bool paused; // send over by server bool onground; bool inwater; int intermission; // don't change view angle, full screen, etc int completed_time; // latched at intermission start double mtime[2]; // the timestamp of last two messages double time; // clients view of time, should be between // servertime and oldservertime to generate // a lerp point for other data double oldtime; // previous cl.time, time-oldtime is used // to decay light values and smooth step ups float last_received_message; // (realtime) for net trouble icon // // information that is static for the entire time connected to a server // struct model_s *model_precache[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; char levelname[40]; // for display on solo scoreboard int viewentity; // cl_entitites[cl.viewentity] = player int maxclients; int gametype; // refresh related state struct model_s *worldmodel; // cl_entitites[0].model struct efrag_s *free_efrags; int num_entities; // held in cl_entities array entity_t viewent; // the gun model int cdtrack, looptrack; // cd audio // frag scoreboard scoreboard_t *scores; // [cl.maxclients] } client_state_t; // // cvars // extern cvar_t cl_name; extern cvar_t cl_color; extern cvar_t cl_upspeed; extern cvar_t cl_forwardspeed; extern cvar_t cl_backspeed; extern cvar_t cl_sidespeed; extern cvar_t cl_movespeedkey; extern cvar_t cl_yawspeed; extern cvar_t cl_pitchspeed; extern cvar_t cl_anglespeedkey; extern cvar_t cl_autofire; extern cvar_t cl_shownet; extern cvar_t cl_nolerp; extern cvar_t cl_pitchdriftspeed; extern cvar_t lookspring; extern cvar_t lookstrafe; extern cvar_t sensitivity; extern cvar_t m_pitch; extern cvar_t m_yaw; extern cvar_t m_forward; extern cvar_t m_side; #define MAX_TEMP_ENTITIES 64 // lightning bolts, etc extern client_state_t cl; // FIXME, allocate dynamically extern efrag_t* cl_efrags; extern entity_t* cl_entities; extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; extern dlight_t cl_dlights[MAX_DLIGHTS]; extern entity_t* cl_temp_entities; extern beam_t cl_beams[MAX_BEAMS]; //============================================================================= // // cl_main // dlight_t *CL_AllocDlight (int key); void CL_DecayLights (void); void CL_Init (void); void CL_EstablishConnection (char *host); void CL_Signon1 (void); void CL_Signon2 (void); void CL_Signon3 (void); void CL_Signon4 (void); void CL_Disconnect (void); void CL_Disconnect_f (void); void CL_NextDemo (void); #define MAX_VISEDICTS 256 extern int cl_numvisedicts; extern entity_t *cl_visedicts[MAX_VISEDICTS]; // // cl_input // typedef struct { int down[2]; // key nums holding it down int state; // low bit is down state } kbutton_t; extern kbutton_t in_mlook, in_klook; extern kbutton_t in_strafe; extern kbutton_t in_speed; void CL_InitInput (void); void CL_SendCmd (void); void CL_SendMove (usercmd_t *cmd); void CL_ParseTEnt (void); void CL_UpdateTEnts (void); void CL_ClearState (void); int CL_ReadFromServer (void); void CL_WriteToServer (usercmd_t *cmd); void CL_BaseMove (usercmd_t *cmd); float CL_KeyState (kbutton_t *key); char *Key_KeynumToString (int keynum); // // cl_demo.c // void CL_StopPlayback (void); int CL_GetMessage (void); void CL_Stop_f (void); void CL_Record_f (void); void CL_PlayDemo_f (void); void CL_TimeDemo_f (void); void CL_Benchmark_f (void); // // cl_parse.c // void CL_ParseServerMessage (void); void CL_NewTranslation (int slot); // // view // void V_StartPitchDrift (void); void V_StopPitchDrift (void); void V_RenderView (void); void V_UpdatePalette (void); void V_Register (void); void V_ParseDamage (void); void V_SetContentsColor (int contents); // // cl_tent // void CL_InitTEnts (void); void CL_SignonReply (void); ================================================ FILE: source/cmd.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cmd.c -- Quake script command processing module #include "quakedef.h" void Cmd_ForwardToServer (void); extern byte *COM_LoadFile (const char *path, int usehunk); #define MAX_ALIAS_NAME 32 typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; } cmdalias_t; cmdalias_t *cmd_alias; int trashtest; int *trashspot; bool cmd_wait; //============================================================================= /* ============ Cmd_Wait_f Causes execution of the remainder of the command buffer to be delayed until next frame. This allows commands like: bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2" ============ */ void Cmd_Wait_f (void) { cmd_wait = true; } /* ============================================================================= COMMAND BUFFER ============================================================================= */ sizebuf_t cmd_text; /* ============ Cbuf_Init ============ */ void Cbuf_Init (void) { SZ_Alloc (&cmd_text, 8192); // space for commands and script files } /* ============ Cbuf_AddText Adds command text at the end of the buffer ============ */ void Cbuf_AddText (const char *text) { int l; l = strlen (text); if (cmd_text.cursize + l >= cmd_text.maxsize) { Con_Printf ("Cbuf_AddText: overflow\n"); return; } SZ_Write (&cmd_text, text, strlen (text)); } /* ============ Cbuf_InsertText Adds command text immediately after the current command Adds a \n to the text FIXME: actually change the command buffer to do less copying ============ */ void Cbuf_InsertText(char *text) { char *temp = NULL; int templen = cmd_text.cursize; // copy off any commands still remaining in the exec buffer if (templen) { temp = Z_Malloc(templen); memcpy(temp, cmd_text.data, templen); SZ_Clear(&cmd_text); } // add the entire text of the file Cbuf_AddText(text); // add the copied off data if (templen) { SZ_Write(&cmd_text, temp, templen); Z_Free(temp); } } /* ============ Cbuf_Execute ============ */ void Cbuf_Execute(void) { int i; char *text; char* line = Sys_BigStackAlloc(1024, "Cbuf_Execute"); int quotes; int notcmd; // JPG - so that the ENTIRE line can be forwarded while (cmd_text.cursize) { // find a \n or ; line break text = (char *)cmd_text.data; quotes = 0; notcmd = strncmp(text, "cmd ", 4); // JPG - so that the ENTIRE line can be forwarded for (i = 0; i < cmd_text.cursize; i++) { if (text[i] == '"') quotes++; if (!(quotes & 1) && text[i] == ';' && notcmd) // JPG - added && cmd so that the ENTIRE line can be forwareded break; // don't break if inside a quoted string if (text[i] == '\n') break; } memmove(line, text, i); line[i] = 0; // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec, alias) can insert data at the // beginning of the text buffer if (i == cmd_text.cursize) cmd_text.cursize = 0; else { i++; cmd_text.cursize -= i; memmove(text, text + i, cmd_text.cursize); } // execute the command line Cmd_ExecuteString(line, src_command); if (cmd_wait) { // skip out while text still remains in buffer, leaving it for next frame cmd_wait = false; break; } } Sys_BigStackFree(1024, "Cbuf_Execute"); } /* ============================================================================== SCRIPT COMMANDS ============================================================================== */ /* =============== Cmd_StuffCmds_f Adds command line parameters as script statements Commands lead with a +, and continue until a - or another + quake +prog jctest.qp +cmd amlev1 quake -nosound +cmd amlev1 =============== */ void Cmd_StuffCmds_f (void) { int i, j; int s; char *text, *build, c; if (Cmd_Argc () != 1) { Con_Printf ("stuffcmds : execute command line parameters\n"); return; } // build the combined string to parse from s = 0; for (i=1 ; i : execute a script file\n"); return; } mark = Hunk_LowMark (); f = (char *)COM_LoadFile (Cmd_Argv(1),0); if (!f) { Con_Printf ("couldn't exec %s\n",Cmd_Argv(1)); return; } Con_Printf ("execing %s\n",Cmd_Argv(1)); Cbuf_InsertText (f); Hunk_FreeToLowMark (mark); } /* =============== Cmd_Echo_f Just prints the rest of the line to the console =============== */ void Cmd_Echo_f (void) { int i; for (i=1 ; inext) Con_Printf ("%s : %s\n", a->name, a->value); return; } s = Cmd_Argv(1); if (strlen(s) >= MAX_ALIAS_NAME) { Con_Printf ("Alias name is too long\n"); return; } // if the alias already exists, reuse it for (a = cmd_alias ; a ; a=a->next) { if (!strcmp(s, a->name)) { Z_Free (a->value); break; } } if (!a) { a = Z_Malloc (sizeof(cmdalias_t)); a->next = cmd_alias; cmd_alias = a; } strcpy (a->name, s); cmd = Sys_BigStackAlloc(1024, "Cmd_Alias_f"); // copy the rest of the command line cmd[0] = 0; // start out with a null string c = Cmd_Argc(); for (i=2 ; i< c ; i++) { strcat (cmd, Cmd_Argv(i)); if (i != c) strcat (cmd, " "); } strcat (cmd, "\n"); a->value = CopyString (cmd); Sys_BigStackFree(1024, "Cmd_Alias_f"); } /* ============================================================================= COMMAND EXECUTION ============================================================================= */ typedef struct cmd_function_s { struct cmd_function_s *next; char *name; xcommand_t function; } cmd_function_t; #define MAX_ARGS 80 static int cmd_argc; static char *cmd_argv[MAX_ARGS]; static char *cmd_null_string = ""; static char *cmd_args = NULL; cmd_source_t cmd_source; static cmd_function_t *cmd_functions; // possible commands to execute /* ============ Cmd_Init ============ */ void Cmd_Init (void) { // // register our commands // Cmd_AddCommand ("stuffcmds",Cmd_StuffCmds_f); Cmd_AddCommand ("exec",Cmd_Exec_f); Cmd_AddCommand ("echo",Cmd_Echo_f); Cmd_AddCommand ("alias",Cmd_Alias_f); Cmd_AddCommand ("cmd", Cmd_ForwardToServer); Cmd_AddCommand ("wait", Cmd_Wait_f); Cmd_AddCommand ("set", Cvar_Set_f); Cmd_AddCommand ("seta", Cvar_Seta_f); } /* ============ Cmd_Argc ============ */ int Cmd_Argc (void) { return cmd_argc; } /* ============ Cmd_Argv ============ */ char *Cmd_Argv (int arg) { if ( (unsigned)arg >= cmd_argc ) return cmd_null_string; return cmd_argv[arg]; } /* ============ Cmd_Args ============ */ char *Cmd_Args (void) { return cmd_args; } /* ============ Cmd_TokenizeString Parses the given string into command line tokens. ============ */ void Cmd_TokenizeString (const char *text) { int i; // clear the args from the last string for (i=0 ; inext) { if (!strcmp (cmd_name, cmd->name)) { Con_Printf ("Cmd_AddCommand: %s already defined\n", cmd_name); return; } } cmd = Hunk_Alloc (sizeof(cmd_function_t)); cmd->name = (char*)cmd_name; cmd->function = function; cmd->next = cmd_functions; cmd_functions = cmd; } /* ============ Cmd_Exists ============ */ bool Cmd_Exists (const char *cmd_name) { cmd_function_t *cmd; for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!strcmp (cmd_name,cmd->name)) return true; } return false; } /* ============ Cmd_CompleteCommand ============ */ char *Cmd_CompleteCommand (char *partial) { cmd_function_t *cmd; int len; len = strlen(partial); if (!len) return NULL; // check functions for (cmd=cmd_functions ; cmd ; cmd=cmd->next) if (!strncmp (partial,cmd->name, len)) return cmd->name; return NULL; } /* ============ Cmd_ExecuteString A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ void Cmd_ExecuteString (char *text, cmd_source_t src) { cmd_function_t *cmd; cmdalias_t *a; cmd_source = src; Cmd_TokenizeString (text); // execute the command line if (!Cmd_Argc()) return; // no tokens // check functions for (cmd=cmd_functions ; cmd ; cmd=cmd->next) { if (!strcasecmp (cmd_argv[0],cmd->name)) { cmd->function (); return; } } // check alias for (a=cmd_alias ; a ; a=a->next) { if (!strcasecmp (cmd_argv[0], a->name)) { Cbuf_InsertText (a->value); return; } } // check cvars if (!Cvar_Command ()) Con_Printf ("Unknown command \"%s\"\n", Cmd_Argv(0)); } /* =================== Cmd_ForwardToServer Sends the entire command line over to the server =================== */ void Cmd_ForwardToServer (void) { if (cls.state != ca_connected) { Con_Printf ("You have to be connected to be able to use %s.\n", Cmd_Argv(0)); return; } if (cls.demoplayback) return; // not really connected MSG_WriteByte (&cls.message, clc_stringcmd); if (strcasecmp(Cmd_Argv(0), "cmd") != 0) { SZ_Print (&cls.message, Cmd_Argv(0)); SZ_Print (&cls.message, " "); } SZ_Print (&cls.message, Cmd_Argc() > 1 ? Cmd_Args() : "\n"); } /* ================ Cmd_CheckParm Returns the position (1 to argc-1) in the command's argument list where the given parameter apears, or 0 if not present ================ */ int Cmd_CheckParm (char *parm) { int i; if (!parm) Sys_Error ("Cmd_CheckParm: NULL"); for (i = 1; i < Cmd_Argc (); i++) if (! strcasecmp (parm, Cmd_Argv (i))) return i; return 0; } ================================================ FILE: source/cmd.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cmd.h -- Command buffer and command execution //=========================================================================== /* Any number of commands can be added in a frame, from several different sources. Most commands come from either keybindings or console line input, but remote servers can also send across commands and entire text files can be execed. The + command line options are also added to the command buffer. The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute (); */ void Cbuf_Init (void); // allocates an initial text buffer that will grow as needed void Cbuf_AddText (const char *text); // as new commands are generated from the console or keybindings, // the text is added to the end of the command buffer. void Cbuf_InsertText (char *text); // when a command wants to issue other commands immediately, the text is // inserted at the beginning of the buffer, before any remaining unexecuted // commands. void Cbuf_Execute (void); // Pulls off \n terminated lines of text from the command buffer and sends // them through Cmd_ExecuteString. Stops when the buffer is empty. // Normally called once per frame, but may be explicitly invoked. // Do not call inside a command function! //=========================================================================== /* Command execution takes a null terminated string, breaks it into tokens, then searches for a command or variable that matches the first token. Commands can come from three sources, but the handler functions may choose to dissallow the action or forward it to a remote server if the source is not apropriate. */ typedef void (*xcommand_t) (void); typedef enum { src_client, // came in over a net connection as a clc_stringcmd // host_client will be valid during this state. src_command // from the command buffer } cmd_source_t; extern cmd_source_t cmd_source; void Cmd_Init (void); void Cmd_AddCommand (const char *cmd_name, xcommand_t function); // called by the init functions of other parts of the program to // register commands and functions to call for them. // The cmd_name is referenced later, so it should not be in temp memory bool Cmd_Exists (const char *cmd_name); // used by the cvar code to check for cvar / command name overlap char *Cmd_CompleteCommand (char *partial); // attempts to match a partial command for automatic command line completion // returns NULL if nothing fits int Cmd_Argc (void); char *Cmd_Argv (int arg); char *Cmd_Args (void); // The functions that execute commands get their parameters with these // functions. Cmd_Argv () will return an empty string, not a NULL // if arg > argc, so string operations are always safe. int Cmd_CheckParm (char *parm); // Returns the position (1 to argc-1) in the command's argument list // where the given parameter apears, or 0 if not present void Cmd_TokenizeString (const char *text); // Takes a null terminated string. Does not need to be /n terminated. // breaks the string up into arg tokens. void Cmd_ExecuteString (char *text, cmd_source_t src); // Parses a single line of text into arguments and tries to execute it. // The text can come from the command buffer, a remote client, or stdin. void Cmd_ForwardToServer (void); // adds the current command line as a clc_stringcmd to the client message. // things like godmode, noclip, etc, are commands directed to the server, // so when they are typed in at the console, they will need to be forwarded. void Cmd_Print (char *text); // used by command functions to send output to either the graphics console or // passed as a print message to the client ================================================ FILE: source/common.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // common.c -- misc functions used in client and server #define _GNU_SOURCE #include #include "quakedef.h" #include #include #include CVAR (registered, 0, CVAR_ROM) CVAR (platform, 0, CVAR_ROM) CVAR (cmdline, 0, CVAR_SERVERINFO) #define NUM_SAFE_ARGVS 7 static char *largv[MAX_NUM_ARGVS + NUM_SAFE_ARGVS + 1]; static char *argvdummy = " "; static char *safeargvs[NUM_SAFE_ARGVS] = {"-stdvid", "-nolan", "-nosound", "-nocdaudio", "-nojoy", "-nomouse", "-dibonly"}; bool com_modified; // set true if using non-id files bool proghack; int com_nummissionpacks; //johnfitz int static_registered = 1; // only for startup check, then set bool msg_suppress_1 = 0; void COM_InitFilesystem (void); // if a packfile directory differs from this, it is assumed to be hacked #define PAK0_COUNT 339 #define PAK0_CRC 32981 char com_token[1024]; int com_argc; char **com_argv; #define CMDLINE_LENGTH 256 char com_cmdline[CMDLINE_LENGTH]; bool standard_quake = true, rogue, hipnotic; // this graphic needs to be in the pak file to use registered features unsigned short pop[] = { 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 ,0x0000,0x0000,0x6600,0x0000,0x0000,0x0000,0x6600,0x0000 ,0x0000,0x0066,0x0000,0x0000,0x0000,0x0000,0x0067,0x0000 ,0x0000,0x6665,0x0000,0x0000,0x0000,0x0000,0x0065,0x6600 ,0x0063,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6563 ,0x0064,0x6561,0x0000,0x0000,0x0000,0x0000,0x0061,0x6564 ,0x0064,0x6564,0x0000,0x6469,0x6969,0x6400,0x0064,0x6564 ,0x0063,0x6568,0x6200,0x0064,0x6864,0x0000,0x6268,0x6563 ,0x0000,0x6567,0x6963,0x0064,0x6764,0x0063,0x6967,0x6500 ,0x0000,0x6266,0x6769,0x6a68,0x6768,0x6a69,0x6766,0x6200 ,0x0000,0x0062,0x6566,0x6666,0x6666,0x6666,0x6562,0x0000 ,0x0000,0x0000,0x0062,0x6364,0x6664,0x6362,0x0000,0x0000 ,0x0000,0x0000,0x0000,0x0062,0x6662,0x0000,0x0000,0x0000 ,0x0000,0x0000,0x0000,0x0061,0x6661,0x0000,0x0000,0x0000 ,0x0000,0x0000,0x0000,0x0000,0x6500,0x0000,0x0000,0x0000 ,0x0000,0x0000,0x0000,0x0000,0x6400,0x0000,0x0000,0x0000 }; /* All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. The "base directory" is the path to the directory holding the quake.exe and all game directories. The sys_* files pass this to host_init in quakeparms_t->basedir. This can be overridden with the "-basedir" command line parm to allow code debugging in a different directory. The base directory is only used during filesystem initialization. The "game directory" is the first tree on the search path and directory that all generated files (savegames, screenshots, demos, config files) will be saved to. This can be overridden with the "-game" command line parameter. The game directory can never be changed while quake is executing. This is a precacution against having a malicious server instruct clients to write files over areas they shouldn't. The "cache directory" is only used during development to save network bandwidth, especially over ISDN / T1 lines. If there is a cache directory specified, when a file is found by the normal search path, it will be mirrored into the cache directory, then opened there. FIXME: The file "parms.txt" will be read out of the game directory and appended to the current command line arguments to allow different games to initialize startup parms differently. This could be used to add a "-sspeed 22050" for the high quality sound edition. Because they are added at the end, they will not override an explicit setting on the original command line. */ //============================================================================ // ClearLink is used for new headnodes void ClearLink (link_t *l) { l->prev = l->next = l; } void RemoveLink (link_t *l) { l->next->prev = l->prev; l->prev->next = l->next; } void InsertLinkBefore (link_t *l, link_t *before) { l->next = before; l->prev = before->prev; l->prev->next = l; l->next->prev = l; } void InsertLinkAfter (link_t *l, link_t *after) { l->next = after->next; l->prev = after; l->prev->next = l; l->next->prev = l; } /* ============================================================================ LIBRARY REPLACEMENT FUNCTIONS ============================================================================ */ void strncpyz (char *dest, const char *src, size_t size) { strncpy (dest, src, size - 1); dest[size-1] = 0; } /* ============================================================================ BYTE ORDER FUNCTIONS ============================================================================ */ bool bigendien; short (*BigShort) (short l); short (*LittleShort) (short l); int (*BigLong) (int l); int (*LittleLong) (int l); float (*BigFloat) (float l); float (*LittleFloat) (float l); short ShortSwap(short l) { byte b1, b2; b1 = l & 255; b2 = (l >> 8) & 255; return (b1 << 8) + b2; } short ShortNoSwap(short l) { return l; } int LongSwap(int l) { byte b1, b2, b3, b4; b1 = l & 255; b2 = (l >> 8) & 255; b3 = (l >> 16) & 255; b4 = (l >> 24) & 255; return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4; } int LongNoSwap(int l) { return l; } float FloatSwap(float f) { union { float f; byte b[4]; } dat1, dat2; dat1.f = f; dat2.b[0] = dat1.b[3]; dat2.b[1] = dat1.b[2]; dat2.b[2] = dat1.b[1]; dat2.b[3] = dat1.b[0]; return dat2.f; } float FloatNoSwap(float f) { return f; } /* ============================================================================== MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors ============================================================================== */ // // writing functions // void MSG_WriteChar (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < -128 || c > 127) Sys_Error ("MSG_WriteChar: range error"); #endif buf = SZ_GetSpace (sb, 1); buf[0] = c; } void MSG_WriteByte (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < 0 || c > 255) Sys_Error ("MSG_WriteByte: range error"); #endif buf = SZ_GetSpace (sb, 1); buf[0] = c; } void MSG_WriteShort (sizebuf_t *sb, int c) { byte *buf; #ifdef PARANOID if (c < ((short)0x8000) || c > (short)0x7fff) Sys_Error ("MSG_WriteShort: range error"); #endif buf = SZ_GetSpace (sb, 2); buf[0] = c&0xff; buf[1] = c>>8; } void MSG_WriteLong (sizebuf_t *sb, int c) { byte *buf; buf = SZ_GetSpace (sb, 4); buf[0] = c&0xff; buf[1] = (c>>8)&0xff; buf[2] = (c>>16)&0xff; buf[3] = c>>24; } void MSG_WriteFloat (sizebuf_t *sb, float f) { union { float f; int l; } dat; dat.f = f; dat.l = LittleLong (dat.l); SZ_Write (sb, &dat.l, 4); } void MSG_WriteString (sizebuf_t *sb, const char *s) { if (!s) SZ_Write (sb, "", 1); else SZ_Write (sb, s, strlen(s)+1); } void MSG_WriteCoord (sizebuf_t *sb, float f) { MSG_WriteShort (sb, (int)(f*8)); } void MSG_WriteAngle (sizebuf_t *sb, float f) { MSG_WriteByte (sb, ((int)f*256/360) & 255); } // JPG - precise aim for ProQuake! void MSG_WritePreciseAngle(sizebuf_t *sb, float f) { int val = (int) f * 65536 / 360; MSG_WriteShort(sb, val & 65535); } // // reading functions // int msg_readcount; bool msg_badread; void MSG_BeginReading (void) { msg_readcount = 0; msg_badread = false; } // returns -1 and sets msg_badread if no more characters are available int MSG_ReadChar (void) { int c; if (msg_readcount+1 > net_message.cursize) { msg_badread = true; return -1; } c = (signed char)net_message.data[msg_readcount]; msg_readcount++; return c; } int MSG_ReadByte (void) { int c; if (msg_readcount+1 > net_message.cursize) { msg_badread = true; return -1; } c = (unsigned char)net_message.data[msg_readcount]; msg_readcount++; return c; } int MSG_ReadShort (void) { int c; if (msg_readcount+2 > net_message.cursize) { msg_badread = true; return -1; } c = (short)(net_message.data[msg_readcount] + (net_message.data[msg_readcount+1]<<8)); msg_readcount += 2; return c; } int MSG_ReadLong (void) { int c; if (msg_readcount+4 > net_message.cursize) { msg_badread = true; return -1; } c = net_message.data[msg_readcount] + (net_message.data[msg_readcount+1]<<8) + (net_message.data[msg_readcount+2]<<16) + (net_message.data[msg_readcount+3]<<24); msg_readcount += 4; return c; } float MSG_ReadFloat (void) { union { byte b[4]; float f; int l; } dat; dat.b[0] = net_message.data[msg_readcount]; dat.b[1] = net_message.data[msg_readcount+1]; dat.b[2] = net_message.data[msg_readcount+2]; dat.b[3] = net_message.data[msg_readcount+3]; msg_readcount += 4; dat.l = LittleLong (dat.l); return dat.f; } char *MSG_ReadString (void) { static char string[2048]; int l,c; l = 0; do { c = MSG_ReadChar (); if (c == -1 || c == 0) break; string[l] = c; l++; } while (l < sizeof(string)-1); string[l] = 0; return string; } float MSG_ReadCoord (void) { return MSG_ReadShort() * (1.0/8); } float MSG_ReadAngle (void) { return MSG_ReadChar() * (360.0/256); } // JPG - exact aim for proquake! float MSG_ReadPreciseAngle(void) { return MSG_ReadShort() * (360.0 / 65536); } //=========================================================================== void SZ_Alloc (sizebuf_t *buf, int startsize) { if (startsize < 256) startsize = 256; buf->data = Hunk_AllocName (startsize, "sizebuf"); buf->maxsize = startsize; buf->cursize = 0; } void SZ_Free (sizebuf_t *buf) { // Z_Free (buf->data); // buf->data = NULL; // buf->maxsize = 0; buf->cursize = 0; } void SZ_Clear (sizebuf_t *buf) { buf->cursize = 0; } void *SZ_GetSpace (sizebuf_t *buf, int length) { void *data; if (buf->cursize + length > buf->maxsize) { if (!buf->allowoverflow) Sys_Error("%s: overflow without allowoverflow set (%d > %d)", __func__, buf->cursize + length, buf->maxsize); if (length > buf->maxsize) Sys_Error("%s: %d is > full buffer size", __func__, length); buf->overflowed = true; Con_Printf ("SZ_GetSpace: overflow"); SZ_Clear (buf); } data = buf->data + buf->cursize; buf->cursize += length; return data; } void SZ_Write (sizebuf_t *buf, const void *data, int length) { memcpy (SZ_GetSpace(buf,length),data,length); } void SZ_Print (sizebuf_t *buf, char *data) { int len; len = strlen(data)+1; // byte * cast to keep VC++ happy if (buf->data[buf->cursize-1]) memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 else memcpy ((byte *)SZ_GetSpace(buf, len-1)-1,data,len); // write over trailing 0 } //============================================================================ /* ============ COM_SkipPath ============ */ char *COM_SkipPath (char *pathname) { char *last; last = pathname; while (*pathname) { if (*pathname=='/') last = pathname+1; pathname++; } return last; } /* ============ COM_StripExtension ============ */ void COM_StripExtension(char *in, char *out) { char *dot; if (!(dot = strrchr(in, '.'))) { strlcpy(out, in, strlen(in) + 1); return; } while (*in && in != dot) *out++ = *in++; *out = 0; } /* ============ COM_FileExtension - Baker 3.76 - Stronger version from ezQuake 1.8 ============ */ char *COM_FileExtension(char *in) { static char exten[8]; int i; if (!(in = strrchr(in, '.'))) return ""; in++; for (i = 0; i<7 && *in; i++, in++) exten[i] = *in; exten[i] = 0; return exten; } /* ============ COM_FileBase ============ */ void COM_FileBase (const char *in, char *out) { char *s, *s2; s = (char*)in + strlen(in) - 1; while (s != in && *s != '.') s--; for (s2 = s ; *s2 && *s2 != '/' ; s2--) ; if (s-s2 < 2) strcpy (out,"?model?"); else { s--; strncpy (out,s2+1, s-s2); out[s-s2] = 0; } } /* ================== COM_ForceExtension If path doesn't have an extension or has a different extension, append(!) specified extension Extension should include the . ================== */ void COM_ForceExtension(char *path, char *extension) { char *src; src = path + strlen(path) - 1; while (*src != '/' && src != path) { if (*src-- == '.') { COM_StripExtension(path, path); strlcat(path, extension, sizeof(path)); return; } } strlcat(path, extension, MAX_OSPATH); } /* ================== COM_DefaultExtension ================== */ void COM_DefaultExtension (char *path, char *extension) { char *src; // // if path doesn't have a .EXT, append extension // (extension should include the .) // src = path + strlen(path) - 1; while (*src != '/' && src != path) { if (*src == '.') return; // it has an extension src--; } strcat (path, extension); } /* ============== COM_Parse Parse a token out of a string ============== */ char *COM_Parse (const char *data) { int c; int len; len = 0; com_token[0] = 0; if (!data) return NULL; // skip whitespace skipwhite: while ( (c = *data) <= ' ') { if (c == 0) return NULL; // end of file; data++; } // skip // comments if (c=='/' && data[1] == '/') { while (*data && *data != '\n') data++; goto skipwhite; } // handle quoted strings specially if (c == '\"') { data++; while (1) { c = *data++; if (c=='\"' || !c) { com_token[len] = 0; return (char*)data; } com_token[len] = c; len++; } } // parse single characters if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') { com_token[len] = c; len++; com_token[len] = 0; return (char*)data+1; } // parse a regular word do { com_token[len] = c; data++; len++; c = *data; if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'') break; } while (c>32); com_token[len] = 0; return (char*)data; } /* ================ COM_CheckParm Returns the position (1 to argc-1) in the program's argument list where the given parameter apears, or 0 if not present ================ */ int COM_CheckParm (const char *parm) { int i; for (i=1 ; inext) { if (s->pack) { Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); } else Con_Printf ("%s\n", s->filename); } } /* ============ COM_WriteFile The filename will be prefixed by the current game directory ============ */ void COM_WriteFile(char *filename, void *data, int len) { int handle; char name[MAX_OSPATH]; Sys_mkdir(com_gamedir); //johnfitz -- if we've switched to a nonexistant gamedir, create it now so we don't crash snprintf(name, sizeof(name), "%s/%s", com_gamedir, filename); handle = Sys_FileOpenWrite(name); if (handle == -1) { Sys_Printf("COM_WriteFile: failed on %s\n", name); return; } Sys_Printf("COM_WriteFile: %s\n", name); Sys_FileWrite(handle, data, len); Sys_FileClose(handle); } /* ============ COM_CreatePath Only used for CopyFile ============ */ void COM_CreatePath (char *path) { char *ofs; for (ofs = path+1 ; *ofs ; ofs++) { if (*ofs == '/') { // create the directory *ofs = 0; Sys_mkdir (path); *ofs = '/'; } } } /* =========== COM_CopyFile Copies a file over from the net to the local cache, creating any directories needed. This is for the convenience of developers using ISDN from home. =========== */ void COM_CopyFile (char *netpath, char *cachepath) { int in, out; int remaining, count; char* buf = Sys_BigStackAlloc(4096, "COM_CopyFile"); remaining = Sys_FileOpenRead (netpath, &in); COM_CreatePath (cachepath); // create directories up to the cache file out = Sys_FileOpenWrite (cachepath); while (remaining) { if (remaining < sizeof(buf)) count = remaining; else count = sizeof(buf); Sys_FileRead (in, buf, count); Sys_FileWrite (out, buf, count); remaining -= count; } Sys_FileClose (in); Sys_FileClose (out); Sys_BigStackFree(4096, "COM_CopyFile"); } /* =========== COM_FindFile Finds the file in the search path. Sets com_filesize and one of handle or file =========== */ int COM_FindFile (const char *filename, int *handle, FILE **file, unsigned int *path_id) { searchpath_t *search; char netpath[MAX_OSPATH]; char cachepath[MAX_OSPATH]; pack_t *pak; int i; int findtime, cachetime; if (file && handle) Sys_Error ("COM_FindFile: both handle and file set"); if (!file && !handle) Sys_Error ("COM_FindFile: neither handle or file set"); // // search through the path, one element at a time // search = com_searchpaths; if (proghack) { // gross hack to use quake 1 progs with quake 2 maps if (!strcmp(filename, "progs.dat")) search = search->next; } for ( ; search ; search = search->next) { // is the element a pak file? if (search->pack) { // look through all the pak file elements pak = search->pack; for (i=0 ; inumfiles ; i++) if (!strcmp (pak->files[i].name, filename)) { // found it! Sys_Printf ("PackFile: %s : %s\n",pak->filename, filename); if (path_id) *path_id = search->path_id; if (handle) { *handle = pak->handle; Sys_FileSeek (pak->handle, pak->files[i].filepos); } else { // open a new file on the pakfile *file = fopen (pak->filename, "rb"); if (*file) fseek (*file, pak->files[i].filepos, SEEK_SET); } com_filesize = pak->files[i].filelen; return com_filesize; } } else { // check a file in the directory tree if (!static_registered) { // if not a registered version, don't ever go beyond base if ( strchr (filename, '/') || strchr (filename,'\\')) continue; } snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename); findtime = Sys_FileTime (netpath); if (findtime == -1) continue; // see if the file needs to be updated in the cache if (!com_cachedir[0]) strcpy (cachepath, netpath); else { sprintf (cachepath,"%s%s", com_cachedir, netpath); cachetime = Sys_FileTime (cachepath); if (cachetime < findtime) COM_CopyFile (netpath, cachepath); strcpy (netpath, cachepath); } Sys_Printf ("FindFile: %s\n",netpath); com_filesize = Sys_FileOpenRead (netpath, &i); if (path_id) *path_id = search->path_id; if (handle) *handle = i; else { Sys_FileClose (i); *file = fopen (netpath, "rb"); } return com_filesize; } } Sys_Printf ("FindFile: can't find %s\n", filename); if (handle) *handle = -1; else *file = NULL; com_filesize = -1; return -1; } /* =========== COM_OpenFile filename never has a leading slash, but may contain directory walks returns a handle and a length it may actually be inside a pak file =========== */ int COM_OpenFile (const char *filename, int *handle, unsigned int *path_id) { return COM_FindFile (filename, handle, NULL, path_id); } /* =========== COM_FOpenFile If the requested file is inside a packfile, a new FILE * will be opened into the file. =========== */ int COM_FOpenFile (char *filename, FILE **file, unsigned int *path_id) { return COM_FindFile (filename, NULL, file, path_id); } /* ============ COM_CloseFile If it is a pak file handle, don't really close it ============ */ void COM_CloseFile (int h) { searchpath_t *s; for (s = com_searchpaths ; s ; s=s->next) if (s->pack && s->pack->handle == h) return; Sys_FileClose (h); } /* ============ COM_LoadFile Filename are reletive to the quake directory. Always appends a 0 byte. ============ */ #define LOADFILE_ZONE 0 #define LOADFILE_HUNK 1 #define LOADFILE_TEMPHUNK 2 #define LOADFILE_STACK 3 cache_user_t *loadcache; byte *loadbuf; int loadsize; byte *COM_LoadFile (const char *path, int usehunk, unsigned int *path_id) { int h; byte *buf; char base[32]; int len; buf = NULL; // quiet compiler warning // look for it in the filesystem or pack files len = COM_OpenFile (path, &h, path_id); if (h == -1) return NULL; // extract the filename base name for hunk tag COM_FileBase (path, base); switch (usehunk) { case LOADFILE_HUNK: buf = Hunk_AllocName (len+1, base); break; case LOADFILE_TEMPHUNK: buf = Hunk_TempAlloc (len+1); break; case LOADFILE_ZONE: buf = Z_Malloc (len+1); break; case LOADFILE_STACK: if (len+1 > loadsize) buf = Hunk_TempAlloc (len+1); else buf = loadbuf; break; default: Sys_Error ("COM_LoadFile: bad usehunk"); break; } if (!buf) Sys_Error ("COM_LoadFile: not enough space for %s", path); ((byte *)buf)[len] = 0; Sys_FileRead (h, buf, len); COM_CloseFile (h); return buf; } byte *COM_LoadHunkFile (const char *path, unsigned int *path_id) { return COM_LoadFile (path, LOADFILE_HUNK, path_id); } byte *COM_LoadTempFile (const char *path, unsigned int *path_id) { return COM_LoadFile (path, LOADFILE_TEMPHUNK, path_id); } // uses temp hunk if larger than bufsize byte *COM_LoadStackFile (char *path, void *buffer, int bufsize, unsigned int *path_id) { byte *buf; loadbuf = (byte *)buffer; loadsize = bufsize; buf = COM_LoadFile (path, LOADFILE_STACK, path_id); return buf; } /* ================= COM_LoadPackFile -- johnfitz -- modified based on topaz's tutorial Takes an explicit (not game tree related) path to a pak file. Loads the header and directory, adding the files at the beginning of the list so they override previous pack files. ================= */ pack_t *COM_LoadPackFile(char *packfile) { dpackheader_t header; int i; packfile_t *newfiles; int numpackfiles; pack_t *pack; int packhandle; dpackfile_t* info; unsigned short crc; if (Sys_FileOpenRead(packfile, &packhandle) == -1) return NULL; Sys_FileRead(packhandle, (void *)&header, sizeof(header)); if (header.id[0] != 'P' || header.id[1] != 'A' || header.id[2] != 'C' || header.id[3] != 'K') Sys_Error("%s is not a packfile", packfile); header.dirofs = LittleLong(header.dirofs); header.dirlen = LittleLong(header.dirlen); numpackfiles = header.dirlen / sizeof(dpackfile_t); if (numpackfiles > MAX_FILES_IN_PACK) Sys_Error("%s has %i files", packfile, numpackfiles); if (numpackfiles != PAK0_COUNT) com_modified = true; // not the original file info = Sys_BigStackAlloc(MAX_FILES_IN_PACK * sizeof(dpackfile_t), "COM_LoadPackFile"); //johnfitz -- dynamic gamedir loading newfiles = Z_Malloc(numpackfiles * sizeof(packfile_t)); //johnfitz Sys_FileSeek(packhandle, header.dirofs); Sys_FileRead(packhandle, (void *)info, header.dirlen); // crc the directory to check for modifications CRC_Init(&crc); for (i = 0; ifilename, packfile); pack->handle = packhandle; pack->numfiles = numpackfiles; pack->files = newfiles; // FitzQuake has this commented out Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles); info = Sys_BigStackAlloc(MAX_FILES_IN_PACK * sizeof(dpackfile_t), "COM_LoadPackFile"); return pack; } /* ================ COM_AddGameDirectory -- johnfitz -- modified based on topaz's tutorial Sets com_gamedir, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ void COM_AddGameDirectory(char *dir) { int i; searchpath_t *search; pack_t *pak; char pakfile[MAX_OSPATH]; unsigned int path_id; strcpy(com_gamedir, dir); // assign a path_id to this game directory if (com_searchpaths) path_id = com_searchpaths->path_id + 1; else path_id = 1; // add the directory to the search path search = Z_Malloc(sizeof(searchpath_t)); search->path_id = path_id; strcpy(search->filename, dir); search->next = com_searchpaths; com_searchpaths = search; SceUID d = sceIoDopen(dir); if (d >= 0) { SceIoDirent file; while (sceIoDread(d, &file) > 0) { if (strcasestr(file.d_name, ".pak") != 0) { sprintf(pakfile,"%s/%s", dir, file.d_name); pak = COM_LoadPackFile (pakfile); if (!pak) continue; search = Z_Malloc (sizeof(searchpath_t)); search->pack = pak; search->next = com_searchpaths; search->path_id = path_id; com_searchpaths = search; } } sceIoDclose(d); } } /* ============ COM_GetFolder ============ */ void COM_GetFolder (char *in, char *out) { char *last = NULL; while (*in) { if (*in == '/') last = out; *out++ = *in++; } if (last) *last = 0; else *out = 0; } /* ================ COM_InitFilesystem ================ */ void COM_InitFilesystem (void) { int i, j; char basedir[MAX_OSPATH]; searchpath_t *search; // // -basedir // Overrides the system supplied base directory (under GAMENAME) // i = COM_CheckParm ("-basedir"); if (i && i < com_argc-1) strcpy (basedir, com_argv[i+1]); else strcpy (basedir, host_parms.basedir); j = strlen (basedir); if (j > 0) { if ((basedir[j-1] == '\\') || (basedir[j-1] == '/')) basedir[j-1] = 0; } // // -cachedir // Overrides the system supplied cache directory (NULL or /qcache) // -cachedir - will disable caching. // i = COM_CheckParm ("-cachedir"); if (i && i < com_argc-1) { if (com_argv[i+1][0] == '-') com_cachedir[0] = 0; else strcpy (com_cachedir, com_argv[i+1]); } else if (host_parms.cachedir) strcpy (com_cachedir, host_parms.cachedir); else com_cachedir[0] = 0; // // start up with GAMENAME by default (referenced by GAMENAME_DIR) // COM_AddGameDirectory (va("%s/"GAMENAME_DIR, basedir) ); strcpy (com_gamedir, va("%s/"GAMENAME_DIR, basedir)); //johnfitz -- track number of mission packs added //since we don't want to allow the "game" command to strip them away com_nummissionpacks = 0; if (COM_CheckParm ("-rogue")){ COM_AddGameDirectory (va("%s/rogue", basedir) ); com_nummissionpacks++; } if (COM_CheckParm ("-hipnotic")){ COM_AddGameDirectory (va("%s/hipnotic", basedir) ); com_nummissionpacks++; } // // -game // Adds basedir/gamedir as an override game // i = COM_CheckParm ("-game"); if (i && i < com_argc-1) { com_modified = true; COM_AddGameDirectory (va("%s/%s", basedir, com_argv[i+1])); } // // -path [] ... // Fully specifies the exact serach path, overriding the generated one // i = COM_CheckParm ("-path"); if (i) { com_modified = true; com_searchpaths = NULL; while (++i < com_argc) { if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-') break; search = Hunk_Alloc (sizeof(searchpath_t)); if ( !strcmp(COM_FileExtension(com_argv[i]), "pak") ) { search->pack = COM_LoadPackFile (com_argv[i]); if (!search->pack) Sys_Error ("Couldn't load packfile: %s", com_argv[i]); } else strcpy (search->filename, com_argv[i]); search->next = com_searchpaths; com_searchpaths = search; } } if (COM_CheckParm ("-proghack")) proghack = true; } float COM_Clamp(float value, float min, float max) { return value < min ? min : (value > max ? max : value); } //======================================================== // strlcat and strlcpy, from OpenBSD /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* $OpenBSD: strlcat.c,v 1.11 2003/06/17 21:56:24 millert Exp $ */ /* $OpenBSD: strlcpy.c,v 1.8 2003/06/17 21:56:24 millert Exp $ */ #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif // #ifndef HAVE_STRLCAT // Baker: strip leading spaces from string char *strltrim(char *s) { char *t; assert(s != NULL); for (t = s; isspace(*t); ++t) continue; memmove(s, t, strlen(t) + 1); /* +1 so that '\0' is moved too */ return s; } // Lower string char* strtolower(char* s) { assert(s != NULL); char* p = s; while (*p != '\0') { *p = tolower(*p); p++; } return s; } ================================================ FILE: source/common.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // comndef.h -- general definitions #include #if !defined BYTE_DEFINED typedef unsigned char byte; #define BYTE_DEFINED 1 #endif #ifndef min #define min(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef max #define max(a, b) ((a) > (b) ? (a) : (b)) #endif #define stringify__(x) #x #define stringify(x) stringify__(x) typedef struct { char name[MAX_QPATH]; int filepos, filelen; } packfile_t; typedef struct pack_s { char filename[MAX_OSPATH]; int handle; int numfiles; packfile_t *files; } pack_t; typedef struct searchpath_s { unsigned int path_id; char filename[MAX_OSPATH]; pack_t *pack; // only one of filename / pack will be used struct searchpath_s *next; } searchpath_t; //============================================================================ typedef struct sizebuf_s { bool allowoverflow; // if false, do a Sys_Error bool overflowed; // set to true if the buffer size failed byte *data; int maxsize; int cursize; } sizebuf_t; void SZ_Alloc (sizebuf_t *buf, int startsize); void SZ_Free (sizebuf_t *buf); void SZ_Clear (sizebuf_t *buf); void *SZ_GetSpace (sizebuf_t *buf, int length); void SZ_Write (sizebuf_t *buf, const void *data, int length); void SZ_Print (sizebuf_t *buf, char *data); // strcats onto the sizebuf //============================================================================ typedef struct link_s { struct link_s *prev, *next; } link_t; void ClearLink (link_t *l); void RemoveLink (link_t *l); void InsertLinkBefore (link_t *l, link_t *before); void InsertLinkAfter (link_t *l, link_t *after); // (type *)STRUCT_FROM_LINK(link_t *link, type, member) // ent = STRUCT_FROM_LINK(link,entity_t,order) // FIXME: remove this mess! #define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m))) //============================================================================ #ifndef NULL #define NULL ((void *)0) #endif #define Q_MAXCHAR ((char)0x7f) #define Q_MAXSHORT ((short)0x7fff) #define Q_MAXINT ((int)0x7fffffff) #define Q_MAXLONG ((int)0x7fffffff) #define Q_MAXFLOAT ((int)0x7fffffff) #define Q_MINCHAR ((char)0x80) #define Q_MINSHORT ((short)0x8000) #define Q_MININT ((int)0x80000000) #define Q_MINLONG ((int)0x80000000) #define Q_MINFLOAT ((int)0x7fffffff) #define Q_CLAMP(_minval, x, _maxval) \ ((x) < (_minval) ? (_minval) : \ (x) > (_maxval) ? (_maxval) : (x)) //============================================================================ extern bool bigendien; extern short (*BigShort) (short l); extern short (*LittleShort) (short l); extern int (*BigLong) (int l); extern int (*LittleLong) (int l); extern float (*BigFloat) (float l); extern float (*LittleFloat) (float l); //============================================================================ void MSG_WriteChar (sizebuf_t *sb, int c); void MSG_WriteByte (sizebuf_t *sb, int c); void MSG_WriteShort (sizebuf_t *sb, int c); void MSG_WriteLong (sizebuf_t *sb, int c); void MSG_WriteFloat (sizebuf_t *sb, float f); void MSG_WriteString (sizebuf_t *sb, const char *s); void MSG_WriteCoord (sizebuf_t *sb, float f); void MSG_WriteAngle (sizebuf_t *sb, float f); void MSG_WritePreciseAngle(sizebuf_t *sb, float f); // JPG - precise aim!! extern int msg_readcount; extern bool msg_badread; // set if a read goes beyond end of message void MSG_BeginReading (void); int MSG_ReadChar (void); int MSG_ReadByte (void); int MSG_ReadShort (void); int MSG_ReadLong (void); float MSG_ReadFloat (void); char *MSG_ReadString (void); float MSG_ReadCoord (void); float MSG_ReadAngle (void); float MSG_ReadPreciseAngle(void); // JPG - precise aim!! //============================================================================ void strncpyz (char *dest, const char *src, size_t size); //============================================================================ extern char com_token[1024]; extern bool com_eof; char *COM_Parse (const char *data); extern int com_argc; extern char **com_argv; int COM_CheckParm (const char *parm); void COM_Init (char *path); void COM_InitArgv (int argc, char **argv); char *COM_SkipPath (char *pathname); void COM_StripExtension (char *in, char *out); void COM_FileBase (const char *in, char *out); void COM_DefaultExtension (char *path, char *extension); void COM_GetFolder (char *in, char *out); char *va(char *format, ...); // does a varargs printf into a temp buffer //============================================================================ extern int com_filesize; struct cache_user_s; extern char com_gamedir[MAX_OSPATH]; void COM_WriteFile (char *filename, void *data, int len); int COM_OpenFile (const char *filename, int *hndl, unsigned int *path_id); int COM_FOpenFile (char *filename, FILE **file, unsigned int *path_id); void COM_CloseFile (int h); byte *COM_LoadStackFile (char *path, void *buffer, int bufsize, unsigned int *path_id); byte *COM_LoadTempFile (const char *path, unsigned int *path_id); byte *COM_LoadHunkFile (const char *path, unsigned int *path_id); float COM_Clamp(float value, float min, float max); extern struct cvar_s registered; extern bool standard_quake, rogue, hipnotic; //============================================================================ #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz); #endif char *strltrim(char *s); char* strtolower(char* s); ================================================ FILE: source/console.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // console.c #ifdef NeXT #include #endif #ifndef _MSC_VER #include #endif #include #include "quakedef.h" int con_linewidth; float con_cursorspeed = 4; #define CON_TEXTSIZE 65536 bool con_forcedup; // because no entities to refresh int con_totallines; // total lines in console scrollback int con_backscroll; // lines up from bottom to display int con_current; // where next message will be printed int con_x; // offset in current line for next print char *con_text=0; CVAR (con_notifytime, 3, CVAR_ARCHIVE) //seconds #define NUM_CON_TIMES 4 float con_times[NUM_CON_TIMES]; // realtime time the line was generated // for transparent notify lines int con_vislines; bool con_debuglog; #define MAXCMDLINE 256 extern char key_lines[32][MAXCMDLINE]; extern int edit_line; extern int key_linepos; bool con_initialized; int con_notifylines; // scan lines to clear for notify lines extern void M_Menu_Main_f (void); /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { if (key_dest == key_console) { if (cls.state == ca_connected) { key_dest = key_game; key_lines[edit_line][1] = 0; // clear any typing key_linepos = 1; } else { M_Menu_Main_f (); } } else key_dest = key_console; SCR_EndLoadingPlaque (); memset (con_times, 0, sizeof(con_times)); } /* ================ Con_Clear_f ================ */ void Con_Clear_f (void) { if (con_text) memset (con_text, ' ', CON_TEXTSIZE); } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify (void) { int i; for (i=0 ; i> 3) - 2; if (width == con_linewidth) return; if (width < 1) // video hasn't been initialized yet { width = 78; con_linewidth = width; con_totallines = CON_TEXTSIZE / con_linewidth; memset(con_text, ' ', CON_TEXTSIZE); } else { oldwidth = con_linewidth; con_linewidth = width; oldtotallines = con_totallines; con_totallines = CON_TEXTSIZE / con_linewidth; numlines = oldtotallines; if (con_totallines < numlines) numlines = con_totallines; numchars = oldwidth; if (con_linewidth < numchars) numchars = con_linewidth; tbuf = Sys_BigStackAlloc(CON_TEXTSIZE, "Con_CheckResize"); memcpy(tbuf, con_text, CON_TEXTSIZE); memset(con_text, ' ', CON_TEXTSIZE); for (i = 0; i con_linewidth) ) con_x = 0; txt++; if (cr) { con_current--; cr = false; } if (!con_x) { Con_Linefeed (); // mark time for transparent overlay if (con_current >= 0) con_times[con_current % NUM_CON_TIMES] = realtime; } switch (c) { case '\n': con_x = 0; break; case '\r': con_x = 0; cr = 1; break; default: // display character and advance y = con_current % con_totallines; con_text[y*con_linewidth+con_x] = c | mask; con_x++; if (con_x >= con_linewidth) con_x = 0; break; } } } /* ================ Con_DebugLog ================ */ void Con_DebugLog(char *file, char *fmt, ...) { va_list argptr; static char data[1024]; int fd; va_start(argptr, fmt); vsprintf(data, fmt, argptr); va_end(argptr); fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); write(fd, data, strlen(data)); close(fd); } /* ================ Con_Printf Handles cursor positioning, line wrapping, etc ================ */ #define MAXPRINTMSG 16384 // FIXME: make a buffer size safe vsprintf? void Con_Printf (const char *fmt, ...) { va_list argptr; char* msg = Sys_BigStackAlloc(MAXPRINTMSG, "Con_Printf"); static bool inupdate; va_start (argptr,fmt); vsprintf (msg,fmt,argptr); va_end (argptr); // also echo to debugging console Sys_Printf ("%s", msg); // also echo to debugging console // log all messages to file if (con_debuglog) Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg); if (!con_initialized) { Sys_BigStackFree(MAXPRINTMSG, "Con_Printf"); return; } if (cls.state == ca_dedicated) return; // no graphics mode // write it to the scrollable buffer Con_Print (msg); // update the screen if the console is displayed if (cls.signon != SIGNONS && !scr_disabled_for_loading ) { // protect against infinite loop if something in SCR_UpdateScreen calls // Con_Printd if (!inupdate) { inupdate = true; SCR_UpdateScreen (); inupdate = false; } } Sys_BigStackFree(MAXPRINTMSG, "Con_Printf"); } /* ================ Con_DPrintf A Con_Printf that only shows up if the "developer" cvar is set ================ */ void Con_DPrintf (char *fmt, ...) { va_list argptr; char* msg; if (!developer.value) return; // don't confuse non-developers with techie stuff... msg = Sys_BigStackAlloc(MAXPRINTMSG, "Con_DPrintf"); va_start (argptr,fmt); vsprintf (msg,fmt,argptr); va_end (argptr); Con_Printf ("%s", msg); Sys_BigStackFree(MAXPRINTMSG, "Con_DPrintf"); } /* ================== Con_SafePrintf Okay to call even when the screen can't be updated ================== */ void Con_SafePrintf (char *fmt, ...) { va_list argptr; char* msg = Sys_BigStackAlloc(1024, "Con_SafePrintf"); int temp; va_start (argptr,fmt); vsprintf (msg,fmt,argptr); va_end (argptr); temp = scr_disabled_for_loading; scr_disabled_for_loading = true; Con_Printf ("%s", msg); scr_disabled_for_loading = temp; Sys_BigStackFree(1024, "Con_SafePrintf"); } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput The input line scrolls horizontally if typing goes beyond the right edge ================ */ void Con_DrawInput (void) { int i; char *text; if (key_dest != key_console && !con_forcedup) return; // don't draw anything text = key_lines[edit_line]; // add the cursor frame text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1); // fill out remainder with spaces for (i=key_linepos+1 ; i< con_linewidth ; i++) text[i] = ' '; // prestep if horizontally scrolling if (key_linepos >= con_linewidth) text += 1 + key_linepos - con_linewidth; // draw it for (i=0 ; i con_notifytime.value) continue; text = con_text + (i % con_totallines)*con_linewidth; clearnotify = 0; scr_copytop = 1; for (x = 0 ; x < con_linewidth ; x++) Batch_Character ( (x+1)<<3, v, text[x]); v += 8; } if (key_dest == key_message) { clearnotify = 0; scr_copytop = 1; x = 0; Batch_String (8, v, "say:", 0); Batch_String(40, v, chat_buffer, 0); x += strlen(chat_buffer); Batch_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1)); v += 8; } Draw_Batched(); if (v > con_notifylines) con_notifylines = v; } /* ================ Con_DrawConsole Draws the console with the solid background The typing input line at the bottom should only be drawn if typing is allowed ================ */ void Con_DrawConsole (int lines, bool drawinput) { int i, sb, x, y; int rows; char *text; int j; char ver[32]; if (lines <= 0) return; GL_SetCanvas (CANVAS_CONSOLE); // draw the background Draw_ConsoleBackground (); // draw the text con_vislines = lines * vid.conheight / glheight; rows = (con_vislines +7)/8; y = vid.conheight - rows*8; rows -= 2; //for input and version lines sb = (con_backscroll) ? 2 : 0; for (i = con_current - rows + 1; i <= con_current - sb; i++, y += 8) { j = i - con_backscroll; if (j < 0) j = 0; text = con_text + (j % con_totallines)*con_linewidth; for (x = 0 ; x < con_linewidth ; x++) Batch_Character ( (x+1)<<3, y, text[x]); } // draw scrollback arrows if (con_backscroll) { y += 8; // blank line for (x = 0; x < con_linewidth; x += 4) Batch_Character ((x + 1)<<3, y, '^'); y += 8; } // draw the input prompt, user text, and cursor if (drawinput) Con_DrawInput (); //draw version number in bottom right y += 8; snprintf (ver, sizeof(ver), "vitaQuake v.%.2f", VERSION); Batch_String((con_linewidth - strlen(ver) + 2)<<3, y, ver, 0); Draw_Batched(); } /* ================== Con_NotifyBox ================== */ void Con_NotifyBox (char *text) { double t1, t2; // during startup for sound / cd warnings Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); Con_Printf (text); Con_Printf ("Press a key.\n"); Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); key_count = -2; // wait for a key down and up key_dest = key_console; do { t1 = Sys_FloatTime (); SCR_UpdateScreen (); Sys_SendKeyEvents (); t2 = Sys_FloatTime (); realtime += t2-t1; // make the cursor blink } while (key_count < 0); Con_Printf ("\n"); key_dest = key_game; realtime = 0; // put the cursor back to invisible } ================================================ FILE: source/console.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // console // extern int con_totallines; extern int con_backscroll; extern bool con_forcedup; // because no entities to refresh extern bool con_initialized; extern byte *con_chars; extern int con_notifylines; // scan lines to clear for notify lines void Con_DrawCharacter (int cx, int line, int num); void Con_CheckResize (void); void Con_Init (void); void Con_DrawConsole (int lines, bool drawinput); void Con_Print (char *txt); void Con_Printf (const char *fmt, ...); void Con_DPrintf (char *fmt, ...); void Con_SafePrintf (char *fmt, ...); void Con_Clear_f (void); void Con_DrawNotify (void); void Con_ClearNotify (void); void Con_ToggleConsole_f (void); void Con_NotifyBox (char *text); // during startup for sound / cd warnings ================================================ FILE: source/crc.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* crc.c */ #include "quakedef.h" #include "crc.h" // this is a 16 bit, non-reflected CRC using the polynomial 0x1021 // and the initial and final xor values shown below... in other words, the // CCITT standard CRC used by XMODEM #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 static unsigned short crctable[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } void CRC_ProcessByte(unsigned short *crcvalue, byte data) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } unsigned short CRC_Value(unsigned short crcvalue) { return crcvalue ^ CRC_XOR_VALUE; } unsigned short CRC_Block(byte *data, int size) { unsigned short crc = CRC_INIT_VALUE; while (size--) crc = (crc << 8) ^ crctable[(crc >> 8) ^ (*data++)]; return crc ^ CRC_XOR_VALUE; } ================================================ FILE: source/crc.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* crc.h */ void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); ================================================ FILE: source/cvar.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cvar.c -- dynamic variable tracking #include "quakedef.h" cvar_t *cvar_vars; char *cvar_null_string = ""; /* ============ Cvar_FindVar ============ */ cvar_t *Cvar_FindVar (const char *var_name) { cvar_t *var; for (var=cvar_vars ; var ; var=var->next) if (!strcmp (var_name, var->name)) return var; return NULL; } /* ============ Cvar_VariableValue ============ */ float Cvar_VariableValue (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return 0; return atof (var->string); } /* ============ Cvar_VariableString ============ */ char *Cvar_VariableString (const char *var_name) { cvar_t *var; var = Cvar_FindVar (var_name); if (!var) return cvar_null_string; return var->string; } /* ============ Cvar_CompleteVariable ============ */ char *Cvar_CompleteVariable (char *partial) { cvar_t *cvar; int len; len = strlen(partial); if (!len) return NULL; // check functions for (cvar=cvar_vars ; cvar ; cvar=cvar->next) if (!strncmp (partial,cvar->name, len)) return (char*)cvar->name; return NULL; } /* ============ Cvar_Set ============ */ void Cvar_SetFull (const char *var_name, const char *value, bool forced) { cvar_t *var; bool changed; var = Cvar_FindVar (var_name); if (!var) { // there is an error in C code if this happens Con_Printf ("Cvar_Set: variable %s not found\n", var_name); return; } if (var->flags & CVAR_ROM && !forced) { Con_Printf ("%s is read-only.\n", var_name); return; } changed = strcmp(var->string, value); Z_Free (var->string); // free the old value string var->string = Z_Malloc (strlen(value)+1); strcpy (var->string, value); var->value = atof (var->string); if (var->flags & CVAR_SERVERINFO && changed) { if (sv.active) SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); } if (var->callback) var->callback(var); } void Cvar_Set (const char *var_name, const char *value) { Cvar_SetFull (var_name, value, false); } void Cvar_ForceSet (const char *var_name, const char *value) { Cvar_SetFull (var_name, value, true); } /* ============ Cvar_SetValue ============ */ void Cvar_SetValue (const char *var_name, float value) { char val[32]; sprintf (val, "%f",value); Cvar_Set (var_name, val); } void Cvar_ToggleValue(cvar_t *cvar) { char val[32]; sprintf(val, "%i", !cvar->value); Cvar_Set(cvar->name, val); } /* ============ Cvar_RegisterVariable Adds a freestanding variable to the variable list. ============ */ void Cvar_RegisterVariable (cvar_t *variable) { char *oldstr; // first check to see if it has already been defined if (Cvar_FindVar (variable->name)) { Con_Printf ("Can't register variable %s, already defined\n", variable->name); return; } // Check for DEBUG-only CVARs. if (variable->flags & CVAR_DEBUG) { #ifndef DEBUG return; #endif } // check for overlap with a command if (Cmd_Exists (variable->name)) { Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name); return; } // copy the value off, because future sets will Z_Free it oldstr = variable->string; variable->string = Z_Malloc (strlen(variable->string)+1); strcpy (variable->string, oldstr); variable->value = atof (variable->string); if (!(variable->flags & CVAR_CALLBACK)) variable->callback = NULL; // link the variable in variable->next = cvar_vars; cvar_vars = variable; } /* ============ Cvar_SetCallback Set a callback function to the var ============ */ void Cvar_SetCallback(cvar_t *var, cvarcallback_t func) { var->callback = func; if (func) var->flags |= CVAR_CALLBACK; else var->flags &= ~CVAR_CALLBACK; } /* ============ Cvar_Command Handles variable inspection and changing from the console ============ */ bool Cvar_Command (void) { cvar_t *v; // check variables v = Cvar_FindVar (Cmd_Argv(0)); if (!v) return false; // perform a variable print or set if (Cmd_Argc() == 1) { Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string); return true; } Cvar_Set (v->name, Cmd_Argv(1)); return true; } /* ============ Cvar_WriteVariables Writes lines containing "set variable value" for all variables with the archive flag set to true. ============ */ void Cvar_WriteVariables (FILE *f) { cvar_t *var; fprintf(f, "// CVARs\n"); for (var = cvar_vars ; var ; var = var->next) if (var->flags & CVAR_ARCHIVE) fprintf (f, "%s \"%s\"\n", var->name, var->string); } void Cvar_Set_f(void) { char *cvarname; char *cvarvalue; cvar_t *var; cvarname = Cmd_Argv(1); cvarvalue = Cmd_Argv(2); var = Cvar_FindVar(cvarname); if (!var) { if (Cmd_Exists(cvarname)) { Con_Printf("%s exists as a command\n", cvarname); return; } var = malloc(sizeof(*var)); if (!var) return; memset(var, 0, sizeof(*var)); var->name = strdup(cvarname); var->string = ""; Cvar_RegisterVariable(var); } Cvar_Set(cvarname, cvarvalue); } void Cvar_Seta_f(void) { char *cvarname; char *cvarvalue; cvar_t *var; cvarname = Cmd_Argv(1); cvarvalue = Cmd_Argv(2); var = Cvar_FindVar(cvarname); if (!var) { if (Cmd_Exists(cvarname)) { Con_Printf("%s exists as a command\n", cvarname); return; } var = malloc(sizeof(*var)); if (!var) return; memset(var, 0, sizeof(*var)); var->name = strdup(cvarname); var->string = ""; var->flags = CVAR_ARCHIVE; Cvar_RegisterVariable(var); } Cvar_Set(cvarname, cvarvalue); } ================================================ FILE: source/cvar.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cvar.h /* cvar_t variables are used to hold scalar or string variables that can be changed or displayed at the console or prog code as well as accessed directly in C code. it is sufficient to initialize a cvar_t with just the first two fields, or you can add a CVAR_ARCHIVE flag for variables that you want saved to the configuration file when the game is quit: cvar_t r_draworder = {"r_draworder","1"}; cvar_t scr_screensize = {"screensize","1",CVAR_ARCHIVE}; Cvars must be registered before use, or they will have a 0 value instead of the float interpretation of the string. Generally, all cvar_t declarations should be registered in the apropriate init function before any console commands are executed: Cvar_RegisterVariable (&host_framerate); C code usually just references a cvar in place: if ( r_draworder.value ) It could optionally ask for the value to be looked up for a string name: if (Cvar_VariableValue ("r_draworder")) Interpreted prog code can access cvars with the cvar(name) or cvar_set (name, value) internal functions: teamplay = cvar("teamplay"); cvar_set ("registered", "1"); The user can access cvars from the console in two ways: r_draworder prints the current value r_draworder 0 sets the current value to 0 Cvars are restricted from having the same names as commands to keep this interface from being ambiguous. */ #define CVAR_NONE BIT(0) // No property (can be omitted) #define CVAR_ARCHIVE BIT(1) // CVAR saved on the CFG. #define CVAR_SERVERINFO BIT(2) // Informative CVAR from the server. #define CVAR_ROM BIT(3) // Only set by the engine. #define CVAR_DEBUG BIT(4) // CVARs only enabled if the DEBUG flag is set. #define CVAR_CALLBACK BIT(5) // CVAR has a callback typedef void(*cvarcallback_t) (struct cvar_s *); typedef struct cvar_s { const char *name; char *string; unsigned int flags; float value; cvarcallback_t callback; struct cvar_s *next; } cvar_t; #define CVAR(name, defaultvalue, flags) cvar_t name = {#name, (char*)#defaultvalue, flags}; #define STATIC_CVAR(name, defaultvalue, flags) static cvar_t name = {#name, #defaultvalue, flags}; void Cvar_RegisterVariable (cvar_t *variable); // registers a cvar that already has the name, string, and optionally the // archive elements set. // equivelant to " " typed at the console void Cvar_Set (const char *var_name, const char *value); void Cvar_ForceSet (const char *var_name, const char *value); // set a callback function to the var void Cvar_SetCallback(cvar_t *var, cvarcallback_t func); // expands value to a string and calls Cvar_Set void Cvar_SetValue (const char *var_name, float value); // Toggles a value void Cvar_ToggleValue(cvar_t *cvar); // returns 0 if not defined or non numeric float Cvar_VariableValue (const char *var_name); // returns an empty string if not defined char *Cvar_VariableString (const char *var_name); // attempts to match a partial variable name for command line completion // returns NULL if nothing fits char *Cvar_CompleteVariable (char *partial); bool Cvar_Command (void); // called by Cmd_ExecuteString when Cmd_Argv(0) doesn't match a known // command. Returns true if the command was a variable reference that // was handled. (print or change) void Cvar_WriteVariables (FILE *f); // Writes lines containing "set variable value" for all variables // with the archive flag set to true. cvar_t *Cvar_FindVar (const char *var_name); void Cvar_Set_f(void); void Cvar_Seta_f(void); extern cvar_t *cvar_vars; ================================================ FILE: source/draw.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // draw.h -- these are the only functions outside the refresh allowed // to touch the vid buffer extern qpic_t *draw_disc; // also used on sbar void Draw_Init (void); void Draw_Character (int x, int y, int num); void Draw_DebugChar (signed char num); void Draw_Pic (int x, int y, qpic_t *pic); void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha); void Draw_TransPic (int x, int y, qpic_t *pic); void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation); void Draw_ConsoleBackground (void); void Draw_BeginDisc (void); void Draw_EndDisc (void); void Draw_TileClear (int x, int y, int w, int h); void Draw_Fill (int x, int y, int w, int h, int c); void Draw_FadeScreen (void); void Draw_String (int x, int y, const char *str, int delta); void Draw_Crosshair(void); qpic_t *Draw_PicFromWad (const char *name); qpic_t *Draw_CachePic (char *path); void Batch_Character (int x, int y, int num); void Batch_String (int x, int y, const char *str, int delta); void Draw_Batched(); ================================================ FILE: source/gl_draw.cpp ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // draw.c -- this is the only file outside the refresh that touches the // vid buffer extern "C"{ #include #include "quakedef.h" extern unsigned short CRC_Block(byte *data, int size); } #define GL_COLOR_INDEX8_EXT 0x80E5 extern unsigned char d_15to8table[65536]; int cs_texture; static byte cs_data[64] = { 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; CVAR (gl_nobind, 0, CVAR_NONE) CVAR (gl_max_size, 4096, CVAR_NONE) CVAR (gl_picmip, 0, CVAR_NONE) CVAR (gl_bilinear, 1, CVAR_ARCHIVE) CVAR (gl_compress, 0, CVAR_ARCHIVE) extern cvar_t crosshaircolor_r; extern cvar_t crosshaircolor_g; extern cvar_t crosshaircolor_b; extern cvar_t cl_crossx; extern cvar_t cl_crossy; extern cvar_t crosshair; extern cvar_t gl_mipmap; extern cvar_t scr_crosshairscale; extern cvar_t scr_conalpha; extern cvar_t scr_menuscale; extern cvar_t scr_sbarscale; extern int tex_cache; byte *draw_chars; // 8*8 graphic characters qpic_t *draw_disc; qpic_t *draw_backtile; int translate_texture; int char_texture; int currentcanvas = -1; typedef struct { int texnum; float sl, tl, sh, th; } glpic_t; byte conback_buffer[sizeof(qpic_t) + sizeof(glpic_t)]; qpic_t *conback = (qpic_t *)&conback_buffer; int gl_lightmap_format = GL_RGBA; int gl_solid_format = 3; int gl_alpha_format = 4; int gl_filter_min = GL_LINEAR; int gl_filter_max = GL_LINEAR; int texels; typedef struct { int texnum; char identifier[64]; int width, height; bool mipmap; } gltexture_t; #define MAX_GLTEXTURES 1024 gltexture_t gltextures[MAX_GLTEXTURES]; int numgltextures = 0; static int LoadExternalPic(char *identifier) { char fname[512]; COM_StripExtension (identifier, fname); int w, h; byte *data = Image_LoadImage (fname, &w, &h); if (data) { int r = GL_LoadTexture32 ("", w, h, data, false, true, false); free(data); return r; } return -1; } // FIXME: It seems the texture manager fails with Half Life BSPs /* * Texture Manager - derived from glesquake */ class textureStore { private: static const GLuint UNUSED = (GLuint) -2; static const GLuint PAGED_OUT = (GLuint) -1; struct entry { entry* next; entry* prev; GLuint real_texnum; // UNUSED, PAGED_OUT bool is32; byte* pData; // 0 ==> not created by us. size_t size; bool alpha; int width; int height; bool mipmap; entry() { next = 0; prev = 0; real_texnum = UNUSED; pData = 0; } void unlink() { if (next) { next->prev = prev; } if (prev) { prev->next = next; } next = 0; prev = 0; } void insertBefore(entry* e){ if (e) { prev = e->prev; if ( prev ) { prev->next = this; } next = e; e->prev = this; } else { prev = 0; next = 0; } } }; public: static textureStore* get() { if (g_pTextureCache == 0) { g_pTextureCache = new textureStore(); } return g_pTextureCache; } // Equivalent of glBindTexture, but uses the virtual texture table void bind(int virtTexNum) { if ( (unsigned int) virtTexNum >= TEXTURE_STORE_NUM_TEXTURES) { Sys_Error("not in the range we're managing"); } mBoundTextureID = virtTexNum; entry* e = &mTextures[virtTexNum]; if ( e->real_texnum == UNUSED) { glGenTextures( 1, &e->real_texnum); } if ( e->pData == 0) { glBindTexture(GL_TEXTURE_2D, e->real_texnum); return; } update(e); } void update(entry* e) { // Update the "LRU" part of the cache unlink(e); e->insertBefore(mFirst); mFirst = e; if (! mLast) { mLast = e; } if (e->real_texnum == PAGED_OUT ) { // Create a real texture // Make sure there is enough room for this texture ensure(e->size); glGenTextures( 1, &e->real_texnum); glBindTexture(GL_TEXTURE_2D, e->real_texnum); if (e->is32) GL_Upload32 ((unsigned*)e->pData, e->width, e->height, e->mipmap, e->alpha); else GL_Upload8 (e->pData, e->width, e->height, e->mipmap, e->alpha); } else { glBindTexture(GL_TEXTURE_2D, e->real_texnum); } } // Create a texture, and remember the data so we can create // it again later. void create(int width, int height, byte* data, bool mipmap, bool alpha, bool is32) { int size = width * height; if (is32) size *= 4; if (size + mLength > mCapacity) { Sys_Error("Ran out of virtual texture space. %d", size); }; entry* e = &mTextures[mBoundTextureID]; // Call evict in case the currently bound texture id is already // in use. (Shouldn't happen in Quake.) // To Do: reclaim the old texture memory from the virtual memory. evict(e); e->alpha = alpha; e->pData = mBase + mLength; memcpy(e->pData, data, size); e->size = size; e->width = width; e->height = height; e->mipmap = mipmap; e->is32 = is32; e->real_texnum = PAGED_OUT; mLength += size; update(e); } // Re-upload the current textures because we've been reset. void rebindAll() { grabMagicTextureIds(); for (entry* e = mFirst; e; e = e->next ) { if (! (e->real_texnum == UNUSED || e->real_texnum == PAGED_OUT)) { glBindTexture(GL_TEXTURE_2D, e->real_texnum); if (e->pData) { if (e->is32) GL_Upload32 ((unsigned*)e->pData, e->width, e->height, e->mipmap, e->alpha); else GL_Upload8 (e->pData, e->width, e->height, e->mipmap, e->alpha); } } } } private: textureStore() { grabMagicTextureIds(); mFirst = 0; mLast = 0; mTextureCount = 0; mBase = (byte*)malloc(TEXTURE_STORE_SIZE); mBase[TEXTURE_STORE_SIZE-1] = 0; mLength = 0; mCapacity = TEXTURE_STORE_SIZE; mRamUsed = 0; mRamSize = LIVE_TEXTURE_LIMIT; } ~textureStore() { free(mBase); } void grabMagicTextureIds() { // reserve these two texture ids. glBindTexture(GL_TEXTURE_2D, UNUSED); glBindTexture(GL_TEXTURE_2D, PAGED_OUT); } void unlink(entry* e) { if (e == mFirst) { mFirst = e->next; } if (e == mLast) { mLast = e->prev; } e->unlink(); } void ensure(int size) { while ( mRamSize - mRamUsed < (unsigned int) size) { entry* e = mLast; if(! e) { Sys_Error("Ran out of entries"); return; } evict(e); } mRamUsed += size; } void evict(entry* e) { unlink(e); if ( e->pData ) { glDeleteTextures(1, &e->real_texnum); e->real_texnum = PAGED_OUT; mRamUsed -= e->size; } } static const size_t TEXTURE_STORE_SIZE = 16 * 1024 * 1024; static const size_t LIVE_TEXTURE_LIMIT = 1 * 1024 * 1024; static const size_t TEXTURE_STORE_NUM_TEXTURES = 512; byte* mBase; size_t mLength; size_t mCapacity; // Keep track of texture RAM. size_t mRamUsed; size_t mRamSize; // The virtual textures entry mTextures[MAX_GLTEXTURES]; entry* mFirst; // LRU queue entry* mLast; size_t mTextureCount; // How many virtual textures have been allocated static textureStore* g_pTextureCache; int mBoundTextureID; }; textureStore* textureStore::g_pTextureCache; void GL_Bind (int texnum) { if (currenttexture == texnum) return; currenttexture = texnum; if (tex_cache) textureStore::get()->bind(texnum); else glBindTexture(GL_TEXTURE_2D, texnum); } /* ============================================================================= scrap allocation Allocate all the little status bar obejcts into a single texture to crutch up stupid hardware / drivers ============================================================================= */ #define MAX_SCRAPS 2 #define BLOCK_WIDTH 256 #define BLOCK_HEIGHT 256 int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH*BLOCK_HEIGHT*4]; bool scrap_dirty; int scrap_texnum; // returns a texture number and the position inside it int Scrap_AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int bestx; int texnum; for (texnum=0 ; texnum= best) break; if (scrap_allocated[texnum][i+j] > best2) best2 = scrap_allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; iwidth, pic->height, pic->data, false, true); } qpic_t *Draw_PicFromWad (const char *name) { qpic_t *p; glpic_t *gl; p = (qpic_t*)W_GetLumpName (name); gl = (glpic_t *)p->data; char fname[64]; sprintf(fname, "gfx/%s", name); int texnum = LoadExternalPic(fname); if (texnum != -1) { gl->texnum = texnum; gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; return p; } // load little ones into the scrap if (p->width < 64 && p->height < 64) { int x, y; int i, j, k; int texnum; texnum = Scrap_AllocBlock (p->width, p->height, &x, &y); scrap_dirty = true; k = 0; for (i=0 ; iheight ; i++) for (j=0 ; jwidth ; j++, k++) scrap_texels[texnum][(y+i)*BLOCK_WIDTH + x + j] = p->data[k]; texnum += scrap_texnum; gl->texnum = texnum; gl->sl = (x+0.01)/(float)BLOCK_WIDTH; gl->sh = (x+p->width-0.01)/(float)BLOCK_WIDTH; gl->tl = (y+0.01)/(float)BLOCK_WIDTH; gl->th = (y+p->height-0.01)/(float)BLOCK_WIDTH; pic_count++; pic_texels += p->width*p->height; } else { gl->texnum = GL_LoadPicTexture (p); gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; } return p; } /* ================ Draw_CachePic ================ */ qpic_t *Draw_CachePic (char *path) { cachepic_t *pic; int i; qpic_t *dat; glpic_t *gl; for (pic=menu_cachepics, i=0 ; iname)) return &pic->pic; if (menu_numcachepics == MAX_CACHED_PICS) Sys_Error ("menu_numcachepics == MAX_CACHED_PICS"); menu_numcachepics++; strcpy (pic->name, path); // // load the pic from disk // dat = (qpic_t *)COM_LoadTempFile (path, NULL); if (!dat) Sys_Error ("Draw_CachePic: failed to load %s", path); SwapPic (dat); // HACK HACK HACK --- we need to keep the bytes for // the translatable player picture just for the menu // configuration dialog if (!strcmp (path, "gfx/menuplyr.lmp")) memcpy (menuplyr_pixels, dat->data, dat->width*dat->height); pic->pic.width = dat->width; pic->pic.height = dat->height; gl = (glpic_t *)pic->pic.data; int texnum = LoadExternalPic(path); if (texnum != -1) gl->texnum = texnum; else gl->texnum = GL_LoadPicTexture (dat); gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; return &pic->pic; } void Draw_CharToConback (int num, byte *dest) { int row, col; byte *source; int drawline; int x; row = num>>4; col = num&15; source = draw_chars + (row<<10) + (col<<3); drawline = 8; while (drawline--) { for (x=0 ; x<8 ; x++) if (source[x] != 255) dest[x] = 0x60 + source[x]; source += 128; dest += 320; } } typedef struct { const char *name; int minimize, maximize; } glmode_t; glmode_t modes[] = { {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, {"GL_LINEAR", GL_LINEAR, GL_LINEAR} }; /* =============== Draw_TextureMode_f =============== */ void Draw_TextureMode_f (void) { int i; gltexture_t *glt; if (Cmd_Argc() == 1) { for (i=0 ; i< 6 ; i++) if (gl_filter_min == modes[i].minimize) { Con_Printf ("%s\n", modes[i].name); return; } Con_Printf ("current filter is unknown???\n"); return; } for (i=0 ; i< 6 ; i++) { if (!strcasecmp ((char*)modes[i].name, Cmd_Argv(1) ) ) break; } if (i == 6) { Con_Printf ("bad filter name\n"); return; } gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; // change all the existing mipmap texture objects for (i=0, glt=gltextures ; imipmap) { GL_Bind (glt->texnum); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } /* =============== Callback_Bilinear_f This callback is used to set at launch the texture mode. =============== */ static void Callback_Bilinear_f(cvar_t *var) { if (gl_bilinear.value) Cbuf_AddText("gl_texturemode GL_LINEAR\n"); else Cbuf_AddText("gl_texturemode GL_NEAREST\n"); } /* =============== Draw_Init =============== */ void Draw_Init (void) { int i; qpic_t *cb; byte *dest, *src; int x, y; char ver[40]; glpic_t *gl; int start; byte *ncdata; int f, fstep, maxsize; Cvar_RegisterVariable (&gl_nobind); Cvar_RegisterVariable (&gl_max_size); Cvar_RegisterVariable (&gl_picmip); Cvar_RegisterVariable (&gl_bilinear); Cvar_RegisterVariable (&st_separation ); Cvar_RegisterVariable (&st_zeropdist ); Cvar_RegisterVariable (&st_fustbal ); Cvar_SetCallback (&gl_bilinear, &Callback_Bilinear_f); // texture_max_size if ((i = COM_CheckParm("-maxsize")) != 0) { maxsize = atoi(com_argv[i+1]); maxsize &= 0xff80; Cvar_SetValue("gl_max_size", maxsize); } Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f); // load the console background and the charset // by hand, because we need to write the version // string into the background before turning // it into a texture draw_chars = (byte*)W_GetLumpName ("conchars"); for (i=0 ; i<256*64 ; i++) if (draw_chars[i] == 0) draw_chars[i] = 255; // proper transparent color unsigned short conchars_crc = CRC_Block(draw_chars, 256*64); if (conchars_crc == 0x4FA4) { // Patching 'Y' glyphs to not make them ugly on original font uint8_t grey = draw_chars[2 + 42 * 128]; uint8_t brown = draw_chars[2 + 123 * 128]; int to_grey[] = { 74 + 40 * 128, 75 + 41 * 128, 76 + 43 * 128, 76 + 44 * 128, 76 + 45 * 128, 74 + 57 * 128, 75 + 58 * 128 }; int to_trans[] = { 74 + 42 * 128, 74 + 43 * 128, 74 + 44 * 128, 74 + 45 * 128, 74 + 59 * 128, 76 + 57 * 128, 74 + 123 * 128, 76 + 121 * 128, 74 + 106 * 128, 74 + 107 * 128, 74 + 108 * 128, 74 + 109 * 128 }; int to_brown[] = { 74 + 104 * 128, 75 + 105 * 128, 76 + 107 * 128, 76 + 108 * 128, 76 + 109 * 128, 74 + 121 * 128, 75 + 122 * 128 }; for (i=0 ; i= 0) { sceIoRead(fd, cs_data, 64); sceIoClose(fd); } cs_texture = GL_LoadTexture ("crosshair", 8, 8, cs_data, false, true); start = Hunk_LowMark(); // External conback texture support int cwidth, cheight; byte *data = Image_LoadImage ("gfx/conback", &cwidth, &cheight); if (data) { conback->width = cwidth; conback->height = cheight; ncdata = data; } else { cb = (qpic_t *)COM_LoadTempFile ("gfx/conback.lmp", NULL); if (!cb) Sys_Error ("Couldn't load gfx/conback.lmp"); SwapPic (cb); // hack the version number directly into the pic sprintf (ver, "(gl %4.2f) %4.2f", (float)GLQUAKE_VERSION, (float)VERSION); dest = cb->data + 320*186 + 320 - 11 - 8*strlen(ver); y = strlen(ver); for (x=0 ; xwidth = cb->width; conback->height = cb->height; ncdata = cb->data; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); // 13/02/2000 changed: M.Tretene glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); gl = (glpic_t *)conback->data; if (data) { gl->texnum = GL_LoadTexture32 ("conback", conback->width, conback->height, ncdata, false, true, false); free(data); } else gl->texnum = GL_LoadTexture ("conback", conback->width, conback->height, ncdata, false, true); // 30/01/2000 modified: M.Tretene gl->sl = 0; gl->sh = 1; gl->tl = 0; gl->th = 1; conback->width = vid.width; conback->height = vid.height; // free loaded console Hunk_FreeToLowMark(start); // save a texture slot for translated picture translate_texture = texture_extension_number++; // save slots for scraps scrap_texnum = texture_extension_number; texture_extension_number += MAX_SCRAPS; // // get the other pics we need // draw_disc = Draw_PicFromWad ("disc"); draw_backtile = Draw_PicFromWad ("backtile"); } void DrawQuad_NoTex(float x, float y, float w, float h, float r, float g, float b, float a) { gVertexBuffer[0] = gVertexBuffer[9] = x; gVertexBuffer[1] = gVertexBuffer[4] = y; gVertexBuffer[3] = gVertexBuffer[6] = x+w; gVertexBuffer[7] = gVertexBuffer[10] = y+h; gVertexBuffer[2] = gVertexBuffer[5] = gVertexBuffer[8] = gVertexBuffer[11] = 0.5f; float color[4] = {r,g,b,a}; GL_DisableState(GL_TEXTURE_COORD_ARRAY); vglVertexAttribPointerMapped(0, gVertexBuffer); gVertexBuffer += 12; glUniform4fv(monocolor, 1, color); GL_DrawPolygon(GL_TRIANGLE_FAN, 4); GL_EnableState(GL_TEXTURE_COORD_ARRAY); } void DrawQuad(float x, float y, float w, float h, float u, float v, float uw, float vh) { gTexCoordBuffer[0] = gTexCoordBuffer[6] = u; gTexCoordBuffer[1] = gTexCoordBuffer[3] = v; gTexCoordBuffer[2] = gTexCoordBuffer[4] = u+uw; gTexCoordBuffer[5] = gTexCoordBuffer[7] = v+vh; gVertexBuffer[0] = gVertexBuffer[9] = x; gVertexBuffer[1] = gVertexBuffer[4] = y; gVertexBuffer[3] = gVertexBuffer[6] = x+w; gVertexBuffer[7] = gVertexBuffer[10] = y+h; gVertexBuffer[2] = gVertexBuffer[5] = gVertexBuffer[8] = gVertexBuffer[11] = 0.5f; vglVertexAttribPointerMapped(0, gVertexBuffer); vglVertexAttribPointerMapped(1, gTexCoordBuffer); gVertexBuffer += 12; gTexCoordBuffer += 8; GL_DrawPolygon(GL_TRIANGLE_FAN, 4); } /* ================ Draw_Character Draws one 8*8 graphics character with 0 being transparent. It can be clipped to the top of the screen to allow the console to be smoothly scrolled off. ================ */ void Draw_Character (int x, int y, int num) { int row, col; float frow, fcol, size; if (num == 0x20) return; // space num &= 255; if (y <= -8) return; // totally off screen row = num>>4; col = num&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; GL_Bind (char_texture); DrawQuad(x, y, 8, 8, fcol, frow, size, size); } /* ================ Draw_String ================ */ void Draw_String (int x, int y, const char *str, int delta) { GL_Bind (char_texture); float *str_vbuffer = gVertexBuffer; float *str_tbuffer = gTexCoordBuffer; int num_vertices = 0; while (*str) { int num = (*str) + delta; int row, col; float frow, fcol, size; if (num != 0x20) { num &= 255; if (y > -8) { row = num>>4; col = num&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; gVertexBuffer[0] = gVertexBuffer[3] = gVertexBuffer[9] = x; gVertexBuffer[1] = gVertexBuffer[10] = gVertexBuffer[13] = y; gVertexBuffer[4] = gVertexBuffer[7] = gVertexBuffer[16] = y + 8; gVertexBuffer[6] = gVertexBuffer[12] = gVertexBuffer[15] = x + 8; gVertexBuffer[2] = gVertexBuffer[5] = gVertexBuffer[8] = gVertexBuffer[11] = gVertexBuffer[14] = gVertexBuffer[17] = 0.5f; gTexCoordBuffer[0] = gTexCoordBuffer[2] = gTexCoordBuffer[6] = fcol; gTexCoordBuffer[1] = gTexCoordBuffer[7] = gTexCoordBuffer[9] = frow; gTexCoordBuffer[3] = gTexCoordBuffer[5] = gTexCoordBuffer[11] = frow+size; gTexCoordBuffer[4] = gTexCoordBuffer[8] = gTexCoordBuffer[10] = fcol+size; gVertexBuffer += 18; gTexCoordBuffer += 12; num_vertices += 6; } } str++; x += 8; } if (num_vertices > 0) { vglVertexAttribPointerMapped(0, str_vbuffer); vglVertexAttribPointerMapped(1, str_tbuffer); GL_DrawPolygon(GL_TRIANGLES, num_vertices); } } /* ================ Batch_Character ================ */ int batched_vertices = 0; float *batched_vbuffer = NULL; float *batched_tbuffer = NULL; int is_batching = 0; void Batch_Character (int x, int y, int num) { if (!is_batching) { is_batching = 1; batched_vbuffer = gVertexBuffer; batched_tbuffer = gTexCoordBuffer; } int row, col; float frow, fcol, size; if (num == 0x20) return; // space num &= 255; if (y <= -8) return; // totally off screen row = num>>4; col = num&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; gVertexBuffer[0] = gVertexBuffer[3] = gVertexBuffer[9] = x; gVertexBuffer[1] = gVertexBuffer[10] = gVertexBuffer[13] = y; gVertexBuffer[4] = gVertexBuffer[7] = gVertexBuffer[16] = y + 8; gVertexBuffer[6] = gVertexBuffer[12] = gVertexBuffer[15] = x + 8; gVertexBuffer[2] = gVertexBuffer[5] = gVertexBuffer[8] = gVertexBuffer[11] = gVertexBuffer[14] = gVertexBuffer[17] = 0.5f; gTexCoordBuffer[0] = gTexCoordBuffer[2] = gTexCoordBuffer[6] = fcol; gTexCoordBuffer[1] = gTexCoordBuffer[7] = gTexCoordBuffer[9] = frow; gTexCoordBuffer[3] = gTexCoordBuffer[5] = gTexCoordBuffer[11] = frow+size; gTexCoordBuffer[4] = gTexCoordBuffer[8] = gTexCoordBuffer[10] = fcol+size; gVertexBuffer += 18; gTexCoordBuffer += 12; batched_vertices += 6; } /* ================ Batch_String ================ */ void Batch_String (int x, int y, const char *str, int delta) { if (!is_batching) { is_batching = 1; batched_vbuffer = gVertexBuffer; batched_tbuffer = gTexCoordBuffer; } while (*str) { int num = (*str) + delta; int row, col; float frow, fcol, size; if (num != 0x20) { num &= 255; if (y > -8) { row = num>>4; col = num&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; gVertexBuffer[0] = gVertexBuffer[3] = gVertexBuffer[9] = x; gVertexBuffer[1] = gVertexBuffer[10] = gVertexBuffer[13] = y; gVertexBuffer[4] = gVertexBuffer[7] = gVertexBuffer[16] = y + 8; gVertexBuffer[6] = gVertexBuffer[12] = gVertexBuffer[15] = x + 8; gVertexBuffer[2] = gVertexBuffer[5] = gVertexBuffer[8] = gVertexBuffer[11] = gVertexBuffer[14] = gVertexBuffer[17] = 0.5f; gTexCoordBuffer[0] = gTexCoordBuffer[2] = gTexCoordBuffer[6] = fcol; gTexCoordBuffer[1] = gTexCoordBuffer[7] = gTexCoordBuffer[9] = frow; gTexCoordBuffer[3] = gTexCoordBuffer[5] = gTexCoordBuffer[11] = frow+size; gTexCoordBuffer[4] = gTexCoordBuffer[8] = gTexCoordBuffer[10] = fcol+size; gVertexBuffer += 18; gTexCoordBuffer += 12; batched_vertices += 6; } } str++; x += 8; } } /* ================ Draw_Batched ================ */ void Draw_Batched() { if (batched_vertices > 0) { GL_Bind (char_texture); vglVertexAttribPointerMapped(0, batched_vbuffer); vglVertexAttribPointerMapped(1, batched_tbuffer); GL_DrawPolygon(GL_TRIANGLES, batched_vertices); batched_vertices = 0; } is_batching = 0; } /* ================ Draw_DebugChar Draws a single character directly to the upper right corner of the screen. This is for debugging lockups by drawing different chars in different parts of the code. ================ */ void Draw_DebugChar (signed char num) { } void Draw_Crosshair(void) { GL_SetCanvas (CANVAS_CROSSHAIR); if (crosshair.value == 2) { GL_EnableState(GL_MODULATE); GL_Bind (cs_texture); GL_Color(crosshaircolor_r.value, crosshaircolor_g.value, crosshaircolor_b.value, 1); DrawQuad(-6, -6, 12, 12, 0, 0, 1, 1); GL_EnableState(GL_REPLACE); } else if (crosshair.value) Draw_Character(-4, -4, '+'); } /* ============= Draw_AlphaPic ============= */ void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) { byte *dest, *source; unsigned short *pusdest; int v, u; glpic_t *gl; if (alpha == 0.0f) return; if (scrap_dirty) Scrap_Upload (); gl = (glpic_t *)pic->data; GL_DisableState(GL_ALPHA_TEST); GL_EnableState(GL_MODULATE); glEnable (GL_BLEND); GL_Color(1,1,1,alpha); GL_Bind (gl->texnum); DrawQuad(x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh - gl->sl, gl->th - gl->tl); GL_Color(1,1,1,1); GL_EnableState(GL_ALPHA_TEST); glDisable (GL_BLEND); glDepthMask(GL_TRUE); } /* ============= Draw_Pic ============= */ void Draw_Pic (int x, int y, qpic_t *pic) { byte *dest, *source; unsigned short *pusdest; int v, u; glpic_t *gl = (glpic_t *)pic->data; if (scrap_dirty) Scrap_Upload (); GL_Color(1, 1, 1, 1); GL_Bind (gl->texnum); DrawQuad(x, y, pic->width, pic->height, gl->sl, gl->tl, gl->sh - gl->sl, gl->th - gl->tl); } /* ============= Draw_TransPic ============= */ void Draw_TransPic (int x, int y, qpic_t *pic) { byte *dest, *source, tbyte; unsigned short *pusdest; int v, u; if (x < 0 || (unsigned)(x + pic->width) > vid.width || y < 0 || (unsigned)(y + pic->height) > vid.height) { Sys_Error ("Draw_TransPic: bad coordinates"); } Draw_Pic (x, y, pic); } /* ============= Draw_TransPicTranslate Only used for the player color selection menu ============= */ void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation) { int v, u, c; unsigned trans[64*64], *dest; byte *src; int p; GL_Bind (translate_texture); c = pic->width * pic->height; dest = trans; for (v=0 ; v<64 ; v++, dest += 64) { src = &menuplyr_pixels[ ((v*pic->height)>>6) *pic->width]; for (u=0 ; u<64 ; u++) { p = src[(u*pic->width)>>6]; if (p == 255) dest[u] = p; else dest[u] = d_8to24table[translation[p]]; } } glTexImage2D (GL_TEXTURE_2D, 0, gl_compress.value ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : gl_alpha_format, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL_Color(1,1,1,1); DrawQuad(x, y, pic->width, pic->height, 0, 0, 1, 1); } /* ================ Draw_ConsoleBackground ================ */ void Draw_ConsoleBackground (void) { GL_SetCanvas (CANVAS_CONSOLE); if (scr_conalpha.value > 0.0f) { if (scr_conalpha.value < 1.0f) Draw_Pic(0, 0, conback); else Draw_AlphaPic(0, 0, conback, scr_conalpha.value); } } /* ============= Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ typedef union ByteToInt_t { byte b[4]; int i; } ByteToInt; void Draw_TileClear (int x, int y, int w, int h) { GL_Color(1,1,1,1); ByteToInt b; memcpy(b.b, draw_backtile->data, sizeof(b.b)); GL_Bind (b.i); DrawQuad(x, y, w, h, x/64.0, y/64.0, w/64.0, h/64.0); } /* ============= Draw_Fill Fills a box of pixels with a single color ============= */ void Draw_Fill (int x, int y, int w, int h, int c) { DrawQuad_NoTex(x, y, w, h, host_basepal[c*3]/255.0, host_basepal[c*3+1]/255.0, host_basepal[c*3+2]/255.0, 1); GL_Color(1,1,1,1); } //============================================================================= /* ================ Draw_FadeScreen ================ */ void Draw_FadeScreen (void) { GL_SetCanvas (CANVAS_DEFAULT); glEnable (GL_BLEND); DrawQuad_NoTex(0, 0, vid.width, vid.height, 0, 0, 0, 0.8f); GL_Color(1,1,1,1); glDisable (GL_BLEND); Sbar_Changed(); } //============================================================================= /* ================ GL_Set2D Setup as if the screen was 320*200 ================ */ void GL_Set2D (void) { currentcanvas = -1; GL_SetCanvas (CANVAS_DEFAULT); glDisable (GL_DEPTH_TEST); glDisable (GL_CULL_FACE); glDisable (GL_BLEND); GL_EnableState(GL_ALPHA_TEST); GL_Color(1,1,1,1); } //==================================================================== /* ================ GL_ResampleTexture ================ */ void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) { int i, j; unsigned *inrow; unsigned frac, fracstep; fracstep = inwidth*0x10000/outwidth; for (i=0 ; i> 1; for (j=0 ; j>16]; frac += fracstep; out[j+1] = inrow[frac>>16]; frac += fracstep; out[j+2] = inrow[frac>>16]; frac += fracstep; out[j+3] = inrow[frac>>16]; frac += fracstep; } } } /* ================ GL_Resample8BitTexture -- JACK ================ */ void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) { int i, j; unsigned char *inrow; unsigned frac, fracstep; fracstep = inwidth*0x10000/outwidth; for (i=0 ; i> 1; for (j=0 ; j>16]; frac += fracstep; out[j+1] = inrow[frac>>16]; frac += fracstep; out[j+2] = inrow[frac>>16]; frac += fracstep; out[j+3] = inrow[frac>>16]; frac += fracstep; } } } /* ================ GL_MipMap Operates in place, quartering the size of the texture ================ */ void GL_MipMap (byte *in, int width, int height) { int i, j; byte *out; width <<=2; height >>= 1; out = in; for (i=0 ; i>2; out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2; out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2; out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2; } } } /* ================ GL_MipMap8Bit Mipping for 8 bit textures ================ */ void GL_MipMap8Bit (byte *in, int width, int height) { int i, j; unsigned short r,g,b; byte *out, *at1, *at2, *at3, *at4; // width <<=2; height >>= 1; out = in; for (i=0 ; i>=5; g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5; b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5; out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)]; } } } /* =============== GL_Upload32 =============== */ void GL_Upload32 (unsigned *data, int width, int height, bool mipmap, bool alpha) { if (!gl_mipmap.value) mipmap = false; int samples; static unsigned int scaled[2048*2048]; // [512*256]; int scaled_width, scaled_height; for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) ; for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) ; scaled_width >>= (int)gl_picmip.value; scaled_height >>= (int)gl_picmip.value; if (scaled_width > gl_max_size.value) scaled_width = gl_max_size.value; if (scaled_height > gl_max_size.value) scaled_height = gl_max_size.value; if (scaled_width * scaled_height > sizeof(scaled)/4) Sys_Error ("GL_LoadTexture: too big"); if (gl_compress.value) { samples = alpha ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; mipmap = false; // Compressed textures do not support mipmaps yet in vitaGL } else samples = alpha ? gl_alpha_format : gl_solid_format; texels += scaled_width * scaled_height; if (scaled_width == width && scaled_height == height) { if (!mipmap) { glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); goto done; } memcpy (scaled, data, width*height*4); } else GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height); glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); if (mipmap) glGenerateMipmap(GL_TEXTURE_2D); done: ; if (mipmap) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } /* =============== GL_Upload8 =============== */ void GL_Upload8 (byte *data, int width, int height, bool mipmap, bool alpha) { static unsigned trans[640*480]; // FIXME, temporary int i, s; bool noalpha; int p; s = width*height; // if there are no transparent pixels, make it a 3 component // texture even if it was specified as otherwise if (alpha) { noalpha = true; for (i=0 ; iidentifier)) { // FIXME: Caching is broken with external textures //if (width != glt->width || height != glt->height) // Sys_Error ("GL_LoadTexture32: cache mismatch for %s, expected: %ld, got %d", identifier, width, glt->width); return gltextures[i].texnum; } } } //else { // 13/02/2000 removed: M.Tretene glt = &gltextures[numgltextures]; numgltextures++; //} strcpy (glt->identifier, identifier); glt->texnum = texture_extension_number; glt->width = width; glt->height = height; glt->mipmap = mipmap; GL_Bind(texture_extension_number ); if (tex_cache) textureStore::get()->create(width, height, data, mipmap, alpha, false); else GL_Upload8 (data, width, height, mipmap, alpha); texture_extension_number++; return texture_extension_number-1; } /* ================ GL_LoadTexture32 ================ */ int GL_LoadTexture32 (const char *identifier, int width, int height, byte *data, bool mipmap, bool alpha, bool fullbright) { bool noalpha; int i, p, s; gltexture_t *glt; // see if the texture is already present if (identifier[0]) { for (i=0, glt=gltextures ; iidentifier)) { if (width != glt->width || height != glt->height) Sys_Error ("GL_LoadTexture32: cache mismatch for %s, expected: %ld, got %d", identifier, width, glt->width); return gltextures[i].texnum; } } } //else { // 13/02/2000 removed: M.Tretene glt = &gltextures[numgltextures]; numgltextures++; //} // Make black pixels transparent for *_luma textures if (fullbright) { int cnt = width * height * 4; for (i=0; iidentifier, identifier); glt->texnum = texture_extension_number; glt->width = width; glt->height = height; glt->mipmap = mipmap; GL_Bind(texture_extension_number ); if (tex_cache) textureStore::get()->create(width, height, data, mipmap, alpha, true); else GL_Upload32 ((unsigned*)data, width, height, mipmap, alpha); texture_extension_number++; return texture_extension_number-1; } /****************************************/ static GLenum oldtarget = 0; // KH // Benchmark int max_fps = 0; int average_fps = 0; // TODO: Add this int min_fps = 999; extern "C"{ extern bool bBenchmarkStarted; }; bool bBlinkBenchmark; void GL_DrawBenchmark(void) { static double lastframetime; double t; extern int fps_count; static int lastfps; int x, y; char st[80],st2[80],st3[80],st4[80]; t = Sys_FloatTime (); if ((t - lastframetime) >= 1.0) { lastfps = fps_count; fps_count = 0; lastframetime = t; bBlinkBenchmark = !bBlinkBenchmark; } sprintf(st, "Current: %3d", lastfps); if (bBenchmarkStarted) { if (lastfps > max_fps) max_fps = lastfps; if (lastfps < min_fps) min_fps = lastfps; sprintf(st2, " Max: %3d", max_fps); sprintf(st3, " Min: %3d", min_fps); // <-- Dat Result really feels cheated } x = 320 - strlen(st) * 8 - 16; GL_SetCanvas (CANVAS_TOPRIGHT); Batch_String(x, 2, st, 0); Batch_String(x, 10, st2, 0); Batch_String(x, 18, st3, 0); Draw_Batched(); if (bBlinkBenchmark) { // Neato messaji GL_SetCanvas (CANVAS_MENU); Draw_String(160 - 37 * 4, 200*0.25, "Benchmark in progress, please wait...", 0); } } void GL_DrawFPS(void){ extern cvar_t show_fps; static double lastframetime; double t; extern int fps_count; static int lastfps; int x; char st[80]; if (!show_fps.value) return; t = Sys_FloatTime (); if ((t - lastframetime) >= 1.0) { lastfps = fps_count; fps_count = 0; lastframetime = t; } sprintf(st, "%3d FPS", lastfps); x = 329 - strlen(st) * 8 - 16; GL_SetCanvas (CANVAS_TOPRIGHT); Draw_String(x, 2, st, 0); } void GL_SetCanvas (int newcanvas) { extern vrect_t scr_vrect; float s; int lines; if (newcanvas == currentcanvas) return; currentcanvas = newcanvas; glMatrixMode(GL_PROJECTION); glLoadIdentity (); switch(newcanvas) { case CANVAS_DEFAULT: glOrtho (0, glwidth, glheight, 0, -99999, 99999); glViewport (glx, gly, glwidth, glheight); break; case CANVAS_CONSOLE: lines = vid.conheight - (scr_con_current * vid.conheight / glheight); glOrtho (0, vid.conwidth, vid.conheight + lines, lines, -99999, 99999); glViewport (glx, gly, glwidth, glheight); break; case CANVAS_MENU: s = fmin((float)glwidth / 320.0, (float)glheight / 200.0); s = Q_CLAMP (1.0, scr_menuscale.value, s); // ericw -- doubled width to 640 to accommodate long keybindings glOrtho (0, 640, 200, 0, -99999, 99999); glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 640*s, 200*s); break; case CANVAS_SBAR: s = Q_CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); if (cl.gametype == GAME_DEATHMATCH) { glOrtho (0, glwidth / s, 48, 0, -99999, 99999); glViewport (glx, gly, glwidth, 48*s); } else { glOrtho (0, 320, 48, 0, -99999, 99999); glViewport (glx + (glwidth - 320*s) / 2, gly, 320*s, 48*s); } break; case CANVAS_CROSSHAIR: //0,0 is center of viewport s = Q_CLAMP (1.0, scr_crosshairscale.value, 10.0); glOrtho (scr_vrect.width/-2/s, scr_vrect.width/2/s, scr_vrect.height/2/s, scr_vrect.height/-2/s, -99999, 99999); glViewport (scr_vrect.x, glheight - scr_vrect.y - scr_vrect.height, scr_vrect.width & ~1, scr_vrect.height & ~1); break; case CANVAS_TOPRIGHT: //used by fps s = 1; glOrtho (0, 320, 200, 0, -99999, 99999); glViewport (glx+glwidth-320*s, gly+glheight-200*s, 320*s, 200*s); break; default: Sys_Error ("GL_SetCanvas: bad canvas type"); } glMatrixMode(GL_MODELVIEW); glLoadIdentity (); } ================================================ FILE: source/gl_fullbright.c ================================================ //gl_fullbright.c #include "quakedef.h" extern void DrawGLPoly (glpoly_t *p); int FindFullbrightTexture (byte *pixels, int num_pix) { int i; for (i = 0; i < num_pix; i++) if (pixels[i] > 223) return 1; return 0; } void ConvertPixels (byte *pixels, int num_pixels) { int i; for (i = 0; i < num_pixels; i++) if (pixels[i] < 224) pixels[i] = 255; } void DrawFullBrightTextures (msurface_t *first_surf, int num_surfs) { int i; msurface_t *fa; texture_t *t; if (r_fullbright.value) return; glEnable (GL_BLEND); for (fa = first_surf, i = 0; i < num_surfs; fa++, i++) { // find the correct texture t = R_TextureAnimation (fa->texinfo->texture); if (t->fullbright != -1 && fa->draw_this_frame) { if (t->luma) glBlendFunc (GL_ONE, GL_ONE); else glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_Bind (t->fullbright); DrawGLPoly (fa->polys); } fa->draw_this_frame = 0; } glDisable (GL_BLEND); glBlendFunc (GL_ZERO, GL_SRC_COLOR); } ================================================ FILE: source/gl_fullbright.h ================================================ //gl_fullbright.h #include "gl_model.h" int FindFullbrightTexture (byte *pixels, int num_pix); void DrawFullBrightTextures (msurface_t *first_surf, int num_surfs); void ConvertPixels (byte *pixels, int num_pixels); ================================================ FILE: source/gl_mesh.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // gl_mesh.c: triangle model functions #include "quakedef.h" /* ================================================================= ALIAS MODEL DISPLAY LIST GENERATION ================================================================= */ model_t *aliasmodel; aliashdr_t *paliashdr; int used[8192]; // the command list holds counts and s/t values that are valid for // every frame int commands[8192]; int numcommands; // all frames will have their vertexes rearranged and expanded // so they are in the order expected by the command list int vertexorder[8192]; int numorder; int allverts, alltris; int stripverts[128]; int striptris[128]; int stripcount; /* ================ StripLength ================ */ static int StripLength (int starttri, int startv) { int m1, m2; int j; mtriangle_t *last, *check; int k; used[starttri] = 2; last = &triangles[starttri]; stripverts[0] = last->vertindex[(startv)%3]; stripverts[1] = last->vertindex[(startv+1)%3]; stripverts[2] = last->vertindex[(startv+2)%3]; striptris[0] = starttri; stripcount = 1; m1 = last->vertindex[(startv+2)%3]; m2 = last->vertindex[(startv+1)%3]; // look for a matching triangle nexttri: for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k=0 ; k<3 ; k++) { if (check->vertindex[k] != m1) continue; if (check->vertindex[ (k+1)%3 ] != m2) continue; // this is the next part of the fan // if we can't use this triangle, this tristrip is done if (used[j]) goto done; // the new edge if (stripcount & 1) m2 = check->vertindex[ (k+2)%3 ]; else m1 = check->vertindex[ (k+2)%3 ]; stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; striptris[stripcount] = j; stripcount++; used[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j=starttri+1 ; jnumtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } /* =========== FanLength =========== */ static int FanLength (int starttri, int startv) { int m1, m2; int j; mtriangle_t *last, *check; int k; used[starttri] = 2; last = &triangles[starttri]; stripverts[0] = last->vertindex[(startv)%3]; stripverts[1] = last->vertindex[(startv+1)%3]; stripverts[2] = last->vertindex[(startv+2)%3]; striptris[0] = starttri; stripcount = 1; m1 = last->vertindex[(startv+0)%3]; m2 = last->vertindex[(startv+2)%3]; // look for a matching triangle nexttri: for (j=starttri+1, check=&triangles[starttri+1] ; jnumtris ; j++, check++) { if (check->facesfront != last->facesfront) continue; for (k=0 ; k<3 ; k++) { if (check->vertindex[k] != m1) continue; if (check->vertindex[ (k+1)%3 ] != m2) continue; // this is the next part of the fan // if we can't use this triangle, this tristrip is done if (used[j]) goto done; // the new edge m2 = check->vertindex[ (k+2)%3 ]; stripverts[stripcount+2] = m2; striptris[stripcount] = j; stripcount++; used[j] = 2; goto nexttri; } } done: // clear the temp used flags for (j=starttri+1 ; jnumtris ; j++) if (used[j] == 2) used[j] = 0; return stripcount; } /* ================ BuildTris Generate a list of trifans or strips for the model, which holds for all frames ================ */ static void BuildTris (void) { int i, j, k; int startv; float s, t; int len, bestlen, besttype; int bestverts[1024]; int besttris[1024]; int type; // // build tristrips // besttype = 0; numorder = 0; numcommands = 0; memset (used, 0, sizeof(used)); for (i=0 ; inumtris ; i++) { // pick an unused triangle and start the trifan if (used[i]) continue; bestlen = 0; for (type = 0 ; type < 2 ; type++) // type = 1; { for (startv =0 ; startv < 3 ; startv++) { if (type == 1) len = StripLength (i, startv); else len = FanLength (i, startv); if (len > bestlen) { besttype = type; bestlen = len; for (j=0 ; jskinwidth / 2; // on back side s = (s + 0.5) / pheader->skinwidth; t = (t + 0.5) / pheader->skinheight; *(float *)&commands[numcommands++] = s; *(float *)&commands[numcommands++] = t; } } commands[numcommands++] = 0; // end of list marker Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands); allverts += numorder; alltris += pheader->numtris; } /* ================ GL_MakeAliasModelDisplayLists ================ */ void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr) { int i,j; aliasmodel = m; paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); BuildTris (); // trifans or lists // save the data out paliashdr->poseverts = numorder; int* cmds = (int*)Hunk_Alloc (numcommands * 4); paliashdr->commands = (byte *)cmds - (byte *)paliashdr; memcpy (cmds, commands, numcommands * 4); trivertx_t* verts = (trivertx_t*)Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t) ); paliashdr->posedata = (byte *)verts - (byte *)paliashdr; for (i=0 ; inumposes ; i++) for (j=0 ; jcache); if (r) return r; Mod_LoadModel (mod, true); if (!mod->cache.data) Sys_Error ("Mod_Extradata: caching failed"); return mod->cache.data; } /* =============== Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) { mnode_t *node; float d; mplane_t *plane; if (!model || !model->nodes) Sys_Error ("Mod_PointInLeaf: bad model"); node = model->nodes; while (1) { if (node->contents < 0) return (mleaf_t *)node; plane = node->plane; d = DotProduct (p,plane->normal) - plane->dist; if (d > 0) node = node->children[0]; else node = node->children[1]; } return NULL; // never reached } /* =================== Mod_DecompressVis =================== */ byte *Mod_DecompressVis (byte *in, model_t *model) { static byte decompressed[MAX_MAP_LEAFS/8]; int c; byte *out; int row; row = (model->numleafs+7)>>3; out = decompressed; if (!in) { // no vis info, so make all visible while (row) { *out++ = 0xff; row--; } return decompressed; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - decompressed < row); return decompressed; } byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) { if (leaf == model->leafs) return mod_novis; return Mod_DecompressVis (leaf->compressed_vis, model); } /* =================== Mod_ClearAll =================== */ static byte *ent_file = NULL; void Mod_ClearAll (void) { int i; model_t *mod; for (i=0 , mod=mod_known ; itype != mod_alias) mod->needload = true; ent_file = NULL; } void Mod_ResetAll (void) { int i; model_t *mod; for (i=0 , mod=mod_known ; iname, name) ) break; if (i == mod_numknown) { if (mod_numknown == MAX_MOD_KNOWN) Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); strcpy (mod->name, name); mod->needload = true; mod_numknown++; } return mod; } /* ================== Mod_TouchModel ================== */ void Mod_TouchModel (char *name) { model_t *mod; mod = Mod_FindName (name); if (!mod->needload) { if (mod->type == mod_alias) Cache_Check (&mod->cache); } } /* ================== Mod_LoadModel Loads a model into the cache ================== */ model_t *Mod_LoadModel (model_t *mod, bool crash) { void *d; unsigned *buf; byte stackbuf[1024]; // avoid dirtying the cache heap if (!mod->needload) { if (mod->type == mod_alias) { d = Cache_Check (&mod->cache); if (d) return mod; } else return mod; // not cached at all } // // because the world is so huge, load it one piece at a time // if (!crash) { } // // load the file // buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), &mod->path_id); if (!buf) { if (crash) Sys_Error ("Mod_NumForName: %s not found", mod->name); return NULL; } // // allocate a new model // COM_FileBase (mod->name, loadname); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = false; switch (LittleLong(*(unsigned *)buf)) { case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel (mod, buf); break; default: Mod_LoadBrushModel (mod, buf); break; } return mod; } /* ================== Mod_ForName Loads in a model for the given name ================== */ model_t *Mod_ForName (char *name, bool crash) { model_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ byte *mod_base; /* ================= Mod_LoadTextures ================= */ void Mod_LoadTextures (lump_t *l) { int i, j, pixels, num, max, altmax, fwidth, fheight; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; char filename[MAX_OSPATH], filename2[MAX_OSPATH], mapname[MAX_OSPATH]; if (!l->filelen) { loadmodel->textures = NULL; return; } m = (dmiptexlump_t *)(mod_base + l->fileofs); m->nummiptex = LittleLong (m->nummiptex); loadmodel->numtextures = m->nummiptex; loadmodel->textures = (texture_t**)Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); for (i=0 ; inummiptex ; i++) { m->dataofs[i] = LittleLong(m->dataofs[i]); if (m->dataofs[i] == -1) continue; mt = (miptex_t *)((byte *)m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); if ( (mt->width & 15) || (mt->height & 15) ) Sys_Error ("Texture %s is not 16 aligned", mt->name); pixels = mt->width*mt->height/64*85; tx = (texture_t*)Hunk_AllocName (sizeof(texture_t), loadname ); loadmodel->textures[i] = tx; memcpy (tx->name, mt->name, sizeof(tx->name)); tx->width = mt->width; tx->height = mt->height; for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); // ericw -- check for pixels extending past the end of the lump. // appears in the wild; e.g. jam2_tronyn.bsp (func_mapjam2), // kellbase1.bsp (quoth), and can lead to a segfault if we read past // the end of the .bsp file buffer if (((byte*)(mt+1) + pixels) > (mod_base + l->fileofs + l->filelen)) { Con_DPrintf("Texture %s extends past end of lump\n", mt->name); pixels = max(0, (mod_base + l->fileofs + l->filelen) - (byte*)(mt+1)); } if (loadmodel->bspversion != HL_BSPVERSION && (!strncmp(mt->name,"sky",3))) { R_InitSky (mt); continue; } if (loadmodel->bspversion == HL_BSPVERSION) { byte *data; if ((data = WAD3_LoadTexture(mt))) { //com_netpath[0] = 0; //alpha_flag = ISALPHATEX(tx->name) ? TEX_ALPHA : 0; texture_mode = GL_LINEAR; tx->gl_texturenum = GL_LoadTexture32 (mt->name, tx->width, tx->height, data, true, false, false); tx->fullbright = -1; free(data); continue; } } // External textures support for regular textures tx->fullbright = -1; tx->gl_texturenum = -1; tx->luma = false; COM_StripExtension (loadmodel->name + 5, mapname); snprintf(filename, sizeof(filename), "textures/%s/%s%s", mapname, tx->name[0] == '*' ? "#" : "", tx->name); byte *data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) { snprintf(filename, sizeof(filename), "textures/%s%s", tx->name[0] == '*' ? "#" : "", tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) { snprintf(filename, sizeof(filename), "textures/bmodels/%s%s", tx->name[0] == '*' ? "#" : "", tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); } } if (data) { tx->gl_texturenum = GL_LoadTexture32 (mt->name, fwidth, fheight, data, true, tx->name[0] == '{', false); free(data); // Fullbright snprintf (filename2, sizeof(filename2), "%s_luma", filename); data = Image_LoadImage (filename2, &fwidth, &fheight); if (!data) { snprintf (filename2, sizeof(filename2), "%s_glow", filename); data = Image_LoadImage (filename2, &fwidth, &fheight); } if (data) { // get a new name for the fullbright mask to avoid cache mismatches sprintf (fbr_mask_name, "fullbright_mask_%s", mt->name); // load the fullbright pixels version of the texture tx->fullbright = GL_LoadTexture32 (fbr_mask_name, fwidth, fheight, data, true, tx->name[0] == '{', true); tx->luma = true; free(data); } } // Fallback to original textures if (tx->gl_texturenum == -1) { tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(mt+1), true, false); if (FindFullbrightTexture ((byte *)(mt+1), pixels)) { // convert any non fullbright pixel to fully transparent ConvertPixels ((byte *)(mt+1), pixels); // get a new name for the fullbright mask to avoid cache mismatches sprintf (fbr_mask_name, "fullbright_mask_%s", mt->name); // load the fullbright pixels version of the texture tx->fullbright = GL_LoadTexture (fbr_mask_name, tx->width, tx->height, (byte *)(mt + 1), true, true); } } } // // sequence the animations // for (i=0 ; inummiptex ; i++) { tx = loadmodel->textures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) continue; // allready sequenced // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); max = tx->name[1]; altmax = 0; if (max >= 'a' && max <= 'z') max -= 'a' - 'A'; if (max >= '0' && max <= '9') { max -= '0'; altmax = 0; anims[max] = tx; max++; } else if (max >= 'A' && max <= 'J') { altmax = max - 'A'; max = 0; altanims[altmax] = tx; altmax++; } else Sys_Error ("Bad animating texture %s", tx->name); for (j=i+1 ; jnummiptex ; j++) { tx2 = loadmodel->textures[j]; if (!tx2 || tx2->name[0] != '+') continue; if (strcmp (tx2->name+2, tx->name+2)) continue; num = tx2->name[1]; if (num >= 'a' && num <= 'z') num -= 'a' - 'A'; if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; if (num+1 > max) max = num + 1; } else if (num >= 'A' && num <= 'J') { num = num - 'A'; altanims[num] = tx2; if (num+1 > altmax) altmax = num+1; } else Sys_Error ("Bad animating texture %s", tx->name); } #define ANIM_CYCLE 2 // link them all together for (j=0 ; jname); tx2->anim_total = max * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = anims[ (j+1)%max ]; if (altmax) tx2->alternate_anims = altanims[0]; } for (j=0 ; jname); tx2->anim_total = altmax * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = altanims[ (j+1)%altmax ]; if (max) tx2->alternate_anims = anims[0]; } } } void Mod_LoadLighting (lump_t *l) { unsigned int path_id; loadmodel->lightdata = NULL; // Half-Life models support if (loadmodel->bspversion == HL_BSPVERSION) { if (!l->filelen) return; loadmodel->lightdata = Hunk_AllocName(l->filelen, loadname); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); return; } // LordHavoc: .lit support begin int i; byte *in, *out, *data; byte d; char litfilename[1024]; // LordHavoc: check for a .lit file strcpy(litfilename, loadmodel->name); COM_StripExtension(litfilename, litfilename); strcat(litfilename, ".lit"); data = (byte*) COM_LoadHunkFile (litfilename, &path_id); if (data && (path_id >= loadmodel->path_id)) { if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') { i = LittleLong(((int *)data)[1]); if (i == 1) { Con_DPrintf("%s loaded", litfilename); loadmodel->lightdata = data + 8; return; } else Con_Printf("Unknown .lit file version (%d)\n", i); } else Con_Printf("Corrupt .lit file (old version?), ignoring\n"); } if (!l->filelen) return; // LordHavoc: no .lit found, expand the white lighting data to color loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, litfilename); in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write out = loadmodel->lightdata; memcpy (in, mod_base + l->fileofs, l->filelen); for (i = 0;i < l->filelen;i++) { d = *in++; *out++ = d; *out++ = d; *out++ = d; } // LordHavoc: .lit support end } /* ================= Mod_LoadVisibility ================= */ void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = (byte*)Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_ParseWadsFromEntityLump For Half-life maps ================= */ static void Mod_ParseWadsFromEntityLump(char *data) { char *s, key[1024], value[1024]; int i, j, k; if (!data || !(data = COM_Parse(data))) return; if (com_token[0] != '{') return; // error while (1) { if (!(data = COM_Parse(data))) return; // error if (com_token[0] == '}') break; // end of worldspawn strncpyz(key, (com_token[0] == '_') ? com_token + 1 : com_token, sizeof(key)); for (s = key + strlen(key) - 1; s >= key && *s == ' '; s--) // remove trailing spaces *s = 0; if (!(data = COM_Parse(data))) return; // error strncpyz(value, com_token, sizeof(value)); if (!strcmp("MaxRange", key)) Cvar_Set("r_maxrange", value); if (!strcmp("wad", key)) { j = 0; for (i = 0; i < strlen(value); i++) { if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':') break; } if (!value[i]) continue; for ( ; i < sizeof(value); i++) { // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'... if (value[i] == '\\' || value[i] == '/' || value[i] == ':') { j = i + 1; } else if (value[i] == ';' || value[i] == 0) { k = value[i]; value[i] = 0; if (value[j]) WAD3_LoadTextureWadFile (value + j); j = i + 1; if (!k) break; } } } } } /* ================= Mod_LoadEntities ================= */ void Mod_LoadEntities (lump_t *l) { char entfilename[128]; unsigned int path_id; loadmodel->entities = NULL; strcpy(entfilename, loadmodel->name); COM_StripExtension(entfilename, entfilename); strcat(entfilename, ".ent"); ent_file = (byte*) COM_LoadHunkFile (entfilename, &path_id); if (ent_file && (path_id >= loadmodel->path_id)) { if (ent_file[0] == '{') { Con_DPrintf("%s loaded", entfilename); loadmodel->entities = (char*)ent_file; return; } else Con_Printf("Corrupt .ent file, ignoring\n"); } if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = (signed char*)Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); if (loadmodel->bspversion == HL_BSPVERSION) Mod_ParseWadsFromEntityLump(loadmodel->entities); } /* ================= Mod_LoadVertexes ================= */ void Mod_LoadVertexes (lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (dvertex_t*)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mvertex_t*)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->vertexes = out; loadmodel->numvertexes = count; for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); out->position[1] = LittleFloat (in->point[1]); out->position[2] = LittleFloat (in->point[2]); } } /* ================= Mod_LoadSubmodels ================= */ void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (dmodel_t*)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (dmodel_t*)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->submodels = out; loadmodel->numsubmodels = count; for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; out->origin[j] = LittleFloat (in->origin[j]); } for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); out->visleafs = LittleLong (in->visleafs); out->firstface = LittleLong (in->firstface); out->numfaces = LittleLong (in->numfaces); } } /* ================= Mod_LoadEdges ================= */ void Mod_LoadEdges (lump_t *l, int bsp2) { medge_t *out; int i, count; if (bsp2) { dledge_t *in = (dledge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t*) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); loadmodel->edges = out; loadmodel->numedges = count; for ( i=0 ; iv[0] = LittleLong(in->v[0]); out->v[1] = LittleLong(in->v[1]); } } else { dsedge_t *in = (dsedge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); loadmodel->edges = out; loadmodel->numedges = count; for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } } /* ================= Mod_LoadTexinfo ================= */ void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; int i, j, count; int miptex; float len1, len2; in = (texinfo_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mtexinfo_t*)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); out->vecs[1][j] = LittleFloat (in->vecs[1][j]); } len1 = Length (out->vecs[0]); len2 = Length (out->vecs[1]); len1 = (len1 + len2)/2; if (len1 < 0.32) out->mipadjust = 4; else if (len1 < 0.49) out->mipadjust = 3; else if (len1 < 0.99) out->mipadjust = 2; else out->mipadjust = 1; miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); if (!loadmodel->textures) { out->texture = r_notexture_mip; // checkerboard texture out->flags = 0; } else { if (miptex >= loadmodel->numtextures) Sys_Error ("miptex >= loadmodel->numtextures"); out->texture = loadmodel->textures[miptex]; if (!out->texture) { out->texture = r_notexture_mip; // texture not found out->flags = 0; } } } } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ void CalcSurfaceExtents (msurface_t *s) { float mins[2], maxs[2], val; int i,j, e; mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; tex = s->texinfo; for (i=0 ; inumedges ; i++) { e = loadmodel->surfedges[s->firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; for (j=0 ; j<2 ; j++) { val = v->position[0] * tex->vecs[j][0] + v->position[1] * tex->vecs[j][1] + v->position[2] * tex->vecs[j][2] + tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (i=0 ; i<2 ; i++) { bmins[i] = (int)floorf(mins[i]/16); bmaxs[i] = (int)ceilf(maxs[i]/16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 /* 256 */ ) Sys_Error ("Bad surface extents"); } } /* ================= Mod_LoadFaces ================= */ void Mod_LoadFaces (lump_t *l, int bsp2) { dsface_t *ins; dlface_t *inl; msurface_t *out; int i, count, surfnum, lofs; int planenum, side, texinfon; if (bsp2) { ins = NULL; inl = (dlface_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*inl)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*inl); } else { ins = (dsface_t *)(mod_base + l->fileofs); inl = NULL; if (l->filelen % sizeof(*ins)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*ins); } out = (msurface_t *)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->surfaces = out; loadmodel->numsurfaces = count; for ( surfnum=0 ; surfnumfirstedge = LittleLong(inl->firstedge); out->numedges = LittleLong(inl->numedges); planenum = LittleLong(inl->planenum); side = LittleLong(inl->side); texinfon = LittleLong (inl->texinfo); for (i=0 ; istyles[i] = inl->styles[i]; lofs = LittleLong(inl->lightofs); inl++; } else { out->firstedge = LittleLong(ins->firstedge); out->numedges = LittleShort(ins->numedges); planenum = LittleShort(ins->planenum); side = LittleShort(ins->side); texinfon = LittleShort (ins->texinfo); for (i=0 ; istyles[i] = ins->styles[i]; lofs = LittleLong(ins->lightofs); ins++; } out->flags = 0; if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + texinfon; CalcSurfaceExtents (out); // lighting info if (lofs == -1) out->samples = NULL; else out->samples = loadmodel->lightdata + (loadmodel->bspversion == HL_BSPVERSION ? lofs : lofs * 3); // LordHavoc // set the drawing flags flag if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky { out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); #ifndef QUAKE2 GL_SubdivideSurface (out); // cut up polygon for warps #endif continue; } if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent { out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); for (i=0 ; i<2 ; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } GL_SubdivideSurface (out); // cut up polygon for warps continue; } } } /* ================= Mod_SetParent ================= */ void Mod_SetParent (mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents < 0) return; Mod_SetParent (node->children[0], node); Mod_SetParent (node->children[1], node); } /* ================= Mod_LoadNodes ================= */ void Mod_LoadNodes_S (lump_t *l) { int i, j, count, p; dsnode_t *in; mnode_t *out; in = (dsnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t*)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->nodes = out; loadmodel->numnodes = count; for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleShort (in->firstface); out->numsurfaces = LittleShort (in->numfaces); for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = (unsigned short)LittleShort(in->children[j]); if (p < count) out->children[j] = loadmodel->nodes + p; else { p = 65535 - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } } void Mod_LoadNodes_L1 (lump_t *l) { int i, j, count, p; dl1node_t *in; mnode_t *out; in = (dl1node_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("Mod_LoadNodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->nodes = out; loadmodel->numnodes = count; for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); out->numsurfaces = LittleLong (in->numfaces); for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = LittleLong(in->children[j]); if (p >= 0 && p < count) out->children[j] = loadmodel->nodes + p; else { p = 0xffffffff - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p >= 0 && p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } } void Mod_LoadNodes_L2 (lump_t *l) { int i, j, count, p; dl2node_t *in; mnode_t *out; in = (dl2node_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("Mod_LoadNodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->nodes = out; loadmodel->numnodes = count; for (i=0 ; iminmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = LittleLong (in->firstface); //johnfitz -- explicit cast as unsigned short out->numsurfaces = LittleLong (in->numfaces); //johnfitz -- explicit cast as unsigned short for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = LittleLong(in->children[j]); if (p > 0 && p < count) out->children[j] = loadmodel->nodes + p; else { p = 0xffffffff - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p >= 0 && p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } } void Mod_LoadNodes (lump_t *l, int bsp2) { if (bsp2 == 2) Mod_LoadNodes_L2(l); else if (bsp2) Mod_LoadNodes_L1(l); else Mod_LoadNodes_S(l); Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } /* ================= Mod_ProcessLeafs ================= */ void Mod_ProcessLeafs_S (dsleaf_t *in, int filelen) { mleaf_t *out; int i, j, count, p; if (filelen % sizeof(*in)) Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name); count = filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz if (count > 32767) Host_Error ("Mod_LoadLeafs: %i leafs exceeds limit of 32767.\n", count); //johnfitz loadmodel->leafs = out; loadmodel->numleafs = count; for (i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + (unsigned short)LittleShort(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces); //johnfitz -- unsigned short p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; if (out->contents != CONTENTS_EMPTY) { for (j=0 ; jnummarksurfaces ; j++) out->firstmarksurface[j]->flags |= SURF_UNDERWATER; } } } void Mod_ProcessLeafs_L1 (dl1leaf_t *in, int filelen) { mleaf_t *out; int i, j, count, p; if (filelen % sizeof(*in)) Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name); count = filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->leafs = out; loadmodel->numleafs = count; for (i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- unsigned short p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; if (out->contents != CONTENTS_EMPTY) { for (j=0 ; jnummarksurfaces ; j++) out->firstmarksurface[j]->flags |= SURF_UNDERWATER; } } } void Mod_ProcessLeafs_L2 (dl2leaf_t *in, int filelen) { mleaf_t *out; int i, j, count, p; if (filelen % sizeof(*in)) Sys_Error ("Mod_ProcessLeafs: funny lump size in %s", loadmodel->name); count = filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->leafs = out; loadmodel->numleafs = count; for (i=0 ; iminmaxs[j] = LittleFloat (in->mins[j]); out->minmaxs[3+j] = LittleFloat (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + LittleLong(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = LittleLong(in->nummarksurfaces); //johnfitz -- unsigned short p = LittleLong(in->visofs); if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) out->ambient_sound_level[j] = in->ambient_level[j]; if (out->contents != CONTENTS_EMPTY) { for (j=0 ; jnummarksurfaces ; j++) out->firstmarksurface[j]->flags |= SURF_UNDERWATER; } } } /* ================= Mod_LoadLeafs ================= */ void Mod_LoadLeafs (lump_t *l, int bsp2) { void *in = (void *)(mod_base + l->fileofs); if (bsp2 == 2) Mod_ProcessLeafs_L2 ((dl2leaf_t *)in, l->filelen); else if (bsp2) Mod_ProcessLeafs_L1 ((dl1leaf_t *)in, l->filelen); else Mod_ProcessLeafs_S ((dsleaf_t *) in, l->filelen); } /* ================= Mod_LoadClipnodes ================= */ void Mod_LoadClipnodes (lump_t *l, int bsp2) { dsclipnode_t *ins; dlclipnode_t *inl; mclipnode_t *out; int i, count; hull_t *hull; if (bsp2) { ins = NULL; inl = (dlclipnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*inl)) Sys_Error ("Mod_LoadClipnodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*inl); } else { ins = (dsclipnode_t *)(mod_base + l->fileofs); inl = NULL; if (l->filelen % sizeof(*ins)) Sys_Error ("Mod_LoadClipnodes: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*ins); } out = (mclipnode_t*)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; if (loadmodel->bspversion == HL_BSPVERSION) { hull = &loadmodel->hulls[1]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -36; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 36; hull = &loadmodel->hulls[2]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -32; hull->clip_mins[1] = -32; hull->clip_mins[2] = -32; hull->clip_maxs[0] = 32; hull->clip_maxs[1] = 32; hull->clip_maxs[2] = 32; hull = &loadmodel->hulls[3]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -18; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 18; } else { hull = &loadmodel->hulls[1]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -16; hull->clip_mins[1] = -16; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 16; hull->clip_maxs[1] = 16; hull->clip_maxs[2] = 32; hull = &loadmodel->hulls[2]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; hull->clip_mins[0] = -32; hull->clip_mins[1] = -32; hull->clip_mins[2] = -24; hull->clip_maxs[0] = 32; hull->clip_maxs[1] = 32; hull->clip_maxs[2] = 64; } if (bsp2) { for (i=0 ; iplanenum = LittleLong(inl->planenum); //johnfitz -- bounds check if (out->planenum < 0 || out->planenum >= loadmodel->numplanes) Host_Error ("Mod_LoadClipnodes: planenum out of bounds"); //johnfitz out->children[0] = LittleLong(inl->children[0]); out->children[1] = LittleLong(inl->children[1]); } } else { for (i=0 ; iplanenum = LittleLong(ins->planenum); //johnfitz -- bounds check if (out->planenum < 0 || out->planenum >= loadmodel->numplanes) Host_Error ("Mod_LoadClipnodes: planenum out of bounds"); //johnfitz //johnfitz -- support clipnodes > 32k out->children[0] = (unsigned short)LittleShort(ins->children[0]); out->children[1] = (unsigned short)LittleShort(ins->children[1]); if (out->children[0] >= count) out->children[0] -= 65536; if (out->children[1] >= count) out->children[1] -= 65536; //johnfitz } } } /* ================= Mod_MakeHull0 Deplicate the drawing hull structure as a clipping hull ================= */ void Mod_MakeHull0 (void) { mnode_t *in, *child; mclipnode_t *out; int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = (mclipnode_t*)Hunk_AllocName ( count*sizeof(*out), loadname); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i=0 ; iplanenum = in->plane - loadmodel->planes; for (j=0 ; j<2 ; j++) { child = in->children[j]; if (child->contents < 0) out->children[j] = child->contents; else out->children[j] = child - loadmodel->nodes; } } } /* ================= Mod_LoadMarksurfaces ================= */ void Mod_LoadMarksurfaces (lump_t *l, int bsp2) { int i, j, count; msurface_t **out; if (bsp2) { unsigned int *in = (unsigned int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i=0 ; i= loadmodel->numsurfaces) Host_Error ("Mod_LoadMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } } else { short *in = (short *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("Mod_LoadMarksurfaces: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i=0 ; i= loadmodel->numsurfaces) Sys_Error ("Mod_LoadMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } } } /* ================= Mod_LoadSurfedges ================= */ void Mod_LoadSurfedges (lump_t *l) { int i, count; int *in, *out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (int*)Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for ( i=0 ; ifileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mplane_t*)Hunk_AllocName ( count*2*sizeof(*out), loadname); loadmodel->planes = out; loadmodel->numplanes = count; for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= RadiusFromBounds ================= */ float RadiusFromBounds (vec3_t mins, vec3_t maxs) { int i; vec3_t corner; for (i=0 ; i<3 ; i++) { corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); } return Length (corner); } /* ================= Mod_LoadBrushModel ================= */ void Mod_LoadBrushModel (model_t *mod, void *buffer) { int i, j; int bsp2; dheader_t *header; dmodel_t *bm; loadmodel->type = mod_brush; header = (dheader_t *)buffer; mod->bspversion = LittleLong (header->version); switch (mod->bspversion) { case Q1_BSPVERSION: case HL_BSPVERSION: bsp2 = 0; break; case BSP2VERSION_2PSB: bsp2 = 1; break; case BSP2VERSION_BSP2: bsp2 = 2; break; default: Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or %i (Half-Life))", mod->name, mod->bspversion, Q1_BSPVERSION, HL_BSPVERSION); break; } // swap all the lumps mod_base = (byte *)header; for (i=0 ; i<(int)(sizeof(dheader_t)/4) ; i++) ((int *)header)[i] = LittleLong ( ((int *)header)[i]); // load into heap Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES], bsp2); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces (&header->lumps[LUMP_FACES], bsp2); Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES], bsp2); Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS], bsp2); Mod_LoadNodes (&header->lumps[LUMP_NODES], bsp2); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES], bsp2); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // for (i=0 ; inumsubmodels ; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes-1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; sprintf (name, "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } } /* ============================================================================== ALIAS MODELS ============================================================================== */ aliashdr_t *pheader; stvert_t stverts[MAXALIASVERTS]; mtriangle_t triangles[MAXALIASTRIS]; // a pose is a single set of vertexes. a frame may be // an animating sequence of poses trivertx_t *poseverts[MAXALIASFRAMES]; int posenum; byte **player_8bit_texels_tbl; byte *player_8bit_texels; /* ================= Mod_LoadAliasFrame ================= */ void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) { trivertx_t *pframe, *pinframe; int i, j; daliasframe_t *pdaliasframe; pdaliasframe = (daliasframe_t *)pin; strcpy (frame->name, pdaliasframe->name); frame->firstpose = posenum; frame->numposes = 1; for (i=0 ; i<3 ; i++) { // these are byte values, so we don't have to worry about // endianness frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; frame->bboxmin.v[i] = pdaliasframe->bboxmax.v[i]; } pinframe = (trivertx_t *)(pdaliasframe + 1); poseverts[posenum] = pinframe; posenum++; pinframe += pheader->numverts; return (void *)pinframe; } /* ================= Mod_LoadAliasGroup ================= */ void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) { daliasgroup_t *pingroup; int i, numframes; daliasinterval_t *pin_intervals; void *ptemp; pingroup = (daliasgroup_t *)pin; numframes = LittleLong (pingroup->numframes); frame->firstpose = posenum; frame->numposes = numframes; for (i=0 ; i<3 ; i++) { // these are byte values, so we don't have to worry about endianness frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; frame->bboxmin.v[i] = pingroup->bboxmax.v[i]; } pin_intervals = (daliasinterval_t *)(pingroup + 1); frame->interval = LittleFloat (pin_intervals->interval); pin_intervals += numframes; ptemp = (void *)pin_intervals; for (i=0 ; inumverts; } return ptemp; } //========================================================= /* ================= Mod_FloodFillSkin Fill background pixels so mipmapping doesn't have haloes - Ed ================= */ typedef struct { short x, y; } floodfill_t; extern unsigned d_8to24table[]; // must be a power of 2 #define FLOODFILL_FIFO_SIZE 0x1000 #define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) #define FLOODFILL_STEP( off, dx, dy ) \ { \ if (pos[off] == fillcolor) \ { \ pos[off] = 255; \ fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ } \ else if (pos[off] != 255) fdc = pos[off]; \ } void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) { byte fillcolor = *skin; // assume this is the pixel to fill floodfill_t fifo[FLOODFILL_FIFO_SIZE]; int inpt = 0, outpt = 0; int filledcolor = -1; int i; if (filledcolor == -1) { filledcolor = 0; // attempt to find opaque black for (i = 0; i < 256; ++i) if (d_8to24table[i] == (255 << 0)) // alpha 1.0 { filledcolor = i; break; } } // can't fill to filled color or to transparent color (used as visited marker) if ((fillcolor == filledcolor) || (fillcolor == 255)) { //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); return; } fifo[inpt].x = 0, fifo[inpt].y = 0; inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; while (outpt != inpt) { int x = fifo[outpt].x, y = fifo[outpt].y; int fdc = filledcolor; byte *pos = &skin[x + skinwidth * y]; outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); skin[x + skinwidth * y] = fdc; } } static int Mod_LoadExternalSkin(char *identifier) { int w, h; byte *data = Image_LoadImage (identifier, &w, &h); if (data) { int r = GL_LoadTexture32 (identifier, w, h, data, false, false, false); free(data); return r; } return -1; } /* =============== Mod_LoadAllSkins =============== */ void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) { int i, j, k; char name[32]; int s, texnum; byte *copy; byte *skin; byte *texels; daliasskingroup_t *pinskingroup; int groupskins; daliasskininterval_t *pinskinintervals; skin = (byte *)(pskintype + 1); if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); s = pheader->skinwidth * pheader->skinheight; for (i=0 ; itype == ALIAS_SKIN_SINGLE) { Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); // save 8 bit texels for the player model to remap if (!strcmp(loadmodel->name,"progs/player.mdl")) { texels = Hunk_AllocName(s, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype + 1), s); } sprintf (name, "%s_%i", loadmodel->name, i); texnum = Mod_LoadExternalSkin(name); pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = texnum != -1 ? texnum : GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), true, false); pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); } else { // animating skin group. yuck. pskintype++; pinskingroup = (daliasskingroup_t *)pskintype; groupskins = LittleLong (pinskingroup->numskins); pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); pskintype = (void *)(pinskinintervals + groupskins); for (j=0 ; jskinwidth, pheader->skinheight ); if (j == 0) { texels = Hunk_AllocName(s, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype), s); } sprintf (name, "%s_%i_%i", loadmodel->name, i,j); texnum = Mod_LoadExternalSkin(name); pheader->gl_texturenum[i][j&3] = texnum != -1 ? texnum : GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype), true, false); pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); } k = j; for (/* */; j < 4; j++) pheader->gl_texturenum[i][j&3] = pheader->gl_texturenum[i][j - k]; } } return (void *)pskintype; } //========================================================================= /* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; int version, numframes, numskins; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]); pheader = Hunk_AllocName (size, loadname); i = LittleLong (pinmodel->flags); mod->flags = ((i & 255) << 24) | (i & 0x00FFFF00); // // endian-adjust and copy the data, starting with the alias model header // pheader->boundingradius = LittleFloat (pinmodel->boundingradius); pheader->numskins = LittleLong (pinmodel->numskins); pheader->skinwidth = LittleLong (pinmodel->skinwidth); pheader->skinheight = LittleLong (pinmodel->skinheight); if (pheader->skinheight > MAX_LBM_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); if (pheader->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices (%d; max = %d)", mod->name, pheader->numverts, MAXALIASVERTS); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); if (pheader->numtris > MAXALIASTRIS) Sys_Error ("model %s has too many triangles (%d; max = %d)", mod->name, pheader->numtris, MAXALIASTRIS); pheader->numframes = LittleLong (pinmodel->numframes); numframes = pheader->numframes; if (numframes < 1) Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = LittleLong (pinmodel->synctype); mod->numframes = pheader->numframes; for (i=0 ; i<3 ; i++) { pheader->scale[i] = LittleFloat (pinmodel->scale[i]); pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } // // load the skins // pskintype = (daliasskintype_t *)&pinmodel[1]; pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype); // // load base s and t vertices // pinstverts = (stvert_t *)pskintype; for (i=0 ; inumverts ; i++) { stverts[i].onseam = LittleLong (pinstverts[i].onseam); stverts[i].s = LittleLong (pinstverts[i].s); stverts[i].t = LittleLong (pinstverts[i].t); } // // load triangle lists // pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; for (i=0 ; inumtris ; i++) { triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j=0 ; j<3 ; j++) { triangles[i].vertindex[j] = LittleLong (pintriangles[i].vertindex[j]); } } // // load the frames // posenum = 0; pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; for (i=0 ; itype); if (frametype == ALIAS_SINGLE) { pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); } else { pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); } } pheader->numposes = posenum; mod->type = mod_alias; // FIXME: do this right mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; // // build the draw lists // GL_MakeAliasModelDisplayLists (mod, pheader); // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } //============================================================================= /* ================= Mod_LoadSpriteFrame ================= */ void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int i, width, height, size, origin[2]; unsigned short *ppixout; byte *ppixin; char name[64]; pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = width * height; pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); memset (pspriteframe, 0, sizeof (mspriteframe_t)); *ppframe = pspriteframe; pspriteframe->width = width; pspriteframe->height = height; origin[0] = LittleLong (pinframe->origin[0]); origin[1] = LittleLong (pinframe->origin[1]); pspriteframe->up = origin[1]; pspriteframe->down = origin[1] - height; pspriteframe->left = origin[0]; pspriteframe->right = width + origin[0]; sprintf (name, "%s_%i", loadmodel->name, framenum); pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true); return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); } /* ================= Mod_LoadSpriteGroup ================= */ void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum) { dspritegroup_t *pingroup; mspritegroup_t *pspritegroup; int i, numframes; dspriteinterval_t *pin_intervals; float *poutintervals; void *ptemp; pingroup = (dspritegroup_t *)pin; numframes = LittleLong (pingroup->numframes); pspritegroup = Hunk_AllocName (sizeof (mspritegroup_t) + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); pspritegroup->numframes = numframes; *ppframe = (mspriteframe_t *)pspritegroup; pin_intervals = (dspriteinterval_t *)(pingroup + 1); poutintervals = Hunk_AllocName (numframes * sizeof (float), loadname); pspritegroup->intervals = poutintervals; for (i=0 ; iinterval); if (*poutintervals <= 0.0) Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); poutintervals++; pin_intervals++; } ptemp = (void *)pin_intervals; for (i=0 ; iframes[i], framenum * 100 + i); } return ptemp; } /* ================= Mod_LoadSpriteModel ================= */ void Mod_LoadSpriteModel (model_t *mod, void *buffer) { int i; int version; dsprite_t *pin; msprite_t *psprite; int numframes; int size; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; version = LittleLong (pin->version); if (version != SPRITE_VERSION) Sys_Error ("%s has wrong version number " "(%i should be %i)", mod->name, version, SPRITE_VERSION); numframes = LittleLong (pin->numframes); size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); psprite = Hunk_AllocName (size, loadname); mod->cache.data = psprite; psprite->type = LittleLong (pin->type); psprite->maxwidth = LittleLong (pin->width); psprite->maxheight = LittleLong (pin->height); psprite->beamlength = LittleFloat (pin->beamlength); mod->synctype = LittleLong (pin->synctype); psprite->numframes = numframes; mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; mod->mins[2] = -psprite->maxheight/2; mod->maxs[2] = psprite->maxheight/2; // // load the frames // if (numframes < 1) Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); mod->numframes = numframes; pframetype = (dspriteframetype_t *)(pin + 1); for (i=0 ; itype); psprite->frames[i].type = frametype; if (frametype == SPR_SINGLE) { pframetype = (dspriteframetype_t *) Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i); } else { pframetype = (dspriteframetype_t *) Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i); } } mod->type = mod_sprite; } //============================================================================= /* ================ Mod_Print ================ */ void Mod_Print (void) { int i; model_t *mod; Con_Printf ("Cached models:\n"); for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { Con_Printf ("%8p : %s\n",mod->cache.data, mod->name); } } ================================================ FILE: source/gl_model.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __MODEL__ #define __MODEL__ #include "modelgen.h" #include "spritegn.h" /* d*_t structures are on-disk representations m*_t structures are in-memory */ // entity effects #define EF_BRIGHTFIELD 1 #define EF_MUZZLEFLASH 2 #define EF_BRIGHTLIGHT 4 #define EF_DIMLIGHT 8 #define EF_BLUE 64 #define EF_RED 128 /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vec3_t position; } mvertex_t; #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 // plane_t structure // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct mplane_s { vec3_t normal; float dist; byte type; // for texture axis selection and fast side tests byte signbits; // signx + signy<<1 + signz<<1 byte pad[2]; } mplane_t; typedef struct texture_s { char name[16]; unsigned width, height; int gl_texturenum; struct msurface_s *texturechain; // for gl_texsort drawing int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these unsigned offsets[MIPLEVELS]; // four mip maps stored int fullbright; bool luma; } texture_t; #define SURF_PLANEBACK 2 #define SURF_DRAWSKY 4 #define SURF_DRAWSPRITE 8 #define SURF_DRAWTURB 0x10 #define SURF_DRAWTILED 0x20 #define SURF_DRAWBACKGROUND 0x40 #define SURF_UNDERWATER 0x80 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { unsigned int v[2]; unsigned int cachededgeoffset; } medge_t; typedef struct { float vecs[2][4]; float mipadjust; texture_t *texture; int flags; } mtexinfo_t; #define VERTEXSIZE 7 typedef struct glpoly_s { struct glpoly_s *next; struct glpoly_s *chain; int numverts; int flags; // for SURF_UNDERWATER float verts[4][VERTEXSIZE]; // variable sized (xyz s1t1 s2t2) } glpoly_t; typedef struct msurface_s { int visframe; // should be drawn when node is crossed mplane_t *plane; int flags; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges short texturemins[2]; short extents[2]; int light_s, light_t; // gl lightmap coordinates glpoly_t *polys; // multiple if warped struct msurface_s *texturechain; mtexinfo_t *texinfo; // lighting info int dlightframe; int dlightbits; int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap bool cached_dlight; // true if dynamic light in cache byte *samples; // [numstyles*surfsize] int draw_this_frame; int overbright; } msurface_t; typedef struct mnode_s { // common with leaf int contents; // 0, to differentiate from leafs int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // node specific mplane_t *plane; struct mnode_s *children[2]; unsigned int firstsurface; unsigned int numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number int visframe; // node needs to be traversed if current float minmaxs[6]; // for bounding box culling struct mnode_s *parent; // leaf specific byte *compressed_vis; efrag_t *efrags; msurface_t **firstmarksurface; int nummarksurfaces; int key; // BSP sequence number for leaf's contents byte ambient_sound_level[NUM_AMBIENTS]; } mleaf_t; //johnfitz -- for clipnodes>32k typedef struct mclipnode_s { int planenum; int children[2]; // negative numbers are contents } mclipnode_t; //johnfitz // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { mclipnode_t *clipnodes; mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; /* ============================================================================== SPRITE MODELS ============================================================================== */ // FIXME: shorten these? typedef struct mspriteframe_s { int width; int height; float up, down, left, right; int gl_texturenum; } mspriteframe_t; typedef struct { int numframes; float *intervals; mspriteframe_t *frames[1]; } mspritegroup_t; typedef struct { spriteframetype_t type; mspriteframe_t *frameptr; } mspriteframedesc_t; typedef struct { int type; int maxwidth; int maxheight; int numframes; float beamlength; // remove? void *cachespot; // remove? mspriteframedesc_t frames[1]; } msprite_t; /* ============================================================================== ALIAS MODELS Alias models are position independent, so the cache manager can move them. ============================================================================== */ typedef struct { int firstpose; int numposes; float interval; trivertx_t bboxmin; trivertx_t bboxmax; int frame; char name[16]; } maliasframedesc_t; typedef struct { trivertx_t bboxmin; trivertx_t bboxmax; int frame; } maliasgroupframedesc_t; typedef struct { int numframes; int intervals; maliasgroupframedesc_t frames[1]; } maliasgroup_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct mtriangle_s { int facesfront; int vertindex[3]; } mtriangle_t; #define MAX_SKINS 32 typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; int numposes; int poseverts; int posedata; // numposes*poseverts trivert_t int commands; // gl command list with embedded s/t int gl_texturenum[MAX_SKINS][4]; int texels[MAX_SKINS]; // only for player skins maliasframedesc_t frames[1]; // variable sized } aliashdr_t; #define MAXALIASVERTS 5120 #define MAXALIASFRAMES 256 #define MAXALIASTRIS 4096 extern aliashdr_t *pheader; extern stvert_t stverts[MAXALIASVERTS]; extern mtriangle_t triangles[MAXALIASTRIS]; extern trivertx_t *poseverts[MAXALIASFRAMES]; //=================================================================== // // Whole model // typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; #define MF_ROCKET 1 // leave a trail #define MF_GRENADE 2 // leave a trail #define MF_GIB 4 // leave a trail #define MF_ROTATE 8 // rotate (bonus items) #define MF_TRACER 16 // green split trail #define MF_ZOMGIB 32 // small blood trail #define MF_TRACER2 64 // orange split trail + rotate #define MF_TRACER3 128 // purple trail typedef struct model_s { char name[MAX_QPATH]; unsigned int path_id; bool needload; // bmodels and sprites don't cache normally modtype_t type; int numframes; synctype_t synctype; int flags; // // volume occupied by the model graphics // vec3_t mins, maxs; float radius; // // solid volume for clipping // bool clipbox; vec3_t clipmins, clipmaxs; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; dmodel_t *submodels; int numplanes; mplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numvertexes; mvertex_t *vertexes; int numedges; medge_t *edges; int numnodes; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numsurfedges; int *surfedges; int numclipnodes; mclipnode_t *clipnodes; int nummarksurfaces; msurface_t **marksurfaces; hull_t hulls[MAX_MAP_HULLS]; int numtextures; texture_t **textures; byte *visdata; byte *lightdata; signed char *entities; int bspversion; // // additional model data // cache_user_t cache; // only access through Mod_Extradata } model_t; //============================================================================ void Mod_Init (void); void Mod_ClearAll (void); void Mod_ResetAll (void); // for gamedir changes (Host_Game_f) model_t *Mod_ForName (char *name, bool crash); void *Mod_Extradata (model_t *mod); // handles caching void Mod_TouchModel (char *name); mleaf_t *Mod_PointInLeaf (float *p, model_t *model); byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); #endif // __MODEL__ ================================================ FILE: source/gl_refrag.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_efrag.c #include "quakedef.h" mnode_t *r_pefragtopnode; // mh - extended efrags - begin #define EXTRA_EFRAGS 32 void R_GetMoreEfrags (void) { int i; cl.free_efrags = (efrag_t *) Hunk_Alloc (EXTRA_EFRAGS * sizeof (efrag_t)); for (i = 0; i < EXTRA_EFRAGS - 1; i++) cl.free_efrags[i].entnext = &cl.free_efrags[i + 1]; cl.free_efrags[i].entnext = NULL; } // mh - extended efrags - end //=========================================================================== /* =============================================================================== ENTITY FRAGMENT FUNCTIONS =============================================================================== */ efrag_t **lastlink; vec3_t r_emins, r_emaxs; entity_t *r_addent; /* ================ R_RemoveEfrags Call when removing an object from the world or moving it to another position ================ */ void R_RemoveEfrags (entity_t *ent) { efrag_t *ef, *old, *walk, **prev; ef = ent->efrag; while (ef) { prev = &ef->leaf->efrags; while (1) { walk = *prev; if (!walk) break; if (walk == ef) { // remove this fragment *prev = ef->leafnext; break; } else prev = &walk->leafnext; } old = ef; ef = ef->entnext; // put it on the free list old->entnext = cl.free_efrags; cl.free_efrags = old; } ent->efrag = NULL; } /* =================== R_SplitEntityOnNode =================== */ void R_SplitEntityOnNode (mnode_t *node) { efrag_t *ef; mplane_t *splitplane; mleaf_t *leaf; int sides; if (node->contents == CONTENTS_SOLID) { return; } // add an efrag if the node is a leaf if ( node->contents < 0) { if (!r_pefragtopnode) r_pefragtopnode = node; leaf = (mleaf_t *)node; // grab an efrag off the free list ef = cl.free_efrags; if (!ef) { // mh - extended efrags - begin R_GetMoreEfrags (); ef = cl.free_efrags; // mh - extended efrags - end } cl.free_efrags = cl.free_efrags->entnext; ef->entity = r_addent; // add the entity link *lastlink = ef; lastlink = &ef->entnext; ef->entnext = NULL; // set the leaf links ef->leaf = leaf; ef->leafnext = leaf->efrags; leaf->efrags = ef; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(r_emins, r_emaxs, splitplane); if (sides == 3) { // split on this plane // if this is the first splitter of this bmodel, remember it if (!r_pefragtopnode) r_pefragtopnode = node; } // recurse down the contacted sides if (sides & 1) R_SplitEntityOnNode (node->children[0]); if (sides & 2) R_SplitEntityOnNode (node->children[1]); } /* =========== R_AddEfrags =========== */ void R_AddEfrags (entity_t *ent) { model_t *entmodel; int i; if (!ent->model) return; r_addent = ent; lastlink = &ent->efrag; r_pefragtopnode = NULL; entmodel = ent->model; for (i=0 ; i<3 ; i++) { r_emins[i] = ent->origin[i] + entmodel->mins[i]; r_emaxs[i] = ent->origin[i] + entmodel->maxs[i]; } R_SplitEntityOnNode (cl.worldmodel->nodes); ent->topnode = r_pefragtopnode; } /* ================ R_StoreEfrags // FIXME: a lot of this goes away with edge-based ================ */ void R_StoreEfrags (efrag_t **ppefrag) { entity_t *pent; model_t *clmodel; efrag_t *pefrag; while ((pefrag = *ppefrag) != NULL) { pent = pefrag->entity; clmodel = pent->model; switch (clmodel->type) { case mod_alias: case mod_brush: case mod_sprite: pent = pefrag->entity; if ((pent->visframe != r_framecount) && (cl_numvisedicts < MAX_VISEDICTS)) { cl_visedicts[cl_numvisedicts++] = pent; // mark that we've recorded this entity for this frame pent->visframe = r_framecount; } ppefrag = &pefrag->leafnext; break; default: Sys_Error ("R_StoreEfrags: Bad entity type %d\n", clmodel->type); } } } ================================================ FILE: source/gl_rlight.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_light.c #include "quakedef.h" int r_dlightframecount; /* ================== R_AnimateLight ================== */ void R_AnimateLight (void) { int i,j,k; // // light animations // 'm' is normal light, 'a' is no light, 'z' is double bright i = (int)(cl.time*10); for (j=0 ; jradius * 0.35; VectorSubtract (light->origin, r_origin, v); if (Length (v) < rad) { // view is inside the dlight AddLightBlend (1, 0.5, 0, light->radius * 0.0003); return; } GL_EnableState(GL_COLOR_ARRAY); GL_DisableState(GL_TEXTURE_COORD_ARRAY); float* pPos = gVertexBuffer; float* pColor = gColorBuffer; *gColorBuffer++ = light->color[0]; *gColorBuffer++ = light->color[1]; *gColorBuffer++ = light->color[2]; *gColorBuffer++ = light->alpha; for (i=0 ; i<3 ; i++) *gVertexBuffer++ = light->origin[i] - vpn[i]*rad; for (i=16 ; i>=0 ; i--) { *gColorBuffer++ = 0.8f; *gColorBuffer++ = 0.4f; *gColorBuffer++ = 0.0f; *gColorBuffer++ = 0.0f; float cos_rad = costablef[i]*rad; float sin_rad = sintablef[i]*rad; for (j=0 ; j<3 ; j++) *gVertexBuffer++ = light->origin[j] + vright[j]*cos_rad + vup[j]*sin_rad; } vglVertexAttribPointerMapped(0, pPos); vglVertexAttribPointerMapped(1, pColor); GL_DrawPolygon(GL_TRIANGLE_FAN, 18); GL_DisableState(GL_COLOR_ARRAY); GL_EnableState(GL_TEXTURE_COORD_ARRAY); GL_Color(0,0,0,1); // Ensure the color ends up being zero just like the non-OpenGLES code } /* ============= R_RenderDlights ============= */ void R_RenderDlights (void) { int i; dlight_t *l; if (!gl_flashblend.value) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame glDepthMask (0); GL_DisableState(GL_TEXTURE_COORD_ARRAY); //->glShadeModel (GL_SMOOTH); glEnable (GL_BLEND); // glBlendFunc (GL_ONE, GL_ONE); // 30/01/2000 removed: M.Tretene l = cl_dlights; for (i=0 ; idie < cl.time || !l->radius) continue; R_RenderDlight (l); } GL_Color(1,1,1,1); glDisable (GL_BLEND); GL_EnableState(GL_TEXTURE_COORD_ARRAY); // glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 30/01/2000 removed: M.Tretene glDepthMask (1); } /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ /* ============= R_MarkLights ============= */ void R_MarkLights (dlight_t *light, int bit, mnode_t *node) { mplane_t *splitplane; float dist; msurface_t *surf; int i; // LordHavoc: .lit support begin (actually this is just a major lighting speedup, no relation to color :) float l, maxdist; int j, s, t; vec3_t impact; loc0: // LordHavoc: .lit support end if (node->contents < 0) return; splitplane = node->plane; // LordHavoc: original code // LordHavoc: .lit support (actually this is just a major lighting speedup, no relation to color :) if (splitplane->type < 3) dist = light->origin[splitplane->type] - splitplane->dist; else dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; // LordHavoc: original code // LordHavoc: .lit support end if (dist > light->radius) { // LordHavoc: .lit support begin (actually this is just a major lighting speedup, no relation to color :) node = node->children[0]; goto loc0; // LordHavoc: .lit support end } if (dist < -light->radius) { // LordHavoc: .lit support begin (actually this is just a major lighting speedup, no relation to color :) node = node->children[1]; goto loc0; // LordHavoc: .lit support end } maxdist = light->radius*light->radius; // LordHavoc: .lit support (actually this is just a major lighting speedup, no relation to color :) // mark the polygons surf = cl.worldmodel->surfaces + node->firstsurface; for (i=0 ; inumsurfaces ; i++, surf++) { // LordHavoc: .lit support begin (actually this is just a major lighting speedup, no relation to color :) // LordHavoc: MAJOR dynamic light speedup here, eliminates marking of surfaces that are too far away from light, thus preventing unnecessary renders and uploads for (j=0 ; j<3 ; j++) impact[j] = light->origin[j] - surf->plane->normal[j]*dist; // clamp center of light to corner and check brightness l = DotProduct (impact, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3] - surf->texturemins[0]; s = l+0.5;if (s < 0) s = 0;else if (s > surf->extents[0]) s = surf->extents[0]; s = l - s; l = DotProduct (impact, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3] - surf->texturemins[1]; t = l+0.5;if (t < 0) t = 0;else if (t > surf->extents[1]) t = surf->extents[1]; t = l - t; // compare to minimum light if ((s*s+t*t+dist*dist) < maxdist) { if (surf->dlightframe != r_dlightframecount) // not dynamic until now { surf->dlightbits = bit; surf->dlightframe = r_dlightframecount; } else // already dynamic surf->dlightbits |= bit; } // LordHavoc: .lit support end } // LordHavoc: .lit support begin (actually this is just a major lighting speedup, no relation to color :) if (node->children[0]->contents >= 0) R_MarkLights (light, bit, node->children[0]); // LordHavoc: original code if (node->children[1]->contents >= 0) R_MarkLights (light, bit, node->children[1]); // LordHavoc: original code // LordHavoc: .lit support end } /* ============= R_PushDlights ============= */ void R_PushDlights (void) { int i; dlight_t *l; if (gl_flashblend.value) return; r_dlightframecount = r_framecount + 1; // because the count hasn't // advanced yet for this frame l = cl_dlights; for (i=0 ; idie < cl.time || !l->radius) continue; R_MarkLights ( l, 1<nodes ); } } /* ============================================================================= LIGHT SAMPLING ============================================================================= */ mplane_t *lightplane; vec3_t lightspot; vec3_t lightcolor; //johnfitz -- lit support via lordhavoc // LordHavoc: .lit support begin // LordHavoc: original code replaced entirely int RecursiveLightPoint (vec3_t color, mnode_t *node, vec3_t start, vec3_t end) { float front, back, frac; vec3_t mid; loc0: if (node->contents < 0) return false; // didn't hit anything // calculate mid point if (node->plane->type < 3) { front = start[node->plane->type] - node->plane->dist; back = end[node->plane->type] - node->plane->dist; } else { front = DotProduct(start, node->plane->normal) - node->plane->dist; back = DotProduct(end, node->plane->normal) - node->plane->dist; } // LordHavoc: optimized recursion if ((back < 0) == (front < 0)) // return RecursiveLightPoint (color, node->children[front < 0], start, end); { node = node->children[front < 0]; goto loc0; } frac = front / (front-back); mid[0] = start[0] + (end[0] - start[0])*frac; mid[1] = start[1] + (end[1] - start[1])*frac; mid[2] = start[2] + (end[2] - start[2])*frac; // go down front side if (RecursiveLightPoint (color, node->children[front < 0], start, mid)) return true; // hit something else { int i, ds, dt; msurface_t *surf; // check for impact on this node VectorCopy (mid, lightspot); lightplane = node->plane; surf = cl.worldmodel->surfaces + node->firstsurface; for (i = 0;i < node->numsurfaces;i++, surf++) { if (surf->flags & SURF_DRAWTILED) continue; // no lightmaps ds = (int) ((float) DotProduct (mid, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]); dt = (int) ((float) DotProduct (mid, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]); if (ds < surf->texturemins[0] || dt < surf->texturemins[1]) continue; ds -= surf->texturemins[0]; dt -= surf->texturemins[1]; if (ds > surf->extents[0] || dt > surf->extents[1]) continue; if (surf->samples) { // LordHavoc: enhanced to interpolate lighting byte *lightmap; int maps, line3, dsfrac = ds & 15, dtfrac = dt & 15, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0; float scale; line3 = ((surf->extents[0]>>4)+1)*3; lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++) { scale = (float) d_lightstylevalue[surf->styles[maps]] * 1.0 / 256.0; r00 += (float) lightmap[ 0] * scale;g00 += (float) lightmap[ 1] * scale;b00 += (float) lightmap[2] * scale; r01 += (float) lightmap[ 3] * scale;g01 += (float) lightmap[ 4] * scale;b01 += (float) lightmap[5] * scale; r10 += (float) lightmap[line3+0] * scale;g10 += (float) lightmap[line3+1] * scale;b10 += (float) lightmap[line3+2] * scale; r11 += (float) lightmap[line3+3] * scale;g11 += (float) lightmap[line3+4] * scale;b11 += (float) lightmap[line3+5] * scale; lightmap += ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting } color[0] += (float) ((int) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00))); color[1] += (float) ((int) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00))); color[2] += (float) ((int) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00))); } return true; // success } // go down back side return RecursiveLightPoint (color, node->children[front >= 0], mid, end); } } vec3_t lightcolor; // LordHavoc: used by model rendering int R_LightPoint (vec3_t p) { vec3_t end; if (!cl.worldmodel->lightdata) { lightcolor[0] = lightcolor[1] = lightcolor[2] = 255; return 255; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 8192; //johnfitz -- was 2048 lightcolor[0] = lightcolor[1] = lightcolor[2] = 0; RecursiveLightPoint (lightcolor, cl.worldmodel->nodes, p, end); return ((lightcolor[0] + lightcolor[1] + lightcolor[2]) * (1.0f / 3.0f)); } // LordHavoc: .lit support end ================================================ FILE: source/gl_rmain.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_main.c #include "quakedef.h" #include entity_t r_worldentity; bool r_cache_thrash; // compatability vec3_t modelorg, r_entorigin; entity_t *currententity; int r_visframecount; // bumped when going to a new PVS int r_framecount; // used for dlight push checking mplane_t frustum[4]; int c_brush_polys, c_alias_polys; bool envmap; // true during envmap command capture int currenttexture = -1; // to avoid unnecessary texture sets int cnttextures[2] = {-1, -1}; // cached int particletexture; // little dot for particles int playertextures; // up to 16 color translated skins int mirrortexturenum; // quake texturenum, not gltexturenum bool mirror; mplane_t *mirror_plane; // // view origin // vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_world_matrix[16]; float r_base_world_matrix[16]; extern cvar_t v_gamma; // muff extern cvar_t gl_outline; float r_fovx, r_fovy; // idea originally nicked from LordHavoc // re-worked + extended - muff 5 Feb 2001 // called inside polyblend float *gamma_vertices = NULL; void DoGamma() { if (v_gamma.value < 0.2) v_gamma.value = 0.2; if (v_gamma.value >= 1) { v_gamma.value = 1; return; } //believe it or not this actually does brighten the picture!! GL_DisableState(GL_TEXTURE_COORD_ARRAY); glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); float color[4] = {1, 1, 1, v_gamma.value}; if (gamma_vertices == NULL) { gamma_vertices = malloc(3 * 4 * sizeof(float)); gamma_vertices[0] = gamma_vertices[3] = gamma_vertices[6] = gamma_vertices[9] = 10; gamma_vertices[1] = gamma_vertices[2] = gamma_vertices[5] = gamma_vertices[10] = 100; gamma_vertices[4] = gamma_vertices[7] = gamma_vertices[8] = gamma_vertices[11] = -100; } glUniform4fv(monocolor, 1, color); vglVertexAttribPointerMapped(0, gamma_vertices); GL_DrawPolygon(GL_TRIANGLE_FAN, 4); //if we do this twice, we double the brightening effect for a wider range of gamma's GL_DrawPolygon(GL_TRIANGLE_FAN, 4); GL_EnableState(GL_TEXTURE_COORD_ARRAY); } // // screen size info // refdef_t r_refdef; mleaf_t *r_viewleaf, *r_oldviewleaf; texture_t *r_notexture_mip; int d_lightstylevalue[256]; // 8.8 fraction of base light value void R_MarkLeaves (void); CVAR (r_norefresh, 0, CVAR_NONE) CVAR (r_drawentities, 1, CVAR_NONE) CVAR (r_drawviewmodel, 1, CVAR_ARCHIVE) CVAR (r_speeds, 0, CVAR_NONE) CVAR (r_fullbright, 0, CVAR_NONE) CVAR (r_lightmap, 0, CVAR_NONE) CVAR (r_shadows, 1, CVAR_ARCHIVE) CVAR (r_mirroralpha, 0.8, CVAR_ARCHIVE) CVAR (r_wateralpha, 1.0, CVAR_ARCHIVE) CVAR (r_dynamic, 1, CVAR_ARCHIVE) CVAR (r_novis, 0, CVAR_NONE) CVAR (gl_xflip, 0, CVAR_ARCHIVE) // fenix@io.com: model interpolation CVAR (r_interpolate_model_animation, 1, CVAR_ARCHIVE) CVAR (r_interpolate_model_transform, 1, CVAR_ARCHIVE) CVAR (gl_finish, 0, CVAR_NONE) CVAR (gl_clear, 0, CVAR_NONE) CVAR (gl_cull, 1, CVAR_NONE) CVAR (gl_texsort, 1, CVAR_NONE) CVAR (gl_smoothmodels, 1, CVAR_NONE) CVAR (gl_affinemodels, 1, CVAR_NONE) CVAR (gl_polyblend, 1, CVAR_NONE) CVAR (gl_flashblend, 0, CVAR_NONE) CVAR (gl_playermip, 0, CVAR_NONE) CVAR (gl_nocolors, 0, CVAR_NONE) CVAR (gl_keeptjunctions, 1, CVAR_NONE) CVAR (gl_reporttjunctions, 0, CVAR_NONE) CVAR (gl_doubleeyes, 1, CVAR_NONE) CVAR (gl_overbright, 0, CVAR_ARCHIVE) // Torch flares. KH CVAR (gl_torchflares, 1, CVAR_ARCHIVE) // BEGIN STEREO DEFS int stereoCameraSelect = -1; // 1 = left, -1 = right CVAR (st_separation, 0, CVAR_ARCHIVE) // CON: st_separation -> separation Between Cameras CVAR (st_zeropdist, 20, CVAR_ARCHIVE) // CON: st_zeropdist -> Dist to zero parallax CVAR (st_fustbal, 1, CVAR_ARCHIVE) // CON: st_fustbal -> frustumBalance adjust // END Stereo Defs extern bool gl_warp; /* ================= R_CullBox Returns true if the box is completely outside the frustom ================= */ bool R_CullBox (vec3_t mins, vec3_t maxs) { int i; for (i=0 ; i<4 ; i++) if (BoxOnPlaneSide (mins, maxs, &frustum[i]) == 2) return true; return false; } void R_RotateForEntity (entity_t *e) { glTranslatef (e->origin[0], e->origin[1], e->origin[2]); glRotatef (e->angles[1], 0, 0, 1); glRotatef (-e->angles[0], 0, 1, 0); glRotatef (e->angles[2], 1, 0, 0); } /* ============= R_BlendedRotateForEntity fenix@io.com: model transform interpolation ============= */ void R_BlendedRotateForEntity (entity_t *e) { float timepassed; float blend; vec3_t d; int i; // positional interpolation timepassed = realtime - e->translate_start_time; if (e->translate_start_time == 0 || timepassed > 1) { e->translate_start_time = realtime; VectorCopy (e->origin, e->origin1); VectorCopy (e->origin, e->origin2); } if (!VectorCompare (e->origin, e->origin2)) { e->translate_start_time = realtime; VectorCopy (e->origin2, e->origin1); VectorCopy (e->origin, e->origin2); blend = 0; }else{ blend = timepassed / 0.1; if (cl.paused || blend > 1) blend = 1; } VectorSubtract (e->origin2, e->origin1, d); glTranslatef ( e->origin1[0] + (blend * d[0]), e->origin1[1] + (blend * d[1]), e->origin1[2] + (blend * d[2])); // orientation interpolation (Euler angles, yuck!) timepassed = realtime - e->rotate_start_time; if (e->rotate_start_time == 0 || timepassed > 1) { e->rotate_start_time = realtime; VectorCopy (e->angles, e->angles1); VectorCopy (e->angles, e->angles2); } if (!VectorCompare (e->angles, e->angles2)) { e->rotate_start_time = realtime; VectorCopy (e->angles2, e->angles1); VectorCopy (e->angles, e->angles2); blend = 0; }else{ blend = timepassed / 0.1; if (cl.paused || blend > 1) blend = 1; } VectorSubtract (e->angles2, e->angles1, d); // always interpolate along the shortest path for (i = 0; i < 3; i++) { if (d[i] > 180){ d[i] -= 360; }else if (d[i] < -180){ d[i] += 360; } } glRotatef ( e->angles1[1] + ( blend * d[1]), 0, 0, 1); glRotatef (-e->angles1[0] + (-blend * d[0]), 0, 1, 0); glRotatef ( e->angles1[2] + ( blend * d[2]), 1, 0, 0); } /* ============================================================= SPRITE MODELS ============================================================= */ /* ================ R_GetSpriteFrame ================ */ mspriteframe_t *R_GetSpriteFrame (entity_t *currententity) { msprite_t *psprite; mspritegroup_t *pspritegroup; mspriteframe_t *pspriteframe; int i, numframes, frame; float *pintervals, fullinterval, targettime, time; psprite = currententity->model->cache.data; frame = currententity->frame; if ((frame >= psprite->numframes) || (frame < 0)) { Con_Printf ("R_DrawSprite: no such frame %d\n", frame); frame = 0; } if (psprite->frames[frame].type == SPR_SINGLE) { pspriteframe = psprite->frames[frame].frameptr; } else { pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr; pintervals = pspritegroup->intervals; numframes = pspritegroup->numframes; fullinterval = pintervals[numframes-1]; time = cl.time + currententity->syncbase; // when loading in Mod_LoadSpriteGroup, we guaranteed all interval values // are positive, so we don't have to worry about division by 0 targettime = time - ((int)(time / fullinterval)) * fullinterval; for (i=0 ; i<(numframes-1) ; i++) { if (pintervals[i] > targettime) break; } pspriteframe = pspritegroup->frames[i]; } return pspriteframe; } /* ================= R_DrawSpriteModel ================= */ float *sprite_model_tcoords = NULL; void R_DrawSpriteModel (entity_t *e) { vec3_t point; mspriteframe_t *frame; float *up, *right; vec3_t v_forward, v_right, v_up; msprite_t *psprite; // don't even bother culling, because it's just a single // polygon without a surface cache frame = R_GetSpriteFrame (e); psprite = currententity->model->cache.data; if (psprite->type == SPR_ORIENTED) { // bullet marks on walls AngleVectors (currententity->angles, v_forward, v_right, v_up); up = v_up; right = v_right; } else { // normal sprite up = vup; right = vright; } GL_Color(1,1,1,1); GL_Bind(frame->gl_texturenum); GL_EnableState(GL_ALPHA_TEST); float* pPoint = gVertexBuffer; if (sprite_model_tcoords == NULL) { sprite_model_tcoords = (float *)malloc(sizeof(float) * 8); sprite_model_tcoords[0] = sprite_model_tcoords[2] = sprite_model_tcoords[3] = sprite_model_tcoords[5] = 0; sprite_model_tcoords[1] = sprite_model_tcoords[4] = sprite_model_tcoords[6] = sprite_model_tcoords[7] = 1; } VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->left, right, gVertexBuffer); gVertexBuffer += 3; VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->left, right, gVertexBuffer); gVertexBuffer += 3; VectorMA (e->origin, frame->up, up, point); VectorMA (point, frame->right, right, gVertexBuffer); gVertexBuffer += 3; VectorMA (e->origin, frame->down, up, point); VectorMA (point, frame->right, right, gVertexBuffer); gVertexBuffer += 3; vglVertexAttribPointerMapped(0, pPoint); vglVertexAttribPointerMapped(1, sprite_model_tcoords); GL_DrawPolygon(GL_TRIANGLE_FAN, 4); GL_DisableState(GL_ALPHA_TEST); } /* ============================================================= ALIAS MODELS ============================================================= */ #define NUMVERTEXNORMALS 162 const float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "anorms.h" }; vec3_t shadevector; extern vec3_t lightcolor; // LordHavoc: .lit support to the definitions at the top // precalculated dot products for quantized angles #define SHADEDOT_QUANT 16 const float r_avertexnormal_dots[SHADEDOT_QUANT][256] = #include "anorm_dots.h" ; float *shadedots = (float*)r_avertexnormal_dots[0]; int lastposenum; // fenix@io.com: model animation interpolation int lastposenum0; // Gongo - cel shade tutorial // cel shading table float celshade[16] = { 0.2, 0.2, 0.2, 0.5, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; /* ============= GL_DrawAliasFrame ============= */ void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) { int i; // for "for" loops vec3_t l; // new - used for cel shading float l2; // cel shading lookup value trivertx_t *verts; int *order; int count; GL_EnableState(GL_COLOR_ARRAY); lastposenum = posenum; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done int primType; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; } else primType = GL_TRIANGLE_STRIP; float *pColor = gColorBuffer; float *pPos = gVertexBuffer; float *pTexCoord = gTexCoordBuffer; int c; for (c = 0; c < count; ++c) { // texture coordinates come from the draw list *gTexCoordBuffer++ = ((float *)order)[0]; *gTexCoordBuffer++ = ((float *)order)[1]; order += 2; // calculate light as vector so that intensity is it's length for ( i = 0; i < 3; i++ ) { if (r_fullbright.value || !cl.worldmodel->lightdata) { l[i] = lightcolor[i]; } else { l[i] = shadedots[verts->lightnormalindex] * lightcolor[i]; // shade as usual } } if (gl_outline.value > 0) { l2 = sqrt( (l[0]*l[0]) + (l[1]*l[1]) + (l[2]*l[2]) ); // get the length of the lighting vector (intensity) if ( l2 > 1.0 ) // if it's greater than 1.0 l2 = 1.0; // we'll clamp down to 1.0, since it'll be the same shade anyway l2 = celshade[(int)(l2 * 15)]; // lookup the value in the cel shade lighting table l2 *= 1.25; // brighten things up a bit VectorNormalize (l); // bring the lighting vector length to 1 so that we can scale it to exactly the value we want VectorScale (l, l2, l); // scale the light to the clamped cel shaded value for ( i = 0; i < 3; i++ ) { if ( l[i] > 1.0 ) // check for overbrights l[i] = 1.0; // clamp down to 1.0 if ( l[i] <= 0.0 ) // check for no light l[i] = 0.15; // provide some minimum light } } *gColorBuffer++ = l[0]; *gColorBuffer++ = l[1]; *gColorBuffer++ = l[2]; *gColorBuffer++ = 1.0f; *gVertexBuffer++ = verts->v[0]; *gVertexBuffer++ = verts->v[1]; *gVertexBuffer++ = verts->v[2]; ++verts; } vglVertexAttribPointerMapped(0, pPos); vglVertexAttribPointerMapped(1, pTexCoord); vglVertexAttribPointerMapped(2, pColor); GL_DrawPolygon(primType, count); } GL_DisableState(GL_COLOR_ARRAY); if (gl_outline.value > 0) { verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); glPolygonMode (GL_BACK, GL_LINE); // we're drawing the outlined edges glEnable (GL_CULL_FACE); // enable culling so that we don't draw the entire wireframe glLineWidth(gl_outline.value); glCullFace (GL_FRONT); // get rid of the front facing wireframe glFrontFace (GL_CW); // hack to avoid using the depth buffer tests glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // make sure the outline shows up GL_DisableState(GL_TEXTURE_COORD_ARRAY); while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done int primType; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; } else primType = GL_TRIANGLE_STRIP; float *pPos = gVertexBuffer; int c; for (c = 0; c < count; ++c) { order += 2; *gVertexBuffer++ = verts->v[0]; *gVertexBuffer++ = verts->v[1]; *gVertexBuffer++ = verts->v[2]; ++verts; } float color[] = {0.0f, 0.0f, 0.0f, 1.0f}; glUniform4fv(monocolor, 1, color); vglVertexAttribPointerMapped(0, pPos); GL_DrawPolygon(primType, count); } glPolygonMode (GL_BACK, GL_FILL); // get out of wireframe mode glFrontFace (GL_CCW); // end of hack for depth buffer glCullFace (GL_BACK); // back to normal face culling glDisable (GL_CULL_FACE); glDisable (GL_BLEND); GL_EnableState(GL_TEXTURE_COORD_ARRAY); } } /* ============= GL_DrawAliasBlendedFrame fenix@io.com: model animation interpolation ============= */ void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend) { int i; // for "for" loops vec3_t l; // new - used for cel shading float l2; // cel shading lookup value trivertx_t* verts1; trivertx_t* verts2; int* order; int count; vec3_t d; extern vec3_t lightcolor; // LordHavoc: .lit support to the definitions at the top GL_EnableState(GL_COLOR_ARRAY); lastposenum0 = pose1; lastposenum = pose2; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += pose1 * paliashdr->poseverts; verts2 += pose2 * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); for (;;) { // get the vertex count and primitive type count = *order++; if (!count) break; int primType; int c; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; }else{ primType = GL_TRIANGLE_STRIP; } float *pColor = gColorBuffer; float *pPos = gVertexBuffer; float *pTexCoord = gTexCoordBuffer; c = count; do { // texture coordinates come from the draw list *gTexCoordBuffer++ = ((float *)order)[0]; *gTexCoordBuffer++ = ((float *)order)[1]; order += 2; // normals and vertexes come from the frame list // blend the light intensity from the two frames together d[0] = shadedots[verts2->lightnormalindex] - shadedots[verts1->lightnormalindex]; // calculate light as vector so that intensity is it's length for ( i = 0; i < 3; i++ ) { l[i] = (shadedots[verts1->lightnormalindex] + (blend * d[0])); // shade as usual l[i] *= lightcolor[i]; // apply colored lighting } if (gl_outline.value > 0) { l2 = sqrt( (l[0]*l[0]) + (l[1]*l[1]) + (l[2]*l[2]) ); // get the length of the lighting vector (intensity) if ( l2 > 1.0 ) // if it's greater than 1.0 l2 = 1.0; // we'll clamp down to 1.0, since it'll be the same shade anyway l2 = celshade[(int)(l2 * 15)]; // lookup the value in the cel shade lighting table l2 *= 1.25; // brighten things up a bit VectorNormalize (l); // bring the lighting vector length to 1 so that we can scale it to exactly the value we want VectorScale (l, l2, l); // scale the light to the clamped cel shaded value for ( i = 0; i < 3; i++ ) { if ( l[i] > 1.0 ) // check for overbrights l[i] = 1.0; // clamp down to 1.0 if ( l[i] <= 0.0 ) // check for no light l[i] = 0.15; // provide some minimum light } } *gColorBuffer++ = l[0]; *gColorBuffer++ = l[1]; *gColorBuffer++ = l[2]; *gColorBuffer++ = 1.0f; VectorSubtract(verts2->v, verts1->v, d); // blend the vertex positions from each frame together *gVertexBuffer++ = verts1->v[0] + (blend * d[0]); *gVertexBuffer++ = verts1->v[1] + (blend * d[1]); *gVertexBuffer++ = verts1->v[2] + (blend * d[2]); verts1++; verts2++; } while (--count); vglVertexAttribPointerMapped(0, pPos); vglVertexAttribPointerMapped(1, pTexCoord); vglVertexAttribPointerMapped(2, pColor); GL_DrawPolygon(primType, c); } GL_DisableState(GL_COLOR_ARRAY); if (gl_outline.value > 0) { verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += pose1 * paliashdr->poseverts; verts2 += pose2 * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); glPolygonMode (GL_BACK, GL_LINE); // we're drawing the outlined edges glEnable (GL_CULL_FACE); // enable culling so that we don't draw the entire wireframe glLineWidth(gl_outline.value); glCullFace (GL_FRONT); // get rid of the front facing wireframe glFrontFace (GL_CW); // hack to avoid using the depth buffer tests glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // make sure the outline shows up GL_DisableState(GL_TEXTURE_COORD_ARRAY); for (;;) { // get the vertex count and primitive type count = *order++; if (!count) break; int primType; int c; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; }else{ primType = GL_TRIANGLE_STRIP; } float *pPos = gVertexBuffer; c = count; // now draw the model again do { order += 2; VectorSubtract(verts2->v, verts1->v, d); // blend the vertex positions from each frame together *gVertexBuffer++ = verts1->v[0] + (blend * d[0]); *gVertexBuffer++ = verts1->v[1] + (blend * d[1]); *gVertexBuffer++ = verts1->v[2] + (blend * d[2]); verts1++; verts2++; } while (--count); float color[] = {0.0f, 0.0f, 0.0f, 1.0f}; glUniform4fv(monocolor, 1, color); vglVertexAttribPointerMapped(0, pPos); GL_DrawPolygon(primType, c); } glPolygonMode (GL_BACK, GL_FILL); // get out of wireframe mode glFrontFace (GL_CCW); // end of hack for depth buffer glCullFace (GL_BACK); // back to normal face culling glDisable (GL_CULL_FACE); glDisable (GL_BLEND); GL_EnableState(GL_TEXTURE_COORD_ARRAY); } } /* ============= GL_DrawAliasShadow ============= */ extern vec3_t lightspot; void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) { float s, t, l; int i, j; int index; trivertx_t *v, *verts; int list; int *order; vec3_t point; float *normal; float height, lheight; int count; lheight = currententity->origin[2] - lightspot[2]; height = 0; verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts += posenum * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); height = -lheight + 1.0; while (1) { // get the vertex count and primitive type count = *order++; if (!count) break; // done int primType; int c; float* pVertex; if (count < 0) { count = -count; primType = GL_TRIANGLE_FAN; }else primType = GL_TRIANGLE_STRIP; pVertex = gVertexBuffer; for(c = 0; c < count; c++) { // texture coordinates come from the draw list // (skipped for shadows) glTexCoord2fv ((float *)order); order += 2; // normals and vertexes come from the frame list gVertexBuffer[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; gVertexBuffer[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; gVertexBuffer[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; gVertexBuffer[0] -= shadevector[0]*(pVertex[2]+lheight); gVertexBuffer[1] -= shadevector[1]*(pVertex[2]+lheight); gVertexBuffer[2] = height; // height -= 0.001; gVertexBuffer += 3; verts++; } GL_DisableState(GL_TEXTURE_COORD_ARRAY); const float color[] = {0,0,0,0.5f}; glUniform4fv(monocolor, 1, color); vglVertexAttribPointerMapped(0, pVertex); GL_DrawPolygon(primType, count); GL_EnableState(GL_TEXTURE_COORD_ARRAY); } } /* ============= GL_DrawAliasBlendedShadow fenix@io.com: model animation interpolation ============= */ void GL_DrawAliasBlendedShadow (aliashdr_t *paliashdr, int pose1, int pose2, entity_t* e) { trivertx_t* verts1; trivertx_t* verts2; int* order; vec3_t point1; vec3_t point2; vec3_t d; float height; float lheight; int count; float blend; GL_DisableState(GL_TEXTURE_COORD_ARRAY); blend = (realtime - e->frame_start_time) / e->frame_interval; if (blend > 1) blend = 1; lheight = e->origin[2] - lightspot[2]; height = -lheight + 1.0; verts1 = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); verts2 = verts1; verts1 += pose1 * paliashdr->poseverts; verts2 += pose2 * paliashdr->poseverts; order = (int *)((byte *)paliashdr + paliashdr->commands); for (;;){ // get the vertex count and primitive type count = *order++; if (!count) break; int primType; int c; float* pVertex; if (count < 0){ count = -count; primType = GL_TRIANGLE_FAN; }else{ primType = GL_TRIANGLE_STRIP; } pVertex = gVertexBuffer; c = count; do{ order += 2; point1[0] = verts1->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point1[1] = verts1->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point1[2] = verts1->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point1[0] -= shadevector[0]*(point1[2]+lheight); point1[1] -= shadevector[1]*(point1[2]+lheight); point2[0] = verts2->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; point2[1] = verts2->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; point2[2] = verts2->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; point2[0] -= shadevector[0]*(point2[2]+lheight); point2[1] -= shadevector[1]*(point2[2]+lheight); VectorSubtract(point2, point1, d); gVertexBuffer[0] = point1[0] + (blend * d[0]); gVertexBuffer[1] = point1[1] + (blend * d[1]); gVertexBuffer[2] = height; gVertexBuffer += 3; verts1++; verts2++; } while (--count); const float color[4] = {0,0,0,0.5f}; glUniform4fv(monocolor, 1, color); vglVertexAttribPointerMapped(0, pVertex); GL_DrawPolygon(primType, c); } GL_EnableState(GL_TEXTURE_COORD_ARRAY); } /* ================= R_SetupAliasFrame ================= */ void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr) { int pose, numposes; float interval; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / interval) % numposes; } GL_DrawAliasFrame (paliashdr, pose); } /* ================= R_SetupAliasBlendedFrame fenix@io.com: model animation interpolation ================= */ void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t* e) { int pose; int numposes; float blend; if ((frame >= paliashdr->numframes) || (frame < 0)) { Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); frame = 0; } pose = paliashdr->frames[frame].firstpose; numposes = paliashdr->frames[frame].numposes; if (numposes > 1) { e->frame_interval = paliashdr->frames[frame].interval; pose += (int)(cl.time / e->frame_interval) % numposes; }else { /* One tenth of a second is a good for most Quake animations. If the nextthink is longer then the animation is usually meant to pause (e.g. check out the shambler magic animation in shambler.qc). If its shorter then things will still be smoothed partly, and the jumps will be less noticable because of the shorter time. So, this is probably a good assumption. */ e->frame_interval = 0.1; } if (e->pose2 != pose) { e->frame_start_time = realtime; e->pose1 = e->pose2; e->pose2 = pose; blend = 0; }else{ blend = (realtime - e->frame_start_time) / e->frame_interval; } // wierd things start happening if blend passes 1 if (cl.paused || blend > 1) blend = 1; GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend); } /* ================= R_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *e) { int i, j; int lnum; vec3_t dist; float add; model_t *clmodel; vec3_t mins, maxs; aliashdr_t *paliashdr; trivertx_t *verts, *v; int index; float s, t, an; int anim; bool torch = false; // Flags is this model is a torch clmodel = currententity->model; VectorAdd (currententity->origin, clmodel->mins, mins); VectorAdd (currententity->origin, clmodel->maxs, maxs); if (R_CullBox (mins, maxs)) return; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // // get lighting information // // LordHavoc: .lit support begin R_LightPoint(currententity->origin); // LordHavoc: lightcolor is all that matters from this // LordHavoc: .lit support end // LordHavoc: .lit support begin if (e == &cl.viewent) { if (lightcolor[0] < 24) lightcolor[0] = 24; if (lightcolor[1] < 24) lightcolor[1] = 24; if (lightcolor[2] < 24) lightcolor[2] = 24; } // LordHavoc: .lit support end for (lnum=0 ; lnum= cl.time) { VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - Length(dist); // LordHavoc: .lit support begin if (add > 0) { lightcolor[0] += add * cl_dlights[lnum].color[0]; lightcolor[1] += add * cl_dlights[lnum].color[1]; lightcolor[2] += add * cl_dlights[lnum].color[2]; } // LordHavoc: .lit support end } } // ZOID: never allow players to go totally black i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) { // LordHavoc: .lit support begin if (lightcolor[0] < 8) lightcolor[0] = 8; if (lightcolor[1] < 8) lightcolor[1] = 8; if (lightcolor[2] < 8) lightcolor[2] = 8; // LordHavoc: .lit support end } // HACK HACK HACK -- no fullbright colors, so make torches full light if (!strcmp (clmodel->name, "progs/flame2.mdl") || !strcmp (clmodel->name, "progs/flame.mdl") ) { torch = true; // This model is a torch. KH // LordHavoc: .lit support begin lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; // LordHavoc: .lit support end } shadedots = (float*)r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; // LordHavoc: .lit support begin VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); // LordHavoc: .lit support end float cs[2]; an = e->angles[1]/180*M_PI; sincosf_neon(-an, cs); shadevector[0] = cs[1]; shadevector[1] = cs[0]; shadevector[2] = 1; VectorNormalize (shadevector); // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); c_alias_polys += paliashdr->numtris; // // draw all the triangles // glPushMatrix (); // fenix@io.com: model transform interpolation if (r_interpolate_model_transform.value){ R_BlendedRotateForEntity (e); }else{ R_RotateForEntity (e); } if (!strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - 30); // double size of eyes, since they are really hard to see in gl glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); } else { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); } anim = (int)(cl.time*10) & 3; GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]); // we can't dynamically colormap textures, so they are cached // seperately for the players. Heads are just uncolored. if (currententity->colormap != vid.colormap && !gl_nocolors.value) { i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) GL_Bind(playertextures - 1 + i); } //->if (gl_smoothmodels.value) //-> glShadeModel (GL_SMOOTH); GL_EnableState(GL_MODULATE); //->if (gl_affinemodels.value) //-> glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); // fenix@io.com: model animation interpolation if (r_interpolate_model_animation.value) { R_SetupAliasBlendedFrame (currententity->frame, paliashdr, currententity); }else{ R_SetupAliasFrame (currententity->frame, paliashdr); } GL_EnableState(GL_REPLACE); //->glShadeModel (GL_FLAT); //->if (gl_affinemodels.value) //-> glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glPopMatrix (); if (torch && gl_torchflares.value) { // Draw torch flares. KH // NOTE: It would be better if we batched these up. // All those state changes are not nice. KH // This relies on unchanged game code! const int TORCH_STYLE = 1; // Flicker. vec3_t lightorigin; // Origin of torch. vec3_t v; // Vector to torch. float radius; // Radius of torch flare. float distance; // Vector distance to torch. float intensity; // Intensity of torch flare. // NOTE: I don't think this is centered on the model. VectorCopy(currententity->origin, lightorigin); radius = 20.0f; VectorSubtract(lightorigin, r_origin, v); // See if view is outside the light. distance = Length(v); if (distance > radius) { glDepthMask (0); //->glShadeModel (GL_SMOOTH); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); // Translate the glow to coincide with the flame. KH glPushMatrix(); glTranslatef(0.0f, 0.0f, 8.0f); GL_DisableState(GL_TEXTURE_COORD_ARRAY); GL_EnableState(GL_COLOR_ARRAY); float* pPos = gVertexBuffer; float* pColor = gColorBuffer; // Diminish torch flare inversely with distance. intensity = (1024.0f - distance) / 1024.0f; // Invert (fades as you approach). intensity = (1.0f - intensity); // Clamp, but don't let the flare disappear. if (intensity > 1.0f) intensity = 1.0f; if (intensity < 0.0f) intensity = 0.0f; // Now modulate with flicker. i = (int)(cl.time*10); if (!cl_lightstyle[TORCH_STYLE].length) { j = 256; } else { j = i % cl_lightstyle[TORCH_STYLE].length; j = cl_lightstyle[TORCH_STYLE].map[j] - 'a'; j = j*22; } intensity *= ((float)j / 255.0f); // Set yellow intensity *gColorBuffer++ = 0.8f*intensity; *gColorBuffer++ = 0.4f*intensity; *gColorBuffer++ = 0.1f; *gColorBuffer++ = 1.0f; for (i=0 ; i<3 ; i++) *gVertexBuffer++ = lightorigin[i] - vpn[i]*radius; for (i=16; i>=0; i--) { *gColorBuffer++ = 0.0f; *gColorBuffer++ = 0.0f; *gColorBuffer++ = 0.0f; *gColorBuffer++ = 0.0f; for (j=0; j<3; j++) *gVertexBuffer++ = lightorigin[j] + vright[j]*costablef[i]*radius + vup[j]*sintablef[i]*radius; } vglVertexAttribPointerMapped(0, pPos); vglVertexAttribPointerMapped(1, pColor); GL_DrawPolygon(GL_TRIANGLE_FAN, 18); GL_EnableState(GL_TEXTURE_COORD_ARRAY); GL_DisableState(GL_COLOR_ARRAY); // Restore previous matrix! KH glPopMatrix(); GL_Color(1,1,1,1); glDisable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask (1); } } if (r_shadows.value) { // // Test for models that we don't want to shadow. KH // Not a nice way to do it... // // Torches. Early-out to avoid the strcmp's. KH if (torch) return; // Grenades. KH if (!strcmp (clmodel->name, "progs/grenade.mdl")) return; // Lightning bolts. KH if (!strncmp (clmodel->name, "progs/bolt", 10)) return; glPushMatrix (); // fenix@io.com: model transform interpolation if (r_interpolate_model_transform.value){ R_BlendedRotateForEntity (e); }else{ R_RotateForEntity (e); } GL_DisableState(GL_TEXTURE_COORD_ARRAY); glEnable (GL_BLEND); // fenix@io.com: model animation interpolation if (r_interpolate_model_animation.value){ GL_DrawAliasBlendedShadow (paliashdr, lastposenum0, lastposenum, currententity); }else{ GL_DrawAliasShadow (paliashdr, lastposenum); } GL_EnableState(GL_TEXTURE_COORD_ARRAY); glDisable (GL_BLEND); GL_Color(1,1,1,1); glPopMatrix (); } } //================================================================================== /* ============= R_DrawEntitiesOnList ============= */ void R_DrawEntitiesOnList (void) { int i; if (!r_drawentities.value) return; // draw sprites seperately, because of alpha blending for (i=0 ; iangles[0] *= 0.3; switch (currententity->model->type) { case mod_alias: R_DrawAliasModel (currententity); break; case mod_brush: glEnable(GL_POLYGON_OFFSET_FILL); R_DrawBrushModel (currententity); glDisable(GL_POLYGON_OFFSET_FILL); break; default: break; } } for (i=0 ; imodel->type) { case mod_sprite: R_DrawSpriteModel (currententity); break; } } //glEnable (GL_DEPTH_TEST); } /* ============= R_DrawViewModel ============= */ void R_DrawViewModel (void) { // fenix@io.com: model transform interpolation float old_interpolate_model_transform; if (!r_drawviewmodel.value) return; if (chase_active.value) return; if (envmap) return; if (!r_drawentities.value) return; if (cl.items & IT_INVISIBILITY) return; if (cl.stats[STAT_HEALTH] <= 0) return; currententity = &cl.viewent; if (!currententity->model) return; // hack the depth range to prevent view model from poking into walls glDepthRangef (gldepthmin, gldepthmin + 0.3f*(gldepthmax-gldepthmin)); // fenix@io.com: model transform interpolation old_interpolate_model_transform = r_interpolate_model_transform.value; r_interpolate_model_transform.value = false; R_DrawAliasModel (currententity); r_interpolate_model_transform.value = old_interpolate_model_transform; glDepthRangef (gldepthmin, gldepthmax); } /* ============ R_PolyBlend ============ */ void R_PolyBlend (void) { if (!gl_polyblend.value) return; glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_DisableState(GL_ALPHA_TEST); glEnable (GL_BLEND); glDisable (GL_DEPTH_TEST); GL_DisableState(GL_TEXTURE_COORD_ARRAY); glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up GL_Color(v_blend[0],v_blend[1],v_blend[2],v_blend[3]); if (v_blend[3]) { if (gamma_vertices == NULL) { gamma_vertices = malloc(3 * 4 * sizeof(float)); gamma_vertices[0] = gamma_vertices[3] = gamma_vertices[6] = gamma_vertices[9] = 10; gamma_vertices[1] = gamma_vertices[2] = gamma_vertices[5] = gamma_vertices[10] = 100; gamma_vertices[4] = gamma_vertices[7] = gamma_vertices[8] = gamma_vertices[11] = -100; } vglVertexAttribPointerMapped(0, gamma_vertices); glUniform4fv(monocolor, 1, v_blend); GL_DrawPolygon(GL_TRIANGLE_FAN, 4); } //gamma trick based on LordHavoc - muff if (v_gamma.value != 1) DoGamma(); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); GL_EnableState(GL_TEXTURE_COORD_ARRAY); GL_EnableState(GL_ALPHA_TEST); } int SignbitsForPlane (mplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) bits |= 1< 1) Cvar_Set ("r_fullbright", "0"); R_AnimateLight (); r_framecount++; // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf r_oldviewleaf = r_viewleaf; r_viewleaf = Mod_PointInLeaf (r_origin, cl.worldmodel); V_SetContentsColor (r_viewleaf->contents); V_CalcBlend (); r_cache_thrash = false; c_brush_polys = 0; c_alias_polys = 0; } #define NEARCLIP 4 #define FARCLIP 4096 void GL_SetFrustum(float fovx, float fovy) { float xmax, ymax; xmax = NEARCLIP * tan( fovx * M_PI / 360.0 ); ymax = NEARCLIP * tan( fovy * M_PI / 360.0 ); if (st_separation.value != 0) { float stereo_add = (st_separation.value * st_fustbal.value * (4.0f / (4 + st_zeropdist.value))) * stereoCameraSelect; glFrustum(-xmax + stereo_add, xmax + stereo_add, -ymax, ymax, NEARCLIP, FARCLIP); glTranslatef(st_separation.value * stereoCameraSelect, 0, 0); } else glFrustum(-xmax, xmax, -ymax, ymax, NEARCLIP, FARCLIP); } /* ============= R_SetupGL ============= */ void R_SetupGL (void) { int i; extern int glwidth, glheight; // // set up viewpoint // glMatrixMode(GL_PROJECTION); glLoadIdentity (); glViewport (glx + r_refdef.vrect.x, gly + glheight - r_refdef.vrect.y - r_refdef.vrect.height, r_refdef.vrect.width, r_refdef.vrect.height); GL_SetFrustum (r_refdef.fov_x, r_refdef.fov_y); if (mirror) { if (mirror_plane->normal[2]) glScalef (1, -1, 1); else glScalef (-1, 1, 1); glCullFace(GL_BACK); } else glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); glLoadIdentity (); glRotatef (-90, 1, 0, 0); // put Z going up glRotatef (90, 0, 0, 1); // put Z going up if (gl_xflip.value){ glScalef (1, -1, 1); glCullFace(GL_BACK); } glRotatef (-r_refdef.viewangles[2], 1, 0, 0); glRotatef (-r_refdef.viewangles[0], 0, 1, 0); glRotatef (-r_refdef.viewangles[1], 0, 0, 1); glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]); glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix); // // set drawing parms // if (gl_cull.value) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); glDisable(GL_BLEND); GL_DisableState(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); } /* ================ R_RenderScene r_refdef must be set before the first call ================ */ void R_RenderScene (void) { R_SetupFrame (); R_SetFrustum (); R_SetupGL (); R_MarkLeaves (); // done here so we know if we're in water R_DrawWorld (); // adds static entities to the list S_ExtraUpdate (); // don't let sound get messed up if going slow R_DrawEntitiesOnList (); R_RenderDlights (); R_DrawParticles (); } /* ============= R_Clear ============= */ void R_Clear (void) { if (gl_clear.value){ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }else{ glClear (GL_DEPTH_BUFFER_BIT); } if (r_mirroralpha.value != 1.0 && st_separation.value == 0) gldepthmax = 0.5; else gldepthmax = 1; gldepthmin = 0; glDepthFunc (GL_LEQUAL); glDepthRangef (gldepthmin, gldepthmax); } /* ============= R_Mirror ============= */ void R_Mirror (void) { float d; msurface_t *s; entity_t *ent; if (!mirror) return; memcpy (r_base_world_matrix, r_world_matrix, sizeof(r_base_world_matrix)); d = DotProduct (r_refdef.vieworg, mirror_plane->normal) - mirror_plane->dist; VectorMA (r_refdef.vieworg, -2*d, mirror_plane->normal, r_refdef.vieworg); d = DotProduct (vpn, mirror_plane->normal); VectorMA (vpn, -2*d, mirror_plane->normal, vpn); r_refdef.viewangles[0] = -asin (vpn[2])/M_PI*180; r_refdef.viewangles[1] = atan2 (vpn[1], vpn[0])/M_PI*180; r_refdef.viewangles[2] = -r_refdef.viewangles[2]; ent = &cl_entities[cl.viewentity]; if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } gldepthmin = 0.5f; gldepthmax = 1.0f; glDepthRangef (gldepthmin, gldepthmax); glDepthFunc (GL_LEQUAL); R_RenderScene (); R_DrawWaterSurfaces (); gldepthmin = 0.0f; gldepthmax = 0.5f; glDepthRangef (gldepthmin, gldepthmax); glDepthFunc (GL_LEQUAL); // blend on top glEnable (GL_BLEND); //mirror fix - from QER GL_EnableState(GL_MODULATE); //mirror fix glMatrixMode(GL_PROJECTION); if (mirror_plane->normal[2]) glScalef (1,-1,1); else glScalef (-1,1,1); glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); glLoadMatrixf (r_base_world_matrix); GL_Color(1,1,1,st_separation.value != 0 ? 1 : r_mirroralpha.value); s = cl.worldmodel->textures[mirrortexturenum]->texturechain; for ( ; s ; s=s->texturechain) R_RenderBrushPoly (s); cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; glDisable (GL_BLEND); GL_Color(1,1,1,1); //mirror fix - from QER GL_EnableState(GL_REPLACE); //mirror fix } /* ================ R_RenderView r_refdef must be set before the first call ================ */ void R_RenderView (void) { double time1, time2; if (r_norefresh.value) return; if (!r_worldentity.model || !cl.worldmodel) Sys_Error ("R_RenderView: NULL worldmodel"); if (r_speeds.value) { time1 = Sys_FloatTime (); c_brush_polys = 0; c_alias_polys = 0; } mirror = false; if (st_separation.value != 0) { stereoCameraSelect = 1; glColorMask( GL_TRUE, GL_FALSE, GL_FALSE ,GL_TRUE ); R_Clear (); R_RenderScene (); R_DrawViewModel (); R_DrawWaterSurfaces (); R_Mirror (); stereoCameraSelect = -1; glColorMask( GL_FALSE, GL_TRUE, GL_TRUE ,GL_TRUE ); R_Clear (); R_RenderScene (); R_DrawViewModel (); R_DrawWaterSurfaces (); R_Mirror (); glColorMask( GL_TRUE, GL_TRUE, GL_TRUE ,GL_TRUE ); } else { R_Clear (); R_RenderScene (); R_DrawViewModel (); R_DrawWaterSurfaces (); R_Mirror (); } R_PolyBlend (); if (r_speeds.value) { time2 = Sys_FloatTime (); Con_Printf ("%3i ms %4i wpoly %4i epoly\n", (int)((time2-time1)*1000), c_brush_polys, c_alias_polys); } } ================================================ FILE: source/gl_rmisc.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_misc.c #include "quakedef.h" extern cvar_t gl_torchflares; extern cvar_t gl_compress; /* ================== R_InitTextures ================== */ void R_InitTextures (void) { int x,y, m; byte *dest; // create a simple checkerboard texture for the default r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture"); r_notexture_mip->width = r_notexture_mip->height = 16; r_notexture_mip->offsets[0] = sizeof(texture_t); r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16; r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8; r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4; for (m=0 ; m<4 ; m++) { dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m]; for (y=0 ; y< (16>>m) ; y++) for (x=0 ; x< (16>>m) ; x++) { if ( (y< (8>>m) ) ^ (x< (8>>m) ) ) *dest++ = 0; else *dest++ = 0xff; } } } byte dottexture[8][8] = { {0,1,1,0,0,0,0,0}, {1,1,1,1,0,0,0,0}, {1,1,1,1,0,0,0,0}, {0,1,1,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, }; void R_InitParticleTexture (void) { int x,y; byte data[8][8][4]; // // particle texture // particletexture = texture_extension_number++; GL_Bind(particletexture); for (x=0 ; x<8 ; x++) { for (y=0 ; y<8 ; y++) { data[y][x][0] = 255; data[y][x][1] = 255; data[y][x][2] = 255; data[y][x][3] = dottexture[x][y]*255; } } glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); GL_EnableState(GL_MODULATE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } /* =============== R_Envmap_f Grab six views for environment mapping tests =============== */ void R_Envmap_f (void) { } /* =============== R_Init =============== */ void R_Init (void) { extern byte *hunk_base; extern cvar_t gl_finish; Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); Cmd_AddCommand ("envmap", R_Envmap_f); Cmd_AddCommand ("pointfile", R_ReadPointFile_f); Cvar_RegisterVariable (&r_norefresh); Cvar_RegisterVariable (&r_lightmap); Cvar_RegisterVariable (&r_fullbright); Cvar_RegisterVariable (&r_drawentities); Cvar_RegisterVariable (&r_drawviewmodel); Cvar_RegisterVariable (&r_shadows); Cvar_RegisterVariable (&r_mirroralpha); Cvar_RegisterVariable (&r_wateralpha); Cvar_RegisterVariable (&r_dynamic); Cvar_RegisterVariable (&r_novis); Cvar_RegisterVariable (&r_speeds); // fenix@io.com: register new cvars for model interpolation Cvar_RegisterVariable (&r_interpolate_model_animation); Cvar_RegisterVariable (&r_interpolate_model_transform); Cvar_RegisterVariable (&gl_finish); Cvar_RegisterVariable (&gl_clear); Cvar_RegisterVariable (&gl_texsort); Cvar_RegisterVariable (&gl_compress); Cvar_RegisterVariable (&gl_cull); Cvar_RegisterVariable (&gl_smoothmodels); Cvar_RegisterVariable (&gl_affinemodels); Cvar_RegisterVariable (&gl_polyblend); Cvar_RegisterVariable (&gl_flashblend); Cvar_RegisterVariable (&gl_playermip); Cvar_RegisterVariable (&gl_nocolors); Cvar_RegisterVariable (&gl_keeptjunctions); Cvar_RegisterVariable (&gl_reporttjunctions); Cvar_RegisterVariable (&gl_doubleeyes); Cvar_RegisterVariable (&gl_torchflares); // Torch flares. KH Cvar_RegisterVariable (&gl_xflip); Cvar_RegisterVariable (&gl_overbright); R_InitParticles (); R_InitParticleTexture (); playertextures = texture_extension_number; texture_extension_number += 16; } /* =============== R_TranslatePlayerSkin Translates a skin texture by the per-player color lookup =============== */ void R_TranslatePlayerSkin (int playernum) { int top, bottom; byte *translate = malloc(256*sizeof(byte)); unsigned *translate32 = malloc(256*sizeof(unsigned)); int i, j, s; model_t *model; aliashdr_t *paliashdr; byte *original; unsigned *pixels, *out; unsigned scaled_width, scaled_height; int inwidth, inheight; byte *inrow; unsigned frac, fracstep; extern byte **player_8bit_texels_tbl; top = cl.scores[playernum].colors & 0xf0; bottom = (cl.scores[playernum].colors &15)<<4; for (i=0 ; i<256 ; i++) translate[i] = i; for (i=0 ; i<16 ; i++) { if (top < 128) // the artists made some backwards ranges. sigh. translate[TOP_RANGE+i] = top+i; else translate[TOP_RANGE+i] = top+15-i; if (bottom < 128) translate[BOTTOM_RANGE+i] = bottom+i; else translate[BOTTOM_RANGE+i] = bottom+15-i; } // // locate the original skin pixels // currententity = &cl_entities[1+playernum]; model = currententity->model; if (!model){ free(translate); free(translate32); return; // player doesn't have a model yet } if (model->type != mod_alias){ free(translate); free(translate32); return; // only translate skins on alias models } paliashdr = (aliashdr_t *)Mod_Extradata (model); s = paliashdr->skinwidth * paliashdr->skinheight; if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->numskins) { Con_Printf("(%d): Invalid player skin #%d\n", playernum, currententity->skinnum); original = (byte *)paliashdr + paliashdr->texels[0]; } else original = (byte *)paliashdr + paliashdr->texels[currententity->skinnum]; if (s & 3) Sys_Error ("R_TranslateSkin: s&3"); inwidth = paliashdr->skinwidth; inheight = paliashdr->skinheight; // because this happens during gameplay, do it fast // instead of sending it through gl_upload 8 GL_Bind(playertextures + playernum); scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512; scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256; // allow users to crunch sizes down even more if they want scaled_width >>= (int)gl_playermip.value; scaled_height >>= (int)gl_playermip.value; #define PIXEL_COUNT (512*256) #define PIXELS_SIZE (PIXEL_COUNT * sizeof(unsigned)) pixels = (unsigned*) malloc(PIXELS_SIZE); if(!pixels) { free(translate); free(translate32); Sys_Error("Out of memory."); } for (i=0 ; i<256 ; i++) translate32[i] = d_8to24table[translate[i]]; out = pixels; fracstep = inwidth*0x10000/scaled_width; for (i=0 ; i> 1; for (j=0 ; j>16]]; frac += fracstep; out[j+1] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+2] = translate32[inrow[frac>>16]]; frac += fracstep; out[j+3] = translate32[inrow[frac>>16]]; frac += fracstep; } } glTexImage2D (GL_TEXTURE_2D, 0, gl_compress.value ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); GL_EnableState(GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); free(pixels); free(translate); free(translate32); } /* =============== R_NewMap =============== */ void R_NewMap (void) { int i; for (i=0 ; i<256 ; i++) d_lightstylevalue[i] = 264; // normal light value memset (&r_worldentity, 0, sizeof(r_worldentity)); r_worldentity.model = cl.worldmodel; // clear out efrags in case the level hasn't been reloaded // FIXME: is this one short? for (i=0 ; inumleafs ; i++) cl.worldmodel->leafs[i].efrags = NULL; r_viewleaf = NULL; R_ClearParticles (); GL_BuildLightmaps (); // identify sky texture skytexturenum = -1; mirrortexturenum = -1; for (i=0 ; inumtextures ; i++) { if (!cl.worldmodel->textures[i]) continue; if (!strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) skytexturenum = i; if (!strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) ) mirrortexturenum = i; cl.worldmodel->textures[i]->texturechain = NULL; } } /* ==================== R_TimeRefresh_f For program optimization ==================== */ void R_TimeRefresh_f (void) { } void D_FlushCaches (void) { } ================================================ FILE: source/gl_rsurf.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_surf.c: surface-related refresh code #include "quakedef.h" #include "gl_fullbright.h" #include int skytexturenum; #ifndef GL_RGBA4 #define GL_RGBA4 0 #endif int lightmap_bytes; // 1, 2, or 4 int lightmap_textures; extern int gl_filter_min; extern int gl_filter_max; unsigned blocklights[3*18*18]; // LordHavoc: .lit support (*3 for RGB) to the definitions at the top #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 #define MAX_LIGHTMAPS 64 int active_lightmaps; typedef struct glRect_s { unsigned char l,t,w,h; } glRect_t; glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; bool lightmap_modified[MAX_LIGHTMAPS]; glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; // the lightmap texture data needs to be kept in // main memory so texsubimage can update properly byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; // For gl_texsort 0 msurface_t *skychain = NULL; msurface_t *waterchain = NULL; /* =============== R_AddDynamicLights =============== */ void R_AddDynamicLights (msurface_t *surf) { // LordHavoc: .lit support begin float cred, cgreen, cblue, brightness; unsigned *bl; // LordHavoc: .lit support end int lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; tex = surf->texinfo; for (lnum=0 ; lnumdlightbits & (1<plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = cl_dlights[lnum].minlight; if (rad < minlight) continue; minlight = rad - minlight; for (i=0 ; i<3 ; i++) { impact[i] = cl_dlights[lnum].origin[i] - surf->plane->normal[i]*dist; } local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; local[0] -= surf->texturemins[0]; local[1] -= surf->texturemins[1]; // LordHavoc: .lit support begin bl = blocklights; cred = cl_dlights[lnum].color[0] * 256.0f; cgreen = cl_dlights[lnum].color[1] * 256.0f; cblue = cl_dlights[lnum].color[2] * 256.0f; // LordHavoc: .lit support end for (t = 0 ; t td) dist = sd + (td>>1); else dist = td + (sd>>1); if (dist < minlight){ // LordHavoc: .lit support begin brightness = rad - dist; bl[0] += (int) (brightness * cred); bl[1] += (int) (brightness * cgreen); bl[2] += (int) (brightness * cblue); // LordHavoc: .lit support end } bl += 3; } } } } /* =============== R_BuildLightMap Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) { int blocksize, smax, tmax; int t; int i, j, size; byte *lightmap; unsigned scale; int maps; int lightadj[4]; unsigned *bl; surf->cached_dlight = (surf->dlightframe == r_framecount); smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; size = smax*tmax; lightmap = surf->samples; // set to full bright if no light data if (r_fullbright.value || !cl.worldmodel->lightdata) { // LordHavoc: .lit support begin bl = blocklights; for (i=0 ; istyles[maps] != 255; maps++) { scale = (float)d_lightstylevalue[surf->styles[maps]]; surf->cached_light[maps] = scale; // 8.8 fraction // LordHavoc: .lit support begin bl = blocklights; for (i=0 ; idlightframe == r_framecount) R_AddDynamicLights (surf); // bound, invert, and shift store: stride -= (smax<<2); bl = blocklights; for (i=0 ; i= 2) { for (j=0 ; j> 7;if (t > 255) t = 255;*dest++ = t; t = bl[1] >> 7;if (t > 255) t = 255;*dest++ = t; t = bl[2] >> 7;if (t > 255) t = 255;*dest++ = t; bl += 3; *dest++ = 255; // LordHavoc: .lit support end } } else if (gl_overbright.value) { for (j=0 ; j> 8) + (t >> 9);if (t > 255) t = 255;*dest++ = t; t = bl[1]; t = (t >> 8) + (t >> 9);if (t > 255) t = 255;*dest++ = t; t = bl[2]; t = (t >> 8) + (t >> 9);if (t > 255) t = 255;*dest++ = t; bl += 3; *dest++ = 255; // LordHavoc: .lit support end } } else { for (j=0 ; j> 8;if (t > 255) t = 255;*dest++ = t; t = bl[1] >> 8;if (t > 255) t = 255;*dest++ = t; t = bl[2] >> 8;if (t > 255) t = 255;*dest++ = t; bl += 3; *dest++ = 255; // LordHavoc: .lit support end } } } } /* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ texture_t *R_TextureAnimation (texture_t *base) { int reletive; int count; if (currententity->frame) { if (base->alternate_anims) base = base->alternate_anims; } if (!base->anim_total) return base; reletive = (int)(cl.time*10) % base->anim_total; count = 0; while (base->anim_min > reletive || base->anim_max <= reletive) { base = base->anim_next; if (!base) Sys_Error ("R_TextureAnimation: broken cycle"); if (++count > 100) Sys_Error ("R_TextureAnimation: infinite cycle"); } return base; } /* ============================================================= BRUSH MODELS ============================================================= */ extern int solidskytexture; extern int alphaskytexture; extern float speedscale; // for top sky and bottom sky void DrawGLWaterPoly (glpoly_t *p); void DrawGLWaterPolyLightmap (glpoly_t *p); /* ================ DrawGLWaterPoly Warp the vertex coordinates ================ */ void DrawGLWaterPoly (glpoly_t *p) { int i; float *v; float s, t, os, ot; vec3_t nv; v = p->verts[0]; float* pnv = gVertexBuffer; float* pnt = gTexCoordBuffer; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { float sinv2 = sinf_neon(v[2]*0.05f+realtime); gVertexBuffer[0] = v[0] + 8*sinf_neon(v[1]*0.05f+realtime)*sinv2; gVertexBuffer[1] = v[1] + 8*sinf_neon(v[0]*0.05f+realtime)*sinv2; gVertexBuffer[2] = v[2]; gTexCoordBuffer[0] = v[3]; gTexCoordBuffer[1] = v[4]; gVertexBuffer +=3; gTexCoordBuffer +=2; } vglVertexAttribPointerMapped(0, pnv); vglVertexAttribPointerMapped(1, pnt); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); } void DrawGLWaterPolyLightmap (glpoly_t *p) { int i; float *v; float s, t, os, ot; vec3_t nv; v = p->verts[0]; float* pnv = gVertexBuffer; float* pnt = gTexCoordBuffer; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { float sinv2 = sinf_neon(v[2]*0.05f+realtime); gVertexBuffer[0] = v[0] + 8*sinf_neon(v[1]*0.05f+realtime)*sinv2; gVertexBuffer[1] = v[1] + 8*sinf_neon(v[0]*0.05f+realtime)*sinv2; gVertexBuffer[2] = v[2]; gTexCoordBuffer[0] = v[5]; gTexCoordBuffer[1] = v[6]; gVertexBuffer +=3; gTexCoordBuffer +=2; } vglVertexAttribPointerMapped(0, pnv); vglVertexAttribPointerMapped(1, pnt); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); } void DrawGLWaterPolyWithLightmap(glpoly_t *p, int t1, int t2) { GL_Bind (t1); int i; float *v; float s, t, os, ot; vec3_t nv; v = p->verts[0]; float *pnv = gVertexBuffer; float *pnt = gTexCoordBuffer; float *pnt2 = gTexCoordBuffer; pnt2 += ((p->numverts+1) * 2); float *gTexCoordBuffer2 = pnt2; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { float sinv2 = sinf_neon(v[2]*0.05f+realtime); gVertexBuffer[0] = v[0] + 8*sinf_neon(v[1]*0.05f+realtime)*sinv2; gVertexBuffer[1] = v[1] + 8*sinf_neon(v[0]*0.05f+realtime)*sinv2; gVertexBuffer[2] = v[2]; gTexCoordBuffer[0] = v[3]; gTexCoordBuffer[1] = v[4]; gTexCoordBuffer2[0] = v[5]; gTexCoordBuffer2[1] = v[6]; gVertexBuffer +=3; gTexCoordBuffer +=2; gTexCoordBuffer2 +=2; } vglVertexAttribPointerMapped(0, pnv); vglVertexAttribPointerMapped(1, pnt); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); GL_Bind (t2); glEnable (GL_BLEND); vglVertexAttribPointerMapped(1, pnt2); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); glDisable (GL_BLEND); gTexCoordBuffer = gTexCoordBuffer2; } /* ================ DrawGLPoly ================ */ void DrawGLPoly (glpoly_t *p) { int i; float* v = p->verts[0]; float* pnv = gVertexBuffer; float* pnt = gTexCoordBuffer; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { gVertexBuffer[0] = v[0]; gVertexBuffer[1] = v[1]; gVertexBuffer[2] = v[2]; gTexCoordBuffer[0] = v[3]; gTexCoordBuffer[1] = v[4]; gVertexBuffer += 3; gTexCoordBuffer +=2; } vglVertexAttribPointerMapped(0, pnv); vglVertexAttribPointerMapped(1, pnt); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); } void DrawGLPolyLightmap (glpoly_t *p) { int i; float* v = p->verts[0]; float* pnv = gVertexBuffer; float* pnt = gTexCoordBuffer; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { gVertexBuffer[0] = v[0]; gVertexBuffer[1] = v[1]; gVertexBuffer[2] = v[2]; gTexCoordBuffer[0] = v[5]; gTexCoordBuffer[1] = v[6]; gVertexBuffer += 3; gTexCoordBuffer +=2; } vglVertexAttribPointerMapped(0, pnv); vglVertexAttribPointerMapped(1, pnt); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); } /* ================ R_BlendLightmaps ================ */ void R_BlendLightmaps (void) { int i, j; glpoly_t *p; float *v; glRect_t *theRect; if (r_fullbright.value) return; GL_EnableState(GL_MODULATE); GL_Color(1,1,1,1); glDepthMask(GL_FALSE); // don't bother writing Z glBlendFunc (GL_ZERO, GL_SRC_COLOR); glEnable (GL_BLEND); for (i=0 ; it, BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); theRect->l = BLOCK_WIDTH; theRect->t = BLOCK_HEIGHT; theRect->h = 0; theRect->w = 0; } for ( ; p ; p=p->chain) { if (p->flags & SURF_UNDERWATER) DrawGLWaterPolyLightmap (p); else { DrawGLPolyLightmap(p); } } } GL_EnableState(GL_REPLACE); glDisable (GL_BLEND); glDepthMask (GL_TRUE); // back to normal Z buffering } /* ================ R_RenderBrushPoly ================ */ void R_RenderBrushPoly (msurface_t *fa) { texture_t *t; byte *base; int maps; glRect_t *theRect; int smax, tmax; c_brush_polys++; if (fa->flags & SURF_DRAWSKY) { // warp texture, no lightmaps EmitBothSkyLayers (fa); return; } t = R_TextureAnimation (fa->texinfo->texture); GL_Bind (t->gl_texturenum); if (fa->flags & SURF_DRAWTURB) { // warp texture, no lightmaps EmitWaterPolys (fa); return; } if (fa->flags & SURF_UNDERWATER) DrawGLWaterPoly (fa->polys); else DrawGLPoly (fa->polys); fa->draw_this_frame = 1; // add the poly to the proper lightmap chain fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; lightmap_polys[fa->lightmaptexturenum] = fa->polys; if (fa->overbright != gl_overbright.value)//MH { fa->overbright = gl_overbright.value; goto dynamic; } // check for lightmap modification for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; maps++) if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic; if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic: if (r_dynamic.value) { lightmap_modified[fa->lightmaptexturenum] = true; theRect = &lightmap_rectchange[fa->lightmaptexturenum]; if (fa->light_t < theRect->t) { if (theRect->h) theRect->h += theRect->t - fa->light_t; theRect->t = fa->light_t; } if (fa->light_s < theRect->l) { if (theRect->w) theRect->w += theRect->l - fa->light_s; theRect->l = fa->light_s; } smax = (fa->extents[0]>>4)+1; tmax = (fa->extents[1]>>4)+1; if ((theRect->w + theRect->l) < (fa->light_s + smax)) theRect->w = (fa->light_s-theRect->l)+smax; if ((theRect->h + theRect->t) < (fa->light_t + tmax)) theRect->h = (fa->light_t-theRect->t)+tmax; base = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); } } } /* ================ R_MirrorChain ================ */ void R_MirrorChain (msurface_t *s) { if (mirror) return; mirror = true; mirror_plane = s->plane; } /* ================ R_DrawWaterSurfaces ================ */ void R_DrawWaterSurfaces (void) { int i; msurface_t *s; texture_t *t; if (r_wateralpha.value == 1.0) return; // // go back to the world matrix // glLoadMatrixf (r_world_matrix); if (r_wateralpha.value < 1.0) { glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_EnableState(GL_MODULATE); GL_DisableState(GL_ALPHA_TEST); glDepthMask(GL_FALSE); glEnable (GL_BLEND); GL_Color(1,1,1,r_wateralpha.value); GL_EnableState(GL_MODULATE); } for (i=0 ; inumtextures ; i++) { t = cl.worldmodel->textures[i]; if (!t) continue; s = t->texturechain; if (!s) continue; if ( !(s->flags & SURF_DRAWTURB ) ) continue; // set modulate mode explicitly GL_Bind (t->gl_texturenum); for ( ; s ; s=s->texturechain) EmitWaterPolys (s); t->texturechain = NULL; } if (r_wateralpha.value < 1.0) { GL_EnableState(GL_REPLACE); GL_EnableState(GL_ALPHA_TEST); GL_Color(1,1,1,1); glDisable (GL_BLEND); glDepthMask(GL_TRUE); } } /* ================ DrawTextureChains ================ */ void DrawTextureChains (void) { int i; msurface_t *s; texture_t *t; for (i=0 ; inumtextures ; i++) { t = cl.worldmodel->textures[i]; if (!t) continue; s = t->texturechain; if (!s) continue; if (i == skytexturenum) R_DrawSkyChain (s); else if (i == mirrortexturenum && r_mirroralpha.value != 1.0 && st_separation.value == 0) { R_MirrorChain (s); continue; } else { if ((s->flags & SURF_DRAWTURB) && r_wateralpha.value != 1.0) continue; // draw translucent water later for ( ; s ; s=s->texturechain) R_RenderBrushPoly (s); } t->texturechain = NULL; } } /* ================= R_DrawBrushModel ================= */ void R_DrawBrushModel (entity_t *e) { int j, k; vec3_t mins, maxs; int i, numsurfaces; msurface_t *psurf; float dot; mplane_t *pplane; model_t *clmodel; bool rotated; bool transparent; currententity = e; currenttexture = -1; clmodel = e->model; if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; for (i=0 ; i<3 ; i++) { mins[i] = e->origin[i] - clmodel->radius; maxs[i] = e->origin[i] + clmodel->radius; } } else { rotated = false; VectorAdd (e->origin, clmodel->mins, mins); VectorAdd (e->origin, clmodel->maxs, maxs); } if (R_CullBox (mins, maxs)) return; transparent = ISTRANSPARENT(e); if (transparent) { glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_EnableState(GL_MODULATE); GL_DisableState(GL_ALPHA_TEST); glDepthMask(GL_FALSE); GL_Color(1,1,1,e->alpha); } else GL_Color(1,1,1,1); memset (lightmap_polys, 0, sizeof(lightmap_polys)); VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (rotated) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; // calculate dynamic lighting for bmodel if it's not an // instanced model if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value) { for (k=0 ; knodes + clmodel->hulls[0].firstclipnode); } } glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug R_RotateForEntity (e); e->angles[0] = -e->angles[0]; // stupid quake bug // // draw texture // for (i=0 ; inummodelsurfaces ; i++, psurf++) { // find which side of the node we are on pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { R_RenderBrushPoly (psurf); if (transparent) psurf->draw_this_frame = 0; } } if (transparent) GL_EnableState(GL_ALPHA_TEST); //else FIXME: Disabling lightmaps (as it should be) causes some glitches related to some depth bug most likely R_BlendLightmaps (); DrawFullBrightTextures (clmodel->surfaces, clmodel->numsurfaces); glPopMatrix (); } /* ============================================================= WORLD MODEL ============================================================= */ /* ================ R_RecursiveWorldNode ================ */ void R_RecursiveWorldNode (mnode_t *node) { int i, c, side, *pindex; vec3_t acceptpt, rejectpt; mplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; double d, dot; vec3_t mins, maxs; if (node->contents == CONTENTS_SOLID) return; // solid if (node->visframe != r_visframecount) return; if (R_CullBox (node->minmaxs, node->minmaxs+3)) return; // if a leaf node, draw stuff if (node->contents < 0) { pleaf = (mleaf_t *)node; mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } // deal with model fragments in this leaf if (pleaf->efrags) R_StoreEfrags (&pleaf->efrags); return; } // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = node->plane; switch (plane->type) { case PLANE_X: dot = modelorg[0] - plane->dist; break; case PLANE_Y: dot = modelorg[1] - plane->dist; break; case PLANE_Z: dot = modelorg[2] - plane->dist; break; default: dot = DotProduct (modelorg, plane->normal) - plane->dist; break; } if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode (node->children[side]); // draw stuff c = node->numsurfaces; if (c) { surf = cl.worldmodel->surfaces + node->firstsurface; if (dot < 0 -BACKFACE_EPSILON) side = SURF_PLANEBACK; else if (dot > BACKFACE_EPSILON) side = 0; { for ( ; c ; c--, surf++) { if (surf->visframe != r_framecount) continue; // don't backface underwater surfaces, because they warp if ( !(surf->flags & SURF_UNDERWATER) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) ) continue; // wrong side // just store it out if (!mirror || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) { surf->texturechain = surf->texinfo->texture->texturechain; surf->texinfo->texture->texturechain = surf; } } } } // recurse down the back side R_RecursiveWorldNode (node->children[!side]); } /* ============= R_DrawWorld ============= */ void R_DrawWorld (void) { entity_t ent; int i; memset (&ent, 0, sizeof(ent)); ent.model = cl.worldmodel; VectorCopy (r_refdef.vieworg, modelorg); currententity = &ent; currenttexture = -1; GL_Color(1,1,1,1); memset (lightmap_polys, 0, sizeof(lightmap_polys)); R_RecursiveWorldNode (cl.worldmodel->nodes); DrawTextureChains (); R_BlendLightmaps (); DrawFullBrightTextures (cl.worldmodel->surfaces, cl.worldmodel->numsurfaces); } /* =============== R_MarkLeaves =============== */ void R_MarkLeaves (void) { byte *vis; mnode_t *node; int i; byte solid[4096]; if (r_oldviewleaf == r_viewleaf && !r_novis.value) return; if (mirror) return; r_visframecount++; r_oldviewleaf = r_viewleaf; if (r_novis.value) { vis = solid; memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); } else vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); for (i=0 ; inumleafs ; i++) { if (vis[i>>3] & (1<<(i&7))) { node = (mnode_t *)&cl.worldmodel->leafs[i+1]; do { if (node->visframe == r_visframecount) break; node->visframe = r_visframecount; node = node->parent; } while (node); } } } /* ============================================================================= LIGHTMAP ALLOCATION ============================================================================= */ // returns a texture number and the position inside it int AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int bestx; int texnum; for (texnum=0 ; texnum= best) break; if (allocated[texnum][i+j] > best2) best2 = allocated[texnum][i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) continue; for (i=0 ; iedges; lnumverts = fa->numedges; vertpage = 0; // // draw texture // poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; poly->numverts = lnumverts; for (i=0 ; isurfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = r_pcurrentvertbase[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = r_pcurrentvertbase[r_pedge->v[1]].position; } s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->texture->height; VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // // lightmap texture coordinates // s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s*16; s += 8; s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t*16; t += 8; t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; poly->verts[i][5] = s; poly->verts[i][6] = t; } // // remove co-linear points - Ed // if (!gl_keeptjunctions.value && !(fa->flags & SURF_UNDERWATER) ) { for (i = 0 ; i < lnumverts ; ++i) { vec3_t v1, v2; float *prev, *this, *next; float f; prev = poly->verts[(i + lnumverts - 1) % lnumverts]; this = poly->verts[i]; next = poly->verts[(i + 1) % lnumverts]; VectorSubtract( this, prev, v1 ); VectorNormalize( v1 ); VectorSubtract( next, prev, v2 ); VectorNormalize( v2 ); // skip co-linear points #define COLINEAR_EPSILON 0.001 if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) { int j; for (j = i + 1; j < lnumverts; ++j) { int k; for (k = 0; k < VERTEXSIZE; ++k) poly->verts[j - 1][k] = poly->verts[j][k]; } --lnumverts; ++nColinElim; // retry next vertex next time, which is now current vertex --i; } } } poly->numverts = lnumverts; } /* ======================== GL_CreateSurfaceLightmap ======================== */ void GL_CreateSurfaceLightmap (msurface_t *surf) { int smax, tmax, s; //, t, l, i; byte *base; if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) return; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); } /* ================== GL_BuildLightmaps Builds the lightmap texture with all the surfaces from all brush models ================== */ void GL_BuildLightmaps (void) { int i, j; model_t *m; extern bool isPermedia; memset (allocated, 0, sizeof(allocated)); r_framecount = 1; // no dlightcache if (!lightmap_textures) { lightmap_textures = texture_extension_number; texture_extension_number += MAX_LIGHTMAPS; } gl_lightmap_format = GL_RGBA; lightmap_bytes = 4; for (j=1 ; jname[0] == '*') continue; r_pcurrentvertbase = m->vertexes; currentmodel = m; for (i=0 ; inumsurfaces ; i++) { GL_CreateSurfaceLightmap (m->surfaces + i); if ( m->surfaces[i].flags & SURF_DRAWTURB ) continue; if ( m->surfaces[i].flags & SURF_DRAWSKY ) continue; BuildSurfaceDisplayList (m->surfaces + i); } } // // upload all lightmaps that were filled // for (i=0 ; i scr_erase_lines) scr_erase_lines = scr_center_lines; scr_centertime_off -= host_frametime; if (scr_centertime_off <= 0 && !cl.intermission) return; if (key_dest != key_game) return; SCR_DrawCenterString (); } //============================================================================= /* ==================== CalcFov ==================== */ float CalcFov (float fov_x, float width, float height) { float a; float x; if (fov_x < 1 || fov_x > 179) Sys_Error ("Bad fov: %f", fov_x); x = width/tan(fov_x/360*M_PI); a = atan (height/x); a = a*360/M_PI; return a; } /* ================= SCR_CalcRefdef Must be called whenever vid changes Internal use only ================= */ static void SCR_CalcRefdef (void) { vrect_t vrect; float size, scale; int h; bool full = false; scr_fullupdate = 0; // force a background redraw vid.recalc_refdef = 0; // force the status bar to redraw Sbar_Changed (); //======================================== // bound viewsize if (viewsize.value < 30) Cvar_Set ("viewsize","30"); if (viewsize.value > 120) Cvar_Set ("viewsize","120"); // bound field of view if (fov.value < 10) Cvar_Set ("fov","10"); if (fov.value > 170) Cvar_Set ("fov","170"); vid.recalc_refdef = 0; //johnfitz -- rewrote this section size = viewsize.value; scale = Q_CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); if (size >= 120 || cl.intermission || scr_sbaralpha.value < 1) //johnfitz -- scr_sbaralpha.value sb_lines = 0; else if (size >= 110) sb_lines = 24 * scale; else sb_lines = 48 * scale; size = fmin(viewsize.value, 100) / 100; //johnfitz //johnfitz -- rewrote this section r_refdef.vrect.width = fmax(glwidth * size, 96); //no smaller than 96, for icons r_refdef.vrect.height = fmin(glheight * size, glheight - sb_lines); //make room for sbar r_refdef.vrect.x = (glwidth - r_refdef.vrect.width)/2; r_refdef.vrect.y = (glheight - sb_lines - r_refdef.vrect.height)/2; //johnfitz r_refdef.fov_x = fov.value; r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height); scr_vrect = r_refdef.vrect; } /* ================= SCR_SizeUp_f Keybinding command ================= */ void SCR_SizeUp_f (void) { Cvar_SetValue ("viewsize", viewsize.value+10); vid.recalc_refdef = 1; } /* ================= SCR_SizeDown_f Keybinding command ================= */ void SCR_SizeDown_f (void) { Cvar_SetValue ("viewsize", viewsize.value-10); vid.recalc_refdef = 1; } //============================================================================ /* ================== SCR_Init ================== */ void SCR_Init (void) { Cvar_RegisterVariable (&viewsize); Cvar_RegisterVariable (&fov); Cvar_RegisterVariable (&scr_sbaralpha); Cvar_RegisterVariable (&scr_conalpha); Cvar_RegisterVariable (&scr_conwidth); Cvar_RegisterVariable (&scr_conscale); Cvar_RegisterVariable (&scr_menuscale); Cvar_RegisterVariable (&scr_sbarscale); Cvar_RegisterVariable (&scr_conspeed); Cvar_RegisterVariable (&showram); Cvar_RegisterVariable (&showturtle); Cvar_RegisterVariable (&showpause); Cvar_RegisterVariable (&scr_centertime); Cvar_RegisterVariable (&scr_printspeed); Cvar_RegisterVariable (&gl_triplebuffer); // // register our commands // Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); Cmd_AddCommand ("sizeup",SCR_SizeUp_f); Cmd_AddCommand ("sizedown",SCR_SizeDown_f); scr_ram = Draw_PicFromWad ("ram"); scr_net = Draw_PicFromWad ("net"); scr_turtle = Draw_PicFromWad ("turtle"); scr_initialized = true; } /* ============== SCR_DrawRam ============== */ void SCR_DrawRam (void) { if (!showram.value) return; if (!r_cache_thrash) return; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram); } /* ============== SCR_DrawTurtle ============== */ void SCR_DrawTurtle (void) { static int count; if (!showturtle.value) return; if (host_frametime < 0.1) { count = 0; return; } count++; if (count < 3) return; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle); } /* ============== SCR_DrawNet ============== */ void SCR_DrawNet (void) { if (realtime - cl.last_received_message < 0.3) return; if (cls.demoplayback) return; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net); } /* ============== DrawPause ============== */ void SCR_DrawPause (void) { qpic_t *pic; if (!showpause.value) // turn off for screenshots return; if (!cl.paused) return; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/pause.lmp"); Draw_Pic ( (320 - pic->width)/2, (240 - 48 - pic->height)/2, pic); } /* ============== SCR_DrawLoading ============== */ void SCR_DrawLoading (void) { qpic_t *pic; if (!scr_drawloading) return; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/loading.lmp"); Draw_Pic ( (320 - pic->width)/2, (240 - 48 - pic->height)/2, pic); } //============================================================================= /* ================== SCR_SetUpToDrawConsole ================== */ void SCR_SetUpToDrawConsole (void) { //johnfitz -- let's hack away the problem of slow console when host_timescale is <0 extern cvar_t host_timescale; float timescale; //johnfitz Con_CheckResize (); if (scr_drawloading) return; // never a console with loading plaque // decide on the height of the console con_forcedup = !cl.worldmodel || cls.signon != SIGNONS; if (con_forcedup) { scr_conlines = vid.height; // full screen scr_con_current = scr_conlines; } else if (key_dest == key_console) scr_conlines = vid.height/2; // half screen else scr_conlines = 0; // none visible timescale = (host_timescale.value > 0) ? host_timescale.value : 1; //johnfitz -- timescale if (scr_conlines < scr_con_current) { scr_con_current -= scr_conspeed.value*host_frametime/timescale; //johnfitz -- timescale if (scr_conlines > scr_con_current) scr_con_current = scr_conlines; } else if (scr_conlines > scr_con_current) { scr_con_current += scr_conspeed.value*host_frametime/timescale; //johnfitz -- timescale if (scr_conlines < scr_con_current) scr_con_current = scr_conlines; } if (clearconsole++ < vid.numpages) { Sbar_Changed (); } else if (clearnotify++ < vid.numpages) { } else con_notifylines = 0; } /* ================== SCR_DrawConsole ================== */ void SCR_DrawConsole (void) { if (scr_con_current) { scr_copyeverything = 1; Con_DrawConsole (scr_con_current, true); clearconsole = 0; } else { if (key_dest == key_game || key_dest == key_message) Con_DrawNotify (); // only draw notify in game } } /* ============================================================================== SCREEN SHOTS ============================================================================== */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; /* ================== SCR_ScreenShot_f ================== */ void SCR_ScreenShot_f (void) { byte *buffer; char pcxname[80]; char checkname[MAX_OSPATH]; int i, c, temp; // // find a file name to save it to // strcpy(pcxname,"quake00.tga"); for (i=0 ; i<=99 ; i++) { pcxname[5] = i/10 + '0'; pcxname[6] = i%10 + '0'; sprintf (checkname, "%s/%s", com_gamedir, pcxname); if (Sys_FileTime(checkname) == -1) break; // file doesn't exist } if (i==100) { Con_Printf ("SCR_ScreenShot_f: Couldn't create a PCX file\n"); return; } buffer = malloc(glwidth*glheight*3 + 18); memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type buffer[12] = glwidth&255; buffer[13] = glwidth>>8; buffer[14] = glheight&255; buffer[15] = glheight>>8; buffer[16] = 24; // pixel size glReadPixels (glx, gly, glwidth, glheight, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); // swap rgb to bgr c = 18+glwidth*glheight*3; for (i=18 ; i 0) { // left Draw_TileClear (0, 0, r_refdef.vrect.x, vid.height - sb_lines); // right Draw_TileClear (r_refdef.vrect.x + r_refdef.vrect.width, 0, vid.width - r_refdef.vrect.x + r_refdef.vrect.width, vid.height - sb_lines); } if (r_refdef.vrect.y > 0) { // top Draw_TileClear (r_refdef.vrect.x, 0, r_refdef.vrect.x + r_refdef.vrect.width, r_refdef.vrect.y); // bottom Draw_TileClear (r_refdef.vrect.x, r_refdef.vrect.y + r_refdef.vrect.height, r_refdef.vrect.width, vid.height - sb_lines - (r_refdef.vrect.height + r_refdef.vrect.y)); } } /* ================== SCR_UpdateScreen This is called every frame, and can also be called explicitly to flush text to the screen. WARNING: be very careful calling this from elsewhere, because the refresh needs almost the entire 256k of stack space! ================== */ void SCR_UpdateScreen (void) { vrect_t vrect; if (block_drawing) return; vid.numpages = (int)(2 + gl_triplebuffer.value); scr_copytop = 0; scr_copyeverything = 0; if (scr_disabled_for_loading) { if (realtime - scr_disabled_time > 60) { scr_disabled_for_loading = false; Con_Printf ("load failed.\n"); } else return; } if (!scr_initialized || !con_initialized) return; // not initialized yet GL_BeginRendering (&glx, &gly, &glwidth, &glheight); // // determine size of refresh window // if (oldfov != fov.value) { oldfov = fov.value; vid.recalc_refdef = true; } if (oldscreensize != viewsize.value) { oldscreensize = viewsize.value; vid.recalc_refdef = true; } if (oldsbaralpha != scr_sbaralpha.value) { oldsbaralpha = scr_sbaralpha.value; vid.recalc_refdef = true; } if (vid.recalc_refdef) SCR_CalcRefdef (); // // do 3D refresh drawing, and then update the screen // SCR_SetUpToDrawConsole (); V_RenderView (); GL_Set2D (); // // draw any areas not covered by the refresh // SCR_TileClear (); if (scr_drawdialog) { Sbar_Draw (); Draw_FadeScreen (); SCR_DrawNotifyString (); scr_copyeverything = true; } else if (scr_drawloading) { SCR_DrawLoading (); Sbar_Draw (); } else if (cl.intermission == 1 && key_dest == key_game) { Sbar_IntermissionOverlay (); } else if (cl.intermission == 2 && key_dest == key_game) { Sbar_FinaleOverlay (); SCR_CheckDrawCenterString (); } else { if (crosshair.value) Draw_Crosshair(); SCR_DrawRam (); SCR_DrawNet (); SCR_DrawTurtle (); SCR_DrawPause (); SCR_CheckDrawCenterString (); Sbar_Draw (); SCR_DrawConsole (); M_Draw (); } V_UpdatePalette (); GL_EndRendering (); } ================================================ FILE: source/gl_vidpsp2.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "quakedef.h" GLuint fb; int fb_tex = -1; float *gVertexBuffer; float *gColorBuffer; float *gTexCoordBuffer; float *gVertexBufferPtr; float *gColorBufferPtr; float *gTexCoordBufferPtr; extern uint8_t netcheck_dialog_running; extern cvar_t vid_vsync; extern bool benchmark; unsigned short d_8to16table[256]; unsigned d_8to24table[256]; unsigned char d_15to8table[65536]; CVAR (show_fps, 0, CVAR_ARCHIVE) CVAR (gl_fog, 0, CVAR_ARCHIVE) extern int isKeyboard; int num_shades=32; int d_con_indirect = 0; int svgalib_inited=0; int UseMouse = 1; int UseKeyboard = 1; CVAR (vid_mode, 5, CVAR_NONE) CVAR (vid_redrawfull, 0, CVAR_NONE) CVAR (vid_waitforrefresh, 1, CVAR_ARCHIVE) CVAR (gl_outline, 0, CVAR_ARCHIVE) CVAR (gl_mipmap, 1, CVAR_ARCHIVE) int gl_ssaa = 1; extern cvar_t scr_conscale; extern cvar_t scr_conwidth; signed char *framebuffer_ptr; int mouse_buttons; int mouse_buttonstate; int mouse_oldbuttonstate; float mouse_x, mouse_y; float old_mouse_x, old_mouse_y; int mx, my; int scr_width = 1280, scr_height = 720; /*-----------------------------------------------------------------------*/ int texture_mode = GL_LINEAR; int texture_extension_number = 1; float gldepthmin, gldepthmax; const char *gl_vendor; const char *gl_renderer; const char *gl_version; const char *gl_extensions; static float vid_gamma = 1.0; bool isPermedia = false; float sintablef[17] = { 0.000000f, 0.382683f, 0.707107f, 0.923879f, 1.000000f, 0.923879f, 0.707107f, 0.382683f, 0.000000f, -0.382683f,-0.707107f,-0.923879f, -1.000000f,-0.923879f,-0.707107f, -0.382683f, 0.000000f }; float costablef[17] = { 1.000000f, 0.923879f, 0.707107f, 0.382683f, 0.000000f,-0.382683f, -0.707107f,-0.923879f,-1.000000f, -0.923879f,-0.707107f,-0.382683f, 0.000000f, 0.382683f, 0.707107f, 0.923879f, 1.000000f }; /*-----------------------------------------------------------------------*/ void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height) { } void D_EndDirectRect (int x, int y, int width, int height) { } void VID_Shutdown(void) { } void VID_ShiftPalette(unsigned char *p) { // VID_SetPalette(p); } void VID_ChangeRes(float scale){ } void VID_SetPalette (unsigned char *palette) { byte *pal; unsigned r,g,b; unsigned v; int r1,g1,b1; int j,k,l,m; unsigned short i; unsigned *table; FILE *f; signed char s[255]; int dist, bestdist; static bool palflag = false; // // 8 8 8 encoding // pal = palette; table = d_8to24table; for (i=0 ; i<256 ; i++) { r = pal[0]; g = pal[1]; b = pal[2]; pal += 3; v = (255<<24) + (r<<0) + (g<<8) + (b<<16); *table++ = v; } d_8to24table[255] &= 0xffffff; // 255 is transparent // JACK: 3D distance calcs - k is last closest, l is the distance. for (i=0; i < (1<<15); i++) { /* Maps 000000000000000 000000000011111 = Red = 0x1F 000001111100000 = Blue = 0x03E0 111110000000000 = Grn = 0x7C00 */ r = ((i & 0x1F) << 3)+4; g = ((i & 0x03E0) >> 2)+4; b = ((i & 0x7C00) >> 7)+4; pal = (unsigned char *)d_8to24table; for (v=0,k=0,bestdist=10000*10000; v<256; v++,pal+=4) { r1 = (int)r - (int)pal[0]; g1 = (int)g - (int)pal[1]; b1 = (int)b - (int)pal[2]; dist = (r1*r1)+(g1*g1)+(b1*b1); if (dist < bestdist) { k=v; bestdist = dist; } } d_15to8table[i]=k; } } #define NUM_FRAG_SHADERS (9) #define NUM_VERT_SHADERS (4) #define MAX_INDICES (32768) uint16_t* indices; GLuint fs[9]; GLuint vs[4]; GLuint programs[9]; void* GL_LoadShader(const char* filename, GLuint idx, GLboolean fragment) { FILE* f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); long int size = ftell(f); fseek(f, 0, SEEK_SET); void* res = malloc(size); fread(res, 1, size, f); fclose(f); if (fragment) vglShaderGxpBinary(1, &fs[idx], res, size); else vglShaderGxpBinary(1, &vs[idx], res, size); free(res); } static int state_mask = 0; GLint monocolor; GLint modulcolor[2]; void GL_SetProgram() { switch (state_mask) { case 0x00: // Everything off case 0x04: // Modulate case 0x08: // Alpha Test case 0x0C: // Alpha Test + Modulate glUseProgram(programs[NO_COLOR]); break; case 0x01: // Texcoord case 0x03: // Texcoord + Color glUseProgram(programs[TEX2D_REPL]); break; case 0x02: // Color case 0x06: // Color + Modulate glUseProgram(programs[RGBA_COLOR]); break; case 0x05: // Modulate + Texcoord glUseProgram(programs[TEX2D_MODUL]); break; case 0x07: // Modulate + Texcoord + Color glUseProgram(programs[TEX2D_MODUL_CLR]); break; case 0x09: // Alpha Test + Texcoord case 0x0B: // Alpha Test + Color + Texcoord glUseProgram(programs[TEX2D_REPL_A]); break; case 0x0A: // Alpha Test + Color case 0x0E: // Alpha Test + Modulate + Color glUseProgram(programs[RGBA_CLR_A]); break; case 0x0D: // Alpha Test + Modulate + Texcoord glUseProgram(programs[TEX2D_MODUL_A]); break; case 0x0F: // Alpha Test + Modulate + Texcood + Color glUseProgram(programs[FULL_A]); break; default: break; } } void GL_EnableState(GLenum state) { switch (state) { case GL_TEXTURE_COORD_ARRAY: state_mask |= 0x01; break; case GL_COLOR_ARRAY: state_mask |= 0x02; break; case GL_MODULATE: state_mask |= 0x04; break; case GL_REPLACE: state_mask &= ~0x04; break; case GL_ALPHA_TEST: state_mask |= 0x08; break; } GL_SetProgram(); } void GL_DisableState(GLenum state) { switch (state) { case GL_TEXTURE_COORD_ARRAY: state_mask &= ~0x01; break; case GL_COLOR_ARRAY: state_mask &= ~0x02; break; case GL_ALPHA_TEST: state_mask &= ~0x08; break; default: break; } GL_SetProgram(); } static float cur_clr[4]; void GL_DrawPolygon(GLenum prim, int num) { if (num == 0) return; if (state_mask == 0x05) glUniform4fv(modulcolor[0], 1, cur_clr); else if (state_mask == 0x0D) glUniform4fv(modulcolor[1], 1, cur_clr); vglDrawObjects(prim, num, GL_TRUE); } void GL_Color(float r, float g, float b, float a) { cur_clr[0] = r; cur_clr[1] = g; cur_clr[2] = b; cur_clr[3] = a; } bool shaders_set = false; void GL_ResetShaders() { glFinish(); if (shaders_set) { for (int i = 0; i < NUM_FRAG_SHADERS; i++) { glDeleteProgram(programs[i]); } for (int i = 0; i < NUM_FRAG_SHADERS; i++) { glDeleteShader(fs[i]); } for (int i = 0; i < NUM_VERT_SHADERS; i++) { glDeleteShader(vs[i]); } } else shaders_set = true; // Loading shaders for (int i = 0; i < NUM_FRAG_SHADERS; i++) { fs[i] = glCreateShader(GL_FRAGMENT_SHADER); } for (int i = 0; i < NUM_VERT_SHADERS; i++) { vs[i] = glCreateShader(GL_VERTEX_SHADER); } if (gl_fog.value) { GL_LoadShader("app0:shaders/modulate_fog_f.gxp", MODULATE, GL_TRUE); GL_LoadShader("app0:shaders/modulate_rgba_fog_f.gxp", MODULATE_WITH_COLOR, GL_TRUE); GL_LoadShader("app0:shaders/replace_fog_f.gxp", REPLACE, GL_TRUE); GL_LoadShader("app0:shaders/modulate_alpha_fog_f.gxp", MODULATE_A, GL_TRUE); GL_LoadShader("app0:shaders/modulate_rgba_alpha_fog_f.gxp", MODULATE_COLOR_A, GL_TRUE); GL_LoadShader("app0:shaders/replace_alpha_fog_f.gxp", REPLACE_A, GL_TRUE); GL_LoadShader("app0:shaders/texture2d_fog_v.gxp", TEXTURE2D, GL_FALSE); GL_LoadShader("app0:shaders/texture2d_rgba_fog_v.gxp", TEXTURE2D_WITH_COLOR, GL_FALSE); } else { GL_LoadShader("app0:shaders/modulate_f.gxp", MODULATE, GL_TRUE); GL_LoadShader("app0:shaders/modulate_rgba_f.gxp", MODULATE_WITH_COLOR, GL_TRUE); GL_LoadShader("app0:shaders/replace_f.gxp", REPLACE, GL_TRUE); GL_LoadShader("app0:shaders/modulate_alpha_f.gxp", MODULATE_A, GL_TRUE); GL_LoadShader("app0:shaders/modulate_rgba_alpha_f.gxp", MODULATE_COLOR_A, GL_TRUE); GL_LoadShader("app0:shaders/replace_alpha_f.gxp", REPLACE_A, GL_TRUE); GL_LoadShader("app0:shaders/texture2d_v.gxp", TEXTURE2D, GL_FALSE); GL_LoadShader("app0:shaders/texture2d_rgba_v.gxp", TEXTURE2D_WITH_COLOR, GL_FALSE); } GL_LoadShader("app0:shaders/rgba_f.gxp", RGBA_COLOR, GL_TRUE); GL_LoadShader("app0:shaders/vertex_f.gxp", MONO_COLOR, GL_TRUE); GL_LoadShader("app0:shaders/rgba_alpha_f.gxp", RGBA_A, GL_TRUE); GL_LoadShader("app0:shaders/rgba_v.gxp", COLOR, GL_FALSE); GL_LoadShader("app0:shaders/vertex_v.gxp", VERTEX_ONLY, GL_FALSE); // Setting up programs for (int i = 0;i < NUM_FRAG_SHADERS; i++) { programs[i] = glCreateProgram(); switch (i) { case TEX2D_REPL: glAttachShader(programs[i], fs[REPLACE]); glAttachShader(programs[i], vs[TEXTURE2D]); vglBindAttribLocation(programs[i], 0, "position", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "texcoord", 2, GL_FLOAT); glLinkProgram(programs[i]); break; case TEX2D_MODUL: glAttachShader(programs[i], fs[MODULATE]); glAttachShader(programs[i], vs[TEXTURE2D]); vglBindAttribLocation(programs[i], 0, "position", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "texcoord", 2, GL_FLOAT); glLinkProgram(programs[i]); modulcolor[0] = glGetUniformLocation(programs[i], "vColor"); break; case TEX2D_MODUL_CLR: glAttachShader(programs[i], fs[MODULATE_WITH_COLOR]); glAttachShader(programs[i], vs[TEXTURE2D_WITH_COLOR]); vglBindAttribLocation(programs[i], 0, "position", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "texcoord", 2, GL_FLOAT); vglBindAttribLocation(programs[i], 2, "color", 4, GL_FLOAT); glLinkProgram(programs[i]); break; case RGBA_COLOR: glAttachShader(programs[i], fs[RGBA_COLOR]); glAttachShader(programs[i], vs[COLOR]); vglBindAttribLocation(programs[i], 0, "aPosition", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "aColor", 4, GL_FLOAT); glLinkProgram(programs[i]); break; case NO_COLOR: glAttachShader(programs[i], fs[MONO_COLOR]); glAttachShader(programs[i], vs[VERTEX_ONLY]); vglBindAttribLocation(programs[i], 0, "aPosition", 3, GL_FLOAT); glLinkProgram(programs[i]); monocolor = glGetUniformLocation(programs[i], "color"); break; case TEX2D_REPL_A: glAttachShader(programs[i], fs[REPLACE_A]); glAttachShader(programs[i], vs[TEXTURE2D]); vglBindAttribLocation(programs[i], 0, "position", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "texcoord", 2, GL_FLOAT); glLinkProgram(programs[i]); break; case TEX2D_MODUL_A: glAttachShader(programs[i], fs[MODULATE_A]); glAttachShader(programs[i], vs[TEXTURE2D]); vglBindAttribLocation(programs[i], 0, "position", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "texcoord", 2, GL_FLOAT); glLinkProgram(programs[i]); modulcolor[1] = glGetUniformLocation(programs[i], "vColor"); break; case FULL_A: glAttachShader(programs[i], fs[MODULATE_COLOR_A]); glAttachShader(programs[i], vs[TEXTURE2D_WITH_COLOR]); vglBindAttribLocation(programs[i], 0, "position", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "texcoord", 2, GL_FLOAT); vglBindAttribLocation(programs[i], 2, "color", 4, GL_FLOAT); glLinkProgram(programs[i]); break; case RGBA_CLR_A: glAttachShader(programs[i], fs[RGBA_A]); glAttachShader(programs[i], vs[COLOR]); vglBindAttribLocation(programs[i], 0, "aPosition", 3, GL_FLOAT); vglBindAttribLocation(programs[i], 1, "aColor", 4, GL_FLOAT); glLinkProgram(programs[i]); break; } GLint tex = glGetUniformLocation(programs[i], "tex"); if (tex != -1) glUniform1i(tex, 0); } } static void Callback_Fog_f(cvar_t *var) { if (gl_fog.value) Cvar_SetValue("r_fullbright", 1); else Cvar_SetValue("r_fullbright", 0); GL_ResetShaders(); } /* =============== GL_Init =============== */ void GL_Init (void) { gl_vendor = glGetString (GL_VENDOR); Con_Printf ("GL_VENDOR: %s\n", gl_vendor); gl_renderer = glGetString (GL_RENDERER); Con_Printf ("GL_RENDERER: %s\n", gl_renderer); gl_version = glGetString (GL_VERSION); Con_Printf ("GL_VERSION: %s\n", gl_version); gl_extensions = glGetString (GL_EXTENSIONS); Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions); glClearColor (1,0,0,0); glCullFace(GL_FRONT); Cvar_RegisterVariable (&gl_fog); Cvar_SetCallback(&gl_fog, &Callback_Fog_f); GL_ResetShaders(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GL_EnableState(GL_ALPHA_TEST); GL_EnableState(GL_TEXTURE_COORD_ARRAY); glPolygonOffset(1, 14); Cvar_RegisterVariable (&show_fps); // muff Cvar_RegisterVariable(&vid_vsync); indices = (uint16_t*)malloc(sizeof(uint16_t)*MAX_INDICES); for (int i = 0; i < MAX_INDICES; i++) { indices[i] = i; } gVertexBufferPtr = (float*)malloc(0x400000); gColorBufferPtr = (float*)malloc(0x200000); gTexCoordBufferPtr = (float*)malloc(0x200000); } /* ================= GL_BeginRendering ================= */ void GL_BeginRendering (int *x, int *y, int *width, int *height) { *x = *y = 0; *width = scr_width; *height = scr_height; if (gl_ssaa > 1) { if (fb_tex == -1) { void *buffer = malloc(vid.width * vid.height * 4); memset(buffer, 0xFF, vid.width * vid.height * 4); fb_tex = GL_LoadTexture32 ("***framebuffer***", scr_width, scr_height, buffer, false, false, false); glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, scr_width, scr_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); free(buffer); glBindTexture(GL_TEXTURE_2D, fb_tex); glEnable(GL_TEXTURE_2D); glGenFramebuffers(1, &fb); glBindFramebuffer(GL_FRAMEBUFFER, fb); glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fb_tex, 0); } else glBindFramebuffer(GL_FRAMEBUFFER, fb); } vglIndexPointerMapped(indices); gVertexBuffer = gVertexBufferPtr; gColorBuffer = gColorBufferPtr; gTexCoordBuffer = gTexCoordBufferPtr; } void GL_EndRendering (void) { if (benchmark) GL_DrawBenchmark (); else GL_DrawFPS (); vglSwapBuffers(isKeyboard || netcheck_dialog_running); GL_SetCanvas(CANVAS_DEFAULT); if (gl_ssaa > 1) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 960, 544, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, fb_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex3f(0, 0, 0); glTexCoord2i(1, 0); glVertex3f(960, 0, 0); glTexCoord2i(1, 1); glVertex3f(960, 544, 0); glTexCoord2i(0, 1); glVertex3f(0, 544, 0); glEnd(); } } static void Check_Gamma (unsigned char *pal) { float f, inf; unsigned char palette[768]; int i; if ((i = COM_CheckParm("-gamma")) == 0) { vid_gamma = 0.7; // default to 0.7 on non-3dfx hardware } else vid_gamma = atof(com_argv[i+1]); for (i=0 ; i<768 ; i++) { f = pow ( (pal[i]+1)/256.0 , vid_gamma ); inf = f*255 + 0.5; if (inf < 0) inf = 0; if (inf > 255) inf = 255; palette[i] = inf; } memcpy (pal, palette, sizeof(palette)); } void VID_Init(unsigned char *palette) { int i; int width = scr_width, height = scr_height; Cvar_RegisterVariable (&vid_mode); Cvar_RegisterVariable (&vid_redrawfull); Cvar_RegisterVariable (&vid_waitforrefresh); Cvar_RegisterVariable (&gl_outline); Cvar_RegisterVariable (&gl_mipmap); vid.maxwarpwidth = width; vid.maxwarpheight = height; vid.colormap = host_colormap; vid.fullbright = 0xFFFF; vid.aspect = (float) width / (float) height; vid.numpages = 2; vid.rowbytes = 2 * width; vid.width = width * gl_ssaa; vid.height = height * gl_ssaa; vid.conwidth = (scr_conwidth.value > 0) ? (int)scr_conwidth.value : (scr_conscale.value > 0) ? (int)(vid.width/scr_conscale.value) : vid.width; vid.conwidth = Q_CLAMP (320, vid.conwidth, vid.width); vid.conwidth &= 0xFFFFFFF8; vid.conheight = vid.conwidth * vid.height / vid.width; GL_Init(); Check_Gamma(palette); VID_SetPalette(palette); Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height); vid.recalc_refdef = 1; // force a surface cache flush } void Force_CenterView_f (void) { cl.viewangles[PITCH] = 0; } ================================================ FILE: source/gl_warp.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // gl_warp.c -- sky and water polygons #include "quakedef.h" extern model_t *loadmodel; extern cvar_t gl_compress; //int skytexturenum; int solidskytexture; int alphaskytexture; float speedscale; // for top sky and bottom sky msurface_t *warpface; extern cvar_t gl_subdivide_size; void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) { int i, j; float *v; mins[0] = mins[1] = mins[2] = 9999; maxs[0] = maxs[1] = maxs[2] = -9999; v = verts; for (i=0 ; i maxs[j]) maxs[j] = *v; } } void SubdividePolygon (int numverts, float *verts) { int i, j, k; vec3_t mins, maxs; float m; float *v; vec3_t front[64], back[64]; int f, b; float dist[64]; float frac; glpoly_t *poly; float s, t; if (numverts > 60) Sys_Error ("numverts = %i", numverts); BoundPoly (numverts, verts, mins, maxs); for (i=0 ; i<3 ; i++) { m = (mins[i] + maxs[i]) * 0.5; m = gl_subdivide_size.value * floor (m/gl_subdivide_size.value + 0.5); if (maxs[i] - m < 8) continue; if (m - mins[i] < 8) continue; // cut it v = verts + i; for (j=0 ; j= 0) { VectorCopy (v, front[f]); f++; } if (dist[j] <= 0) { VectorCopy (v, back[b]); b++; } if (dist[j] == 0 || dist[j+1] == 0) continue; if ( (dist[j] > 0) != (dist[j+1] > 0) ) { // clip point frac = dist[j] / (dist[j] - dist[j+1]); for (k=0 ; k<3 ; k++) front[f][k] = back[b][k] = v[k] + frac*(v[3+k] - v[k]); f++; b++; } } SubdividePolygon (f, front[0]); SubdividePolygon (b, back[0]); return; } poly = Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); poly->next = warpface->polys; warpface->polys = poly; poly->numverts = numverts; for (i=0 ; iverts[i]); s = DotProduct (verts, warpface->texinfo->vecs[0]); t = DotProduct (verts, warpface->texinfo->vecs[1]); poly->verts[i][3] = s; poly->verts[i][4] = t; } } /* ================ GL_SubdivideSurface Breaks a polygon up along axial 64 unit boundaries so that turbulent and sky warps can be done reasonably. ================ */ void GL_SubdivideSurface (msurface_t *fa) { vec3_t verts[64]; int numverts; int i; int lindex; float *vec; texture_t *t; warpface = fa; // // convert edges back to a normal polygon // numverts = 0; for (i=0 ; inumedges ; i++) { lindex = loadmodel->surfedges[fa->firstedge + i]; if (lindex > 0) vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; else vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; VectorCopy (vec, verts[numverts]); numverts++; } SubdividePolygon (numverts, verts[0]); } //========================================================= // speed up sin calculations - Ed float turbsin[] = { #include "gl_warp_sin.h" }; #define TURBSCALE (256.0 / (2 * M_PI)) /* ============= EmitWaterPolys Does a water warp on the pre-fragmented glpoly_t chain ============= */ void EmitWaterPolys (msurface_t *fa) { glpoly_t *p; float *v; int i; float s, t, os, ot; for (p=fa->polys ; p ; p=p->next) { float *pUV = gTexCoordBuffer; float *pPoint = gVertexBuffer; for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) { os = v[3]; ot = v[4]; s = os + turbsin[(int)((ot*0.125+realtime) * TURBSCALE) & 255]; s *= (0.015625f); t = ot + turbsin[(int)((os*0.125+realtime) * TURBSCALE) & 255]; t *= (0.015625f); *gTexCoordBuffer++ = s; *gTexCoordBuffer++ = t; memcpy(gVertexBuffer, &v[0], sizeof(vec3_t)); gVertexBuffer += 3; } vglVertexAttribPointerMapped(0, pPoint); vglVertexAttribPointerMapped(1, pUV); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); } } /* ============= EmitSkyPolys ============= */ void EmitSkyPolys (msurface_t *fa) { glpoly_t *p; float *v; int i; float s, t; vec3_t dir; float length; for (p=fa->polys ; p ; p=p->next) { float *pUV = gTexCoordBuffer; float *pPoint = gVertexBuffer; for (i=0,v=p->verts[0] ; inumverts ; i++, v+=VERTEXSIZE) { VectorSubtract (v, r_origin, dir); dir[2] *= 3; // flatten the sphere length = dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]; length = sqrt (length); length = 378/length; dir[0] *= length; dir[1] *= length; s = (speedscale + dir[0]) * (0.0078125f); t = (speedscale + dir[1]) * (0.0078125f); *gTexCoordBuffer++ = s; *gTexCoordBuffer++ = t; memcpy(gVertexBuffer, &v[0], sizeof(vec3_t)); gVertexBuffer += 3; } vglVertexAttribPointerMapped(0, pPoint); vglVertexAttribPointerMapped(1, pUV); GL_DrawPolygon(GL_TRIANGLE_FAN, p->numverts); } } /* =============== EmitBothSkyLayers Does a sky warp on the pre-fragmented glpoly_t chain This will be called for brushmodels, the world will have them chained together. =============== */ void EmitBothSkyLayers (msurface_t *fa) { int i; int lindex; float *vec; GL_Bind (solidskytexture); speedscale = realtime*8; speedscale -= (int)speedscale & ~127 ; EmitSkyPolys (fa); glEnable (GL_BLEND); GL_Bind (alphaskytexture); speedscale = realtime*16; speedscale -= (int)speedscale & ~127 ; EmitSkyPolys (fa); glDisable (GL_BLEND); } #ifndef QUAKE2 /* ================= R_DrawSkyChain ================= */ void R_DrawSkyChain (msurface_t *s) { msurface_t *fa; // used when gl_texsort is on GL_Bind(solidskytexture); speedscale = realtime*8; speedscale -= (int)speedscale & ~127 ; for (fa=s ; fa ; fa=fa->texturechain) EmitSkyPolys (fa); glEnable (GL_BLEND); GL_Bind (alphaskytexture); speedscale = realtime*16; speedscale -= (int)speedscale & ~127 ; for (fa=s ; fa ; fa=fa->texturechain) EmitSkyPolys (fa); glDisable (GL_BLEND); } #endif //=============================================================== /* ============= R_InitSky A sky texture is 256*128, with the right side being a masked overlay ============== */ void R_InitSky (miptex_t *mt) { int i, j, p; byte *src; unsigned trans[16384]; unsigned transpix; int r, g, b; unsigned *rgba; src = (byte *)mt + mt->offsets[0]; // make an average value for the back to avoid // a fringe on the top level r = g = b = 0; for (i=0 ; i<128 ; i++) for (j=0 ; j<128 ; j++) { p = src[i*256 + j + 128]; rgba = &d_8to24table[p]; trans[(i*128) + j] = *rgba; r += ((byte *)rgba)[0]; g += ((byte *)rgba)[1]; b += ((byte *)rgba)[2]; } ((byte *)&transpix)[0] = r/(16384); ((byte *)&transpix)[1] = g/(16384); ((byte *)&transpix)[2] = b/(16384); ((byte *)&transpix)[3] = 0; if (!solidskytexture) solidskytexture = texture_extension_number++; GL_Bind (solidskytexture ); glTexImage2D (GL_TEXTURE_2D, 0, gl_compress.value ? GL_COMPRESSED_RGBA_S3TC_DXT1_EXT : gl_solid_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); for (i=0 ; i<128 ; i++) for (j=0 ; j<128 ; j++) { p = src[i*256 + j]; if (p == 0) trans[(i*128) + j] = transpix; else trans[(i*128) + j] = d_8to24table[p]; } if (!alphaskytexture) alphaskytexture = texture_extension_number++; GL_Bind(alphaskytexture); glTexImage2D (GL_TEXTURE_2D, 0, gl_compress.value ? GL_COMPRESSED_RGBA_S3TC_DXT5_EXT : gl_alpha_format, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, trans); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } ================================================ FILE: source/gl_warp_sin.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, ================================================ FILE: source/glquake.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // disable data conversion warnings #pragma warning(disable : 4244) // MIPS #pragma warning(disable : 4136) // X86 #pragma warning(disable : 4051) // ALPHA #ifdef _WIN32 #include #endif #include void GL_BeginRendering (int *x, int *y, int *width, int *height); void GL_EndRendering (void); #ifdef _WIN32 // Function prototypes for the Texture Object Extension routines typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, const GLboolean *); typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, const GLclampf *); typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); extern BINDTEXFUNCPTR bindTexFunc; extern DELTEXFUNCPTR delTexFunc; extern TEXSUBIMAGEPTR TexSubImage2DFunc; #endif extern int texture_extension_number; extern int texture_mode; extern float gldepthmin, gldepthmax; void GL_Upload32 (unsigned *data, int width, int height, bool mipmap, bool alpha); void GL_Upload8 (byte *data, int width, int height, bool mipmap, bool alpha); int GL_LoadTexture (const char *identifier, int width, int height, byte *data, bool mipmap, bool alpha); int GL_LoadTexture32 (const char *identifier, int width, int height, byte *data, bool mipmap, bool alpha, bool fullbright); typedef struct { float x, y, z; float s, t; float r, g, b; } glvert_t; extern glvert_t glv; extern int glx, gly, glwidth, glheight; #ifdef _WIN32 extern PROC glArrayElementEXT; extern PROC glColorPointerEXT; extern PROC glTexturePointerEXT; extern PROC glVertexPointerEXT; #endif // r_local.h -- private refresh defs #define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) // normalizing factor so player model works out to about // 1 pixel per triangle #define MAX_LBM_HEIGHT 480 #define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf #define SKYSHIFT 7 #define SKYSIZE (1 << SKYSHIFT) #define SKYMASK (SKYSIZE - 1) #define BACKFACE_EPSILON 0.01 void R_TimeRefresh_f (void); void R_ReadPointFile_f (void); texture_t *R_TextureAnimation (texture_t *base); typedef struct surfcache_s { struct surfcache_s *next; struct surfcache_s **owner; // NULL is an empty chunk of memory int lightadj[MAXLIGHTMAPS]; // checked for strobe flush int dlight; int size; // including header unsigned width; unsigned height; // DEBUG only needed for debug float mipscale; struct texture_s *texture; // checked for animating textures byte data[4]; // width*height elements } surfcache_t; typedef struct { pixel_t *surfdat; // destination for generated surface int rowbytes; // destination logical width in bytes msurface_t *surf; // description for surface to generate fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting texture_t *texture; // corrected for animating textures int surfmip; // mipmapped ratio of surface texels / world pixels int surfwidth; // in mipmapped texels int surfheight; // in mipmapped texels } drawsurf_t; typedef enum { pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2 } ptype_t; // !!! if this is changed, it must be changed in d_ifacea.h too !!! typedef struct particle_s { // driver-usable fields vec3_t org; float color; // drivers never touch the following fields struct particle_s *next; vec3_t vel; float ramp; float die; ptype_t type; } particle_t; //==================================================== extern entity_t r_worldentity; extern bool r_cache_thrash; // compatability extern vec3_t modelorg, r_entorigin; extern entity_t *currententity; extern int r_visframecount; // ??? what difs? extern int r_framecount; extern mplane_t frustum[4]; extern int c_brush_polys, c_alias_polys; // // view origin // extern vec3_t vup; extern vec3_t vpn; extern vec3_t vright; extern vec3_t r_origin; // // screen size info // extern refdef_t r_refdef; extern mleaf_t *r_viewleaf, *r_oldviewleaf; extern texture_t *r_notexture_mip; extern int d_lightstylevalue[256]; // 8.8 fraction of base light value extern bool envmap; extern int currenttexture; extern int cnttextures[2]; extern int particletexture; extern int playertextures; extern int skytexturenum; // index in cl.loadmodel, not gl texture object extern cvar_t r_norefresh; extern cvar_t r_drawentities; extern cvar_t r_drawworld; extern cvar_t r_drawviewmodel; extern cvar_t r_speeds; extern cvar_t r_waterwarp; extern cvar_t r_fullbright; extern cvar_t r_lightmap; extern cvar_t r_shadows; extern cvar_t r_mirroralpha; extern cvar_t r_wateralpha; extern cvar_t r_dynamic; extern cvar_t r_novis; extern cvar_t gl_clear; extern cvar_t gl_cull; extern cvar_t gl_poly; extern cvar_t gl_texsort; extern cvar_t gl_smoothmodels; extern cvar_t gl_affinemodels; extern cvar_t gl_polyblend; extern cvar_t gl_keeptjunctions; extern cvar_t gl_reporttjunctions; extern cvar_t gl_flashblend; extern cvar_t gl_nocolors; extern cvar_t gl_doubleeyes; extern cvar_t gl_xflip; extern cvar_t gl_overbright; extern int gl_lightmap_format; extern int gl_solid_format; extern int gl_alpha_format; extern cvar_t gl_max_size; extern cvar_t gl_playermip; extern int mirrortexturenum; // quake texturenum, not gltexturenum extern bool mirror; extern mplane_t *mirror_plane; extern float r_world_matrix[16]; extern const char *gl_vendor; extern const char *gl_renderer; extern const char *gl_version; extern const char *gl_extensions; // Stereo extern cvar_t st_separation; extern cvar_t st_zeropdist; extern cvar_t st_fustbal; //end stereo void R_TranslatePlayerSkin (int playernum); void GL_Bind (int texnum); #ifndef _WIN32 #define APIENTRY /* */ #endif // fenix@io.com: model interpolation extern cvar_t r_interpolate_model_animation; extern cvar_t r_interpolate_model_transform; #define ISTRANSPARENT(ent) ((ent)->alpha > 0.0f && (ent)->alpha < 1.0f) ================================================ FILE: source/glquake2.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // disable data conversion warnings #pragma warning(disable : 4244) // MIPS #pragma warning(disable : 4136) // X86 #pragma warning(disable : 4051) // ALPHA #include #include void GL_BeginRendering (int *x, int *y, int *width, int *height); void GL_EndRendering (void); // Function prototypes for the Texture Object Extension routines typedef GLboolean (APIENTRY *ARETEXRESFUNCPTR)(GLsizei, const GLuint *, const GLboolean *); typedef void (APIENTRY *BINDTEXFUNCPTR)(GLenum, GLuint); typedef void (APIENTRY *DELTEXFUNCPTR)(GLsizei, const GLuint *); typedef void (APIENTRY *GENTEXFUNCPTR)(GLsizei, GLuint *); typedef GLboolean (APIENTRY *ISTEXFUNCPTR)(GLuint); typedef void (APIENTRY *PRIORTEXFUNCPTR)(GLsizei, const GLuint *, const GLclampf *); typedef void (APIENTRY *TEXSUBIMAGEPTR)(int, int, int, int, int, int, int, int, void *); extern BINDTEXFUNCPTR bindTexFunc; extern DELTEXFUNCPTR delTexFunc; extern TEXSUBIMAGEPTR TexSubImage2DFunc; extern int texture_extension_number; extern int texture_mode; extern float gldepthmin, gldepthmax; void GL_Upload32 (unsigned *data, int width, int height, bool mipmap, bool alpha, bool modulate); void GL_Upload8 (byte *data, int width, int height, bool mipmap, bool alpha, bool modulate); int GL_LoadTexture (const char *identifier, int width, int height, byte *data, int mipmap, int alpha, int modulate); typedef struct { float x, y, z; float s, t; float r, g, b; } glvert_t; extern glvert_t glv; extern int glx, gly, glwidth, glheight; extern PROC glArrayElementEXT; extern PROC glColorPointerEXT; extern PROC glTexturePointerEXT; extern PROC glVertexPointerEXT; // r_local.h -- private refresh defs #define MAXALIASVERTS 2000 // TODO: tune this #define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0) // normalizing factor so player model works out to about // 1 pixel per triangle #define MAX_LBM_HEIGHT 480 #define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf #define SKYSHIFT 7 #define SKYSIZE (1 << SKYSHIFT) #define SKYMASK (SKYSIZE - 1) #define BACKFACE_EPSILON 0.01 void R_TimeRefresh_f (void); void R_ReadPointFile_f (void); texture_t *R_TextureAnimation (texture_t *base); typedef struct surfcache_s { struct surfcache_s *next; struct surfcache_s **owner; // NULL is an empty chunk of memory int lightadj[MAXLIGHTMAPS]; // checked for strobe flush int dlight; int size; // including header unsigned width; unsigned height; // DEBUG only needed for debug float mipscale; struct texture_s *texture; // checked for animating textures byte data[4]; // width*height elements } surfcache_t; typedef struct { pixel_t *surfdat; // destination for generated surface int rowbytes; // destination logical width in bytes msurface_t *surf; // description for surface to generate fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting texture_t *texture; // corrected for animating textures int surfmip; // mipmapped ratio of surface texels / world pixels int surfwidth; // in mipmapped texels int surfheight; // in mipmapped texels } drawsurf_t; typedef enum { pt_static, pt_grav, pt_slowgrav, pt_fire, pt_explode, pt_explode2, pt_blob, pt_blob2 } ptype_t; // !!! if this is changed, it must be changed in d_ifacea.h too !!! typedef struct particle_s { // driver-usable fields vec3_t org; float color; // drivers never touch the following fields struct particle_s *next; vec3_t vel; float ramp; float die; ptype_t type; } particle_t; //==================================================== extern entity_t r_worldentity; extern bool r_cache_thrash; // compatability extern vec3_t modelorg, r_entorigin; extern entity_t *currententity; extern int r_visframecount; // ??? what difs? extern int r_framecount; extern mplane_t frustum[4]; extern int c_brush_polys, c_alias_polys; // // view origin // extern vec3_t vup; extern vec3_t vpn; extern vec3_t vright; extern vec3_t r_origin; // // screen size info // extern refdef_t r_refdef; extern mleaf_t *r_viewleaf, *r_oldviewleaf; extern texture_t *r_notexture_mip; extern int d_lightstylevalue[256]; // 8.8 fraction of base light value extern bool envmap; extern int currenttexture; extern int particletexture; extern int playertextures; extern int skytexturenum; // index in cl.loadmodel, not gl texture object extern cvar_t r_drawentities; extern cvar_t r_drawworld; extern cvar_t r_drawviewmodel; extern cvar_t r_speeds; extern cvar_t r_waterwarp; extern cvar_t r_fullbright; extern cvar_t r_lightmap; extern cvar_t r_shadows; extern cvar_t r_dynamic; extern cvar_t gl_clear; extern cvar_t gl_cull; extern cvar_t gl_poly; extern cvar_t gl_texsort; extern cvar_t gl_smoothmodels; extern cvar_t gl_affinemodels; extern cvar_t gl_fogblend; extern cvar_t gl_polyblend; extern cvar_t gl_keeptjunctions; extern cvar_t gl_reporttjunctions; extern int gl_lightmap_format; extern int gl_solid_format; extern int gl_alpha_format; void R_TranslatePlayerSkin (int playernum); void GL_Bind (int texnum); ================================================ FILE: source/host.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // host.c -- coordinates spawning and killing of local servers #include "quakedef.h" #include "r_local.h" /* A server can always be started, even if the system started out as a client to a remote system. A client can NOT be started if the system started as a dedicated server. Memory is cleared / released when a server or client begins, not when they end. */ CVAR(developer, 1, CVAR_DEBUG) CVAR(temp1, 0, CVAR_NONE) // Ch0wW: Honestly never saw that CVAR being used, maybe for MOD support? CVAR(host_framerate, 0, CVAR_NONE) CVAR(host_speeds, 0, CVAR_NONE) CVAR(host_timescale, 0, CVAR_NONE) CVAR(sys_ticrate, 0.05, CVAR_NONE) CVAR(serverprofile, 0, CVAR_NONE) CVAR(skill, 1, CVAR_NONE) CVAR(deathmatch, 0, CVAR_SERVERINFO) CVAR(coop, 0, CVAR_SERVERINFO) CVAR(fraglimit, 0, CVAR_SERVERINFO) CVAR(timelimit, 0, CVAR_SERVERINFO) CVAR(teamplay, 0, CVAR_SERVERINFO) CVAR(samelevel, 0, CVAR_SERVERINFO) CVAR(noexit, 0, CVAR_SERVERINFO) CVAR(pausable, 1, CVAR_SERVERINFO) //---------------------------------------------- quakeparms_t host_parms; bool host_initialized; // true if into command execution double host_frametime; double host_time; double realtime; // without any filtering or bounding double oldrealtime; // last frame run int host_framecount; int host_hunklevel; int minimum_memory; int fps_count; client_t *host_client; // current client jmp_buf host_abortserver; byte *host_basepal; byte *host_colormap; extern void CL_RunParticles (void); /* ================ Host_EndGame ================ */ void Host_EndGame (char *message, ...) { va_list argptr; char* string = Sys_BigStackAlloc(1024, "Host_EndGame"); va_start (argptr,message); vsprintf (string,message,argptr); va_end (argptr); Con_DPrintf ("Host_EndGame: %s\n",string); if (sv.active) Host_ShutdownServer (false); if (cls.state == ca_dedicated) Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit Sys_BigStackFree(1024, "Host_EndGame"); if (cls.demonum != -1) { CL_StopPlayback(); CL_NextDemo(); } else CL_Disconnect (); longjmp (host_abortserver, 1); } /* ================ Host_Error This shuts down both the client and server ================ */ void Host_Error (char *error, ...) { va_list argptr; char* string; static bool inerror = false; if (inerror) Sys_Error ("Host_Error: recursively entered"); inerror = true; SCR_EndLoadingPlaque (); // reenable screen updates string = Sys_BigStackAlloc(1024, "Host_Error"); va_start (argptr,error); vsprintf (string,error,argptr); va_end (argptr); Con_Printf ("Host_Error: %s\n",string); if (sv.active) Host_ShutdownServer (false); if (cls.state == ca_dedicated) Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit Sys_BigStackFree(1024, "Host_Error"); CL_Disconnect (); cls.demonum = -1; inerror = false; longjmp (host_abortserver, 1); } /* ================ Host_FindMaxClients -- ToDo: Clean this up ================ */ void Host_FindMaxClients (void) { int i; svs.maxclients = 1; i = COM_CheckParm ("-dedicated"); if (i) { cls.state = ca_dedicated; if (i != (com_argc - 1)) { svs.maxclients = atoi (com_argv[i+1]); } else svs.maxclients = 8; } else cls.state = ca_disconnected; i = COM_CheckParm ("-listen"); if (i) { if (cls.state == ca_dedicated) Sys_Error ("Only one of -dedicated or -listen can be specified"); if (i != (com_argc - 1)) svs.maxclients = atoi (com_argv[i+1]); else svs.maxclients = 8; } if (svs.maxclients < 1) svs.maxclients = 8; else if (svs.maxclients > MAX_SCOREBOARD) svs.maxclients = MAX_SCOREBOARD; svs.maxclientslimit = svs.maxclients; if (svs.maxclientslimit < 4) svs.maxclientslimit = 4; svs.clients = (client_t*)Hunk_AllocName(svs.maxclientslimit * sizeof(client_t), "clients"); Cvar_SetValue ("deathmatch", svs.maxclients > 1 ? 1.0 : 0.0); } /* ======================= Host_InitLocal ====================== */ void Host_InitLocal (void) { Host_InitCommands (); Cvar_RegisterVariable (&host_framerate); Cvar_RegisterVariable (&host_speeds); Cvar_RegisterVariable (&host_timescale); Cvar_RegisterVariable (&sys_ticrate); Cvar_RegisterVariable (&serverprofile); Cvar_RegisterVariable (&fraglimit); Cvar_RegisterVariable (&timelimit); Cvar_RegisterVariable (&teamplay); Cvar_RegisterVariable (&samelevel); Cvar_RegisterVariable (&noexit); Cvar_RegisterVariable (&skill); Cvar_RegisterVariable (&developer); Cvar_RegisterVariable (&deathmatch); Cvar_RegisterVariable (&coop); Cvar_RegisterVariable (&pausable); Cvar_RegisterVariable (&temp1); Host_FindMaxClients (); host_time = 1.0; // so a think at time 0 won't get called } /* =============== Host_WriteConfiguration Writes key bindings and archived cvars to config.cfg =============== */ void Host_WriteConfiguration (char *name) { FILE *f; // dedicated servers initialize the host but don't parse and set the // config.cfg cvars if (host_initialized) { f = fopen (va("%s/%s",com_gamedir, name), "w"); if (!f) { Sys_Error ("Couldn't write config.cfg.\n"); return; } fprintf(f, "//=====================================\n"); fprintf(f, "//Generated by %s, do not modify!\n", ENGINE_NAME); fprintf(f, "//=====================================\n\n"); Key_WriteBindings (f); Cvar_WriteVariables (f); fclose (f); } } /* ================= SV_ClientPrintf Sends text across to be displayed FIXME: make this just a stuffed echo? ================= */ void SV_ClientPrintf (const char *fmt, ...) { va_list argptr; char* string = Sys_BigStackAlloc(1024, "SV_ClientPrintf"); va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_print); MSG_WriteString (&host_client->message, string); Sys_BigStackFree(1024, "SV_ClientPrintf"); } /* ================= SV_BroadcastPrintf Sends text to all active clients ================= */ void SV_BroadcastPrintf (char *fmt, ...) { va_list argptr; char* string = Sys_BigStackAlloc(1024, "SV_BroadcastPrintf"); int i; va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); for (i = 0; i < svs.maxclients; i++) { if (svs.clients[i].active && svs.clients[i].spawned) { MSG_WriteByte(&svs.clients[i].message, svc_print); MSG_WriteString(&svs.clients[i].message, string); } } Sys_BigStackFree(1024, "SV_BroadcastPrintf"); } /* ================= Host_ClientCommands Send text over to the client to be executed ================= */ void Host_ClientCommands (char *fmt, ...) { va_list argptr; char* string = Sys_BigStackAlloc(1024, "Host_ClientCommands"); va_start (argptr,fmt); vsprintf (string, fmt,argptr); va_end (argptr); MSG_WriteByte (&host_client->message, svc_stufftext); MSG_WriteString (&host_client->message, string); Sys_BigStackFree(1024, "Host_ClientCommands"); } /* ===================== SV_DropClient Called when the player is getting totally kicked off the host if (crash = true), don't bother sending signofs ===================== */ void SV_DropClient (bool crash) { int saveSelf; int i; client_t *client; if (!host_client->active) return; if (!crash) { // send any final messages (don't check for errors) if (NET_CanSendMessage (host_client->netconnection)) { MSG_WriteByte (&host_client->message, svc_disconnect); NET_SendMessage (host_client->netconnection, &host_client->message); } if (host_client->edict && host_client->spawned) { // call the prog function for removing a client // this will set the body to a dead frame, among other things saveSelf = pr_global_struct->self; pr_global_struct->self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (pr_global_struct->ClientDisconnect); pr_global_struct->self = saveSelf; } Sys_Printf ("Client %s removed\n",host_client->name); } /*if (host_client->netconnection->proquake_connection == MOD_QSMACK) qsmackActive = false;*/ // break the net connection NET_Close (host_client->netconnection); host_client->netconnection = NULL; // free the client (the body stays around) host_client->active = false; host_client->name[0] = 0; host_client->old_frags = -999999; net_activeconnections--; // send notification to all clients for (i=0, client = svs.clients ; iactive) continue; MSG_WriteByte (&client->message, svc_updatename); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteString (&client->message, ""); MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteShort (&client->message, 0); MSG_WriteByte (&client->message, svc_updatecolors); MSG_WriteByte (&client->message, host_client - svs.clients); MSG_WriteByte (&client->message, 0); } } /* ================== Host_ShutdownServer This only happens at the end of a game, not between levels ================== */ void Host_ShutdownServer(bool crash) { int i; int count; sizebuf_t buf; char message[4]; double start; if (!sv.active) return; sv.active = false; // stop all client sounds immediately if (cls.state == ca_connected) CL_Disconnect (); // flush any pending messages - like the score!!! start = Sys_FloatTime(); do { count = 0; for (i=0, host_client = svs.clients ; iactive && host_client->message.cursize) { if (NET_CanSendMessage (host_client->netconnection)) { NET_SendMessage(host_client->netconnection, &host_client->message); SZ_Clear (&host_client->message); } else { NET_GetMessage(host_client->netconnection); count++; } } } if ((Sys_FloatTime() - start) > 3.0) break; } while (count); // make sure all the clients know we're disconnecting buf.data = message; buf.maxsize = 4; buf.cursize = 0; MSG_WriteByte(&buf, svc_disconnect); count = NET_SendToAll(&buf, 5); if (count) Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count); for (i=0, host_client = svs.clients ; iactive) SV_DropClient(crash); // // clear structures // memset (&sv, 0, sizeof(sv)); memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t)); } /* ================ Host_ClearMemory This clears all the memory used by both the client and server, but does not reinitialize anything. ================ */ void Host_ClearMemory (void) { Con_DPrintf ("Clearing memory\n"); D_FlushCaches (); Mod_ClearAll (); if (host_hunklevel) Hunk_FreeToLowMark (host_hunklevel); cls.signon = 0; memset (&sv, 0, sizeof(sv)); memset (&cl, 0, sizeof(cl)); } //============================================================================ /* =================== Host_FilterTime Returns false if the time is too short to run a frame =================== */ extern bool benchmark; bool Host_FilterTime (float time) { realtime += time; if ( ( (!cls.timedemo || !benchmark) || (cls.timedemo && benchmark) ) && realtime - oldrealtime < 1.0/72.0) return false; // framerate is too high host_frametime = realtime - oldrealtime; oldrealtime = realtime; //johnfitz -- host_timescale is more intuitive than host_framerate if (host_timescale.value > 0) host_frametime *= host_timescale.value; //johnfitz else if (host_framerate.value > 0) host_frametime = host_framerate.value; else { // don't allow really long or short frames if (host_frametime > 0.1) host_frametime = 0.1; if (host_frametime < 0.001) host_frametime = 0.001; } return true; } /* =================== Host_GetConsoleCommands Add them exactly as if they had been typed at the console =================== */ void Host_GetConsoleCommands (void) { char *cmd; while (1) { cmd = Sys_ConsoleInput (); if (!cmd) break; Cbuf_AddText (cmd); } } /* ================== Host_ServerFrame ================== */ #ifdef FPS_20 void _Host_ServerFrame (void) { // run the world state pr_global_struct->frametime = host_frametime; // read client messages SV_RunClients (); // move things around and think // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) SV_Physics (); } void Host_ServerFrame (void) { float save_host_frametime; float temp_host_frametime; // run the world state pr_global_struct->frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram (); // check for new clients SV_CheckForNewClients (); temp_host_frametime = save_host_frametime = host_frametime; while(temp_host_frametime > (1.0/72.0)) { if (temp_host_frametime > 0.05) host_frametime = 0.05; else host_frametime = temp_host_frametime; temp_host_frametime -= host_frametime; _Host_ServerFrame (); } host_frametime = save_host_frametime; // send all messages to the clients SV_SendClientMessages (); } #else void Host_ServerFrame (void) { static double port_time = 0; if (port_time > sv.time + 1 || port_time < sv.time - 60) { port_time = sv.time; Cmd_ExecuteString(va("port %d\n", net_hostport), src_command); } // run the world state pr_global_struct->frametime = host_frametime; // set the time and clear the general datagram SV_ClearDatagram (); // check for new clients SV_CheckForNewClients (); // read client messages SV_RunClients (); // move things around and think // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) SV_Physics (); // send all messages to the clients SV_SendClientMessages (); } #endif /* ================== Host_Frame Runs all active servers ================== */ void _Host_Frame (float time) { static double time1 = 0; static double time2 = 0; static double time3 = 0; int pass1, pass2, pass3; if (setjmp (host_abortserver) ) return; // something bad happened, or the server disconnected // keep the random time dependent rand (); // decide the simulation time if (!Host_FilterTime (time)) return; // don't run too fast, or packets will flood out // get new key events Sys_SendKeyEvents (); // allow mice or other external controllers to add commands IN_Commands (); // process console commands Cbuf_Execute (); NET_Poll(); // if running the server locally, make intentions now if (sv.active){ CL_SendCmd (); } //------------------- // // server operations // //------------------- // check for commands typed to the host Host_GetConsoleCommands (); if (sv.active){ Host_ServerFrame (); } //------------------- // // client operations // //------------------- // if running the server remotely, send intentions now after // the incoming messages have been read if (!sv.active){ CL_SendCmd (); } host_time += host_frametime; // fetch results from server if (cls.state == ca_connected) { CL_ReadFromServer (); } // update video if (host_speeds.value) time1 = Sys_FloatTime (); SCR_UpdateScreen (); CL_RunParticles (); //johnfitz -- seperated from rendering if (host_speeds.value) time2 = Sys_FloatTime (); // update audio if (cls.signon == SIGNONS) { S_Update (r_origin, vpn, vright, vup); CL_DecayLights (); } else S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); CDAudio_Update(); if (host_speeds.value) { pass1 = (time1 - time3)*1000; time3 = Sys_FloatTime (); pass2 = (time2 - time1)*1000; pass3 = (time3 - time2)*1000; Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n", pass1+pass2+pass3, pass1, pass2, pass3); } host_framecount++; fps_count++; } void Host_Frame (float time) { double time1, time2; static double timetotal; static int timecount; int i, c, m; if (!serverprofile.value) { _Host_Frame (time); return; } time1 = Sys_FloatTime (); _Host_Frame (time); time2 = Sys_FloatTime (); timetotal += time2 - time1; timecount++; if (timecount < 1000) return; m = timetotal*1000/timecount; timecount = 0; timetotal = 0; c = 0; for (i=0 ; iargv[0]; for (i = 0; i < com_argc; i++) { Sys_FileRead (vcrFile, &len, sizeof(int)); p = malloc(len); Sys_FileRead (vcrFile, p, len); com_argv[i+1] = p; } com_argc++; /* add one for arg[0] */ parms->argc = com_argc; parms->argv = com_argv; } if ( (n = COM_CheckParm("-record")) != 0) { vcrFile = Sys_FileOpenWrite("quake.vcr"); i = VCR_SIGNATURE; Sys_FileWrite(vcrFile, &i, sizeof(int)); i = com_argc - 1; Sys_FileWrite(vcrFile, &i, sizeof(int)); for (i = 1; i < com_argc; i++) { if (i == n) { len = 10; Sys_FileWrite(vcrFile, &len, sizeof(int)); Sys_FileWrite(vcrFile, "-playback", len); continue; } len = strlen(com_argv[i]) + 1; Sys_FileWrite(vcrFile, &len, sizeof(int)); Sys_FileWrite(vcrFile, com_argv[i], len); } } } /* ==================== Host_Init ==================== */ void Host_Init (quakeparms_t *parms) { if (standard_quake) minimum_memory = MINIMUM_MEMORY; else minimum_memory = MINIMUM_MEMORY_LEVELPAK; if (COM_CheckParm ("-minmemory")) parms->memsize = minimum_memory; host_parms = *parms; if (parms->memsize < minimum_memory) Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000); com_argc = parms->argc; com_argv = parms->argv; Memory_Init (parms->membase, parms->memsize); Cbuf_Init (); Cmd_Init (); V_Init (); Chase_Init (); Host_InitVCR (parms); COM_Init (parms->basedir); Host_InitLocal (); W_LoadWadFile ("gfx.wad"); Key_Init (); Con_Init (); M_Init (); PR_Init (); Mod_Init (); NET_Init (); SV_Init (); Host_Version_f(); Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0)); R_InitTextures (); // needed even for dedicated servers if (cls.state != ca_dedicated) { host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp", NULL); if (!host_basepal) Sys_Error ("Couldn't load gfx/palette.lmp"); host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp", NULL); if (!host_colormap) Sys_Error ("Couldn't load gfx/colormap.lmp"); #ifndef _WIN32 // on non win32, mouse comes before video for security reasons IN_Init (); #endif VID_Init (host_basepal); Draw_Init (); SCR_Init (); R_Init (); #ifndef _WIN32 // on Win32, sound initialization has to come before video initialization, so we // can put up a popup if the sound hardware is in use S_Init (); #else #ifdef GLQUAKE // FIXME: doesn't use the new one-window approach yet S_Init (); #endif #endif // _WIN32 CDAudio_Init (); Sbar_Init (); CL_Init (); #ifdef _WIN32 // on non win32, mouse comes before video for security reasons IN_Init (); #endif } Cbuf_InsertText ("exec quake.rc\n"); Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark (); host_initialized = true; Sys_Printf ("======== %s Initialized=========\n", ENGINE_NAME); } /* =============== Host_Shutdown FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better to run quit through here before the final handoff to the sys code. =============== */ void Host_Shutdown(void) { static bool isdown = false; if (isdown) { printf ("recursive shutdown\n"); return; } isdown = true; // keep Con_Printf from trying to update the screen scr_disabled_for_loading = true; Host_WriteConfiguration ("config.cfg"); CDAudio_Shutdown (); NET_Shutdown (); S_Shutdown(); IN_Shutdown (); if (cls.state != ca_dedicated) { VID_Shutdown(); } } ================================================ FILE: source/host_cmd.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" extern cvar_t pausable; extern int com_nummissionpacks; //johnfitz extern void Host_WriteConfiguration (char *name); extern void COM_ForceExtension(char *path, char *extension); int current_skill; void Mod_Print (void); /* ================== Host_Quit_f ================== */ extern void M_Menu_Quit_f (void); void Host_Quit_f (void) { if (key_dest != key_console && cls.state != ca_dedicated) { M_Menu_Quit_f (); return; } CL_Disconnect (); Host_ShutdownServer(false); Sys_Quit (); } //============================================================================== //johnfitz -- dynamic gamedir stuff //============================================================================== extern bool com_modified; extern searchpath_t *com_searchpaths; pack_t *COM_LoadPackFile (char *packfile); // Kill all the search packs until the game path is found. Kill it, then return // the next path to it. void KillGameDir(searchpath_t *search) { searchpath_t *search_killer; while (search) { if (*search->filename) { com_searchpaths = search->next; Z_Free(search); return; //once you hit the dir, youve already freed the paks } Sys_FileClose (search->pack->handle); //johnfitz search_killer = search->next; Z_Free(search->pack->files); Z_Free(search->pack); Z_Free(search); search = search_killer; } } // Return the number of games in memory int NumGames(searchpath_t *search) { int found = 0; while (search) { if (*search->filename) found++; search = search->next; } return found; } void ExtraMaps_NewGame (void); /* =============== Host_WriteConfig_f Writes key bindings and ONLY archived cvars to a custom config file =============== */ void Host_WriteConfig_f (void) { char name[MAX_OSPATH]; if (Cmd_Argc() != 2) { Con_Printf ("Usage: writeconfig \n"); return; } strncpyz (name, Cmd_Argv(1), sizeof(name)); COM_ForceExtension (name, ".cfg"); Con_Printf ("Writing %s\n", name); Host_WriteConfiguration (name); } /* ================== Host_Game_f ================== */ extern char mp_path[32]; extern char* mod_path; void Host_Game_f (void) { int i; searchpath_t *search = com_searchpaths; pack_t *pak; char pakfile[MAX_OSPATH]; //FIXME: it's confusing to use this string for two different things if (Cmd_Argc() > 1) { if (!registered.value) //disable command for shareware quake Con_Printf("Shareware version detected, issues may be encountered\n"); if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } strcpy (pakfile, va("%s/%s", host_parms.basedir, Cmd_Argv(1))); if (!strcasecmp(pakfile, com_gamedir)) //no change { Con_Printf("\"game\" is already \"%s\"\n", COM_SkipPath(com_gamedir)); return; } sprintf(mp_path, Cmd_Argv(1)); mod_path = mp_path; com_modified = true; //Kill the server CL_Disconnect (); Host_ShutdownServer(true); //Write config file Host_WriteConfiguration ("config.cfg"); //Kill the extra game if it is loaded if (NumGames(com_searchpaths) > 1 + com_nummissionpacks) KillGameDir(com_searchpaths); strcpy (com_gamedir, pakfile); if (strcasecmp(Cmd_Argv(1), GAMENAME_DIR)) //game is not id1 { search = Z_Malloc(sizeof(searchpath_t)); strcpy (search->filename, pakfile); search->next = com_searchpaths; com_searchpaths = search; //Load the paks if any are found: for (i = 0; ; i++) { sprintf (pakfile, "%s/pak%i.pak", com_gamedir, i); pak = COM_LoadPackFile (pakfile); if (!pak) break; search = Z_Malloc(sizeof(searchpath_t)); search->pack = pak; search->next = com_searchpaths; com_searchpaths = search; } } //clear out and reload appropriate data Cache_Flush (); Mod_ResetAll(); ExtraMaps_NewGame (); //Cbuf_InsertText ("exec quake.rc\n"); Con_Printf("\"game\" changed to \"%s\"\n", COM_SkipPath(com_gamedir)); } else //Diplay the current gamedir Con_Printf("\"game\" is \"%s\"\n", COM_SkipPath(com_gamedir)); } //============================================================================== //johnfitz -- extramaps management //============================================================================== typedef struct extralevel_s { char name[32]; struct extralevel_s *next; } extralevel_t; extralevel_t *extralevels; void ExtraMaps_Add (char *name) { extralevel_t *level,*cursor,*prev; //ingore duplicate for (level = extralevels; level; level = level->next) if (!strcmp (name, level->name)) return; level = Z_Malloc(sizeof(extralevel_t)); strcpy (level->name, name); //insert each entry in alphabetical order if (extralevels == NULL || strcasecmp(level->name, extralevels->name) < 0) //insert at front { level->next = extralevels; extralevels = level; } else //insert later { prev = extralevels; cursor = extralevels->next; while (cursor && (strcasecmp(level->name, cursor->name) > 0)) { prev = cursor; cursor = cursor->next; } level->next = prev->next; prev->next = level; } } void ExtraMaps_Init (void) //TODO: move win32 specific stuff to sys_win.c { } void ExtraMaps_Clear (void) { extralevel_t *blah; while (extralevels) { blah = extralevels->next; Z_Free(extralevels); extralevels = blah; } } void ExtraMaps_NewGame (void) { ExtraMaps_Clear (); ExtraMaps_Init (); } /* ================== Host_Maps_f ================== */ void Host_Maps_f (void) { int i; extralevel_t *level; for (level=extralevels, i=0; level; level=level->next, i++) Con_SafePrintf (" %s\n", level->name); if (i) Con_SafePrintf ("%i map(s)\n", i); else Con_SafePrintf ("no maps found\n"); } //============================================================================== //johnfitz -- modlist management //============================================================================== typedef struct mod_s { char name[MAX_OSPATH]; struct mod_s *next; } mod_t; mod_t *modlist; void Modlist_Add (char *name) { mod_t *mod,*cursor,*prev; //ingore duplicate for (mod = modlist; mod; mod = mod->next) if (!strcmp (name, mod->name)) return; mod = Z_Malloc(sizeof(mod_t)); strcpy (mod->name, name); //insert each entry in alphabetical order if (modlist == NULL || strcasecmp(mod->name, modlist->name) < 0) //insert at front { mod->next = modlist; modlist = mod; } else //insert later { prev = modlist; cursor = modlist->next; while (cursor && (strcasecmp(mod->name, cursor->name) > 0)) { prev = cursor; cursor = cursor->next; } mod->next = prev->next; prev->next = mod; } } void Modlist_Init (void) //TODO: move win32 specific stuff to sys_win.c { #ifdef _WIN32 WIN32_FIND_DATA FindFileData, FindChildData; HANDLE Find, FindProgs, FindPak; char filestring[MAX_OSPATH], childstring[MAX_OSPATH]; int temp; sprintf (filestring,"%s/*", host_parms.basedir); Find = FindFirstFile(filestring, &FindFileData); if (Find == INVALID_HANDLE_VALUE) { FindClose (Find); return; } do { sprintf (childstring,"%s/%s/progs.dat", host_parms.basedir, FindFileData.cFileName); FindProgs = FindFirstFile(childstring, &FindChildData); sprintf (childstring,"%s/%s/*.pak", host_parms.basedir, FindFileData.cFileName); FindPak = FindFirstFile(childstring, &FindChildData); if (FindProgs != INVALID_HANDLE_VALUE || FindPak != INVALID_HANDLE_VALUE) Modlist_Add (FindFileData.cFileName); FindClose (FindProgs); FindClose (FindPak); } while (FindNextFile(Find, &FindFileData)); FindClose (Find); #endif } /* ================== Host_Mods_f -- johnfitz list all potential mod directories (contain either a pak file or a progs.dat) ================== */ void Host_Mods_f (void) { int i; mod_t *mod; for (mod = modlist, i=0; mod; mod = mod->next, i++) Con_SafePrintf (" %s\n", mod->name); if (i) Con_SafePrintf ("%i mod(s)\n", i); else Con_SafePrintf ("no mods found\n"); } //============================================================================== /* ============= Host_Mapname_f -- johnfitz ============= */ void Host_Mapname_f (void) { char name[MAX_QPATH]; if (sv.active) { COM_StripExtension (sv.worldmodel->name + 5, name); Con_Printf ("\"mapname\" is \"%s\"\n", name); return; } if (cls.state == ca_connected) { COM_StripExtension (cl.worldmodel->name + 5, name); Con_Printf ("\"mapname\" is \"%s\"\n", name); return; } Con_Printf ("no map loaded\n"); } /* ================== Host_Status_f ================== */ void Host_Status_f (void) { client_t *client; int seconds; int minutes; int hours = 0; int j; void (*print) (const char *fmt, ...); if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } print = Con_Printf; } else print = SV_ClientPrintf; print ("host: %s\n", Cvar_VariableString ("hostname")); print ("version: %4.2f\n", VERSION); if (tcpipAvailable) print ("tcp/ip: %s\n", my_tcpip_address); print ("map: %s\n", sv.name); print ("players: %i active (%i max)\n\n", net_activeconnections, svs.maxclients); for (j=0, client = svs.clients ; jactive) continue; seconds = (int)(net_time - client->netconnection->connecttime); minutes = seconds / 60; if (minutes) { seconds -= (minutes * 60); hours = minutes / 60; if (hours) minutes -= (hours * 60); } else hours = 0; print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j+1, client->name, (int)client->edict->v.frags, hours, minutes, seconds); print (" %s\n", client->netconnection->address); } } /* ================== Host_God_f Sets client to godmode ================== */ void Host_God_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch && !host_client->privileged) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; if (!((int)sv_player->v.flags & FL_GODMODE) ) SV_ClientPrintf ("godmode OFF\n"); else SV_ClientPrintf ("godmode ON\n"); } void Host_Notarget_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch && !host_client->privileged) return; sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET; if (!((int)sv_player->v.flags & FL_NOTARGET) ) SV_ClientPrintf ("notarget OFF\n"); else SV_ClientPrintf ("notarget ON\n"); } bool noclip_anglehack; void Host_Noclip_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch && !host_client->privileged) return; if (sv_player->v.movetype != MOVETYPE_NOCLIP) { noclip_anglehack = true; sv_player->v.movetype = MOVETYPE_NOCLIP; SV_ClientPrintf ("noclip ON\n"); } else { noclip_anglehack = false; sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf ("noclip OFF\n"); } } /* ================== Host_Fly_f Sets client to flymode ================== */ void Host_Fly_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch && !host_client->privileged) return; if (sv_player->v.movetype != MOVETYPE_FLY) { sv_player->v.movetype = MOVETYPE_FLY; SV_ClientPrintf ("flymode ON\n"); } else { sv_player->v.movetype = MOVETYPE_WALK; SV_ClientPrintf ("flymode OFF\n"); } } /* ================== Host_Ping_f ================== */ void Host_Ping_f (void) { int i, j; float total; client_t *client; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } SV_ClientPrintf ("Client ping times:\n"); for (i=0, client = svs.clients ; iactive) continue; total = 0; for (j=0 ; jping_times[j]; total /= NUM_PING_TIMES; SV_ClientPrintf ("%4i %s\n", (int)(total*1000), client->name); } } /* =============================================================================== SERVER TRANSITIONS =============================================================================== */ /* ====================== Host_Map_f handle a map command from the console. Active clients are kicked off. ====================== */ void Host_Map_f (void) { int i; char name[MAX_QPATH]; if (cmd_source != src_command) return; cls.demonum = -1; // stop demo loop in case this fails CL_Disconnect (); Host_ShutdownServer(false); key_dest = key_game; // remove console or menu SCR_BeginLoadingPlaque (); cls.mapstring[0] = 0; for (i=0 ; i : continue game on a new level\n"); return; } if (!sv.active || cls.demoplayback) { Con_Printf ("Only the server may changelevel\n"); return; } SV_SaveSpawnparms (); strcpy (level, Cmd_Argv(1)); SV_SpawnServer (level); } /* ================== Host_Restart_f Restarts the current server for a dead player ================== */ void Host_Restart_f (void) { char mapname[MAX_QPATH]; if (cls.demoplayback || !sv.active) return; if (cmd_source != src_command) return; strcpy (mapname, sv.name); // must copy out, because it gets cleared // in sv_spawnserver SV_SpawnServer (mapname); } /* ================== Host_Reconnect_f This command causes the client to wait for the signon messages again. This is sent just before a server changes levels ================== */ void Host_Reconnect_f (void) { SCR_BeginLoadingPlaque (); cls.signon = 0; // need new connection messages } extern char server_name[MAX_QPATH]; // JPG 3.50 /* ===================== Host_Connect_f User command to connect to server ===================== */ void Host_Connect_f (void) { char name[MAX_QPATH]; cls.demonum = -1; // stop demo loop in case this fails if (cls.demoplayback) { CL_StopPlayback (); CL_Disconnect (); } strcpy (name, Cmd_Argv(1)); CL_EstablishConnection (name); Host_Reconnect_f (); strcpy(server_name, name); // JPG 3.50 } /* =============================================================================== LOAD / SAVE GAME =============================================================================== */ #define SAVEGAME_VERSION 5 /* =============== Host_SavegameComment Writes a SAVEGAME_COMMENT_LENGTH character comment describing the current =============== */ void Host_SavegameComment (char *text) { int i; char kills[20]; for (i=0 ; i : save a game\n"); return; } if (strstr(Cmd_Argv(1), "..")) { Con_Printf ("Relative pathnames are not allowed.\n"); return; } for (i=0 ; iv.health <= 0) ) { Con_Printf ("Can't savegame with a dead player\n"); return; } } sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); Con_Printf ("Saving game to %s...\n", name); f = fopen (name, "w"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fprintf (f, "%i\n", SAVEGAME_VERSION); Host_SavegameComment (comment); fprintf (f, "%s\n", comment); for (i=0 ; ispawn_parms[i]); fprintf (f, "%d\n", current_skill); fprintf (f, "%s\n", sv.name); fprintf (f, "%f\n",sv.time); // write the light styles for (i=0 ; i : load a game\n"); return; } cls.demonum = -1; // stop demo loop in case this fails sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1)); COM_DefaultExtension (name, ".sav"); // we can't call SCR_BeginLoadingPlaque, because too much stack space has // been used. The menu calls it before stuffing loadgame command // SCR_BeginLoadingPlaque (); Con_Printf ("Loading game from %s...\n", name); f = fopen (name, "r"); if (!f) { Con_Printf ("ERROR: couldn't open.\n"); return; } fscanf (f, "%i\n", &version); if (version != SAVEGAME_VERSION) { fclose (f); Con_Printf ("Savegame is version %i, not %i\n", version, SAVEGAME_VERSION); return; } str = Sys_BigStackAlloc(32768, "Host_Loadgame_f"); fscanf (f, "%s\n", str); for (i=0 ; iv, 0, progs->entityfields * 4); ent->free = false; ED_ParseEdict (start, ent); // link it into the bsp tree if (!ent->free) SV_LinkEdict (ent, false); } entnum++; } sv.num_edicts = entnum; sv.time = time; fclose (f); for (i=0 ; ispawn_parms[i] = spawn_parms[i]; if (cls.state != ca_dedicated) { CL_EstablishConnection ("local"); Host_Reconnect_f (); } Sys_BigStackFree(32768, "Host_Loadgame_f"); } //============================================================================ /* ====================== Host_Name_f ====================== */ void Host_Name_f (void) { int a; char *newName; if (Cmd_Argc () == 1) { Con_Printf ("\"name\" is \"%s\"\n", cl_name.string); return; } if (Cmd_Argc () == 2) newName = Cmd_Argv(1); else newName = Cmd_Args(); newName[15] = 0; // JPG 3.02 - remove bad characters for (a = 0 ; newName[a] ; a++) { if (newName[a] == 10) newName[a] = ' '; else if (newName[a] == 13) newName[a] += 128; } if (cmd_source == src_command) { if (strcmp(cl_name.string, newName) == 0) return; Cvar_Set ("_cl_name", newName); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } if (host_client->name[0] && strcmp(host_client->name, "unconnected") ) if (strcmp(host_client->name, newName) != 0) Con_Printf ("%s renamed to %s\n", host_client->name, newName); strcpy (host_client->name, newName); host_client->edict->v.netname = host_client->name - pr_strings; // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatename); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteString (&sv.reliable_datagram, host_client->name); } void Host_Version_f (void) { Con_Printf ("%s - Version %4.2f (GIT: %s)\n", ENGINE_NAME, VERSION, GIT_VERSION); Con_Printf ("Compiled: "__TIME__" "__DATE__"\n"); Con_Printf ("ProQuake protocol: %4.2f. \n", VERSION_PROQUAKE); } #ifdef IDGODS void Host_Please_f (void) { client_t *cl; int j; if (cmd_source != src_command) return; if ((Cmd_Argc () == 3) && strcmp(Cmd_Argv(1), "#") == 0) { j = atof(Cmd_Argv(2)) - 1; if (j < 0 || j >= svs.maxclients) return; if (!svs.clients[j].active) return; cl = &svs.clients[j]; if (cl->privileged) { cl->privileged = false; cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET); cl->edict->v.movetype = MOVETYPE_WALK; noclip_anglehack = false; } else cl->privileged = true; } if (Cmd_Argc () != 2) return; for (j=0, cl = svs.clients ; jactive) continue; if (strcasecmp(cl->name, Cmd_Argv(1)) == 0) { if (cl->privileged) { cl->privileged = false; cl->edict->v.flags = (int)cl->edict->v.flags & ~(FL_GODMODE|FL_NOTARGET); cl->edict->v.movetype = MOVETYPE_WALK; noclip_anglehack = false; } else cl->privileged = true; break; } } } #endif void Host_Say(bool teamonly) { client_t *client; client_t *save; int j; char *p; unsigned char text[64]; bool fromServer = false; if (cmd_source == src_command) { if (cls.state == ca_dedicated) { fromServer = true; teamonly = false; } else { Cmd_ForwardToServer (); return; } } if (Cmd_Argc () < 2) return; save = host_client; p = Cmd_Args(); // remove quotes if present if (*p == '"') { p++; p[strlen(p)-1] = 0; } // turn on color set 1 if (!fromServer) sprintf (text, "%c%s: ", 1, save->name); else sprintf (text, "%c<%s> ", 1, hostname.string); j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator if (strlen(p) > j) p[j] = 0; strcat (text, p); strcat (text, "\n"); for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client || !client->active || !client->spawned) continue; if (teamplay.value && teamonly && client->edict->v.team != save->edict->v.team) continue; host_client = client; SV_ClientPrintf("%s", text); } host_client = save; Sys_Printf("%s", &text[1]); } void Host_Say_f(void) { Host_Say(false); } void Host_Say_Team_f(void) { Host_Say(true); } void Host_Tell_f(void) { client_t *client; client_t *save; int j; char *p; char text[64]; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (Cmd_Argc () < 3) return; strcpy(text, host_client->name); strcat(text, ": "); p = Cmd_Args(); // remove quotes if present if (*p == '"') { p++; p[strlen(p)-1] = 0; } // check length & truncate if necessary j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator if (strlen(p) > j) p[j] = 0; strcat (text, p); strcat (text, "\n"); save = host_client; for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) { if (!client->active || !client->spawned) continue; if (strcasecmp(client->name, Cmd_Argv(1))) continue; host_client = client; SV_ClientPrintf("%s", text); break; } host_client = save; } /* ================== Host_Color_f ================== */ void Host_Color_f(void) { int top, bottom; int playercolor; if (Cmd_Argc() == 1) { Con_Printf ("\"color\" is \"%i %i\"\n", ((int)cl_color.value) >> 4, ((int)cl_color.value) & 0x0f); Con_Printf ("color <0-13> [0-13]\n"); return; } if (Cmd_Argc() == 2) top = bottom = atoi(Cmd_Argv(1)); else { top = atoi(Cmd_Argv(1)); bottom = atoi(Cmd_Argv(2)); } top &= 15; if (top > 13) top = 13; bottom &= 15; if (bottom > 13) bottom = 13; playercolor = top*16 + bottom; if (cmd_source == src_command) { Cvar_SetValue ("_cl_color", playercolor); if (cls.state == ca_connected) Cmd_ForwardToServer (); return; } host_client->colors = playercolor; host_client->edict->v.team = bottom + 1; // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_updatecolors); MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients); MSG_WriteByte (&sv.reliable_datagram, host_client->colors); } /* ================== Host_Kill_f ================== */ void Host_Kill_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (sv_player->v.health <= 0) { SV_ClientPrintf ("Can't suicide -- already dead!\n"); return; } pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientKill); } /* ================== Host_Pause_f ================== */ void Host_Pause_f (void) { if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (!pausable.value) SV_ClientPrintf ("Pause not allowed.\n"); else { sv.paused ^= 1; if (svs.maxclients > 1) SV_BroadcastPrintf ("%s %s the game\n", pr_strings + sv_player->v.netname, sv.paused ? "paused":"unpaused"); // send notification to all clients MSG_WriteByte (&sv.reliable_datagram, svc_setpause); MSG_WriteByte (&sv.reliable_datagram, sv.paused); } } //=========================================================================== /* ================== Host_PreSpawn_f ================== */ void Host_PreSpawn_f (void) { if (cmd_source == src_command) { Con_Printf ("prespawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("prespawn not valid -- already spawned\n"); return; } SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 2); host_client->sendsignon = true; } /* ================== Host_Spawn_f ================== */ void Host_Spawn_f (void) { int i; client_t *client; edict_t *ent; float *sendangle; if (cmd_source == src_command) { Con_Printf ("spawn is not valid from the console\n"); return; } if (host_client->spawned) { Con_Printf ("Spawn not valid -- already spawned\n"); return; } // run the entrance script if (sv.loadgame) { // loaded games are fully inited already // if this is the last client to be connected, unpause sv.paused = false; } else { // set up the edict ent = host_client->edict; memset (&ent->v, 0, progs->entityfields * 4); ent->v.colormap = NUM_FOR_EDICT(ent); ent->v.team = (host_client->colors & 15) + 1; ent->v.netname = host_client->name - pr_strings; // copy spawn parms out of the client_t for (i=0 ; i< NUM_SPAWN_PARMS ; i++) (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; // call the spawn function pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(sv_player); PR_ExecuteProgram (pr_global_struct->ClientConnect); if ((Sys_FloatTime() - host_client->netconnection->connecttime) <= sv.time) Sys_Printf ("%s entered the game\n", host_client->name); PR_ExecuteProgram (pr_global_struct->PutClientInServer); } // send all current names, colors, and frag counts SZ_Clear (&host_client->message); // send time of update MSG_WriteByte (&host_client->message, svc_time); MSG_WriteFloat (&host_client->message, sv.time); for (i=0, client = svs.clients ; imessage, svc_updatename); MSG_WriteByte (&host_client->message, i); MSG_WriteString (&host_client->message, client->name); MSG_WriteByte (&host_client->message, svc_updatefrags); MSG_WriteByte (&host_client->message, i); MSG_WriteShort (&host_client->message, client->old_frags); MSG_WriteByte (&host_client->message, svc_updatecolors); MSG_WriteByte (&host_client->message, i); MSG_WriteByte (&host_client->message, client->colors); } // send all current light styles for (i=0 ; imessage, svc_lightstyle); MSG_WriteByte (&host_client->message, (char)i); MSG_WriteString (&host_client->message, sv.lightstyles[i]); } // // send some stats // MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS); MSG_WriteLong (&host_client->message, pr_global_struct->total_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS); MSG_WriteLong (&host_client->message, pr_global_struct->total_monsters); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_SECRETS); MSG_WriteLong (&host_client->message, pr_global_struct->found_secrets); MSG_WriteByte (&host_client->message, svc_updatestat); MSG_WriteByte (&host_client->message, STAT_MONSTERS); MSG_WriteLong (&host_client->message, pr_global_struct->killed_monsters); // // send a fixangle // Never send a roll angle, because savegames can catch the server // in a state where it is expecting the client to correct the angle // and it won't happen if the game was just loaded, so you wind up // with a permanent head tilt ent = EDICT_NUM( 1 + (host_client - svs.clients) ); MSG_WriteByte (&host_client->message, svc_setangle); sendangle = sv.loadgame ? ent->v.v_angle : ent->v.angles; for (i=0 ; i < 2 ; i++) MSG_WriteAngle (&host_client->message, sendangle[i]); MSG_WriteAngle (&host_client->message, 0 ); SV_WriteClientdataToMessage (sv_player, &host_client->message); MSG_WriteByte (&host_client->message, svc_signonnum); MSG_WriteByte (&host_client->message, 3); host_client->sendsignon = true; } /* ================== Host_Begin_f ================== */ void Host_Begin_f (void) { if (cmd_source == src_command) { Con_Printf ("begin is not valid from the console\n"); return; } host_client->spawned = true; } //=========================================================================== /* ================== Host_Kick_f Kicks a user off of the server ================== */ void Host_Kick_f (void) { char *who; char *message = NULL; client_t *save; int i; bool byNumber = false; if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } } else if (pr_global_struct->deathmatch && !host_client->privileged) return; save = host_client; if (Cmd_Argc() > 2 && strcmp(Cmd_Argv(1), "#") == 0) { i = atof(Cmd_Argv(2)) - 1; if (i < 0 || i >= svs.maxclients) return; if (!svs.clients[i].active) return; host_client = &svs.clients[i]; byNumber = true; } else { for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) { if (!host_client->active) continue; if (strcasecmp(host_client->name, Cmd_Argv(1)) == 0) break; } } if (i < svs.maxclients) { if (cmd_source == src_command) if (cls.state == ca_dedicated) who = "Console"; else who = cl_name.string; else who = save->name; // can't kick yourself! if (host_client == save) return; if (Cmd_Argc() > 2) { message = COM_Parse(Cmd_Args()); if (byNumber) { message++; // skip the # while (*message == ' ') // skip white space message++; message += strlen(Cmd_Argv(2)); // skip the number } while (*message && *message == ' ') message++; } if (message) SV_ClientPrintf ("Kicked by %s: %s\n", who, message); else SV_ClientPrintf ("Kicked by %s\n", who); SV_DropClient (false); } host_client = save; } /* =============================================================================== DEBUGGING TOOLS =============================================================================== */ /* ================== Host_Give_f ================== */ void Host_Give_f (void) { char *t; int v, w; eval_t *val; if (cmd_source == src_command) { Cmd_ForwardToServer (); return; } if (pr_global_struct->deathmatch && !host_client->privileged) return; t = Cmd_Argv(1); v = atoi (Cmd_Argv(2)); switch (t[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // MED 01/04/97 added hipnotic give stuff if (hipnotic) { if (t[0] == '6') { if (t[1] == 'a') sv_player->v.items = (int)sv_player->v.items | HIT_PROXIMITY_GUN; else sv_player->v.items = (int)sv_player->v.items | IT_GRENADE_LAUNCHER; } else if (t[0] == '9') sv_player->v.items = (int)sv_player->v.items | HIT_LASER_CANNON; else if (t[0] == '0') sv_player->v.items = (int)sv_player->v.items | HIT_MJOLNIR; else if (t[0] >= '2') sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2')); } else { if (t[0] >= '2') sv_player->v.items = (int)sv_player->v.items | (IT_SHOTGUN << (t[0] - '2')); } break; case 's': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_shells1"); if (val) val->_float = v; } sv_player->v.ammo_shells = v; break; case 'n': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_nails1"); if (val) { val->_float = v; if (sv_player->v.weapon <= IT_LIGHTNING) sv_player->v.ammo_nails = v; } } else { sv_player->v.ammo_nails = v; } break; case 'l': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_lava_nails"); if (val) { val->_float = v; if (sv_player->v.weapon > IT_LIGHTNING) sv_player->v.ammo_nails = v; } } break; case 'r': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_rockets1"); if (val) { val->_float = v; if (sv_player->v.weapon <= IT_LIGHTNING) sv_player->v.ammo_rockets = v; } } else { sv_player->v.ammo_rockets = v; } break; case 'm': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_multi_rockets"); if (val) { val->_float = v; if (sv_player->v.weapon > IT_LIGHTNING) sv_player->v.ammo_rockets = v; } } break; case 'h': sv_player->v.health = v; break; case 'c': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_cells1"); if (val) { val->_float = v; if (sv_player->v.weapon <= IT_LIGHTNING) sv_player->v.ammo_cells = v; } } else { sv_player->v.ammo_cells = v; } break; case 'p': if (rogue) { val = GetEdictFieldValue(sv_player, "ammo_plasma"); if (val) { val->_float = v; if (sv_player->v.weapon > IT_LIGHTNING) sv_player->v.ammo_cells = v; } } break; } } edict_t *FindViewthing (void) { int i; edict_t *e; for (i=0 ; iv.classname, "viewthing") ) return e; } Con_Printf ("No viewthing on map\n"); return NULL; } /* ================== Host_Viewmodel_f ================== */ void Host_Viewmodel_f (void) { edict_t *e; model_t *m; e = FindViewthing (); if (!e) return; m = Mod_ForName (Cmd_Argv(1), false); if (!m) { Con_Printf ("Can't load %s\n", Cmd_Argv(1)); return; } e->v.frame = 0; cl.model_precache[(int)e->v.modelindex] = m; } /* ================== Host_Viewframe_f ================== */ void Host_Viewframe_f (void) { edict_t *e; int f; model_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; f = atoi(Cmd_Argv(1)); if (f >= m->numframes) f = m->numframes-1; e->v.frame = f; } void PrintFrameName (model_t *m, int frame) { aliashdr_t *hdr; maliasframedesc_t *pframedesc; hdr = (aliashdr_t *)Mod_Extradata (m); if (!hdr) return; pframedesc = &hdr->frames[frame]; Con_Printf ("frame %i: %s\n", frame, pframedesc->name); } /* ================== Host_Viewnext_f ================== */ void Host_Viewnext_f (void) { edict_t *e; model_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; e->v.frame = e->v.frame + 1; if (e->v.frame >= m->numframes) e->v.frame = m->numframes - 1; PrintFrameName (m, e->v.frame); } /* ================== Host_Viewprev_f ================== */ void Host_Viewprev_f (void) { edict_t *e; model_t *m; e = FindViewthing (); if (!e) return; m = cl.model_precache[(int)e->v.modelindex]; e->v.frame = e->v.frame - 1; if (e->v.frame < 0) e->v.frame = 0; PrintFrameName (m, e->v.frame); } /* =============================================================================== DEMO LOOP CONTROL =============================================================================== */ /* ================== Host_Startdemos_f ================== */ void Host_Startdemos_f (void) { int i, c; if (cls.state == ca_dedicated) { if (!sv.active) Cbuf_AddText ("map start\n"); return; } c = Cmd_Argc() - 1; if (c > MAX_DEMOS) { Con_Printf ("Max %i demos in demoloop\n", MAX_DEMOS); c = MAX_DEMOS; } Con_Printf ("%i demo(s) in loop\n", c); for (i=1 ; if = f; return buf; } static void Buf_Free(stdio_buffer_t *buf) { free(buf); } static inline int Buf_GetC(stdio_buffer_t *buf) { if (buf->pos >= buf->size) { buf->size = fread(buf->buffer, 1, sizeof(buf->buffer), buf->f); buf->pos = 0; if (buf->size == 0) return EOF; } return buf->buffer[buf->pos++]; } /* ============ Image_LoadImage returns a pointer to hunk allocated RGBA data TODO: search order: tga png jpg pcx lmp ============ */ byte *Image_LoadImage (const char *name, int *width, int *height) { FILE *f; snprintf (loadfilename, sizeof(loadfilename), "%s.tga", name); COM_FOpenFile (loadfilename, &f, NULL); if (f) return Image_LoadTGA (f, width, height); snprintf (loadfilename, sizeof(loadfilename), "%s.pcx", name); COM_FOpenFile (loadfilename, &f, NULL); if (f) return Image_LoadPCX (f, width, height); return NULL; } //============================================================================== // // TGA // //============================================================================== typedef struct targaheader_s { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } targaheader_t; #define TARGAHEADERSIZE 18 //size on disk targaheader_t targa_header; int fgetLittleShort (FILE *f) { byte b1, b2; b1 = fgetc(f); b2 = fgetc(f); return (short)(b1 + b2*256); } int fgetLittleLong (FILE *f) { byte b1, b2, b3, b4; b1 = fgetc(f); b2 = fgetc(f); b3 = fgetc(f); b4 = fgetc(f); return b1 + (b2<<8) + (b3<<16) + (b4<<24); } /* ============= Image_LoadTGA ============= */ byte *Image_LoadTGA (FILE *fin, int *width, int *height) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *targa_rgba; int realrow; //johnfitz -- fix for upside-down targas bool upside_down; //johnfitz -- fix for upside-down targas stdio_buffer_t *buf; targa_header.id_length = fgetc(fin); targa_header.colormap_type = fgetc(fin); targa_header.image_type = fgetc(fin); targa_header.colormap_index = fgetLittleShort(fin); targa_header.colormap_length = fgetLittleShort(fin); targa_header.colormap_size = fgetc(fin); targa_header.x_origin = fgetLittleShort(fin); targa_header.y_origin = fgetLittleShort(fin); targa_header.width = fgetLittleShort(fin); targa_header.height = fgetLittleShort(fin); targa_header.pixel_size = fgetc(fin); targa_header.attributes = fgetc(fin); if (targa_header.image_type!=2 && targa_header.image_type!=10) Sys_Error ("Image_LoadTGA: %s is not a type 2 or type 10 targa\n", loadfilename); if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24)) Sys_Error ("Image_LoadTGA: %s is not a 24bit or 32bit targa\n", loadfilename); columns = targa_header.width; rows = targa_header.height; numPixels = columns * rows; upside_down = !(targa_header.attributes & 0x20); //johnfitz -- fix for upside-down targas targa_rgba = (byte *) malloc (numPixels*4); if (targa_header.id_length != 0) fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment buf = Buf_Alloc(fin); if (targa_header.image_type==2) // Uncompressed, RGB images { for(row=rows-1; row>=0; row--) { //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz for(column=0; column=0; row--) { //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz for(column=0; column0) row--; else goto breakOut; //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz } } } else // non run-length packet { for(j=0;j0) row--; else goto breakOut; //johnfitz -- fix for upside-down targas realrow = upside_down ? row : rows - 1 - row; pixbuf = targa_rgba + realrow*columns*4; //johnfitz } } } } breakOut:; } } Buf_Free(buf); fclose(fin); *width = (int)(targa_header.width); *height = (int)(targa_header.height); return targa_rgba; } //============================================================================== // // PCX // //============================================================================== typedef struct { char signature; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hdpi,vdpi; byte colortable[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; } pcxheader_t; /* ============ Image_LoadPCX ============ */ byte *Image_LoadPCX (FILE *f, int *width, int *height) { pcxheader_t pcx; int x, y, w, h, readbyte, runlength, start; byte *p, *data; byte palette[768]; stdio_buffer_t *buf; start = ftell (f); //save start of file (since we might be inside a pak file, SEEK_SET might not be the start of the pcx) fread(&pcx, sizeof(pcx), 1, f); pcx.xmin = (unsigned short)LittleShort (pcx.xmin); pcx.ymin = (unsigned short)LittleShort (pcx.ymin); pcx.xmax = (unsigned short)LittleShort (pcx.xmax); pcx.ymax = (unsigned short)LittleShort (pcx.ymax); pcx.bytes_per_line = (unsigned short)LittleShort (pcx.bytes_per_line); if (pcx.signature != 0x0A) Sys_Error ("'%s' is not a valid PCX file", loadfilename); if (pcx.version != 5) Sys_Error ("'%s' is version %i, should be 5", loadfilename, pcx.version); if (pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.color_planes != 1) Sys_Error ("'%s' has wrong encoding or bit depth", loadfilename); w = pcx.xmax - pcx.xmin + 1; h = pcx.ymax - pcx.ymin + 1; data = (byte *) malloc((w*h+1)*4); //+1 to allow reading padding byte on last line //load palette fseek (f, start + com_filesize - 768, SEEK_SET); fread (palette, 1, 768, f); //back to start of image data fseek (f, start + sizeof(pcx), SEEK_SET); buf = Buf_Alloc(f); for (y=0; y= 0xC0) { runlength = readbyte & 0x3F; readbyte = Buf_GetC(buf); } else runlength = 1; while(runlength--) { p[0] = palette[readbyte*3]; p[1] = palette[readbyte*3+1]; p[2] = palette[readbyte*3+2]; p[3] = 255; p += 4; x++; } } } Buf_Free(buf); fclose(f); *width = w; *height = h; return data; } ================================================ FILE: source/image.h ================================================ /* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2010-2014 QuakeSpasm developers 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef IMAGE_H #define IMAGE_H //image.h -- image reading / writing //be sure to free the hunk after using these loading functions byte *Image_LoadTGA (FILE *f, int *width, int *height); byte *Image_LoadPCX (FILE *f, int *width, int *height); byte *Image_LoadImage (const char *name, int *width, int *height); #endif /* IMAGE_H */ ================================================ FILE: source/in_psp2.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include CVAR (m_filter, 0, CVAR_ARCHIVE) CVAR (pstv_rumble, 1, CVAR_ARCHIVE) CVAR (retrotouch, 0, CVAR_ARCHIVE) CVAR (psvita_touchmode, 0, CVAR_ARCHIVE) // 0: as a button / 1: as a joystick CVAR (psvita_front_sensitivity_x, 1, CVAR_ARCHIVE) CVAR (psvita_front_sensitivity_y, 0.5, CVAR_ARCHIVE) CVAR (psvita_back_sensitivity_x, 1, CVAR_ARCHIVE) CVAR (psvita_back_sensitivity_y, 0.5, CVAR_ARCHIVE) CVAR (motioncam, 0, CVAR_ARCHIVE) CVAR (motion_horizontal_sensitivity, 0, CVAR_ARCHIVE) CVAR (motion_vertical_sensitivity, 0, CVAR_ARCHIVE) extern cvar_t always_run, invert_camera; extern void Log (const char *format, ...); #define lerp(value, from_max, to_max) ((((value*10) * (to_max*10))/(from_max*10))/10) uint64_t rumble_tick = 0; SceCtrlData oldanalogs, analogs; SceMotionState motionstate; void IN_Init (void) { Cvar_RegisterVariable (&m_filter); Cvar_RegisterVariable (&retrotouch); Cvar_RegisterVariable (&always_run); Cvar_RegisterVariable (&invert_camera); Cvar_RegisterVariable (&pstv_rumble); Cvar_RegisterVariable(&psvita_touchmode); Cvar_RegisterVariable (&motioncam); Cvar_RegisterVariable (&motion_horizontal_sensitivity); Cvar_RegisterVariable (&motion_vertical_sensitivity); //Touchscreen sensitivity Cvar_RegisterVariable(&psvita_front_sensitivity_x); Cvar_RegisterVariable(&psvita_front_sensitivity_y); Cvar_RegisterVariable(&psvita_back_sensitivity_x); Cvar_RegisterVariable(&psvita_back_sensitivity_y); sceMotionReset(); sceMotionStartSampling(); } void IN_ResetInputs(void) { // Set default PSVITA controls Cbuf_AddText("unbindall\n"); Cbuf_AddText("bind CROSS +jump\n"); // Cross Cbuf_AddText("bind SQUARE +attack\n"); // Square Cbuf_AddText("bind CIRCLE \"impulse 12\"\n"); // Circle Cbuf_AddText("bind TRIANGLE \"impulse 10\"\n"); // Triangle Cbuf_AddText("bind LTRIGGER +jump\n"); // Left Trigger Cbuf_AddText("bind RTRIGGER +attack\n"); // Right Trigger Cbuf_AddText("bind UPARROW +forward\n"); // Up Cbuf_AddText("bind DOWNARROW +back\n"); // Down Cbuf_AddText("bind LEFTARROW +moveleft\n"); // Left Cbuf_AddText("bind RIGHTARROW +moveright\n"); // Right Cbuf_AddText("bind TOUCH +showscores\n"); // Touchscreen Cbuf_AddText("bind SELECT +showscores\n"); // Touchscreen Cbuf_AddText("sensitivity 3.5\n"); // Right Analog Sensitivity } void IN_Shutdown (void) { } void IN_Commands (void) { } void IN_StartRumble (void) { if (!pstv_rumble.value) return; SceCtrlActuator handle; handle.small = 100; handle.large = 100; sceCtrlSetActuator(1, &handle); rumble_tick = sceKernelGetProcessTimeWide(); } void IN_StopRumble (void) { SceCtrlActuator handle; handle.small = 0; handle.large = 0; sceCtrlSetActuator(1, &handle); rumble_tick = 0; } void IN_RescaleAnalog(int *x, int *y, int dead) { //radial and scaled deadzone //http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html float analogX = (float) *x; float analogY = (float) *y; float deadZone = (float) dead; float maximum = 128.0f; float magnitude = sqrt(analogX * analogX + analogY * analogY); if (magnitude >= deadZone) { float scalingFactor = maximum / magnitude * (magnitude - deadZone) / (maximum - deadZone); *x = (int) (analogX * scalingFactor); *y = (int) (analogY * scalingFactor); } else { *x = 0; *y = 0; } } void IN_Move (usercmd_t *cmd) { // ANALOGS if ((in_speed.state & 1) || always_run.value){ cl_forwardspeed.value = 400; cl_backspeed.value = 400; cl_sidespeed.value = 700; }else{ cl_forwardspeed.value = 200; cl_backspeed.value = 200; cl_sidespeed.value = 300; } sceCtrlPeekBufferPositive(0, &analogs, 1); int left_x = analogs.lx - 127; int left_y = analogs.ly - 127; int right_x = analogs.rx - 127; int right_y = analogs.ry - 127; // Left analog support for player movement float x_mov = abs(left_x) < 30 ? 0 : (left_x * cl_sidespeed.value) * 0.01; float y_mov = abs(left_y) < 30 ? 0 : (left_y * (left_y > 0 ? cl_backspeed.value : cl_forwardspeed.value)) * 0.01; cmd->forwardmove -= y_mov; if (gl_xflip.value) cmd->sidemove -= x_mov; else cmd->sidemove += x_mov; // Right analog support for camera movement IN_RescaleAnalog(&right_x, &right_y, 30); float x_cam = (right_x * sensitivity.value) * 0.008; float y_cam = (right_y * sensitivity.value) * 0.008; if (gl_xflip.value) cl.viewangles[YAW] += x_cam; else cl.viewangles[YAW] -= x_cam; V_StopPitchDrift(); if (invert_camera.value) cl.viewangles[PITCH] -= y_cam; else cl.viewangles[PITCH] += y_cam; // TOUCH SUPPORT // Retrotouch support for camera movement SceTouchData touch; if (retrotouch.value){ sceTouchPeek(SCE_TOUCH_PORT_BACK, &touch, 1); if (touch.reportNum > 0) { int raw_x = lerp(touch.report[0].x, 1919, 960); int raw_y = lerp(touch.report[0].y, 1087, 544); int touch_x = raw_x - 480; int touch_y = raw_y - 272; x_cam = abs(touch_x) < 20 ? 0 : touch_x * psvita_back_sensitivity_x.value * 0.008; y_cam = abs(touch_y) < 20 ? 0 : touch_y * psvita_back_sensitivity_x.value * 0.008; cl.viewangles[YAW] -= x_cam; V_StopPitchDrift(); if (invert_camera.value) cl.viewangles[PITCH] -= y_cam; else cl.viewangles[PITCH] += y_cam; } } if (psvita_touchmode.value == 1) { sceTouchPeek(SCE_TOUCH_PORT_FRONT, &touch, 1); if (touch.reportNum > 0) { int raw_x = lerp(touch.report[0].x, 1919, 960); int raw_y = lerp(touch.report[0].y, 1087, 544); int touch_x = raw_x - 480; int touch_y = raw_y - 272; x_cam = abs(touch_x) < 20 ? 0 : touch_x * psvita_front_sensitivity_x.value * 0.008; y_cam = abs(touch_y) < 20 ? 0 : touch_y * psvita_front_sensitivity_y.value * 0.008; cl.viewangles[YAW] -= x_cam; V_StopPitchDrift(); if (invert_camera.value) cl.viewangles[PITCH] -= y_cam; else cl.viewangles[PITCH] += y_cam; } } // gyro analog support for camera movement if (motioncam.value){ sceMotionGetState(&motionstate); // not sure why YAW or the horizontal x axis is the controlled by angularVelocity.y // and the PITCH or the vertical y axis is controlled by angularVelocity.x but its what seems to work float x_gyro_cam = motionstate.angularVelocity.y * motion_horizontal_sensitivity.value; float y_gyro_cam = motionstate.angularVelocity.x * motion_vertical_sensitivity.value; if (gl_xflip.value) cl.viewangles[YAW] -= x_gyro_cam; else cl.viewangles[YAW] += x_gyro_cam; V_StopPitchDrift(); if (invert_camera.value) cl.viewangles[PITCH] += y_gyro_cam; else cl.viewangles[PITCH] -= y_gyro_cam; } if (pq_fullpitch.value) cl.viewangles[PITCH] = COM_Clamp(cl.viewangles[PITCH], -90, 90); else cl.viewangles[PITCH] = COM_Clamp(cl.viewangles[PITCH], -70, 80); } ================================================ FILE: source/input.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // input.h -- external (non-keyboard) input devices void IN_Init (void); void IN_Shutdown (void); void IN_StartRumble (void); void IN_StopRumble (void); void IN_Commands (void); // oportunity for devices to stick commands on the script buffer void IN_Move (usercmd_t *cmd); // add additional movement on top of the keyboard move cmd void IN_ClearStates (void); // restores all button and position states to defaults void IN_ResetInputs (void); ================================================ FILE: source/keys.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" /* key up events are sent even if in console mode */ #define MAXCMDLINE 256 char key_lines[32][MAXCMDLINE]; int key_linepos; int shift_down=false; int key_lastpress; int edit_line=0; int history_line=0; keydest_t key_dest; int key_count; // incremented every key event char *keybindings[256]; bool consolekeys[256]; // if true, can't be rebound while in console bool menubound[256]; // if true, can't be rebound while in menu int keyshift[256]; // key to map to if shift held down in console int key_repeats[256]; // if > 1, it is autorepeating bool keydown[256]; typedef struct { char *name; int keynum; } keyname_t; keyname_t keynames[] = { {"TAB", K_TAB}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"TOUCH", K_TOUCH}, {"CTRL", K_CTRL}, {"SHIFT", K_SHIFT}, {"F1", K_F1}, {"F2", K_F2}, {"F3", K_F3}, {"F4", K_F4}, {"F5", K_F5}, {"F6", K_F6}, {"F7", K_F7}, {"F8", K_F8}, {"F9", K_F9}, {"F10", K_F10}, {"F11", K_F11}, {"F12", K_F12}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, // PSP / PSVITA Buttons {"CROSS", K_CROSS}, {"SQUARE", K_SQUARE }, {"TRIANGLE", K_TRIANGLE }, {"CIRCLE", K_CIRCLE }, {"LTRIGGER", K_LEFTTRIGGER }, {"RTRIGGER", K_RIGHTTRIGGER }, {"START", K_START }, // Start Button {"SELECT", K_SELECT }, // Select Button // OTHER Buttons {"AUX9", K_AUX9}, {"AUX10", K_AUX10}, {"AUX11", K_AUX11}, {"AUX12", K_AUX12}, {"AUX13", K_AUX13}, {"AUX14", K_AUX14}, {"AUX15", K_AUX15}, {"AUX16", K_AUX16}, {"AUX17", K_AUX17}, {"AUX18", K_AUX18}, {"AUX19", K_AUX19}, {"AUX20", K_AUX20}, {"AUX21", K_AUX21}, {"AUX22", K_AUX22}, {"AUX23", K_AUX23}, {"AUX24", K_AUX24}, {"AUX25", K_AUX25}, {"AUX26", K_AUX26}, {"AUX27", K_AUX27}, {"AUX28", K_AUX28}, {"AUX29", K_AUX29}, {"AUX30", K_AUX30}, {"AUX31", K_AUX31}, {"AUX32", K_AUX32}, {"PAUSE", K_PAUSE}, {"SEMICOLON", ';'}, // because a raw semicolon seperates commands {NULL,0} }; /* ============================================================================== LINE TYPING INTO THE CONSOLE ============================================================================== */ void Key_SendText(char *Text) { Cbuf_AddText(Text); Cbuf_AddText("\n"); Con_Printf("]%s\n", Text); edit_line = (edit_line + 1) & 31; history_line = edit_line; key_lines[edit_line][0] = ']'; key_linepos = 1; } /* ==================== Key_Console Interactive line editing and console scrollback ==================== */ void Key_Console (int key) { char *cmd; if (key == K_CROSS) { Key_SendText(key_lines[edit_line] + 1); if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command // may take some time return; } if (key == K_RIGHTARROW) // Was K_TAB { // command completion cmd = Cmd_CompleteCommand (key_lines[edit_line]+1); if (!cmd) cmd = Cvar_CompleteVariable (key_lines[edit_line]+1); if (cmd) { strcpy (key_lines[edit_line]+1, cmd); key_linepos = strlen(cmd)+1; key_lines[edit_line][key_linepos] = ' '; key_linepos++; key_lines[edit_line][key_linepos] = 0; return; } } if (key == K_BACKSPACE || key == K_LEFTARROW) { if (key_linepos > 1) key_linepos--; return; } if (key == K_UPARROW) { do { history_line = (history_line - 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) history_line = (edit_line+1)&31; strcpy(key_lines[edit_line], key_lines[history_line]); key_linepos = strlen(key_lines[edit_line]); return; } if (key == K_DOWNARROW) { if (history_line == edit_line) return; do { history_line = (history_line + 1) & 31; } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { key_lines[edit_line][0] = ']'; key_linepos = 1; } else { strcpy(key_lines[edit_line], key_lines[history_line]); key_linepos = strlen(key_lines[edit_line]); } return; } if (key == K_LEFTTRIGGER) // Scrolling up the console { con_backscroll += 2; if (con_backscroll > con_totallines - (vid.height>>3) - 1) con_backscroll = con_totallines - (vid.height>>3) - 1; return; } if (key == K_RIGHTTRIGGER) // Scrolling down the console { con_backscroll -= 2; if (con_backscroll < 0) con_backscroll = 0; return; } if (key == K_HOME) { con_backscroll = con_totallines - (vid.height>>3) - 1; return; } if (key == K_START) { con_backscroll = 0; return; } if (key < 32 || key > 127) return; // non printable if (key_linepos < MAXCMDLINE-1) { key_lines[edit_line][key_linepos] = key; key_linepos++; key_lines[edit_line][key_linepos] = 0; } } //============================================================================ char chat_buffer[32]; bool team_message = false; void Key_Message (int key) { static int chat_bufferlen = 0; if (key == K_CROSS) { if (team_message) Cbuf_AddText ("say_team \""); else Cbuf_AddText ("say \""); Cbuf_AddText(chat_buffer); Cbuf_AddText("\"\n"); key_dest = key_game; chat_bufferlen = 0; chat_buffer[0] = 0; return; } if (key == K_START) { key_dest = key_game; chat_bufferlen = 0; chat_buffer[0] = 0; return; } if (key < 32 || key > 127) return; // non printable if (key == K_LEFTARROW) { if (chat_bufferlen) { chat_bufferlen--; chat_buffer[chat_bufferlen] = 0; } return; } if (chat_bufferlen == 31) return; // all full chat_buffer[chat_bufferlen++] = key; chat_buffer[chat_bufferlen] = 0; } //============================================================================ /* =================== Key_StringToKeynum Returns a key number to be used to index keybindings[] by looking at the given string. Single ascii characters return themselves, while the K_* names are matched up. =================== */ int Key_StringToKeynum (char *str) { keyname_t *kn; if (!str || !str[0]) return -1; if (!str[1]) return str[0]; for (kn=keynames ; kn->name ; kn++) { if (!strcasecmp(str,kn->name)) return kn->keynum; } return -1; } /* =================== Key_KeynumToString Returns a string (either a single ascii char, or a K_* name) for the given keynum. FIXME: handle quote special (general escape sequence?) =================== */ char *Key_KeynumToString (int keynum) { keyname_t *kn; static char tinystr[2]; if (keynum == -1) return ""; if (keynum > 32 && keynum < 127) { // printable ascii tinystr[0] = keynum; tinystr[1] = 0; return tinystr; } for (kn=keynames ; kn->name ; kn++) if (keynum == kn->keynum) return kn->name; return ""; } /* =================== Key_SetBinding =================== */ void Key_SetBinding (int keynum, char *binding) { char *new; int l; if (keynum == -1) return; // free old bindings if (keybindings[keynum]) { Z_Free (keybindings[keynum]); keybindings[keynum] = NULL; } // allocate memory for new binding l = strlen (binding); new = Z_Malloc (l+1); strcpy (new, binding); new[l] = 0; keybindings[keynum] = new; } /* =================== Key_Unbind_f =================== */ void Key_Unbind_f (void) { int b; if (Cmd_Argc() != 2) { Con_Printf ("unbind : remove commands from a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b==-1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } Key_SetBinding (b, ""); } void Key_Unbindall_f (void) { int i; for (i=0 ; i<256 ; i++) if (keybindings[i]) Key_SetBinding (i, ""); } /* =================== Key_Bind_f =================== */ void Key_Bind_f (void) { int i, c, b; char *cmd; c = Cmd_Argc(); if (c != 2 && c != 3) { Con_Printf ("bind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum (Cmd_Argv(1)); if (b==-1) { Con_Printf ("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } if (c == 2) { if (keybindings[b]) Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b] ); else Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); return; } cmd = Sys_BigStackAlloc(1024, "Key_Bind_f"); // copy the rest of the command line cmd[0] = 0; // start out with a null string for (i=2 ; i< c ; i++) { if (i > 2) strcat (cmd, " "); strcat (cmd, Cmd_Argv(i)); } Key_SetBinding (b, cmd); Sys_BigStackFree(1024, "Key_Bind_f"); } /* ============ Key_WriteBindings Writes lines containing "bind key value" ============ */ void Key_WriteBindings (FILE *f) { int i; fprintf(f, "// Key bindings\n"); for (i=0 ; i<256 ; i++) if (keybindings[i]) if (*keybindings[i]) fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); } /* =================== Key_Init =================== */ void Key_Init (void) { int i; for (i=0 ; i<32 ; i++) { key_lines[i][0] = ']'; key_lines[i][1] = 0; } key_linepos = 1; //consolekeys[K_ENTER] = true; consolekeys[K_TAB] = true; consolekeys[K_UPARROW] = true; consolekeys[K_DOWNARROW] = true; consolekeys[K_LEFTARROW] = true; consolekeys[K_RIGHTARROW] = true; // Now acts as TAB // Add PSVita buttons to stop interfering in MP consolekeys[K_START] = true; // Toggles console consolekeys[K_SELECT] = true; consolekeys[K_LEFTTRIGGER] = true; consolekeys[K_RIGHTTRIGGER] = true; consolekeys[K_CROSS] = true; consolekeys[K_TRIANGLE] = true; consolekeys[K_SQUARE] = true; consolekeys[K_CIRCLE] = true; menubound[K_START] = true; // // register our functions // Cmd_AddCommand ("bind",Key_Bind_f); Cmd_AddCommand ("unbind",Key_Unbind_f); Cmd_AddCommand ("unbindall",Key_Unbindall_f); } /* =================== Key_Event Called by the system between frames for both key up and key down events Should NOT be called during an interrupt! =================== */ void Key_Event (int key, bool down) { char *kb; char* cmd; keydown[key] = down; if (!down) key_repeats[key] = 0; key_lastpress = key; key_count++; if (key_count <= 0) { return; // just catching keys for Con_NotifyBox } // update auto-repeat status if (down) { key_repeats[key]++; if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1) { return; // ignore most autorepeats } if (key >= 200 && !keybindings[key] && key != K_START && key != K_SELECT ) Con_Printf ("%s is unbound, please set a button in the options.\n", Key_KeynumToString (key) ); } if (key == K_SHIFT) shift_down = down; // // handle escape specialy, so the user can never unbind it // if (key == K_START || key == K_ENTER) { if (!down) return; switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_game: case key_console: M_ToggleMenu_f (); break; case key_benchmark: break; default: Sys_Error ("Bad key_dest"); } return; } cmd = Sys_BigStackAlloc(1024, "Key_Event"); // // key up events only generate commands if the game key binding is // a button command (leading + sign). These will occur even in console mode, // to keep the character from continuing an action started before a console // switch. Button commands include the kenum as a parameter, so multiple // downs can be matched with ups // if (!down) { kb = keybindings[key]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } if (keyshift[key] != key) { kb = keybindings[keyshift[key]]; if (kb && kb[0] == '+') { sprintf (cmd, "-%s %i\n", kb+1, key); Cbuf_AddText (cmd); } } Sys_BigStackFree(1024, "Key_Event"); return; } // // during demo playback, most keys bring up the main menu // if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game) { M_ToggleMenu_f (); Sys_BigStackFree(1024, "Key_Event"); return; } // // if not a consolekey, send to the interpreter no matter what mode is // if ( (key_dest == key_menu && menubound[key]) || (key_dest == key_console && !consolekeys[key]) || (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) ) { kb = keybindings[key]; if (kb) { if (kb[0] == '+') { // button commands add keynum as a parm sprintf (cmd, "%s %i\n", kb, key); Cbuf_AddText (cmd); } else { Cbuf_AddText (kb); Cbuf_AddText ("\n"); } } Sys_BigStackFree(1024, "Key_Event"); return; } if (!down) { Sys_BigStackFree(1024, "Key_Event"); return; // other systems only care about key down events } if (shift_down) { key = keyshift[key]; } switch (key_dest) { case key_message: Key_Message (key); break; case key_menu: M_Keydown (key); break; case key_game: case key_console: Key_Console (key); break; case key_benchmark: CL_Disconnect(); break; default: Sys_Error ("Bad key_dest"); } Sys_BigStackFree(1024, "Key_Event"); } /* =================== Key_ClearStates =================== */ void Key_ClearStates (void) { int i; for (i=0 ; i<256 ; i++) { keydown[i] = false; key_repeats[i] = 0; } } ================================================ FILE: source/keys.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // these are the key numbers that should be passed to Key_Event // #define K_TAB 9 #define K_ENTER 13 #define K_ESCAPE 27 #define K_SPACE 32 // normal keys should be passed as lowercased ascii #define K_BACKSPACE 127 #define K_UPARROW 128 #define K_DOWNARROW 129 #define K_LEFTARROW 130 #define K_RIGHTARROW 131 #define K_TOUCH 132 #define K_CTRL 133 #define K_SHIFT 134 #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_PAUSE 255 // // mouse buttons generate virtual keys // #define K_MOUSE1 200 #define K_MOUSE2 201 #define K_MOUSE3 202 // // joystick buttons // #define K_JOY1 203 #define K_JOY2 204 #define K_JOY3 205 #define K_JOY4 206 // PSP / PSVita Buttons #define K_CROSS 207 #define K_SQUARE 208 #define K_TRIANGLE 209 #define K_CIRCLE 210 #define K_LEFTTRIGGER 211 #define K_RIGHTTRIGGER 212 #define K_SELECT 213 #define K_START 214 // // aux keys are for multi-buttoned joysticks to generate so they can use // the normal binding process // #define K_AUX9 215 #define K_AUX10 216 #define K_AUX11 217 #define K_AUX12 218 #define K_AUX13 219 #define K_AUX14 220 #define K_AUX15 221 #define K_AUX16 222 #define K_AUX17 223 #define K_AUX18 224 #define K_AUX19 225 #define K_AUX20 226 #define K_AUX21 227 #define K_AUX22 228 #define K_AUX23 229 #define K_AUX24 230 #define K_AUX25 231 #define K_AUX26 232 #define K_AUX27 233 #define K_AUX28 234 #define K_AUX29 235 #define K_AUX30 236 #define K_AUX31 237 #define K_AUX32 238 // JACK: Intellimouse(c) Mouse Wheel Support #define K_MWHEELUP 239 #define K_MWHEELDOWN 240 typedef enum {key_game, key_console, key_message, key_menu, key_benchmark} keydest_t; extern keydest_t key_dest; extern char *keybindings[256]; extern int key_repeats[256]; extern int key_count; // incremented every key event extern int key_lastpress; void Key_Event (int key, bool down); void Key_Init (void); void Key_WriteBindings (FILE *f); void Key_SetBinding (int keynum, char *binding); void Key_ClearStates (void); void Key_SendText(char *strText); ================================================ FILE: source/mathlib.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // mathlib.c -- math primitives #include #include #include "quakedef.h" extern void Sys_Error(const char *error, ...); vec3_t vec3_origin = { 0,0,0 }; int nanmask = 255 << 23; /*-----------------------------------------------------------------*/ void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal) { float d; vec3_t n; float inv_denom; inv_denom = 1.0F / DotProduct(normal, normal); d = DotProduct(normal, p) * inv_denom; n[0] = normal[0] * inv_denom; n[1] = normal[1] * inv_denom; n[2] = normal[2] * inv_denom; dst[0] = p[0] - d * n[0]; dst[1] = p[1] - d * n[1]; dst[2] = p[2] - d * n[2]; } /* ** assumes "src" is normalized */ void PerpendicularVector(vec3_t dst, const vec3_t src) { int pos; int i; float minelem = 1.0F; vec3_t tempvec; /* ** find the smallest magnitude axially aligned vector */ for (pos = 0, i = 0; i < 3; i++) { float absrc = fabsf(src[i]); if (absrc < minelem) { pos = i; minelem = absrc; } } tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; tempvec[pos] = 1.0F; /* ** project the point onto the plane defined by src */ ProjectPointOnPlane(dst, tempvec, src); /* ** normalize the result */ VectorNormalize(dst); } void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees) { float m[3][3]; float im[3][3]; float zrot[3][3]; float tmpmat[3][3]; float rot[3][3]; int i; vec3_t vr, vup, vf; vf[0] = dir[0]; vf[1] = dir[1]; vf[2] = dir[2]; PerpendicularVector(vr, dir); CrossProduct(vr, vf, vup); m[0][0] = vr[0]; m[1][0] = vr[1]; m[2][0] = vr[2]; m[0][1] = vup[0]; m[1][1] = vup[1]; m[2][1] = vup[2]; m[0][2] = vf[0]; m[1][2] = vf[1]; m[2][2] = vf[2]; memcpy(im, m, sizeof(im)); im[0][1] = m[1][0]; im[0][2] = m[2][0]; im[1][0] = m[0][1]; im[1][2] = m[2][1]; im[2][0] = m[0][2]; im[2][1] = m[1][2]; memset(zrot, 0, sizeof(zrot)); zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; float cs[2]; sincosf_c(DEG2RAD(degrees), cs); zrot[0][0] = cs[1]; zrot[0][1] = cs[0]; zrot[1][0] = -cs[0]; zrot[1][1] = cs[1]; R_ConcatRotations(m, zrot, tmpmat); R_ConcatRotations(tmpmat, im, rot); for (i = 0; i < 3; i++) { dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; } } /*-----------------------------------------------------------------*/ float anglemod(float a) { a = (360.0 / 65536) * ((int)(a*(65536 / 360.0)) & 65535); return a; } /* ================== BOPS_Error Split out like this for ASM to call. ================== */ void BOPS_Error(void) { Sys_Error("BoxOnPlaneSide: Bad signbits"); } #if !id386 /* ================== BoxOnPlaneSide Returns 1, 2, or 1 + 2 ================== */ int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, mplane_t *p) { float dist1, dist2; int sides; // general case switch (p->signbits) { case 0: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; break; case 1: dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; break; case 2: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; break; case 3: dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; break; case 4: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; break; case 5: dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; break; case 6: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; break; case 7: dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; break; default: dist1 = dist2 = 0; // shut up compiler BOPS_Error(); break; } sides = 0; if (dist1 >= p->dist) sides = 1; if (dist2 < p->dist) sides |= 2; #ifdef PARANOID if (sides == 0) Sys_Error("BoxOnPlaneSide: sides==0"); #endif return sides; } #endif void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float sr, sp, sy, cr, cp, cy; v4sf src = { angles[YAW] * (M_PI * 2 / 360), angles[PITCH] * (M_PI * 2 / 360), angles[ROLL] * (M_PI * 2 / 360), 0 }; v4sf sins, coss; sincos_ps(src, &sins, &coss); sy = sins[0]; cy = coss[0]; sp = sins[1]; cp = coss[1]; sr = sins[2]; cr = coss[2]; forward[0] = cp*cy; forward[1] = cp*sy; forward[2] = -sp; right[0] = (-1 * sr*sp*cy + -1 * cr*-sy); right[1] = (-1 * sr*sp*sy + -1 * cr*cy); right[2] = -1 * sr*cp; up[0] = (cr*sp*cy + -sr*-sy); up[1] = (cr*sp*sy + -sr*cy); up[2] = cr*cp; } int VectorCompare(vec3_t v1, vec3_t v2) { int i; for (i = 0; i<3; i++) if (v1[i] != v2[i]) return 0; return 1; } void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) { vecc[0] = veca[0] + scale*vecb[0]; vecc[1] = veca[1] + scale*vecb[1]; vecc[2] = veca[2] + scale*vecb[2]; } vec_t _DotProduct(vec3_t v1, vec3_t v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } void _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out) { out[0] = veca[0] - vecb[0]; out[1] = veca[1] - vecb[1]; out[2] = veca[2] - vecb[2]; } void _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out) { out[0] = veca[0] + vecb[0]; out[1] = veca[1] + vecb[1]; out[2] = veca[2] + vecb[2]; } void _VectorCopy(vec3_t in, vec3_t out) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; } void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross) { cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; } double sqrt(double x); vec_t Length(vec3_t v) { int i; float length; length = 0; for (i = 0; i< 3; i++) length += v[i] * v[i]; length = sqrtf(length); // FIXME return length; } float VectorLength(vec3_t v) { float length; length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; return sqrtf(length); } float VectorNormalize(vec3_t v) { float length, ilength; length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; length = sqrt(length); // FIXME if (length) { ilength = 1 / length; v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; } return length; } void VectorInverse(vec3_t v) { v[0] = -v[0]; v[1] = -v[1]; v[2] = -v[2]; } void VectorScale(vec3_t in, vec_t scale, vec3_t out) { out[0] = in[0] * scale; out[1] = in[1] * scale; out[2] = in[2] * scale; } int Q_log2(int val) { int answer = 0; while (val >>= 1) answer++; return answer; } /* ================ R_ConcatRotations ================ */ void R_ConcatRotations(float in1[3][3], float in2[3][3], float out[3][3]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; } /* ================ R_ConcatTransforms ================ */ void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]) { out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + in1[0][2] * in2[2][0]; out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + in1[0][2] * in2[2][1]; out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + in1[0][2] * in2[2][2]; out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] + in1[0][2] * in2[2][3] + in1[0][3]; out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + in1[1][2] * in2[2][0]; out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + in1[1][2] * in2[2][1]; out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + in1[1][2] * in2[2][2]; out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] + in1[1][2] * in2[2][3] + in1[1][3]; out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + in1[2][2] * in2[2][0]; out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + in1[2][2] * in2[2][1]; out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + in1[2][2] * in2[2][2]; out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] + in1[2][2] * in2[2][3] + in1[2][3]; } /* =================== FloorDivMod Returns mathematically correct (floor-based) quotient and remainder for numer and denom, both of which should contain no fractional part. The quotient must fit in 32 bits. ==================== */ void FloorDivMod(double numer, double denom, int *quotient, int *rem) { int q, r; double x; #ifndef PARANOID if (denom <= 0.0) Sys_Error("FloorDivMod: bad denominator %d\n", denom); // if ((floor(numer) != numer) || (floor(denom) != denom)) // Sys_Error ("FloorDivMod: non-integer numer or denom %f %f\n", // numer, denom); #endif if (numer >= 0.0) { x = floor(numer / denom); q = (int)x; r = (int)floor(numer - (x * denom)); } else { // // perform operations with positive values, and fix mod to make floor-based // x = floor(-numer / denom); q = -(int)x; r = (int)floor(-numer - (x * denom)); if (r != 0) { q--; r = (int)denom - r; } } *quotient = q; *rem = r; } /* =================== GreatestCommonDivisor ==================== */ int GreatestCommonDivisor(int i1, int i2) { if (i1 > i2) { if (i2 == 0) return (i1); return GreatestCommonDivisor(i2, i1 % i2); } else { if (i1 == 0) return (i2); return GreatestCommonDivisor(i1, i2 % i1); } } #if !id386 // TODO: move to nonintel.c /* =================== Invert24To16 Inverts an 8.24 value to a 16.16 value ==================== */ fixed16_t Invert24To16(fixed16_t val) { if (val < 256) return (0xFFFFFFFF); return (fixed16_t) (((double)0x10000 * (double)0x1000000 / (double)val) + 0.5); } #endif int ParseFloats(const signed char *s, float *f, int *f_size) { int i, argc; if (!s || !f || !f_size) Sys_Error("ParseFloats() wrong params"); if (f_size[0] <= 0) return (f_size[0] = 0); // array have no size, unusual but no crime Cmd_TokenizeString(s); argc = min(Cmd_Argc(), f_size[0]); for (i = 0; i < argc; i++) f[i] = atof(Cmd_Argv(i)); for (; i < f_size[0]; i++) f[i] = 0; // zeroing unused elements return (f_size[0] = argc); } ================================================ FILE: source/mathlib.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // mathlib.h #include typedef float vec_t; typedef vec_t vec3_t[3]; typedef vec_t vec5_t[5]; typedef int fixed4_t; typedef int fixed8_t; typedef int fixed16_t; #define FIXED16_MAX INT_MAX; #define DEG2RAD(a) ((a * M_PI) / 180.0f) struct mplane_s; extern vec3_t vec3_origin; /// LordHavoc: this function never returns exactly MIN or exactly MAX, because /// of a QuakeC bug in id1 where the line /// self.nextthink = self.nexthink + random() * 0.5; /// can result in 0 (self.nextthink is 0 at this point in the code to begin /// with), causing "stone monsters" that never spawned properly, also MAX is /// avoided because some people use random() as an index into arrays or for /// loop conditions, where hitting exactly MAX may be a fatal error #define lhrandom(MIN,MAX) (((double)(rand() + 0.5) / ((double)RAND_MAX + 1)) * ((MAX)-(MIN)) + (MIN)) #define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) #define VectorSubtract(a,b,out) {out[0]=a[0]-b[0];out[1]=a[1]-b[1];out[2]=a[2]-b[2];} #define VectorAdd(a,b,out) {out[0]=a[0]+b[0];out[1]=a[1]+b[1];out[2]=a[2]+b[2];} #define VectorCopy(a,out) {out[0]=a[0];out[1]=a[1];out[2]=a[2];} #define VectorClear(a) ((a)[0] = (a)[1] = (a)[2] = 0) #define VectorNegate(a, out) ((out)[0] = -(a)[0], (out)[1] = -(a)[1], (out)[2] = -(a)[2]) #define VectorRandom(v) do{(v)[0] = lhrandom(-1, 1);(v)[1] = lhrandom(-1, 1);(v)[2] = lhrandom(-1, 1);}while(DotProduct(v, v) > 1) void VectorMA (vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); vec_t _DotProduct (vec3_t v1, vec3_t v2); void _VectorSubtract (vec3_t veca, vec3_t vecb, vec3_t out); void _VectorAdd (vec3_t veca, vec3_t vecb, vec3_t out); void _VectorCopy (vec3_t in, vec3_t out); int VectorCompare (vec3_t v1, vec3_t v2); vec_t Length (vec3_t v); void CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); float VectorNormalize (vec3_t v); // returns vector length void VectorInverse (vec3_t v); void VectorScale (vec3_t in, vec_t scale, vec3_t out); int Q_log2(int val); void R_ConcatRotations (float in1[3][3], float in2[3][3], float out[3][3]); void R_ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4]); void FloorDivMod (double numer, double denom, int *quotient, int *rem); fixed16_t Invert24To16(fixed16_t val); int GreatestCommonDivisor (int i1, int i2); void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); int BoxOnPlaneSide (vec3_t emins, vec3_t emaxs, struct mplane_s *plane); float anglemod(float a); #define BOX_ON_PLANE_SIDE(emins, emaxs, p) \ (((p)->type < 3)? \ ( \ ((p)->dist <= (emins)[(p)->type])? \ 1 \ : \ ( \ ((p)->dist >= (emaxs)[(p)->type])?\ 2 \ : \ 3 \ ) \ ) \ : \ BoxOnPlaneSide( (emins), (emaxs), (p))) int ParseFloats(const signed char *s, float *f, int *f_size); ================================================ FILE: source/menu.c ================================================ /* Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2020 Asakura Reiko 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "quakedef.h" #include "net_dgrm.h" char res_string[256]; CVAR (vid_vsync, 1, CVAR_ARCHIVE) extern cvar_t fov; extern cvar_t crosshair; extern cvar_t invert_camera; extern cvar_t pstv_rumble; extern cvar_t retrotouch; extern cvar_t scr_sbaralpha; extern cvar_t motioncam; extern cvar_t motion_horizontal_sensitivity; extern cvar_t motion_vertical_sensitivity; extern cvar_t gl_torchflares; extern cvar_t show_fps; extern cvar_t gl_fog; extern cvar_t gl_outline; extern cvar_t r_viewmodeloffset; extern cvar_t st_separation; extern int scr_width; extern int scr_height; extern uint8_t is_uma0; int cfg_width; int cfg_height; extern cvar_t gl_bilinear; int m_state = m_none; extern ModsList* mods; extern int max_mod_idx; void (*vid_menudrawfn)(void); void (*vid_menukeyfn)(int key); void M_Menu_Main_f (void); void M_Menu_SinglePlayer_f (void); void M_Menu_Load_f (void); void M_Menu_Save_f (void); void M_Menu_MultiPlayer_f (void); void M_Menu_Setup_f (void); void M_Menu_Net_f (void); void M_Menu_OnlineServerList_f (void); void M_Menu_Options_f (void); void M_Menu_Keys_f (void); void M_Menu_Video_f (void); void M_Menu_Help_f (void); void M_Menu_Quit_f (void); void M_Menu_LanConfig_f (void); void M_Menu_GameOptions_f (void); void M_Menu_Search_f (void); void M_Menu_ServerList_f (void); void M_Main_Draw (void); void M_SinglePlayer_Draw (void); void M_Load_Draw (void); void M_Save_Draw (void); void M_MultiPlayer_Draw (void); void M_Setup_Draw (void); void M_Net_Draw (void); void M_OnlineServerList_Draw (void); void M_Options_Draw (void); void M_Keys_Draw (void); void M_Graphics_Draw (void); void M_Video_Draw (void); void M_Mods_Draw (void); void M_Help_Draw (void); void M_Quit_Draw (void); void M_LanConfig_Draw (void); void M_GameOptions_Draw (void); void M_Search_Draw (void); void M_ServerList_Draw (void); void M_Main_Key (int key); void M_SinglePlayer_Key (int key); void M_Load_Key (int key); void M_Save_Key (int key); void M_MultiPlayer_Key (int key); void M_Setup_Key (int key); void M_Net_Key (int key); void M_OnlineServerList_Key (int key); void M_Options_Key (int key); void M_Keys_Key (int key); void M_Graphics_Key (int key); void M_Video_Key (int key); void M_Mods_key (int key); void M_Help_Key (int key); void M_Quit_Key (int key); void M_LanConfig_Key (int key); void M_GameOptions_Key (int key); void M_Search_Key (int key); void M_ServerList_Key (int key); bool m_entersound; // play after drawing a frame, so caching // won't disrupt the sound bool m_recursiveDraw; int m_return_state; bool m_return_onerror; char m_return_reason [32]; #define StartingGame (m_multiplayer_cursor == 1) #define JoiningGame (m_multiplayer_cursor == 0) #define SerialConfig (m_net_cursor == 0) #define DirectConfig (m_net_cursor == 1) #define IPXConfig (m_net_cursor == 2) #define TCPIPConfig (m_net_cursor == 3) void M_ConfigureNetSubsystem(void); int antialiasing = 2; uint8_t netcheck_dialog_running = 0; void SetResolution(int w, int h){ char res_str[64]; FILE *f = NULL; if (is_uma0) f = fopen("uma0:data/Quake/resolution.cfg", "wb"); else f = fopen("ux0:data/Quake/resolution.cfg", "wb"); sprintf(res_str, "%dx%d", w, h); fwrite(res_str, 1, strlen(res_str), f); fclose(f); cfg_width = w; cfg_height = h; } void SetAntiAliasing(int m){ char res_str[64]; FILE *f = NULL; if (is_uma0) f = fopen("uma0:data/Quake/antialiasing.cfg", "wb"); else f = fopen("ux0:data/Quake/antialiasing.cfg", "wb"); sprintf(res_str, "%d", m); fwrite(res_str, 1, strlen(res_str), f); fclose(f); } void M_DrawColorBar (int x, int y, int highlight) { int i; int intense = highlight * 16 + (highlight < 8 ? 11 : 4); for (i = 0; i < 14; i++) { // take the approximate midpoint colour (handle backward ranges) int c = i * 16 + (i < 8 ? 8 : 7); // braw baseline colour (offset downwards a little so that it fits correctly Draw_Fill (x + i * 8, y + 4, 8, 8, c); } // draw the highlight rectangle Draw_Fill (x - 1 + highlight * 8, y + 3, 10, 10, 15); // redraw the highlighted color at brighter intensity Draw_Fill (x + highlight * 8, y + 4, 8, 8, intense); } /* ================ M_DrawCharacter Draws one solid graphics character ================ */ void M_DrawCharacter (int cx, int line, int num) { Batch_Character ( cx, line, num); } void M_Print (int cx, int cy, char *str) { Batch_String(cx, cy, str, 128); } void M_PrintCentered (int cy, char *str) { int cx = 160 - strlen(str) * 4; Batch_String(cx, cy, str, 128); } void M_PrintWhite (int cx, int cy, char *str) { Batch_String(cx, cy, str, 0); } void M_DrawTransPic (int x, int y, qpic_t *pic) { Draw_TransPic (x, y, pic); } void M_DrawPic (int x, int y, qpic_t *pic) { Draw_Pic (x, y, pic); } byte identityTable[256]; byte translationTable[256]; void M_BuildTranslationTable(int top, int bottom) { int j; byte *dest, *source; for (j = 0; j < 256; j++) identityTable[j] = j; dest = translationTable; source = identityTable; memcpy (dest, source, 256); if (top < 128) // the artists made some backwards ranges. sigh. memcpy (dest + TOP_RANGE, source + top, 16); else for (j=0 ; j<16 ; j++) dest[TOP_RANGE+j] = source[top+15-j]; if (bottom < 128) memcpy (dest + BOTTOM_RANGE, source + bottom, 16); else for (j=0 ; j<16 ; j++) dest[BOTTOM_RANGE+j] = source[bottom+15-j]; } void M_DrawTransPicTranslate (int x, int y, qpic_t *pic) { Draw_TransPicTranslate (x, y, pic, translationTable); } void M_DrawTextBox (int x, int y, int width, int lines) { qpic_t *p; int cx, cy; int n; // draw left side cx = x; cy = y; p = Draw_CachePic ("gfx/box_tl.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_ml.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bl.lmp"); M_DrawTransPic (cx, cy+8, p); // draw middle cx += 8; while (width > 0) { cy = y; p = Draw_CachePic ("gfx/box_tm.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mm.lmp"); for (n = 0; n < lines; n++) { cy += 8; if (n == 1) p = Draw_CachePic ("gfx/box_mm2.lmp"); M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_bm.lmp"); M_DrawTransPic (cx, cy+8, p); width -= 2; cx += 16; } // draw right side cy = y; p = Draw_CachePic ("gfx/box_tr.lmp"); M_DrawTransPic (cx, cy, p); p = Draw_CachePic ("gfx/box_mr.lmp"); for (n = 0; n < lines; n++) { cy += 8; M_DrawTransPic (cx, cy, p); } p = Draw_CachePic ("gfx/box_br.lmp"); M_DrawTransPic (cx, cy+8, p); } //============================================================================= int m_save_demonum; /* ================ M_ToggleMenu_f ================ */ void M_ToggleMenu_f (void) { m_entersound = true; if (key_dest == key_menu) { if (m_state != m_main) { M_Menu_Main_f (); return; } key_dest = key_game; m_state = m_none; return; } if (key_dest == key_console) { Con_ToggleConsole_f (); } else { M_Menu_Main_f (); } } //============================================================================= /* MAIN MENU */ int m_main_cursor; #define MAIN_ITEMS 5 void M_Menu_Main_f (void) { if (key_dest != key_menu) { m_save_demonum = cls.demonum; cls.demonum = -1; } key_dest = key_menu; m_state = m_main; m_entersound = true; } void M_Main_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/ttl_main.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp") ); f = (int)(host_time * 10)%6; M_DrawTransPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); M_PrintCentered (190, " Thanks for the awesome support "); M_PrintCentered (198, " on Patreon to: "); M_PrintCentered (206, " Tain Sueiras, polytoad "); M_PrintCentered (214, " drd7of14, The Vita3K Project "); Draw_Batched(); } void M_Main_Key (int key) { switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: key_dest = key_game; m_state = m_none; cls.demonum = m_save_demonum; if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected) CL_NextDemo (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_main_cursor >= MAIN_ITEMS) m_main_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_main_cursor < 0) m_main_cursor = MAIN_ITEMS - 1; break; case K_CIRCLE: case K_CROSS: m_entersound = true; switch (m_main_cursor) { case 0: M_Menu_SinglePlayer_f (); break; case 1: M_Menu_MultiPlayer_f (); break; case 2: M_Menu_Options_f (); break; case 3: M_Menu_Help_f (); break; case 4: M_Menu_Quit_f (); break; } } } //============================================================================= /* SINGLE PLAYER MENU */ int m_singleplayer_cursor; #define SINGLEPLAYER_ITEMS 3 void M_Menu_SinglePlayer_f (void) { key_dest = key_menu; m_state = m_singleplayer; m_entersound = true; } void M_SinglePlayer_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/ttl_sgl.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTransPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") ); f = (int)(host_time * 10)%6; M_DrawTransPic (54, 32 + m_singleplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); } void M_SinglePlayer_Key (int key) { switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS) m_singleplayer_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_singleplayer_cursor < 0) m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1; break; case K_CROSS: // Cross case K_CIRCLE: // Circle m_entersound = true; switch (m_singleplayer_cursor) { case 0: key_dest = key_game; Cbuf_AddText ("disconnect\n"); // Ch0wW: Disconnect all the time to reset original NetQuake behaviour. Cbuf_AddText ("maxplayers 1\n"); Cbuf_AddText ("map start\n"); break; case 1: M_Menu_Load_f (); break; case 2: M_Menu_Save_f (); break; } } } //============================================================================= /* LOAD/SAVE MENU */ int load_cursor; // 0 < load_cursor < MAX_SAVEGAMES #define MAX_SAVEGAMES 12 char m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1]; int loadable[MAX_SAVEGAMES]; void M_ScanSaves (void) { int i, j; char name[MAX_OSPATH]; FILE *f; int version; for (i=0 ; iwidth)/2, 4, p); for (i=0 ; i< MAX_SAVEGAMES; i++) M_Print (16, 32 + 8*i, m_filenames[i]); // line cursor M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1)); Draw_Batched(); } void M_Save_Draw (void) { int i; qpic_t *p; p = Draw_CachePic ("gfx/p_save.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); for (i=0 ; i= MAX_SAVEGAMES) load_cursor = 0; break; } } void M_Save_Key (int k) { switch (k) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_SinglePlayer_f (); break; case K_CIRCLE: case K_CROSS: m_state = m_none; key_dest = key_game; Cbuf_AddText (va("save s%i\n", load_cursor)); return; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("misc/menu1.wav"); load_cursor--; if (load_cursor < 0) load_cursor = MAX_SAVEGAMES-1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); load_cursor++; if (load_cursor >= MAX_SAVEGAMES) load_cursor = 0; break; } } //============================================================================= /* MULTIPLAYER MENU */ int m_multiplayer_cursor; #define MULTIPLAYER_ITEMS 3 void M_Menu_MultiPlayer_f (void) { key_dest = key_menu; m_state = m_multiplayer; m_entersound = true; } void M_MultiPlayer_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTransPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") ); f = (int)(host_time * 10)%6; M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); if (tcpipAvailable) return; Draw_String ((320/2) - ((27*8)/2), 148, "No Communications Available", 0); } void M_MultiPlayer_Key (int key) { switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Main_f (); break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) m_multiplayer_cursor = 0; break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); if (--m_multiplayer_cursor < 0) m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; break; case K_CIRCLE: // Circle case K_CROSS: // Cross m_entersound = true; switch (m_multiplayer_cursor) { case 0: if (tcpipAvailable) M_Menu_Net_f (); break; case 1: if (tcpipAvailable) M_Menu_Net_f (); break; case 2: M_Menu_Setup_f (); break; } } } //============================================================================= /* BENCHMARK MENU */ void M_Menu_Benchmark_f (void) { key_dest = key_menu; m_state = m_benchmark; m_entersound = true; } extern int max_fps; extern int min_fps; extern int average_fps; void M_Benchmark_Draw (void) { char s[80],s1[80],s2[80]; sprintf(s, " Max FPS: %3d", max_fps); sprintf(s1, " Min FPS: %3d", min_fps); sprintf(s2, "Average FPS: %3d", average_fps); M_Print(30, 20, "Benchmark results"); M_Print (64, 40, s); M_Print (64, 48, s1); M_Print (64, 56, s2); Draw_Batched(); } void M_Benchmark_Key (int key) { switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: case K_CIRCLE: // Circle case K_CROSS: // Cross M_Menu_Options_f (); break; } } //============================================================================= /* SETUP MENU */ int setup_cursor = 4; int setup_cursor_table[] = {40, 56, 80, 104, 140}; char setup_hostname[16]; char setup_myname[16]; int setup_oldtop; int setup_oldbottom; int setup_top; int setup_bottom; #define NUM_SETUP_CMDS 5 void M_Menu_Setup_f (void) { key_dest = key_menu; m_state = m_setup; m_entersound = true; strcpy(setup_myname, cl_name.string); strcpy(setup_hostname, hostname.string); setup_top = setup_oldtop = ((int)cl_color.value) >> 4; setup_bottom = setup_oldbottom = ((int)cl_color.value) & 15; } void M_Setup_Draw (void) { qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTextBox (160, 32, 16, 1); M_DrawTextBox (160, 48, 16, 1); M_DrawColorBar (64, 88, setup_top); M_DrawColorBar (64, 112, setup_bottom); M_DrawTextBox (64, 140-8, 14, 1); p = Draw_CachePic ("gfx/bigbox.lmp"); M_DrawTransPic (176, 64, p); p = Draw_CachePic ("gfx/menuplyr.lmp"); M_BuildTranslationTable(setup_top*16, setup_bottom*16); M_DrawTransPicTranslate (188, 72, p); M_Print (64, 40, "Hostname"); M_Print (168, 40, setup_hostname); M_Print (64, 56, "Your name"); M_Print (168, 56, setup_myname); M_Print (64, 80, "Shirt color"); M_Print (64, 104, "Pants color"); M_Print (72, 140, "Accept Changes"); M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1)); if (setup_cursor == 0) M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); if (setup_cursor == 1) M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1)); Draw_Batched(); } void M_Setup_Key (int k) { int l; switch (k) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_MultiPlayer_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); setup_cursor--; if (setup_cursor < 0) setup_cursor = NUM_SETUP_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); setup_cursor++; if (setup_cursor >= NUM_SETUP_CMDS) setup_cursor = 0; break; case K_LEFTARROW: if (setup_cursor < 2) return; S_LocalSound ("misc/menu3.wav"); if (setup_cursor == 2) setup_top = setup_top - 1; if (setup_cursor == 3) setup_bottom = setup_bottom - 1; break; case K_RIGHTARROW: if (setup_cursor < 2) return; forward: S_LocalSound ("misc/menu3.wav"); if (setup_cursor == 2) setup_top = setup_top + 1; if (setup_cursor == 3) setup_bottom = setup_bottom + 1; break; case K_CIRCLE: // Circle case K_CROSS: // Cross if (setup_cursor == 0 || setup_cursor == 1) return; if (setup_cursor == 2 || setup_cursor == 3) goto forward; // setup_cursor == 4 (OK) if (strcmp(cl_name.string, setup_myname) != 0) Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) ); if (strcmp(hostname.string, setup_hostname) != 0) Cvar_Set("hostname", setup_hostname); if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom) Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) ); m_entersound = true; M_Menu_MultiPlayer_f (); break; case K_BACKSPACE: if (setup_cursor == 0) { if (strlen(setup_hostname)) setup_hostname[strlen(setup_hostname)-1] = 0; } if (setup_cursor == 1) { if (strlen(setup_myname)) setup_myname[strlen(setup_myname)-1] = 0; } break; default: if (k < 32 || k > 127) break; if (setup_cursor == 0) { l = strlen(setup_hostname); if (l < 15) { setup_hostname[l+1] = 0; setup_hostname[l] = k; } } if (setup_cursor == 1) { l = strlen(setup_myname); if (l < 15) { setup_myname[l+1] = 0; setup_myname[l] = k; } } } if (setup_top > 13) setup_top = 0; if (setup_top < 0) setup_top = 13; if (setup_bottom > 13) setup_bottom = 0; if (setup_bottom < 0) setup_bottom = 13; } //============================================================================= /* NET MENU */ int m_net_cursor; int m_net_items; int m_net_saveHeight; char *net_helpMessage [] = { /* .........1.........2.... */ " Commonly used to play ", " over the Internet, but ", " also used on a Local ", " Area Network. " }; void M_Menu_Net_f (void) { key_dest = key_menu; m_state = m_net; m_entersound = true; m_net_items = 1; if (m_net_cursor >= m_net_items) m_net_cursor = 0; m_net_cursor--; M_Net_Key (K_DOWNARROW); } void M_Net_Draw (void) { int f; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); f = 32; if (tcpipAvailable) p = Draw_CachePic ("gfx/netmen4.lmp"); else p = Draw_CachePic ("gfx/dim_tcp.lmp"); M_DrawTransPic (72, f, p); f = (320-26*8)/2; M_DrawTextBox (f, 134, 24, 4); f += 8; Draw_String (f, 166, net_helpMessage[m_net_cursor*4+0], 128); f = (int)(host_time * 10)%6; M_DrawTransPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); } void M_Net_Key (int k) { M_Menu_LanConfig_f(); } //============================================================================= /* MODS MENU */ int mods_cursor; void M_Menu_Mods_f (void) { key_dest = key_menu; m_state = m_mods; m_entersound = true; } ModsList *cur = NULL; void M_Mods_Draw (void) { float r; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); M_Print (60, 10, "Select the mod to load"); ModsList *ptr = mods; int j = 0; while (ptr != NULL) { M_Print (60, 32 + j * 8, ptr->name); if (j == mods_cursor) cur = ptr; ptr = ptr->next; j++; } // cursor M_DrawCharacter (50, 32 + mods_cursor*8, 12+((int)(realtime*4)&1)); Draw_Batched(); } void M_Mods_Key (int k) { switch (k) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Options_f (); break; case K_CIRCLE: case K_CROSS: m_entersound = true; char cmd[128]; sprintf(cmd, "game %s\n", cur->name); Cbuf_AddText (cmd); M_Menu_Main_f (); return; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); mods_cursor--; if (mods_cursor < 0) mods_cursor = max_mod_idx; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); mods_cursor++; if (mods_cursor > max_mod_idx) mods_cursor = 0; break; } if (mods_cursor > max_mod_idx) { if (k == K_UPARROW) mods_cursor = max_mod_idx; else mods_cursor = 0; } } //============================================================================= /* GRAPHICS MENU */ # define GRAPHICS_ITEMS 14 #define SLIDER_RANGE 10 int graphics_cursor; #define N_RES 7 static const int res[N_RES][2] = { {480, 272}, {640, 368}, {720, 408}, {960, 544}, {1280, 720}, {1440, 1080}, {1920, 1080}, }; int r_idx = -1; void M_Menu_Graphics_f (void) { key_dest = key_menu; m_state = m_graphics; m_entersound = true; } void M_AdjustSliders2 (int dir) { S_LocalSound ("misc/menu3.wav"); switch (graphics_cursor) { case 0: // bilinear filtering Cvar_ToggleValue(&gl_bilinear); break; case 1: // gamma v_gamma.value -= dir * 0.05; if (v_gamma.value < 0.5) v_gamma.value = 0.5; if (v_gamma.value > 1) v_gamma.value = 1; Cvar_SetValue ("v_gamma", v_gamma.value); break; case 2: // overbright gl_overbright.value += dir; if (gl_overbright.value < 0) gl_overbright.value = 0; if (gl_overbright.value > 2) gl_overbright.value = 2; Cvar_SetValue ("gl_overbright", gl_overbright.value); break; case 3: // hud transparency scr_sbaralpha.value += dir * 0.1; if (scr_sbaralpha.value < 0) scr_sbaralpha.value = 0; if (scr_sbaralpha.value > 1) scr_sbaralpha.value = 1; Cvar_SetValue ("scr_sbaralpha", scr_sbaralpha.value); break; case 4: // mirrors opacity r_mirroralpha.value += dir * 0.1; if (r_mirroralpha.value < 0) r_mirroralpha.value = 0; if (r_mirroralpha.value > 1) r_mirroralpha.value = 1; Cvar_SetValue ("r_mirroralpha", r_mirroralpha.value); break; case 5: // water opacity r_wateralpha.value += dir * 0.1; if (r_wateralpha.value < 0) r_wateralpha.value = 0; if (r_wateralpha.value > 1) r_wateralpha.value = 1; Cvar_SetValue ("r_wateralpha", r_wateralpha.value); break; case 6: // dynamic torchflares Cvar_ToggleValue(&gl_torchflares); break; case 7: // dynamic shadows Cvar_ToggleValue(&r_shadows); break; case 8: // Fog if (gl_fog.value) Cvar_SetValue ("r_fullbright", 0); else Cvar_SetValue ("r_fullbright", 1); Cvar_ToggleValue (&gl_fog); break; case 9: // cel shading gl_outline.value += dir; if (gl_outline.value > 6) gl_outline.value = 6; else if (gl_outline.value < 0) gl_outline.value = 0; Cvar_SetValue ("gl_outline",gl_outline.value); break; case 10: // anaglyph 3d st_separation.value = st_separation.value == 0.5 ? 0.0 : 0.5; Cvar_SetValue ("st_separation",st_separation.value); break; case 11: // antialiasing antialiasing += dir; if (antialiasing < 0) antialiasing = 8; else if (antialiasing > 8) antialiasing = 0; SetAntiAliasing(antialiasing); break; case 12: // resolution if (r_idx == -1) { for (r_idx = 0; r_idx < N_RES; r_idx++) { if (cfg_width == res[r_idx][0]) break; } } r_idx = (r_idx + dir + N_RES) % N_RES; SetResolution(res[r_idx][0], res[r_idx][1]); break; case 13: Cvar_SetValue ("vid_vsync", !vid_vsync.value); break; case 14: // performance test key_dest = key_benchmark; m_state = m_none; cls.demonum = m_save_demonum; Cbuf_AddText("benchmark demo1\n"); break; default: break; } } void M_DrawSlider (int x, int y, float range) { int i; if (range < 0) range = 0; if (range > 1) range = 1; M_DrawCharacter (x-8, y, 128); for (i=0 ; iwidth)/2, 4, p); M_Print (16, 32, " Bilinear Filtering"); M_DrawCheckbox (220, 32, gl_bilinear.value); M_Print (16, 40, " Brightness"); r = (1.0 - v_gamma.value) / 0.5; M_DrawSlider (220, 40, r); M_Print (16, 48, " Light Overbright"); r = gl_overbright.value / 2; M_DrawSlider (220, 48, r); M_Print (16, 56, " HUD Transparency"); M_DrawSlider (220, 56, scr_sbaralpha.value); M_Print (16, 64, " Mirrors Opacity"); r = r_mirroralpha.value; M_DrawSlider (220, 64, r); M_Print (16, 72, " Water Opacity"); r = r_wateralpha.value; M_DrawSlider (220, 72, r); M_Print (16, 80, " Dynamic Lights"); M_DrawCheckbox (220, 80, gl_torchflares.value); M_Print (16, 88, " Dynamic Shadows"); M_DrawCheckbox (220, 88, r_shadows.value); M_Print (16, 96, " Fog Rendering"); M_DrawCheckbox (220, 96, gl_fog.value); M_Print (16, 104, " Cel Shading"); r = gl_outline.value / 6; M_DrawSlider (220, 104, r); M_Print (16, 112," Anaglyph 3D"); M_DrawCheckbox (220, 112, st_separation.value != 0); M_Print (16, 120," Anti-Aliasing"); switch (antialiasing) { case 1: M_Print (220, 120, "MSAA 2x"); break; case 2: M_Print (220, 120, "MSAA 4x"); break; case 3: M_Print (220, 120, "SSAA 2x"); break; case 4: M_Print (220, 120, "SSAA 4x"); break; case 5: M_Print (220, 120, "MSAA 2x + SSAA 2x"); break; case 6: M_Print (220, 120, "MSAA 2x + SSAA 4x"); break; case 7: M_Print (220, 120, "MSAA 4x + SSAA 2x"); break; case 8: M_Print (220, 120, "MSAA 4x + SSAA 4x"); break; default: M_Print (220, 120, "Disabled"); break; } char res_str[64]; sprintf(res_str, "%dx%d", cfg_width, cfg_height); M_Print (16, 128," Resolution"); M_Print (220, 128, res_str); M_Print (16, 136," V-Sync"); M_DrawCheckbox (220, 136, vid_vsync.value); M_Print (16, 152," Test Performance"); // Warn users for reboot required if (graphics_cursor == 11 || graphics_cursor == 12) { M_PrintCentered (210, "Editing this option will require"); M_PrintCentered (218, " an app reboot to take effect "); } // cursor if (graphics_cursor == GRAPHICS_ITEMS) M_DrawCharacter (200, 152, 12+((int)(realtime*4)&1)); else M_DrawCharacter (200, 32 + graphics_cursor*8, 12+((int)(realtime*4)&1)); Draw_Batched(); } void M_Graphics_Key (int k) { switch (k) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Options_f (); break; case K_CIRCLE: case K_CROSS: m_entersound = true; M_AdjustSliders2 (1); return; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); graphics_cursor--; if (graphics_cursor < 0) graphics_cursor = GRAPHICS_ITEMS; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); graphics_cursor++; if (graphics_cursor > GRAPHICS_ITEMS) graphics_cursor = 0; break; case K_LEFTARROW: M_AdjustSliders2 (-1); break; case K_RIGHTARROW: M_AdjustSliders2 (1); break; } } //============================================================================= /* OPTIONS MENU */ #define OPTIONS_ITEMS 20 int options_cursor; void M_Menu_Options_f (void) { key_dest = key_menu; m_state = m_options; m_entersound = true; } char *w_pos[] = {"Center", "Right", "Left", "Hidden"}; int w_pos_idx = -1; void M_AdjustSliders (int dir) { S_LocalSound ("misc/menu3.wav"); switch (options_cursor) { case 5: // screen size viewsize.value += dir * 10; if (viewsize.value < 30) viewsize.value = 30; if (viewsize.value > 120) viewsize.value = 120; Cvar_SetValue ("viewsize", viewsize.value); break; case 6: // camera sensitivity sensitivity.value += dir * 0.5; if (sensitivity.value < 1) sensitivity.value = 1; if (sensitivity.value > 11) sensitivity.value = 11; Cvar_SetValue ("sensitivity", sensitivity.value); break; case 7: // invert camera Cvar_ToggleValue (&invert_camera); break; case 8: // music volume bgmvolume.value += dir * 0.1; if (bgmvolume.value < 0) bgmvolume.value = 0; if (bgmvolume.value > 1) bgmvolume.value = 1; Cvar_SetValue ("bgmvolume", bgmvolume.value); break; case 9: // sfx volume volume.value += dir * 0.1; if (volume.value < 0) volume.value = 0; if (volume.value > 1) volume.value = 1; Cvar_SetValue ("volume", volume.value); break; case 10: // retrotouch Cvar_SetValue ("retrotouch", !retrotouch.value); break; case 11: // motion camera Cvar_SetValue ("motioncam", !motioncam.value); break; case 12: // motion camera sensitivity horizontal motion_horizontal_sensitivity.value += dir * 0.5; if (motion_horizontal_sensitivity.value < 0) motion_horizontal_sensitivity.value = 0; if (motion_horizontal_sensitivity.value > 10) motion_horizontal_sensitivity.value = 10; Cvar_SetValue ("motion_horizontal_sensitivity", motion_horizontal_sensitivity.value); break; case 13: // motion camera sensitivity vertical motion_vertical_sensitivity.value += dir * 0.5; if (motion_vertical_sensitivity.value < 0) motion_vertical_sensitivity.value = 0; if (motion_vertical_sensitivity.value > 10) motion_vertical_sensitivity.value = 10; Cvar_SetValue ("motion_vertical_sensitivity", motion_vertical_sensitivity.value); break; case 14: // rumble Cvar_SetValue ("pstv_rumble", !pstv_rumble.value); break; case 15: // show fps Cvar_SetValue ("show_fps", !show_fps.value); break; case 16: // crosshair crosshair.value += dir; if (crosshair.value > 2) crosshair.value = 0; else if (crosshair.value < 0) crosshair.value = 2; Cvar_SetValue ("crosshair", crosshair.value); break; case 17: // show weapon w_pos_idx += dir; if (w_pos_idx > 3) w_pos_idx = 0; if (w_pos_idx < 0) w_pos_idx = 3; switch (w_pos_idx) { case 1: Cvar_SetValue ("r_drawviewmodel", 1); Cvar_SetValue ("r_viewmodeloffset", 8); break; case 2: Cvar_SetValue ("r_drawviewmodel", 1); Cvar_SetValue ("r_viewmodeloffset", -8); break; case 3: Cvar_SetValue ("r_drawviewmodel", 0); break; default: Cvar_SetValue ("r_drawviewmodel", 1); Cvar_SetValue ("r_viewmodeloffset", 0); break; } break; case 18: // field of view fov.value += dir * 5; if (fov.value > 130) fov.value = 130; else if (fov.value < 75) fov.value = 75; Cvar_SetValue ("fov",fov.value); break; case 19: // smooth animations Cvar_ToggleValue(&r_interpolate_model_animation); Cvar_ToggleValue(&r_interpolate_model_transform); break; case 20: // specular mode Cvar_SetValue ("gl_xflip", !gl_xflip.value); break; default: break; } } void M_Options_Draw (void) { float r; qpic_t *p; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_option.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_Print (16, 32, " Controls Settings"); M_Print (16, 40, " Graphics Settings"); M_Print (16, 48, " Open Console"); M_Print (16, 56, " Open Mods Menu"); M_Print (16, 64, " Reset to defaults"); M_Print (16, 72, " Screen size"); r = (viewsize.value - 30) / (120 - 30); M_DrawSlider (220, 72, r); M_Print (16, 80, " Camera Sensitivity"); r = (sensitivity.value - 1)/10; M_DrawSlider (220, 80, r); M_Print (16, 88, " Invert Camera"); M_DrawCheckbox (220, 88, invert_camera.value); M_Print (16, 96," Music Volume"); r = bgmvolume.value; M_DrawSlider (220, 96, r); M_Print (16, 104," Sound Volume"); r = volume.value; M_DrawSlider (220, 104, r); M_Print (16, 112," Use Retrotouch"); M_DrawCheckbox (220, 112, retrotouch.value); M_Print (16, 120," Use Gyroscope"); M_DrawCheckbox (220, 120, motioncam.value); M_Print (16, 128," Gyro X Sensitivity"); r = motion_horizontal_sensitivity.value/10; M_DrawSlider (220, 128, r); M_Print (16, 136," Gyro Y Sensitivity"); r = motion_vertical_sensitivity.value/10; M_DrawSlider (220, 136, r); M_Print (16, 144," Rumble Effect"); M_DrawCheckbox (220, 144, pstv_rumble.value); M_Print (16, 152," Show Framerate"); M_DrawCheckbox (220, 152, show_fps.value); M_Print (16, 160," Show Crosshair"); if (crosshair.value == 0) M_Print (220, 160, "Off"); else if (crosshair.value == 1) M_Print (220, 160, "Original"); else M_Print (220, 160, "Custom"); M_Print (16, 168," Weapon Position"); if (w_pos_idx == -1) { if (!r_drawviewmodel.value) w_pos_idx = 3; else if (r_viewmodeloffset.value < 0) w_pos_idx = 2; else if (r_viewmodeloffset.value > 0) w_pos_idx = 1; else w_pos_idx = 0; } M_Print (220, 168, w_pos[w_pos_idx]); M_Print (16, 176," Field of View"); r = (fov.value - 75) / 55; M_DrawSlider (220, 176, r); M_Print (16, 184," Smooth Animations"); M_DrawCheckbox (220, 184, r_interpolate_model_animation.value); M_Print (16, 192," Specular Mode"); M_DrawCheckbox (220, 192, gl_xflip.value); M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1)); Draw_Batched(); } void M_Options_Key (int k) { switch (k) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Main_f (); break; case K_CIRCLE: case K_CROSS: m_entersound = true; switch (options_cursor) { case 0: // Controls Settings M_Menu_Keys_f (); break; case 1: // Graphics Settings M_Menu_Graphics_f (); break; case 2: // Open Console m_state = m_none; Con_ToggleConsole_f (); break; case 3: // Open Mods Menu M_Menu_Mods_f (); break; case 4: // Reset to defaults Cbuf_AddText ("exec default.cfg\n"); IN_ResetInputs(); viewsize.value = 120; v_gamma.value = 1; sensitivity.value = 3; invert_camera.value = 0; bgmvolume.value = 1.0; volume.value = 0.7f; retrotouch.value = 0; pstv_rumble.value = 1.0f; show_fps.value = 0; r_drawviewmodel.value = 1; crosshair.value = 1; fov.value = 90; gl_fog.value = 0; gl_torchflares.value = 1; r_shadows.value = 1; r_interpolate_model_animation.value = 0; r_interpolate_model_transform.value = 0; r_mirroralpha.value = 0.8f; r_wateralpha.value = 1.0f; gl_xflip.value = 0; motioncam.value = 0; vid_vsync.value = 1; motion_horizontal_sensitivity.value = 3; motion_vertical_sensitivity.value = 3; scr_sbaralpha.value = 0.5f; gl_outline.value = 0; st_separation.value = 0; w_pos_idx = 0; r_viewmodeloffset.value = 0; gl_overbright.value = 0; Cvar_SetValue ("viewsize", viewsize.value); Cvar_SetValue ("v_gamma", v_gamma.value); Cvar_SetValue ("sensitivity", sensitivity.value); Cvar_SetValue ("invert_camera", invert_camera.value); Cvar_SetValue ("bgmvolume", bgmvolume.value); Cvar_SetValue ("volume", volume.value); Cvar_SetValue ("retrotouch", retrotouch.value); Cvar_SetValue ("pstv_rumble", pstv_rumble.value); Cvar_SetValue ("show_fps", show_fps.value); Cvar_SetValue ("r_drawviewmodel", r_drawviewmodel.value); Cvar_SetValue ("crosshair", crosshair.value); Cvar_SetValue ("fov", fov.value); Cvar_SetValue ("gl_fog", gl_fog.value); Cvar_SetValue ("gl_torchflares", gl_torchflares.value); Cvar_SetValue ("r_shadows", r_shadows.value); Cvar_SetValue ("r_interpolate_model_animation", r_interpolate_model_animation.value); Cvar_SetValue ("r_interpolate_model_transform", r_interpolate_model_transform.value); Cvar_SetValue ("r_mirroralpha", r_mirroralpha.value); Cvar_SetValue ("r_wateralpha", r_wateralpha.value); Cvar_SetValue ("gl_xflip", gl_xflip.value); Cvar_SetValue ("motioncam", motioncam.value); Cvar_SetValue ("motion_horizontal_sensitivity", motion_horizontal_sensitivity.value); Cvar_SetValue ("motion_vertical_sensitivity", motion_vertical_sensitivity.value); Cvar_SetValue ("scr_sbaralpha", scr_sbaralpha.value); Cvar_SetValue ("vid_vsync", vid_vsync.value); Cvar_SetValue ("gl_outline", gl_outline.value); Cvar_SetValue ("r_viewmodeloffset", r_viewmodeloffset.value); Cvar_SetValue ("st_separation",st_separation.value); Cvar_SetValue ("gl_overbright",gl_overbright.value); SetResolution(960, 544); antialiasing = 2; r_idx = -1; SetAntiAliasing(antialiasing); Cbuf_AddText ("gl_texturemode GL_LINEAR\n"); break; default: // All other settings M_AdjustSliders (1); break; } return; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); options_cursor--; if (options_cursor < 0) options_cursor = OPTIONS_ITEMS; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); options_cursor++; if (options_cursor > OPTIONS_ITEMS) options_cursor = 0; break; case K_LEFTARROW: M_AdjustSliders (-1); break; case K_RIGHTARROW: M_AdjustSliders (1); break; } } //============================================================================= /* KEYS MENU */ #define BIND_BINDABLE 0 #define BIND_SEPARATOR 1 char *bindnames[][2] = { {"+attack", "attack"}, {"impulse 10", "next weapon"}, {"impulse 12", "previous weapon" }, {"+jump", "jump / swim up"}, {"+forward", "move forward"}, {"+back", "move back"}, {"+moveleft", "move left" }, {"+moveright", "move right" }, {"+speed", "run" }, {"+moveup", "swim up" }, {"+movedown", "swim down" }, {"centerview", "center view"}, {"+left", "turn left"}, {"+right", "turn right"}, {"+strafe", "sidestep"}, {"+lookup", "look up"}, {"+lookdown", "look down"} }; #define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) int keys_cursor; int bind_grab; void M_Menu_Keys_f (void) { key_dest = key_menu; m_state = m_keys; m_entersound = true; } void M_FindKeysForCommand (char *command, int *twokeys) { int count; int j; int l; char *b; twokeys[0] = twokeys[1] = -1; l = strlen(command); count = 0; for (j=0 ; j<256 ; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) { twokeys[count] = j; count++; if (count == 2) break; } } } void M_UnbindCommand (char *command) { int j; int l; char *b; l = strlen(command); for (j=0 ; j<256 ; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) Key_SetBinding (j, ""); } } void M_Keys_Draw (void) { int i, l; int keys[2]; char *name; int x, y; qpic_t *p; p = Draw_CachePic ("gfx/ttl_cstm.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); if (bind_grab) M_Print (12, 32, "Press a key or button for this action"); else M_Print (18, 32, "Cross to change, Select to clear"); // search for known bindings for (i=0 ; i= NUMCOMMANDS) keys_cursor = 0; break; case K_CIRCLE: // go into bind mode case K_CROSS: // go into bind mode M_FindKeysForCommand (bindnames[keys_cursor][0], keys); S_LocalSound ("misc/menu2.wav"); if (keys[1] != -1) M_UnbindCommand (bindnames[keys_cursor][0]); bind_grab = true; break; case K_SELECT: // delete bindings S_LocalSound ("misc/menu2.wav"); M_UnbindCommand (bindnames[keys_cursor][0]); break; } } //============================================================================= /* VIDEO MENU */ void M_Menu_Video_f (void) { key_dest = key_menu; m_state = m_video; m_entersound = true; } void M_Video_Draw (void) { (*vid_menudrawfn) (); } void M_Video_Key (int key) { (*vid_menukeyfn) (key); } //============================================================================= /* HELP MENU */ int help_page; #define NUM_HELP_PAGES 6 void M_Menu_Help_f (void) { key_dest = key_menu; m_state = m_help; m_entersound = true; help_page = 0; } void M_Help_Draw (void) { M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) ); } void M_Help_Key (int key) { switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Main_f (); break; case K_UPARROW: case K_RIGHTARROW: m_entersound = true; if (++help_page >= NUM_HELP_PAGES) help_page = 0; break; case K_DOWNARROW: case K_LEFTARROW: m_entersound = true; if (--help_page < 0) help_page = NUM_HELP_PAGES-1; break; } } //============================================================================= /* QUIT MENU */ int msgNumber; int m_quit_prevstate; bool wasInMenus; #ifndef _WIN32 char *quitMessage [] = { /* .........1.........2.... */ " Are you gonna quit ", " this game just like ", " everything else? ", " ", " Milord, methinks that ", " thou art a lowly ", " quitter. Is this true? ", " ", " Do I need to bust your ", " face open for trying ", " to quit? ", " ", " Man, I oughta smack you", " for trying to quit! ", " Press X to get ", " smacked out. ", " What, you want to stop ", " playing VitaQuake? ", " Press X or O to ", " return to LiveArea. ", " Press X to quit like a ", " big loser in life. ", " Return back to stay ", " proud and successful! ", " If you press X to ", " quit, I will summon ", " Satan all over your ", " memory card! ", " Um, Asmodeus dislikes ", " his children trying to ", " quit. Press X to return", " to your Tinkertoys. ", " If you quit now, I'll ", " throw a blanket-party ", " for you next time! ", " " }; #endif void M_Menu_Quit_f (void) { if (m_state == m_quit) return; wasInMenus = (key_dest == key_menu); key_dest = key_menu; m_quit_prevstate = m_state; m_state = m_quit; m_entersound = true; msgNumber = rand()&7; } void M_Quit_Key (int key) { switch (key) { case K_TRIANGLE: case K_SELECT: case 'n': case 'N': if (wasInMenus) { m_state = m_quit_prevstate; m_entersound = true; } else { key_dest = key_game; m_state = m_none; } break; case K_CROSS: case K_CIRCLE: key_dest = key_console; Host_Quit_f (); break; default: break; } } void M_Quit_Draw (void) { if (wasInMenus) { m_state = m_quit_prevstate; m_recursiveDraw = true; M_Draw (); m_state = m_quit; } #ifdef _WIN32 // ToDo: Move this to a Credits subsection M_DrawTextBox (0, 0, 38, 23); M_PrintWhite (16, 12, " Quake version 1.09 by id Software\n\n"); M_PrintWhite (16, 28, "Programming Art \n"); M_Print (16, 36, " John Carmack Adrian Carmack\n"); M_Print (16, 44, " Michael Abrash Kevin Cloud\n"); M_Print (16, 52, " John Cash Paul Steed\n"); M_Print (16, 60, " Dave 'Zoid' Kirsch\n"); M_PrintWhite (16, 68, "Design Biz\n"); M_Print (16, 76, " John Romero Jay Wilbur\n"); M_Print (16, 84, " Sandy Petersen Mike Wilson\n"); M_Print (16, 92, " American McGee Donna Jackson\n"); M_Print (16, 100, " Tim Willits Todd Hollenshead\n"); M_PrintWhite (16, 108, "Support Projects\n"); M_Print (16, 116, " Barrett Alexander Shawn Green\n"); M_PrintWhite (16, 124, "Sound Effects\n"); M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n"); M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n"); M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n"); M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n"); M_PrintWhite (16, 164, "registered trademark licensed to\n"); M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n"); M_PrintWhite (16, 180, "reserved. Press y to exit\n"); #else M_DrawTextBox (56, 76, 24, 4); M_Print (64, 84, quitMessage[msgNumber*4+0]); M_Print (64, 92, quitMessage[msgNumber*4+1]); M_Print (64, 100, quitMessage[msgNumber*4+2]); M_Print (64, 108, quitMessage[msgNumber*4+3]); #endif Draw_Batched(); } //============================================================================= /* LAN CONFIG MENU */ int lanConfig_cursor = 1; int lanConfig_cursor_table [] = {72, 92, 112, 144, 158}; #define NUM_LANCONFIG_CMDS 4 int lanConfig_port; char lanConfig_portname[6]; char lanConfig_joinname[22]; void M_Menu_LanConfig_f (void) { key_dest = key_menu; m_state = m_lanconfig; m_entersound = true; if (lanConfig_cursor == -1) { if (JoiningGame && TCPIPConfig) lanConfig_cursor = NUM_LANCONFIG_CMDS; } if (StartingGame && lanConfig_cursor >= 3) lanConfig_cursor = 1; lanConfig_port = DEFAULTnet_hostport; sprintf(lanConfig_portname, "%u", lanConfig_port); m_return_onerror = false; m_return_reason[0] = 0; } char protocol[64]; uint8_t proto_idx = 0; void M_LanConfig_Draw (void) { qpic_t *p; int basex; char *startJoin; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); basex = (320-p->width)/2; M_DrawPic (basex, 4, p); if (StartingGame) startJoin = "New Game"; else startJoin = "Join Game"; if (proto_idx == 0) sprintf(protocol, "TCP/IP"); else sprintf(protocol, "AdHoc"); M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1); if (JoiningGame) { M_DrawTextBox (basex+8, lanConfig_cursor_table[3]-8, 22, 1); M_Print (basex, lanConfig_cursor_table[2], "Search for local games..."); M_Print (basex, 128, "Join game at:"); M_Print (basex+16, lanConfig_cursor_table[3], lanConfig_joinname); M_Print (basex, 158, "Join an online server"); } else { M_DrawTextBox (basex, lanConfig_cursor_table[2]-8, 2, 1); M_Print (basex+8, lanConfig_cursor_table[2], "OK"); } M_Print (basex, 32, va ("%s - %s", startJoin, protocol)); basex += 8; M_Print (basex, 52, "Address:"); M_Print (basex+9*8, 52, my_tcpip_address); M_Print (basex, lanConfig_cursor_table[0], "Port"); M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname); M_Print (basex, lanConfig_cursor_table[1], "Protocol"); M_Print (basex+9*8, lanConfig_cursor_table[1], protocol); M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1)); if (lanConfig_cursor == 0) M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1)); if (lanConfig_cursor == 3) M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [3], 10+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (basex, 168, m_return_reason); Draw_Batched(); } void M_LanConfig_Key (int key) { int l; switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_MultiPlayer_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); lanConfig_cursor--; if (lanConfig_cursor < 0) if (JoiningGame) lanConfig_cursor = NUM_LANCONFIG_CMDS; else lanConfig_cursor = NUM_LANCONFIG_CMDS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); lanConfig_cursor++; if (lanConfig_cursor > NUM_LANCONFIG_CMDS) lanConfig_cursor = 0; break; case K_CIRCLE: case K_CROSS: if (lanConfig_cursor == 0) break; m_entersound = true; M_ConfigureNetSubsystem (); if (lanConfig_cursor == 1) { Datagram_Shutdown(); proto_idx = (proto_idx + 1) % 2; if (proto_idx == 1) { // Setup AdHoc // Start sceNetAdhoc and sceNetAdhocctl sceNetAdhocInit(); SceNetAdhocctlAdhocId adhocId; memset(&adhocId, 0, sizeof(SceNetAdhocctlAdhocId)); adhocId.type = SCE_NET_ADHOCCTL_ADHOCTYPE_RESERVED; memcpy(&adhocId.data[0], "QUAK00001", SCE_NET_ADHOCCTL_ADHOCID_LEN); sceNetAdhocctlInit(&adhocId); SceNetCheckDialogParam param; sceNetCheckDialogParamInit(¶m); SceNetAdhocctlGroupName groupName; memset(groupName.data, 0, SCE_NET_ADHOCCTL_GROUPNAME_LEN); param.groupName = &groupName; memcpy(¶m.npCommunicationId.data, "QUAK00001", 9); param.npCommunicationId.term = '\0'; param.npCommunicationId.num = 0; param.mode = SCE_NETCHECK_DIALOG_MODE_PSP_ADHOC_CONN; param.timeoutUs = 0; int res = sceNetCheckDialogInit(¶m); if (res >= 0) { netcheck_dialog_running = 1; } } else Datagram_Init(); } if (lanConfig_cursor == 2) { if (StartingGame) { M_Menu_GameOptions_f (); break; } M_Menu_Search_f(); break; } if (lanConfig_cursor == 3) { m_return_state = m_state; m_return_onerror = true; key_dest = key_game; m_state = m_none; Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) ); break; } if (lanConfig_cursor == 4) { M_Menu_OnlineServerList_f (); break; } break; case K_BACKSPACE: if (lanConfig_cursor == 0) { if (strlen(lanConfig_portname)) lanConfig_portname[strlen(lanConfig_portname)-1] = 0; } if (lanConfig_cursor == 3) { if (strlen(lanConfig_joinname)) lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0; } break; default: if (key < 32 || key > 127) break; if (lanConfig_cursor == 3) { l = strlen(lanConfig_joinname); if (l < 21) { lanConfig_joinname[l+1] = 0; lanConfig_joinname[l] = key; } } if (key < '0' || key > '9') break; if (lanConfig_cursor == 0) { l = strlen(lanConfig_portname); if (l < 5) { lanConfig_portname[l+1] = 0; lanConfig_portname[l] = key; } } } if (StartingGame && lanConfig_cursor == 3) if (key == K_UPARROW) lanConfig_cursor = 2; else lanConfig_cursor = 0; l = atoi(lanConfig_portname); if (l > 65535) l = lanConfig_port; else lanConfig_port = l; sprintf(lanConfig_portname, "%u", lanConfig_port); } //============================================================================= /* ONLINE SERVERLIST MENU */ int onlineServerList_cursor = 0; void M_Menu_OnlineServerList_f (void) { key_dest = key_menu; m_state = m_onlineserverlist; m_entersound = true; m_return_onerror = false; m_return_reason[0] = 0; } #define NUM_SERVERS 5 void M_OnlineServerList_Draw (void) { qpic_t *p; int basex; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); basex = (320-p->width)/2; M_DrawPic (basex, 4, p); M_Print (basex, 32, "EU Official Server (Shareware Only)"); M_Print (basex, 40, "EU Official Server (Deatmatch Maps)"); M_Print (basex, 48, "NCTech Spaceball1 Server"); M_Print (basex, 56, "Shmack Practice Mode Server"); M_Print (basex, 64, "Clan HDZ DM Server"); M_DrawCharacter (basex-8, 32+onlineServerList_cursor*8, 12+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (basex, 148, m_return_reason); Draw_Batched(); } void M_OnlineServerList_Key (int key) { int l; switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_MultiPlayer_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); onlineServerList_cursor--; if (onlineServerList_cursor < 0) onlineServerList_cursor = NUM_SERVERS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); onlineServerList_cursor++; if (onlineServerList_cursor >= NUM_SERVERS) onlineServerList_cursor = 0; break; case K_CIRCLE: case K_CROSS: m_return_state = m_state; m_return_onerror = true; key_dest = key_game; m_state = m_none; Cbuf_AddText ("stopdemo\n"); if (onlineServerList_cursor == 0) Cbuf_AddText ("connect 212.24.100.151\n"); if (onlineServerList_cursor == 1) Cbuf_AddText ("connect 212.24.100.151:27000\n"); if (onlineServerList_cursor == 2) Cbuf_AddText ("connect quake.nctech.ca\n"); if (onlineServerList_cursor == 3) Cbuf_AddText ("connect quake.shmack.net\n"); if (onlineServerList_cursor == 4) Cbuf_AddText ("connect dm.clanhdz.com\n"); break; } } //============================================================================= /* GAME OPTIONS MENU */ typedef struct { char *name; char *description; } level_t; level_t levels[] = { {"start", "Entrance"}, // 0 {"e1m1", "Slipgate Complex"}, // 1 {"e1m2", "Castle of the Damned"}, {"e1m3", "The Necropolis"}, {"e1m4", "The Grisly Grotto"}, {"e1m5", "Gloom Keep"}, {"e1m6", "The Door To Chthon"}, {"e1m7", "The House of Chthon"}, {"e1m8", "Ziggurat Vertigo"}, {"e2m1", "The Installation"}, // 9 {"e2m2", "Ogre Citadel"}, {"e2m3", "Crypt of Decay"}, {"e2m4", "The Ebon Fortress"}, {"e2m5", "The Wizard's Manse"}, {"e2m6", "The Dismal Oubliette"}, {"e2m7", "Underearth"}, {"e3m1", "Termination Central"}, // 16 {"e3m2", "The Vaults of Zin"}, {"e3m3", "The Tomb of Terror"}, {"e3m4", "Satan's Dark Delight"}, {"e3m5", "Wind Tunnels"}, {"e3m6", "Chambers of Torment"}, {"e3m7", "The Haunted Halls"}, {"e4m1", "The Sewage System"}, // 23 {"e4m2", "The Tower of Despair"}, {"e4m3", "The Elder God Shrine"}, {"e4m4", "The Palace of Hate"}, {"e4m5", "Hell's Atrium"}, {"e4m6", "The Pain Maze"}, {"e4m7", "Azure Agony"}, {"e4m8", "The Nameless City"}, {"end", "Shub-Niggurath's Pit"}, // 31 {"dm1", "Place of Two Deaths"}, // 32 {"dm2", "Claustrophobopolis"}, {"dm3", "The Abandoned Base"}, {"dm4", "The Bad Place"}, {"dm5", "The Cistern"}, {"dm6", "The Dark Zone"} }; //MED 01/06/97 added hipnotic levels level_t hipnoticlevels[] = { {"start", "Command HQ"}, // 0 {"hip1m1", "The Pumping Station"}, // 1 {"hip1m2", "Storage Facility"}, {"hip1m3", "The Lost Mine"}, {"hip1m4", "Research Facility"}, {"hip1m5", "Military Complex"}, {"hip2m1", "Ancient Realms"}, // 6 {"hip2m2", "The Black Cathedral"}, {"hip2m3", "The Catacombs"}, {"hip2m4", "The Crypt"}, {"hip2m5", "Mortum's Keep"}, {"hip2m6", "The Gremlin's Domain"}, {"hip3m1", "Tur Torment"}, // 12 {"hip3m2", "Pandemonium"}, {"hip3m3", "Limbo"}, {"hip3m4", "The Gauntlet"}, {"hipend", "Armagon's Lair"}, // 16 {"hipdm1", "The Edge of Oblivion"} // 17 }; //PGM 01/07/97 added rogue levels //PGM 03/02/97 added dmatch level level_t roguelevels[] = { {"start", "Split Decision"}, {"r1m1", "Deviant's Domain"}, {"r1m2", "Dread Portal"}, {"r1m3", "Judgement Call"}, {"r1m4", "Cave of Death"}, {"r1m5", "Towers of Wrath"}, {"r1m6", "Temple of Pain"}, {"r1m7", "Tomb of the Overlord"}, {"r2m1", "Tempus Fugit"}, {"r2m2", "Elemental Fury I"}, {"r2m3", "Elemental Fury II"}, {"r2m4", "Curse of Osiris"}, {"r2m5", "Wizard's Keep"}, {"r2m6", "Blood Sacrifice"}, {"r2m7", "Last Bastion"}, {"r2m8", "Source of Evil"}, {"ctf1", "Division of Change"} }; typedef struct { char *description; int firstLevel; int levels; } episode_t; episode_t episodes[] = { {"Welcome to Quake", 0, 1}, {"Doomed Dimension", 1, 8}, {"Realm of Black Magic", 9, 7}, {"Netherworld", 16, 7}, {"The Elder World", 23, 8}, {"Final Level", 31, 1}, {"Deathmatch Arena", 32, 6} }; //MED 01/06/97 added hipnotic episodes episode_t hipnoticepisodes[] = { {"Scourge of Armagon", 0, 1}, {"Fortress of the Dead", 1, 5}, {"Dominion of Darkness", 6, 6}, {"The Rift", 12, 4}, {"Final Level", 16, 1}, {"Deathmatch Arena", 17, 1} }; //PGM 01/07/97 added rogue episodes //PGM 03/02/97 added dmatch episode episode_t rogueepisodes[] = { {"Introduction", 0, 1}, {"Hell's Fortress", 1, 7}, {"Corridors of Time", 8, 8}, {"Deathmatch Arena", 16, 1} }; int startepisode; int startlevel; int maxplayers; bool m_serverInfoMessage = false; double m_serverInfoMessageTime; void M_Menu_GameOptions_f (void) { key_dest = key_menu; m_state = m_gameoptions; m_entersound = true; if (maxplayers == 0) maxplayers = svs.maxclients; if (maxplayers < 2) maxplayers = svs.maxclientslimit; } int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120}; #define NUM_GAMEOPTIONS 9 int gameoptions_cursor; void M_GameOptions_Draw (void) { qpic_t *p; int x; M_DrawTransPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") ); p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); M_DrawTextBox (152, 32, 10, 1); M_Print (160, 40, "begin game"); M_Print (0, 56, " Max players"); M_Print (160, 56, va("%i", maxplayers) ); M_Print (0, 64, " Game Type"); if (coop.value) M_Print (160, 64, "Cooperative"); else M_Print (160, 64, "Deathmatch"); M_Print (0, 72, " Teamplay"); if (rogue) { char *msg; switch((int)teamplay.value) { case 1: msg = "No Friendly Fire"; break; case 2: msg = "Friendly Fire"; break; case 3: msg = "Tag"; break; case 4: msg = "Capture the Flag"; break; case 5: msg = "One Flag CTF"; break; case 6: msg = "Three Team CTF"; break; default: msg = "Off"; break; } M_Print (160, 72, msg); } else { char *msg; switch((int)teamplay.value) { case 1: msg = "No Friendly Fire"; break; case 2: msg = "Friendly Fire"; break; default: msg = "Off"; break; } M_Print (160, 72, msg); } M_Print (0, 80, " Skill"); if (skill.value == 0) M_Print (160, 80, "Easy difficulty"); else if (skill.value == 1) M_Print (160, 80, "Normal difficulty"); else if (skill.value == 2) M_Print (160, 80, "Hard difficulty"); else M_Print (160, 80, "Nightmare difficulty"); M_Print (0, 88, " Frag Limit"); if (fraglimit.value == 0) M_Print (160, 88, "none"); else M_Print (160, 88, va("%i frags", (int)fraglimit.value)); M_Print (0, 96, " Time Limit"); if (timelimit.value == 0) M_Print (160, 96, "none"); else M_Print (160, 96, va("%i minutes", (int)timelimit.value)); M_Print (0, 112, " Episode"); //MED 01/06/97 added hipnotic episodes if (hipnotic) M_Print (160, 112, hipnoticepisodes[startepisode].description); //PGM 01/07/97 added rogue episodes else if (rogue) M_Print (160, 112, rogueepisodes[startepisode].description); else M_Print (160, 112, episodes[startepisode].description); M_Print (0, 120, " Level"); //MED 01/06/97 added hipnotic episodes if (hipnotic) { M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description); M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name); } //PGM 01/07/97 added rogue episodes else if (rogue) { M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description); M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name); } else { M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description); M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name); } // line cursor M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1)); if (m_serverInfoMessage) { if ((realtime - m_serverInfoMessageTime) < 5.0) { x = (320-26*8)/2 + 8; M_Print (x, 146, " More than 4 players "); M_Print (x, 154, " requires using command "); M_Print (x, 162, "line parameters; please "); M_Print (x, 170, " see techinfo.txt. "); Draw_Batched(); M_DrawTextBox ((320-26*8)/2, 138, 24, 4); } else { Draw_Batched(); m_serverInfoMessage = false; } } else Draw_Batched(); } void M_NetStart_Change (int dir) { int count; switch (gameoptions_cursor) { case 1: maxplayers += dir; if (maxplayers > svs.maxclientslimit) { maxplayers = svs.maxclientslimit; m_serverInfoMessage = true; m_serverInfoMessageTime = realtime; } if (maxplayers < 2) maxplayers = 2; break; case 2: Cvar_SetValue ("coop", coop.value ? 0 : 1); break; case 3: if (rogue) count = 6; else count = 2; Cvar_SetValue ("teamplay", teamplay.value + dir); if (teamplay.value > count) Cvar_SetValue ("teamplay", 0); else if (teamplay.value < 0) Cvar_SetValue ("teamplay", count); break; case 4: Cvar_SetValue ("skill", skill.value + dir); if (skill.value > 3) Cvar_SetValue ("skill", 0); if (skill.value < 0) Cvar_SetValue ("skill", 3); break; case 5: Cvar_SetValue ("fraglimit", fraglimit.value + dir*10); if (fraglimit.value > 100) Cvar_SetValue ("fraglimit", 0); if (fraglimit.value < 0) Cvar_SetValue ("fraglimit", 100); break; case 6: Cvar_SetValue ("timelimit", timelimit.value + dir*5); if (timelimit.value > 60) Cvar_SetValue ("timelimit", 0); if (timelimit.value < 0) Cvar_SetValue ("timelimit", 60); break; case 7: startepisode += dir; //MED 01/06/97 added hipnotic count if (hipnotic) count = 6; //PGM 01/07/97 added rogue count //PGM 03/02/97 added 1 for dmatch episode else if (rogue) count = 4; else if (registered.value) count = 7; else count = 2; if (startepisode < 0) startepisode = count - 1; if (startepisode >= count) startepisode = 0; startlevel = 0; break; case 8: startlevel += dir; //MED 01/06/97 added hipnotic episodes if (hipnotic) count = hipnoticepisodes[startepisode].levels; //PGM 01/06/97 added hipnotic episodes else if (rogue) count = rogueepisodes[startepisode].levels; else count = episodes[startepisode].levels; if (startlevel < 0) startlevel = count - 1; if (startlevel >= count) startlevel = 0; break; } } void M_GameOptions_Key (int key) { switch (key) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_Net_f (); break; case K_UPARROW: S_LocalSound ("misc/menu1.wav"); gameoptions_cursor--; if (gameoptions_cursor < 0) gameoptions_cursor = NUM_GAMEOPTIONS-1; break; case K_DOWNARROW: S_LocalSound ("misc/menu1.wav"); gameoptions_cursor++; if (gameoptions_cursor >= NUM_GAMEOPTIONS) gameoptions_cursor = 0; break; case K_LEFTARROW: if (gameoptions_cursor == 0) break; S_LocalSound ("misc/menu3.wav"); M_NetStart_Change (-1); break; case K_RIGHTARROW: if (gameoptions_cursor == 0) break; S_LocalSound ("misc/menu3.wav"); M_NetStart_Change (1); break; case K_CIRCLE: case K_CROSS: S_LocalSound ("misc/menu2.wav"); if (gameoptions_cursor == 0) { if (sv.active) Cbuf_AddText ("disconnect\n"); Cbuf_AddText ("listen 0\n"); // so host_netport will be re-examined Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) ); SCR_BeginLoadingPlaque (); if (hipnotic) Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) ); else if (rogue) Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) ); else Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) ); return; } M_NetStart_Change (1); break; } } //============================================================================= /* SEARCH MENU */ bool searchComplete = false; double searchCompleteTime; void M_Menu_Search_f (void) { key_dest = key_menu; m_state = m_search; m_entersound = false; slistSilent = true; slistLocal = false; searchComplete = false; NET_Slist_f(); } void M_Search_Draw (void) { qpic_t *p; int x; p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); x = (320/2) - ((12*8)/2) + 4; M_DrawTextBox (x-8, 32, 12, 1); Draw_String (x, 40, "Searching...", 128); if(slistInProgress) { NET_Poll(); return; } if (! searchComplete) { searchComplete = true; searchCompleteTime = realtime; } if (hostCacheCount) { M_Menu_ServerList_f (); return; } Draw_String ((320/2) - ((22*8)/2), 64, "No Quake servers found", 0); if ((realtime - searchCompleteTime) < 3.0) return; M_Menu_LanConfig_f (); } void M_Search_Key (int key) { } //============================================================================= /* SLIST MENU */ int slist_cursor; bool slist_sorted; void M_Menu_ServerList_f (void) { key_dest = key_menu; m_state = m_slist; m_entersound = true; slist_cursor = 0; m_return_onerror = false; m_return_reason[0] = 0; slist_sorted = false; } void M_ServerList_Draw (void) { int n; char string [64]; qpic_t *p; if (!slist_sorted) { if (hostCacheCount > 1) { int i,j; hostcache_t temp; for (i = 0; i < hostCacheCount; i++) for (j = i+1; j < hostCacheCount; j++) if (strcmp(hostcache[j].name, hostcache[i].name) < 0) { memcpy(&temp, &hostcache[j], sizeof(hostcache_t)); memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t)); memcpy(&hostcache[i], &temp, sizeof(hostcache_t)); } } slist_sorted = true; } p = Draw_CachePic ("gfx/p_multi.lmp"); M_DrawPic ( (320-p->width)/2, 4, p); for (n = 0; n < hostCacheCount; n++) { if (hostcache[n].maxusers) sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers); else sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map); M_Print (16, 32 + 8*n, string); } M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1)); if (*m_return_reason) M_PrintWhite (16, 148, m_return_reason); } void M_ServerList_Key (int k) { switch (k) { case K_ENTER: case K_START: case K_TRIANGLE: M_Menu_LanConfig_f (); break; case K_SPACE: M_Menu_Search_f (); break; case K_UPARROW: case K_LEFTARROW: S_LocalSound ("misc/menu1.wav"); slist_cursor--; if (slist_cursor < 0) slist_cursor = hostCacheCount - 1; break; case K_DOWNARROW: case K_RIGHTARROW: S_LocalSound ("misc/menu1.wav"); slist_cursor++; if (slist_cursor >= hostCacheCount) slist_cursor = 0; break; case K_CIRCLE: case K_CROSS: S_LocalSound ("misc/menu2.wav"); m_return_state = m_state; m_return_onerror = true; slist_sorted = false; key_dest = key_game; m_state = m_none; Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) ); break; default: break; } } //============================================================================= /* Menu Subsystem */ void M_Init (void) { Cmd_AddCommand ("togglemenu", M_ToggleMenu_f); Cmd_AddCommand ("menu_main", M_Menu_Main_f); Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f); Cmd_AddCommand ("menu_load", M_Menu_Load_f); Cmd_AddCommand ("menu_save", M_Menu_Save_f); Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f); Cmd_AddCommand ("menu_setup", M_Menu_Setup_f); Cmd_AddCommand ("menu_options", M_Menu_Options_f); Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("menu_video", M_Menu_Video_f); Cmd_AddCommand ("help", M_Menu_Help_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); } void M_Draw (void) { if (m_state == m_none || key_dest != key_menu) return; if (!m_recursiveDraw) { scr_copyeverything = 1; if (scr_con_current) { Draw_ConsoleBackground (); VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } else Draw_FadeScreen (); scr_fullupdate = 0; } else { m_recursiveDraw = false; } GL_SetCanvas (CANVAS_MENU); //johnfitz switch (m_state) { case m_none: break; case m_main: M_Main_Draw (); break; case m_singleplayer: M_SinglePlayer_Draw (); break; case m_load: M_Load_Draw (); break; case m_save: M_Save_Draw (); break; case m_multiplayer: M_MultiPlayer_Draw (); break; case m_setup: M_Setup_Draw (); break; case m_net: M_Net_Draw (); break; case m_options: M_Options_Draw (); break; case m_graphics: M_Graphics_Draw(); break; case m_mods: M_Mods_Draw(); break; case m_keys: M_Keys_Draw (); break; case m_video: M_Video_Draw (); break; case m_help: M_Help_Draw (); break; case m_quit: M_Quit_Draw (); break; case m_lanconfig: M_LanConfig_Draw (); break; case m_gameoptions: M_GameOptions_Draw (); break; case m_search: M_Search_Draw (); break; case m_slist: M_ServerList_Draw (); break; case m_onlineserverlist: M_OnlineServerList_Draw(); break; case m_benchmark: M_Benchmark_Draw(); break; } if (m_entersound) { S_LocalSound ("misc/menu2.wav"); m_entersound = false; } VID_UnlockBuffer (); S_ExtraUpdate (); VID_LockBuffer (); } void M_Keydown (int key) { switch (m_state) { case m_none: return; case m_main: M_Main_Key (key); return; case m_singleplayer: M_SinglePlayer_Key (key); return; case m_load: M_Load_Key (key); return; case m_save: M_Save_Key (key); return; case m_multiplayer: M_MultiPlayer_Key (key); return; case m_setup: M_Setup_Key (key); return; case m_net: M_Net_Key (key); return; case m_options: M_Options_Key (key); return; case m_graphics: M_Graphics_Key (key); return; case m_mods: M_Mods_Key (key); return; case m_keys: M_Keys_Key (key); return; case m_video: M_Video_Key (key); return; case m_help: M_Help_Key (key); return; case m_quit: M_Quit_Key (key); return; case m_lanconfig: M_LanConfig_Key (key); return; case m_gameoptions: M_GameOptions_Key (key); return; case m_search: M_Search_Key (key); break; case m_slist: M_ServerList_Key (key); return; case m_onlineserverlist: M_OnlineServerList_Key (key); break; case m_benchmark: M_Benchmark_Key (key); break; } } void M_ConfigureNetSubsystem(void) { // enable/disable net systems to match desired config Cbuf_AddText ("stopdemo\n"); //if (IPXConfig || TCPIPConfig) net_hostport = lanConfig_port; } ================================================ FILE: source/menu.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // the net drivers should just set the apropriate bits in m_activenet, // instead of having the menu code look through their internal tables // #define MNET_IPX 1 #define MNET_TCP 2 extern int m_activenet; // // menus // void M_Init (void); void M_Keydown (int key); void M_Draw (void); void M_ToggleMenu_f (void); ================================================ FILE: source/model.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __MODEL__ #define __MODEL__ #include "modelgen.h" #include "spritegn.h" /* d*_t structures are on-disk representations m*_t structures are in-memory */ /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vec3_t position; } mvertex_t; #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 // plane_t structure // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct mplane_s { vec3_t normal; float dist; byte type; // for texture axis selection and fast side tests byte signbits; // signx + signy<<1 + signz<<1 byte pad[2]; } mplane_t; typedef struct texture_s { char name[16]; unsigned width, height; int anim_total; // total tenths in sequence ( 0 = no) int anim_min, anim_max; // time for this frame min <=time< max struct texture_s *anim_next; // in the animation sequence struct texture_s *alternate_anims; // bmodels in frmae 1 use these unsigned offsets[MIPLEVELS]; // four mip maps stored } texture_t; #define SURF_PLANEBACK BIT(1) #define SURF_DRAWSKY BIT(2) #define SURF_DRAWSPRITE BIT(3) #define SURF_DRAWTURB BIT(4) #define SURF_DRAWTILED BIT(5) #define SURF_DRAWBACKGROUND BIT(6) // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { unsigned short v[2]; unsigned int cachededgeoffset; } medge_t; typedef struct { float vecs[2][4]; float mipadjust; texture_t *texture; int flags; } mtexinfo_t; typedef struct msurface_s { int visframe; // should be drawn when node is crossed int dlightframe; int dlightbits; mplane_t *plane; int flags; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges // surface generation data struct surfcache_s *cachespots[MIPLEVELS]; short texturemins[2]; short extents[2]; mtexinfo_t *texinfo; // lighting info byte styles[MAXLIGHTMAPS]; byte *samples; // [numstyles*surfsize] } msurface_t; typedef struct mnode_s { // common with leaf int contents; // 0, to differentiate from leafs int visframe; // node needs to be traversed if current short minmaxs[6]; // for bounding box culling struct mnode_s *parent; // node specific mplane_t *plane; struct mnode_s *children[2]; unsigned short firstsurface; unsigned short numsurfaces; } mnode_t; typedef struct mleaf_s { // common with node int contents; // wil be a negative contents number int visframe; // node needs to be traversed if current short minmaxs[6]; // for bounding box culling struct mnode_s *parent; // leaf specific byte *compressed_vis; efrag_t *efrags; msurface_t **firstmarksurface; int nummarksurfaces; int key; // BSP sequence number for leaf's contents byte ambient_sound_level[NUM_AMBIENTS]; } mleaf_t; // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { dclipnode_t *clipnodes; mplane_t *planes; int firstclipnode; int lastclipnode; vec3_t clip_mins; vec3_t clip_maxs; } hull_t; /* ============================================================================== SPRITE MODELS ============================================================================== */ // FIXME: shorten these? typedef struct mspriteframe_s { int width; int height; void *pcachespot; // remove? float up, down, left, right; byte pixels[4]; } mspriteframe_t; typedef struct { int numframes; float *intervals; mspriteframe_t *frames[1]; } mspritegroup_t; typedef struct { spriteframetype_t type; mspriteframe_t *frameptr; } mspriteframedesc_t; typedef struct { int type; int maxwidth; int maxheight; int numframes; float beamlength; // remove? void *cachespot; // remove? mspriteframedesc_t frames[1]; } msprite_t; /* ============================================================================== ALIAS MODELS Alias models are position independent, so the cache manager can move them. ============================================================================== */ typedef struct { aliasframetype_t type; trivertx_t bboxmin; trivertx_t bboxmax; int frame; char name[16]; } maliasframedesc_t; typedef struct { aliasskintype_t type; void *pcachespot; int skin; } maliasskindesc_t; typedef struct { trivertx_t bboxmin; trivertx_t bboxmax; int frame; } maliasgroupframedesc_t; typedef struct { int numframes; int intervals; maliasgroupframedesc_t frames[1]; } maliasgroup_t; typedef struct { int numskins; int intervals; maliasskindesc_t skindescs[1]; } maliasskingroup_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct mtriangle_s { int facesfront; int vertindex[3]; } mtriangle_t; typedef struct { int model; int stverts; int skindesc; int triangles; maliasframedesc_t frames[1]; } aliashdr_t; //=================================================================== // // Whole model // typedef enum {mod_brush, mod_sprite, mod_alias} modtype_t; #define MF_ROCKET 1 // leave a trail #define MF_GRENADE 2 // leave a trail #define MF_GIB 4 // leave a trail #define MF_ROTATE 8 // rotate (bonus items) #define MF_TRACER 16 // green split trail #define MF_ZOMGIB 32 // small blood trail #define MF_TRACER2 64 // orange split trail + rotate #define MF_TRACER3 128 // purple trail typedef struct model_s { char name[MAX_QPATH]; bool needload; // bmodels and sprites don't cache normally modtype_t type; int numframes; synctype_t synctype; int flags; // // volume occupied by the model // vec3_t mins, maxs; float radius; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; dmodel_t *submodels; int numplanes; mplane_t *planes; int numleafs; // number of visible leafs, not counting 0 mleaf_t *leafs; int numvertexes; mvertex_t *vertexes; int numedges; medge_t *edges; int numnodes; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numsurfedges; int *surfedges; int numclipnodes; dclipnode_t *clipnodes; int nummarksurfaces; msurface_t **marksurfaces; hull_t hulls[MAX_MAP_HULLS]; int numtextures; texture_t **textures; byte *visdata; byte *lightdata; signed char *entities; // // additional model data // cache_user_t cache; // only access through Mod_Extradata } model_t; //============================================================================ void Mod_Init (void); void Mod_ClearAll (void); model_t *Mod_ForName (char *name, bool crash); void *Mod_Extradata (model_t *mod); // handles caching void Mod_TouchModel (char *name); mleaf_t *Mod_PointInLeaf (float *p, model_t *model); byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model); #endif // __MODEL__ ================================================ FILE: source/modelgen.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // modelgen.h: header file for model generation program // // ********************************************************* // * This file must be identical in the modelgen directory * // * and in the Quake directory, because it's used to * // * pass data from one to the other via model files. * // ********************************************************* #ifdef INCLUDELIBS #include #include #include #include #include "cmdlib.h" #include "scriplib.h" #include "trilib.h" #include "lbmlib.h" #include "mathlib.h" #endif #define ALIAS_VERSION 6 #define ALIAS_ONSEAM 0x0020 // must match definition in spritegn.h #ifndef SYNCTYPE_T #define SYNCTYPE_T typedef enum {ST_SYNC=0, ST_RAND } synctype_t; #endif typedef enum { ALIAS_SINGLE=0, ALIAS_GROUP } aliasframetype_t; typedef enum { ALIAS_SKIN_SINGLE=0, ALIAS_SKIN_GROUP } aliasskintype_t; typedef struct { int ident; int version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; int numskins; int skinwidth; int skinheight; int numverts; int numtris; int numframes; synctype_t synctype; int flags; float size; } mdl_t; // TODO: could be shorts typedef struct { int onseam; int s; int t; } stvert_t; typedef struct dtriangle_s { int facesfront; int vertindex[3]; } dtriangle_t; #define DT_FACES_FRONT 0x0010 // This mirrors trivert_t in trilib.h, is present so Quake knows how to // load this data typedef struct { byte v[3]; byte lightnormalindex; } trivertx_t; typedef struct { trivertx_t bboxmin; // lightnormal isn't used trivertx_t bboxmax; // lightnormal isn't used char name[16]; // frame name from grabbing } daliasframe_t; typedef struct { int numframes; trivertx_t bboxmin; // lightnormal isn't used trivertx_t bboxmax; // lightnormal isn't used } daliasgroup_t; typedef struct { int numskins; } daliasskingroup_t; typedef struct { float interval; } daliasinterval_t; typedef struct { float interval; } daliasskininterval_t; typedef struct { int type; } daliasframetype_t; typedef struct { int type; } daliasskintype_t; #define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') // little-endian "IDPO" ================================================ FILE: source/mpdosock.h ================================================ /* WINSOCK.H--definitions to be used with the WINSOCK.DLL * Copyright (c) 1993-1995, Microsoft Corp. All rights reserved. * * This header file corresponds to version 1.1 of the Windows Sockets specification. * * This file includes parts which are Copyright (c) 1982-1986 Regents * of the University of California. All rights reserved. The * Berkeley Software License Agreement specifies the terms and * conditions for redistribution. * */ #ifndef _WINSOCKAPI_ #define _WINSOCKAPI_ #define FAR #define PASCAL /* * Basic system type definitions, taken from the BSD file sys/types.h. */ typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned long u_long; /* * The new type to be used in all * instances which refer to sockets. */ typedef u_int SOCKET; /* * Commands for ioctlsocket(), taken from the BSD file fcntl.h. * * * Ioctl's have the command encoded in the lower word, * and the size of any in or out parameters in the upper * word. The high 2 bits of the upper word are used * to encode the in/out status of the parameter; for now * we restrict parameters to at most 128 bytes. */ #define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ #define IOC_VOID 0x20000000 /* no parameters */ #define IOC_OUT 0x40000000 /* copy out parameters */ #define IOC_IN 0x80000000 /* copy in parameters */ #define IOC_INOUT (IOC_IN|IOC_OUT) /* 0x20000000 distinguishes new & old ioctl's */ #define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) #define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) #define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) #define FIONREAD _IOR('f', 127, u_long) /* get # bytes to read */ #define FIONBIO _IOW('f', 126, u_long) /* set/clear non-blocking i/o */ #define FIOASYNC _IOW('f', 125, u_long) /* set/clear async i/o */ /* Socket I/O Controls */ #define SIOCSHIWAT _IOW('s', 0, u_long) /* set high watermark */ #define SIOCGHIWAT _IOR('s', 1, u_long) /* get high watermark */ #define SIOCSLOWAT _IOW('s', 2, u_long) /* set low watermark */ #define SIOCGLOWAT _IOR('s', 3, u_long) /* get low watermark */ #define SIOCATMARK _IOR('s', 7, u_long) /* at oob mark? */ /* * Structures returned by network data base library, taken from the * BSD file netdb.h. All addresses are supplied in host order, and * returned in network order (suitable for use in system calls). */ struct hostent { char FAR * h_name; /* official name of host */ char FAR * FAR * h_aliases; /* alias list */ short h_addrtype; /* host address type */ short h_length; /* length of address */ char FAR * FAR * h_addr_list; /* list of addresses */ #define h_addr h_addr_list[0] /* address, for backward compat */ }; /* * It is assumed here that a network number * fits in 32 bits. */ struct netent { char FAR * n_name; /* official name of net */ char FAR * FAR * n_aliases; /* alias list */ short n_addrtype; /* net address type */ u_long n_net; /* network # */ }; struct servent { char FAR * s_name; /* official service name */ char FAR * FAR * s_aliases; /* alias list */ short s_port; /* port # */ char FAR * s_proto; /* protocol to use */ }; struct protoent { char FAR * p_name; /* official protocol name */ char FAR * FAR * p_aliases; /* alias list */ short p_proto; /* protocol # */ }; /* * Constants and structures defined by the internet system, * Per RFC 790, September 1981, taken from the BSD file netinet/in.h. */ /* * Protocols */ #define IPPROTO_IP 0 /* dummy for IP */ #define IPPROTO_ICMP 1 /* control message protocol */ #define IPPROTO_GGP 2 /* gateway^2 (deprecated) */ #define IPPROTO_TCP 6 /* tcp */ #define IPPROTO_PUP 12 /* pup */ #define IPPROTO_UDP 17 /* user datagram protocol */ #define IPPROTO_IDP 22 /* xns idp */ #define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */ #define IPPROTO_RAW 255 /* raw IP packet */ #define IPPROTO_MAX 256 /* * Port/socket numbers: network standard functions */ #define IPPORT_ECHO 7 #define IPPORT_DISCARD 9 #define IPPORT_SYSTAT 11 #define IPPORT_DAYTIME 13 #define IPPORT_NETSTAT 15 #define IPPORT_FTP 21 #define IPPORT_TELNET 23 #define IPPORT_SMTP 25 #define IPPORT_TIMESERVER 37 #define IPPORT_NAMESERVER 42 #define IPPORT_WHOIS 43 #define IPPORT_MTP 57 /* * Port/socket numbers: host specific functions */ #define IPPORT_TFTP 69 #define IPPORT_RJE 77 #define IPPORT_FINGER 79 #define IPPORT_TTYLINK 87 #define IPPORT_SUPDUP 95 /* * UNIX TCP sockets */ #define IPPORT_EXECSERVER 512 #define IPPORT_LOGINSERVER 513 #define IPPORT_CMDSERVER 514 #define IPPORT_EFSSERVER 520 /* * UNIX UDP sockets */ #define IPPORT_BIFFUDP 512 #define IPPORT_WHOSERVER 513 #define IPPORT_ROUTESERVER 520 /* 520+1 also used */ /* * Ports < IPPORT_RESERVED are reserved for * privileged processes (e.g. root). */ #define IPPORT_RESERVED 1024 /* * Link numbers */ #define IMPLINK_IP 155 #define IMPLINK_LOWEXPER 156 #define IMPLINK_HIGHEXPER 158 /* * Internet address (old style... should be updated) */ struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; #define s_addr S_un.S_addr /* can be used for most tcp & ip code */ #define s_host S_un.S_un_b.s_b2 /* host on imp */ #define s_net S_un.S_un_b.s_b1 /* network */ #define s_imp S_un.S_un_w.s_w2 /* imp */ #define s_impno S_un.S_un_b.s_b4 /* imp # */ #define s_lh S_un.S_un_b.s_b3 /* logical host */ }; /* * Definitions of bits in internet address integers. * On subnets, the decomposition of addresses to host and net parts * is done according to subnet mask, not the masks here. */ #define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) #define IN_CLASSA_NET 0xff000000 #define IN_CLASSA_NSHIFT 24 #define IN_CLASSA_HOST 0x00ffffff #define IN_CLASSA_MAX 128 #define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) #define IN_CLASSB_NET 0xffff0000 #define IN_CLASSB_NSHIFT 16 #define IN_CLASSB_HOST 0x0000ffff #define IN_CLASSB_MAX 65536 #define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) #define IN_CLASSC_NET 0xffffff00 #define IN_CLASSC_NSHIFT 8 #define IN_CLASSC_HOST 0x000000ff #define INADDR_ANY (u_long)0x00000000 #define INADDR_LOOPBACK 0x7f000001 #define INADDR_BROADCAST (u_long)0xffffffff #define INADDR_NONE 0xffffffff /* * Socket address, internet style. */ struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; #define WSADESCRIPTION_LEN 256 #define WSASYS_STATUS_LEN 128 /* * Options for use with [gs]etsockopt at the IP level. */ #define IP_OPTIONS 1 /* set/get IP per-packet options */ #define IP_MULTICAST_IF 2 /* set/get IP multicast interface */ #define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */ #define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */ #define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */ #define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */ #define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */ #define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */ #define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */ /* * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. */ struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast address of group */ struct in_addr imr_interface; /* local IP address of interface */ }; /* * Definitions related to sockets: types, address families, options, * taken from the BSD file sys/socket.h. */ /* * This is used instead of -1, since the * SOCKET type is unsigned. */ #define INVALID_SOCKET (SOCKET)(~0) #define SOCKET_ERROR (-1) /* * Types */ #define SOCK_STREAM 1 /* stream socket */ #define SOCK_DGRAM 2 /* datagram socket */ #define SOCK_RAW 3 /* raw-protocol interface */ #define SOCK_RDM 4 /* reliably-delivered message */ #define SOCK_SEQPACKET 5 /* sequenced packet stream */ /* * Option flags per-socket. */ #define SO_DEBUG 0x0001 /* turn on debugging info recording */ #define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ #define SO_REUSEADDR 0x0004 /* allow local address reuse */ #define SO_KEEPALIVE 0x0008 /* keep connections alive */ #define SO_DONTROUTE 0x0010 /* just use interface addresses */ #define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */ #define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */ #define SO_LINGER 0x0080 /* linger on close if data present */ #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ #define SO_DONTLINGER (u_int)(~SO_LINGER) /* * Additional options. */ #define SO_SNDBUF 0x1001 /* send buffer size */ #define SO_RCVBUF 0x1002 /* receive buffer size */ #define SO_SNDLOWAT 0x1003 /* send low-water mark */ #define SO_RCVLOWAT 0x1004 /* receive low-water mark */ #define SO_SNDTIMEO 0x1005 /* send timeout */ #define SO_RCVTIMEO 0x1006 /* receive timeout */ #define SO_ERROR 0x1007 /* get error status and clear */ #define SO_TYPE 0x1008 /* get socket type */ /* * Options for connect and disconnect data and options. Used only by * non-TCP/IP transports such as DECNet, OSI TP4, etc. */ #define SO_CONNDATA 0x7000 #define SO_CONNOPT 0x7001 #define SO_DISCDATA 0x7002 #define SO_DISCOPT 0x7003 #define SO_CONNDATALEN 0x7004 #define SO_CONNOPTLEN 0x7005 #define SO_DISCDATALEN 0x7006 #define SO_DISCOPTLEN 0x7007 /* * Option for opening sockets for synchronous access. */ #define SO_OPENTYPE 0x7008 #define SO_SYNCHRONOUS_ALERT 0x10 #define SO_SYNCHRONOUS_NONALERT 0x20 /* * Other NT-specific options. */ #define SO_MAXDG 0x7009 #define SO_MAXPATHDG 0x700A /* * TCP options. */ #define TCP_NODELAY 0x0001 #define TCP_BSDURGENT 0x7000 /* * Address families. */ #define AF_UNSPEC 0 /* unspecified */ #define AF_UNIX 1 /* local to host (pipes, portals) */ #define AF_INET 2 /* internetwork: UDP, TCP, etc. */ #define AF_IMPLINK 3 /* arpanet imp addresses */ #define AF_PUP 4 /* pup protocols: e.g. BSP */ #define AF_CHAOS 5 /* mit CHAOS protocols */ #define AF_IPX 6 /* IPX and SPX */ #define AF_NS 6 /* XEROX NS protocols */ #define AF_ISO 7 /* ISO protocols */ #define AF_OSI AF_ISO /* OSI is ISO */ #define AF_ECMA 8 /* european computer manufacturers */ #define AF_DATAKIT 9 /* datakit protocols */ #define AF_CCITT 10 /* CCITT protocols, X.25 etc */ #define AF_SNA 11 /* IBM SNA */ #define AF_DECnet 12 /* DECnet */ #define AF_DLI 13 /* Direct data link interface */ #define AF_LAT 14 /* LAT */ #define AF_HYLINK 15 /* NSC Hyperchannel */ #define AF_APPLETALK 16 /* AppleTalk */ #define AF_NETBIOS 17 /* NetBios-style addresses */ #define AF_VOICEVIEW 18 /* VoiceView */ #define AF_MAX 19 /* * Structure used by kernel to store most * addresses. */ struct sockaddr { u_short sa_family; /* address family */ char sa_data[14]; /* up to 14 bytes of direct address */ }; /* * Structure used by kernel to pass protocol * information in raw sockets. */ struct sockproto { u_short sp_family; /* address family */ u_short sp_protocol; /* protocol */ }; /* * Protocol families, same as address families for now. */ #define PF_UNSPEC AF_UNSPEC #define PF_UNIX AF_UNIX #define PF_INET AF_INET #define PF_IMPLINK AF_IMPLINK #define PF_PUP AF_PUP #define PF_CHAOS AF_CHAOS #define PF_NS AF_NS #define PF_IPX AF_IPX #define PF_ISO AF_ISO #define PF_OSI AF_OSI #define PF_ECMA AF_ECMA #define PF_DATAKIT AF_DATAKIT #define PF_CCITT AF_CCITT #define PF_SNA AF_SNA #define PF_DECnet AF_DECnet #define PF_DLI AF_DLI #define PF_LAT AF_LAT #define PF_HYLINK AF_HYLINK #define PF_APPLETALK AF_APPLETALK #define PF_VOICEVIEW AF_VOICEVIEW #define PF_MAX AF_MAX /* * Structure used for manipulating linger option. */ struct linger { u_short l_onoff; /* option on/off */ u_short l_linger; /* linger time */ }; /* * Level number for (get/set)sockopt() to apply to socket itself. */ #define SOL_SOCKET 0xffff /* options for socket level */ /* * Maximum queue length specifiable by listen. */ #define SOMAXCONN 5 #define MSG_OOB 0x1 /* process out-of-band data */ #define MSG_PEEK 0x2 /* peek at incoming message */ #define MSG_DONTROUTE 0x4 /* send without using routing tables */ #define MSG_MAXIOVLEN 16 #define MSG_PARTIAL 0x8000 /* partial send or recv for message xport */ /* * Define constant based on rfc883, used by gethostbyxxxx() calls. */ #define MAXGETHOSTSTRUCT 1024 /* * Define flags to be used with the WSAAsyncSelect() call. */ #define FD_READ 0x01 #define FD_WRITE 0x02 #define FD_OOB 0x04 #define FD_ACCEPT 0x08 #define FD_CONNECT 0x10 #define FD_CLOSE 0x20 /* * All Windows Sockets error constants are biased by WSABASEERR from * the "normal" */ #define WSABASEERR 10000 /* * Windows Sockets definitions of regular Microsoft C error constants */ #define WSAEINTR (WSABASEERR+4) #define WSAEBADF (WSABASEERR+9) #define WSAEACCES (WSABASEERR+13) #define WSAEFAULT (WSABASEERR+14) #define WSAEINVAL (WSABASEERR+22) #define WSAEMFILE (WSABASEERR+24) /* * Windows Sockets definitions of regular Berkeley error constants */ #define WSAEWOULDBLOCK (WSABASEERR+35) #define WSAEINPROGRESS (WSABASEERR+36) #define WSAEALREADY (WSABASEERR+37) #define WSAENOTSOCK (WSABASEERR+38) #define WSAEDESTADDRREQ (WSABASEERR+39) #define WSAEMSGSIZE (WSABASEERR+40) #define WSAEPROTOTYPE (WSABASEERR+41) #define WSAENOPROTOOPT (WSABASEERR+42) #define WSAEPROTONOSUPPORT (WSABASEERR+43) #define WSAESOCKTNOSUPPORT (WSABASEERR+44) #define WSAEOPNOTSUPP (WSABASEERR+45) #define WSAEPFNOSUPPORT (WSABASEERR+46) #define WSAEAFNOSUPPORT (WSABASEERR+47) #define WSAEADDRINUSE (WSABASEERR+48) #define WSAEADDRNOTAVAIL (WSABASEERR+49) #define WSAENETDOWN (WSABASEERR+50) #define WSAENETUNREACH (WSABASEERR+51) #define WSAENETRESET (WSABASEERR+52) #define WSAECONNABORTED (WSABASEERR+53) #define WSAECONNRESET (WSABASEERR+54) #define WSAENOBUFS (WSABASEERR+55) #define WSAEISCONN (WSABASEERR+56) #define WSAENOTCONN (WSABASEERR+57) #define WSAESHUTDOWN (WSABASEERR+58) #define WSAETOOMANYREFS (WSABASEERR+59) #define WSAETIMEDOUT (WSABASEERR+60) #define WSAECONNREFUSED (WSABASEERR+61) #define WSAELOOP (WSABASEERR+62) #define WSAENAMETOOLONG (WSABASEERR+63) #define WSAEHOSTDOWN (WSABASEERR+64) #define WSAEHOSTUNREACH (WSABASEERR+65) #define WSAENOTEMPTY (WSABASEERR+66) #define WSAEPROCLIM (WSABASEERR+67) #define WSAEUSERS (WSABASEERR+68) #define WSAEDQUOT (WSABASEERR+69) #define WSAESTALE (WSABASEERR+70) #define WSAEREMOTE (WSABASEERR+71) #define WSAEDISCON (WSABASEERR+101) /* * Extended Windows Sockets error constant definitions */ #define WSASYSNOTREADY (WSABASEERR+91) #define WSAVERNOTSUPPORTED (WSABASEERR+92) #define WSANOTINITIALISED (WSABASEERR+93) /* * Error return codes from gethostbyname() and gethostbyaddr() * (when using the resolver). Note that these errors are * retrieved via WSAGetLastError() and must therefore follow * the rules for avoiding clashes with error numbers from * specific implementations or language run-time systems. * For this reason the codes are based at WSABASEERR+1001. * Note also that [WSA]NO_ADDRESS is defined only for * compatibility purposes. */ #define h_errno WSAGetLastError() /* Authoritative Answer: Host not found */ #define WSAHOST_NOT_FOUND (WSABASEERR+1001) #define HOST_NOT_FOUND WSAHOST_NOT_FOUND /* Non-Authoritative: Host not found, or SERVERFAIL */ #define WSATRY_AGAIN (WSABASEERR+1002) #define TRY_AGAIN WSATRY_AGAIN /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ #define WSANO_RECOVERY (WSABASEERR+1003) #define NO_RECOVERY WSANO_RECOVERY /* Valid name, no data record of requested type */ #define WSANO_DATA (WSABASEERR+1004) #define NO_DATA WSANO_DATA /* no address, look for MX record */ #define WSANO_ADDRESS WSANO_DATA #define NO_ADDRESS WSANO_ADDRESS /* Socket function prototypes */ #ifdef __cplusplus extern "C" { #endif SOCKET PASCAL FAR accept (SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); int PASCAL FAR bind (SOCKET s, const struct sockaddr FAR *addr, int namelen); int PASCAL FAR closesocket (SOCKET s); int PASCAL FAR connect (SOCKET s, const struct sockaddr FAR *name, int namelen); int PASCAL FAR ioctlsocket (SOCKET s, long cmd, u_long FAR *argp); int PASCAL FAR getpeername (SOCKET s, struct sockaddr FAR *name, int FAR * namelen); int PASCAL FAR getsockname (SOCKET s, struct sockaddr FAR *name, int FAR * namelen); int PASCAL FAR getsockopt (SOCKET s, int level, int optname, char FAR * optval, int FAR *optlen); u_long PASCAL FAR htonl (u_long hostlong); u_short PASCAL FAR htons (u_short hostshort); unsigned long PASCAL FAR inet_addr (const char FAR * cp); char FAR * PASCAL FAR inet_ntoa (struct in_addr in); int PASCAL FAR listen (SOCKET s, int backlog); u_long PASCAL FAR ntohl (u_long netlong); u_short PASCAL FAR ntohs (u_short netshort); int PASCAL FAR recv (SOCKET s, char FAR * buf, int len, int flags); int PASCAL FAR recvfrom (SOCKET s, char FAR * buf, int len, int flags, struct sockaddr FAR *from, int FAR * fromlen); int PASCAL FAR send (SOCKET s, const char FAR * buf, int len, int flags); int PASCAL FAR sendto (SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR *to, int tolen); int PASCAL FAR setsockopt (SOCKET s, int level, int optname, const char FAR * optval, int optlen); int PASCAL FAR shutdown (SOCKET s, int how); SOCKET PASCAL FAR socket (int af, int type, int protocol); /* Database function prototypes */ struct hostent FAR * PASCAL FAR gethostbyaddr(const char FAR * addr, int len, int type); struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name); int PASCAL FAR gethostname (char FAR * name, int namelen); struct servent FAR * PASCAL FAR getservbyport(int port, const char FAR * proto); struct servent FAR * PASCAL FAR getservbyname(const char FAR * name, const char FAR * proto); struct protoent FAR * PASCAL FAR getprotobynumber(int proto); struct protoent FAR * PASCAL FAR getprotobyname(const char FAR * name); #ifdef __cplusplus } #endif /* Microsoft Windows Extended data types */ typedef struct sockaddr SOCKADDR; typedef struct sockaddr *PSOCKADDR; typedef struct sockaddr FAR *LPSOCKADDR; typedef struct sockaddr_in SOCKADDR_IN; typedef struct sockaddr_in *PSOCKADDR_IN; typedef struct sockaddr_in FAR *LPSOCKADDR_IN; typedef struct linger LINGER; typedef struct linger *PLINGER; typedef struct linger FAR *LPLINGER; typedef struct in_addr IN_ADDR; typedef struct in_addr *PIN_ADDR; typedef struct in_addr FAR *LPIN_ADDR; typedef struct fd_set FD_SET; typedef struct fd_set *PFD_SET; typedef struct fd_set FAR *LPFD_SET; typedef struct hostent HOSTENT; typedef struct hostent *PHOSTENT; typedef struct hostent FAR *LPHOSTENT; typedef struct servent SERVENT; typedef struct servent *PSERVENT; typedef struct servent FAR *LPSERVENT; typedef struct protoent PROTOENT; typedef struct protoent *PPROTOENT; typedef struct protoent FAR *LPPROTOENT; typedef struct timeval TIMEVAL; typedef struct timeval *PTIMEVAL; typedef struct timeval FAR *LPTIMEVAL; #endif /* _WINSOCKAPI_ */ ================================================ FILE: source/neon_mathfun.c ================================================ /* NEON implementation of sin, cos, exp and log Inspired by Intel Approximate Math library, and based on the corresponding algorithms of the cephes math library */ /* Copyright (C) 2011 Julien Pommier This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. (this is the zlib license) */ #include "neon_mathfun.h" #define c_inv_mant_mask ~0x7f800000u #define c_cephes_SQRTHF 0.707106781186547524 #define c_cephes_log_p0 7.0376836292E-2 #define c_cephes_log_p1 - 1.1514610310E-1 #define c_cephes_log_p2 1.1676998740E-1 #define c_cephes_log_p3 - 1.2420140846E-1 #define c_cephes_log_p4 + 1.4249322787E-1 #define c_cephes_log_p5 - 1.6668057665E-1 #define c_cephes_log_p6 + 2.0000714765E-1 #define c_cephes_log_p7 - 2.4999993993E-1 #define c_cephes_log_p8 + 3.3333331174E-1 #define c_cephes_log_q1 -2.12194440e-4 #define c_cephes_log_q2 0.693359375 /* natural logarithm computed for 4 simultaneous float return NaN for x <= 0 */ v4sf log_ps(v4sf x) { v4sf one = vdupq_n_f32(1); x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */ v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0)); v4si ux = vreinterpretq_s32_f32(x); v4si emm0 = vshrq_n_s32(ux, 23); /* keep only the fractional part */ ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask)); ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f))); x = vreinterpretq_f32_s32(ux); emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f)); v4sf e = vcvtq_f32_s32(emm0); e = vaddq_f32(e, one); /* part2: if( x < SQRTHF ) { e -= 1; x = x + x - 1.0; } else { x = x - 1.0; } */ v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF)); v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask)); x = vsubq_f32(x, one); e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask))); x = vaddq_f32(x, tmp); v4sf z = vmulq_f32(x,x); v4sf y = vdupq_n_f32(c_cephes_log_p0); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7)); y = vmulq_f32(y, x); y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8)); y = vmulq_f32(y, x); y = vmulq_f32(y, z); tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1)); y = vaddq_f32(y, tmp); tmp = vmulq_f32(z, vdupq_n_f32(0.5f)); y = vsubq_f32(y, tmp); tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2)); x = vaddq_f32(x, y); x = vaddq_f32(x, tmp); x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN return x; } #define c_exp_hi 88.3762626647949f #define c_exp_lo -88.3762626647949f #define c_cephes_LOG2EF 1.44269504088896341 #define c_cephes_exp_C1 0.693359375 #define c_cephes_exp_C2 -2.12194440e-4 #define c_cephes_exp_p0 1.9875691500E-4 #define c_cephes_exp_p1 1.3981999507E-3 #define c_cephes_exp_p2 8.3334519073E-3 #define c_cephes_exp_p3 4.1665795894E-2 #define c_cephes_exp_p4 1.6666665459E-1 #define c_cephes_exp_p5 5.0000001201E-1 /* exp() computed for 4 float at once */ v4sf exp_ps(v4sf x) { v4sf tmp, fx; v4sf one = vdupq_n_f32(1); x = vminq_f32(x, vdupq_n_f32(c_exp_hi)); x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo)); /* express exp(x) as exp(g + n*log(2)) */ fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF)); /* perform a floorf */ tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx)); /* if greater, substract 1 */ v4su mask = vcgtq_f32(tmp, fx); mask = vandq_u32(mask, vreinterpretq_u32_f32(one)); fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask)); tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1)); v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2)); x = vsubq_f32(x, tmp); x = vsubq_f32(x, z); static const float cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 }; v4sf y = vld1q_dup_f32(cephes_exp_p+0); v4sf c1 = vld1q_dup_f32(cephes_exp_p+1); v4sf c2 = vld1q_dup_f32(cephes_exp_p+2); v4sf c3 = vld1q_dup_f32(cephes_exp_p+3); v4sf c4 = vld1q_dup_f32(cephes_exp_p+4); v4sf c5 = vld1q_dup_f32(cephes_exp_p+5); y = vmulq_f32(y, x); z = vmulq_f32(x,x); y = vaddq_f32(y, c1); y = vmulq_f32(y, x); y = vaddq_f32(y, c2); y = vmulq_f32(y, x); y = vaddq_f32(y, c3); y = vmulq_f32(y, x); y = vaddq_f32(y, c4); y = vmulq_f32(y, x); y = vaddq_f32(y, c5); y = vmulq_f32(y, z); y = vaddq_f32(y, x); y = vaddq_f32(y, one); /* build 2^n */ int32x4_t mm; mm = vcvtq_s32_f32(fx); mm = vaddq_s32(mm, vdupq_n_s32(0x7f)); mm = vshlq_n_s32(mm, 23); v4sf pow2n = vreinterpretq_f32_s32(mm); y = vmulq_f32(y, pow2n); return y; } #define c_minus_cephes_DP1 -0.78515625 #define c_minus_cephes_DP2 -2.4187564849853515625e-4 #define c_minus_cephes_DP3 -3.77489497744594108e-8 #define c_sincof_p0 -1.9515295891E-4 #define c_sincof_p1 8.3321608736E-3 #define c_sincof_p2 -1.6666654611E-1 #define c_coscof_p0 2.443315711809948E-005 #define c_coscof_p1 -1.388731625493765E-003 #define c_coscof_p2 4.166664568298827E-002 #define c_cephes_FOPI 1.27323954473516 // 4 / M_PI /* evaluation of 4 sines & cosines at once. The code is the exact rewriting of the cephes sinf function. Precision is excellent as long as x < 8192 (I did not bother to take into account the special handling they have for greater values -- it does not return garbage for arguments over 8192, though, but the extra precision is missing). Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the surprising but correct result. Note also that when you compute sin(x), cos(x) is available at almost no extra price so both sin_ps and cos_ps make use of sincos_ps.. */ void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x v4sf xmm1, xmm2, xmm3, y; v4su emm2; v4su sign_mask_sin, sign_mask_cos; sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0)); x = vabsq_f32(x); /* scale by 4/Pi */ y = vmulq_f32(x, vdupq_n_f32(c_cephes_FOPI)); /* store the integer part of y in mm0 */ emm2 = vcvtq_u32_f32(y); /* j=(j+1) & (~1) (see the cephes sources) */ emm2 = vaddq_u32(emm2, vdupq_n_u32(1)); emm2 = vandq_u32(emm2, vdupq_n_u32(~1)); y = vcvtq_f32_u32(emm2); /* get the polynom selection mask there is one polynom for 0 <= x <= Pi/4 and another one for Pi/4 typedef float32x4_t v4sf; // vector of 4 float typedef uint32x4_t v4su; // vector of 4 uint32 typedef int32x4_t v4si; // vector of 4 uint32 v4sf log_ps(v4sf x); v4sf exp_ps(v4sf x); void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos); v4sf sin_ps(v4sf x); v4sf cos_ps(v4sf x); #endif ================================================ FILE: source/net.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net.h -- quake's interface to the networking layer struct qsockaddr { short sa_family; unsigned char sa_data[14]; }; #define NET_NAMELEN 64 #define NET_MAXMESSAGE 8192 #define NET_HEADERSIZE (2 * sizeof(unsigned int)) #define NET_DATAGRAMSIZE (MAX_DATAGRAM + NET_HEADERSIZE) // NetHeader flags #define NETFLAG_LENGTH_MASK 0x0000ffff #define NETFLAG_DATA 0x00010000 #define NETFLAG_ACK 0x00020000 #define NETFLAG_NAK 0x00040000 #define NETFLAG_EOM 0x00080000 #define NETFLAG_UNRELIABLE 0x00100000 #define NETFLAG_CTL 0x80000000 #define NET_PROTOCOL_VERSION 3 // This is the network info/connection protocol. It is used to find Quake // servers, get info about them, and connect to them. Once connected, the // Quake game protocol (documented elsewhere) is used. // // // General notes: // game_name is currently always "QUAKE", but is there so this same protocol // can be used for future games as well; can you say Quake2? // // CCREQ_CONNECT // string game_name "QUAKE" // byte net_protocol_version NET_PROTOCOL_VERSION // // CCREQ_SERVER_INFO // string game_name "QUAKE" // byte net_protocol_version NET_PROTOCOL_VERSION // // CCREQ_PLAYER_INFO // byte player_number // // CCREQ_RULE_INFO // string rule // // // // CCREP_ACCEPT // long port // // CCREP_REJECT // string reason // // CCREP_SERVER_INFO // string server_address // string host_name // string level_name // byte current_players // byte max_players // byte protocol_version NET_PROTOCOL_VERSION // // CCREP_PLAYER_INFO // byte player_number // string name // long colors // long frags // long connect_time // string address // // CCREP_RULE_INFO // string rule // string value // note: // There are two address forms used above. The short form is just a // port number. The address that goes along with the port is defined as // "whatever address you receive this reponse from". This lets us use // the host OS to solve the problem of multiple host addresses (possibly // with no routing between them); the host will use the right address // when we reply to the inbound connection request. The long from is // a full address and port in a string. It is used for returning the // address of a server that is not running locally. #define CCREQ_CONNECT 0x01 #define CCREQ_SERVER_INFO 0x02 #define CCREQ_PLAYER_INFO 0x03 #define CCREQ_RULE_INFO 0x04 #define CCREP_ACCEPT 0x81 #define CCREP_REJECT 0x82 #define CCREP_SERVER_INFO 0x83 #define CCREP_PLAYER_INFO 0x84 #define CCREP_RULE_INFO 0x85 // ProQuake -- MOD Compatibility #define MOD_NONE 0x00 // NetQuake style #define MOD_PROQUAKE 0x01 // ProQuake style #define MOD_QSMACK 0x02 // QSmack style (?) typedef struct qsocket_s { struct qsocket_s *next; double connecttime; double lastMessageTime; double lastSendTime; bool disconnected; bool canSend; bool sendNext; int driver; int landriver; int socket; void *driverdata; unsigned int ackSequence; unsigned int sendSequence; unsigned int unreliableSendSequence; int sendMessageLength; byte sendMessage [NET_MAXMESSAGE]; unsigned int receiveSequence; unsigned int unreliableReceiveSequence; int receiveMessageLength; byte receiveMessage [NET_MAXMESSAGE]; struct qsockaddr addr; char address[NET_NAMELEN]; // ProQuake protocol additions byte proquake_connection; // 0: NetQuake, 1: ProQuake, 2: QSmack (ProQuake) byte proquake_version; // = floor(version * 10) (must fit in one byte) (ProQuake) byte proquake_flags; // Connection flags (ProQuake 3 behaviour) int client_port; // Client Port used (ProQuake 3 behaviour) bool net_wait; // NAT Fix for servers (ProQuake 3.4) } qsocket_t; extern qsocket_t *net_activeSockets; extern qsocket_t *net_freeSockets; extern int net_numsockets; typedef struct { char *name; bool initialized; int controlSock; int (*Init) (void); void (*Shutdown) (void); void (*Listen) (bool state); int (*OpenSocket) (int port); int (*CloseSocket) (int socket); int (*Connect) (int socket, struct qsockaddr *addr); int (*CheckNewConnections) (void); int (*Read) (int socket, byte *buf, int len, struct qsockaddr *addr); int (*Write) (int socket, byte *buf, int len, struct qsockaddr *addr); int (*Broadcast) (int socket, byte *buf, int len); char * (*AddrToString) (struct qsockaddr *addr); int (*StringToAddr) (char *string, struct qsockaddr *addr); int (*GetSocketAddr) (int socket, struct qsockaddr *addr); int (*GetNameFromAddr) (struct qsockaddr *addr, char *name); int (*GetAddrFromName) (char *name, struct qsockaddr *addr); int (*AddrCompare) (struct qsockaddr *addr1, struct qsockaddr *addr2); int (*GetSocketPort) (struct qsockaddr *addr); int (*SetSocketPort) (struct qsockaddr *addr, int port); } net_landriver_t; #define MAX_NET_DRIVERS 8 extern int net_numlandrivers; extern net_landriver_t net_landrivers[MAX_NET_DRIVERS]; typedef struct { char *name; bool initialized; int (*Init) (void); void (*Listen) (bool state); void (*SearchForHosts) (bool xmit); qsocket_t *(*Connect) (char *host); qsocket_t *(*CheckNewConnections) (void); int (*QGetMessage) (qsocket_t *sock); int (*QSendMessage) (qsocket_t *sock, sizebuf_t *data); int (*SendUnreliableMessage) (qsocket_t *sock, sizebuf_t *data); bool (*CanSendMessage) (qsocket_t *sock); bool (*CanSendUnreliableMessage) (qsocket_t *sock); void (*Close) (qsocket_t *sock); void (*Shutdown) (void); int controlSock; } net_driver_t; extern int net_numdrivers; extern net_driver_t net_drivers[MAX_NET_DRIVERS]; extern int DEFAULTnet_hostport; extern int net_hostport; extern int net_driverlevel; extern cvar_t hostname; extern char playername[]; extern int playercolor; extern int messagesSent; extern int messagesReceived; extern int unreliableMessagesSent; extern int unreliableMessagesReceived; qsocket_t *NET_NewQSocket (void); void NET_FreeQSocket(qsocket_t *); double SetNetTime(void); #define HOSTCACHESIZE 8 typedef struct { char name[16]; char map[16]; char cname[32]; int users; int maxusers; int driver; int ldriver; struct qsockaddr addr; } hostcache_t; extern int hostCacheCount; extern hostcache_t hostcache[HOSTCACHESIZE]; #if !defined(_WIN32 ) && !defined (__linux__) && !defined (__sun__) #ifndef htonl extern unsigned long htonl (unsigned long hostlong); #endif #ifndef htons extern unsigned short htons (unsigned short hostshort); #endif #ifndef ntohl extern unsigned long ntohl (unsigned long netlong); #endif #ifndef ntohs extern unsigned short ntohs (unsigned short netshort); #endif #endif #ifdef IDGODS bool IsID(struct qsockaddr *addr); #endif //============================================================================ // // public network functions // //============================================================================ extern double net_time; extern sizebuf_t net_message; extern int net_activeconnections; void NET_Init (void); void NET_Shutdown (void); struct qsocket_s *NET_CheckNewConnections (void); // returns a new connection number if there is one pending, else -1 struct qsocket_s *NET_Connect (char *host); // called by client to connect to a host. Returns -1 if not able to bool NET_CanSendMessage (qsocket_t *sock); // Returns true or false if the given qsocket can currently accept a // message to be transmitted. int NET_GetMessage (struct qsocket_s *sock); // returns data in net_message sizebuf // returns 0 if no data is waiting // returns 1 if a message was received // returns 2 if an unreliable message was received // returns -1 if the connection died int NET_SendMessage (struct qsocket_s *sock, sizebuf_t *data); int NET_SendUnreliableMessage (struct qsocket_s *sock, sizebuf_t *data); // returns 0 if the message connot be delivered reliably, but the connection // is still considered valid // returns 1 if the message was sent properly // returns -1 if the connection died int NET_SendToAll(sizebuf_t *data, int blocktime); // This is a reliable *blocking* send to all attached clients. void NET_Close (struct qsocket_s *sock); // if a dead connection is returned by a get or send function, this function // should be called when it is convenient // Server calls when a client is kicked off for a game related misbehavior // like an illegal protocal conversation. Client calls when disconnecting // from a server. // A netcon_t number will not be reused until this function is called for it void NET_Poll(void); typedef struct _PollProcedure { struct _PollProcedure *next; double nextTime; void (*procedure)(); void *arg; } PollProcedure; void SchedulePollProcedure(PollProcedure *pp, double timeOffset); extern bool tcpipAvailable; extern char my_tcpip_address[NET_NAMELEN]; extern bool slistInProgress; extern bool slistSilent; extern bool slistLocal; void NET_Slist_f (void); ================================================ FILE: source/net_adhoc.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_adhoc.h int AdHoc_Init (void); void AdHoc_Shutdown (void); void AdHoc_Listen (bool state); int AdHoc_OpenSocket (int port); int AdHoc_CloseSocket (int socket); int AdHoc_Connect (int socket, struct qsockaddr *addr); int AdHoc_CheckNewConnections (void); int AdHoc_Read (int socket, byte *buf, int len, struct qsockaddr *addr); int AdHoc_Write (int socket, byte *buf, int len, struct qsockaddr *addr); int AdHoc_Broadcast (int socket, byte *buf, int len); char *AdHoc_AddrToString (struct qsockaddr *addr); int AdHoc_StringToAddr (char *string, struct qsockaddr *addr); int AdHoc_GetSocketAddr (int socket, struct qsockaddr *addr); int AdHoc_GetNameFromAddr (struct qsockaddr *addr, char *name); int AdHoc_GetAddrFromName (char *name, struct qsockaddr *addr); int AdHoc_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int AdHoc_GetSocketPort (struct qsockaddr *addr); int AdHoc_SetSocketPort (struct qsockaddr *addr, int port); ================================================ FILE: source/net_adhoc_psp2.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_adhoc.c #include "quakedef.h" #include "net_adhoc.h" #include #include #include #ifdef __sun__ #include #endif #ifdef NeXT #include #endif extern cvar_t hostname; extern void Log (const char *format, ...); #define ADHOC_NET 29 typedef struct sockaddr_adhoc { char len; short family; uint16_t port; char mac[6]; char zero[6]; } sockaddr_adhoc; static int net_acceptsocket = -1; // socket for fielding new connections static int net_controlsocket; static int net_broadcastsocket = 0; static struct qsockaddr broadcastaddr; #include "net_adhoc.h" //============================================================================= int AdHoc_Init (void) { Log("ADHoc_Init called..."); struct qsockaddr addr; char *colon; // if the quake hostname isn't set, set it to player nickname if (!strcmp(hostname.string, "UNNAMED")) { SceAppUtilInitParam init_param; SceAppUtilBootParam boot_param; memset(&init_param, 0, sizeof(SceAppUtilInitParam)); memset(&boot_param, 0, sizeof(SceAppUtilBootParam)); sceAppUtilInit(&init_param, &boot_param); char nick[SCE_SYSTEM_PARAM_USERNAME_MAXSIZE]; sceAppUtilSystemParamGetString(SCE_SYSTEM_PARAM_ID_USERNAME, nick, SCE_SYSTEM_PARAM_USERNAME_MAXSIZE); Cvar_Set ("hostname", nick); } if ((net_controlsocket = AdHoc_OpenSocket (0)) == -1) Sys_Error("AdHoc_Init: Unable to open control socket\n"); ((struct sockaddr_adhoc *)&broadcastaddr)->family = ADHOC_NET; int i; for(i=0; i<6; i++) ((struct sockaddr_adhoc *)&broadcastaddr)->mac[i] = 0xFF; ((struct sockaddr_adhoc *)&broadcastaddr)->port = sceNetHtons(net_hostport); AdHoc_GetSocketAddr (net_controlsocket, &addr); strcpy(my_tcpip_address, AdHoc_AddrToString (&addr)); colon = strrchr (my_tcpip_address, ':'); if (colon) *colon = 0; AdHoc_Listen(1); Con_Printf("AdHoc Initialized as IP %s\n", my_tcpip_address); tcpipAvailable = true; return net_controlsocket; } //============================================================================= void AdHoc_Shutdown (void) { Log("AdHoc_Shutdown"); AdHoc_Listen (false); AdHoc_CloseSocket (net_controlsocket); sceNetAdhocctlTerm(); sceNetAdhocTerm(); } //============================================================================= void AdHoc_Listen (bool state) { Log("AdHoc_Listen"); // enable listening if (state) { if (net_acceptsocket != -1) return; if ((net_acceptsocket = AdHoc_OpenSocket (net_hostport)) == -1) Sys_Error ("AdHoc_Listen: Unable to open accept socket\n"); return; } // disable listening if (net_acceptsocket == -1) return; AdHoc_CloseSocket (net_acceptsocket); net_acceptsocket = -1; } //============================================================================= int AdHoc_OpenSocket (int port) { Log("AdHoc_OpenSocket(%ld)", port); uint8_t mac[8]; sceNetAdhocctlGetEtherAddr((SceNetEtherAddr *)mac); int rc = sceNetAdhocPdpCreate((SceNetEtherAddr *)mac, port, 0x2000, 0); if(rc < 0) return -1; return rc; } //============================================================================= int AdHoc_CloseSocket (int socket) { Log("AdHoc_CloseSocket"); if (socket == net_broadcastsocket) net_broadcastsocket = 0; return sceNetAdhocPdpDelete(socket, 0); } //============================================================================= int AdHoc_Connect (int socket, struct qsockaddr *addr) { Log("AdHoc_Connect"); return 0; } //============================================================================= static SceNetAdhocPdpStat gPdpStat; SceNetAdhocPdpStat *findPdpStat(int socket, SceNetAdhocPdpStat *pdpStat) { if(socket == pdpStat->id) { memcpy(&gPdpStat, pdpStat, sizeof(SceNetAdhocPdpStat)); return &gPdpStat; } if(pdpStat->next) return findPdpStat(socket, pdpStat->next); return (SceNetAdhocPdpStat *)-1; } int AdHoc_CheckNewConnections (void) { Log("AdHoc_CheckNewConnections"); SceNetAdhocPdpStat pdpStat[20]; int len = sizeof(SceNetAdhocPdpStat) * 20; if (net_acceptsocket == -1) return -1; int err = sceNetAdhocGetPdpStat(&len, pdpStat); if(err < 0) return -1; SceNetAdhocPdpStat *tempPdp = findPdpStat(net_acceptsocket, pdpStat); if (tempPdp < 0) return -1; else if (tempPdp->rcv_sb_cc > 0) return net_acceptsocket; return -1; } //============================================================================= int AdHoc_Read (int socket, byte *buf, int len, struct qsockaddr *addr) { unsigned short port; int datalength = len; int ret; ret = sceNetAdhocPdpRecv(socket, (SceNetEtherAddr *)((sockaddr_adhoc *)addr)->mac, &port, buf, &datalength, 0, 1); Log("AdHoc_Read returned %ld",ret); if (ret == SCE_ERROR_NET_ADHOC_WOULD_BLOCK) return 0; else if (ret < 0) return -1; ((sockaddr_adhoc *)addr)->port = port; return datalength; } //============================================================================= int AdHoc_MakeSocketBroadcastCapable (int socket) { Log("AdHoc_MakeSocketBroadcastCapable"); net_broadcastsocket = socket; return 0; } //============================================================================= int AdHoc_Broadcast (int socket, byte *buf, int len) { Log("AdHoc_Broadcast"); int ret; if (socket != net_broadcastsocket) { if (net_broadcastsocket != 0) Sys_Error("Attempted to use multiple broadcasts sockets\n"); ret = AdHoc_MakeSocketBroadcastCapable (socket); if (ret == -1) { Con_Printf("Unable to make socket broadcast capable\n"); return ret; } } return AdHoc_Write (socket, buf, len, &broadcastaddr); } //============================================================================= int AdHoc_Write (int socket, byte *buf, int len, struct qsockaddr *addr) { int ret; ret = sceNetAdhocPdpSend(socket, (SceNetEtherAddr *)((sockaddr_adhoc *)addr)->mac, ((sockaddr_adhoc *)addr)->port, buf, len, 0, 1); Log("AdHoc_Write returned %ld",ret); if (ret == SCE_ERROR_NET_ADHOC_WOULD_BLOCK) return 0; else if (ret < 0) return -1; return ret; } //============================================================================= char *AdHoc_AddrToString (struct qsockaddr *addr) { static char buffer[22]; int haddr; sceNetEtherNtostr((SceNetEtherAddr *)((sockaddr_adhoc *)addr)->mac, buffer, 22); sprintf(buffer + strlen(buffer), ":%d", ((sockaddr_adhoc *)addr)->port); Log("AdHoc_AddrToString returned %s",buffer); return buffer; } //============================================================================= int AdHoc_StringToAddr (char *string, struct qsockaddr *addr) { Log("AdHoc_StringToAddr(%s)",string); int ha1, ha2, ha3, ha4, ha5, ha6, hp; int r = sscanf(string, "%x:%x:%x:%x:%x:%x:%d", &ha1, &ha2, &ha3, &ha4, &ha5, &ha6, &hp); if (r < 7) hp = net_hostport; addr->sa_family = ADHOC_NET; ((struct sockaddr_adhoc *)addr)->mac[0] = ha1 & 0xFF; ((struct sockaddr_adhoc *)addr)->mac[1] = ha2 & 0xFF; ((struct sockaddr_adhoc *)addr)->mac[2] = ha3 & 0xFF; ((struct sockaddr_adhoc *)addr)->mac[3] = ha4 & 0xFF; ((struct sockaddr_adhoc *)addr)->mac[4] = ha5 & 0xFF; ((struct sockaddr_adhoc *)addr)->mac[5] = ha6 & 0xFF; ((struct sockaddr_adhoc *)addr)->port = hp & 0xFFFF; return 0; } //============================================================================= int AdHoc_GetSocketAddr (int socket, struct qsockaddr *addr) { Log("AdHoc_GetSocketAddr"); SceNetAdhocPdpStat pdpStat[20]; int len = sizeof(SceNetAdhocPdpStat) * 20; int err = sceNetAdhocGetPdpStat(&len, pdpStat); if(err<0) return -1; SceNetAdhocPdpStat *tempPdp = findPdpStat(socket, pdpStat); if(tempPdp < 0) return -1; memcpy(((struct sockaddr_adhoc *)addr)->mac, &tempPdp->laddr, 6); ((struct sockaddr_adhoc *)addr)->port = tempPdp->lport; addr->sa_family = ADHOC_NET; return 0; } //============================================================================= int AdHoc_GetNameFromAddr (struct qsockaddr *addr, char *name) { strcpy (name, AdHoc_AddrToString (addr)); return 0; } //============================================================================= int AdHoc_GetAddrFromName(char *name, struct qsockaddr *addr) { Log("AdHoc_GetAddrFromName(%s)", name); return AdHoc_StringToAddr(name, addr); } //============================================================================= int AdHoc_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (memcmp(((struct sockaddr_adhoc *)addr1)->mac, ((struct sockaddr_adhoc *)addr2)->mac, 6) != 0) return -1; if (((struct sockaddr_adhoc *)addr1)->port != ((struct sockaddr_adhoc *)addr2)->port) return 1; Log("AdHoc_AddrCompare returned 0"); return 0; } //============================================================================= int AdHoc_GetSocketPort (struct qsockaddr *addr) { Log("AdHoc_GetSocketPort"); return sceNetNtohs(((struct sockaddr_adhoc *)addr)->port); } int AdHoc_SetSocketPort (struct qsockaddr *addr, int port) { Log("AdHoc_SetSocketPort"); ((struct sockaddr_adhoc *)addr)->port = sceNetHtons(port); return 0; } //============================================================================= ================================================ FILE: source/net_dgrm.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_dgrm.c // This is enables a simple IP banning mechanism //#define BAN_TEST #ifdef BAN_TEST #if defined(_WIN32) #include #elif defined (NeXT) #include #include #else #define AF_INET 2 /* internet */ struct in_addr { union { struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { unsigned short s_w1,s_w2; } S_un_w; unsigned long S_addr; } S_un; }; #define s_addr S_un.S_addr /* can be used for most tcp & ip code */ struct sockaddr_in { short sin_family; unsigned short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; //char *inet_ntoa(struct in_addr in); #define inet_ntoa(x) 1 //unsigned long inet_addr(const char *cp); #define inet_addr(x) 1 #endif #endif // BAN_TEST #include "quakedef.h" #include "net_dgrm.h" extern cvar_t pq_password; // these two macros are to make the code more readable #define sfunc net_landrivers[sock->landriver] #define dfunc net_landrivers[net_landriverlevel] static int net_landriverlevel; /* statistic counters */ int packetsSent = 0; int packetsReSent = 0; int packetsReceived = 0; int receivedDuplicateCount = 0; int shortPacketCount = 0; int droppedDatagrams; static int myDriverLevel; struct { unsigned int length; unsigned int sequence; byte data[MAX_DATAGRAM]; } packetBuffer; extern int m_return_state; //extern int m_state; extern bool m_return_onerror; extern char m_return_reason[32]; // JPG - recognize ip:port void Strip_Port(char *ch) { int occurrances = 0; char *last_valid_ch = NULL; while (ch) { ch = strchr(ch, ':'); if (ch){ occurrances++; last_valid_ch = ch; ch++; } } if ((occurrances == 1) || (occurrances == 6)) { ch = last_valid_ch; int old_port = net_hostport; sscanf(ch + 1, "%d", &net_hostport); if (proto_idx == 0) { for (; ch[-1] == ' '; ch--); *ch = 0; } if (net_hostport != old_port) Con_Printf("Setting port to %d\n", net_hostport); } else //R00k if not specifying port then use default port { net_hostport = DEFAULTnet_hostport; Con_Printf("Using port %d\n", net_hostport); } } #ifdef DEBUG char *StrAddr (struct qsockaddr *addr) { static char buf[34]; byte *p = (byte *)addr; int n; for (n = 0; n < 16; n++) sprintf (buf + n * 2, "%02x", *p++); return buf; } #endif #ifdef BAN_TEST unsigned long banAddr = 0x00000000; unsigned long banMask = 0xffffffff; void NET_Ban_f (void) { char addrStr [32]; char maskStr [32]; void (*print) (char *fmt, ...); if (cmd_source == src_command) { if (!sv.active) { Cmd_ForwardToServer (); return; } print = Con_Printf; } else { if (pr_global_struct->deathmatch && !host_client->privileged) return; print = SV_ClientPrintf; } switch (Cmd_Argc ()) { case 1: if (((struct in_addr *)&banAddr)->s_addr) { strcpy(addrStr, inet_ntoa(*(struct in_addr *)&banAddr)); strcpy(maskStr, inet_ntoa(*(struct in_addr *)&banMask)); print("Banning %s [%s]\n", addrStr, maskStr); } else print("Banning not active\n"); break; case 2: if (strcasecmp(Cmd_Argv(1), "off") == 0) banAddr = 0x00000000; else banAddr = inet_addr(Cmd_Argv(1)); banMask = 0xffffffff; break; case 3: banAddr = inet_addr(Cmd_Argv(1)); banMask = inet_addr(Cmd_Argv(2)); break; default: print("BAN ip_address [mask]\n"); break; } } #endif // JPG 3.00 - this code appears multiple times, so factor it out qsocket_t *Datagram_Reject(char *message, int acceptsock, struct qsockaddr *addr) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_REJECT); MSG_WriteString(&net_message, message); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write(acceptsock, net_message.data, net_message.cursize, addr); SZ_Clear(&net_message); return NULL; } int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; #ifdef DEBUG if (data->cursize == 0) Sys_Error("Datagram_SendMessage: zero length message\n"); if (data->cursize > NET_MAXMESSAGE) Sys_Error("Datagram_SendMessage: message too big %u\n", data->cursize); if (sock->canSend == false) Sys_Error("SendMessage: called with canSend == false\n"); #endif memcpy(sock->sendMessage, data->data, data->cursize); sock->sendMessageLength = data->cursize; if (data->cursize <= MAX_DATAGRAM) { dataLen = data->cursize; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence++); memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->canSend = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } int SendMessageNext (qsocket_t *sock) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence++); memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->sendNext = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsSent++; return 1; } int ReSendMessage (qsocket_t *sock) { unsigned int packetLen; unsigned int dataLen; unsigned int eom; if (sock->sendMessageLength <= MAX_DATAGRAM) { dataLen = sock->sendMessageLength; eom = NETFLAG_EOM; } else { dataLen = MAX_DATAGRAM; eom = 0; } packetLen = NET_HEADERSIZE + dataLen; packetBuffer.length = BigLong(packetLen | (NETFLAG_DATA | eom)); packetBuffer.sequence = BigLong(sock->sendSequence - 1); memcpy (packetBuffer.data, sock->sendMessage, dataLen); sock->sendNext = false; if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; sock->lastSendTime = net_time; packetsReSent++; return 1; } bool Datagram_CanSendMessage (qsocket_t *sock) { if (sock->sendNext) SendMessageNext (sock); return sock->canSend; } bool Datagram_CanSendUnreliableMessage (qsocket_t *sock) { return true; } int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { int packetLen; #ifdef DEBUG if (data->cursize == 0) Sys_Error("Datagram_SendUnreliableMessage: zero length message\n"); if (data->cursize > MAX_DATAGRAM) Sys_Error("Datagram_SendUnreliableMessage: message too big %u\n", data->cursize); #endif packetLen = NET_HEADERSIZE + data->cursize; packetBuffer.length = BigLong(packetLen | NETFLAG_UNRELIABLE); packetBuffer.sequence = BigLong(sock->unreliableSendSequence++); memcpy (packetBuffer.data, data->data, data->cursize); if (sfunc.Write (sock->socket, (byte *)&packetBuffer, packetLen, &sock->addr) == -1) return -1; packetsSent++; return 1; } int Datagram_GetMessage (qsocket_t *sock) { unsigned int length; unsigned int flags; int ret = 0; struct qsockaddr readaddr; unsigned int sequence; unsigned int count; if (!sock->canSend) if ((net_time - sock->lastSendTime) > 1.0) ReSendMessage (sock); while(1) { length = sfunc.Read (sock->socket, (byte *)&packetBuffer, NET_DATAGRAMSIZE, &readaddr); // if ((rand() & 255) > 220) // continue; if (length == 0) break; if (length == -1) { Con_Printf("Read error\n"); return -1; } // ProQuake: NAT support for Servers if (!sock->net_wait && sfunc.AddrCompare(&readaddr, &sock->addr) != 0) { #ifdef DEBUG Con_DPrintf("Forged packet received\n"); Con_DPrintf("Expected: %s\n", StrAddr (&sock->addr)); Con_DPrintf("Received: %s\n", StrAddr (&readaddr)); #endif continue; } if (length < NET_HEADERSIZE) { shortPacketCount++; continue; } length = BigLong(packetBuffer.length); flags = length & (~NETFLAG_LENGTH_MASK); length &= NETFLAG_LENGTH_MASK; if (length > NET_DATAGRAMSIZE) { Con_Printf("Invalid length\n"); return -1; } if (flags & NETFLAG_CTL) continue; sequence = BigLong(packetBuffer.sequence); packetsReceived++; // ProQuake if (sock->net_wait) { sock->addr = readaddr; strcpy(sock->address, sfunc.AddrToString(&readaddr)); sock->net_wait = false; } if (flags & NETFLAG_UNRELIABLE) { if (sequence < sock->unreliableReceiveSequence) { Con_DPrintf("Got a stale datagram\n"); ret = 0; break; } if (sequence != sock->unreliableReceiveSequence) { count = sequence - sock->unreliableReceiveSequence; droppedDatagrams += count; Con_DPrintf("Dropped %u datagram(s)\n", count); } sock->unreliableReceiveSequence = sequence + 1; length -= NET_HEADERSIZE; SZ_Clear (&net_message); SZ_Write (&net_message, packetBuffer.data, length); ret = 2; break; } if (flags & NETFLAG_ACK) { if (sequence != (sock->sendSequence - 1)) { Con_DPrintf("Stale ACK received\n"); continue; } if (sequence == sock->ackSequence) { sock->ackSequence++; if (sock->ackSequence != sock->sendSequence) Con_DPrintf("ack sequencing error\n"); } else { Con_DPrintf("Duplicate ACK received\n"); continue; } sock->sendMessageLength -= MAX_DATAGRAM; if (sock->sendMessageLength > 0) { memcpy(sock->sendMessage, sock->sendMessage+MAX_DATAGRAM, sock->sendMessageLength); sock->sendNext = true; } else { sock->sendMessageLength = 0; sock->canSend = true; } continue; } if (flags & NETFLAG_DATA) { packetBuffer.length = BigLong(NET_HEADERSIZE | NETFLAG_ACK); packetBuffer.sequence = BigLong(sequence); sfunc.Write (sock->socket, (byte *)&packetBuffer, NET_HEADERSIZE, &readaddr); if (sequence != sock->receiveSequence) { receivedDuplicateCount++; continue; } sock->receiveSequence++; length -= NET_HEADERSIZE; if (flags & NETFLAG_EOM) { SZ_Clear(&net_message); SZ_Write(&net_message, sock->receiveMessage, sock->receiveMessageLength); SZ_Write(&net_message, packetBuffer.data, length); sock->receiveMessageLength = 0; ret = 1; break; } memcpy(sock->receiveMessage + sock->receiveMessageLength, packetBuffer.data, length); sock->receiveMessageLength += length; continue; } } if (sock->sendNext) SendMessageNext (sock); return ret; } void PrintStats(qsocket_t *s) { Con_Printf("canSend = %4u \n", s->canSend); Con_Printf("sendSeq = %4u ", s->sendSequence); Con_Printf("recvSeq = %4u \n", s->receiveSequence); Con_Printf("\n"); } void NET_Stats_f (void) { qsocket_t *s; if (Cmd_Argc () == 1) { Con_Printf("unreliable messages sent = %i\n", unreliableMessagesSent); Con_Printf("unreliable messages recv = %i\n", unreliableMessagesReceived); Con_Printf("reliable messages sent = %i\n", messagesSent); Con_Printf("reliable messages received = %i\n", messagesReceived); Con_Printf("packetsSent = %i\n", packetsSent); Con_Printf("packetsReSent = %i\n", packetsReSent); Con_Printf("packetsReceived = %i\n", packetsReceived); Con_Printf("receivedDuplicateCount = %i\n", receivedDuplicateCount); Con_Printf("shortPacketCount = %i\n", shortPacketCount); Con_Printf("droppedDatagrams = %i\n", droppedDatagrams); } else if (strcmp(Cmd_Argv(1), "*") == 0) { for (s = net_activeSockets; s; s = s->next) PrintStats(s); for (s = net_freeSockets; s; s = s->next) PrintStats(s); } else { for (s = net_activeSockets; s; s = s->next) if (strcasecmp(Cmd_Argv(1), s->address) == 0) break; if (s == NULL) for (s = net_freeSockets; s; s = s->next) if (strcasecmp(Cmd_Argv(1), s->address) == 0) break; if (s == NULL) return; PrintStats(s); } } static bool testInProgress = false; static int testPollCount; static int testDriver; static int testSocket; static void Test_Poll(void); PollProcedure testPollProcedure = {NULL, 0.0, Test_Poll}; static void Test_Poll(void) { struct qsockaddr clientaddr; int control; int len; char name[32]; char address[64]; int colors; int frags; int connectTime; byte playerNumber; net_landriverlevel = testDriver; while (1) { len = dfunc.Read (testSocket, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) break; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) break; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) break; if ((control & NETFLAG_LENGTH_MASK) != len) break; if (MSG_ReadByte() != CCREP_PLAYER_INFO) Sys_Error("Unexpected repsonse to Player Info request\n"); playerNumber = MSG_ReadByte(); strcpy(name, MSG_ReadString()); colors = MSG_ReadLong(); frags = MSG_ReadLong(); connectTime = MSG_ReadLong(); strcpy(address, MSG_ReadString()); Con_Printf("%s\n frags:%3i colors:%u %u time:%u\n %s\n", name, frags, colors >> 4, colors & 0x0f, connectTime / 60, address); } testPollCount--; if (testPollCount) { SchedulePollProcedure(&testPollProcedure, 0.1); } else { dfunc.CloseSocket(testSocket); testInProgress = false; } } static void Test_f (void) { char *host; int n; int max = MAX_SCOREBOARD; struct qsockaddr sendaddr; if (testInProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; max = hostcache[n].maxusers; memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) return; JustDoIt: testSocket = dfunc.OpenSocket(0); if (testSocket == -1) return; testInProgress = true; testPollCount = 20; testDriver = net_landriverlevel; for (n = 0; n < max; n++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_PLAYER_INFO); MSG_WriteByte(&net_message, n); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (testSocket, net_message.data, net_message.cursize, &sendaddr); } SZ_Clear(&net_message); SchedulePollProcedure(&testPollProcedure, 0.1); } static bool test2InProgress = false; static int test2Driver; static int test2Socket; static void Test2_Poll(void); PollProcedure test2PollProcedure = {NULL, 0.0, Test2_Poll}; static void Test2_Poll(void) { struct qsockaddr clientaddr; int control; int len; char name[256]; char value[256]; net_landriverlevel = test2Driver; name[0] = 0; len = dfunc.Read (test2Socket, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) goto Reschedule; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) goto Error; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) goto Error; if ((control & NETFLAG_LENGTH_MASK) != len) goto Error; if (MSG_ReadByte() != CCREP_RULE_INFO) goto Error; strcpy(name, MSG_ReadString()); if (name[0] == 0) goto Done; strcpy(value, MSG_ReadString()); Con_Printf("%-16.16s %-16.16s\n", name, value); SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, name); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (test2Socket, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); Reschedule: SchedulePollProcedure(&test2PollProcedure, 0.05); return; Error: Con_Printf("Unexpected repsonse to Rule Info request\n"); Done: dfunc.CloseSocket(test2Socket); test2InProgress = false; return; } static void Test2_f (void) { char *host; int n; struct qsockaddr sendaddr; if (test2InProgress) return; host = Cmd_Argv (1); if (host && hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (strcasecmp (host, hostcache[n].name) == 0) { if (hostcache[n].driver != myDriverLevel) continue; net_landriverlevel = hostcache[n].ldriver; memcpy(&sendaddr, &hostcache[n].addr, sizeof(struct qsockaddr)); break; } if (n < hostCacheCount) goto JustDoIt; } for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (!net_landrivers[net_landriverlevel].initialized) continue; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) != -1) break; } if (net_landriverlevel == net_numlandrivers) return; JustDoIt: test2Socket = dfunc.OpenSocket(0); if (test2Socket == -1) return; test2InProgress = true; test2Driver = net_landriverlevel; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_RULE_INFO); MSG_WriteString(&net_message, ""); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (test2Socket, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); SchedulePollProcedure(&test2PollProcedure, 0.05); } int first_boot = 1; int Datagram_Init (void) { int i; int csock; myDriverLevel = net_driverlevel; if (first_boot) Cmd_AddCommand ("net_stats", NET_Stats_f); if (COM_CheckParm("-nolan")) return -1; i = proto_idx; //for (i = 0; i < net_numlandrivers; i++) { csock = net_landrivers[i].Init (); if (csock == -1) return 0; //continue; net_landrivers[i].initialized = true; net_landrivers[i].controlSock = csock; } if (first_boot) { #ifdef BAN_TEST Cmd_AddCommand ("ban", NET_Ban_f); #endif Cmd_AddCommand ("test", Test_f); Cmd_AddCommand ("test2", Test2_f); first_boot = 0; } return 0; } void Datagram_Shutdown (void) { int i; // // shutdown the lan drivers // for (i = 0; i < net_numlandrivers; i++) { if (net_landrivers[i].initialized) { net_landrivers[i].Shutdown (); net_landrivers[i].initialized = false; } } } void Datagram_Close (qsocket_t *sock) { sfunc.CloseSocket(sock->socket); } void Datagram_Listen (bool state) { int i; for (i = 0; i < net_numlandrivers; i++) if (net_landrivers[i].initialized) net_landrivers[i].Listen (state); } static qsocket_t *_Datagram_CheckNewConnections (void) { struct qsockaddr clientaddr; struct qsockaddr newaddr; int newsock; int acceptsock; qsocket_t *sock; qsocket_t *s; int len; int command; int control; int ret; byte mod, mod_version, mod_flags; // ProQuake acceptsock = dfunc.CheckNewConnections(); if (acceptsock == -1) return NULL; SZ_Clear(&net_message); len = dfunc.Read (acceptsock, net_message.data, net_message.maxsize, &clientaddr); if (len < sizeof(int)) return NULL; net_message.cursize = len; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) return NULL; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) return NULL; if ((control & NETFLAG_LENGTH_MASK) != len) return NULL; command = MSG_ReadByte(); if (command == CCREQ_SERVER_INFO) { char name[256]; strcpy(name, hostname.string); if (strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_SERVER_INFO); dfunc.GetSocketAddr(acceptsock, &newaddr); MSG_WriteString(&net_message, dfunc.AddrToString(&newaddr)); MSG_WriteString(&net_message, name); MSG_WriteString(&net_message, sv.name); MSG_WriteByte(&net_message, net_activeconnections); MSG_WriteByte(&net_message, svs.maxclients); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_PLAYER_INFO) { int playerNumber; int activeNumber; int clientNumber; client_t *client; playerNumber = MSG_ReadByte(); activeNumber = -1; for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++) { if (client->active) { activeNumber++; if (activeNumber == playerNumber) break; } } if (clientNumber == svs.maxclients) return NULL; SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_PLAYER_INFO); MSG_WriteByte(&net_message, playerNumber); MSG_WriteString(&net_message, client->name); MSG_WriteLong(&net_message, client->colors); MSG_WriteLong(&net_message, (int)client->edict->v.frags); MSG_WriteLong(&net_message, (int)(net_time - client->netconnection->connecttime)); MSG_WriteString(&net_message, client->netconnection->address); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command == CCREQ_RULE_INFO) { char *prevCvarName; cvar_t *var; // find the search start location prevCvarName = MSG_ReadString(); if (*prevCvarName) { var = Cvar_FindVar (prevCvarName); if (!var) return NULL; var = var->next; } else var = cvar_vars; // search for the next server cvar while (var) { if (var->flags & CVAR_SERVERINFO) break; var = var->next; } // send the response SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_RULE_INFO); if (var) { MSG_WriteString(&net_message, var->name); MSG_WriteString(&net_message, var->string); } *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } if (command != CCREQ_CONNECT) return NULL; if (strcmp(MSG_ReadString(), "QUAKE") != 0) return NULL; if (MSG_ReadByte() != NET_PROTOCOL_VERSION) return Datagram_Reject("Incompatible version.\n", acceptsock, &clientaddr); #ifdef BAN_TEST // check for a ban if (clientaddr.sa_family == AF_INET) { unsigned long testAddr; testAddr = ((struct sockaddr_in *)&clientaddr)->sin_addr.s_addr; if ((testAddr & banMask) == banAddr) return Datagram_Reject("You have been banned.\n", acceptsock, &clientaddr); } #endif // see if this guy is already connected for (s = net_activeSockets; s; s = s->next) { if (s->driver != net_driverlevel) continue; ret = dfunc.AddrCompare(&clientaddr, &s->addr); if (ret >= 0) { // is this a duplicate connection reqeust? if (ret == 0 && net_time - s->connecttime < 2.0) { // yes, so send a duplicate reply SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(s->socket, &newaddr); MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return NULL; } // it's somebody coming back in from a crash/disconnect // so close the old qsocket and let their retry get them back in //NET_Close(s); //return NULL; } } // ProQuake additions, passwords, stuffs... if (len > 12) mod = MSG_ReadByte(); else mod = MOD_NONE; if (len > 13) mod_version = MSG_ReadByte(); else mod_version = 0; if (len > 14) mod_flags = MSG_ReadByte(); else mod_flags = 0; // allocate a QSocket sock = NET_NewQSocket (); if (sock == NULL) return Datagram_Reject("Server is full.\n", acceptsock, &clientaddr); // allocate a network socket newsock = dfunc.OpenSocket(0); if (newsock == -1) { NET_FreeQSocket(sock); return NULL; } // connect to the client if (dfunc.Connect (newsock, &clientaddr) == -1) { dfunc.CloseSocket(newsock); NET_FreeQSocket(sock); return NULL; } // everything is allocated, just fill in the details // JPG - support for mods sock->proquake_connection = mod; sock->proquake_version = mod_version; sock->proquake_flags = mod_flags; if (mod == MOD_PROQUAKE && mod_version >= 34) // ProQuake 3.40 sock->net_wait = true; sock->socket = newsock; sock->landriver = net_landriverlevel; sock->addr = clientaddr; strcpy(sock->address, dfunc.AddrToString(&clientaddr)); // send him back the info about the server connection he has been allocated SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREP_ACCEPT); dfunc.GetSocketAddr(newsock, &newaddr); sock->client_port = dfunc.GetSocketPort(&newaddr); MSG_WriteLong(&net_message, sock->client_port); MSG_WriteByte(&net_message, MOD_PROQUAKE); // JPG - added this MSG_WriteByte(&net_message, VERSION_PROQUAKE * 10); // JPG 3.00 MSG_WriteByte(&net_message, 0); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (acceptsock, net_message.data, net_message.cursize, &clientaddr); SZ_Clear(&net_message); return sock; } qsocket_t *Datagram_CheckNewConnections (void) { qsocket_t *ret = NULL; for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) if (net_landrivers[net_landriverlevel].initialized) if ((ret = _Datagram_CheckNewConnections ()) != NULL) break; return ret; } static void _Datagram_SearchForHosts (bool xmit) { int ret; int n; int i; struct qsockaddr readaddr; struct qsockaddr myaddr; int control; dfunc.GetSocketAddr (dfunc.controlSock, &myaddr); if (xmit) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_SERVER_INFO); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Broadcast(dfunc.controlSock, net_message.data, net_message.cursize); SZ_Clear(&net_message); } while ((ret = dfunc.Read (dfunc.controlSock, net_message.data, net_message.maxsize, &readaddr)) > 0) { if (ret < sizeof(int)) continue; net_message.cursize = ret; // don't answer our own query if (dfunc.AddrCompare(&readaddr, &myaddr) >= 0) continue; // is the cache full? if (hostCacheCount == HOSTCACHESIZE) continue; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) continue; if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) continue; if ((control & NETFLAG_LENGTH_MASK) != ret) continue; if (MSG_ReadByte() != CCREP_SERVER_INFO) continue; dfunc.GetAddrFromName(MSG_ReadString(), &readaddr); // search the cache for this server for (n = 0; n < hostCacheCount; n++) if (dfunc.AddrCompare(&readaddr, &hostcache[n].addr) == 0) break; // is it already there? if (n < hostCacheCount) continue; // add it hostCacheCount++; strcpy(hostcache[n].name, MSG_ReadString()); strcpy(hostcache[n].map, MSG_ReadString()); hostcache[n].users = MSG_ReadByte(); hostcache[n].maxusers = MSG_ReadByte(); if (MSG_ReadByte() != NET_PROTOCOL_VERSION) { strcpy(hostcache[n].cname, hostcache[n].name); hostcache[n].cname[14] = 0; strcpy(hostcache[n].name, "*"); strcat(hostcache[n].name, hostcache[n].cname); } memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); hostcache[n].driver = net_driverlevel; hostcache[n].ldriver = net_landriverlevel; strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); // check for a name conflict for (i = 0; i < hostCacheCount; i++) { if (i == n) continue; if (strcasecmp (hostcache[n].name, hostcache[i].name) == 0) { i = strlen(hostcache[n].name); if (i < 15 && hostcache[n].name[i-1] > '8') { hostcache[n].name[i] = '0'; hostcache[n].name[i+1] = 0; } else hostcache[n].name[i-1]++; i = -1; } } } } void Datagram_SearchForHosts (bool xmit) { for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) { if (hostCacheCount == HOSTCACHESIZE) break; if (net_landrivers[net_landriverlevel].initialized) _Datagram_SearchForHosts (xmit); } } static qsocket_t *_Datagram_Connect (char *host) { struct qsockaddr sendaddr; struct qsockaddr readaddr; qsocket_t *sock; int newsock; int ret; int reps; double start_time; int control; char *reason; // see if we can resolve the host name if (dfunc.GetAddrFromName(host, &sendaddr) == -1) return NULL; newsock = dfunc.OpenSocket (0); if (newsock == -1) return NULL; sock = NET_NewQSocket (); if (sock == NULL) goto ErrorReturn2; sock->socket = newsock; sock->landriver = net_landriverlevel; // connect to the host if (dfunc.Connect (newsock, &sendaddr) == -1) goto ErrorReturn; // send the connection request Con_Printf("trying...\n"); SCR_UpdateScreen (); start_time = net_time; for (reps = 0; reps < 3; reps++) { SZ_Clear(&net_message); // save space for the header, filled in later MSG_WriteLong(&net_message, 0); MSG_WriteByte(&net_message, CCREQ_CONNECT); MSG_WriteString(&net_message, "QUAKE"); MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION); // We are telling to the server we are using ProQuake, for compatibility purposes. MSG_WriteByte(&net_message, MOD_PROQUAKE); // Flag telling our client is ProQuake. MSG_WriteByte(&net_message, VERSION_PROQUAKE * 10); // Version used of ProQuake. MSG_WriteByte(&net_message, 0); // Connection Flags (3.0) MSG_WriteLong(&net_message, pq_password.value); // Password (ProQuake 3.0) //MSG_WriteByte(&net_message, 1); // Ch0wW: Added a byte to tell we're on a console ( *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK)); dfunc.Write (newsock, net_message.data, net_message.cursize, &sendaddr); SZ_Clear(&net_message); do { ret = dfunc.Read (newsock, net_message.data, net_message.maxsize, &readaddr); // if we got something, validate it if (ret > 0) { // is it from the right place? if (sfunc.AddrCompare(&readaddr, &sendaddr) != 0) { #ifdef DEBUG Con_Printf("wrong reply address\n"); Con_Printf("Expected: %s\n", StrAddr (&sendaddr)); Con_Printf("Received: %s\n", StrAddr (&readaddr)); SCR_UpdateScreen (); #endif ret = 0; continue; } if (ret < sizeof(int)) { ret = 0; continue; } net_message.cursize = ret; MSG_BeginReading (); control = BigLong(*((int *)net_message.data)); MSG_ReadLong(); if (control == -1) { ret = 0; continue; } if ((control & (~NETFLAG_LENGTH_MASK)) != NETFLAG_CTL) { ret = 0; continue; } if ((control & NETFLAG_LENGTH_MASK) != ret) { ret = 0; continue; } } } while (ret == 0 && (SetNetTime() - start_time) < 2.5); if (ret) break; Con_Printf("still trying...\n"); SCR_UpdateScreen (); start_time = SetNetTime(); } if (ret == 0) { reason = "No Response"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } if (ret == -1) { reason = "Network Error"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } int len = ret; ret = MSG_ReadByte(); if (ret == CCREP_REJECT) { reason = MSG_ReadString(); Con_Printf(reason); strncpy(m_return_reason, reason, 31); goto ErrorReturn; } if (ret == CCREP_ACCEPT) { sock->client_port = MSG_ReadLong(); // ProQuake: Change the port. memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); dfunc.SetSocketPort(&sock->addr, sock->client_port); //Con_Printf ("Client port is %s\n", dfunc.AddrToString(&sock->addr)); // Client has received CCREP_ACCEPT meaning client may connect // Now find out if this is a ProQuake server ... // ProQuake -- Looking for what kind of mod it is using if (len > 9) sock->proquake_connection = MSG_ReadByte(); else sock->proquake_connection = MOD_NONE; // ProQuake -- Looking for the version the server uses if (len > 10) sock->proquake_version = MSG_ReadByte(); else sock->proquake_version = 0; if (len > 11) sock->proquake_flags = MSG_ReadByte(); else sock->proquake_flags = 0; } else { reason = "Bad Response"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } dfunc.GetNameFromAddr (&sendaddr, sock->address); Con_Printf ("Connection accepted\n"); sock->lastMessageTime = SetNetTime(); // switch the connection to the specified address if (dfunc.Connect (newsock, &sock->addr) == -1) { reason = "Connect to Game failed"; Con_Printf("%s\n", reason); strcpy(m_return_reason, reason); goto ErrorReturn; } m_return_onerror = false; return sock; ErrorReturn: NET_FreeQSocket(sock); ErrorReturn2: dfunc.CloseSocket(newsock); if (m_return_onerror) { key_dest = key_menu; m_state = m_return_state; m_return_onerror = false; } return NULL; } qsocket_t *Datagram_Connect (char *host) { qsocket_t *ret = NULL; Strip_Port(host); for (net_landriverlevel = 0; net_landriverlevel < net_numlandrivers; net_landriverlevel++) if (net_landrivers[net_landriverlevel].initialized) if ((ret = _Datagram_Connect (host)) != NULL) break; return ret; } ================================================ FILE: source/net_dgrm.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_dgrm.h extern uint8_t proto_idx; int Datagram_Init (void); void Datagram_Listen (bool state); void Datagram_SearchForHosts (bool xmit); qsocket_t *Datagram_Connect (char *host); qsocket_t *Datagram_CheckNewConnections (void); int Datagram_GetMessage (qsocket_t *sock); int Datagram_SendMessage (qsocket_t *sock, sizebuf_t *data); int Datagram_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); bool Datagram_CanSendMessage (qsocket_t *sock); bool Datagram_CanSendUnreliableMessage (qsocket_t *sock); void Datagram_Close (qsocket_t *sock); void Datagram_Shutdown (void); ================================================ FILE: source/net_loop.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_loop.c #include "quakedef.h" #include "net_loop.h" bool localconnectpending = false; qsocket_t *loop_client = NULL; qsocket_t *loop_server = NULL; int Loop_Init (void) { if (cls.state == ca_dedicated) return -1; return 0; } void Loop_Shutdown (void) { } void Loop_Listen (bool state) { } void Loop_SearchForHosts (bool xmit) { if (!sv.active) return; hostCacheCount = 1; if (strcmp(hostname.string, "UNNAMED") == 0) strcpy(hostcache[0].name, "local"); else strcpy(hostcache[0].name, hostname.string); strcpy(hostcache[0].map, sv.name); hostcache[0].users = net_activeconnections; hostcache[0].maxusers = svs.maxclients; hostcache[0].driver = net_driverlevel; strcpy(hostcache[0].cname, "local"); } qsocket_t *Loop_Connect (char *host) { if (strcmp(host,"local") != 0) return NULL; localconnectpending = true; if (!loop_client) { if (!(loop_client = NET_NewQSocket ())) { Con_Printf("Loop_Connect: no qsocket available\n"); return NULL; } strcpy (loop_client->address, "localhost"); } loop_client->receiveMessageLength = 0; loop_client->sendMessageLength = 0; loop_client->canSend = true; loop_client->proquake_connection = MOD_PROQUAKE; loop_client->client_port = 0; if (!loop_server) { if (!(loop_server = NET_NewQSocket ())) { Con_Printf("Loop_Connect: no qsocket available\n"); return NULL; } strcpy (loop_server->address, "LOCAL"); } loop_server->receiveMessageLength = 0; loop_server->sendMessageLength = 0; loop_server->canSend = true; loop_server->proquake_connection = MOD_PROQUAKE; loop_server->client_port = 0; loop_client->driverdata = (void *)loop_server; loop_server->driverdata = (void *)loop_client; return loop_client; } qsocket_t *Loop_CheckNewConnections (void) { if (!localconnectpending) return NULL; localconnectpending = false; loop_server->sendMessageLength = 0; loop_server->receiveMessageLength = 0; loop_server->canSend = true; loop_client->sendMessageLength = 0; loop_client->receiveMessageLength = 0; loop_client->canSend = true; return loop_server; } static int IntAlign(int value) { return (value + (sizeof(int) - 1)) & (~(sizeof(int) - 1)); } int Loop_GetMessage (qsocket_t *sock) { int ret; int length; if (sock->receiveMessageLength == 0) return 0; ret = sock->receiveMessage[0]; length = sock->receiveMessage[1] + (sock->receiveMessage[2] << 8); // alignment byte skipped here SZ_Clear (&net_message); SZ_Write (&net_message, &sock->receiveMessage[4], length); length = IntAlign(length + 4); sock->receiveMessageLength -= length; if (sock->receiveMessageLength) memcpy(sock->receiveMessage, &sock->receiveMessage[length], sock->receiveMessageLength); if (sock->driverdata && ret == 1) ((qsocket_t *)sock->driverdata)->canSend = true; return ret; } int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data) { byte *buffer; int *bufferLength; if (!sock->driverdata) return -1; bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; if ((*bufferLength + data->cursize + 4) > NET_MAXMESSAGE) Sys_Error("Loop_SendMessage: overflow\n"); buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; // message type *buffer++ = 1; // length *buffer++ = data->cursize & 0xff; *buffer++ = data->cursize >> 8; // align buffer++; // message memcpy(buffer, data->data, data->cursize); *bufferLength = IntAlign(*bufferLength + data->cursize + 4); sock->canSend = false; return 1; } int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { byte *buffer; int *bufferLength; if (!sock->driverdata) return -1; bufferLength = &((qsocket_t *)sock->driverdata)->receiveMessageLength; if ((*bufferLength + data->cursize + sizeof(byte) + sizeof(short)) > NET_MAXMESSAGE) return 0; buffer = ((qsocket_t *)sock->driverdata)->receiveMessage + *bufferLength; // message type *buffer++ = 2; // length *buffer++ = data->cursize & 0xff; *buffer++ = data->cursize >> 8; // align buffer++; // message memcpy(buffer, data->data, data->cursize); *bufferLength = IntAlign(*bufferLength + data->cursize + 4); return 1; } bool Loop_CanSendMessage (qsocket_t *sock) { if (!sock->driverdata) return false; return sock->canSend; } bool Loop_CanSendUnreliableMessage (qsocket_t *sock) { return true; } void Loop_Close (qsocket_t *sock) { if (sock->driverdata) ((qsocket_t *)sock->driverdata)->driverdata = NULL; sock->receiveMessageLength = 0; sock->sendMessageLength = 0; sock->canSend = true; if (sock == loop_client) loop_client = NULL; else loop_server = NULL; } ================================================ FILE: source/net_loop.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_loop.h int Loop_Init (void); void Loop_Listen (bool state); void Loop_SearchForHosts (bool xmit); qsocket_t *Loop_Connect (char *host); qsocket_t *Loop_CheckNewConnections (void); int Loop_GetMessage (qsocket_t *sock); int Loop_SendMessage (qsocket_t *sock, sizebuf_t *data); int Loop_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data); bool Loop_CanSendMessage (qsocket_t *sock); bool Loop_CanSendUnreliableMessage (qsocket_t *sock); void Loop_Close (qsocket_t *sock); void Loop_Shutdown (void); ================================================ FILE: source/net_main.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_main.c #include "quakedef.h" #include "net_vcr.h" CVAR (net_messagetimeout, 300, CVAR_NONE) CVAR (hostname, "UNNAMED", CVAR_ARCHIVE) CVAR (pq_password, "", CVAR_NONE) // ProQuake 3 behaviour char server_name[MAX_QPATH]; //---------------------------------------------- qsocket_t *net_activeSockets = NULL; qsocket_t *net_freeSockets = NULL; int net_numsockets = 0; bool tcpipAvailable = false; int net_hostport; int DEFAULTnet_hostport = 26000; char my_tcpip_address[NET_NAMELEN]; static bool listening = false; bool slistInProgress = false; bool slistSilent = false; bool slistLocal = true; static double slistStartTime; static int slistLastShown; static void Slist_Send(void); static void Slist_Poll(void); PollProcedure slistSendProcedure = {NULL, 0.0, Slist_Send}; PollProcedure slistPollProcedure = {NULL, 0.0, Slist_Poll}; sizebuf_t net_message; int net_activeconnections = 0; int messagesSent = 0; int messagesReceived = 0; int unreliableMessagesSent = 0; int unreliableMessagesReceived = 0; #ifdef IDGODS cvar_t idgods = {"idgods", "0"}; #endif int vcrFile = -1; bool recording = false; // these two macros are to make the code more readable #define sfunc net_drivers[sock->driver] #define dfunc net_drivers[net_driverlevel] int net_driverlevel; double net_time; double SetNetTime(void) { net_time = Sys_FloatTime(); return net_time; } /* =================== NET_NewQSocket Called by drivers when a new communications endpoint is required The sequence and buffer fields will be filled in properly =================== */ qsocket_t *NET_NewQSocket (void) { qsocket_t *sock; if (net_freeSockets == NULL) return NULL; if (net_activeconnections >= svs.maxclients) return NULL; // get one from free list sock = net_freeSockets; net_freeSockets = sock->next; // add it to active list sock->next = net_activeSockets; net_activeSockets = sock; sock->disconnected = false; sock->connecttime = net_time; strcpy (sock->address,"UNSET ADDRESS"); sock->driver = net_driverlevel; sock->socket = 0; sock->driverdata = NULL; sock->canSend = true; sock->sendNext = false; sock->lastMessageTime = net_time; sock->ackSequence = 0; sock->sendSequence = 0; sock->unreliableSendSequence = 0; sock->sendMessageLength = 0; sock->receiveSequence = 0; sock->unreliableReceiveSequence = 0; sock->receiveMessageLength = 0; return sock; } void NET_FreeQSocket(qsocket_t *sock) { qsocket_t *s; // remove it from active list if (sock == net_activeSockets) net_activeSockets = net_activeSockets->next; else { for (s = net_activeSockets; s; s = s->next) if (s->next == sock) { s->next = sock->next; break; } if (!s) Sys_Error ("NET_FreeQSocket: not active\n"); } // add it to free list sock->next = net_freeSockets; net_freeSockets = sock; sock->disconnected = true; } static void NET_Listen_f (void) { if (Cmd_Argc () != 2) { Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0); return; } listening = atoi(Cmd_Argv(1)) ? true : false; for (net_driverlevel=0 ; net_driverlevel svs.maxclientslimit) { n = svs.maxclientslimit; Con_Printf ("\"maxplayers\" set to \"%u\"\n", n); } if ((n == 1) && listening) Cbuf_AddText ("listen 0\n"); if ((n > 1) && (!listening)) Cbuf_AddText ("listen 1\n"); svs.maxclients = n; if (n == 1) Cvar_Set ("deathmatch", "0"); else Cvar_Set ("deathmatch", "1"); } static void NET_Port_f (void) { int n; if (Cmd_Argc () != 2) { Con_Printf ("\"port\" is \"%u\"\n", net_hostport); return; } n = atoi(Cmd_Argv(1)); if (n < 1 || n > 65534) { Con_Printf ("Bad value, must be between 1 and 65534\n"); return; } DEFAULTnet_hostport = n; net_hostport = n; if (listening) { // force a change to the new port Cbuf_AddText ("listen 0\n"); Cbuf_AddText ("listen 1\n"); } } static void PrintSlistHeader(void) { Con_Printf("Server Map Users\n"); Con_Printf("--------------- --------------- -----\n"); slistLastShown = 0; } static void PrintSlist(void) { int n; for (n = slistLastShown; n < hostCacheCount; n++) { if (hostcache[n].maxusers) Con_Printf("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers); else Con_Printf("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map); } slistLastShown = n; } static void PrintSlistTrailer(void) { if (hostCacheCount) Con_Printf("== end list ==\n\n"); else Con_Printf("No Quake servers found.\n\n"); } void NET_Slist_f (void) { if (slistInProgress) return; if (! slistSilent) { Con_Printf("Looking for Quake servers...\n"); PrintSlistHeader(); } slistInProgress = true; slistStartTime = Sys_FloatTime(); SchedulePollProcedure(&slistSendProcedure, 0.0); SchedulePollProcedure(&slistPollProcedure, 0.1); hostCacheCount = 0; } static void Slist_Send(void) { for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (!slistLocal && net_driverlevel == 0) continue; if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.SearchForHosts (true); } if ((Sys_FloatTime() - slistStartTime) < 0.5) SchedulePollProcedure(&slistSendProcedure, 0.75); } static void Slist_Poll(void) { for (net_driverlevel=0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (!slistLocal && net_driverlevel == 0) continue; if (net_drivers[net_driverlevel].initialized == false) continue; dfunc.SearchForHosts (false); } if (! slistSilent) PrintSlist(); if ((Sys_FloatTime() - slistStartTime) < 1.5) { SchedulePollProcedure(&slistPollProcedure, 0.1); return; } if (! slistSilent) PrintSlistTrailer(); slistInProgress = false; slistSilent = false; slistLocal = true; } /* =================== NET_Connect =================== */ int hostCacheCount = 0; hostcache_t hostcache[HOSTCACHESIZE]; qsocket_t *NET_Connect (char *host) { qsocket_t *ret; int n; int numdrivers = net_numdrivers; SetNetTime(); if (host && *host == 0) host = NULL; if (host) { if (strcasecmp (host, "local") == 0) { numdrivers = 1; goto JustDoIt; } if (hostCacheCount) { for (n = 0; n < hostCacheCount; n++) if (strcasecmp (host, hostcache[n].name) == 0) { host = hostcache[n].cname; break; } if (n < hostCacheCount) goto JustDoIt; } } slistSilent = host ? true : false; NET_Slist_f (); while(slistInProgress) NET_Poll(); if (host == NULL) { if (hostCacheCount != 1) return NULL; host = hostcache[0].cname; Con_Printf("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host); } if (hostCacheCount) for (n = 0; n < hostCacheCount; n++) if (strcasecmp (host, hostcache[n].name) == 0) { host = hostcache[n].cname; break; } JustDoIt: for (net_driverlevel=0 ; net_driverleveladdress, NET_NAMELEN); } return ret; } } if (recording) { vcrConnect.time = host_time; vcrConnect.op = VCR_OP_CONNECT; vcrConnect.session = 0; Sys_FileWrite (vcrFile, &vcrConnect, sizeof(vcrConnect)); } return NULL; } /* =================== NET_Close =================== */ void NET_Close (qsocket_t *sock) { if (!sock) return; if (sock->disconnected) return; SetNetTime(); // call the driver_Close function sfunc.Close (sock); NET_FreeQSocket(sock); } /* ================= NET_GetMessage If there is a complete message, return it in net_message returns 0 if no data is waiting returns 1 if a message was received returns -1 if connection is invalid ================= */ struct { double time; int op; long session; int ret; int len; } vcrGetMessage; extern void PrintStats(qsocket_t *s); int NET_GetMessage (qsocket_t *sock) { int ret; if (!sock) return -1; if (sock->disconnected) { Con_Printf("NET_GetMessage: disconnected socket\n"); return -1; } SetNetTime(); ret = sfunc.QGetMessage(sock); // see if this connection has timed out if (ret == 0 && sock->driver) { if (net_time - sock->lastMessageTime > net_messagetimeout.value) { NET_Close(sock); return -1; } } if (ret > 0) { if (sock->driver) { sock->lastMessageTime = net_time; if (ret == 1) messagesReceived++; else if (ret == 2) unreliableMessagesReceived++; } if (recording) { vcrGetMessage.time = host_time; vcrGetMessage.op = VCR_OP_GETMESSAGE; vcrGetMessage.session = (long)sock; vcrGetMessage.ret = ret; vcrGetMessage.len = net_message.cursize; Sys_FileWrite (vcrFile, &vcrGetMessage, 24); Sys_FileWrite (vcrFile, net_message.data, net_message.cursize); } } else { if (recording) { vcrGetMessage.time = host_time; vcrGetMessage.op = VCR_OP_GETMESSAGE; vcrGetMessage.session = (long)sock; vcrGetMessage.ret = ret; Sys_FileWrite (vcrFile, &vcrGetMessage, 20); } } return ret; } /* ================== NET_SendMessage Try to send a complete length+message unit over the reliable stream. returns 0 if the message cannot be delivered reliably, but the connection is still considered valid returns 1 if the message was sent properly returns -1 if the connection died ================== */ struct { double time; int op; long session; int r; } vcrSendMessage; int NET_SendMessage (qsocket_t *sock, sizebuf_t *data) { int r; if (!sock) return -1; if (sock->disconnected) { Con_Printf("NET_SendMessage: disconnected socket\n"); return -1; } SetNetTime(); r = sfunc.QSendMessage(sock, data); if (r == 1 && sock->driver) messagesSent++; if (recording) { vcrSendMessage.time = host_time; vcrSendMessage.op = VCR_OP_SENDMESSAGE; vcrSendMessage.session = (long)sock; vcrSendMessage.r = r; Sys_FileWrite (vcrFile, &vcrSendMessage, 20); } return r; } int NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data) { int r; if (!sock) return -1; if (sock->disconnected) { Con_Printf("NET_SendMessage: disconnected socket\n"); return -1; } SetNetTime(); r = sfunc.SendUnreliableMessage(sock, data); if (r == 1 && sock->driver) unreliableMessagesSent++; if (recording) { vcrSendMessage.time = host_time; vcrSendMessage.op = VCR_OP_SENDMESSAGE; vcrSendMessage.session = (long)sock; vcrSendMessage.r = r; Sys_FileWrite (vcrFile, &vcrSendMessage, 20); } return r; } /* ================== NET_CanSendMessage Returns true or false if the given qsocket can currently accept a message to be transmitted. ================== */ bool NET_CanSendMessage (qsocket_t *sock) { int r; if (!sock) return false; if (sock->disconnected) return false; SetNetTime(); r = sfunc.CanSendMessage(sock); if (recording) { vcrSendMessage.time = host_time; vcrSendMessage.op = VCR_OP_CANSENDMESSAGE; vcrSendMessage.session = (long)sock; vcrSendMessage.r = r; Sys_FileWrite (vcrFile, &vcrSendMessage, 20); } return r; } int NET_SendToAll(sizebuf_t *data, int blocktime) { double start; int i; int count = 0; bool state1 [MAX_SCOREBOARD]; bool state2 [MAX_SCOREBOARD]; for (i=0, host_client = svs.clients ; inetconnection) continue; if (host_client->active) { if (host_client->netconnection->driver == 0) { NET_SendMessage(host_client->netconnection, data); state1[i] = true; state2[i] = true; continue; } count++; state1[i] = false; state2[i] = false; } else { state1[i] = true; state2[i] = true; } } start = Sys_FloatTime(); while (count) { count = 0; for (i=0, host_client = svs.clients ; inetconnection)) { state1[i] = true; NET_SendMessage(host_client->netconnection, data); } else { NET_GetMessage (host_client->netconnection); } count++; continue; } if (! state2[i]) { if (NET_CanSendMessage (host_client->netconnection)) { state2[i] = true; } else { NET_GetMessage (host_client->netconnection); } count++; continue; } } if ((Sys_FloatTime() - start) > blocktime) break; } return count; } //============================================================================= /* ==================== NET_Init ==================== */ void NET_Init (void) { int i; int controlSocket; qsocket_t *s; if (COM_CheckParm("-playback")) { net_numdrivers = 1; net_drivers[0].Init = VCR_Init; } if (COM_CheckParm("-record")) recording = true; i = COM_CheckParm ("-port"); if (!i) i = COM_CheckParm ("-udpport"); if (!i) i = COM_CheckParm ("-ipxport"); if (i) { if (i < com_argc-1) DEFAULTnet_hostport = atoi (com_argv[i+1]); else Sys_Error ("NET_Init: you must specify a number after -port"); } net_hostport = DEFAULTnet_hostport; if (COM_CheckParm("-listen") || cls.state == ca_dedicated) listening = true; net_numsockets = svs.maxclientslimit; if (cls.state != ca_dedicated) net_numsockets++; SetNetTime(); for (i = 0; i < net_numsockets; i++) { s = (qsocket_t *)Hunk_AllocName(sizeof(qsocket_t), "qsocket"); s->next = net_freeSockets; net_freeSockets = s; s->disconnected = true; } // allocate space for network message buffer SZ_Alloc (&net_message, NET_MAXMESSAGE); Cvar_RegisterVariable (&net_messagetimeout); Cvar_RegisterVariable (&hostname); #ifdef IDGODS Cvar_RegisterVariable (&idgods); #endif Cmd_AddCommand ("slist", NET_Slist_f); Cmd_AddCommand ("listen", NET_Listen_f); Cmd_AddCommand ("maxplayers", MaxPlayers_f); Cmd_AddCommand ("port", NET_Port_f); // initialize all the drivers for (net_driverlevel=0 ; net_driverlevelnext) NET_Close(sock); // // shutdown the drivers // for (net_driverlevel = 0; net_driverlevel < net_numdrivers; net_driverlevel++) { if (net_drivers[net_driverlevel].initialized == true) { net_drivers[net_driverlevel].Shutdown (); net_drivers[net_driverlevel].initialized = false; } } if (vcrFile != -1) { Con_Printf ("Closing vcrfile.\n"); Sys_FileClose(vcrFile); } } static PollProcedure *pollProcedureList = NULL; void NET_Poll(void) { PollProcedure *pp; SetNetTime(); for (pp = pollProcedureList; pp; pp = pp->next) { if (pp->nextTime > net_time) break; pollProcedureList = pp->next; pp->procedure(pp->arg); } } void SchedulePollProcedure(PollProcedure *proc, double timeOffset) { PollProcedure *pp, *prev; proc->nextTime = Sys_FloatTime() + timeOffset; for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next) { if (pp->nextTime >= proc->nextTime) break; prev = pp; } if (prev == NULL) { proc->next = pollProcedureList; pollProcedureList = proc; return; } proc->next = pp; prev->next = proc; } #ifdef IDGODS #define IDNET 0xc0f62800 bool IsID(struct qsockaddr *addr) { if (idgods.value == 0.0) return false; if (addr->sa_family != 2) return false; if ((BigLong(*(int *)&addr->sa_data[2]) & 0xffffff00) == IDNET) return true; return false; } #endif ================================================ FILE: source/net_none.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "net_loop.h" net_driver_t net_drivers[MAX_NET_DRIVERS] = { { "Loopback", false, Loop_Init, Loop_Listen, Loop_SearchForHosts, Loop_Connect, Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown } }; int net_numdrivers = 1; net_landriver_t net_landrivers[MAX_NET_DRIVERS]; int net_numlandrivers = 0; ================================================ FILE: source/net_psp2.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "net_loop.h" #include "net_dgrm.h" net_driver_t net_drivers[MAX_NET_DRIVERS] = { { "Loopback", false, Loop_Init, Loop_Listen, Loop_SearchForHosts, Loop_Connect, Loop_CheckNewConnections, Loop_GetMessage, Loop_SendMessage, Loop_SendUnreliableMessage, Loop_CanSendMessage, Loop_CanSendUnreliableMessage, Loop_Close, Loop_Shutdown } , { "Datagram", false, Datagram_Init, Datagram_Listen, Datagram_SearchForHosts, Datagram_Connect, Datagram_CheckNewConnections, Datagram_GetMessage, Datagram_SendMessage, Datagram_SendUnreliableMessage, Datagram_CanSendMessage, Datagram_CanSendUnreliableMessage, Datagram_Close, Datagram_Shutdown } }; int net_numdrivers = 2; #include "net_udp.h" #include "net_adhoc.h" net_landriver_t net_landrivers[MAX_NET_DRIVERS] = { { "UDP", false, 0, UDP_Init, UDP_Shutdown, UDP_Listen, UDP_OpenSocket, UDP_CloseSocket, UDP_Connect, UDP_CheckNewConnections, UDP_Read, UDP_Write, UDP_Broadcast, UDP_AddrToString, UDP_StringToAddr, UDP_GetSocketAddr, UDP_GetNameFromAddr, UDP_GetAddrFromName, UDP_AddrCompare, UDP_GetSocketPort, UDP_SetSocketPort }, { "AdHoc", false, 0, AdHoc_Init, AdHoc_Shutdown, AdHoc_Listen, AdHoc_OpenSocket, AdHoc_CloseSocket, AdHoc_Connect, AdHoc_CheckNewConnections, AdHoc_Read, AdHoc_Write, AdHoc_Broadcast, AdHoc_AddrToString, AdHoc_StringToAddr, AdHoc_GetSocketAddr, AdHoc_GetNameFromAddr, AdHoc_GetAddrFromName, AdHoc_AddrCompare, AdHoc_GetSocketPort, AdHoc_SetSocketPort } }; int net_numlandrivers = 2; ================================================ FILE: source/net_udp.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_udp.h int UDP_Init (void); void UDP_Shutdown (void); void UDP_Listen (bool state); int UDP_OpenSocket (int port); int UDP_CloseSocket (int socket); int UDP_Connect (int socket, struct qsockaddr *addr); int UDP_CheckNewConnections (void); int UDP_Read (int socket, byte *buf, int len, struct qsockaddr *addr); int UDP_Write (int socket, byte *buf, int len, struct qsockaddr *addr); int UDP_Broadcast (int socket, byte *buf, int len); char *UDP_AddrToString (struct qsockaddr *addr); int UDP_StringToAddr (char *string, struct qsockaddr *addr); int UDP_GetSocketAddr (int socket, struct qsockaddr *addr); int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name); int UDP_GetAddrFromName (char *name, struct qsockaddr *addr); int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2); int UDP_GetSocketPort (struct qsockaddr *addr); int UDP_SetSocketPort (struct qsockaddr *addr, int port); ================================================ FILE: source/net_udp_psp2.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_udp.c #include "quakedef.h" #include "net_udp.h" #include #include #include #ifdef __sun__ #include #endif #ifdef NeXT #include #endif extern cvar_t hostname; extern void Log (const char *format, ...); #define MAX_NAME 512 struct hostent{ char *h_name; /* official (cannonical) name of host */ char **h_aliases; /* pointer to array of pointers of alias names */ int h_addrtype; /* host address type: AF_INET */ int h_length; /* length of address: 4 */ char **h_addr_list; /* pointer to array of pointers with IPv4 addresses */ }; #define h_addr h_addr_list[0] // Since using standard sceNet structs seems to cause problems, let's re-implement linux-like interface #define AF_INET SCE_NET_AF_INET #define SOCK_DGRAM SCE_NET_SOCK_DGRAM #define IPPROTO_UDP SCE_NET_IPPROTO_UDP #define SOL_SOCKET SCE_NET_SOL_SOCKET #define MSG_PEEK SCE_NET_MSG_PEEK #define INADDR_BROADCAST SCE_NET_INADDR_BROADCAST #define SO_BROADCAST SCE_NET_SO_BROADCAST #define INADDR_ANY SCE_NET_INADDR_ANY #define SO_NBIO SCE_NET_SO_NBIO struct in_addr { unsigned long s_addr; // load with inet_aton() }; struct sockaddr_in { short sin_family; // e.g. AF_INET unsigned short sin_port; // e.g. htons(3490) struct in_addr sin_addr; // see struct in_addr, below char sin_zero[8]; // zero this if you want to }; struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address }; inline int convertSceNetSockaddrIn(struct SceNetSockaddrIn* src, struct sockaddr_in* dst){ if (dst == NULL || src == NULL) return -1; dst->sin_family = src->sin_family; dst->sin_port = src->sin_port; dst->sin_addr.s_addr = src->sin_addr.s_addr; return 0; } inline int convertSockaddrIn(struct SceNetSockaddrIn* dst, const struct sockaddr_in* src){ if (dst == NULL || src == NULL) return -1; dst->sin_family = src->sin_family; dst->sin_port = src->sin_port; dst->sin_addr.s_addr = src->sin_addr.s_addr; return 0; } inline int convertSceNetSockaddr(struct SceNetSockaddr* src, struct sockaddr* dst){ if (dst == NULL || src == NULL) return -1; dst->sa_family = src->sa_family; memcpy(dst->sa_data,src->sa_data,14); return 0; } inline int convertSockaddr(struct SceNetSockaddr* dst, const struct sockaddr* src){ if (dst == NULL || src == NULL) return -1; dst->sa_family = src->sa_family; memcpy(dst->sa_data,src->sa_data,14); return 0; } inline int socket(int domain, int type, int protocol){ return sceNetSocket("Socket", domain, type, protocol); } inline int recvfrom(int sockfd, void* buf, long len, int flags, struct sockaddr* src_addr, unsigned int* addrlen){ struct SceNetSockaddr tmp; int res = sceNetRecvfrom(sockfd, buf, len, flags, &tmp, addrlen); if (src_addr != NULL) convertSceNetSockaddr(&tmp, src_addr); return res; } inline int getsockname(int sockfd, struct sockaddr *addr, unsigned int *addrlen){ struct SceNetSockaddr tmp; convertSockaddr(&tmp, addr); int res = sceNetGetsockname(sockfd, &tmp, addrlen); convertSceNetSockaddr(&tmp, addr); return res; } inline int bind(int sockfd, const struct sockaddr* addr, unsigned int addrlen){ struct SceNetSockaddr tmp; convertSockaddr(&tmp, addr); return sceNetBind(sockfd, &tmp, addrlen); } inline int close(int sockfd){ return sceNetSocketClose(sockfd); } inline int setsockopt(int sockfd, int level, int optname, const void *optval, unsigned int optlen){ return sceNetSetsockopt(sockfd, level, optname, optval, optlen); } inline unsigned int sendto(int sockfd, const void *buf, unsigned int len, int flags, const struct sockaddr *dest_addr, unsigned int addrlen){ struct SceNetSockaddr tmp; convertSockaddr(&tmp, dest_addr); return sceNetSendto(sockfd, buf, len, flags, &tmp, addrlen); } // Copy-pasted from xyz code static struct hostent *gethostbyname(const char *name) { static struct hostent ent; static char sname[MAX_NAME] = ""; static struct SceNetInAddr saddr = { 0 }; static char *addrlist[2] = { (char *) &saddr, NULL }; int rid; int err; rid = sceNetResolverCreate("resolver", NULL, 0); if(rid < 0) { return NULL; } err = sceNetResolverStartNtoa(rid, name, &saddr, 0, 0, 0); sceNetResolverDestroy(rid); if(err < 0) { return NULL; } ent.h_name = sname; ent.h_aliases = 0; ent.h_addrtype = SCE_NET_AF_INET; ent.h_length = sizeof(struct SceNetInAddr); ent.h_addr_list = addrlist; ent.h_addr = addrlist[0]; return &ent; } static int net_acceptsocket = -1; // socket for fielding new connections static int net_controlsocket; static int net_broadcastsocket = 0; static struct qsockaddr broadcastaddr; static unsigned long myAddr; #include "net_udp.h" //============================================================================= static void *net_memory = NULL; #define NET_INIT_SIZE 1*1024*1024 int UDP_Init (void) { Log("UDP_Init called..."); struct hostent *local; char buff[15]; struct qsockaddr addr; char *colon; SceNetInitParam initparam; if (COM_CheckParm ("-noudp")) return -1; // Start SceNet & SceNetCtl int ret = sceNetShowNetstat(); if (ret == SCE_NET_ERROR_ENOTINIT) { net_memory = malloc(NET_INIT_SIZE); initparam.memory = net_memory; initparam.size = NET_INIT_SIZE; initparam.flags = 0; ret = sceNetInit(&initparam); if (ret < 0) return -1; ret = sceNetCtlInit(); if (ret < 0){ sceNetTerm(); free(net_memory); return -1; } } // Getting IP address SceNetCtlInfo info; sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_IP_ADDRESS, &info); sceNetInetPton(SCE_NET_AF_INET, info.ip_address, &myAddr); // if the quake hostname isn't set, set it to player nickname if (!strcmp(hostname.string, "UNNAMED")) { SceAppUtilInitParam init_param; SceAppUtilBootParam boot_param; memset(&init_param, 0, sizeof(SceAppUtilInitParam)); memset(&boot_param, 0, sizeof(SceAppUtilBootParam)); sceAppUtilInit(&init_param, &boot_param); char nick[SCE_SYSTEM_PARAM_USERNAME_MAXSIZE]; sceAppUtilSystemParamGetString(SCE_SYSTEM_PARAM_ID_USERNAME, nick, SCE_SYSTEM_PARAM_USERNAME_MAXSIZE); Cvar_Set ("hostname", nick); } if ((net_controlsocket = UDP_OpenSocket (0)) == -1) Sys_Error("UDP_Init: Unable to open control socket\n"); ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; ((struct sockaddr_in *)&broadcastaddr)->sin_port = sceNetHtons(net_hostport); UDP_GetSocketAddr (net_controlsocket, &addr); strcpy(my_tcpip_address, UDP_AddrToString (&addr)); colon = strrchr (my_tcpip_address, ':'); if (colon) *colon = 0; Con_Printf("UDP Initialized as IP %s\n", my_tcpip_address); tcpipAvailable = true; return net_controlsocket; } //============================================================================= void UDP_Shutdown (void) { Log("UDP_Shutdown"); UDP_Listen (false); UDP_CloseSocket (net_controlsocket); //sceNetCtlTerm(); //sceNetTerm(); //free(net_memory); } //============================================================================= void UDP_Listen (bool state) { Log("UDP_Listen"); // enable listening if (state) { if (net_acceptsocket != -1) return; if ((net_acceptsocket = UDP_OpenSocket (net_hostport)) == -1) Sys_Error ("UDP_Listen: Unable to open accept socket\n"); return; } // disable listening if (net_acceptsocket == -1) return; UDP_CloseSocket (net_acceptsocket); net_acceptsocket = -1; } //============================================================================= int UDP_OpenSocket (int port) { Log("UDP_OpenSocket(%ld)",port); int newsocket; struct sockaddr_in address; uint32_t _true = true; if ((newsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) return -1; if (setsockopt(newsocket, SOL_SOCKET, SO_NBIO, (char *)&_true, sizeof(uint32_t)) == -1) goto ErrorReturn; memset(&address, 0, sizeof(struct sockaddr_in)); // JPG 1.05 - fix by JDC address.sin_family = AF_INET; address.sin_addr.s_addr = myAddr; address.sin_port = sceNetHtons(port); if( bind(newsocket, (void*)&address, sizeof(address)) == -1) goto ErrorReturn; return newsocket; ErrorReturn: close(newsocket); return -1; } //============================================================================= int UDP_CloseSocket (int socket) { Log("UDP_CloseSocket"); if (socket == net_broadcastsocket) net_broadcastsocket = 0; return close(socket); } //============================================================================= /* ============ PartialIPAddress this lets you type only as much of the net address as required, using the local network components to fill in the rest ============ */ static int PartialIPAddress (char *in, struct qsockaddr *hostaddr) { char buff[256]; char *b; int addr; int num; int mask; int run; int port; buff[0] = '.'; b = buff; strcpy(buff+1, in); if (buff[1] == '.') b++; addr = 0; mask=-1; while (*b == '.') { b++; num = 0; run = 0; while (!( *b < '0' || *b > '9')) { num = num*10 + *b++ - '0'; if (++run > 3) return -1; } if ((*b < '0' || *b > '9') && *b != '.' && *b != ':' && *b != 0) return -1; if (num < 0 || num > 255) return -1; mask<<=8; addr = (addr<<8) + num; } if (*b++ == ':') port = atoi(b); else port = net_hostport; Log("PartialIPAddress(%s): port: %ld",in,port); hostaddr->sa_family = SCE_NET_AF_INET; ((struct sockaddr_in *)hostaddr)->sin_port = sceNetHtons((short)port); ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & sceNetHtonl(mask)) | sceNetHtonl(addr); return 0; } //============================================================================= int UDP_Connect (int socket, struct qsockaddr *addr) { Log("UDP_Connect"); return 0; } //============================================================================= int UDP_CheckNewConnections (void) { Log("UDP_CheckNewConnections"); char buf[4096]; if (net_acceptsocket == -1) return -1; if (recvfrom(net_acceptsocket, buf, sizeof(buf), MSG_PEEK, NULL, NULL) >= 0) return net_acceptsocket; return -1; } //============================================================================= int UDP_Read (int socket, byte *buf, int len, struct qsockaddr *addr) { int addrlen = sizeof (struct qsockaddr); int ret; ret = recvfrom(socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); Log("UDP_Read returned %ld",ret); if (ret == SCE_NET_ERROR_EAGAIN || ret == SCE_NET_ERROR_ECONNREFUSED) return 0; else if (ret < 0) return -1; return ret; } //============================================================================= int UDP_MakeSocketBroadcastCapable (int socket) { int i = 1; Log("UDP_MakeSocketBroadcastCapable"); // make this socket broadcast capable if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) return -1; net_broadcastsocket = socket; return 0; } //============================================================================= int UDP_Broadcast (int socket, byte *buf, int len) { Log("UDP_Broadcast"); int ret; if (socket != net_broadcastsocket) { if (net_broadcastsocket != 0) Sys_Error("Attempted to use multiple broadcasts sockets\n"); ret = UDP_MakeSocketBroadcastCapable (socket); if (ret == -1) { Con_Printf("Unable to make socket broadcast capable\n"); return ret; } } return UDP_Write (socket, buf, len, &broadcastaddr); } //============================================================================= int UDP_Write (int socket, byte *buf, int len, struct qsockaddr *addr) { int ret; ret = sendto(socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); Log("UDP_Write returned %ld",ret); if (ret == SCE_NET_ERROR_EAGAIN) return 0; else if (ret < 0) return -1; return ret; } //============================================================================= char *UDP_AddrToString (struct qsockaddr *addr) { static char buffer[22]; int haddr; haddr = sceNetNtohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, sceNetNtohs(((struct sockaddr_in *)addr)->sin_port)); Log("UDP_AddrToString returned %s",buffer); return buffer; } //============================================================================= int UDP_StringToAddr (char *string, struct qsockaddr *addr) { Log("UDP_StringToAddr(%s)",string); int ha1, ha2, ha3, ha4, hp; int ipaddr; sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; addr->sa_family = AF_INET; ((struct sockaddr_in *)addr)->sin_addr.s_addr = sceNetHtonl(ipaddr); ((struct sockaddr_in *)addr)->sin_port = sceNetHtons(hp); return 0; } //============================================================================= int UDP_GetSocketAddr (int socket, struct qsockaddr *addr) { Log("UDP_GetSocketAddr"); int addrlen = sizeof(struct qsockaddr); unsigned int a, tmp; memset(addr, 0, sizeof(struct qsockaddr)); int ret = getsockname(socket, (struct sockaddr *)addr, &addrlen); Log("getsockname returned %ld", ret); a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; sceNetInetPton(SCE_NET_AF_INET, "127.0.0.1", &tmp); if (a == 0 || a == tmp) ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; return 0; } //============================================================================= int UDP_GetNameFromAddr (struct qsockaddr *addr, char *name) { strcpy (name, UDP_AddrToString (addr)); return 0; } //============================================================================= int UDP_GetAddrFromName(char *name, struct qsockaddr *addr) { Log("UDP_GetAddrFromName(%s)",name); struct hostent *hostentry; if (name[0] >= '0' && name[0] <= '9') return PartialIPAddress (name, addr); hostentry = gethostbyname (name); if (!hostentry) return -1; addr->sa_family = SCE_NET_AF_INET; ((struct sockaddr_in *)addr)->sin_port = sceNetHtons(net_hostport); ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; return 0; } //============================================================================= int UDP_AddrCompare (struct qsockaddr *addr1, struct qsockaddr *addr2) { if (addr1->sa_family != addr2->sa_family){ Log("UDP_AddrCompare returned -1 (1st case)"); return -1; } if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr){ Log("UDP_AddrCompare returned -1 (2nd case)"); return -1; } if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port){ Log("UDP_AddrCompare returned 1"); return 1; } Log("UDP_AddrCompare returned 0"); return 0; } //============================================================================= int UDP_GetSocketPort (struct qsockaddr *addr) { Log("UDP_GetSocketPort"); return sceNetNtohs(((struct sockaddr_in *)addr)->sin_port); } int UDP_SetSocketPort (struct qsockaddr *addr, int port) { Log("UDP_SetSocketPort"); ((struct sockaddr_in *)addr)->sin_port = sceNetHtons(port); return 0; } //============================================================================= ================================================ FILE: source/net_vcr.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_vcr.c #include "quakedef.h" #include "net_vcr.h" extern int vcrFile; // This is the playback portion of the VCR. It reads the file produced // by the recorder and plays it back to the host. The recording contains // everything necessary (events, timestamps, and data) to duplicate the game // from the viewpoint of everything above the network layer. static struct { double time; int op; long session; } next; int VCR_Init (void) { net_drivers[0].Init = VCR_Init; net_drivers[0].SearchForHosts = VCR_SearchForHosts; net_drivers[0].Connect = VCR_Connect; net_drivers[0].CheckNewConnections = VCR_CheckNewConnections; net_drivers[0].QGetMessage = VCR_GetMessage; net_drivers[0].QSendMessage = VCR_SendMessage; net_drivers[0].CanSendMessage = VCR_CanSendMessage; net_drivers[0].Close = VCR_Close; net_drivers[0].Shutdown = VCR_Shutdown; Sys_FileRead(vcrFile, &next, sizeof(next)); return 0; } void VCR_ReadNext (void) { if (Sys_FileRead(vcrFile, &next, sizeof(next)) == 0) { next.op = 255; Sys_Error ("=== END OF PLAYBACK===\n"); } if (next.op < 1 || next.op > VCR_MAX_MESSAGE) Sys_Error ("VCR_ReadNext: bad op"); } void VCR_Listen (bool state) { } void VCR_Shutdown (void) { } int VCR_GetMessage (qsocket_t *sock) { int ret; if (host_time != next.time || next.op != VCR_OP_GETMESSAGE || next.session != *(long *)(&sock->driverdata)) Sys_Error ("VCR missmatch"); Sys_FileRead(vcrFile, &ret, sizeof(int)); if (ret != 1) { VCR_ReadNext (); return ret; } Sys_FileRead(vcrFile, &net_message.cursize, sizeof(int)); Sys_FileRead(vcrFile, net_message.data, net_message.cursize); VCR_ReadNext (); return 1; } int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data) { int ret; if (host_time != next.time || next.op != VCR_OP_SENDMESSAGE || next.session != *(long *)(&sock->driverdata)) Sys_Error ("VCR missmatch"); Sys_FileRead(vcrFile, &ret, sizeof(int)); VCR_ReadNext (); return ret; } bool VCR_CanSendMessage (qsocket_t *sock) { bool ret; if (host_time != next.time || next.op != VCR_OP_CANSENDMESSAGE || next.session != *(long *)(&sock->driverdata)) Sys_Error ("VCR missmatch"); Sys_FileRead(vcrFile, &ret, sizeof(int)); VCR_ReadNext (); return ret; } void VCR_Close (qsocket_t *sock) { } void VCR_SearchForHosts (bool xmit) { } qsocket_t *VCR_Connect (char *host) { return NULL; } qsocket_t *VCR_CheckNewConnections (void) { qsocket_t *sock; if (host_time != next.time || next.op != VCR_OP_CONNECT) Sys_Error ("VCR missmatch"); if (!next.session) { VCR_ReadNext (); return NULL; } sock = NET_NewQSocket (); *(long *)(&sock->driverdata) = next.session; Sys_FileRead (vcrFile, sock->address, NET_NAMELEN); VCR_ReadNext (); return sock; } ================================================ FILE: source/net_vcr.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // net_vcr.h #define VCR_OP_CONNECT 1 #define VCR_OP_GETMESSAGE 2 #define VCR_OP_SENDMESSAGE 3 #define VCR_OP_CANSENDMESSAGE 4 #define VCR_MAX_MESSAGE 4 int VCR_Init (void); void VCR_Listen (bool state); void VCR_SearchForHosts (bool xmit); qsocket_t *VCR_Connect (char *host); qsocket_t *VCR_CheckNewConnections (void); int VCR_GetMessage (qsocket_t *sock); int VCR_SendMessage (qsocket_t *sock, sizebuf_t *data); bool VCR_CanSendMessage (qsocket_t *sock); void VCR_Close (qsocket_t *sock); void VCR_Shutdown (void); ================================================ FILE: source/nonintel.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // nonintel.c: code for non-Intel processors only // #include "quakedef.h" #include "r_local.h" #include "d_local.h" #if !id386 /* ================ R_Surf8Patch ================ */ void R_Surf8Patch () { // we only patch code on Intel } /* ================ R_Surf16Patch ================ */ void R_Surf16Patch () { // we only patch code on Intel } /* ================ R_SurfacePatch ================ */ void R_SurfacePatch (void) { // we only patch code on Intel } #endif // !id386 ================================================ FILE: source/pr_cmds.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) #define PR_MAX_TEMPSTRING 2048 // 2001-10-25 Enhanced temp string handling by Maddes char pr_varstring_temp[PR_MAX_TEMPSTRING]; // 2001-10-25 Enhanced temp string handling by Maddes // 2001-10-20 Extension System by LordHavoc start char *pr_extensions[] = { "DP_CON_SET", "DP_CON_SETA", "DP_EF_BLUE", "DP_EF_NODRAW", "DP_EF_RED", "DP_ENT_ALPHA", "DP_GFX_EXTERNALTEXTURES", "DP_GFX_EXTERNALTEXTURES_PERMAPTEXTURES", "DP_HALFLIFE_MAP", "DP_LITSUPPORT", "DP_QC_ASINACOSATANATAN2TAN", "DP_QC_COPYENTITY", "DP_QC_CVAR_STRING", "DP_QC_EDICT_NUM", "DP_QC_ETOS", "DP_QC_FINDCHAIN", "DP_QC_FINDCHAINFLOAT", "DP_QC_MINMAXBOUND", "DP_QC_NUM_FOR_EDICT", "DP_QC_RANDOMVEC", "DP_QC_SINCOSSQRTPOW", "DP_QC_TRACEBOX", "DP_SND_FAKETRACKS", "DP_SV_MODELFLAGS_AS_EFFECTS", "DP_SV_NODRAWTOCLIENT", "DP_SV_DRAWONLYTOCLIENT", "EXT_BITSHIFT", "FRIK_FILE" }; int pr_numextensions = sizeof(pr_extensions)/sizeof(pr_extensions[0]); bool extension_find(char *name) { int i; for (i=0; i < pr_numextensions; i++) { if (!strcasecmp(pr_extensions[i], name)) return true; } return false; } /* ================= PF_extension_find returns true if the extension is supported by the server float extension_find(string name) ================= */ void PF_extension_find (void) { G_FLOAT(OFS_RETURN) = extension_find(G_STRING(OFS_PARM0)); } // 2001-10-20 Extension System by LordHavoc end /* =============================================================================== BUILT-IN FUNCTIONS =============================================================================== */ char *PF_VarString (int first) { int i; // 2001-10-25 Enhanced temp string handling by Maddes start int maxlen; char *add; pr_varstring_temp[0] = 0; for (i=first ; i < pr_argc ; i++) { maxlen = PR_MAX_TEMPSTRING - strlen(pr_varstring_temp) - 1; // -1 is EndOfString add = G_STRING((OFS_PARM0+i*3)); if (maxlen > strlen(add)) { strcat (pr_varstring_temp, add); } else { strncat (pr_varstring_temp, add, maxlen); pr_varstring_temp[PR_MAX_TEMPSTRING-1] = 0; break; // can stop here } } return pr_varstring_temp; // 2001-10-25 Enhanced temp string handling by Maddes end } /* ================= PF_errror This is a TERMINAL error, which will kill off the entire server. Dumps self. error(value) ================= */ void PF_error(void) { char *s; edict_t *ed; s = PF_VarString(0); Con_Printf("======SERVER ERROR in %s:\n%s\n" , pr_strings + pr_xfunction->s_name, s); ed = PROG_TO_EDICT(pr_global_struct->self); ED_Print(ed); Host_Error("Program error"); } /* ================= PF_objerror Dumps out self, then an error message. The program is aborted and self is removed, but the level can continue. objerror(value) ================= */ void PF_objerror(void) { char *s; edict_t *ed; s = PF_VarString(0); Con_Printf("======OBJECT ERROR in %s:\n%s\n" , pr_strings + pr_xfunction->s_name, s); ed = PROG_TO_EDICT(pr_global_struct->self); ED_Print(ed); ED_Free(ed); Host_Error("Program error"); } /* ============== PF_makevectors Writes new values for v_forward, v_up, and v_right based on angles makevectors(vector) ============== */ void PF_makevectors(void) { AngleVectors(G_VECTOR(OFS_PARM0), pr_global_struct->v_forward, pr_global_struct->v_right, pr_global_struct->v_up); } /* ================= PF_setorigin This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. setorigin (entity, origin) ================= */ void PF_setorigin(void) { edict_t *e; float *org; e = G_EDICT(OFS_PARM0); org = G_VECTOR(OFS_PARM1); VectorCopy(org, e->v.origin); SV_LinkEdict(e, false); } void PF_copyentity(void) { edict_t *in, *out; in = G_EDICT(OFS_PARM0); out = G_EDICT(OFS_PARM1); memcpy(out, in, pr_edict_size); } void SetMinMaxSize(edict_t *e, float *min, float *max, bool rotate) { float *angles; vec3_t rmin, rmax; float bounds[2][3]; float xvector[2], yvector[2]; float a; vec3_t base, transformed; int i, j, k, l; for (i = 0; i<3; i++) if (min[i] > max[i]) PR_RunError("backwards mins/maxs"); rotate = false; // FIXME: implement rotation properly again if (!rotate) { VectorCopy(min, rmin); VectorCopy(max, rmax); } else { // find min / max for rotations angles = e->v.angles; a = angles[1] / 180 * M_PI; float ca = cosf(a); float sa = sinf(a); xvector[0] = ca; xvector[1] = sa; yvector[0] = -sa; yvector[1] = ca; VectorCopy(min, bounds[0]); VectorCopy(max, bounds[1]); rmin[0] = rmin[1] = rmin[2] = 9999; rmax[0] = rmax[1] = rmax[2] = -9999; for (i = 0; i <= 1; i++) { base[0] = bounds[i][0]; for (j = 0; j <= 1; j++) { base[1] = bounds[j][1]; for (k = 0; k <= 1; k++) { base[2] = bounds[k][2]; // transform the point transformed[0] = xvector[0] * base[0] + yvector[0] * base[1]; transformed[1] = xvector[1] * base[0] + yvector[1] * base[1]; transformed[2] = base[2]; for (l = 0; l<3; l++) { if (transformed[l] < rmin[l]) rmin[l] = transformed[l]; if (transformed[l] > rmax[l]) rmax[l] = transformed[l]; } } } } } // set derived values VectorCopy(rmin, e->v.mins); VectorCopy(rmax, e->v.maxs); VectorSubtract(max, min, e->v.size); SV_LinkEdict(e, false); } /* ================= PF_setsize the size box is rotated by the current angle setsize (entity, minvector, maxvector) ================= */ void PF_setsize(void) { edict_t *e; float *min, *max; e = G_EDICT(OFS_PARM0); min = G_VECTOR(OFS_PARM1); max = G_VECTOR(OFS_PARM2); SetMinMaxSize(e, min, max, false); } /* ================= PF_setmodel setmodel(entity, model) ================= */ void PF_setmodel(void) { edict_t *e; char *m, **check; model_t *mod; int i; e = G_EDICT(OFS_PARM0); m = G_STRING(OFS_PARM1); // check to see if model was properly precached for (i = 0, check = sv.model_precache; *check; i++, check++) if (!strcmp(*check, m)) break; if (!*check) PR_RunError("no precache: %s\n", m); e->v.model = m - pr_strings; e->v.modelindex = i; //SV_ModelIndex (m); mod = sv.models[(int)e->v.modelindex]; // Mod_ForName (m, true); if (mod) SetMinMaxSize(e, mod->mins, mod->maxs, true); else SetMinMaxSize(e, vec3_origin, vec3_origin, true); } /* ================= PF_bprint broadcast print to everyone on server bprint(value) ================= */ void PF_bprint(void) { char *s; s = PF_VarString(0); SV_BroadcastPrintf("%s", s); } /* ================= PF_sprint single print to a specific client sprint(clientent, value) ================= */ void PF_sprint(void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > svs.maxclients) { Con_Printf("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum - 1]; MSG_WriteChar(&client->message, svc_print); MSG_WriteString(&client->message, s); } /* ================= PF_centerprint single print to a specific client centerprint(clientent, value) ================= */ void PF_centerprint(void) { char *s; client_t *client; int entnum; entnum = G_EDICTNUM(OFS_PARM0); s = PF_VarString(1); if (entnum < 1 || entnum > svs.maxclients) { Con_Printf("tried to sprint to a non-client\n"); return; } client = &svs.clients[entnum - 1]; MSG_WriteChar(&client->message, svc_centerprint); MSG_WriteString(&client->message, s); } /* ================= PF_normalize vector normalize(vector) ================= */ void PF_normalize(void) { float *value1; vec3_t newvalue; float new; value1 = G_VECTOR(OFS_PARM0); new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2] * value1[2]; new = sqrt(new); if (new == 0) newvalue[0] = newvalue[1] = newvalue[2] = 0; else { new = 1 / new; newvalue[0] = value1[0] * new; newvalue[1] = value1[1] * new; newvalue[2] = value1[2] * new; } VectorCopy(newvalue, G_VECTOR(OFS_RETURN)); } /* ================= PF_vlen scalar vlen(vector) ================= */ void PF_vlen(void) { float *value1; float new; value1 = G_VECTOR(OFS_PARM0); new = value1[0] * value1[0] + value1[1] * value1[1] + value1[2] * value1[2]; new = sqrt(new); G_FLOAT(OFS_RETURN) = new; } /* ================= PF_vectoyaw float vectoyaw(vector) ================= */ void PF_vectoyaw(void) { float *value1; float yaw; value1 = G_VECTOR(OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) yaw = 0; else { yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; } G_FLOAT(OFS_RETURN) = yaw; } /* ================= PF_vectoangles vector vectoangles(vector) ================= */ void PF_vectoangles(void) { float *value1; float forward; float yaw, pitch; value1 = G_VECTOR(OFS_PARM0); if (value1[1] == 0 && value1[0] == 0) { yaw = 0; if (value1[2] > 0) pitch = 90; else pitch = 270; } else { yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI); if (yaw < 0) yaw += 360; forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]); pitch = (int)(atan2(value1[2], forward) * 180 / M_PI); if (pitch < 0) pitch += 360; } G_FLOAT(OFS_RETURN + 0) = pitch; G_FLOAT(OFS_RETURN + 1) = yaw; G_FLOAT(OFS_RETURN + 2) = 0; } /* ================= PF_Random Returns a number from 0<= num < 1 random() ================= */ void PF_random(void) { float num; num = (rand() & 0x7fff) / ((float)0x7fff); G_FLOAT(OFS_RETURN) = num; } /* ================= PF_particle particle(origin, color, count) ================= */ void PF_particle(void) { float *org, *dir; float color; float count; org = G_VECTOR(OFS_PARM0); dir = G_VECTOR(OFS_PARM1); color = G_FLOAT(OFS_PARM2); count = G_FLOAT(OFS_PARM3); SV_StartParticle(org, dir, color, count); } /* ================= PF_ambientsound ================= */ void PF_ambientsound(void) { char **check; char *samp; float *pos; float vol, attenuation; int i, soundnum; pos = G_VECTOR(OFS_PARM0); samp = G_STRING(OFS_PARM1); vol = G_FLOAT(OFS_PARM2); attenuation = G_FLOAT(OFS_PARM3); // check to see if samp was properly precached for (soundnum = 0, check = sv.sound_precache; *check; check++, soundnum++) if (!strcmp(*check, samp)) break; if (!*check) { Con_Printf("no precache: %s\n", samp); return; } // add an svc_spawnambient command to the level signon packet MSG_WriteByte(&sv.signon, svc_spawnstaticsound); for (i = 0; i<3; i++) MSG_WriteCoord(&sv.signon, pos[i]); MSG_WriteByte(&sv.signon, soundnum); MSG_WriteByte(&sv.signon, vol * 255); MSG_WriteByte(&sv.signon, attenuation * 64); } /* ================= PF_sound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. ================= */ void PF_sound(void) { char *sample; int channel; edict_t *entity; int volume; float attenuation; entity = G_EDICT(OFS_PARM0); channel = G_FLOAT(OFS_PARM1); sample = G_STRING(OFS_PARM2); volume = G_FLOAT(OFS_PARM3) * 255; attenuation = G_FLOAT(OFS_PARM4); if (volume < 0 || volume > 255) Sys_Error("SV_StartSound: volume = %i", volume); if (attenuation < 0 || attenuation > 4) Sys_Error("SV_StartSound: attenuation = %f", attenuation); if (channel < 0 || channel > 7) Sys_Error("SV_StartSound: channel = %i", channel); SV_StartSound(entity, channel, sample, volume, attenuation); } /* ================= PF_break break() ================= */ void PF_break(void) { Con_Printf("break statement\n"); *(int *)-4 = 0; // dump to debugger // PR_RunError ("break statement"); } /* ================= PF_traceline Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. traceline (vector1, vector2, tryents) ================= */ void PF_traceline(void) { float *v1, *v2; trace_t trace; int nomonsters; edict_t *ent; v1 = G_VECTOR(OFS_PARM0); v2 = G_VECTOR(OFS_PARM1); nomonsters = G_FLOAT(OFS_PARM2); ent = G_EDICT(OFS_PARM3); trace = SV_Move(v1, vec3_origin, vec3_origin, v2, nomonsters, ent); pr_global_struct->trace_allsolid = trace.allsolid; pr_global_struct->trace_startsolid = trace.startsolid; pr_global_struct->trace_fraction = trace.fraction; pr_global_struct->trace_inwater = trace.inwater; pr_global_struct->trace_inopen = trace.inopen; VectorCopy(trace.endpos, pr_global_struct->trace_endpos); VectorCopy(trace.plane.normal, pr_global_struct->trace_plane_normal); pr_global_struct->trace_plane_dist = trace.plane.dist; if (trace.ent) pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); else pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); } void PF_tracebox (void) { float *v1, *v2, *mins, *maxs; trace_t trace; int nomonsters; edict_t *ent; v1 = G_VECTOR(OFS_PARM0); mins = G_VECTOR(OFS_PARM1); maxs = G_VECTOR(OFS_PARM2); v2 = G_VECTOR(OFS_PARM3); nomonsters = G_FLOAT(OFS_PARM4); ent = G_EDICT(OFS_PARM5); trace = SV_Move (v1, mins, maxs, v2, nomonsters, ent); pr_global_struct->trace_allsolid = trace.allsolid; pr_global_struct->trace_startsolid = trace.startsolid; pr_global_struct->trace_fraction = trace.fraction; pr_global_struct->trace_inwater = trace.inwater; pr_global_struct->trace_inopen = trace.inopen; VectorCopy (trace.endpos, pr_global_struct->trace_endpos); VectorCopy (trace.plane.normal, pr_global_struct->trace_plane_normal); pr_global_struct->trace_plane_dist = trace.plane.dist; if (trace.ent) pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); else pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); } #ifdef QUAKE2 extern trace_t SV_Trace_Toss(edict_t *ent, edict_t *ignore); void PF_TraceToss(void) { trace_t trace; edict_t *ent; edict_t *ignore; ent = G_EDICT(OFS_PARM0); ignore = G_EDICT(OFS_PARM1); trace = SV_Trace_Toss(ent, ignore); pr_global_struct->trace_allsolid = trace.allsolid; pr_global_struct->trace_startsolid = trace.startsolid; pr_global_struct->trace_fraction = trace.fraction; pr_global_struct->trace_inwater = trace.inwater; pr_global_struct->trace_inopen = trace.inopen; VectorCopy(trace.endpos, pr_global_struct->trace_endpos); VectorCopy(trace.plane.normal, pr_global_struct->trace_plane_normal); pr_global_struct->trace_plane_dist = trace.plane.dist; if (trace.ent) pr_global_struct->trace_ent = EDICT_TO_PROG(trace.ent); else pr_global_struct->trace_ent = EDICT_TO_PROG(sv.edicts); } #endif /* ================= PF_checkpos Returns true if the given entity can move to the given position from it's current position by walking or rolling. FIXME: make work... scalar checkpos (entity, vector) ================= */ void PF_checkpos(void) { } //============================================================================ byte checkpvs[MAX_MAP_LEAFS / 8]; int PF_newcheckclient(int check) { int i; byte *pvs; edict_t *ent; mleaf_t *leaf; vec3_t org; // cycle to the next one if (check < 1) check = 1; if (check > svs.maxclients) check = svs.maxclients; if (check == svs.maxclients) i = 1; else i = check + 1; for (; ; i++) { if (i == svs.maxclients + 1) i = 1; ent = EDICT_NUM(i); if (i == check) break; // didn't find anything else if (ent->free) continue; if (ent->v.health <= 0) continue; if ((int)ent->v.flags & FL_NOTARGET) continue; // anything that is a client, or has a client as an enemy break; } // get the PVS for the entity VectorAdd(ent->v.origin, ent->v.view_ofs, org); leaf = Mod_PointInLeaf(org, sv.worldmodel); pvs = Mod_LeafPVS(leaf, sv.worldmodel); memcpy(checkpvs, pvs, (sv.worldmodel->numleafs + 7) >> 3); return i; } /* ================= PF_checkclient Returns a client (or object that has a client enemy) that would be a valid target. If there are more than one valid options, they are cycled each frame If (self.origin + self.viewofs) is not in the PVS of the current target, it is not returned at all. name checkclient () ================= */ #define MAX_CHECK 16 int c_invis, c_notvis; void PF_checkclient(void) { edict_t *ent, *self; mleaf_t *leaf; int l; vec3_t view; // find a new check if on a new frame if (sv.time - sv.lastchecktime >= 0.1) { sv.lastcheck = PF_newcheckclient(sv.lastcheck); sv.lastchecktime = sv.time; } // return check if it might be visible ent = EDICT_NUM(sv.lastcheck); if (ent->free || ent->v.health <= 0) { RETURN_EDICT(sv.edicts); return; } // if current entity can't possibly see the check entity, return 0 self = PROG_TO_EDICT(pr_global_struct->self); VectorAdd(self->v.origin, self->v.view_ofs, view); leaf = Mod_PointInLeaf(view, sv.worldmodel); l = (leaf - sv.worldmodel->leafs) - 1; if ((l<0) || !(checkpvs[l >> 3] & (1 << (l & 7)))) { c_notvis++; RETURN_EDICT(sv.edicts); return; } // might be able to see it c_invis++; RETURN_EDICT(ent); } //============================================================================ /* ================= PF_stuffcmd Sends text over to the client's execution buffer stuffcmd (clientent, value) ================= */ void PF_stuffcmd(void) { int entnum; char *str; client_t *old; entnum = G_EDICTNUM(OFS_PARM0); if (entnum < 1 || entnum > svs.maxclients) PR_RunError("Parm 0 not a client"); str = G_STRING(OFS_PARM1); old = host_client; host_client = &svs.clients[entnum - 1]; Host_ClientCommands("%s", str); host_client = old; } /* ================= PF_localcmd Sends text over to the client's execution buffer localcmd (string) ================= */ void PF_localcmd(void) { char *str; str = G_STRING(OFS_PARM0); Cbuf_AddText(str); } /* ================= PF_cvar float cvar (string) ================= */ void PF_cvar(void) { char *str; str = G_STRING(OFS_PARM0); G_FLOAT(OFS_RETURN) = Cvar_VariableValue(str); } /* ================= PF_cvar_set float cvar (string) ================= */ void PF_cvar_set(void) { char *var, *val; var = G_STRING(OFS_PARM0); val = G_STRING(OFS_PARM1); Cvar_Set(var, val); } /* ================= PF_findradius Returns a chain of entities that have origins within a spherical area findradius (origin, radius) ================= */ void PF_findradius(void) { edict_t *ent, *chain; float rad; float *org; vec3_t eorg; int i, j; chain = (edict_t *)sv.edicts; org = G_VECTOR(OFS_PARM0); rad = G_FLOAT(OFS_PARM1); ent = NEXT_EDICT(sv.edicts); for (i = 1; ifree) continue; if (ent->v.solid == SOLID_NOT) continue; for (j = 0; j<3; j++) eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5); if (Length(eorg) > rad) continue; ent->v.chain = EDICT_TO_PROG(chain); chain = ent; } RETURN_EDICT(chain); } /* ========= PF_dprint ========= */ void PF_dprint(void) { Con_DPrintf("%s", PF_VarString(0)); } char pr_string_temp[PR_MAX_TEMPSTRING]; void PF_ftos(void) { float v; v = G_FLOAT(OFS_PARM0); if (v == (int)v) sprintf(pr_string_temp, "%d", (int)v); else sprintf(pr_string_temp, "%5.1f", v); G_INT(OFS_RETURN) = pr_string_temp - pr_strings; } void PF_fabs(void) { float v; v = G_FLOAT(OFS_PARM0); G_FLOAT(OFS_RETURN) = fabs(v); } void PF_vtos(void) { sprintf(pr_string_temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM0)[0], G_VECTOR(OFS_PARM0)[1], G_VECTOR(OFS_PARM0)[2]); G_INT(OFS_RETURN) = pr_string_temp - pr_strings; } void PF_etos(void) { sprintf(pr_string_temp, "entity %i", G_EDICTNUM(OFS_PARM0)); G_INT(OFS_RETURN) = pr_string_temp - pr_strings; } void PF_Spawn(void) { edict_t *ed; ed = ED_Alloc(); RETURN_EDICT(ed); } void PF_Remove(void) { edict_t *ed; ed = G_EDICT(OFS_PARM0); ED_Free(ed); } // entity (entity start, .string field, string match) find = #5; void PF_Find(void) { int e; int f; char *s, *t; edict_t *ed; e = G_EDICTNUM(OFS_PARM0); f = G_INT(OFS_PARM1); s = G_STRING(OFS_PARM2); if (!s) PR_RunError("PF_Find: bad search string"); for (e++; e < sv.num_edicts; e++) { ed = EDICT_NUM(e); if (ed->free) continue; t = E_STRING(ed, f); if (!t) continue; if (!strcmp(t, s)) { RETURN_EDICT(ed); return; } } RETURN_EDICT(sv.edicts); } void PR_CheckEmptyString(char *s) { if (s[0] <= ' ') PR_RunError("Bad string"); } void PF_precache_file(void) { // precache_file is only used to copy files with qcc, it does nothing G_INT(OFS_RETURN) = G_INT(OFS_PARM0); } void PF_precache_sound(void) { char *s; int i; if (sv.state != ss_loading) PR_RunError("PF_Precache_*: Precache can only be done in spawn functions"); s = G_STRING(OFS_PARM0); G_INT(OFS_RETURN) = G_INT(OFS_PARM0); PR_CheckEmptyString(s); for (i = 0; iself); yaw = G_FLOAT(OFS_PARM0); dist = G_FLOAT(OFS_PARM1); if (!((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) { G_FLOAT(OFS_RETURN) = 0; return; } yaw = yaw*M_PI * 2 / 360; move[0] = cosf(yaw)*dist; move[1] = sinf(yaw)*dist; move[2] = 0; // save program state, because SV_movestep may call other progs oldf = pr_xfunction; oldself = pr_global_struct->self; G_FLOAT(OFS_RETURN) = SV_movestep(ent, move, true); // restore program state pr_xfunction = oldf; pr_global_struct->self = oldself; } /* =============== PF_droptofloor void() droptofloor =============== */ void PF_droptofloor(void) { edict_t *ent; vec3_t end; trace_t trace; ent = PROG_TO_EDICT(pr_global_struct->self); VectorCopy(ent->v.origin, end); end[2] -= 256; trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.fraction == 1 || trace.allsolid) G_FLOAT(OFS_RETURN) = 0; else { VectorCopy(trace.endpos, ent->v.origin); SV_LinkEdict(ent, false); ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); G_FLOAT(OFS_RETURN) = 1; } } /* =============== PF_lightstyle void(float style, string value) lightstyle =============== */ void PF_lightstyle(void) { int style; char *val; client_t *client; int j; style = G_FLOAT(OFS_PARM0); val = G_STRING(OFS_PARM1); // change the string in sv sv.lightstyles[style] = val; // send message to all clients on this server if (sv.state != ss_active) return; for (j = 0, client = svs.clients; jactive || client->spawned) { MSG_WriteChar(&client->message, svc_lightstyle); MSG_WriteChar(&client->message, style); MSG_WriteString(&client->message, val); } } void PF_rint(void) { float f; f = G_FLOAT(OFS_PARM0); if (f > 0) G_FLOAT(OFS_RETURN) = (int)(f + 0.5); else G_FLOAT(OFS_RETURN) = (int)(f - 0.5); } void PF_floor(void) { G_FLOAT(OFS_RETURN) = floor(G_FLOAT(OFS_PARM0)); } void PF_ceil(void) { G_FLOAT(OFS_RETURN) = ceil(G_FLOAT(OFS_PARM0)); } /* ============= PF_checkbottom ============= */ void PF_checkbottom(void) { edict_t *ent; ent = G_EDICT(OFS_PARM0); G_FLOAT(OFS_RETURN) = SV_CheckBottom(ent); } /* ============= PF_pointcontents ============= */ void PF_pointcontents(void) { float *v; v = G_VECTOR(OFS_PARM0); G_FLOAT(OFS_RETURN) = SV_PointContents(v); } /* ============= PF_nextent entity nextent(entity) ============= */ void PF_nextent(void) { int i; edict_t *ent; i = G_EDICTNUM(OFS_PARM0); while (1) { i++; if (i == sv.num_edicts) { RETURN_EDICT(sv.edicts); return; } ent = EDICT_NUM(i); if (!ent->free) { RETURN_EDICT(ent); return; } } } /* ============= PF_aim Pick a vector for the player to shoot along vector aim(entity, missilespeed) ============= */ CVAR (sv_aim, 0.93, CVAR_NONE) void PF_aim(void) { edict_t *ent, *check, *bestent; vec3_t start, dir, end, bestdir; int i, j; trace_t tr; float dist, bestdist; float speed; ent = G_EDICT(OFS_PARM0); speed = G_FLOAT(OFS_PARM1); VectorCopy(ent->v.origin, start); start[2] += 20; // try sending a trace straight VectorCopy(pr_global_struct->v_forward, dir); VectorMA(start, 2048, dir, end); tr = SV_Move(start, vec3_origin, vec3_origin, end, false, ent); if (tr.ent && tr.ent->v.takedamage == DAMAGE_AIM && (!teamplay.value || ent->v.team <= 0 || ent->v.team != tr.ent->v.team)) { VectorCopy(pr_global_struct->v_forward, G_VECTOR(OFS_RETURN)); return; } // try all possible entities VectorCopy(dir, bestdir); bestdist = sv_aim.value; bestent = NULL; check = NEXT_EDICT(sv.edicts); for (i = 1; iv.takedamage != DAMAGE_AIM) continue; if (check == ent) continue; if (teamplay.value && ent->v.team > 0 && ent->v.team == check->v.team) continue; // don't aim at teammate for (j = 0; j<3; j++) end[j] = check->v.origin[j] + 0.5*(check->v.mins[j] + check->v.maxs[j]); VectorSubtract(end, start, dir); VectorNormalize(dir); dist = DotProduct(dir, pr_global_struct->v_forward); if (dist < bestdist) continue; // to far to turn tr = SV_Move(start, vec3_origin, vec3_origin, end, false, ent); if (tr.ent == check) { // can shoot at this one bestdist = dist; bestent = check; } } if (bestent) { VectorSubtract(bestent->v.origin, ent->v.origin, dir); dist = DotProduct(dir, pr_global_struct->v_forward); VectorScale(pr_global_struct->v_forward, dist, end); end[2] = dir[2]; VectorNormalize(end); VectorCopy(end, G_VECTOR(OFS_RETURN)); } else { VectorCopy(bestdir, G_VECTOR(OFS_RETURN)); } } /* ============== PF_changeyaw This was a major timewaster in progs, so it was converted to C ============== */ void PF_changeyaw(void) { edict_t *ent; float ideal, current, move, speed; ent = PROG_TO_EDICT(pr_global_struct->self); current = anglemod(ent->v.angles[1]); ideal = ent->v.ideal_yaw; speed = ent->v.yaw_speed; if (current == ideal) return; move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->v.angles[1] = anglemod(current + move); } #ifdef QUAKE2 /* ============== PF_changepitch ============== */ void PF_changepitch(void) { edict_t *ent; float ideal, current, move, speed; ent = G_EDICT(OFS_PARM0); current = anglemod(ent->v.angles[0]); ideal = ent->v.idealpitch; speed = ent->v.pitch_speed; if (current == ideal) return; move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } ent->v.angles[0] = anglemod(current + move); } #endif /* =============================================================================== MESSAGE WRITING =============================================================================== */ #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 sizebuf_t *WriteDest(void) { int entnum; int dest; edict_t *ent; dest = G_FLOAT(OFS_PARM0); switch (dest) { case MSG_BROADCAST: return &sv.datagram; case MSG_ONE: ent = PROG_TO_EDICT(pr_global_struct->msg_entity); entnum = NUM_FOR_EDICT(ent); if (entnum < 1 || entnum > svs.maxclients) PR_RunError("WriteDest: not a client"); return &svs.clients[entnum - 1].message; case MSG_ALL: return &sv.reliable_datagram; case MSG_INIT: return &sv.signon; default: PR_RunError("WriteDest: bad destination"); break; } return NULL; } void PF_WriteByte(void) { MSG_WriteByte(WriteDest(), G_FLOAT(OFS_PARM1)); } void PF_WriteChar(void) { MSG_WriteChar(WriteDest(), G_FLOAT(OFS_PARM1)); } void PF_WriteShort(void) { MSG_WriteShort(WriteDest(), G_FLOAT(OFS_PARM1)); } void PF_WriteLong(void) { MSG_WriteLong(WriteDest(), G_FLOAT(OFS_PARM1)); } void PF_WriteAngle(void) { MSG_WriteAngle(WriteDest(), G_FLOAT(OFS_PARM1)); } void PF_WriteCoord(void) { MSG_WriteCoord(WriteDest(), G_FLOAT(OFS_PARM1)); } void PF_WriteString(void) { MSG_WriteString(WriteDest(), G_STRING(OFS_PARM1)); } void PF_WriteEntity(void) { MSG_WriteShort(WriteDest(), G_EDICTNUM(OFS_PARM1)); } //============================================================================= int SV_ModelIndex(char *name); void PF_makestatic(void) { edict_t *ent; int i; ent = G_EDICT(OFS_PARM0); MSG_WriteByte(&sv.signon, svc_spawnstatic); MSG_WriteByte(&sv.signon, SV_ModelIndex(pr_strings + ent->v.model)); MSG_WriteByte(&sv.signon, ent->v.frame); MSG_WriteByte(&sv.signon, ent->v.colormap); MSG_WriteByte(&sv.signon, ent->v.skin); for (i = 0; i<3; i++) { MSG_WriteCoord(&sv.signon, ent->v.origin[i]); MSG_WriteAngle(&sv.signon, ent->v.angles[i]); } // throw the entity away now ED_Free(ent); } //============================================================================= /* ============== PF_setspawnparms ============== */ void PF_setspawnparms(void) { edict_t *ent; int i; client_t *client; ent = G_EDICT(OFS_PARM0); i = NUM_FOR_EDICT(ent); if (i < 1 || i > svs.maxclients) PR_RunError("Entity is not a client"); // copy spawn parms out of the client_t client = svs.clients + (i - 1); for (i = 0; i< NUM_SPAWN_PARMS; i++) (&pr_global_struct->parm1)[i] = client->spawn_parms[i]; } /* ============== PF_changelevel ============== */ void PF_changelevel(void) { #ifdef QUAKE2 char *s1, *s2; if (svs.changelevel_issued) return; svs.changelevel_issued = true; s1 = G_STRING(OFS_PARM0); s2 = G_STRING(OFS_PARM1); if ((int)pr_global_struct->serverflags & (SFL_NEW_UNIT | SFL_NEW_EPISODE)) Cbuf_AddText(va("changelevel %s %s\n", s1, s2)); else Cbuf_AddText(va("changelevel2 %s %s\n", s1, s2)); #else char *s; // make sure we don't issue two changelevels if (svs.changelevel_issued) return; svs.changelevel_issued = true; s = G_STRING(OFS_PARM0); Cbuf_AddText(va("changelevel %s\n", s)); #endif } #ifdef QUAKE2 #define CONTENT_WATER -3 #define CONTENT_SLIME -4 #define CONTENT_LAVA -5 #define FL_IMMUNE_WATER 131072 #define FL_IMMUNE_SLIME 262144 #define FL_IMMUNE_LAVA 524288 #define CHAN_VOICE 2 #define CHAN_BODY 4 #define ATTN_NORM 1 void PF_WaterMove(void) { edict_t *self; int flags; int waterlevel; int watertype; float drownlevel; float damage = 0.0; self = PROG_TO_EDICT(pr_global_struct->self); if (self->v.movetype == MOVETYPE_NOCLIP) { self->v.air_finished = sv.time + 12; G_FLOAT(OFS_RETURN) = damage; return; } if (self->v.health < 0) { G_FLOAT(OFS_RETURN) = damage; return; } if (self->v.deadflag == DEAD_NO) drownlevel = 3; else drownlevel = 1; flags = (int)self->v.flags; waterlevel = (int)self->v.waterlevel; watertype = (int)self->v.watertype; if (!(flags & (FL_IMMUNE_WATER + FL_GODMODE))) if (((flags & FL_SWIM) && (waterlevel < drownlevel)) || (waterlevel >= drownlevel)) { if (self->v.air_finished < sv.time) if (self->v.pain_finished < sv.time) { self->v.dmg = self->v.dmg + 2; if (self->v.dmg > 15) self->v.dmg = 10; // T_Damage (self, world, world, self.dmg, 0, FALSE); damage = self->v.dmg; self->v.pain_finished = sv.time + 1.0; } } else { if (self->v.air_finished < sv.time) // sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM); SV_StartSound(self, CHAN_VOICE, "player/gasp2.wav", 255, ATTN_NORM); else if (self->v.air_finished < sv.time + 9) // sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM); SV_StartSound(self, CHAN_VOICE, "player/gasp1.wav", 255, ATTN_NORM); self->v.air_finished = sv.time + 12.0; self->v.dmg = 2; } if (!waterlevel) { if (flags & FL_INWATER) { // play leave water sound // sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM); SV_StartSound(self, CHAN_BODY, "misc/outwater.wav", 255, ATTN_NORM); self->v.flags = (float)(flags &~FL_INWATER); } self->v.air_finished = sv.time + 12.0; G_FLOAT(OFS_RETURN) = damage; return; } if (watertype == CONTENT_LAVA) { // do damage if (!(flags & (FL_IMMUNE_LAVA + FL_GODMODE))) if (self->v.dmgtime < sv.time) { if (self->v.radsuit_finished < sv.time) self->v.dmgtime = sv.time + 0.2; else self->v.dmgtime = sv.time + 1.0; // T_Damage (self, world, world, 10*self.waterlevel, 0, TRUE); damage = (float)(10 * waterlevel); } } else if (watertype == CONTENT_SLIME) { // do damage if (!(flags & (FL_IMMUNE_SLIME + FL_GODMODE))) if (self->v.dmgtime < sv.time && self->v.radsuit_finished < sv.time) { self->v.dmgtime = sv.time + 1.0; // T_Damage (self, world, world, 4*self.waterlevel, 0, TRUE); damage = (float)(4 * waterlevel); } } if (!(flags & FL_INWATER)) { // player enter water sound if (watertype == CONTENT_LAVA) // sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM); SV_StartSound(self, CHAN_BODY, "player/inlava.wav", 255, ATTN_NORM); if (watertype == CONTENT_WATER) // sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM); SV_StartSound(self, CHAN_BODY, "player/inh2o.wav", 255, ATTN_NORM); if (watertype == CONTENT_SLIME) // sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM); SV_StartSound(self, CHAN_BODY, "player/slimbrn2.wav", 255, ATTN_NORM); self->v.flags = (float)(flags | FL_INWATER); self->v.dmgtime = 0; } if (!(flags & FL_WATERJUMP)) { // self.velocity = self.velocity - 0.8*self.waterlevel*frametime*self.velocity; VectorMA(self->v.velocity, -0.8 * self->v.waterlevel * host_frametime, self->v.velocity, self->v.velocity); } G_FLOAT(OFS_RETURN) = damage; } #endif void PF_sin(void) { G_FLOAT(OFS_RETURN) = sinf(G_FLOAT(OFS_PARM0)); } void PF_cos(void) { G_FLOAT(OFS_RETURN) = cosf(G_FLOAT(OFS_PARM0)); } void PF_sqrt(void) { G_FLOAT(OFS_RETURN) = sqrt(G_FLOAT(OFS_PARM0)); } void PF_asin(void) { G_FLOAT(OFS_RETURN) = asin(G_FLOAT(OFS_PARM0)); } void PF_acos(void) { G_FLOAT(OFS_RETURN) = acos(G_FLOAT(OFS_PARM0)); } void PF_atan(void) { G_FLOAT(OFS_RETURN) = atan(G_FLOAT(OFS_PARM0)); } void PF_atan2(void) { G_FLOAT(OFS_RETURN) = atan2(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1)); } void PF_tan(void) { G_FLOAT(OFS_RETURN) = tan(G_FLOAT(OFS_PARM0)); } void PF_randomvec(void) { vec3_t temp; VectorRandom(temp); VectorCopy(temp, G_VECTOR(OFS_RETURN)); } /* ================= PF_min Returns the minimum of two or more supplied floats float min(float f1, float f2, ...) ================= */ void PF_min (void) { // LordHavoc: 3+ argument enhancement suggested by FrikaC if (pr_argc == 2) G_FLOAT(OFS_RETURN) = min(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1)); else if (pr_argc >= 3) { int i; float f = G_FLOAT(OFS_PARM0); for (i = 1;i < pr_argc;i++) if (G_FLOAT((OFS_PARM0+i*3)) < f) f = G_FLOAT((OFS_PARM0+i*3)); G_FLOAT(OFS_RETURN) = f; } else PR_RunError("min: must supply at least 2 floats\n"); } /* ================= PF_max Returns the maximum of two or more supplied floats float max(float f1, float f2, ...) ================= */ void PF_max (void) { // LordHavoc: 3+ argument enhancement suggested by FrikaC if (pr_argc == 2) G_FLOAT(OFS_RETURN) = max(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1)); else if (pr_argc >= 3) { int i; float f = G_FLOAT(OFS_PARM0); for (i = 1;i < pr_argc;i++) if (G_FLOAT((OFS_PARM0+i*3)) > f) f = G_FLOAT((OFS_PARM0+i*3)); G_FLOAT(OFS_RETURN) = f; } else PR_RunError("max: must supply at least 2 floats\n"); } /* ================= PF_bound Returns number bounded by supplied range float bound(float min, float f, float max) ================= */ void PF_bound (void) { G_FLOAT(OFS_RETURN) = min(G_FLOAT(OFS_PARM2), max(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1))); } /* ================= PF_pow Returns base raised to power exp (base^exp) float pow(float base, float exp) ================= */ void PF_pow(void) { G_FLOAT(OFS_RETURN) = pow(G_FLOAT(OFS_PARM0), G_FLOAT(OFS_PARM1)); } // chained search for strings in entity fields // entity(.string field, string match) findchain = #402; void PF_findchain (void) { int i; int f; char *s, *t; edict_t *ent, *chain; chain = (edict_t *)sv.edicts; f = G_INT(OFS_PARM0); s = G_STRING(OFS_PARM1); if (!s || !s[0]) { RETURN_EDICT(sv.edicts); return; } ent = NEXT_EDICT(sv.edicts); for (i = 1;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; t = E_STRING(ent,f); if (!t) continue; if (strcmp(t,s)) continue; ent->v.chain = EDICT_TO_PROG(chain); chain = ent; } RETURN_EDICT(chain); } // LordHavoc: chained search for float, int, and entity reference fields // entity(.string field, float match) findchainfloat = #403; void PF_findchainfloat (void) { int i; int f; float s; edict_t *ent, *chain; chain = (edict_t *)sv.edicts; f = G_INT(OFS_PARM0); s = G_FLOAT(OFS_PARM1); ent = NEXT_EDICT(sv.edicts); for (i = 1;i < sv.num_edicts;i++, ent = NEXT_EDICT(ent)) { if (ent->free) continue; if (E_FLOAT(ent,f) != s) continue; ent->v.chain = EDICT_TO_PROG(chain); chain = ent; } RETURN_EDICT(chain); } void PF_log(void) { G_FLOAT(OFS_RETURN) = log(G_FLOAT(OFS_PARM0)); } void PF_bitshift(void) { int n1 = (int)fabs(G_FLOAT(OFS_PARM0)); int n2 = (int)G_FLOAT(OFS_PARM1); if (!n1) G_FLOAT(OFS_RETURN) = n1; else if (n2 < 0) G_FLOAT(OFS_RETURN) = (n1 >> -n2); else G_FLOAT(OFS_RETURN) = (n1 << n2); } void PF_Fixme(void) { PR_RunError("unimplemented bulitin"); } // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes start /* ================= PF_strzone string strzone (string) ================= */ void PF_strzone (void) { char *m, *p; m = G_STRING(OFS_PARM0); p = Z_Malloc(strlen(m) + 1); strcpy(p, m); G_INT(OFS_RETURN) = p - pr_strings; } /* ================= PF_strunzone string strunzone (string) ================= */ void PF_strunzone (void) { Z_Free(G_STRING(OFS_PARM0)); G_INT(OFS_PARM0) = OFS_NULL; // empty the def }; /* ================= PF_strlen float strlen (string) ================= */ void PF_strlen (void) { char *p = G_STRING(OFS_PARM0); G_FLOAT(OFS_RETURN) = strlen(p); } /* ================= PF_strcat string strcat (string, string) ================= */ void PF_strcat (void) { char *s1, *s2; int maxlen; // 2001-10-25 Enhanced temp string handling by Maddes s1 = G_STRING(OFS_PARM0); s2 = PF_VarString(1); // 2001-10-25 Enhanced temp string handling by Maddes start pr_string_temp[0] = 0; if (strlen(s1) < PR_MAX_TEMPSTRING) { strcpy(pr_string_temp, s1); } else { strncpy(pr_string_temp, s1, PR_MAX_TEMPSTRING); pr_string_temp[PR_MAX_TEMPSTRING-1] = 0; } maxlen = PR_MAX_TEMPSTRING - strlen(pr_string_temp) - 1; // -1 is EndOfString if (maxlen > 0) { if (maxlen > strlen(s2)) { strcat (pr_string_temp, s2); } else { strncat (pr_string_temp, s2, maxlen); pr_string_temp[PR_MAX_TEMPSTRING-1] = 0; } } // 2001-10-25 Enhanced temp string handling by Maddes end G_INT(OFS_RETURN) = pr_string_temp - pr_strings; } /* ================= PF_substring string substring (string, float, float) ================= */ void PF_substring (void) { int offset, length; int maxoffset; // 2001-10-25 Enhanced temp string handling by Maddes char *p; p = G_STRING(OFS_PARM0); offset = (int)G_FLOAT(OFS_PARM1); // for some reason, Quake doesn't like G_INT length = (int)G_FLOAT(OFS_PARM2); // cap values maxoffset = strlen(p); if (offset > maxoffset) { offset = maxoffset; } if (offset < 0) offset = 0; // 2001-10-25 Enhanced temp string handling by Maddes start if (length >= PR_MAX_TEMPSTRING) length = PR_MAX_TEMPSTRING-1; // 2001-10-25 Enhanced temp string handling by Maddes end if (length < 0) length = 0; p += offset; strncpy(pr_string_temp, p, length); pr_string_temp[length]=0; G_INT(OFS_RETURN) = pr_string_temp - pr_strings; } /* ================= PF_stof float stof (string) ================= */ // thanks Zoid, taken from QuakeWorld void PF_stof (void) { char *s; s = G_STRING(OFS_PARM0); G_FLOAT(OFS_RETURN) = atof(s); } /* ================= PF_stov vector stov (string) ================= */ void PF_stov (void) { char *v; int i; vec3_t d; v = G_STRING(OFS_PARM0); for (i=0; i<3; i++) { while(v && (v[0] == ' ' || v[0] == '\'')) //skip unneeded data v++; d[i] = atof(v); while (v && v[0] != ' ') // skip to next space v++; } VectorCopy (d, G_VECTOR(OFS_RETURN)); } // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes end // 2001-09-20 QuakeC file access by FrikaC/Maddes start /* ================= PF_fopen float fopen (string,float) ================= */ void PF_fopen (void) { char *p = G_STRING(OFS_PARM0); char *ftemp; int fmode = G_FLOAT(OFS_PARM1); int h = 0, fsize = 0; switch (fmode) { case 0: // read Sys_FileOpenRead (va("%s/%s",com_gamedir, p), &h); G_FLOAT(OFS_RETURN) = (float) h; return; case 1: // append -- this is nasty // copy whole file into the zone fsize = Sys_FileOpenRead(va("%s/%s",com_gamedir, p), &h); if (h == -1) { h = Sys_FileOpenWrite(va("%s/%s",com_gamedir, p)); G_FLOAT(OFS_RETURN) = (float) h; return; } ftemp = Z_Malloc(fsize + 1); Sys_FileRead(h, ftemp, fsize); Sys_FileClose(h); // spit it back out h = Sys_FileOpenWrite(va("%s/%s",com_gamedir, p)); Sys_FileWrite(h, ftemp, fsize); Z_Free(ftemp); // free it from memory G_FLOAT(OFS_RETURN) = (float) h; // return still open handle return; default: // write h = Sys_FileOpenWrite (va("%s/%s", com_gamedir, p)); G_FLOAT(OFS_RETURN) = (float) h; return; } } /* ================= PF_fclose void fclose (float) ================= */ void PF_fclose (void) { int h = (int)G_FLOAT(OFS_PARM0); Sys_FileClose(h); } /* ================= PF_fgets string fgets (float) ================= */ void PF_fgets (void) { // reads one line (up to a \n) into a string int h; int i; int count; char buffer; h = (int)G_FLOAT(OFS_PARM0); count = Sys_FileRead(h, &buffer, 1); if (count && buffer == '\r') // carriage return { count = Sys_FileRead(h, &buffer, 1); // skip } if (!count) // EndOfFile { G_INT(OFS_RETURN) = OFS_NULL; // void string return; } i = 0; while (count && buffer != '\n') { if (i < PR_MAX_TEMPSTRING-1) // no place for character in temp string { pr_string_temp[i++] = buffer; } // read next character count = Sys_FileRead(h, &buffer, 1); if (count && buffer == '\r') // carriage return { count = Sys_FileRead(h, &buffer, 1); // skip } }; pr_string_temp[i] = 0; G_INT(OFS_RETURN) = pr_string_temp - pr_strings; } /* ================= PF_fputs void fputs (float,string) ================= */ void PF_fputs (void) { // writes to file, like bprint float handle = G_FLOAT(OFS_PARM0); char *str = PF_VarString(1); Sys_FileWrite (handle, str, strlen(str)); } // 2001-09-20 QuakeC file access by FrikaC/Maddes end // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes start /* ================= PF_cmd_find float cmd_find (string) ================= */ void PF_cmd_find (void) { char *cmdname; float result; cmdname = G_STRING(OFS_PARM0); result = Cmd_Exists (cmdname); G_FLOAT(OFS_RETURN) = result; } // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes end // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes start /* ================= PF_cvar_find float cvar_find (string) ================= */ void PF_cvar_find (void) { char *varname; float result; varname = G_STRING(OFS_PARM0); result = 0; if (Cvar_FindVar (varname)) { result = 1; } G_FLOAT(OFS_RETURN) = result; } // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes end // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes start /* ================= PF_cvar_string string cvar_string (string) ================= */ void PF_cvar_string (void) { char *varname; cvar_t *var; varname = G_STRING(OFS_PARM0); var = Cvar_FindVar (varname); if (!var) { Con_DPrintf ("Cvar_String: variable \"%s\" not found\n", varname); // 2001-09-09 Made 'Cvar not found' a developer message by Maddes G_INT(OFS_RETURN) = OFS_NULL; } else { G_INT(OFS_RETURN) = var->string - pr_strings; } } // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes end // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes start /* PF_WriteFloat void (float to, float f) WriteFloat */ void PF_WriteFloat (void) { MSG_WriteFloat (WriteDest(), G_FLOAT(OFS_PARM1)); } // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes end // 2001-09-25 New BuiltIn Function: etof() by Maddes start /* ================= PF_etof float etof (entity) ================= */ void PF_etof (void) { G_FLOAT(OFS_RETURN) = G_EDICTNUM(OFS_PARM0); } // 2001-09-25 New BuiltIn Function: etof() by Maddes end // 2001-09-25 New BuiltIn Function: ftoe() by Maddes start /* ================= PF_ftoe entity ftoe (float) ================= */ void PF_ftoe (void) { edict_t *e; e = EDICT_NUM(G_FLOAT(OFS_PARM0)); RETURN_EDICT(e); } // 2001-09-25 New BuiltIn Function: ftoe() by Maddes end builtin_t *pr_builtins; int pr_numbuiltins; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start // for builtin function definitions see Quake Standards Group at http://www.quakesrc.org/ /* ================= PF_builtin_find float builtin_find (string) ================= */ void PF_builtin_find (void) { int j; float funcno; char *funcname; funcno = 0; funcname = G_STRING(OFS_PARM0); // search function name for ( j=1 ; j < pr_ebfs_numbuiltins ; j++) { if ((pr_ebfs_builtins[j].funcname) && (!(strcasecmp(funcname,pr_ebfs_builtins[j].funcname)))) { break; // found } } if (j < pr_ebfs_numbuiltins) { funcno = pr_ebfs_builtins[j].funcno; } G_FLOAT(OFS_RETURN) = funcno; } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end ebfs_builtin_t pr_ebfs_builtins[] = { { 0, NULL, PF_Fixme }, // has to be first entry as it is needed for initialization in PR_LoadProgs() { 1, "makevectors", PF_makevectors }, // void(entity e) makevectors = #1; { 2, "setorigin", PF_setorigin }, // void(entity e, vector o) setorigin = #2; { 3, "setmodel", PF_setmodel }, // void(entity e, string m) setmodel = #3; { 4, "setsize", PF_setsize }, // void(entity e, vector min, vector max) setsize = #4; // { 5, "fixme", PF_Fixme }, // void(entity e, vector min, vector max) setabssize = #5; { 6, "break", PF_break }, // void() break = #6; { 7, "random", PF_random }, // float() random = #7; { 8, "sound", PF_sound }, // void(entity e, float chan, string samp) sound = #8; { 9, "normalize", PF_normalize }, // vector(vector v) normalize = #9; { 10, "error", PF_error }, // void(string e) error = #10; { 11, "objerror", PF_objerror }, // void(string e) objerror = #11; { 12, "vlen", PF_vlen }, // float(vector v) vlen = #12; { 13, "vectoyaw", PF_vectoyaw }, // float(vector v) vectoyaw = #13; { 14, "spawn", PF_Spawn }, // entity() spawn = #14; { 15, "remove", PF_Remove }, // void(entity e) remove = #15; { 16, "traceline", PF_traceline }, // float(vector v1, vector v2, float tryents) traceline = #16; { 17, "checkclient", PF_checkclient }, // entity() clientlist = #17; { 18, "find", PF_Find }, // entity(entity start, .string fld, string match) find = #18; { 19, "precache_sound", PF_precache_sound }, // void(string s) precache_sound = #19; { 20, "precache_model", PF_precache_model }, // void(string s) precache_model = #20; { 21, "stuffcmd", PF_stuffcmd }, // void(entity client, string s)stuffcmd = #21; { 22, "findradius", PF_findradius }, // entity(vector org, float rad) findradius = #22; { 23, "bprint", PF_bprint }, // void(string s) bprint = #23; { 24, "sprint", PF_sprint }, // void(entity client, string s) sprint = #24; { 25, "dprint", PF_dprint }, // void(string s) dprint = #25; { 26, "ftos", PF_ftos }, // void(string s) ftos = #26; { 27, "vtos", PF_vtos }, // void(string s) vtos = #27; { 28, "coredump", PF_coredump }, { 29, "traceon", PF_traceon }, { 30, "traceoff", PF_traceoff }, { 31, "eprint", PF_eprint }, // void(entity e) debug print an entire entity { 32, "walkmove", PF_walkmove }, // float(float yaw, float dist) walkmove { 34, "droptofloor", PF_droptofloor }, { 35, "lightstyle", PF_lightstyle }, { 36, "rint", PF_rint }, { 37, "floor", PF_floor }, { 38, "ceil", PF_ceil }, { 40, "checkbottom", PF_checkbottom }, { 41, "pointcontents", PF_pointcontents }, { 43, "fabs", PF_fabs }, { 44, "aim", PF_aim }, { 45, "cvar", PF_cvar }, { 46, "localcmd", PF_localcmd }, { 47, "nextent", PF_nextent }, { 48, "particle", PF_particle }, { 49, "ChangeYaw", PF_changeyaw }, { 51, "vectoangles", PF_vectoangles }, { 52, "WriteByte", PF_WriteByte }, { 53, "WriteChar", PF_WriteChar }, { 54, "WriteShort", PF_WriteShort }, { 55, "WriteLong", PF_WriteLong }, { 56, "WriteCoord", PF_WriteCoord }, { 57, "WriteAngle", PF_WriteAngle }, { 58, "WriteString", PF_WriteString }, { 59, "WriteEntity", PF_WriteEntity }, { 60, "sin", PF_sin }, { 61, "cos", PF_cos }, { 62, "sqrt", PF_sqrt }, #ifdef QUAKE2 { 63, "changepitch", PF_changepitch }, { 64, "TraceToss", PF_TraceToss }, #endif { 65, "etos", PF_etos }, #ifdef QUAKE2 { 66, "WaterMove", PF_WaterMove }, #endif { 67, "movetogoal", SV_MoveToGoal }, { 68, "precache_file", PF_precache_file }, { 69, "makestatic", PF_makestatic }, { 70, "changelevel", PF_changelevel }, { 72, "cvar_set", PF_cvar_set }, { 73, "centerprint", PF_centerprint }, { 74, "ambientsound", PF_ambientsound }, { 75, "precache_model2", PF_precache_model }, { 76, "precache_sound2", PF_precache_sound }, // precache_sound2 is different only for qcc { 77, "precache_file2", PF_precache_file }, { 78, "setspawnparms", PF_setspawnparms }, { 81, "stof", PF_stof }, // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes { 90, "tracebox", PF_tracebox}, { 91, "randomvec", PF_randomvec}, { 94, "min", PF_min}, { 95, "max", PF_max}, { 96, "bound", PF_bound}, { 97, "pow", PF_pow }, { PR_DEFAULT_FUNCNO_EXTENSION_FIND, "extension_find", PF_extension_find }, // 2001-10-20 Extension System by Lord Havoc/Maddes { PR_DEFAULT_FUNCNO_BUILTIN_FIND, "builtin_find", PF_builtin_find }, // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes { 101, "cmd_find", PF_cmd_find }, // 2001-09-16 New BuiltIn Function: cmd_find() by Maddes { 102, "cvar_find", PF_cvar_find }, // 2001-09-16 New BuiltIn Function: cvar_find() by Maddes { 107, "WriteFloat", PF_WriteFloat }, // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes { 110, "fopen", PF_fopen }, { 111, "fclose", PF_fclose }, { 112, "fgets", PF_fgets }, { 113, "fputs", PF_fputs }, { 114, "strlen", PF_strlen }, { 115, "strcat", PF_strcat }, { 116, "substring", PF_substring }, { 117, "stov", PF_stov }, { 118, "strzone", PF_strzone }, { 119, "strunzone", PF_strunzone }, { 218, "bitshift", PF_bitshift }, { 400, "copyentity", PF_copyentity }, { 402, "findchain", PF_findchain }, { 403, "findchainfloat", PF_findchainfloat }, { 448, "cvar_string", PF_cvar_string }, // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes { 459, "ftoe", PF_ftoe }, // 2001-09-25 New BuiltIn Function: ftoe() by Maddes { 471, "asin", PF_asin }, { 472, "acos", PF_acos }, { 473, "atan", PF_atan }, { 474, "atan2", PF_atan2 }, { 475, "tan", PF_tan }, { 512, "etof", PF_etof }, // 2001-09-25 New BuiltIn Function: etof() by Maddes { 532, "log", PF_log } }; int pr_ebfs_numbuiltins = sizeof(pr_ebfs_builtins)/sizeof(pr_ebfs_builtins[0]); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end ================================================ FILE: source/pr_comp.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // this file is shared by quake and qcc typedef int func_t; typedef int string_t; typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t; #define OFS_NULL 0 #define OFS_RETURN 1 #define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors #define OFS_PARM1 7 #define OFS_PARM2 10 #define OFS_PARM3 13 #define OFS_PARM4 16 #define OFS_PARM5 19 #define OFS_PARM6 22 #define OFS_PARM7 25 #define RESERVED_OFS 28 enum { OP_DONE, OP_MUL_F, OP_MUL_V, OP_MUL_FV, OP_MUL_VF, OP_DIV_F, OP_ADD_F, OP_ADD_V, OP_SUB_F, OP_SUB_V, OP_EQ_F, OP_EQ_V, OP_EQ_S, OP_EQ_E, OP_EQ_FNC, OP_NE_F, OP_NE_V, OP_NE_S, OP_NE_E, OP_NE_FNC, OP_LE, OP_GE, OP_LT, OP_GT, OP_LOAD_F, OP_LOAD_V, OP_LOAD_S, OP_LOAD_ENT, OP_LOAD_FLD, OP_LOAD_FNC, OP_ADDRESS, OP_STORE_F, OP_STORE_V, OP_STORE_S, OP_STORE_ENT, OP_STORE_FLD, OP_STORE_FNC, OP_STOREP_F, OP_STOREP_V, OP_STOREP_S, OP_STOREP_ENT, OP_STOREP_FLD, OP_STOREP_FNC, OP_RETURN, OP_NOT_F, OP_NOT_V, OP_NOT_S, OP_NOT_ENT, OP_NOT_FNC, OP_IF, OP_IFNOT, OP_CALL0, OP_CALL1, OP_CALL2, OP_CALL3, OP_CALL4, OP_CALL5, OP_CALL6, OP_CALL7, OP_CALL8, OP_STATE, OP_GOTO, OP_AND, OP_OR, OP_BITAND, OP_BITOR }; typedef struct statement_s { unsigned short op; unsigned short a,b,c; } dstatement_t; typedef struct { unsigned short type; // if DEF_SAVEGLOBGAL bit is set // the variable needs to be saved in savegames unsigned short ofs; int s_name; } ddef_t; #define DEF_SAVEGLOBAL (1<<15) #define MAX_PARMS 8 typedef struct { int first_statement; // negative numbers are builtins int parm_start; int locals; // total ints of parms + locals int profile; // runtime int s_name; int s_file; // source file defined in int numparms; byte parm_size[MAX_PARMS]; } dfunction_t; #define PROG_VERSION 6 typedef struct { int version; int crc; // check of header file int ofs_statements; int numstatements; // statement 0 is an error int ofs_globaldefs; int numglobaldefs; int ofs_fielddefs; int numfielddefs; int ofs_functions; int numfunctions; // function 0 is an empty int ofs_strings; int numstrings; // first string is a null string int ofs_globals; int numglobals; int entityfields; } dprograms_t; ================================================ FILE: source/pr_edict.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_edict.c -- entity dictionary #include "quakedef.h" dprograms_t *progs; dfunction_t *pr_functions; char *pr_strings; ddef_t *pr_fielddefs; ddef_t *pr_globaldefs; dstatement_t *pr_statements; globalvars_t *pr_global_struct; float *pr_globals; // same as pr_global_struct int pr_edict_size; // in bytes unsigned short pr_crc; int type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4}; ddef_t *ED_FieldAtOfs (int ofs); bool ED_ParseEpair (void *base, ddef_t *key, char *s); cvar_t nomonsters = {"nomonsters", "0"}; cvar_t gamecfg = {"gamecfg", "0"}; cvar_t scratch1 = {"scratch1", "0"}; cvar_t scratch2 = {"scratch2", "0"}; cvar_t scratch3 = {"scratch3", "0"}; cvar_t scratch4 = {"scratch4", "0"}; cvar_t savedgamecfg = {"savedgamecfg", "0", true}; cvar_t saved1 = {"saved1", "0", true}; cvar_t saved2 = {"saved2", "0", true}; cvar_t saved3 = {"saved3", "0", true}; cvar_t saved4 = {"saved4", "0", true}; cvar_t pr_checkextension = {"pr_checkextension", "0", false, false}; // 2001-10-20 Extension System by LordHavoc (DP compatibility) // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start cvar_t pr_builtin_find = {"pr_builtin_find", "0", false, false}; cvar_t pr_builtin_remap = {"pr_builtin_remap", "0", false, false}; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end #define MAX_FIELD_LEN 64 #define GEFV_CACHESIZE 2 typedef struct { ddef_t *pcache; char field[MAX_FIELD_LEN]; } gefv_cache; static gefv_cache gefvCache[GEFV_CACHESIZE] = {{NULL, ""}, {NULL, ""}}; int eval_alpha; int eval_renderamt; int eval_nodrawtoclient; int eval_drawonlytoclient; ddef_t *ED_FindField (char *name); dfunction_t *ED_FindFunction (char *name); int FindFieldOffset (char *field) { ddef_t *d; if (!(d = ED_FindField(field))) return 0; return d->ofs*4; } dfunction_t *EndFrameQC; void FindEdictFieldOffsets (void) { eval_alpha = FindFieldOffset ("alpha"); eval_renderamt = FindFieldOffset ("renderamt"); eval_nodrawtoclient = FindFieldOffset("nodrawtoclient"); eval_drawonlytoclient = FindFieldOffset("drawonlytoclient"); EndFrameQC = ED_FindFunction ("EndFrame"); } /* ================= ED_ClearEdict Sets everything to NULL ================= */ void ED_ClearEdict (edict_t *e) { memset (&e->v, 0, progs->entityfields * 4); e->free = false; } /* ================= ED_Alloc Either finds a free edict, or allocates a new one. Try to avoid reusing an entity that was recently freed, because it can cause the client to think the entity morphed into something else instead of being removed and recreated, which can cause interpolated angles and bad trails. ================= */ edict_t *ED_Alloc (void) { int i; edict_t *e; for ( i=svs.maxclients+1 ; ifree && ( e->freetime < 2 || sv.time - e->freetime > 0.5 ) ) { ED_ClearEdict (e); return e; } } if (i == MAX_EDICTS) Sys_Error ("ED_Alloc: no free edicts"); sv.num_edicts++; e = EDICT_NUM(i); ED_ClearEdict (e); return e; } /* ================= ED_Free Marks the edict as free FIXME: walk all entities and NULL out references to this entity ================= */ void ED_Free (edict_t *ed) { SV_UnlinkEdict (ed); // unlink from world bsp ed->free = true; ed->v.model = 0; ed->v.takedamage = 0; ed->v.modelindex = 0; ed->v.colormap = 0; ed->v.skin = 0; ed->v.frame = 0; VectorCopy (vec3_origin, ed->v.origin); VectorCopy (vec3_origin, ed->v.angles); ed->v.nextthink = -1; ed->v.solid = 0; ed->freetime = sv.time; } //=========================================================================== /* ============ ED_GlobalAtOfs ============ */ ddef_t *ED_GlobalAtOfs (int ofs) { ddef_t *def; int i; for (i=0 ; inumglobaldefs ; i++) { def = &pr_globaldefs[i]; if (def->ofs == ofs) return def; } return NULL; } /* ============ ED_FieldAtOfs ============ */ ddef_t *ED_FieldAtOfs (int ofs) { ddef_t *def; int i; for (i=0 ; inumfielddefs ; i++) { def = &pr_fielddefs[i]; if (def->ofs == ofs) return def; } return NULL; } /* ============ ED_FindField ============ */ ddef_t *ED_FindField (char *name) { ddef_t *def; int i; for (i=0 ; inumfielddefs ; i++) { def = &pr_fielddefs[i]; if (!strcmp(pr_strings + def->s_name,name) ) return def; } return NULL; } /* ============ ED_FindGlobal ============ */ ddef_t *ED_FindGlobal (char *name) { ddef_t *def; int i; for (i=0 ; inumglobaldefs ; i++) { def = &pr_globaldefs[i]; if (!strcmp(pr_strings + def->s_name,name) ) return def; } return NULL; } /* ============ ED_FindFunction ============ */ dfunction_t *ED_FindFunction (char *name) { dfunction_t *func; int i; for (i=0 ; inumfunctions ; i++) { func = &pr_functions[i]; if (!strcmp(pr_strings + func->s_name,name) ) return func; } return NULL; } eval_t *GetEdictFieldValue(edict_t *ed, char *field) { ddef_t *def = NULL; int i; static int rep = 0; for (i=0 ; iv + def->ofs*4); } /* ============ PR_ValueString Returns a string describing *data in a type specific manner ============= */ char *PR_ValueString (etype_t type, eval_t *val) { static char line[256]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: sprintf (line, "%s", pr_strings + val->string); break; case ev_entity: sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); break; case ev_function: f = pr_functions + val->function; sprintf (line, "%s()", pr_strings + f->s_name); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); sprintf (line, ".%s", pr_strings + def->s_name); break; case ev_void: sprintf (line, "void"); break; case ev_float: sprintf (line, "%5.1f", val->_float); break; case ev_vector: sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); break; case ev_pointer: sprintf (line, "pointer"); break; default: sprintf (line, "bad type %i", type); break; } return line; } /* ============ PR_UglyValueString Returns a string describing *data in a type specific manner Easier to parse than PR_ValueString ============= */ char *PR_UglyValueString (etype_t type, eval_t *val) { static char line[256]; ddef_t *def; dfunction_t *f; type &= ~DEF_SAVEGLOBAL; switch (type) { case ev_string: sprintf (line, "%s", pr_strings + val->string); break; case ev_entity: sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); break; case ev_function: f = pr_functions + val->function; sprintf (line, "%s", pr_strings + f->s_name); break; case ev_field: def = ED_FieldAtOfs ( val->_int ); sprintf (line, "%s", pr_strings + def->s_name); break; case ev_void: sprintf (line, "void"); break; case ev_float: sprintf (line, "%f", val->_float); break; case ev_vector: sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); break; default: sprintf (line, "bad type %i", type); break; } return line; } /* ============ PR_GlobalString Returns a string with a description and the contents of a global, padded to 20 field width ============ */ char *PR_GlobalString (int ofs) { char *s; int i; ddef_t *def; void *val; static char line[128]; val = (void *)&pr_globals[ofs]; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(?)", ofs); else { s = PR_ValueString (def->type, val); sprintf (line,"%i(%s)%s", ofs, pr_strings + def->s_name, s); } i = strlen(line); for ( ; i<20 ; i++) strcat (line," "); strcat (line," "); return line; } char *PR_GlobalStringNoContents (int ofs) { int i; ddef_t *def; static char line[128]; def = ED_GlobalAtOfs(ofs); if (!def) sprintf (line,"%i(?)", ofs); else sprintf (line,"%i(%s)", ofs, pr_strings + def->s_name); i = strlen(line); for ( ; i<20 ; i++) strcat (line," "); strcat (line," "); return line; } /* ============= ED_Print For debugging ============= */ void ED_Print (edict_t *ed) { int l; ddef_t *d; int *v; int i, j; char *name; int type; if (ed->free) { Con_Printf ("FREE\n"); return; } Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed)); for (i=1 ; inumfielddefs ; i++) { d = &pr_fielddefs[i]; name = pr_strings + d->s_name; if (name[strlen(name)-2] == '_') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j=0 ; jtype, (eval_t *)v)); } } /* ============= ED_Write For savegames ============= */ void ED_Write (FILE *f, edict_t *ed) { ddef_t *d; int *v; int i, j; char *name; int type; fprintf (f, "{\n"); if (ed->free) { fprintf (f, "}\n"); return; } for (i=1 ; inumfielddefs ; i++) { d = &pr_fielddefs[i]; name = pr_strings + d->s_name; if (name[strlen(name)-2] == '_') continue; // skip _x, _y, _z vars v = (int *)((char *)&ed->v + d->ofs*4); // if the value is still all 0, skip the field type = d->type & ~DEF_SAVEGLOBAL; for (j=0 ; jtype, (eval_t *)v)); } fprintf (f, "}\n"); } void ED_PrintNum (int ent) { ED_Print (EDICT_NUM(ent)); } /* ============= ED_PrintEdicts For debugging, prints all the entities in the current server ============= */ void ED_PrintEdicts (void) { int i; Con_Printf ("%i entities\n", sv.num_edicts); for (i=0 ; i= sv.num_edicts) { Con_Printf("Bad edict number\n"); return; } ED_PrintNum (i); } /* ============= ED_Count For debugging ============= */ void ED_Count (void) { int i; edict_t *ent; int active, models, solid, step; active = models = solid = step = 0; for (i=0 ; ifree) continue; active++; if (ent->v.solid) solid++; if (ent->v.model) models++; if (ent->v.movetype == MOVETYPE_STEP) step++; } Con_Printf ("num_edicts:%3i\n", sv.num_edicts); Con_Printf ("active :%3i\n", active); Con_Printf ("view :%3i\n", models); Con_Printf ("touch :%3i\n", solid); Con_Printf ("step :%3i\n", step); } /* ============================================================================== ARCHIVING GLOBALS FIXME: need to tag constants, doesn't really work ============================================================================== */ /* ============= ED_WriteGlobals ============= */ void ED_WriteGlobals (FILE *f) { ddef_t *def; int i; char *name; int type; fprintf (f,"{\n"); for (i=0 ; inumglobaldefs ; i++) { def = &pr_globaldefs[i]; type = def->type; if ( !(def->type & DEF_SAVEGLOBAL) ) continue; type &= ~DEF_SAVEGLOBAL; if (type != ev_string && type != ev_float && type != ev_entity) continue; name = pr_strings + def->s_name; fprintf (f,"\"%s\" ", name); fprintf (f,"\"%s\"\n", PR_UglyValueString(type, (eval_t *)&pr_globals[def->ofs])); } fprintf (f,"}\n"); } /* ============= ED_ParseGlobals ============= */ void ED_ParseGlobals (char *data) { char keyname[64]; ddef_t *key; while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Sys_Error ("ED_ParseEntity: EOF without closing brace"); strcpy (keyname, com_token); // parse value data = COM_Parse (data); if (!data) Sys_Error ("ED_ParseEntity: EOF without closing brace"); if (com_token[0] == '}') Sys_Error ("ED_ParseEntity: closing brace without data"); key = ED_FindGlobal (keyname); if (!key) { Con_Printf ("'%s' is not a global\n", keyname); continue; } if (!ED_ParseEpair ((void *)pr_globals, key, com_token)) Host_Error ("ED_ParseGlobals: parse error"); } } //============================================================================ /* ============= ED_NewString ============= */ char *ED_NewString (char *string) { char *new, *new_p; int i,l; l = strlen(string) + 1; new = Hunk_Alloc (l); new_p = new; for (i=0 ; i< l ; i++) { if (string[i] == '\\' && i < l-1) { i++; if (string[i] == 'n') *new_p++ = '\n'; else *new_p++ = '\\'; } else *new_p++ = string[i]; } return new; } /* ============= ED_ParseEval Can parse either fields or globals returns false if error ============= */ bool ED_ParseEpair (void *base, ddef_t *key, char *s) { int i; char string[128]; ddef_t *def; char *v, *w; void *d; dfunction_t *func; d = (void *)((int *)base + key->ofs); switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: *(string_t *)d = ED_NewString (s) - pr_strings; break; case ev_float: *(float *)d = atof (s); break; case ev_vector: strcpy (string, s); v = string; w = string; for (i=0 ; i<3 ; i++) { while (*v && *v != ' ') v++; *v = 0; ((float *)d)[i] = atof (w); w = v = v+1; } break; case ev_entity: *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); break; case ev_field: def = ED_FindField (s); if (!def) { Con_Printf ("Can't find field %s\n", s); return false; } *(int *)d = G_INT(def->ofs); break; case ev_function: func = ED_FindFunction (s); if (!func) { Con_Printf ("Can't find function %s\n", s); return false; } *(func_t *)d = func - pr_functions; break; default: break; } return true; } /* ==================== ED_ParseEdict Parses an edict out of the given string, returning the new position ed should be a properly initialized empty edict. Used for initial level load and for savegames. ==================== */ char *ED_ParseEdict (char *data, edict_t *ent) { ddef_t *key; bool anglehack; bool init; char keyname[256]; int n; init = false; // clear it if (ent != sv.edicts) // hack memset (&ent->v, 0, progs->entityfields * 4); // go through all the dictionary pairs while (1) { // parse key data = COM_Parse (data); if (com_token[0] == '}') break; if (!data) Sys_Error ("ED_ParseEntity: EOF without closing brace"); // anglehack is to allow QuakeEd to write single scalar angles // and allow them to be turned into vectors. (FIXME...) if (!strcmp(com_token, "angle")) { strcpy (com_token, "angles"); anglehack = true; } else anglehack = false; // FIXME: change light to _light to get rid of this hack if (!strcmp(com_token, "light")) strcpy (com_token, "light_lev"); // hack for single light def strcpy (keyname, com_token); // another hack to fix heynames with trailing spaces n = strlen(keyname); while (n && keyname[n-1] == ' ') { keyname[n-1] = 0; n--; } // parse value data = COM_Parse (data); if (!data) Sys_Error ("ED_ParseEntity: EOF without closing brace"); if (com_token[0] == '}') Sys_Error ("ED_ParseEntity: closing brace without data"); init = true; // keynames with a leading underscore are used for utility comments, // and are immediately discarded by quake if (keyname[0] == '_') continue; if (!(key = ED_FindField(keyname))) { if (strcmp(keyname, "sky") && strcmp(keyname, "fog")) // Now supported in worldspawn Con_SafePrintf("'%s' is not a field\n", keyname); continue; } if (anglehack) { char temp[32]; strcpy (temp, com_token); sprintf (com_token, "0 %s 0", temp); } if (!ED_ParseEpair ((void *)&ent->v, key, com_token)) Host_Error ("ED_ParseEdict: parse error"); } if (!init) ent->free = true; return data; } /* ================ ED_LoadFromFile The entities are directly placed in the array, rather than allocated with ED_Alloc, because otherwise an error loading the map would have entity number references out of order. Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. Used for both fresh maps and savegame loads. A fresh map would also need to call ED_CallSpawnFunctions () to let the objects initialize themselves. ================ */ void ED_LoadFromFile (char *data) { edict_t *ent; int inhibit; dfunction_t *func; ent = NULL; inhibit = 0; pr_global_struct->time = sv.time; // parse ents while (1) { // parse the opening brace data = COM_Parse (data); if (!data) break; if (com_token[0] != '{') Sys_Error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = EDICT_NUM(0); else ent = ED_Alloc (); data = ED_ParseEdict (data, ent); // remove things from different skill levels or deathmatch if (deathmatch.value) { if (((int)ent->v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) { ED_Free (ent); inhibit++; continue; } } else if ((current_skill == 0 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_EASY)) || (current_skill == 1 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (current_skill >= 2 && ((int)ent->v.spawnflags & SPAWNFLAG_NOT_HARD)) ) { ED_Free (ent); inhibit++; continue; } // // immediately call spawn function // if (!ent->v.classname) { Con_Printf ("No classname for:\n"); ED_Print (ent); ED_Free (ent); continue; } // look for the spawn function func = ED_FindFunction ( pr_strings + ent->v.classname ); if (!func) { Con_Printf ("No spawn function for:\n"); ED_Print (ent); ED_Free (ent); continue; } pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (func - pr_functions); } Con_DPrintf ("%i entities inhibited\n", inhibit); } /* =============== PR_LoadProgs =============== */ void PR_LoadProgs (const char *progsname) { int i; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start int j; int funcno; char *funcname; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end // flush the non-C variable lookup cache for (i=0 ; iversion != PROG_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", progsname, progs->version, PROG_VERSION); if (progs->crc != PROGHEADER_CRC) Sys_Error ("%s has wrong CRC number (%i should be %i)", progsname, progs->crc, PROGHEADER_CRC); pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); pr_strings = (char *)progs + progs->ofs_strings; pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); pr_globals = (float *)pr_global_struct; pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); // byte swap the lumps for (i=0 ; inumstatements ; i++) { pr_statements[i].op = LittleShort(pr_statements[i].op); pr_statements[i].a = LittleShort(pr_statements[i].a); pr_statements[i].b = LittleShort(pr_statements[i].b); pr_statements[i].c = LittleShort(pr_statements[i].c); } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start // initialize function numbers for PROGS.DAT pr_numbuiltins = 0; pr_builtins = NULL; if (pr_builtin_remap.value) { // remove all previous assigned function numbers for ( j=1 ; j < pr_ebfs_numbuiltins; j++) { pr_ebfs_builtins[j].funcno = 0; } } else { // use default function numbers for ( j=1 ; j < pr_ebfs_numbuiltins; j++) { pr_ebfs_builtins[j].funcno = pr_ebfs_builtins[j].default_funcno; // determine highest builtin number (when NOT remapped) if (pr_ebfs_builtins[j].funcno > pr_numbuiltins) { pr_numbuiltins = pr_ebfs_builtins[j].funcno; } } } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end for (i=0 ; inumfunctions; i++) { pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); pr_functions[i].locals = LittleLong (pr_functions[i].locals); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start if (pr_builtin_remap.value) { if (pr_functions[i].first_statement < 0) // builtin function { funcno = -pr_functions[i].first_statement; funcname = pr_strings + pr_functions[i].s_name; // search function name for ( j=1 ; j < pr_ebfs_numbuiltins ; j++) { if (!(strcasecmp(funcname, pr_ebfs_builtins[j].funcname))) { break; // found } } if (j < pr_ebfs_numbuiltins) // found { pr_ebfs_builtins[j].funcno = funcno; } else { Con_DPrintf("Can not assign builtin number #%i to %s - function unknown\n", funcno, funcname); } } } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm start if (pr_builtin_remap.value) { // check for unassigned functions and try to assign their default function number for ( i=1 ; i < pr_ebfs_numbuiltins; i++) { if ((!pr_ebfs_builtins[i].funcno) && (pr_ebfs_builtins[i].default_funcno)) // unassigned and has a default number { // check if default number is already assigned to another function for ( j=1 ; j < pr_ebfs_numbuiltins; j++) { if (pr_ebfs_builtins[j].funcno == pr_ebfs_builtins[i].default_funcno) { break; // number already assigned to another builtin function } } if (j < pr_ebfs_numbuiltins) // already assigned { Con_DPrintf("Can not assign default builtin number #%i to %s - number is already assigned to %s\n", pr_ebfs_builtins[i].default_funcno, pr_ebfs_builtins[i].funcname, pr_ebfs_builtins[j].funcname); } else { pr_ebfs_builtins[i].funcno = pr_ebfs_builtins[i].default_funcno; } } // determine highest builtin number (when remapped) if (pr_ebfs_builtins[i].funcno > pr_numbuiltins) { pr_numbuiltins = pr_ebfs_builtins[i].funcno; } } } pr_numbuiltins++; // allocate and initialize builtin list for execution time pr_builtins = Hunk_AllocName (pr_numbuiltins*sizeof(builtin_t), "builtins"); for ( i=0 ; i < pr_numbuiltins ; i++) { pr_builtins[i] = pr_ebfs_builtins[0].function; } // create builtin list for execution time and set cvars accordingly Cvar_Set("pr_builtin_find", "0"); Cvar_Set("pr_checkextension", "0"); // 2001-10-20 Extension System by Lord Havoc/Maddes (DP compatibility) for ( j=1 ; j < pr_ebfs_numbuiltins ; j++) { if (pr_ebfs_builtins[j].funcno) // only put assigned functions into builtin list { pr_builtins[pr_ebfs_builtins[j].funcno] = pr_ebfs_builtins[j].function; } if (pr_ebfs_builtins[j].default_funcno == PR_DEFAULT_FUNCNO_BUILTIN_FIND) { Cvar_SetValue("pr_builtin_find", pr_ebfs_builtins[j].funcno); } // 2001-10-20 Extension System by Lord Havoc/Maddes (DP compatibility) start if (pr_ebfs_builtins[j].default_funcno == PR_DEFAULT_FUNCNO_EXTENSION_FIND) { Cvar_SetValue("pr_checkextension", pr_ebfs_builtins[j].funcno); } // 2001-10-20 Extension System by Lord Havoc/Maddes (DP compatibility) end } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes/Firestorm end for (i=0 ; inumglobaldefs ; i++) { pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs); pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); } for (i=0 ; inumfielddefs ; i++) { pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) Sys_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs); pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); } for (i=0 ; inumglobals ; i++) ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); FindEdictFieldOffsets (); } // 2001-10-20 Extension System by LordHavoc start void PR_Extension_List_f (void) { int i; char *partial; int len; int count; if (Cmd_Argc() > 1) { partial = Cmd_Argv (1); len = strlen(partial); } else { partial = NULL; len = 0; } count=0; for (i=0; i < pr_numextensions; i++) { if (partial && strncasecmp (partial, pr_extensions[i], len)) { continue; } Con_Printf ("%s\n", pr_extensions[i]); } Con_Printf ("------------\n"); if (partial) { Con_Printf ("%i beginning with \"%s\" out of ", count, partial); } Con_Printf ("%i extensions\n", i); } // 2001-10-20 Extension System by LordHavoc end // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start /* ============= PR_BuiltInList_f For debugging, prints all builtin functions with assigned and default number ============= */ void PR_BuiltInList_f (void) { int i; char *partial; int len; int count; if (Cmd_Argc() > 1) { partial = Cmd_Argv (1); len = strlen(partial); } else { partial = NULL; len = 0; } count=0; for (i=1; i < pr_ebfs_numbuiltins; i++) { if (partial && strncasecmp (partial, pr_ebfs_builtins[i].funcname, len)) { continue; } count++; Con_Printf ("%i(%i): %s\n", pr_ebfs_builtins[i].funcno, pr_ebfs_builtins[i].default_funcno, pr_ebfs_builtins[i].funcname); } Con_Printf ("------------\n"); if (partial) { Con_Printf ("%i beginning with \"%s\" out of ", count, partial); } Con_Printf ("%i builtin functions\n", i); } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end /* =============== PR_Init =============== */ void PR_Init (void) { Cmd_AddCommand ("edict", ED_PrintEdict_f); Cmd_AddCommand ("edicts", ED_PrintEdicts); Cmd_AddCommand ("edictcount", ED_Count); Cmd_AddCommand ("profile", PR_Profile_f); Cmd_AddCommand ("builtinlist", PR_BuiltInList_f); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes Cmd_AddCommand ("extensionlist", PR_Extension_List_f); // 2001-10-20 Extension System by LordHavoc Cvar_RegisterVariable (&nomonsters); Cvar_RegisterVariable (&gamecfg); Cvar_RegisterVariable (&scratch1); Cvar_RegisterVariable (&scratch2); Cvar_RegisterVariable (&scratch3); Cvar_RegisterVariable (&scratch4); Cvar_RegisterVariable (&savedgamecfg); Cvar_RegisterVariable (&saved1); Cvar_RegisterVariable (&saved2); Cvar_RegisterVariable (&saved3); Cvar_RegisterVariable (&saved4); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start Cvar_RegisterVariable (&pr_builtin_find); Cvar_RegisterVariable (&pr_builtin_remap); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end Cvar_RegisterVariable (&pr_checkextension); // 2001-10-20 Extension System by LordHavoc (DP compatibility) } edict_t *EDICT_NUM(int n) { if (n < 0 || n >= sv.max_edicts) Sys_Error ("EDICT_NUM: bad number %i", n); return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size); } int NUM_FOR_EDICT(edict_t *e) { int b; b = (byte *)e - (byte *)sv.edicts; b = b / pr_edict_size; if (b < 0 || b >= sv.num_edicts) Sys_Error ("NUM_FOR_EDICT: bad pointer"); return b; } ================================================ FILE: source/pr_exec.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" /* */ typedef struct { int s; dfunction_t *f; } prstack_t; #define MAX_STACK_DEPTH 32 prstack_t pr_stack[MAX_STACK_DEPTH]; int pr_depth; #define LOCALSTACK_SIZE 2048 int localstack[LOCALSTACK_SIZE]; int localstack_used; bool pr_trace; dfunction_t *pr_xfunction; int pr_xstatement; int pr_argc; const char *pr_opnames[] = { "DONE", "MUL_F", "MUL_V", "MUL_FV", "MUL_VF", "DIV", "ADD_F", "ADD_V", "SUB_F", "SUB_V", "EQ_F", "EQ_V", "EQ_S", "EQ_E", "EQ_FNC", "NE_F", "NE_V", "NE_S", "NE_E", "NE_FNC", "LE", "GE", "LT", "GT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "INDIRECT", "ADDRESS", "STORE_F", "STORE_V", "STORE_S", "STORE_ENT", "STORE_FLD", "STORE_FNC", "STOREP_F", "STOREP_V", "STOREP_S", "STOREP_ENT", "STOREP_FLD", "STOREP_FNC", "RETURN", "NOT_F", "NOT_V", "NOT_S", "NOT_ENT", "NOT_FNC", "IF", "IFNOT", "CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7", "CALL8", "STATE", "GOTO", "AND", "OR", "BITAND", "BITOR" }; char *PR_GlobalString (int ofs); char *PR_GlobalStringNoContents (int ofs); //============================================================================= /* ================= PR_PrintStatement ================= */ void PR_PrintStatement (dstatement_t *s) { int i; if ( (unsigned)s->op < sizeof(pr_opnames)/sizeof(pr_opnames[0])) { Con_Printf ("%s ", pr_opnames[s->op]); i = strlen(pr_opnames[s->op]); for ( ; i<10 ; i++) Con_Printf (" "); } if (s->op == OP_IF || s->op == OP_IFNOT) Con_Printf ("%sbranch %i",PR_GlobalString(s->a),s->b); else if (s->op == OP_GOTO) { Con_Printf ("branch %i",s->a); } else if ( (unsigned)(s->op - OP_STORE_F) < 6) { Con_Printf ("%s",PR_GlobalString(s->a)); Con_Printf ("%s", PR_GlobalStringNoContents(s->b)); } else { if (s->a) Con_Printf ("%s",PR_GlobalString(s->a)); if (s->b) Con_Printf ("%s",PR_GlobalString(s->b)); if (s->c) Con_Printf ("%s", PR_GlobalStringNoContents(s->c)); } Con_Printf ("\n"); } /* ============ PR_StackTrace ============ */ void PR_StackTrace (void) { dfunction_t *f; int i; if (pr_depth == 0) { Con_Printf ("\n"); return; } pr_stack[pr_depth].f = pr_xfunction; for (i=pr_depth ; i>=0 ; i--) { f = pr_stack[i].f; if (!f) { Con_Printf ("\n"); } else Con_Printf ("%12s : %s\n", pr_strings + f->s_file, pr_strings + f->s_name); } } /* ============ PR_Profile_f ============ */ void PR_Profile_f (void) { dfunction_t *f, *best; int max; int num; int i; num = 0; do { max = 0; best = NULL; for (i=0 ; inumfunctions ; i++) { f = &pr_functions[i]; if (f->profile > max) { max = f->profile; best = f; } } if (best) { if (num < 10) Con_Printf ("%7i %s\n", best->profile, pr_strings+best->s_name); num++; best->profile = 0; } } while (best); } /* ============ PR_RunError Aborts the currently executing function ============ */ void PR_RunError (char *error, ...) { va_list argptr; char* string = Sys_BigStackAlloc(1024, "PR_RunError"); va_start (argptr,error); vsprintf (string,error,argptr); va_end (argptr); PR_PrintStatement (pr_statements + pr_xstatement); PR_StackTrace (); Con_Printf ("%s\n", string); pr_depth = 0; // dump the stack so host_error can shutdown functions Sys_BigStackFree(1024, "PR_RunError"); Host_Error ("Program error"); } /* ============================================================================ PR_ExecuteProgram The interpretation main loop ============================================================================ */ /* ==================== PR_EnterFunction Returns the new program statement counter ==================== */ int PR_EnterFunction (dfunction_t *f) { int i, j, c, o; pr_stack[pr_depth].s = pr_xstatement; pr_stack[pr_depth].f = pr_xfunction; pr_depth++; if (pr_depth >= MAX_STACK_DEPTH) PR_RunError ("stack overflow"); // save off any locals that the new function steps on c = f->locals; if (localstack_used + c > LOCALSTACK_SIZE) PR_RunError ("PR_ExecuteProgram: locals stack overflow\n"); for (i=0 ; i < c ; i++) localstack[localstack_used+i] = ((int *)pr_globals)[f->parm_start + i]; localstack_used += c; // copy parameters o = f->parm_start; for (i=0 ; inumparms ; i++) { for (j=0 ; jparm_size[i] ; j++) { ((int *)pr_globals)[o] = ((int *)pr_globals)[OFS_PARM0+i*3+j]; o++; } } pr_xfunction = f; return f->first_statement - 1; // offset the s++ } /* ==================== PR_LeaveFunction ==================== */ int PR_LeaveFunction (void) { int i, c; if (pr_depth <= 0) Sys_Error ("prog stack underflow"); // restore locals from the stack c = pr_xfunction->locals; localstack_used -= c; if (localstack_used < 0) PR_RunError ("PR_ExecuteProgram: locals stack underflow\n"); for (i=0 ; i < c ; i++) ((int *)pr_globals)[pr_xfunction->parm_start + i] = localstack[localstack_used+i]; // up stack pr_depth--; pr_xfunction = pr_stack[pr_depth].f; return pr_stack[pr_depth].s; } /* ==================== PR_ExecuteProgram ==================== */ void PR_ExecuteProgram (func_t fnum) { eval_t *a, *b, *c; int s; dstatement_t *st; dfunction_t *f, *newf; int runaway; int i; edict_t *ed; int exitdepth; eval_t *ptr; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start char *funcname; char *remaphint; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end if (!fnum || fnum >= progs->numfunctions) { if (pr_global_struct->self) ED_Print (PROG_TO_EDICT(pr_global_struct->self)); Host_Error ("PR_ExecuteProgram: NULL function"); } f = &pr_functions[fnum]; runaway = 1000000; pr_trace = false; // make a stack frame exitdepth = pr_depth; s = PR_EnterFunction (f); while (1) { s++; // next statement st = &pr_statements[s]; a = (eval_t *)&pr_globals[st->a]; b = (eval_t *)&pr_globals[st->b]; c = (eval_t *)&pr_globals[st->c]; if (!--runaway) PR_RunError ("runaway loop error"); pr_xfunction->profile++; pr_xstatement = s; if (pr_trace) PR_PrintStatement (st); switch (st->op) { case OP_ADD_F: c->_float = a->_float + b->_float; break; case OP_ADD_V: c->vector[0] = a->vector[0] + b->vector[0]; c->vector[1] = a->vector[1] + b->vector[1]; c->vector[2] = a->vector[2] + b->vector[2]; break; case OP_SUB_F: c->_float = a->_float - b->_float; break; case OP_SUB_V: c->vector[0] = a->vector[0] - b->vector[0]; c->vector[1] = a->vector[1] - b->vector[1]; c->vector[2] = a->vector[2] - b->vector[2]; break; case OP_MUL_F: c->_float = a->_float * b->_float; break; case OP_MUL_V: c->_float = a->vector[0]*b->vector[0] + a->vector[1]*b->vector[1] + a->vector[2]*b->vector[2]; break; case OP_MUL_FV: c->vector[0] = a->_float * b->vector[0]; c->vector[1] = a->_float * b->vector[1]; c->vector[2] = a->_float * b->vector[2]; break; case OP_MUL_VF: c->vector[0] = b->_float * a->vector[0]; c->vector[1] = b->_float * a->vector[1]; c->vector[2] = b->_float * a->vector[2]; break; case OP_DIV_F: c->_float = a->_float / b->_float; break; case OP_BITAND: c->_float = (int)a->_float & (int)b->_float; break; case OP_BITOR: c->_float = (int)a->_float | (int)b->_float; break; case OP_GE: c->_float = a->_float >= b->_float; break; case OP_LE: c->_float = a->_float <= b->_float; break; case OP_GT: c->_float = a->_float > b->_float; break; case OP_LT: c->_float = a->_float < b->_float; break; case OP_AND: c->_float = a->_float && b->_float; break; case OP_OR: c->_float = a->_float || b->_float; break; case OP_NOT_F: c->_float = !a->_float; break; case OP_NOT_V: c->_float = !a->vector[0] && !a->vector[1] && !a->vector[2]; break; case OP_NOT_S: c->_float = !a->string || !pr_strings[a->string]; break; case OP_NOT_FNC: c->_float = !a->function; break; case OP_NOT_ENT: c->_float = (PROG_TO_EDICT(a->edict) == sv.edicts); break; case OP_EQ_F: c->_float = a->_float == b->_float; break; case OP_EQ_V: c->_float = (a->vector[0] == b->vector[0]) && (a->vector[1] == b->vector[1]) && (a->vector[2] == b->vector[2]); break; case OP_EQ_S: c->_float = !strcmp(pr_strings+a->string,pr_strings+b->string); break; case OP_EQ_E: c->_float = a->_int == b->_int; break; case OP_EQ_FNC: c->_float = a->function == b->function; break; case OP_NE_F: c->_float = a->_float != b->_float; break; case OP_NE_V: c->_float = (a->vector[0] != b->vector[0]) || (a->vector[1] != b->vector[1]) || (a->vector[2] != b->vector[2]); break; case OP_NE_S: c->_float = strcmp(pr_strings+a->string,pr_strings+b->string); break; case OP_NE_E: c->_float = a->_int != b->_int; break; case OP_NE_FNC: c->_float = a->function != b->function; break; //================== case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FLD: // integers case OP_STORE_S: case OP_STORE_FNC: // pointers b->_int = a->_int; break; case OP_STORE_V: b->vector[0] = a->vector[0]; b->vector[1] = a->vector[1]; b->vector[2] = a->vector[2]; break; case OP_STOREP_F: case OP_STOREP_ENT: case OP_STOREP_FLD: // integers case OP_STOREP_S: case OP_STOREP_FNC: // pointers ptr = (eval_t *)((byte *)sv.edicts + b->_int); ptr->_int = a->_int; break; case OP_STOREP_V: ptr = (eval_t *)((byte *)sv.edicts + b->_int); ptr->vector[0] = a->vector[0]; ptr->vector[1] = a->vector[1]; ptr->vector[2] = a->vector[2]; break; case OP_ADDRESS: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range #endif if (ed == (edict_t *)sv.edicts && sv.state == ss_active) PR_RunError ("assignment to world entity"); c->_int = (byte *)((int *)&ed->v + b->_int) - (byte *)sv.edicts; break; case OP_LOAD_F: case OP_LOAD_FLD: case OP_LOAD_ENT: case OP_LOAD_S: case OP_LOAD_FNC: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range #endif a = (eval_t *)((int *)&ed->v + b->_int); c->_int = a->_int; break; case OP_LOAD_V: ed = PROG_TO_EDICT(a->edict); #ifdef PARANOID NUM_FOR_EDICT(ed); // make sure it's in range #endif a = (eval_t *)((int *)&ed->v + b->_int); c->vector[0] = a->vector[0]; c->vector[1] = a->vector[1]; c->vector[2] = a->vector[2]; break; //================== case OP_IFNOT: if (!a->_int) s += (signed short)st->b - 1; // offset the s++ break; case OP_IF: if (a->_int) s += (signed short)st->b - 1; // offset the s++ break; case OP_GOTO: s += (signed short)st->a - 1; // offset the s++ break; case OP_CALL0: case OP_CALL1: case OP_CALL2: case OP_CALL3: case OP_CALL4: case OP_CALL5: case OP_CALL6: case OP_CALL7: case OP_CALL8: pr_argc = st->op - OP_CALL0; if (!a->function) PR_RunError ("NULL function"); newf = &pr_functions[a->function]; if (newf->first_statement < 0) { // negative statements are built in functions i = -newf->first_statement; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start if ( (i >= pr_numbuiltins) || (pr_builtins[i] == pr_ebfs_builtins[0].function) ) { funcname = pr_strings + newf->s_name; if (pr_builtin_remap.value) { remaphint = NULL; } else { remaphint = "Try \"builtin remapping\" by setting PR_BUILTIN_REMAP to 1\n"; } PR_RunError ("Bad builtin call number %i for %s\nPlease contact the PROGS.DAT author\n \ Use BUILTINLIST to see all assigned builtin functions\n%s", i, funcname, remaphint); } // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end pr_builtins[i] (); break; } s = PR_EnterFunction (newf); break; case OP_DONE: case OP_RETURN: pr_globals[OFS_RETURN] = pr_globals[st->a]; pr_globals[OFS_RETURN+1] = pr_globals[st->a+1]; pr_globals[OFS_RETURN+2] = pr_globals[st->a+2]; s = PR_LeaveFunction (); if (pr_depth == exitdepth) return; // all done break; case OP_STATE: ed = PROG_TO_EDICT(pr_global_struct->self); #ifdef FPS_20 ed->v.nextthink = pr_global_struct->time + 0.05; #else ed->v.nextthink = pr_global_struct->time + 0.1; #endif if (a->_float != ed->v.frame) { ed->v.frame = a->_float; } ed->v.think = b->function; break; default: PR_RunError ("Bad opcode %i", st->op); } } } ================================================ FILE: source/progdefs.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef QUAKE2 #include "progdefs.q2" #else #include "progdefs.q1" #endif ================================================ FILE: source/progdefs.q1 ================================================ /* file generated by qcc, do not modify */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; float force_retouch; string_t mapname; float deathmatch; float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float parm1; float parm2; float parm3; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientDisconnect; func_t SetNewParms; func_t SetChangeParms; } globalvars_t; typedef struct { float modelindex; vec3_t absmin; vec3_t absmax; float ltime; float movetype; float solid; vec3_t origin; vec3_t oldorigin; vec3_t velocity; vec3_t angles; vec3_t avelocity; vec3_t punchangle; string_t classname; string_t model; float frame; float skin; float effects; vec3_t mins; vec3_t maxs; vec3_t size; func_t touch; func_t use; func_t think; func_t blocked; float nextthink; int groundentity; float health; float frags; float weapon; string_t weaponmodel; float weaponframe; float currentammo; float ammo_shells; float ammo_nails; float ammo_rockets; float ammo_cells; float items; float takedamage; int chain; float deadflag; vec3_t view_ofs; float button0; float button1; float button2; float impulse; float fixangle; vec3_t v_angle; float idealpitch; string_t netname; int 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; int aiment; int goalentity; float spawnflags; string_t target; string_t targetname; float dmg_take; float dmg_save; int dmg_inflictor; int owner; vec3_t movedir; string_t message; float sounds; string_t noise; string_t noise1; string_t noise2; string_t noise3; } entvars_t; #define PROGHEADER_CRC 5927 ================================================ FILE: source/progdefs.q2 ================================================ /* file generated by qcc, do not modify */ typedef struct { int pad[28]; int self; int other; int world; float time; float frametime; float force_retouch; string_t mapname; string_t startspot; float deathmatch; float coop; float teamplay; float serverflags; float total_secrets; float total_monsters; float found_secrets; float killed_monsters; float parm1; float parm2; float parm3; float parm4; float parm5; float parm6; float parm7; float parm8; float parm9; float parm10; float parm11; float parm12; float parm13; float parm14; float parm15; float parm16; vec3_t v_forward; vec3_t v_up; vec3_t v_right; float trace_allsolid; float trace_startsolid; float trace_fraction; vec3_t trace_endpos; vec3_t trace_plane_normal; float trace_plane_dist; int trace_ent; float trace_inopen; float trace_inwater; int msg_entity; string_t null; func_t main; func_t StartFrame; func_t PlayerPreThink; func_t PlayerPostThink; func_t ClientKill; func_t ClientConnect; func_t PutClientInServer; func_t ClientDisconnect; func_t SetNewParms; func_t SetChangeParms; } globalvars_t; typedef struct { float modelindex; vec3_t absmin; vec3_t absmax; float ltime; float movetype; float solid; vec3_t origin; vec3_t oldorigin; vec3_t velocity; vec3_t angles; vec3_t avelocity; vec3_t basevelocity; vec3_t punchangle; string_t classname; string_t model; float frame; float skin; float effects; float drawPercent; float gravity; float mass; float light_level; vec3_t mins; vec3_t maxs; vec3_t size; func_t touch; func_t use; func_t think; func_t blocked; float nextthink; int groundentity; float health; float frags; float weapon; string_t weaponmodel; float weaponframe; float currentammo; float ammo_shells; float ammo_nails; float ammo_rockets; float ammo_cells; float items; float items2; float takedamage; int chain; float deadflag; vec3_t view_ofs; float button0; float button1; float button2; float impulse; float fixangle; vec3_t v_angle; float idealpitch; float pitch_speed; string_t netname; int 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; int aiment; int goalentity; float spawnflags; string_t target; string_t targetname; float dmg_take; float dmg_save; int dmg_inflictor; int owner; vec3_t movedir; string_t message; float sounds; string_t noise; string_t noise1; string_t noise2; string_t noise3; float dmg; float dmgtime; float air_finished; float pain_finished; float radsuit_finished; float speed; } entvars_t; #define PROGHEADER_CRC 31586 ================================================ FILE: source/progs.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "pr_comp.h" // defs shared with qcc #include "progdefs.h" // generated by program cdefs typedef union eval_s { string_t string; float _float; float vector[3]; func_t function; int _int; int edict; } eval_t; #define MAX_ENT_LEAFS 16 typedef struct edict_s { bool free; link_t area; // linked to a division node or leaf int num_leafs; short leafnums[MAX_ENT_LEAFS]; entity_state_t baseline; float freetime; // sv.time when the object was freed entvars_t v; // C exported fields from progs // other fields from progs come immediately after } edict_t; #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area) //============================================================================ extern dprograms_t *progs; extern dfunction_t *pr_functions; extern char *pr_strings; extern ddef_t *pr_globaldefs; extern ddef_t *pr_fielddefs; extern dstatement_t *pr_statements; extern globalvars_t *pr_global_struct; extern float *pr_globals; // same as pr_global_struct extern int pr_edict_size; // in bytes //============================================================================ void PR_Init (void); void PR_ExecuteProgram (func_t fnum); void PR_LoadProgs (const char *progsname); void PR_Profile_f (void); edict_t *ED_Alloc (void); void ED_Free (edict_t *ed); char *ED_NewString (char *string); // returns a copy of the string allocated from the server's string heap void ED_Print (edict_t *ed); void ED_Write (FILE *f, edict_t *ed); char *ED_ParseEdict (char *data, edict_t *ent); void ED_WriteGlobals (FILE *f); void ED_ParseGlobals (char *data); void ED_LoadFromFile (char *data); //define EDICT_NUM(n) ((edict_t *)(sv.edicts+ (n)*pr_edict_size)) //define NUM_FOR_EDICT(e) (((byte *)(e) - sv.edicts)/pr_edict_size) edict_t *EDICT_NUM(int n); int NUM_FOR_EDICT(edict_t *e); #define NEXT_EDICT(e) ((edict_t *)( (byte *)e + pr_edict_size)) #define EDICT_TO_PROG(e) ((byte *)e - (byte *)sv.edicts) #define PROG_TO_EDICT(e) ((edict_t *)((byte *)sv.edicts + e)) //============================================================================ #define G_FLOAT(o) (pr_globals[o]) #define G_INT(o) (*(int *)&pr_globals[o]) #define G_EDICT(o) ((edict_t *)((byte *)sv.edicts+ *(int *)&pr_globals[o])) #define G_EDICTNUM(o) NUM_FOR_EDICT(G_EDICT(o)) #define G_VECTOR(o) (&pr_globals[o]) #define G_STRING(o) (pr_strings + *(string_t *)&pr_globals[o]) #define G_FUNCTION(o) (*(func_t *)&pr_globals[o]) #define E_FLOAT(e,o) (((float*)&e->v)[o]) #define E_INT(e,o) (*(int *)&((float*)&e->v)[o]) #define E_VECTOR(e,o) (&((float*)&e->v)[o]) #define E_STRING(e,o) (pr_strings + *(string_t *)&((float*)&e->v)[o]) extern int type_size[8]; typedef void (*builtin_t) (void); extern builtin_t *pr_builtins; extern int pr_numbuiltins; // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start typedef struct ebfs_builtin_s { int default_funcno; char *funcname; builtin_t function; int funcno; } ebfs_builtin_t; extern ebfs_builtin_t pr_ebfs_builtins[]; extern int pr_ebfs_numbuiltins; #define PR_DEFAULT_FUNCNO_BUILTIN_FIND 100 extern cvar_t pr_builtin_find; extern cvar_t pr_builtin_remap; #define PR_DEFAULT_FUNCNO_EXTENSION_FIND 99 // 2001-10-20 Extension System by Lord Havoc/Maddes // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end // 2001-10-20 Extension System by LordHavoc start extern char *pr_extensions[]; extern int pr_numextensions; // 2001-10-20 Extension System by LordHavoc end extern int pr_argc; extern bool pr_trace; extern dfunction_t *pr_xfunction; extern int pr_xstatement; extern unsigned short pr_crc; void PR_RunError (char *error, ...); void ED_PrintEdicts (void); void ED_PrintNum (int ent); eval_t *GetEdictFieldValue(edict_t *ed, char *field); #define GETEDICTFIELDVALUE(ed, fieldoffset) (fieldoffset ? (eval_t *)((byte *)&ed->v + fieldoffset) : NULL) extern int eval_alpha; extern int eval_renderamt; extern int eval_drawonlytoclient; extern int eval_nodrawtoclient; ================================================ FILE: source/protocol.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // protocol.h -- communications protocols /* ================ Here is a short list of known protocol used by other NetQuake ports: - 15: Default NetQuake protocol. - 666: FitzQuake protocol (also used by QuakeSpasm) - 35, 36, 37, 38: ProQuake protocol ================ */ #define PROTOCOL_NETQUAKE 15 // Official NetQuake protocol used. #define PROTOCOL_FITZQUAKE 666 // Protocol for FitzQuake 0.85 // if the high bit of the servercmd is set, the low bits are fast update flags: #define U_MOREBITS BIT(0) #define U_ORIGIN1 BIT(1) #define U_ORIGIN2 BIT(2) #define U_ORIGIN3 BIT(3) #define U_ANGLE2 BIT(4) #define U_NOLERP BIT(5) // don't interpolate movement #define U_FRAME BIT(6) #define U_SIGNAL BIT(7) // just differentiates from other updates // svc_update can pass all of the fast update bits, plus more #define U_ANGLE1 BIT(8) #define U_ANGLE3 BIT(9) #define U_MODEL BIT(10) #define U_COLORMAP BIT(11) #define U_SKIN BIT(12) #define U_EFFECTS BIT(13) #define U_LONGENTITY BIT(14) // FitzQuake -- Protocol extender #define U_EXTEND1 BIT(15) #define U_ALPHA BIT(16) // 1 byte, uses ENTALPHA_ENCODE, not sent if equal to baseline #define U_FRAME2 BIT(17) // 1 byte, this is .frame & 0xFF00 (second byte) #define U_MODEL2 BIT(18) // 1 byte, this is .modelindex & 0xFF00 (second byte) #define U_LERPFINISH BIT(19) // 1 byte, 0.0-1.0 maps to 0-255, not sent if exactly 0.1, this is ent->v.nextthink - sv.time, used for lerping #define U_SCALE BIT(20) // 1 byte, for PROTOCOL_RMQ PRFL_EDICTSCALE, currently read but ignored #define U_RENDERAMT BIT(21) #define U_UNUSED22 BIT(22) #define U_EXTEND2 BIT(23) // another byte to follow, future expansion #define SU_VIEWHEIGHT BIT(0) #define SU_IDEALPITCH BIT(1) #define SU_PUNCH1 BIT(2) #define SU_PUNCH2 BIT(3) #define SU_PUNCH3 BIT(4) #define SU_VELOCITY1 BIT(5) #define SU_VELOCITY2 BIT(6) #define SU_VELOCITY3 BIT(7) //define SU_AIMENT BIT(8) AVAILABLE BIT #define SU_ITEMS BIT(9) #define SU_ONGROUND BIT(10) // no data follows, the bit is it #define SU_INWATER BIT(11) // no data follows, the bit is it #define SU_WEAPONFRAME BIT(12) #define SU_ARMOR BIT(13) #define SU_WEAPON BIT(14) // FitzQuake -- Protocol extender #define SU_EXTEND1 BIT(15) // another byte to follow #define SU_WEAPON2 BIT(16) // 1 byte, this is .weaponmodel & 0xFF00 (second byte) #define SU_ARMOR2 BIT(17) // 1 byte, this is .armorvalue & 0xFF00 (second byte) #define SU_AMMO2 BIT(18) // 1 byte, this is .currentammo & 0xFF00 (second byte) #define SU_SHELLS2 BIT(19) // 1 byte, this is .ammo_shells & 0xFF00 (second byte) #define SU_NAILS2 BIT(20) // 1 byte, this is .ammo_nails & 0xFF00 (second byte) #define SU_ROCKETS2 BIT(21) // 1 byte, this is .ammo_rockets & 0xFF00 (second byte) #define SU_CELLS2 BIT(22) // 1 byte, this is .ammo_cells & 0xFF00 (second byte) #define SU_EXTEND2 BIT(23) // another byte to follow #define SU_WEAPONFRAME2 BIT(24) // 1 byte, this is .weaponframe & 0xFF00 (second byte) #define SU_WEAPONALPHA BIT(25) // 1 byte, this is alpha for weaponmodel, uses ENTALPHA_ENCODE, not sent if ENTALPHA_DEFAULT #define SU_UNUSED26 BIT(26) #define SU_UNUSED27 BIT(27) #define SU_UNUSED28 BIT(28) #define SU_UNUSED29 BIT(29) #define SU_UNUSED30 BIT(30) #define SU_EXTEND3 BIT(31) // another byte to follow, future expansion // a sound with no channel is a local only sound #define SND_VOLUME BIT(0) // a byte #define SND_ATTENUATION BIT(1) // a byte #define SND_LOOPING BIT(2) // a long // defaults for clientinfo messages #define DEFAULT_VIEWHEIGHT 22 // game types sent by serverinfo // these determine which intermission screen plays #define GAME_COOP 0 #define GAME_DEATHMATCH 1 //================== // note that there are some defs.qc that mirror to these numbers // also related to svc_strings[] in cl_parse //================== // // server to client // #define svc_bad 0 #define svc_nop 1 #define svc_disconnect 2 #define svc_updatestat 3 // [byte] [long] #define svc_version 4 // [long] server version #define svc_setview 5 // [short] entity number #define svc_sound 6 // #define svc_time 7 // [float] server time #define svc_print 8 // [string] null terminated string #define svc_stufftext 9 // [string] stuffed into client's console buffer // the string should be \n terminated #define svc_setangle 10 // [angle3] set the view angle to this absolute value #define svc_serverinfo 11 // [long] version // [string] signon string // [string]..[0]model cache // [string]...[0]sounds cache #define svc_lightstyle 12 // [byte] [string] #define svc_updatename 13 // [byte] [string] #define svc_updatefrags 14 // [byte] [short] #define svc_clientdata 15 // #define svc_stopsound 16 // #define svc_updatecolors 17 // [byte] [byte] #define svc_particle 18 // [vec3] #define svc_damage 19 #define svc_spawnstatic 20 // svc_spawnbinary 21 #define svc_spawnbaseline 22 #define svc_temp_entity 23 #define svc_setpause 24 // [byte] on / off #define svc_signonnum 25 // [byte] used for the signon sequence #define svc_centerprint 26 // [string] to put in center of the screen #define svc_killedmonster 27 #define svc_foundsecret 28 #define svc_spawnstaticsound 29 // [coord3] [byte] samp [byte] vol [byte] aten #define svc_intermission 30 // [string] music #define svc_finale 31 // [string] music [string] text #define svc_cdtrack 32 // [byte] track [byte] looptrack #define svc_sellscreen 33 #define svc_cutscene 34 // // client to server // #define clc_bad 0 #define clc_nop 1 #define clc_disconnect 2 #define clc_move 3 // [usercmd_t] #define clc_stringcmd 4 // [string] message // // temp entity events // #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_EXPLOSION2 12 // PGM 01/21/97 #define TE_BEAM 13 // PGM 01/21/97 #ifdef QUAKE2 #define TE_IMPLOSION 14 #define TE_RAILTRAIL 15 #endif ================================================ FILE: source/quakeasm.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // quakeasm.h: general asm header file // //#define GLQUAKE 1 #if defined(_WIN32) && !defined(WINDED) #if defined(_M_IX86) #define __i386__ 1 #endif #endif #ifdef __i386__ #define id386 1 #else #define id386 0 #endif // !!! must be kept the same as in d_iface.h !!! #define TRANSPARENT_COLOR 255 #ifndef NeXT #ifndef GLQUAKE .extern C(d_zistepu) .extern C(d_pzbuffer) .extern C(d_zistepv) .extern C(d_zrowbytes) .extern C(d_ziorigin) .extern C(r_turb_s) .extern C(r_turb_t) .extern C(r_turb_pdest) .extern C(r_turb_spancount) .extern C(r_turb_turb) .extern C(r_turb_pbase) .extern C(r_turb_sstep) .extern C(r_turb_tstep) .extern C(r_bmodelactive) .extern C(d_sdivzstepu) .extern C(d_tdivzstepu) .extern C(d_sdivzstepv) .extern C(d_tdivzstepv) .extern C(d_sdivzorigin) .extern C(d_tdivzorigin) .extern C(sadjust) .extern C(tadjust) .extern C(bbextents) .extern C(bbextentt) .extern C(cacheblock) .extern C(d_viewbuffer) .extern C(cachewidth) .extern C(d_pzbuffer) .extern C(d_zrowbytes) .extern C(d_zwidth) .extern C(d_scantable) .extern C(r_lightptr) .extern C(r_numvblocks) .extern C(prowdestbase) .extern C(pbasesource) .extern C(r_lightwidth) .extern C(lightright) .extern C(lightrightstep) .extern C(lightdeltastep) .extern C(lightdelta) .extern C(lightright) .extern C(lightdelta) .extern C(sourcetstep) .extern C(surfrowbytes) .extern C(lightrightstep) .extern C(lightdeltastep) .extern C(r_sourcemax) .extern C(r_stepback) .extern C(colormap) .extern C(blocksize) .extern C(sourcesstep) .extern C(lightleft) .extern C(blockdivshift) .extern C(blockdivmask) .extern C(lightleftstep) .extern C(r_origin) .extern C(r_ppn) .extern C(r_pup) .extern C(r_pright) .extern C(ycenter) .extern C(xcenter) .extern C(d_vrectbottom_particle) .extern C(d_vrectright_particle) .extern C(d_vrecty) .extern C(d_vrectx) .extern C(d_pix_shift) .extern C(d_pix_min) .extern C(d_pix_max) .extern C(d_y_aspect_shift) .extern C(screenwidth) .extern C(r_leftclipped) .extern C(r_leftenter) .extern C(r_rightclipped) .extern C(r_rightenter) .extern C(modelorg) .extern C(xscale) .extern C(r_refdef) .extern C(yscale) .extern C(r_leftexit) .extern C(r_rightexit) .extern C(r_lastvertvalid) .extern C(cacheoffset) .extern C(newedges) .extern C(removeedges) .extern C(r_pedge) .extern C(r_framecount) .extern C(r_u1) .extern C(r_emitted) .extern C(edge_p) .extern C(surface_p) .extern C(surfaces) .extern C(r_lzi1) .extern C(r_v1) .extern C(r_ceilv1) .extern C(r_nearzi) .extern C(r_nearzionly) .extern C(edge_aftertail) .extern C(edge_tail) .extern C(current_iv) .extern C(edge_head_u_shift20) .extern C(span_p) .extern C(edge_head) .extern C(fv) .extern C(edge_tail_u_shift20) .extern C(r_apverts) .extern C(r_anumverts) .extern C(aliastransform) .extern C(r_avertexnormals) .extern C(r_plightvec) .extern C(r_ambientlight) .extern C(r_shadelight) .extern C(aliasxcenter) .extern C(aliasycenter) .extern C(a_sstepxfrac) .extern C(r_affinetridesc) .extern C(acolormap) .extern C(d_pcolormap) .extern C(r_affinetridesc) .extern C(d_sfrac) .extern C(d_ptex) .extern C(d_pedgespanpackage) .extern C(d_tfrac) .extern C(d_light) .extern C(d_zi) .extern C(d_pdest) .extern C(d_pz) .extern C(d_aspancount) .extern C(erroradjustup) .extern C(errorterm) .extern C(d_xdenom) .extern C(r_p0) .extern C(r_p1) .extern C(r_p2) .extern C(a_tstepxfrac) .extern C(r_sstepx) .extern C(r_tstepx) .extern C(a_ststepxwhole) .extern C(zspantable) .extern C(skintable) .extern C(r_zistepx) .extern C(erroradjustdown) .extern C(d_countextrastep) .extern C(ubasestep) .extern C(a_ststepxwhole) .extern C(a_tstepxfrac) .extern C(r_lstepx) .extern C(a_spans) .extern C(erroradjustdown) .extern C(d_pdestextrastep) .extern C(d_pzextrastep) .extern C(d_sfracextrastep) .extern C(d_ptexextrastep) .extern C(d_countextrastep) .extern C(d_tfracextrastep) .extern C(d_lightextrastep) .extern C(d_ziextrastep) .extern C(d_pdestbasestep) .extern C(d_pzbasestep) .extern C(d_sfracbasestep) .extern C(d_ptexbasestep) .extern C(ubasestep) .extern C(d_tfracbasestep) .extern C(d_lightbasestep) .extern C(d_zibasestep) .extern C(zspantable) .extern C(r_lstepy) .extern C(r_sstepy) .extern C(r_tstepy) .extern C(r_zistepy) .extern C(D_PolysetSetEdgeTable) .extern C(D_RasterizeAliasPolySmooth) .extern float_point5 .extern Float2ToThe31nd .extern izistep .extern izi .extern FloatMinus2ToThe31nd .extern float_1 .extern float_particle_z_clip .extern float_minus_1 .extern float_0 .extern fp_16 .extern fp_64k .extern fp_1m .extern fp_1m_minus_1 .extern fp_8 .extern entryvec_table .extern advancetable .extern sstep .extern tstep .extern pspantemp .extern counttemp .extern jumptemp .extern reciprocal_table .extern DP_Count .extern DP_u .extern DP_v .extern DP_32768 .extern DP_Color .extern DP_Pix .extern DP_EntryTable .extern pbase .extern s .extern t .extern sfracf .extern tfracf .extern snext .extern tnext .extern spancountminus1 .extern zi16stepu .extern sdivz16stepu .extern tdivz16stepu .extern zi8stepu .extern sdivz8stepu .extern tdivz8stepu .extern reciprocal_table_16 .extern entryvec_table_16 .extern ceil_cw .extern single_cw .extern fp_64kx64k .extern pz .extern spr8entryvec_table #endif .extern C(snd_scaletable) .extern C(paintbuffer) .extern C(snd_linear_count) .extern C(snd_p) .extern C(snd_vol) .extern C(snd_out) .extern C(vright) .extern C(vup) .extern C(vpn) .extern C(BOPS_Error) #endif ================================================ FILE: source/quakedef.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // quakedef.h -- primary header for client #define m_none 0 #define m_main 1 #define m_singleplayer 2 #define m_load 3 #define m_save 4 #define m_multiplayer 5 #define m_setup 6 #define m_net 7 #define m_options 8 #define m_video 9 #define m_keys 10 #define m_help 11 #define m_quit 12 #define m_lanconfig 13 #define m_gameoptions 14 #define m_search 15 #define m_slist 16 #define m_onlineserverlist 17 #define m_benchmark 18 #define m_mods 19 #define m_graphics 20 extern int m_state; #define QUAKE_GAME // as opposed to utilities //#define DEBUG #define ENGINE_NAME "vitaQuake" #define VERSION 4.20 #define VERSION_PROQUAKE 3.50 #define GLQUAKE_VERSION 1.00 //define PARANOID // speed sapping error checking #define GAMENAME_DIR "id1" #include #include #include #include #include #include #include #include "neon_mathfun.h" #if defined(_WIN32) && !defined(WINDED) #if defined(_M_IX86) #define __i386__ 1 #endif void VID_LockBuffer (void); void VID_UnlockBuffer (void); #else #define VID_LockBuffer() #define VID_UnlockBuffer() #endif #if defined __i386__ // && !defined __sun__ #define id386 1 #else #define id386 0 #endif #if id386 #define UNALIGNED_OK 1 // set to 0 if unaligned accesses are not supported #else #define UNALIGNED_OK 0 #endif // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define CACHE_SIZE 32 // used to align key data structures #define MINIMUM_MEMORY 0x550000 #define MINIMUM_MEMORY_LEVELPAK (MINIMUM_MEMORY + 0x100000) #define MAX_NUM_ARGVS 50 // up / down #define PITCH 0 // left / right #define YAW 1 // fall over #define ROLL 2 #define MAX_QPATH 64 // max length of a quake game pathname #define MAX_OSPATH 128 // max length of a filesystem pathname #define ON_EPSILON 0.1 // point on plane side epsilon #define MAX_MSGLEN 8000 // max length of a reliable message #define MAX_DATAGRAM 1024 // max length of unreliable message // // per-level limits // #define MAX_EDICTS 32768 #define MAX_LIGHTSTYLES 64 #define MAX_MODELS 256 // these are sent over the net as bytes #define MAX_SOUNDS 256 // so they cannot be blindly increased #define SAVEGAME_COMMENT_LENGTH 39 #define MAX_STYLESTRING 64 #define BIT(x) (1< #include "quakedef.h" #include "r_local.h" #define DEFAULT_NUM_PARTICLES 1024 // default max # of particles at one time #define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's on the command line #define ABSOLUTE_MAX_PARTICLES 8192 static int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; static int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; static int ramp3[6] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03}; particle_t *active_particles, *free_particles; particle_t *particles; int r_numparticles; vec3_t r_pright, r_pup, r_ppn; extern float d_8to32ftable[]; /* =============== R_InitParticles =============== */ void R_InitParticles (void) { int i; if ((i = COM_CheckParm ("-particles")) && i+1 < com_argc) { r_numparticles = (int)(atoi(com_argv[i+1])); if (r_numparticles < ABSOLUTE_MIN_PARTICLES) r_numparticles = ABSOLUTE_MIN_PARTICLES; else if (r_numparticles > ABSOLUTE_MAX_PARTICLES) r_numparticles = ABSOLUTE_MAX_PARTICLES; } else { r_numparticles = DEFAULT_NUM_PARTICLES; } particles = (particle_t *) Hunk_AllocName (r_numparticles * sizeof(particle_t), "particles"); } /* =============== R_EntityParticles =============== */ #define NUMVERTEXNORMALS 162 extern float r_avertexnormals[NUMVERTEXNORMALS][3]; vec3_t avelocities[NUMVERTEXNORMALS]; float beamlength = 16; vec3_t avelocity = {23, 7, 3}; float partstep = 0.01; float timescale = 0.01; void R_EntityParticles (entity_t *ent) { int i; particle_t *p; float angle; float sr, sp, sy, cr, cp, cy; vec3_t forward; float dist; dist = 64; if (!avelocities[0][0]) { for (i = 0 ; i < NUMVERTEXNORMALS; i++) { avelocities[i][0] = (rand() & 255) * 0.01; avelocities[i][1] = (rand() & 255) * 0.01; avelocities[i][2] = (rand() & 255) * 0.01; } } for (i=0 ; inext; p->next = active_particles; active_particles = p; p->die = cl.time + 0.01; p->color = 0x6f; p->type = pt_explode; p->org[0] = ent->origin[0] + r_avertexnormals[i][0]*dist + forward[0]*beamlength; p->org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*beamlength; p->org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*beamlength; } } /* =============== R_ClearParticles =============== */ void R_ClearParticles (void) { int i; free_particles = &particles[0]; active_particles = NULL; for (i=0 ;inext; p->next = active_particles; active_particles = p; p->die = 99999; p->color = (-c)&15; p->type = pt_static; VectorCopy (vec3_origin, p->vel); VectorCopy (org, p->org); } fclose (f); Con_Printf ("%i points read\n", c); } /* =============== R_ParseParticleEffect Parse an effect out of the server message =============== */ void R_ParseParticleEffect (void) { vec3_t org, dir; int i, count, msgcount, color; for (i=0 ; i<3 ; i++) org[i] = MSG_ReadCoord (); for (i=0 ; i<3 ; i++) dir[i] = MSG_ReadChar () * (1.0/16); msgcount = MSG_ReadByte (); color = MSG_ReadByte (); if (msgcount == 255) count = 1024; else count = msgcount; R_RunParticleEffect (org, dir, color, count); } /* =============== R_ParticleExplosion =============== */ void R_ParticleExplosion (vec3_t org) { int i, j; particle_t *p; for (i=0 ; i<1024 ; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->color = ramp1[0]; p->ramp = rand()&3; if (i & 1) { p->type = pt_explode; p->die = cl.time + (8 - p->ramp) / 10.0f; } else { p->type = pt_explode2; p->die = cl.time + (8 - p->ramp) / 15.0f; } for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } /* =============== R_ParticleExplosion2 =============== */ void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) { int i, j; particle_t *p; int colorMod = 0; for (i=0; i<512; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 0.3; p->color = colorStart + (colorMod % colorLength); colorMod++; p->type = pt_blob; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } /* =============== R_BlobExplosion =============== */ void R_BlobExplosion (vec3_t org) { int i, j; particle_t *p; for (i=0 ; i<1024 ; i++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 1 + (rand()&8)*0.05; if (i & 1) { p->type = pt_blob; p->color = 66 + rand()%6; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } else { p->type = pt_blob2; p->color = 150 + rand()%6; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } } } /* =============== R_RunParticleEffect =============== */ void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) { int i, j; particle_t *p; for (i=0 ; inext; p->next = active_particles; active_particles = p; if (count == 1024) { // rocket explosion p->color = ramp1[0]; p->ramp = rand()&3; if (i & 1) { p->type = pt_explode; p->die = cl.time + (8 - p->ramp) / 10.0f; } else { p->type = pt_explode2; p->die = cl.time + (8 - p->ramp) / 15.0f; } for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()%32)-16); p->vel[j] = (rand()%512)-256; } } else { p->die = cl.time + 0.1*(rand()%5); p->color = (color&~7) + (rand()&7); p->type = pt_slowgrav; for (j=0 ; j<3 ; j++) { p->org[j] = org[j] + ((rand()&15)-8); p->vel[j] = dir[j]*15;// + (rand()%300)-150; } } } } /* =============== R_LavaSplash =============== */ void R_LavaSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i=-16 ; i<16 ; i++) for (j=-16 ; j<16 ; j++) for (k=0 ; k<1 ; k++) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 2 + (rand()&31) * 0.02; p->color = 224 + (rand()&7); p->type = pt_slowgrav; dir[0] = j*8 + (rand()&7); dir[1] = i*8 + (rand()&7); dir[2] = 256; p->org[0] = org[0] + dir[0]; p->org[1] = org[1] + dir[1]; p->org[2] = org[2] + (rand()&63); VectorNormalize (dir); vel = 50 + (rand()&63); VectorScale (dir, vel, p->vel); } } /* =============== R_TeleportSplash =============== */ void R_TeleportSplash (vec3_t org) { int i, j, k; particle_t *p; float vel; vec3_t dir; for (i=-16 ; i<16 ; i+=4) for (j=-16 ; j<16 ; j+=4) for (k=-24 ; k<32 ; k+=4) { if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->die = cl.time + 0.2 + (rand()&7) * 0.02; p->color = 7 + (rand()&7); p->type = pt_slowgrav; dir[0] = j*8; dir[1] = i*8; dir[2] = k*8; p->org[0] = org[0] + i + (rand()&3); p->org[1] = org[1] + j + (rand()&3); p->org[2] = org[2] + k + (rand()&3); VectorNormalize (dir); vel = 50 + (rand()&63); VectorScale (dir, vel, p->vel); } } /* =============== R_RocketTrail FIXME -- rename function and use #defined types instead of numbers =============== */ void R_RocketTrail (vec3_t start, vec3_t end, int type) { vec3_t vec; float len; int j; particle_t *p; int dec; static int tracercount; VectorSubtract (end, start, vec); len = VectorNormalize (vec); if (type < 128) dec = 3; else { dec = 1; type -= 128; } while (len > 0) { len -= dec; if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorCopy (vec3_origin, p->vel); p->die = cl.time + 2; switch (type) { case 0: // rocket trail p->ramp = (rand()&3); if (p->ramp >= 6) p->ramp -= 6; p->die = cl.time + (6 - p->ramp) / 5.0f; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); break; case 1: // smoke trail p->ramp = (rand()&2) + 2; p->die = cl.time + (6 - p->ramp) / 5.0f; p->color = ramp3[(int)p->ramp]; p->type = pt_fire; for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); break; case 2: // blood p->type = pt_grav; p->color = 67 + (rand()&3); for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); break; case 3: case 5: // tracer p->die = cl.time + 0.5; p->type = pt_static; if (type == 3) p->color = 52 + ((tracercount&4)<<1); else p->color = 230 + ((tracercount&4)<<1); tracercount++; VectorCopy (start, p->org); if (tracercount & 1) { p->vel[0] = 30*vec[1]; p->vel[1] = 30*-vec[0]; } else { p->vel[0] = 30*-vec[1]; p->vel[1] = 30*vec[0]; } break; case 4: // slight blood p->type = pt_grav; p->color = 67 + (rand()&3); for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()%6)-3); len -= 3; break; case 6: // voor trail p->color = 9*16 + 8 + (rand()&3); p->type = pt_static; p->die = cl.time + 0.3; for (j=0 ; j<3 ; j++) p->org[j] = start[j] + ((rand()&15)-8); break; } VectorAdd (start, vec, start); } } /* =============== CL_RunParticles -- johnfitz -- all the particle behavior, separated from R_DrawParticles =============== */ void CL_RunParticles (void) { particle_t *p, *kill; int i; float time1, time2, time3, dvel, frametime, scale, grav; extern cvar_t sv_gravity; frametime = cl.time - cl.oldtime; time3 = frametime * 15; time2 = frametime * 10; time1 = frametime * 5; grav = frametime * sv_gravity.value * 0.05; dvel = 4*frametime; for ( ;; ) { kill = active_particles; if (kill && kill->die < cl.time) { active_particles = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } for (p=active_particles ; p ; p=p->next) { for ( ;; ) { kill = p->next; if (kill && kill->die < cl.time) { p->next = kill->next; kill->next = free_particles; free_particles = kill; continue; } break; } p->org[0] += p->vel[0]*frametime; p->org[1] += p->vel[1]*frametime; p->org[2] += p->vel[2]*frametime; switch (p->type) { case pt_static: break; case pt_fire: p->ramp += time1; if (p->ramp >= 6) p->die = cl.time - 0.01f; else p->color = ramp3[(int)p->ramp]; p->vel[2] += grav; break; case pt_explode: p->ramp += time2; if (p->ramp >=8) p->die = cl.time - 0.01f; else p->color = ramp1[(int)p->ramp]; for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_explode2: p->ramp += time3; if (p->ramp >=8) p->die = cl.time - 0.01f; else p->color = ramp2[(int)p->ramp]; for (i=0 ; i<3 ; i++) p->vel[i] -= p->vel[i]*frametime; p->vel[2] -= grav; break; case pt_blob: for (i=0 ; i<3 ; i++) p->vel[i] += p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_blob2: for (i=0 ; i<2 ; i++) p->vel[i] -= p->vel[i]*dvel; p->vel[2] -= grav; break; case pt_grav: case pt_slowgrav: p->vel[2] -= grav; break; } } } /* =============== R_DrawParticles =============== */ extern cvar_t sv_gravity; void R_DrawParticles (void) { particle_t *p, *kill; vec3_t up, right, p_up, p_right, p_upright; //johnfitz -- p_ vectors GLubyte color[4], *c; //johnfitz -- particle transparency float alpha; //johnfitz -- particle transparency float grav; int i; float time2, time3; float time1; float dvel; float frametime; float* pPos = gVertexBuffer; float* pColor = gColorBuffer; float* pUV = gTexCoordBuffer; float colors[3]; float scale; GL_Bind(particletexture); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); GL_EnableState(GL_MODULATE); glDepthMask (GL_FALSE); //johnfitz -- fix for particle z-buffer bug GL_EnableState(GL_COLOR_ARRAY); //->glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); VectorScale (vup, 1.50, up); VectorScale (vright, 1.50, right); int num_vertices = 0; for (p=active_particles ; p ; p=p->next) { num_vertices += 3; // hack a scale up to keep particles from disapearing scale = (p->org[0] - r_origin[0])*vpn[0] + (p->org[1] - r_origin[1])*vpn[1] + (p->org[2] - r_origin[2])*vpn[2]; if (scale < 20) scale = 1.08f; //johnfitz -- added .08 to be consistent else scale = 1 + scale * 0.004; scale *= 1.27; c = (GLubyte *) &d_8to24table[(int)p->color]; colors[0] = ((float)(c[0])) / 255.0f; colors[1] = ((float)(c[1])) / 255.0f; colors[2] = ((float)(c[2])) / 255.0f; *gColorBuffer++ = colors[0]; *gColorBuffer++ = colors[1]; *gColorBuffer++ = colors[2]; *gColorBuffer++ = 1.0f; *gTexCoordBuffer++ = 0.0f; *gTexCoordBuffer++ = 0.0f; *gVertexBuffer++ = p->org[0]; *gVertexBuffer++ = p->org[1]; *gVertexBuffer++ = p->org[2]; *gColorBuffer++ = colors[0]; *gColorBuffer++ = colors[1]; *gColorBuffer++ = colors[2]; *gColorBuffer++ = 1.0f; *gTexCoordBuffer++ = 1.0f; *gTexCoordBuffer++ = 0.0f; VectorMA (p->org, scale, up, p_up); *gVertexBuffer++ = (p_up[0]); *gVertexBuffer++ = (p_up[1]); *gVertexBuffer++ = (p_up[2]); *gColorBuffer++ = colors[0]; *gColorBuffer++ = colors[1]; *gColorBuffer++ = colors[2]; *gColorBuffer++ = 1.0f; *gTexCoordBuffer++ = 0.0; *gTexCoordBuffer++ = 1.0; VectorMA (p->org, scale, right, p_right); *gVertexBuffer++ = (p_right[0]); *gVertexBuffer++ = (p_right[1]); *gVertexBuffer++ = (p_right[2]); } vglVertexAttribPointerMapped(0, pPos); vglVertexAttribPointerMapped(1, pUV); vglVertexAttribPointerMapped(2, pColor); GL_DrawPolygon(GL_TRIANGLES, num_vertices); GL_DisableState(GL_COLOR_ARRAY); //->glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glDepthMask (GL_TRUE); glDisable (GL_BLEND); GL_EnableState(GL_REPLACE); GL_Color(1,1,1,1); } ================================================ FILE: source/r_shared.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef GLQUAKE // r_shared.h: general refresh-related stuff shared between the refresh and the // driver // FIXME: clean up and move into d_iface.h #ifndef _R_SHARED_H_ #define _R_SHARED_H_ #define MAXVERTS 16 // max points in a surface polygon #define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate // polygon (while processing) // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define MAXHEIGHT 1024 #define MAXWIDTH 1280 #define MAXDIMENSION ((MAXHEIGHT > MAXWIDTH) ? MAXHEIGHT : MAXWIDTH) #define SIN_BUFFER_SIZE (MAXDIMENSION+CYCLE) #define INFINITE_DISTANCE 0x10000 // distance that's always guaranteed to // be farther away than anything in // the scene //=================================================================== extern void R_DrawLine (polyvert_t *polyvert0, polyvert_t *polyvert1); extern int cachewidth; extern pixel_t *cacheblock; extern int screenwidth; extern float pixelAspect; extern int r_drawnpolycount; extern cvar_t r_clearcolor; extern int sintable[SIN_BUFFER_SIZE]; extern int intsintable[SIN_BUFFER_SIZE]; extern vec3_t vup, base_vup; extern vec3_t vpn, base_vpn; extern vec3_t vright, base_vright; extern entity_t *currententity; #define NUMSTACKEDGES 2400 #define MINEDGES NUMSTACKEDGES #define NUMSTACKSURFACES 800 #define MINSURFACES NUMSTACKSURFACES #define MAXSPANS 3000 // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct espan_s { int u, v, count; struct espan_s *pnext; } espan_t; // FIXME: compress, make a union if that will help // insubmodel is only 1, flags is fewer than 32, spanstate could be a byte typedef struct surf_s { struct surf_s *next; // active surface stack in r_edge.c struct surf_s *prev; // used in r_edge.c for active surf stack struct espan_s *spans; // pointer to linked list of spans to draw int key; // sorting key (BSP order) int last_u; // set during tracing int spanstate; // 0 = not in span // 1 = in span // -1 = in inverted span (end before // start) int flags; // currentface flags void *data; // associated data like msurface_t entity_t *entity; float nearzi; // nearest 1/z on surface, for mipmapping bool insubmodel; float d_ziorigin, d_zistepu, d_zistepv; int pad[2]; // to 64 bytes } surf_t; extern surf_t *surfaces, *surface_p, *surf_max; // surfaces are generated in back to front order by the bsp, so if a surf // pointer is greater than another one, it should be drawn in front // surfaces[1] is the background, and is used as the active surface stack. // surfaces[0] is a dummy, because index 0 is used to indicate no surface // attached to an edge_t //=================================================================== extern vec3_t sxformaxis[4]; // s axis transformed into viewspace extern vec3_t txformaxis[4]; // t axis transformed into viewspac extern vec3_t modelorg, base_modelorg; extern float xcenter, ycenter; extern float xscale, yscale; extern float xscaleinv, yscaleinv; extern float xscaleshrink, yscaleshrink; extern int d_lightstylevalue[256]; // 8.8 frac of base light value extern void TransformVector (vec3_t in, vec3_t out); extern void SetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv); extern int r_skymade; extern void R_MakeSky (void); extern int ubasestep, errorterm, erroradjustup, erroradjustdown; // flags in finalvert_t.flags #define ALIAS_LEFT_CLIP 0x0001 #define ALIAS_TOP_CLIP 0x0002 #define ALIAS_RIGHT_CLIP 0x0004 #define ALIAS_BOTTOM_CLIP 0x0008 #define ALIAS_Z_CLIP 0x0010 // !!! if this is changed, it must be changed in d_ifacea.h too !!! #define ALIAS_ONSEAM 0x0020 // also defined in modelgen.h; // must be kept in sync #define ALIAS_XY_CLIP_MASK 0x000F // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct edge_s { fixed16_t u; fixed16_t u_step; struct edge_s *prev, *next; unsigned short surfs[2]; struct edge_s *nextremove; float nearzi; medge_t *owner; } edge_t; #endif // _R_SHARED_H_ #endif // GLQUAKE ================================================ FILE: source/render.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // refresh.h -- public interface to refresh functions #define MAXCLIPPLANES 11 #define TOP_RANGE 16 // soldier uniform colors #define BOTTOM_RANGE 96 //============================================================================= typedef struct efrag_s { struct mleaf_s *leaf; struct efrag_s *leafnext; struct entity_s *entity; struct efrag_s *entnext; } efrag_t; typedef struct entity_s { bool forcelink; // model changed int update_type; entity_state_t baseline; // to fill in defaults in updates double msgtime; // time of last update vec3_t msg_origins[2]; // last two updates (0 is newest) vec3_t origin; vec3_t msg_angles[2]; // last two updates (0 is newest) vec3_t angles; struct model_s *model; // NULL = no model struct efrag_s *efrag; // linked list of efrags int frame; float syncbase; // for client-side animations byte *colormap; int effects; // light, particals, etc int skinnum; // for Alias models int visframe; // last frame this entity was // found in an active leaf int dlightframe; // dynamic lighting int dlightbits; // FIXME: could turn these into a union int trivial_accept; struct mnode_s *topnode; // for bmodels, first world node // that splits bmodel, or NULL if // not split // fenix@io.com: model animation interpolation float frame_start_time; float frame_interval; int pose1; int pose2; // fenix@io.com: model transform interpolation float translate_start_time; vec3_t origin1; vec3_t origin2; float rotate_start_time; vec3_t angles1; vec3_t angles2; int modelflags; float alpha; } entity_t; // !!! if this is changed, it must be changed in asm_draw.h too !!! typedef struct { vrect_t vrect; // subwindow in video for refresh // FIXME: not need vrect next field here? vrect_t aliasvrect; // scaled Alias version int vrectright, vrectbottom; // right & bottom screen coords int aliasvrectright, aliasvrectbottom; // scaled Alias versions float vrectrightedge; // rightmost right edge we care about, // for use in edge list float fvrectx, fvrecty; // for floating-point compares float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping int vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 int vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 float fvrectright_adj, fvrectbottom_adj; // right and bottom edges, for clamping float fvrectright; // rightmost edge, for Alias clamping float fvrectbottom; // bottommost edge, for Alias clamping float horizontalFieldOfView; // at Z = 1.0, this many X is visible // 2.0 = 90 degrees float xOrigin; // should probably always be 0.5 float yOrigin; // between be around 0.3 to 0.5 vec3_t vieworg; vec3_t viewangles; float fov_x, fov_y; int ambientlight; } refdef_t; // // refresh // extern int reinit_surfcache; extern refdef_t r_refdef; extern vec3_t r_origin, vpn, vright, vup; extern struct texture_s *r_notexture_mip; void R_Init (void); void R_InitTextures (void); void R_InitEfrags (void); void R_RenderView (void); // must set r_refdef first void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect); // called whenever r_refdef or vid change void R_InitSky (struct miptex_s *mt); // called at level load void R_AddEfrags (entity_t *ent); void R_RemoveEfrags (entity_t *ent); void R_NewMap (void); void R_ParseParticleEffect (void); void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); void R_RocketTrail (vec3_t start, vec3_t end, int type); #ifdef QUAKE2 void R_DarkFieldParticles (entity_t *ent); #endif void R_EntityParticles (entity_t *ent); void R_BlobExplosion (vec3_t org); void R_ParticleExplosion (vec3_t org); void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength); void R_LavaSplash (vec3_t org); void R_TeleportSplash (vec3_t org); void R_PushDlights (void); // // surface cache related // extern int reinit_surfcache; // if 1, surface cache is currently empty and extern bool r_cache_thrash; // set if thrashing the surface cache int D_SurfaceCacheForRes (int width, int height); void D_FlushCaches (void); void D_DeleteSurfaceCache (void); void D_InitCaches (void *buffer, int size); void R_SetVrect (vrect_t *pvrect, vrect_t *pvrectin, int lineadj); extern entity_t r_worldentity; ================================================ FILE: source/sbar.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sbar.c -- status bar code #include "quakedef.h" extern void M_Print (int cx, int cy, char *str); extern cvar_t scr_sbaralpha; extern cvar_t scr_sbarscale; extern cvar_t viewsize; int sb_updates; // if >= vid.numpages, no update needed #define STAT_MINUS 10 // num frame for '-' stats digit qpic_t *sb_nums[2][11]; qpic_t *sb_colon, *sb_slash; qpic_t *sb_ibar; qpic_t *sb_sbar; qpic_t *sb_scorebar; qpic_t *sb_weapons[7][8]; // 0 is active, 1 is owned, 2-5 are flashes qpic_t *sb_ammo[4]; qpic_t *sb_sigil[4]; qpic_t *sb_armor[3]; qpic_t *sb_items[32]; qpic_t *sb_faces[7][2]; // 0 is gibbed, 1 is dead, 2-6 are alive // 0 is static, 1 is temporary animation qpic_t *sb_face_invis; qpic_t *sb_face_quad; qpic_t *sb_face_invuln; qpic_t *sb_face_invis_invuln; bool sb_showscores; qpic_t *rsb_invbar[2]; qpic_t *rsb_weapons[5]; qpic_t *rsb_items[2]; qpic_t *rsb_ammo[3]; qpic_t *rsb_teambord; // PGM 01/19/97 - team color border //MED 01/04/97 added two more weapons + 3 alternates for grenade launcher qpic_t *hsb_weapons[7][5]; // 0 is active, 1 is owned, 2-5 are flashes //MED 01/04/97 added array to simplify weapon parsing int hipweapons[4] = {HIT_LASER_CANNON_BIT,HIT_MJOLNIR_BIT,4,HIT_PROXIMITY_GUN_BIT}; //MED 01/04/97 added hipnotic items array qpic_t *hsb_items[2]; void Sbar_MiniDeathmatchOverlay (void); void Sbar_DeathmatchOverlay (void); void M_DrawPic (int x, int y, qpic_t *pic); /* =============== Sbar_ShowScores Tab key down =============== */ void Sbar_ShowScores (void) { if (sb_showscores) return; sb_showscores = true; sb_updates = 0; } /* =============== Sbar_DontShowScores Tab key up =============== */ void Sbar_DontShowScores (void) { sb_showscores = false; sb_updates = 0; } /* =============== Sbar_Changed =============== */ void Sbar_Changed (void) { sb_updates = 0; // update next frame } /* =============== Sbar_Init =============== */ void Sbar_Init (void) { int i; for (i=0 ; i<10 ; i++) { sb_nums[0][i] = Draw_PicFromWad (va("num_%i",i)); sb_nums[1][i] = Draw_PicFromWad (va("anum_%i",i)); } sb_nums[0][10] = Draw_PicFromWad ("num_minus"); sb_nums[1][10] = Draw_PicFromWad ("anum_minus"); sb_colon = Draw_PicFromWad ("num_colon"); sb_slash = Draw_PicFromWad ("num_slash"); sb_weapons[0][0] = Draw_PicFromWad ("inv_shotgun"); sb_weapons[0][1] = Draw_PicFromWad ("inv_sshotgun"); sb_weapons[0][2] = Draw_PicFromWad ("inv_nailgun"); sb_weapons[0][3] = Draw_PicFromWad ("inv_snailgun"); sb_weapons[0][4] = Draw_PicFromWad ("inv_rlaunch"); sb_weapons[0][5] = Draw_PicFromWad ("inv_srlaunch"); sb_weapons[0][6] = Draw_PicFromWad ("inv_lightng"); sb_weapons[1][0] = Draw_PicFromWad ("inv2_shotgun"); sb_weapons[1][1] = Draw_PicFromWad ("inv2_sshotgun"); sb_weapons[1][2] = Draw_PicFromWad ("inv2_nailgun"); sb_weapons[1][3] = Draw_PicFromWad ("inv2_snailgun"); sb_weapons[1][4] = Draw_PicFromWad ("inv2_rlaunch"); sb_weapons[1][5] = Draw_PicFromWad ("inv2_srlaunch"); sb_weapons[1][6] = Draw_PicFromWad ("inv2_lightng"); for (i=0 ; i<5 ; i++) { sb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_shotgun",i+1)); sb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_sshotgun",i+1)); sb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_nailgun",i+1)); sb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_snailgun",i+1)); sb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_rlaunch",i+1)); sb_weapons[2+i][5] = Draw_PicFromWad (va("inva%i_srlaunch",i+1)); sb_weapons[2+i][6] = Draw_PicFromWad (va("inva%i_lightng",i+1)); } sb_ammo[0] = Draw_PicFromWad ("sb_shells"); sb_ammo[1] = Draw_PicFromWad ("sb_nails"); sb_ammo[2] = Draw_PicFromWad ("sb_rocket"); sb_ammo[3] = Draw_PicFromWad ("sb_cells"); sb_armor[0] = Draw_PicFromWad ("sb_armor1"); sb_armor[1] = Draw_PicFromWad ("sb_armor2"); sb_armor[2] = Draw_PicFromWad ("sb_armor3"); sb_items[0] = Draw_PicFromWad ("sb_key1"); sb_items[1] = Draw_PicFromWad ("sb_key2"); sb_items[2] = Draw_PicFromWad ("sb_invis"); sb_items[3] = Draw_PicFromWad ("sb_invuln"); sb_items[4] = Draw_PicFromWad ("sb_suit"); sb_items[5] = Draw_PicFromWad ("sb_quad"); sb_sigil[0] = Draw_PicFromWad ("sb_sigil1"); sb_sigil[1] = Draw_PicFromWad ("sb_sigil2"); sb_sigil[2] = Draw_PicFromWad ("sb_sigil3"); sb_sigil[3] = Draw_PicFromWad ("sb_sigil4"); sb_faces[4][0] = Draw_PicFromWad ("face1"); sb_faces[4][1] = Draw_PicFromWad ("face_p1"); sb_faces[3][0] = Draw_PicFromWad ("face2"); sb_faces[3][1] = Draw_PicFromWad ("face_p2"); sb_faces[2][0] = Draw_PicFromWad ("face3"); sb_faces[2][1] = Draw_PicFromWad ("face_p3"); sb_faces[1][0] = Draw_PicFromWad ("face4"); sb_faces[1][1] = Draw_PicFromWad ("face_p4"); sb_faces[0][0] = Draw_PicFromWad ("face5"); sb_faces[0][1] = Draw_PicFromWad ("face_p5"); sb_face_invis = Draw_PicFromWad ("face_invis"); sb_face_invuln = Draw_PicFromWad ("face_invul2"); sb_face_invis_invuln = Draw_PicFromWad ("face_inv2"); sb_face_quad = Draw_PicFromWad ("face_quad"); Cmd_AddCommand ("+showscores", Sbar_ShowScores); Cmd_AddCommand ("-showscores", Sbar_DontShowScores); sb_sbar = Draw_PicFromWad ("sbar"); sb_ibar = Draw_PicFromWad ("ibar"); sb_scorebar = Draw_PicFromWad ("scorebar"); //MED 01/04/97 added new hipnotic weapons if (hipnotic) { hsb_weapons[0][0] = Draw_PicFromWad ("inv_laser"); hsb_weapons[0][1] = Draw_PicFromWad ("inv_mjolnir"); hsb_weapons[0][2] = Draw_PicFromWad ("inv_gren_prox"); hsb_weapons[0][3] = Draw_PicFromWad ("inv_prox_gren"); hsb_weapons[0][4] = Draw_PicFromWad ("inv_prox"); hsb_weapons[1][0] = Draw_PicFromWad ("inv2_laser"); hsb_weapons[1][1] = Draw_PicFromWad ("inv2_mjolnir"); hsb_weapons[1][2] = Draw_PicFromWad ("inv2_gren_prox"); hsb_weapons[1][3] = Draw_PicFromWad ("inv2_prox_gren"); hsb_weapons[1][4] = Draw_PicFromWad ("inv2_prox"); for (i=0 ; i<5 ; i++) { hsb_weapons[2+i][0] = Draw_PicFromWad (va("inva%i_laser",i+1)); hsb_weapons[2+i][1] = Draw_PicFromWad (va("inva%i_mjolnir",i+1)); hsb_weapons[2+i][2] = Draw_PicFromWad (va("inva%i_gren_prox",i+1)); hsb_weapons[2+i][3] = Draw_PicFromWad (va("inva%i_prox_gren",i+1)); hsb_weapons[2+i][4] = Draw_PicFromWad (va("inva%i_prox",i+1)); } hsb_items[0] = Draw_PicFromWad ("sb_wsuit"); hsb_items[1] = Draw_PicFromWad ("sb_eshld"); } if (rogue) { rsb_invbar[0] = Draw_PicFromWad ("r_invbar1"); rsb_invbar[1] = Draw_PicFromWad ("r_invbar2"); rsb_weapons[0] = Draw_PicFromWad ("r_lava"); rsb_weapons[1] = Draw_PicFromWad ("r_superlava"); rsb_weapons[2] = Draw_PicFromWad ("r_gren"); rsb_weapons[3] = Draw_PicFromWad ("r_multirock"); rsb_weapons[4] = Draw_PicFromWad ("r_plasma"); rsb_items[0] = Draw_PicFromWad ("r_shield1"); rsb_items[1] = Draw_PicFromWad ("r_agrav1"); // PGM 01/19/97 - team color border rsb_teambord = Draw_PicFromWad ("r_teambord"); // PGM 01/19/97 - team color border rsb_ammo[0] = Draw_PicFromWad ("r_ammolava"); rsb_ammo[1] = Draw_PicFromWad ("r_ammomulti"); rsb_ammo[2] = Draw_PicFromWad ("r_ammoplasma"); } } //============================================================================= // drawing routines are relative to the status bar location /* ============= Sbar_DrawPic ============= */ inline __attribute__((always_inline)) void Sbar_DrawPic (int x, int y, qpic_t *pic) { Draw_Pic (x, y + 24, pic); } /* ============= Sbar_DrawPicAlpha ============= */ inline __attribute__((always_inline)) void Sbar_DrawPicAlpha (int x, int y, qpic_t *pic, float alpha) { Draw_AlphaPic (x, y + 24, pic, alpha); } /* ================ Sbar_DrawCharacter Draws one solid graphics character ================ */ inline __attribute__((always_inline)) void Sbar_DrawCharacter (int x, int y, int num) { Batch_Character (x, y + 24, num); } /* ================ Sbar_DrawString ================ */ inline __attribute__((always_inline)) void Sbar_DrawString (int x, int y, char *str) { Batch_String (x, y + 24, str, 0); } /* ============= Sbar_itoa ============= */ int Sbar_itoa (int num, char *buf) { char *str; int pow10; int dig; str = buf; if (num < 0) { *str++ = '-'; num = -num; } for (pow10 = 10 ; num >= pow10 ; pow10 *= 10) ; do { pow10 /= 10; dig = num/pow10; *str++ = '0'+dig; num -= dig*pow10; } while (pow10 != 1); *str = 0; return str-buf; } /* ============= Sbar_DrawNum ============= */ void Sbar_DrawNum (int x, int y, int num, int digits, int color) { char str[12]; char *ptr; int l, frame; num = min(999,num); //johnfitz -- cap high values rather than truncating number l = Sbar_itoa (num, str); ptr = str; if (l > digits) ptr += (l-digits); if (l < digits) x += (digits-l)*24; while (*ptr) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; Sbar_DrawPic (x,y,sb_nums[color][frame]); //johnfitz -- DrawTransPic is obsolete x += 24; ptr++; } } //============================================================================= int fragsort[MAX_SCOREBOARD]; char scoreboardtext[MAX_SCOREBOARD][20]; int scoreboardtop[MAX_SCOREBOARD]; int scoreboardbottom[MAX_SCOREBOARD]; int scoreboardcount[MAX_SCOREBOARD]; int scoreboardlines; /* =============== Sbar_SortFrags =============== */ void Sbar_SortFrags (void) { int i, j, k; // sort by frags scoreboardlines = 0; for (i=0 ; ifrags, s->name); top = s->colors & 0xf0; bottom = (s->colors & 15) <<4; scoreboardtop[i] = Sbar_ColorForMap (top); scoreboardbottom[i] = Sbar_ColorForMap (bottom); } } /* =============== Sbar_SoloScoreboard =============== */ void Sbar_SoloScoreboard (void) { char str[80]; int minutes, seconds, tens, units; int l; sprintf (str,"Monsters:%3i /%3i", cl.stats[STAT_MONSTERS], cl.stats[STAT_TOTALMONSTERS]); Sbar_DrawString (8, 4, str); sprintf (str,"Secrets :%3i /%3i", cl.stats[STAT_SECRETS], cl.stats[STAT_TOTALSECRETS]); Sbar_DrawString (8, 12, str); // time minutes = cl.time / 60; seconds = cl.time - 60*minutes; tens = seconds / 10; units = seconds - 10*tens; sprintf (str,"Time :%3i:%i%i", minutes, tens, units); Sbar_DrawString (184, 4, str); // draw level name l = strlen (cl.levelname); Sbar_DrawString (232 - l*4, 12, cl.levelname); Draw_Batched(); } /* =============== Sbar_DrawScoreboard =============== */ void Sbar_DrawScoreboard (void) { Sbar_SoloScoreboard (); if (cl.gametype == GAME_DEATHMATCH) Sbar_DeathmatchOverlay (); } //============================================================================= /* =============== Sbar_DrawInventory =============== */ void Sbar_DrawInventory (void) { int i; char num[6]; float time; int flashon; if (rogue) { if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) Sbar_DrawPicAlpha (0, -24, rsb_invbar[0], scr_sbaralpha.value); else Sbar_DrawPicAlpha (0, -24, rsb_invbar[1], scr_sbaralpha.value); } else { Sbar_DrawPicAlpha (0, -24, sb_ibar, scr_sbaralpha.value); } // weapons for (i=0 ; i<7 ; i++) { if (cl.items & (IT_SHOTGUN<= 10) { if ( cl.stats[STAT_ACTIVEWEAPON] == (IT_SHOTGUN< 1) sb_updates = 0; // force update to remove flash } } // MED 01/04/97 // hipnotic weapons if (hipnotic) { int grenadeflashing=0; for (i=0 ; i<4 ; i++) { if (cl.items & (1<= 10) { if ( cl.stats[STAT_ACTIVEWEAPON] == (1< 1) sb_updates = 0; // force update to remove flash } } } if (rogue) { // check for powered up weapon. if ( cl.stats[STAT_ACTIVEWEAPON] >= RIT_LAVA_NAILGUN ) { for (i=0;i<5;i++) { if (cl.stats[STAT_ACTIVEWEAPON] == (RIT_LAVA_NAILGUN << i)) { Sbar_DrawPic ((i+2)*24, -16, rsb_weapons[i]); } } } } // ammo counts for (i=0 ; i<4 ; i++) { sprintf (num, "%3i",cl.stats[STAT_SHELLS+i] ); if (num[0] != ' ') Sbar_DrawCharacter ( (6*i+1)*8 - 2, -24, 18 + num[0] - '0'); if (num[1] != ' ') Sbar_DrawCharacter ( (6*i+2)*8 - 2, -24, 18 + num[1] - '0'); if (num[2] != ' ') Sbar_DrawCharacter ( (6*i+3)*8 - 2, -24, 18 + num[2] - '0'); } Draw_Batched(); flashon = 0; // items for (i=0 ; i<6 ; i++) if (cl.items & (1<<(17+i))) { time = cl.item_gettime[17+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; } else { //MED 01/04/97 changed keys if (!hipnotic || (i>1)) { Sbar_DrawPic (192 + i*16, -16, sb_items[i]); } } if (time && time > cl.time - 2) sb_updates = 0; } //MED 01/04/97 added hipnotic items // hipnotic items if (hipnotic) { for (i=0 ; i<2 ; i++) if (cl.items & (1<<(24+i))) { time = cl.item_gettime[24+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; } else { Sbar_DrawPic (288 + i*16, -16, hsb_items[i]); } if (time && time > cl.time - 2) sb_updates = 0; } } if (rogue) { // new rogue items for (i=0 ; i<2 ; i++) { if (cl.items & (1<<(29+i))) { time = cl.item_gettime[29+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; } else { Sbar_DrawPic (288 + i*16, -16, rsb_items[i]); } if (time && time > cl.time - 2) sb_updates = 0; } } } else { // sigils for (i=0 ; i<4 ; i++) { if (cl.items & (1<<(28+i))) { time = cl.item_gettime[28+i]; if (time && time > cl.time - 2 && flashon ) { // flash frame sb_updates = 0; } else Sbar_DrawPic (320-32 + i*8, -16, sb_sigil[i]); if (time && time > cl.time - 2) sb_updates = 0; } } } } //============================================================================= /* =============== Sbar_DrawFrags -- johnfitz -- heavy revision =============== */ void Sbar_DrawFrags (void) { int numscores, i, x, color; char num[12]; scoreboard_t *s; Sbar_SortFrags (); // draw the text numscores = min(scoreboardlines, 4); for (i = 0, x = 184; iname[0]) continue; // top color color = s->colors & 0xf0; color = Sbar_ColorForMap (color); Draw_Fill (x + 10, 1, 28, 4, color); // bottom color color = (s->colors & 15)<<4; color = Sbar_ColorForMap (color); Draw_Fill (x + 10, 5, 28, 3, color); // number sprintf (num, "%3i", s->frags); Sbar_DrawCharacter (x + 12, -24, num[0]); Sbar_DrawCharacter (x + 20, -24, num[1]); Sbar_DrawCharacter (x + 28, -24, num[2]); // brackets if (fragsort[i] == cl.viewentity - 1) { Sbar_DrawCharacter (x + 6, -24, 16); Sbar_DrawCharacter (x + 32, -24, 17); } Draw_Batched(); } } //============================================================================= /* =============== Sbar_DrawFace =============== */ void Sbar_DrawFace (void) { int f, anim; // PGM 01/19/97 - team color drawing // PGM 03/02/97 - fixed so color swatch only appears in CTF modes if (rogue && (cl.maxclients != 1) && (teamplay.value>3) && (teamplay.value<7)) { int top, bottom; int xofs; char num[12]; scoreboard_t *s; s = &cl.scores[cl.viewentity - 1]; // draw background top = s->colors & 0xf0; bottom = (s->colors & 15)<<4; top = Sbar_ColorForMap (top); bottom = Sbar_ColorForMap (bottom); if (cl.gametype == GAME_DEATHMATCH) xofs = 113; else xofs = ((vid.width - 320)>>1) + 113; Sbar_DrawPic (112, 0, rsb_teambord); Draw_Fill (xofs, /*vid.height-*/24+3, 22, 9, top); //johnfitz -- sbar coords are now relative Draw_Fill (xofs, /*vid.height-*/24+12, 22, 9, bottom); //johnfitz -- sbar coords are now relative // draw number f = s->frags; sprintf (num, "%3i",f); if (top==8) { if (num[0] != ' ') Sbar_DrawCharacter(109, 3, 18 + num[0] - '0'); if (num[1] != ' ') Sbar_DrawCharacter(116, 3, 18 + num[1] - '0'); if (num[2] != ' ') Sbar_DrawCharacter(123, 3, 18 + num[2] - '0'); } else { Sbar_DrawCharacter ( 109, 3, num[0]); Sbar_DrawCharacter ( 116, 3, num[1]); Sbar_DrawCharacter ( 123, 3, num[2]); } Draw_Batched(); return; } // PGM 01/19/97 - team color drawing if ( (cl.items & (IT_INVISIBILITY | IT_INVULNERABILITY) ) == (IT_INVISIBILITY | IT_INVULNERABILITY) ) { Sbar_DrawPic (112, 0, sb_face_invis_invuln); return; } if (cl.items & IT_QUAD) { Sbar_DrawPic (112, 0, sb_face_quad ); return; } if (cl.items & IT_INVISIBILITY) { Sbar_DrawPic (112, 0, sb_face_invis ); return; } if (cl.items & IT_INVULNERABILITY) { Sbar_DrawPic (112, 0, sb_face_invuln); return; } if (cl.stats[STAT_HEALTH] >= 100) f = 4; else f = cl.stats[STAT_HEALTH] / 20; if (cl.time <= cl.faceanimtime) { anim = 1; sb_updates = 0; // make sure the anim gets drawn over } else anim = 0; Sbar_DrawPic (112, 0, sb_faces[f][anim]); } /* =============== Sbar_Draw =============== */ void Sbar_Draw (void) { if (scr_con_current == vid.height) return; // console is full screen if (cl.intermission) return; //johnfitz -- never draw sbar during intermission if (sb_updates >= vid.numpages) return; scr_copyeverything = 1; sb_updates++; GL_SetCanvas (CANVAS_DEFAULT); //johnfitz if (viewsize.value < 100.0) Draw_TileClear (0, vid.height - sb_lines, vid.width, sb_lines); GL_SetCanvas (CANVAS_SBAR); //johnfitz if (viewsize.value < 110.0) { Sbar_DrawInventory (); if (cl.maxclients != 1) Sbar_DrawFrags (); } if (sb_showscores || cl.stats[STAT_HEALTH] <= 0) { Sbar_DrawPicAlpha (0, 0, sb_scorebar, scr_sbaralpha.value); Sbar_DrawScoreboard (); sb_updates = 0; } else if (viewsize.value < 120) { Sbar_DrawPicAlpha (0, 0, sb_sbar, scr_sbaralpha.value); // keys (hipnotic only) //MED 01/04/97 moved keys here so they would not be overwritten if (hipnotic) { if (cl.items & IT_KEY1) Sbar_DrawPic (209, 3, sb_items[0]); if (cl.items & IT_KEY2) Sbar_DrawPic (209, 12, sb_items[1]); } // armor if (cl.items & IT_INVULNERABILITY) { Sbar_DrawNum (24, 0, 666, 3, 1); Sbar_DrawPic (0, 0, draw_disc); } else { if (rogue) { Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); if (cl.items & RIT_ARMOR3) Sbar_DrawPic (0, 0, sb_armor[2]); else if (cl.items & RIT_ARMOR2) Sbar_DrawPic (0, 0, sb_armor[1]); else if (cl.items & RIT_ARMOR1) Sbar_DrawPic (0, 0, sb_armor[0]); } else { Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3 , cl.stats[STAT_ARMOR] <= 25); if (cl.items & IT_ARMOR3) Sbar_DrawPic (0, 0, sb_armor[2]); else if (cl.items & IT_ARMOR2) Sbar_DrawPic (0, 0, sb_armor[1]); else if (cl.items & IT_ARMOR1) Sbar_DrawPic (0, 0, sb_armor[0]); } } // face Sbar_DrawFace (); // health Sbar_DrawNum (136, 0, cl.stats[STAT_HEALTH], 3 , cl.stats[STAT_HEALTH] <= 25); // ammo icon if (rogue) { if (cl.items & RIT_SHELLS) Sbar_DrawPic (224, 0, sb_ammo[0]); else if (cl.items & RIT_NAILS) Sbar_DrawPic (224, 0, sb_ammo[1]); else if (cl.items & RIT_ROCKETS) Sbar_DrawPic (224, 0, sb_ammo[2]); else if (cl.items & RIT_CELLS) Sbar_DrawPic (224, 0, sb_ammo[3]); else if (cl.items & RIT_LAVA_NAILS) Sbar_DrawPic (224, 0, rsb_ammo[0]); else if (cl.items & RIT_PLASMA_AMMO) Sbar_DrawPic (224, 0, rsb_ammo[1]); else if (cl.items & RIT_MULTI_ROCKETS) Sbar_DrawPic (224, 0, rsb_ammo[2]); } else { if (cl.items & IT_SHELLS) Sbar_DrawPic (224, 0, sb_ammo[0]); else if (cl.items & IT_NAILS) Sbar_DrawPic (224, 0, sb_ammo[1]); else if (cl.items & IT_ROCKETS) Sbar_DrawPic (224, 0, sb_ammo[2]); else if (cl.items & IT_CELLS) Sbar_DrawPic (224, 0, sb_ammo[3]); } Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); } if (cl.gametype == GAME_DEATHMATCH) Sbar_MiniDeathmatchOverlay (); } //============================================================================= /* ================== Sbar_IntermissionNumber ================== */ void Sbar_IntermissionNumber (int x, int y, int num, int digits, int color) { char str[12]; char *ptr; int l, frame; l = Sbar_itoa (num, str); ptr = str; if (l > digits) ptr += (l-digits); if (l < digits) x += (digits-l)*24; while (*ptr) { if (*ptr == '-') frame = STAT_MINUS; else frame = *ptr -'0'; Draw_Pic (x,y,sb_nums[color][frame]); x += 24; ptr++; } } /* ================== Sbar_DeathmatchOverlay ================== */ void Sbar_DeathmatchOverlay (void) { qpic_t *pic; int i, k, l; int top, bottom; int x, y, f; char num[12]; scoreboard_t *s; GL_SetCanvas (CANVAS_MENU); //johnfitz scr_copyeverything = 1; scr_fullupdate = 0; pic = Draw_CachePic ("gfx/ranking.lmp"); M_DrawPic ((320-pic->width)/2, 8, pic); // scores Sbar_SortFrags (); // draw the text l = scoreboardlines; x = 80; y = 40; for (i=0 ; iname[0]) continue; // draw background top = s->colors & 0xf0; bottom = (s->colors & 15)<<4; top = Sbar_ColorForMap (top); bottom = Sbar_ColorForMap (bottom); Draw_Fill ( x, y, 40, 4, top); Draw_Fill ( x, y+4, 40, 4, bottom); // draw number f = s->frags; sprintf (num, "%3i",f); Batch_Character ( x+8 , y, num[0]); Batch_Character ( x+16 , y, num[1]); Batch_Character ( x+24 , y, num[2]); if (k == cl.viewentity - 1) Batch_Character ( x - 8, y, 12); // draw name M_Print (x+64, y, s->name); Draw_Batched(); y += 10; } GL_SetCanvas (CANVAS_SBAR); //johnfitz } /* ================== Sbar_DeathmatchOverlay ================== */ void Sbar_MiniDeathmatchOverlay (void) { qpic_t *pic; int i, k, l; int top, bottom; int x, y, f; char num[12]; scoreboard_t *s; int numlines; float scale = Q_CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); //johnfitz if (((float)glwidth / scale) < 512 || !sb_lines) return; scr_copyeverything = 1; scr_fullupdate = 0; // scores Sbar_SortFrags (); // draw the text l = scoreboardlines; y = vid.height - sb_lines; numlines = (viewsize.value >= 110) ? 3 : 6; //johnfitz if (numlines < 3) return; //find us for (i = 0; i < scoreboardlines; i++) if (fragsort[i] == cl.viewentity - 1) break; if (i == scoreboardlines) // we're not there i = 0; else // figure out start i = i - numlines/2; if (i > scoreboardlines - numlines) i = scoreboardlines - numlines; if (i < 0) i = 0; x = 324; y = (viewsize.value >= 110) ? 24 : 0; //johnfitz -- start at the right place for ( ; i < scoreboardlines && y <= 48; i++, y+=8) //johnfitz -- change y init, test, inc { k = fragsort[i]; s = &cl.scores[k]; if (!s->name[0]) continue; // draw background top = s->colors & 0xf0; bottom = (s->colors & 15)<<4; top = Sbar_ColorForMap (top); bottom = Sbar_ColorForMap (bottom); Draw_Fill ( x, y+1, 40, 3, top); Draw_Fill ( x, y+4, 40, 4, bottom); // draw number f = s->frags; sprintf (num, "%3i",f); Batch_Character ( x+8 , y, num[0]); Batch_Character ( x+16 , y, num[1]); Batch_Character ( x+24 , y, num[2]); if (k == cl.viewentity - 1) { Batch_Character ( x, y, 16); Batch_Character ( x + 32, y, 17); } // draw name Batch_String (x+48, y, s->name, 0); Draw_Batched(); y += 8; } } /* ================== Sbar_IntermissionOverlay ================== */ void Sbar_IntermissionOverlay (void) { qpic_t *pic; int dig; int num; scr_copyeverything = 1; scr_fullupdate = 0; if (cl.gametype == GAME_DEATHMATCH) { Sbar_DeathmatchOverlay (); return; } GL_SetCanvas (CANVAS_MENU); //johnfitz //muff@yakko.globalnet.co.uk //dead easy stuff really pic = Draw_CachePic ("gfx/complete.lmp"); Draw_Pic (64, 24, pic); pic = Draw_CachePic ("gfx/inter.lmp"); Draw_TransPic (0, 56, pic); dig = cl.completed_time/60; Sbar_IntermissionNumber (152, 64, dig, 3, 0); //johnfitz -- was 160 num = cl.completed_time - dig*60; Draw_Pic (224,64,sb_colon); //johnfitz -- was 234 Draw_Pic (240,64,sb_nums[0][num/10]); //johnfitz -- was 246 Draw_Pic (264,64,sb_nums[0][num%10]); //johnfitz -- was 266 Sbar_IntermissionNumber (152, 104, cl.stats[STAT_SECRETS], 3, 0); //johnfitz -- was 160 Draw_Pic (224,104,sb_slash); //johnfitz -- was 232 Sbar_IntermissionNumber (240, 104, cl.stats[STAT_TOTALSECRETS], 3, 0); //johnfitz -- was 248 Sbar_IntermissionNumber (152, 144, cl.stats[STAT_MONSTERS], 3, 0); //johnfitz -- was 160 Draw_Pic (224,144,sb_slash); //johnfitz -- was 232 Sbar_IntermissionNumber (240, 144, cl.stats[STAT_TOTALMONSTERS], 3, 0); //johnfitz -- was 248 //end of muff } /* ================== Sbar_FinaleOverlay ================== */ void Sbar_FinaleOverlay (void) { qpic_t *pic; scr_copyeverything = 1; GL_SetCanvas (CANVAS_MENU); //johnfitz pic = Draw_CachePic ("gfx/finale.lmp"); Draw_TransPic ( (320-pic->width)/2, 16, pic); } ================================================ FILE: source/sbar.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // the status bar is only redrawn if something has changed, but if anything // does, the entire thing will be redrawn for the next vid.numpages frames. #define SBAR_HEIGHT 24 extern int sb_lines; // scan lines to draw void Sbar_Init (void); void Sbar_Changed (void); // call whenever any of the client stats represented on the sbar changes void Sbar_Draw (void); // called every frame by screen void Sbar_IntermissionOverlay (void); // called each frame after the level has been completed void Sbar_FinaleOverlay (void); ================================================ FILE: source/screen.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // screen.h void SCR_Init (void); void SCR_UpdateScreen (void); void SCR_Benchmark (void); void SCR_SizeUp (void); void SCR_SizeDown (void); void SCR_BringDownConsole (void); void SCR_CenterPrint (char *str); void SCR_BeginLoadingPlaque (void); void SCR_EndLoadingPlaque (void); int SCR_ModalMessage (char *text); extern float scr_con_current; extern float scr_conlines; // lines of console to display extern int scr_fullupdate; // set to 0 to force full redraw extern int sb_lines; extern int clearnotify; // set to 0 whenever notify text is drawn extern bool scr_disabled_for_loading; extern bool scr_skipupdate; extern cvar_t viewsize; // only the refresh window will be updated unless these variables are flagged extern int scr_copytop; extern int scr_copyeverything; extern bool block_drawing; void SCR_UpdateWholeScreen (void); ================================================ FILE: source/server.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // server.h typedef struct { int maxclients; int maxclientslimit; struct client_s *clients; // [maxclients] int serverflags; // episode completion information bool changelevel_issued; // cleared when at SV_SpawnServer } server_static_t; //============================================================================= typedef enum {ss_loading, ss_active} server_state_t; typedef struct { bool active; // false if only a net client bool paused; bool loadgame; // handle connections specially double time; int lastcheck; // used by PF_checkclient double lastchecktime; char name[64]; // map name #ifdef QUAKE2 char startspot[64]; #endif char modelname[64]; // maps/.bsp, for model_precache[0] struct model_s *worldmodel; char *model_precache[MAX_MODELS]; // NULL terminated struct model_s *models[MAX_MODELS]; char *sound_precache[MAX_SOUNDS]; // NULL terminated char *lightstyles[MAX_LIGHTSTYLES]; int num_edicts; int max_edicts; edict_t *edicts; // can NOT be array indexed, because // edict_t is variable sized, but can // be used to reference the world ent server_state_t state; // some actions are only valid during load sizebuf_t datagram; byte datagram_buf[MAX_DATAGRAM]; sizebuf_t reliable_datagram; // copied to all clients at end of frame byte reliable_datagram_buf[MAX_DATAGRAM]; sizebuf_t signon; byte signon_buf[8192]; } server_t; #define NUM_PING_TIMES 16 #define NUM_SPAWN_PARMS 16 typedef struct client_s { bool active; // false = client is free bool spawned; // false = don't send datagrams bool dropasap; // has been told to go to another level bool privileged; // can execute any host command bool sendsignon; // only valid before spawned double last_message; // reliable messages must be sent // periodically struct qsocket_s *netconnection; // communications handle usercmd_t cmd; // movement vec3_t wishdir; // intended motion calced from cmd sizebuf_t message; // can be added to at any time, // copied and clear once per frame byte msgbuf[MAX_MSGLEN]; edict_t *edict; // EDICT_NUM(clientnum+1) char name[32]; // for printing to other people int colors; float ping_times[NUM_PING_TIMES]; int num_pings; // ping_times[num_pings%NUM_PING_TIMES] // spawn parms are carried from level to level float spawn_parms[NUM_SPAWN_PARMS]; // client known data for deltas int old_frags; } client_t; //============================================================================= // edict->movetype values #define MOVETYPE_NONE 0 // never moves #define MOVETYPE_ANGLENOCLIP 1 #define MOVETYPE_ANGLECLIP 2 #define MOVETYPE_WALK 3 // gravity #define MOVETYPE_STEP 4 // gravity, special edge handling #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 // extra size to monsters #define MOVETYPE_BOUNCE 10 #ifdef QUAKE2 #define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity #define MOVETYPE_FOLLOW 12 // track movement of aiment #endif // 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 // edict->deadflag values #define DEAD_NO 0 #define DEAD_DYING 1 #define DEAD_DEAD 2 #define DAMAGE_NO 0 #define DAMAGE_YES 1 #define DAMAGE_AIM 2 // edict->flags #define FL_FLY 1 #define FL_SWIM 2 //#define FL_GLIMPSE 4 #define FL_CONVEYOR 4 #define FL_CLIENT 8 #define FL_INWATER 16 #define FL_MONSTER 32 #define FL_GODMODE 64 #define FL_NOTARGET 128 #define FL_ITEM 256 #define FL_ONGROUND 512 #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 #ifdef QUAKE2 #define FL_FLASHLIGHT 8192 #define FL_ARCHIVE_OVERRIDE 1048576 #endif // entity effects #define EF_BRIGHTFIELD 1 #define EF_MUZZLEFLASH 2 #define EF_BRIGHTLIGHT 4 #define EF_DIMLIGHT 8 #define EF_NODRAW 16 #ifdef QUAKE2 #define EF_DARKLIGHT 16 #define EF_DARKFIELD 32 #define EF_LIGHT 64 #endif #define EF_NOMODELFLAGS 0x800000 // indicates the model's .effects should be ignored (allows overriding them) #define EF_ROCKET 0x1000000 // leave a trail #define EF_GRENADE 0x2000000 // leave a trail #define EF_GIB 0x4000000 // leave a trail #define EF_ROTATE 0x8000000 // rotate (bonus items) #define EF_TRACER 0x10000000 // green split trail #define EF_ZOMGIB 0x20000000 // small blood trail #define EF_TRACER2 0x40000000 // orange split trail + rotate #define EF_TRACER3 0x80000000 // purple trail #define SPAWNFLAG_NOT_EASY 256 #define SPAWNFLAG_NOT_MEDIUM 512 #define SPAWNFLAG_NOT_HARD 1024 #define SPAWNFLAG_NOT_DEATHMATCH 2048 #ifdef QUAKE2 // server flags #define SFL_EPISODE_1 1 #define SFL_EPISODE_2 2 #define SFL_EPISODE_3 4 #define SFL_EPISODE_4 8 #define SFL_NEW_UNIT 16 #define SFL_NEW_EPISODE 32 #define SFL_CROSS_TRIGGERS 65280 #endif //============================================================================ extern cvar_t teamplay; extern cvar_t skill; extern cvar_t deathmatch; extern cvar_t coop; extern cvar_t fraglimit; extern cvar_t timelimit; extern server_static_t svs; // persistant server info extern server_t sv; // local server extern client_t *host_client; extern jmp_buf host_abortserver; extern double host_time; extern edict_t *sv_player; extern cvar_t pq_fullpitch; // JPG 2.01 //=========================================================== void SV_Init (void); void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count); void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, float attenuation); void SV_DropClient (bool crash); void SV_SendClientMessages (void); void SV_ClearDatagram (void); int SV_ModelIndex (char *name); void SV_SetIdealPitch (void); void SV_AddUpdates (void); void SV_ClientThink (void); void SV_AddClientToServer (struct qsocket_s *ret); void SV_ClientPrintf (const char *fmt, ...); void SV_BroadcastPrintf (char *fmt, ...); void SV_Physics (void); bool SV_CheckBottom (edict_t *ent); bool SV_movestep (edict_t *ent, vec3_t move, bool relink); void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg); void SV_MoveToGoal (void); void SV_CheckForNewClients (void); void SV_RunClients (void); void SV_SaveSpawnparms (); #ifdef QUAKE2 void SV_SpawnServer (char *server, char *startspot); #else void SV_SpawnServer (char *server); #endif void Host_Version_f (void); ================================================ FILE: source/snd_dma.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_dma.c -- main control for any streaming sound output device #include "quakedef.h" CVAR (bgmvolume, 1, CVAR_ARCHIVE) CVAR (volume, 0.7, CVAR_ARCHIVE) CVAR (nosound, 0, CVAR_ROM) // Ch0wW: since it's not from the CMDLine... CVAR (precache, 1, CVAR_ROM) CVAR(loadas8bit, 0, CVAR_NONE) CVAR(bgmbuffer, 4096, CVAR_NONE) CVAR(ambient_level, 0.3, CVAR_NONE) CVAR(ambient_fade, 100, CVAR_NONE) CVAR(snd_noextraupdate, 0, CVAR_NONE) CVAR(snd_show, 0, CVAR_NONE) CVAR(_snd_mixahead, 0.1, CVAR_NONE) //---------------------------------------------- void S_Play(void); void S_PlayVol(void); void S_SoundList(void); void S_Update_(); void S_StopAllSounds(bool clear); void S_StopAllSoundsC(void); // ======================================================================= // Internal sound data & structures // ======================================================================= channel_t channels[MAX_CHANNELS]; int total_channels; int snd_blocked = 0; static bool snd_ambient = 1; bool snd_initialized = false; // pointer should go away volatile dma_t *shm = 0; volatile dma_t sn; vec3_t listener_origin; vec3_t listener_forward; vec3_t listener_right; vec3_t listener_up; vec_t sound_nominal_clip_dist=1000.0; int soundtime; // sample PAIRS int paintedtime; // sample PAIRS #define MAX_SFX 512 sfx_t *known_sfx; // hunk allocated [MAX_SFX] int num_sfx; sfx_t *ambient_sfx[NUM_AMBIENTS]; int desired_speed = 11025; int desired_bits = 16; int sound_started=0; // ==================================================================== // User-setable variables // ==================================================================== // // Fake dma is a synchronous faking of the DMA progress used for // isolating performance in the renderer. The fakedma_updates is // number of times S_Update() is called per second. // bool fakedma = false; int fakedma_updates = 15; void S_AmbientOff (void) { snd_ambient = false; } void S_AmbientOn (void) { snd_ambient = true; } void S_SoundInfo_f(void) { if (!sound_started || !shm) { Con_Printf ("sound system not started\n"); return; } Con_Printf("%5d stereo\n", shm->channels - 1); Con_Printf("%5d samples\n", shm->samples); Con_Printf("%5d samplepos\n", shm->samplepos); Con_Printf("%5d samplebits\n", shm->samplebits); Con_Printf("%5d submission_chunk\n", shm->submission_chunk); Con_Printf("%5d speed\n", shm->speed); Con_Printf("0x%x dma buffer\n", shm->buffer); Con_Printf("%5d total_channels\n", total_channels); } /* ================ S_Startup ================ */ void S_Startup (void) { int rc; if (!snd_initialized) return; if (!fakedma) { rc = SNDDMA_Init(); if (!rc) { #ifndef _WIN32 Con_Printf("S_Startup: SNDDMA_Init failed.\n"); #endif sound_started = 0; return; } } sound_started = 1; } /* ================ S_Init ================ */ void S_Init (void) { Con_Printf("\nSound Initialization\n"); if (COM_CheckParm("-nosound")) return; if (COM_CheckParm("-simsound")) fakedma = true; Cmd_AddCommand("play", S_Play); Cmd_AddCommand("playvol", S_PlayVol); Cmd_AddCommand("stopsound", S_StopAllSoundsC); Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); Cvar_RegisterVariable(&nosound); Cvar_RegisterVariable(&volume); Cvar_RegisterVariable(&precache); Cvar_RegisterVariable(&loadas8bit); Cvar_RegisterVariable(&bgmvolume); Cvar_RegisterVariable(&bgmbuffer); Cvar_RegisterVariable(&ambient_level); Cvar_RegisterVariable(&ambient_fade); Cvar_RegisterVariable(&snd_noextraupdate); Cvar_RegisterVariable(&snd_show); Cvar_RegisterVariable(&_snd_mixahead); if (host_parms.memsize < 0x800000) { Cvar_Set ("loadas8bit", "1"); Con_Printf ("loading all sounds as 8bit\n"); } snd_initialized = true; S_Startup (); SND_InitScaletable (); known_sfx = Hunk_AllocName (MAX_SFX*sizeof(sfx_t), "sfx_t"); num_sfx = 0; // create a piece of DMA memory if (fakedma) { shm = (void *) Hunk_AllocName(sizeof(*shm), "shm"); shm->splitbuffer = 0; shm->samplebits = 16; shm->speed = 22050; shm->channels = 2; shm->samples = 32768; shm->samplepos = 0; shm->soundalive = true; shm->gamealive = true; shm->submission_chunk = 1; shm->buffer = Hunk_AllocName(1<<16, "shmbuf"); } Con_Printf ("Sound sampling rate: %i\n", shm->speed); // provides a tick sound until washed clean // if (shm->buffer) // shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); S_StopAllSounds (true); } // ======================================================================= // Shutdown sound engine // ======================================================================= void S_Shutdown(void) { if (!sound_started) return; if (shm) shm->gamealive = 0; shm = 0; sound_started = 0; if (!fakedma) { SNDDMA_Shutdown(); } } // ======================================================================= // Load a sound // ======================================================================= /* ================== S_FindName ================== */ sfx_t *S_FindName (char *name) { int i; sfx_t *sfx; if (!name) Sys_Error ("S_FindName: NULL\n"); if (strlen(name) >= MAX_QPATH) Sys_Error ("Sound name too long: %s", name); // see if already loaded for (i=0 ; i < num_sfx ; i++) if (!strcmp(known_sfx[i].name, name)) { return &known_sfx[i]; } if (num_sfx == MAX_SFX) Sys_Error ("S_FindName: out of sfx_t"); sfx = &known_sfx[i]; strcpy (sfx->name, name); num_sfx++; return sfx; } /* ================== S_TouchSound ================== */ void S_TouchSound (char *name) { sfx_t *sfx; if (!sound_started) return; sfx = S_FindName (name); Cache_Check (&sfx->cache); } /* ================== S_PrecacheSound ================== */ sfx_t *S_PrecacheSound (char *name) { sfx_t *sfx; if (!sound_started || nosound.value) return NULL; sfx = S_FindName (name); // cache it in if (precache.value) S_LoadSound (sfx); return sfx; } //============================================================================= /* ================= SND_PickChannel ================= */ channel_t *SND_PickChannel(int entnum, int entchannel) { int ch_idx; int first_to_die; int life_left; // Check for replacement sound, or find the best one to replace first_to_die = -1; life_left = 0x7fffffff; for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++) { if (entchannel != 0 // channel 0 never overrides && channels[ch_idx].entnum == entnum && (channels[ch_idx].entchannel == entchannel || entchannel == -1) ) { // always override sound from same entity first_to_die = ch_idx; break; } // don't let monster sounds override player sounds if (channels[ch_idx].entnum == cl.viewentity && entnum != cl.viewentity && channels[ch_idx].sfx) continue; if (channels[ch_idx].end - paintedtime < life_left) { life_left = channels[ch_idx].end - paintedtime; first_to_die = ch_idx; } } if (first_to_die == -1) return NULL; if (channels[first_to_die].sfx) channels[first_to_die].sfx = NULL; return &channels[first_to_die]; } /* ================= SND_Spatialize ================= */ void SND_Spatialize(channel_t *ch) { vec_t dot; vec_t ldist, rdist, dist; vec_t lscale, rscale, scale; vec3_t source_vec; sfx_t *snd; // anything coming from the view entity will always be full volume if (ch->entnum == cl.viewentity) { ch->leftvol = ch->master_vol; ch->rightvol = ch->master_vol; return; } // calculate stereo seperation and distance attenuation snd = ch->sfx; VectorSubtract(ch->origin, listener_origin, source_vec); dist = VectorNormalize(source_vec) * ch->dist_mult; dot = DotProduct(listener_right, source_vec); if (shm->channels == 1) { rscale = 1.0; lscale = 1.0; } else { rscale = 1.0 + dot; lscale = 1.0 - dot; } // add in distance effect scale = (1.0 - dist) * rscale; ch->rightvol = (int) (ch->master_vol * scale); if (ch->rightvol < 0) ch->rightvol = 0; scale = (1.0 - dist) * lscale; ch->leftvol = (int) (ch->master_vol * scale); if (ch->leftvol < 0) ch->leftvol = 0; } // ======================================================================= // Start a sound effect // ======================================================================= void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation) { channel_t *target_chan, *check; sfxcache_t *sc; int vol; int ch_idx; int skip; if (!sound_started) return; if (!sfx) return; if (nosound.value) return; vol = fvol*255; // pick a channel to play on target_chan = SND_PickChannel(entnum, entchannel); if (!target_chan) return; // spatialize memset (target_chan, 0, sizeof(*target_chan)); VectorCopy(origin, target_chan->origin); target_chan->dist_mult = attenuation / sound_nominal_clip_dist; target_chan->master_vol = vol; target_chan->entnum = entnum; target_chan->entchannel = entchannel; SND_Spatialize(target_chan); if (!target_chan->leftvol && !target_chan->rightvol) return; // not audible at all // new channel sc = S_LoadSound (sfx); if (!sc) { target_chan->sfx = NULL; return; // couldn't load the sound's data } target_chan->sfx = sfx; target_chan->pos = 0.0; target_chan->end = paintedtime + sc->length; // if an identical sound has also been started this frame, offset the pos // a bit to keep it from just making the first one louder check = &channels[NUM_AMBIENTS]; for (ch_idx=NUM_AMBIENTS ; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS ; ch_idx++, check++) { if (check == target_chan) continue; if (check->sfx == sfx && !check->pos) { skip = rand () % (int)(0.1*shm->speed); if (skip >= target_chan->end) skip = target_chan->end - 1; target_chan->pos += skip; target_chan->end -= skip; break; } } } void S_StopSound(int entnum, int entchannel) { int i; for (i=0 ; ibuffer && !pDSBuf)) #else if (!sound_started || !shm || !shm->buffer) #endif return; if (shm->samplebits == 8) clear = 0x80; else clear = 0; #ifdef _WIN32 if (pDSBuf) { DWORD dwSize; DWORD *pData; int reps; HRESULT hresult; reps = 0; while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pData, &dwSize, NULL, NULL, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_Printf ("S_ClearBuffer: DS::Lock Sound Buffer Failed\n"); S_Shutdown (); return; } if (++reps > 10000) { Con_Printf ("S_ClearBuffer: DS: couldn't restore buffer\n"); S_Shutdown (); return; } } memset(pData, clear, shm->samples * shm->samplebits/8); pDSBuf->lpVtbl->Unlock(pDSBuf, pData, dwSize, NULL, 0); } else #endif { memset(shm->buffer, clear, shm->samples * shm->samplebits/8); } } /* ================= S_StaticSound ================= */ void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) { channel_t *ss; sfxcache_t *sc; if (!sfx) return; if (total_channels == MAX_CHANNELS) { Con_Printf ("total_channels == MAX_CHANNELS\n"); return; } ss = &channels[total_channels]; total_channels++; sc = S_LoadSound (sfx); if (!sc) return; if (sc->loopstart == -1) { Con_Printf ("Sound %s not looped\n", sfx->name); return; } ss->sfx = sfx; VectorCopy (origin, ss->origin); ss->master_vol = vol; ss->dist_mult = (attenuation/64) / sound_nominal_clip_dist; ss->end = paintedtime + sc->length; SND_Spatialize (ss); } //============================================================================= /* =================== S_UpdateAmbientSounds =================== */ void S_UpdateAmbientSounds (void) { mleaf_t *l; float vol; int ambient_channel; channel_t *chan; if (!snd_ambient) return; // calc ambient sound levels if (!cl.worldmodel) return; l = Mod_PointInLeaf (listener_origin, cl.worldmodel); if (!l || !ambient_level.value) { for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) channels[ambient_channel].sfx = NULL; return; } for (ambient_channel = 0 ; ambient_channel< NUM_AMBIENTS ; ambient_channel++) { chan = &channels[ambient_channel]; chan->sfx = ambient_sfx[ambient_channel]; vol = ambient_level.value * l->ambient_sound_level[ambient_channel]; if (vol < 8) vol = 0; // don't adjust volume too fast if (chan->master_vol < vol) { chan->master_vol += host_frametime * ambient_fade.value; if (chan->master_vol > vol) chan->master_vol = vol; } else if (chan->master_vol > vol) { chan->master_vol -= host_frametime * ambient_fade.value; if (chan->master_vol < vol) chan->master_vol = vol; } chan->leftvol = chan->rightvol = chan->master_vol; } } /* ============ S_Update Called once each time through the main loop ============ */ void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) { int i, j; int total; channel_t *ch; channel_t *combine; if (!sound_started || (snd_blocked > 0)) return; VectorCopy(origin, listener_origin); VectorCopy(forward, listener_forward); VectorCopy(right, listener_right); VectorCopy(up, listener_up); // update general area ambient sound sources S_UpdateAmbientSounds (); combine = NULL; // update spatialization for static and dynamic sounds ch = channels+NUM_AMBIENTS; for (i=NUM_AMBIENTS ; isfx) continue; SND_Spatialize(ch); // respatialize channel if (!ch->leftvol && !ch->rightvol) continue; // try to combine static sounds with a previous channel of the same // sound effect so we don't mix five torches every frame if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) { // see if it can just use the last one if (combine && combine->sfx == ch->sfx) { combine->leftvol += ch->leftvol; combine->rightvol += ch->rightvol; ch->leftvol = ch->rightvol = 0; continue; } // search for one combine = channels+MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; for (j=MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS ; jsfx == ch->sfx) break; if (j == total_channels) { combine = NULL; } else { if (combine != ch) { combine->leftvol += ch->leftvol; combine->rightvol += ch->rightvol; ch->leftvol = ch->rightvol = 0; } continue; } } } // // debugging output // if (snd_show.value) { total = 0; ch = channels; for (i=0 ; isfx && (ch->leftvol || ch->rightvol) ) { //Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); total++; } Con_Printf ("----(%i)----\n", total); } // mix some sound S_Update_(); } void GetSoundtime(void) { int samplepos; static int buffers; static int oldsamplepos; int fullsamples; fullsamples = shm->samples / shm->channels; // it is possible to miscount buffers if it has wrapped twice between // calls to S_Update. Oh well. #ifdef __sun__ soundtime = SNDDMA_GetSamples(); #else samplepos = SNDDMA_GetDMAPos(); if (samplepos < oldsamplepos) { buffers++; // buffer wrapped if (paintedtime > 0x40000000) { // time to chop things off to avoid 32 bit limits buffers = 0; paintedtime = fullsamples; S_StopAllSounds (true); } } oldsamplepos = samplepos; soundtime = buffers*fullsamples + samplepos/shm->channels; #endif } void S_ExtraUpdate (void) { #ifdef _WIN32 IN_Accumulate (); #endif if (snd_noextraupdate.value) return; // don't pollute timings S_Update_(); } void S_Update_(void) { unsigned endtime; int samps; if (!sound_started || (snd_blocked > 0)) return; // Updates DMA time GetSoundtime(); // check to make sure that we haven't overshot if (paintedtime < soundtime) { //Con_Printf ("S_Update_ : overflow\n"); paintedtime = soundtime; } // mix ahead of current position endtime = soundtime + _snd_mixahead.value * shm->speed; samps = shm->samples >> (shm->channels-1); if (endtime - soundtime > samps) endtime = soundtime + samps; #ifdef _WIN32 // if the buffer was lost or stopped, restore it and/or restart it { DWORD dwStatus; if (pDSBuf) { if (pDSBuf->lpVtbl->GetStatus (pDSBuf, &dwStatus) != DD_OK) Con_Printf ("Couldn't get sound buffer status\n"); if (dwStatus & DSBSTATUS_BUFFERLOST) pDSBuf->lpVtbl->Restore (pDSBuf); if (!(dwStatus & DSBSTATUS_PLAYING)) pDSBuf->lpVtbl->Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); } } #endif S_PaintChannels (endtime); SNDDMA_Submit (); } /* =============================================================================== console functions =============================================================================== */ void S_Play(void) { static int hash=345; int i; char name[256]; sfx_t *sfx; i = 1; while (icache); if (!sc) continue; size = sc->length*sc->width*(sc->stereo+1); total += size; if (sc->loopstart >= 0) Con_Printf ("L"); else Con_Printf (" "); Con_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name); } Con_Printf ("Total resident: %i\n", total); } void S_LocalSound (char *sound) { sfx_t *sfx; if (nosound.value) return; if (!sound_started) return; sfx = S_PrecacheSound (sound); if (!sfx) { Con_Printf ("S_LocalSound: can't cache %s\n", sound); return; } S_StartSound (cl.viewentity, -1, sfx, vec3_origin, 1, 1); } void S_ClearPrecache (void) { } void S_BeginPrecaching (void) { } void S_EndPrecaching (void) { } ================================================ FILE: source/snd_mem.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_mem.c: sound caching #include "quakedef.h" int cache_full_cycle; byte *S_Alloc (int size); /* ================ ResampleSfx ================ */ void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data) { int outcount; int srcsample; float stepscale; int i; int sample, samplefrac, fracstep; sfxcache_t *sc; sc = Cache_Check (&sfx->cache); if (!sc) return; stepscale = (float)inrate / shm->speed; // this is usually 0.5, 1, or 2 outcount = sc->length / stepscale; sc->length = outcount; if (sc->loopstart != -1) sc->loopstart = sc->loopstart / stepscale; sc->speed = shm->speed; if (loadas8bit.value) sc->width = 1; else sc->width = inwidth; sc->stereo = 0; // resample / decimate to the current source rate if (stepscale == 1 && inwidth == 1 && sc->width == 1) { // fast special case for (i=0 ; idata)[i] = (int)( (unsigned char)(data[i]) - 128); } else { // general case samplefrac = 0; fracstep = stepscale*256; for (i=0 ; i> 8; samplefrac += fracstep; if (inwidth == 2) sample = LittleShort ( ((short *)data)[srcsample] ); else sample = (int)( (unsigned char)(data[srcsample]) - 128) << 8; if (sc->width == 2) ((short *)sc->data)[i] = sample; else ((signed char *)sc->data)[i] = sample >> 8; } } } //============================================================================= /* ============== S_LoadSound ============== */ sfxcache_t *S_LoadSound (sfx_t *s) { char namebuffer[256]; byte *data; wavinfo_t info; int len; float stepscale; sfxcache_t *sc; byte stackbuf[1*1024]; // avoid dirtying the cache heap // see if still in memory sc = Cache_Check (&s->cache); if (sc) return sc; //Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); // load it in strcpy(namebuffer, "sound/"); strcat(namebuffer, s->name); // Con_Printf ("loading %s\n",namebuffer); data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf), NULL); if (!data) { Con_Printf ("Couldn't load %s\n", namebuffer); return NULL; } info = GetWavinfo (s->name, data, com_filesize); if (info.channels != 1) { Con_Printf ("%s is a stereo sample\n",s->name); return NULL; } stepscale = (float)info.rate / shm->speed; len = info.samples / stepscale; len = len * info.width * info.channels; sc = Cache_Alloc ( &s->cache, len + sizeof(sfxcache_t), s->name); if (!sc) return NULL; sc->length = info.samples; sc->loopstart = info.loopstart; sc->speed = info.rate; sc->width = info.width; sc->stereo = info.channels; ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); return sc; } /* =============================================================================== WAV loading =============================================================================== */ byte *data_p; byte *iff_end; byte *last_chunk; byte *iff_data; int iff_chunk_len; short GetLittleShort(void) { short val = 0; val = *data_p; val = val + (*(data_p+1)<<8); data_p += 2; return val; } int GetLittleLong(void) { int val = 0; val = *data_p; val = val + (*(data_p+1)<<8); val = val + (*(data_p+2)<<16); val = val + (*(data_p+3)<<24); data_p += 4; return val; } void FindNextChunk(char *name) { while (1) { data_p=last_chunk; if (data_p >= iff_end) { // didn't find the chunk data_p = NULL; return; } data_p += 4; iff_chunk_len = GetLittleLong(); if (iff_chunk_len < 0) { data_p = NULL; return; } // if (iff_chunk_len > 1024*1024) // Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); data_p -= 8; last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 ); if (!strncmp(data_p, name, 4)) return; } } void FindChunk(char *name) { last_chunk = iff_data; FindNextChunk (name); } void DumpChunks(void) { char str[5]; str[4] = 0; data_p=iff_data; do { memcpy (str, data_p, 4); data_p += 4; iff_chunk_len = GetLittleLong(); Con_Printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len); data_p += (iff_chunk_len + 1) & ~1; } while (data_p < iff_end); } /* ============ GetWavinfo ============ */ wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength) { wavinfo_t info; int i; int format; int samples; memset (&info, 0, sizeof(info)); if (!wav) return info; iff_data = wav; iff_end = wav + wavlength; // find "RIFF" chunk FindChunk("RIFF"); if (!(data_p && !strncmp(data_p+8, "WAVE", 4))) { Con_Printf("Missing RIFF/WAVE chunks\n"); return info; } // get "fmt " chunk iff_data = data_p + 12; // DumpChunks (); FindChunk("fmt "); if (!data_p) { Con_Printf("Missing fmt chunk\n"); return info; } data_p += 8; format = GetLittleShort(); if (format != 1) { Con_Printf("Microsoft PCM format only\n"); return info; } info.channels = GetLittleShort(); info.rate = GetLittleLong(); data_p += 4+2; info.width = GetLittleShort() / 8; // get cue chunk FindChunk("cue "); if (data_p) { data_p += 32; info.loopstart = GetLittleLong(); // Con_Printf("loopstart=%d\n", sfx->loopstart); // if the next chunk is a LIST chunk, look for a cue length marker FindNextChunk ("LIST"); if (data_p) { if (!strncmp (data_p + 28, "mark", 4)) { // this is not a proper parse, but it works with cooledit... data_p += 24; i = GetLittleLong (); // samples in loop info.samples = info.loopstart + i; // Con_Printf("looped length: %i\n", i); } } } else info.loopstart = -1; // find data chunk FindChunk("data"); if (!data_p) { Con_Printf("Missing data chunk\n"); return info; } data_p += 4; samples = GetLittleLong () / info.width; if (info.samples) { if (samples < info.samples) Sys_Error ("Sound %s has a bad loop length", name); } else info.samples = samples; info.dataofs = data_p - wav; return info; } ================================================ FILE: source/snd_mix.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // snd_mix.c -- portable code to mix sounds for snd_dma.c #include "quakedef.h" #define DWORD unsigned long #define PAINTBUFFER_SIZE 512 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; int snd_scaletable[32][256]; int *snd_p, snd_linear_count, snd_vol; short *snd_out; void Snd_WriteLinearBlastStereo16 (void); #if !id386 void Snd_WriteLinearBlastStereo16 (void) { int i; int val; for (i=0 ; i>8; if (val > 0x7fff) snd_out[i] = 0x7fff; else if (val < (short)0x8000) snd_out[i] = (short)0x8000; else snd_out[i] = val; val = (snd_p[i+1]*snd_vol)>>8; if (val > 0x7fff) snd_out[i+1] = 0x7fff; else if (val < (short)0x8000) snd_out[i+1] = (short)0x8000; else snd_out[i+1] = val; } } #endif void S_TransferStereo16 (int endtime) { int lpos; int lpaintedtime; DWORD *pbuf; #ifdef _WIN32 int reps; DWORD dwSize,dwSize2; DWORD *pbuf2; HRESULT hresult; #endif snd_vol = volume.value*256; snd_p = (int *) paintbuffer; lpaintedtime = paintedtime; #ifdef _WIN32 if (pDSBuf) { reps = 0; while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, &pbuf2, &dwSize2, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); S_Shutdown (); S_Startup (); return; } if (++reps > 10000) { Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); S_Shutdown (); S_Startup (); return; } } } else #endif { pbuf = (DWORD *)shm->buffer; } while (lpaintedtime < endtime) { // handle recirculating buffer issues lpos = lpaintedtime & ((shm->samples>>1)-1); snd_out = (short *) pbuf + (lpos<<1); snd_linear_count = (shm->samples>>1) - lpos; if (lpaintedtime + snd_linear_count > endtime) snd_linear_count = endtime - lpaintedtime; snd_linear_count <<= 1; // write a linear blast of samples Snd_WriteLinearBlastStereo16 (); snd_p += snd_linear_count; lpaintedtime += (snd_linear_count>>1); } #ifdef _WIN32 if (pDSBuf) pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); #endif } void S_TransferPaintBuffer(int endtime) { int out_idx; int count; int out_mask; int *p; int step; int val; int snd_vol; DWORD *pbuf; #ifdef _WIN32 int reps; DWORD dwSize,dwSize2; DWORD *pbuf2; HRESULT hresult; #endif if (shm->samplebits == 16 && shm->channels == 2) { S_TransferStereo16 (endtime); return; } p = (int *) paintbuffer; count = (endtime - paintedtime) * shm->channels; out_mask = shm->samples - 1; out_idx = paintedtime * shm->channels & out_mask; step = 3 - shm->channels; snd_vol = volume.value*256; #ifdef _WIN32 if (pDSBuf) { reps = 0; while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, &pbuf2,&dwSize2, 0)) != DS_OK) { if (hresult != DSERR_BUFFERLOST) { Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n"); S_Shutdown (); S_Startup (); return; } if (++reps > 10000) { Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n"); S_Shutdown (); S_Startup (); return; } } } else #endif { pbuf = (DWORD *)shm->buffer; } if (shm->samplebits == 16) { short *out = (short *) pbuf; while (count--) { val = (*p * snd_vol) >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (shm->samplebits == 8) { unsigned char *out = (unsigned char *) pbuf; while (count--) { val = (*p * snd_vol) >> 8; p+= step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val>>8) + 128; out_idx = (out_idx + 1) & out_mask; } } #ifdef _WIN32 if (pDSBuf) { DWORD dwNewpos, dwWrite; int il = paintedtime; int ir = endtime - paintedtime; ir += il; pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite); // if ((dwNewpos >= il) && (dwNewpos <= ir)) // Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos); } #endif } /* =============================================================================== CHANNEL MIXING =============================================================================== */ void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); void S_PaintChannels(int endtime) { int i; int end; channel_t *ch; sfxcache_t *sc; int ltime, count; while (paintedtime < endtime) { // if paintbuffer is smaller than DMA buffer end = endtime; if (endtime - paintedtime > PAINTBUFFER_SIZE) end = paintedtime + PAINTBUFFER_SIZE; // clear the paint buffer memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); // paint in the channels. ch = channels; for (i=0; isfx) continue; if (!ch->leftvol && !ch->rightvol) continue; sc = S_LoadSound (ch->sfx); if (!sc) continue; ltime = paintedtime; while (ltime < end) { // paint up to end if (ch->end < end) count = ch->end - ltime; else count = end - ltime; if (count > 0) { if (sc->width == 1) SND_PaintChannelFrom8(ch, sc, count); else SND_PaintChannelFrom16(ch, sc, count); ltime += count; } // if at end of loop, restart if (ltime >= ch->end) { if (sc->loopstart >= 0) { ch->pos = sc->loopstart; ch->end = ltime + sc->length - ch->pos; } else { // channel just stopped ch->sfx = NULL; break; } } } } // transfer out according to DMA format S_TransferPaintBuffer(end); paintedtime = end; } } void SND_InitScaletable (void) { int i, j; for (i=0 ; i<32 ; i++) for (j=0 ; j<256 ; j++) snd_scaletable[i][j] = ((signed char)j) * i * 8; } #if !id386 void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) { int data; int *lscale, *rscale; unsigned char *sfx; int i; if (ch->leftvol > 255) ch->leftvol = 255; if (ch->rightvol > 255) ch->rightvol = 255; lscale = snd_scaletable[ch->leftvol >> 3]; rscale = snd_scaletable[ch->rightvol >> 3]; sfx = (signed char *)sc->data + ch->pos; for (i=0 ; ipos += count; } #endif // !id386 void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) { int data; int left, right; int leftvol, rightvol; signed short *sfx; int i; leftvol = ch->leftvol; rightvol = ch->rightvol; sfx = (signed short *)sc->data + ch->pos; for (i=0 ; i> 8; right = (data * rightvol) >> 8; paintbuffer[i].left += left; paintbuffer[i].right += right; } ch->pos += count; } ================================================ FILE: source/snd_psp2.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "quakedef.h" #define u64 uint64_t #define u8 uint8_t #define SAMPLE_RATE (48000) #define AUDIOSIZE (16384) u8 *audiobuffer; SceRtcTick initial_tick; int snd_inited; int chn = -1; int stop_audio = false; //int update = false; float tickRate; static int audio_thread(int args, void *argp) { chn = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, AUDIOSIZE / 2, SAMPLE_RATE, SCE_AUDIO_OUT_MODE_MONO); sceAudioOutSetConfig(chn, -1, -1, -1); int vol[] = {32767, 32767}; sceAudioOutSetVolume(chn, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vol); while (!stop_audio) { sceAudioOutOutput(chn, audiobuffer); } sceAudioOutReleasePort(chn); free(audiobuffer); sceKernelExitDeleteThread(0); return 0; } bool SNDDMA_Init(void) { audiobuffer = malloc(AUDIOSIZE); memset(audiobuffer, 0, AUDIOSIZE); /* Fill the audio DMA information block */ shm = &sn; shm->splitbuffer = 0; shm->samplebits = 16; shm->speed = SAMPLE_RATE; shm->channels = 1; shm->samples = AUDIOSIZE / (shm->samplebits / 8); shm->samplepos = 0; shm->submission_chunk = 1; shm->buffer = audiobuffer; tickRate = 1.0f / sceRtcGetTickResolution(); SceUID audiothread = sceKernelCreateThread("Audio Thread", (void*)&audio_thread, 0x10000100, 0x10000, 0, 0, NULL); int res = sceKernelStartThread(audiothread, sizeof(audiothread), &audiothread); if (res != 0){ Sys_Error("Failed to init audio thread (0x%x)", res); return 0; } sceRtcGetCurrentTick(&initial_tick); snd_initialized = 1; return 1; } int SNDDMA_GetDMAPos(void) { if (!snd_initialized) return 0; SceRtcTick tick; sceRtcGetCurrentTick(&tick); const unsigned int deltaTick = tick.tick - initial_tick.tick; const float deltaSecond = deltaTick * tickRate; u64 samplepos = deltaSecond * SAMPLE_RATE; shm->samplepos = samplepos; return samplepos; } void SNDDMA_Shutdown(void) { if(snd_initialized){ stop_audio = true; chn = -1; } } /* ============== SNDDMA_Submit Send sound to device if buffer isn't really the dma buffer =============== */ void SNDDMA_Submit(void) { //if(snd_initialized) //update = true; } ================================================ FILE: source/sound.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sound.h -- client sound i/o functions #ifndef __SOUND__ #define __SOUND__ #define DEFAULT_SOUND_PACKET_VOLUME 255 #define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 // !!! if this is changed, it much be changed in asm_i386.h too !!! typedef struct { int left; int right; } portable_samplepair_t; typedef struct sfx_s { char name[MAX_QPATH]; cache_user_t cache; } sfx_t; // !!! if this is changed, it much be changed in asm_i386.h too !!! typedef struct { int length; int loopstart; int speed; int width; int stereo; byte data[1]; // variable sized } sfxcache_t; typedef struct { bool gamealive; bool soundalive; bool splitbuffer; int channels; int samples; // mono samples in buffer int submission_chunk; // don't mix less than this # int samplepos; // in mono samples int samplebits; int speed; unsigned char *buffer; } dma_t; // !!! if this is changed, it much be changed in asm_i386.h too !!! typedef struct { sfx_t *sfx; // sfx number int leftvol; // 0-255 volume int rightvol; // 0-255 volume int end; // end time in global paintsamples int pos; // sample position in sfx int looping; // where to loop, -1 = no looping int entnum; // to allow overriding a specific sound int entchannel; // vec3_t origin; // origin of sound effect vec_t dist_mult; // distance multiplier (attenuation/clipK) int master_vol; // 0-255 master volume } channel_t; typedef struct { int rate; int width; int channels; int loopstart; int samples; int dataofs; // chunk starts this many bytes from file start } wavinfo_t; void S_Init (void); void S_Startup (void); void S_Shutdown (void); void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, float attenuation); void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); void S_StopSound (int entnum, int entchannel); void S_StopAllSounds(bool clear); void S_ClearBuffer (void); void S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up); void S_ExtraUpdate (void); sfx_t *S_PrecacheSound (char *sample); void S_TouchSound (char *sample); void S_ClearPrecache (void); void S_BeginPrecaching (void); void S_EndPrecaching (void); void S_PaintChannels(int endtime); void S_InitPaintChannels (void); // picks a channel based on priorities, empty slots, number of channels channel_t *SND_PickChannel(int entnum, int entchannel); // spatializes a channel void SND_Spatialize(channel_t *ch); // initializes cycling through a DMA buffer and returns information on it bool SNDDMA_Init(void); // gets the current DMA position int SNDDMA_GetDMAPos(void); // shutdown the DMA xfer. void SNDDMA_Shutdown(void); // ==================================================================== // User-setable variables // ==================================================================== #define MAX_CHANNELS 128 #define MAX_DYNAMIC_CHANNELS 8 extern channel_t channels[MAX_CHANNELS]; // 0 to MAX_DYNAMIC_CHANNELS-1 = normal entity sounds // MAX_DYNAMIC_CHANNELS to MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS -1 = water, etc // MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS to total_channels = static sounds extern int total_channels; // // Fake dma is a synchronous faking of the DMA progress used for // isolating performance in the renderer. The fakedma_updates is // number of times S_Update() is called per second. // extern bool fakedma; extern int fakedma_updates; extern int paintedtime; extern vec3_t listener_origin; extern vec3_t listener_forward; extern vec3_t listener_right; extern vec3_t listener_up; extern volatile dma_t *shm; extern volatile dma_t sn; extern vec_t sound_nominal_clip_dist; extern cvar_t loadas8bit; extern cvar_t bgmvolume; extern cvar_t volume; extern bool snd_initialized; extern int snd_blocked; void S_LocalSound (char *s); sfxcache_t *S_LoadSound (sfx_t *s); wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength); void SND_InitScaletable (void); void SNDDMA_Submit(void); void S_AmbientOff (void); void S_AmbientOn (void); #endif ================================================ FILE: source/spritegn.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // // spritegn.h: header file for sprite generation program // // ********************************************************** // * This file must be identical in the spritegen directory * // * and in the Quake directory, because it's used to * // * pass data from one to the other via .spr files. * // ********************************************************** //------------------------------------------------------- // This program generates .spr sprite package files. // The format of the files is as follows: // // dsprite_t file header structure // // // dspriteframe_t frame header structure // sprite bitmap // // dspriteframe_t frame header structure // sprite bitmap // //------------------------------------------------------- #ifdef INCLUDELIBS #include #include #include #include #include "cmdlib.h" #include "scriplib.h" #include "dictlib.h" #include "trilib.h" #include "lbmlib.h" #include "mathlib.h" #endif #define SPRITE_VERSION 1 // must match definition in modelgen.h #ifndef SYNCTYPE_T #define SYNCTYPE_T typedef enum {ST_SYNC=0, ST_RAND } synctype_t; #endif // TODO: shorten these? typedef struct { int ident; int version; int type; float boundingradius; int width; int height; int numframes; float beamlength; synctype_t synctype; } dsprite_t; #define SPR_VP_PARALLEL_UPRIGHT 0 #define SPR_FACING_UPRIGHT 1 #define SPR_VP_PARALLEL 2 #define SPR_ORIENTED 3 #define SPR_VP_PARALLEL_ORIENTED 4 typedef struct { int origin[2]; int width; int height; } dspriteframe_t; typedef struct { int numframes; } dspritegroup_t; typedef struct { float interval; } dspriteinterval_t; typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; typedef struct { int type; } dspriteframetype_t; #define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') // little-endian "IDSP" ================================================ FILE: source/sv_main.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_main.c -- server main program #include "quakedef.h" server_t sv; server_static_t svs; cvar_t sv_progs = {"sv_progs", "progs.dat"}; #define MODSTRLEN (sizeof("*" stringify(MAX_MODELS)) / sizeof(char)) char localmodels[MAX_MODELS][MODSTRLEN]; // inline model names for precache //============================================================================ /* =============== SV_Init =============== */ void SV_Init (void) { int i; extern cvar_t sv_maxvelocity; extern cvar_t sv_gravity; extern cvar_t sv_nostep; extern cvar_t sv_friction; extern cvar_t sv_edgefriction; extern cvar_t sv_stopspeed; extern cvar_t sv_maxspeed; extern cvar_t sv_accelerate; extern cvar_t sv_idealpitchscale; extern cvar_t sv_aim; Cvar_RegisterVariable (&sv_maxvelocity); Cvar_RegisterVariable (&sv_gravity); Cvar_RegisterVariable (&sv_friction); Cvar_RegisterVariable (&sv_edgefriction); Cvar_RegisterVariable (&sv_stopspeed); Cvar_RegisterVariable (&sv_maxspeed); Cvar_RegisterVariable (&sv_accelerate); Cvar_RegisterVariable (&sv_idealpitchscale); Cvar_RegisterVariable (&sv_aim); Cvar_RegisterVariable (&sv_nostep); Cvar_RegisterVariable (&sv_progs); Cvar_RegisterVariable(&pq_fullpitch); // JPG 2.01 for (i=0 ; i MAX_DATAGRAM-16) return; MSG_WriteByte (&sv.datagram, svc_particle); MSG_WriteCoord (&sv.datagram, org[0]); MSG_WriteCoord (&sv.datagram, org[1]); MSG_WriteCoord (&sv.datagram, org[2]); for (i=0 ; i<3 ; i++) { v = dir[i]*16; if (v > 127) v = 127; else if (v < -128) v = -128; MSG_WriteChar (&sv.datagram, v); } MSG_WriteByte (&sv.datagram, count); MSG_WriteByte (&sv.datagram, color); } /* ================== SV_StartSound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. (max 4 attenuation) ================== */ void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, float attenuation) { int sound_num; int field_mask; int i; int ent; if (volume < 0 || volume > 255) Sys_Error ("SV_StartSound: volume = %i", volume); if (attenuation < 0 || attenuation > 4) Sys_Error ("SV_StartSound: attenuation = %f", attenuation); if (channel < 0 || channel > 7) Sys_Error ("SV_StartSound: channel = %i", channel); if (sv.datagram.cursize > MAX_DATAGRAM-16) return; // find precache number for sound for (sound_num=1 ; sound_numv.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i])); } /* ============================================================================== CLIENT SPAWNING ============================================================================== */ /* ================ SV_SendServerinfo Sends the first message from the server to a connected client. This will be sent on the initial connection and upon each server load. ================ */ void SV_SendServerinfo (client_t *client) { char **s; char* message = Sys_BigStackAlloc(2048, "SV_SendServerinfo"); if (svs.maxclients > 1) { MSG_WriteByte(&client->message, svc_print); snprintf(message, sizeof(message), "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n" "\n \01\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\02\03"); MSG_WriteString(&client->message, message); MSG_WriteByte(&client->message, svc_print); snprintf(message, sizeof(message), "\02\n \04ProQuake Server Version %4.2f\06" "\n \07\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\10\11", VERSION_PROQUAKE); MSG_WriteString(&client->message, message); } MSG_WriteByte (&client->message, svc_serverinfo); MSG_WriteLong (&client->message, PROTOCOL_NETQUAKE); MSG_WriteByte (&client->message, svs.maxclients); if (!coop.value && deathmatch.value) MSG_WriteByte (&client->message, GAME_DEATHMATCH); else MSG_WriteByte (&client->message, GAME_COOP); sprintf (message, pr_strings+sv.edicts->v.message); MSG_WriteString (&client->message,message); for (s = sv.model_precache+1 ; *s ; s++) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); for (s = sv.sound_precache+1 ; *s ; s++) MSG_WriteString (&client->message, *s); MSG_WriteByte (&client->message, 0); // send music MSG_WriteByte (&client->message, svc_cdtrack); MSG_WriteByte (&client->message, sv.edicts->v.sounds); MSG_WriteByte (&client->message, sv.edicts->v.sounds); // set view MSG_WriteByte (&client->message, svc_setview); MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); if (!pq_fullpitch.value && client->netconnection->proquake_connection != MOD_QSMACK) { // Ch0wW: Re-add it MSG_WriteByte(&client->message, svc_stufftext); MSG_WriteString(&client->message, "pq_fullpitch 0; cl_fullpitch 0\n"); } MSG_WriteByte (&client->message, svc_signonnum); MSG_WriteByte (&client->message, 1); client->sendsignon = true; client->spawned = false; // need prespawn, spawn, etc Sys_BigStackFree(2048, "SV_SendServerinfo"); } /* ================ SV_ConnectClient Initializes a client_t for a new net connection. This will only be called once for a player each game, not once for each level change. ================ */ void SV_ConnectClient (int clientnum) { edict_t *ent; client_t *client; int edictnum; struct qsocket_s *netconnection; int i; float spawn_parms[NUM_SPAWN_PARMS]; client = svs.clients + clientnum; if (client->netconnection->proquake_connection == MOD_PROQUAKE) Con_DPrintf("ProQuake Client %s connected\n", client->netconnection->address); else Con_DPrintf ("Client %s connected\n", client->netconnection->address); edictnum = clientnum+1; ent = EDICT_NUM(edictnum); // set up the client_t netconnection = client->netconnection; if (sv.loadgame) memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); memset (client, 0, sizeof(*client)); client->netconnection = netconnection; strcpy (client->name, "unconnected"); client->active = true; client->spawned = false; client->edict = ent; client->message.data = client->msgbuf; client->message.maxsize = sizeof(client->msgbuf); client->message.allowoverflow = true; // we can catch it #ifdef IDGODS client->privileged = IsID(&client->netconnection->addr); #else client->privileged = false; #endif if (sv.loadgame) memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms)); else { // call the progs to get default spawn parms for the new client PR_ExecuteProgram (pr_global_struct->SetNewParms); for (i=0 ; ispawn_parms[i] = (&pr_global_struct->parm1)[i]; } SV_SendServerinfo (client); } /* =================== SV_CheckForNewClients =================== */ void SV_CheckForNewClients (void) { struct qsocket_s *ret; int i; // // check for new connections // while (1) { ret = NET_CheckNewConnections (); if (!ret) break; // // init a new client structure // for (i=0 ; icontents < 0) { if (node->contents != CONTENTS_SOLID) { pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); for (i=0 ; iplane; d = DotProduct (org, plane->normal) - plane->dist; if (d > 8) node = node->children[0]; else if (d < -8) node = node->children[1]; else { // go down both SV_AddToFatPVS (org, node->children[0]); node = node->children[1]; } } } /* ============= SV_FatPVS Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the given point. ============= */ byte *SV_FatPVS (vec3_t org) { fatbytes = (sv.worldmodel->numleafs+31)>>3; memset (fatpvs, 0, fatbytes); SV_AddToFatPVS (org, sv.worldmodel->nodes); return fatpvs; } //============================================================================= /* ============= SV_WriteEntitiesToClient ============= */ void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) { int e, i; int bits; byte *pvs; vec3_t org; float miss, alpha; edict_t *ent; eval_t *val; int clentnum; // find the client's PVS VectorAdd (clent->v.origin, clent->v.view_ofs, org); pvs = SV_FatPVS (org); clentnum = EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes // send over all entities (excpet the client) that touch the pvs ent = NEXT_EDICT(sv.edicts); for (e=1 ; ev.effects == EF_NODRAW) continue; // ignore if not touching a PV leaf if (ent != clent) // clent is ALWAYS sent { // ignore ents without visible models if (!ent->v.modelindex || !pr_strings[ent->v.model]) continue; if ((val = GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)) && val->edict && val->edict != clentnum) continue; if ((val = GETEDICTFIELDVALUE(ent, eval_nodrawtoclient)) && val->edict == clentnum) continue; for (i=0 ; i < ent->num_leafs ; i++) if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) break; if (i == ent->num_leafs) continue; // not visible } if (msg->cursize + 24 > msg->maxsize) { Con_Printf ("packet overflow\n"); return; } // send an update bits = 0; for (i=0 ; i<3 ; i++) { miss = ent->v.origin[i] - ent->baseline.origin[i]; if ( miss < -0.1 || miss > 0.1 ) bits |= U_ORIGIN1<v.angles[0] != ent->baseline.angles[0] ) bits |= U_ANGLE1; if ( ent->v.angles[1] != ent->baseline.angles[1] ) bits |= U_ANGLE2; if ( ent->v.angles[2] != ent->baseline.angles[2] ) bits |= U_ANGLE3; if (ent->v.movetype == MOVETYPE_STEP) bits |= U_NOLERP; // don't mess up the step animation if (ent->baseline.colormap != ent->v.colormap) bits |= U_COLORMAP; if (ent->baseline.skin != ent->v.skin) bits |= U_SKIN; if (ent->baseline.frame != ent->v.frame) bits |= U_FRAME; if ((val = GetEdictFieldValue(ent, "modelflags"))) { i= (unsigned int)ent->v.effects; i |= ((unsigned int)val->_float & 0xff) << 24; ent->v.effects = i; } if (ent->baseline.effects != ent->v.effects) bits |= U_EFFECTS; if (ent->baseline.modelindex != ent->v.modelindex) bits |= U_MODEL; if (val = GETEDICTFIELDVALUE(ent, eval_alpha)) { if ((val->_float < 1.0f) && (val->_float > 0.0f)) { alpha = val->_float; bits |= U_ALPHA; } } if (val = GETEDICTFIELDVALUE(ent, eval_renderamt)) { if ((val->_float < 255.0f) && (val->_float > 0.0f)) { alpha = val->_float; bits |= U_RENDERAMT; } } if (e >= 256) bits |= U_LONGENTITY; if (bits >= 0x100) bits |= U_MOREBITS; if (bits >= 0x10000) bits |= U_EXTEND1; if (bits >= 0x1000000) bits |= U_EXTEND2; // // write the message // MSG_WriteByte (msg,bits | U_SIGNAL); if (bits & U_MOREBITS) MSG_WriteByte (msg, bits>>8); if (bits & U_EXTEND1) MSG_WriteByte (msg, bits>>16); if (bits & U_EXTEND2) MSG_WriteByte (msg, bits>>24); if (bits & U_LONGENTITY) MSG_WriteShort (msg,e); else MSG_WriteByte (msg,e); if (bits & U_MODEL) MSG_WriteByte (msg, ent->v.modelindex); if (bits & U_FRAME) MSG_WriteByte (msg, ent->v.frame); if (bits & U_COLORMAP) MSG_WriteByte (msg, ent->v.colormap); if (bits & U_SKIN) MSG_WriteByte (msg, ent->v.skin); if (bits & U_EFFECTS) MSG_WriteByte (msg, ent->v.effects); if (bits & U_ORIGIN1) MSG_WriteCoord (msg, ent->v.origin[0]); if (bits & U_ANGLE1) MSG_WriteAngle(msg, ent->v.angles[0]); if (bits & U_ORIGIN2) MSG_WriteCoord (msg, ent->v.origin[1]); if (bits & U_ANGLE2) MSG_WriteAngle(msg, ent->v.angles[1]); if (bits & U_ORIGIN3) MSG_WriteCoord (msg, ent->v.origin[2]); if (bits & U_ANGLE3) MSG_WriteAngle(msg, ent->v.angles[2]); if (bits & U_ALPHA) MSG_WriteByte(msg, (int)(alpha * 255)); if (bits & U_RENDERAMT) MSG_WriteByte(msg, (int)(alpha)); } } /* ============= SV_CleanupEnts ============= */ void SV_CleanupEnts (void) { int e; edict_t *ent; ent = NEXT_EDICT(sv.edicts); for (e=1 ; ev.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; } } /* ================== SV_WriteClientdataToMessage ================== */ void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg) { int bits; int i; edict_t *other; int items; #ifndef QUAKE2 eval_t *val; #endif // // send a damage message // if (ent->v.dmg_take || ent->v.dmg_save) { other = PROG_TO_EDICT(ent->v.dmg_inflictor); MSG_WriteByte (msg, svc_damage); MSG_WriteByte (msg, ent->v.dmg_save); MSG_WriteByte (msg, ent->v.dmg_take); for (i=0 ; i<3 ; i++) MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); ent->v.dmg_take = 0; ent->v.dmg_save = 0; } // // send the current viewpos offset from the view entity // SV_SetIdealPitch (); // how much to look up / down ideally // a fixangle might get lost in a dropped packet. Oh well. if ( ent->v.fixangle ) { MSG_WriteByte (msg, svc_setangle); for (i=0 ; i < 3 ; i++) MSG_WriteAngle (msg, ent->v.angles[i] ); ent->v.fixangle = 0; } bits = 0; if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT; if (ent->v.idealpitch) bits |= SU_IDEALPITCH; // stuff the sigil bits into the high bits of items for sbar, or else // mix in items2 #ifdef QUAKE2 items = (int)ent->v.items | ((int)ent->v.items2 << 23); #else val = GetEdictFieldValue(ent, "items2"); if (val) items = (int)ent->v.items | ((int)val->_float << 23); else items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); #endif bits |= SU_ITEMS; if ( (int)ent->v.flags & FL_ONGROUND) bits |= SU_ONGROUND; if ( ent->v.waterlevel >= 2) bits |= SU_INWATER; for (i=0 ; i<3 ; i++) { if (ent->v.punchangle[i]) bits |= (SU_PUNCH1<v.velocity[i]) bits |= (SU_VELOCITY1<v.weaponframe) bits |= SU_WEAPONFRAME; if (ent->v.armorvalue) bits |= SU_ARMOR; // if (ent->v.weapon) bits |= SU_WEAPON; // send the data MSG_WriteByte (msg, svc_clientdata); MSG_WriteShort (msg, bits); if (bits & SU_VIEWHEIGHT) MSG_WriteChar (msg, ent->v.view_ofs[2]); if (bits & SU_IDEALPITCH) MSG_WriteChar (msg, ent->v.idealpitch); for (i=0 ; i<3 ; i++) { if (bits & (SU_PUNCH1<v.punchangle[i]); if (bits & (SU_VELOCITY1<v.velocity[i]/16); } // [always sent] if (bits & SU_ITEMS) MSG_WriteLong (msg, items); if (bits & SU_WEAPONFRAME) MSG_WriteByte (msg, ent->v.weaponframe); if (bits & SU_ARMOR) MSG_WriteByte (msg, ent->v.armorvalue); if (bits & SU_WEAPON) MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel)); MSG_WriteShort (msg, ent->v.health); MSG_WriteByte (msg, ent->v.currentammo); MSG_WriteByte (msg, ent->v.ammo_shells); MSG_WriteByte (msg, ent->v.ammo_nails); MSG_WriteByte (msg, ent->v.ammo_rockets); MSG_WriteByte (msg, ent->v.ammo_cells); if (standard_quake) { MSG_WriteByte (msg, ent->v.weapon); } else { for(i=0;i<32;i++) { if ( ((int)ent->v.weapon) & (1<edict, &msg); SV_WriteEntitiesToClient (client->edict, &msg); // copy the server datagram if there is space if (msg.cursize + sv.datagram.cursize < msg.maxsize) SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize); // send the datagram if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) { SV_DropClient (true);// if the message couldn't send, kick off return false; } return true; } /* ======================= SV_UpdateToReliableMessages ======================= */ void SV_UpdateToReliableMessages (void) { int i, j; client_t *client; // check for changes to be sent over the reliable streams for (i=0, host_client = svs.clients ; iold_frags != host_client->edict->v.frags) { for (j=0, client = svs.clients ; jactive) continue; MSG_WriteByte (&client->message, svc_updatefrags); MSG_WriteByte (&client->message, i); MSG_WriteShort (&client->message, host_client->edict->v.frags); } host_client->old_frags = host_client->edict->v.frags; } } for (j=0, client = svs.clients ; jactive) continue; SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); } SZ_Clear (&sv.reliable_datagram); } /* ======================= SV_SendNop Send a nop message without trashing or sending the accumulated client message buffer ======================= */ void SV_SendNop (client_t *client) { sizebuf_t msg; byte buf[4]; msg.data = buf; msg.maxsize = sizeof(buf); msg.cursize = 0; MSG_WriteChar (&msg, svc_nop); if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) SV_DropClient (true); // if the message couldn't send, kick off client->last_message = realtime; } /* ======================= SV_SendClientMessages ======================= */ void SV_SendClientMessages (void) { int i; // update frags, names, etc SV_UpdateToReliableMessages (); // build individual updates for (i=0, host_client = svs.clients ; iactive) continue; if (host_client->spawned) { if (!SV_SendClientDatagram (host_client)) continue; } else { // the player isn't totally in the game yet // send small keepalive messages if too much time has passed // send a full message when the next signon stage has been requested // some other message data (name changes, etc) may accumulate // between signon stages if (!host_client->sendsignon) { if (realtime - host_client->last_message > 5) SV_SendNop (host_client); continue; // don't send out non-signon messages } } // ProQuake - NAT fix for servers. if (host_client->netconnection->net_wait) continue; // check for an overflowed message. Should only happen // on a very fucked up connection that backs up a lot, then // changes level if (host_client->message.overflowed) { SV_DropClient (true); host_client->message.overflowed = false; continue; } if (host_client->message.cursize || host_client->dropasap) { if (!NET_CanSendMessage (host_client->netconnection)) { // I_Printf ("can't write\n"); continue; } if (host_client->dropasap) SV_DropClient (false); // went to another level else { if (NET_SendMessage (host_client->netconnection , &host_client->message) == -1) SV_DropClient (true); // if the message couldn't send, kick off SZ_Clear (&host_client->message); host_client->last_message = realtime; host_client->sendsignon = false; } } } // clear muzzle flashes SV_CleanupEnts (); } /* ============================================================================== SERVER SPAWNING ============================================================================== */ /* ================ SV_ModelIndex ================ */ int SV_ModelIndex (char *name) { int i; if (!name || !name[0]) return 0; for (i=0 ; ifree) continue; if (entnum > svs.maxclients && !svent->v.modelindex) continue; // // create entity baseline // VectorCopy (svent->v.origin, svent->baseline.origin); VectorCopy (svent->v.angles, svent->baseline.angles); svent->baseline.frame = svent->v.frame; svent->baseline.skin = svent->v.skin; if (entnum > 0 && entnum <= svs.maxclients) { svent->baseline.colormap = entnum; svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); } else { svent->baseline.colormap = 0; svent->baseline.modelindex = SV_ModelIndex(pr_strings + svent->v.model); } // // add to the message // MSG_WriteByte (&sv.signon,svc_spawnbaseline); MSG_WriteShort (&sv.signon,entnum); MSG_WriteByte (&sv.signon, svent->baseline.modelindex); MSG_WriteByte (&sv.signon, svent->baseline.frame); MSG_WriteByte (&sv.signon, svent->baseline.colormap); MSG_WriteByte (&sv.signon, svent->baseline.skin); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); } } } /* ================ SV_SendReconnect Tell all the clients that the server is changing levels ================ */ void SV_SendReconnect (void) { char data[128]; sizebuf_t msg; msg.data = data; msg.cursize = 0; msg.maxsize = sizeof(data); MSG_WriteChar (&msg, svc_stufftext); MSG_WriteString (&msg, "reconnect\n"); NET_SendToAll (&msg, 5); if (cls.state != ca_dedicated) #ifdef QUAKE2 Cbuf_InsertText ("reconnect\n"); #else Cmd_ExecuteString ("reconnect\n", src_command); #endif } /* ================ SV_SaveSpawnparms Grabs the current state of each client for saving across the transition to another level ================ */ void SV_SaveSpawnparms (void) { int i, j; svs.serverflags = pr_global_struct->serverflags; for (i=0, host_client = svs.clients ; iactive) continue; // call the progs to get default spawn parms for the new client pr_global_struct->self = EDICT_TO_PROG(host_client->edict); PR_ExecuteProgram (pr_global_struct->SetChangeParms); for (j=0 ; jspawn_parms[j] = (&pr_global_struct->parm1)[j]; } } /* ================ SV_SpawnServer This is called at the start of each level ================ */ extern float scr_centertime_off; #ifdef QUAKE2 void SV_SpawnServer (char *server, char *startspot) #else void SV_SpawnServer (char *server) #endif { edict_t *ent; int i; // let's not have any servers with no name if (hostname.string[0] == 0) Cvar_Set ("hostname", "UNNAMED"); scr_centertime_off = 0; Con_DPrintf ("SpawnServer: %s\n",server); svs.changelevel_issued = false; // now safe to issue another // // tell all connected clients that we are going to a new level // if (sv.active) { SV_SendReconnect (); } // // make cvars consistant // if (coop.value) Cvar_SetValue ("deathmatch", 0); current_skill = (int)(skill.value + 0.5); if (current_skill < 0) current_skill = 0; if (current_skill > 3) current_skill = 3; Cvar_SetValue ("skill", (float)current_skill); // // set up the new server // Host_ClearMemory (); memset (&sv, 0, sizeof(sv)); strcpy (sv.name, server); #ifdef QUAKE2 if (startspot) strcpy(sv.startspot, startspot); #endif // load progs to get entity field count PR_LoadProgs (sv_progs.string); // allocate server memory sv.max_edicts = MAX_EDICTS; sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts"); sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.cursize = 0; sv.datagram.data = sv.datagram_buf; sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); sv.reliable_datagram.cursize = 0; sv.reliable_datagram.data = sv.reliable_datagram_buf; sv.signon.maxsize = sizeof(sv.signon_buf); sv.signon.cursize = 0; sv.signon.data = sv.signon_buf; // leave slots at start for clients only sv.num_edicts = svs.maxclients+1; for (i=0 ; inumsubmodels ; i++) { sv.model_precache[1+i] = localmodels[i]; sv.models[i+1] = Mod_ForName (localmodels[i], false); } // // load the rest of the entities // ent = EDICT_NUM(0); memset (&ent->v, 0, progs->entityfields * 4); ent->free = false; ent->v.model = sv.worldmodel->name - pr_strings; ent->v.modelindex = 1; // world model ent->v.solid = SOLID_BSP; ent->v.movetype = MOVETYPE_PUSH; if (coop.value) pr_global_struct->coop = coop.value; else pr_global_struct->deathmatch = deathmatch.value; pr_global_struct->mapname = sv.name - pr_strings; #ifdef QUAKE2 pr_global_struct->startspot = sv.startspot - pr_strings; #endif // serverflags are for cross level information (sigils) pr_global_struct->serverflags = svs.serverflags; ED_LoadFromFile (sv.worldmodel->entities); sv.active = true; // all setup is completed, any further precache statements are errors sv.state = ss_active; // run two frames to allow everything to settle host_frametime = 0.1; SV_Physics (); SV_Physics (); // create a baseline for more efficient communications SV_CreateBaseline (); // send serverinfo to all connected clients for (i=0,host_client = svs.clients ; iactive) SV_SendServerinfo (host_client); Con_DPrintf ("Server spawned.\n"); } ================================================ FILE: source/sv_move.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_move.c -- monster movement #include "quakedef.h" #define STEPSIZE 18 /* ============= SV_CheckBottom Returns false if any part of the bottom of the entity is off an edge that is not a staircase. ============= */ int c_yes, c_no; bool SV_CheckBottom (edict_t *ent) { vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint start[2] = mins[2] - 1; for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = x ? maxs[0] : mins[0]; start[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (start) != CONTENTS_SOLID) goto realcheck; } c_yes++; return true; // we got out easy realcheck: c_no++; // // check it for real... // start[2] = mins[2]; // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; stop[2] = start[2] - 2*STEPSIZE; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); if (trace.fraction == 1.0) return false; mid = bottom = trace.endpos[2]; // the corners must be within 16 of the midpoint for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, ent); if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) return false; } c_yes++; return true; } /* ============= SV_movestep Called by monster program code. The move will be adjusted for slopes and stairs, but if the move isn't possible, no move is done, false is returned, and pr_global_struct->trace_normal is set to the normal of the blocking wall ============= */ bool SV_movestep (edict_t *ent, vec3_t move, bool relink) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; edict_t *enemy; // try the move VectorCopy (ent->v.origin, oldorg); VectorAdd (ent->v.origin, move, neworg); // flying monsters don't step up if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) { // try one move with vertical motion, then one without for (i=0 ; i<2 ; i++) { VectorAdd (ent->v.origin, move, neworg); enemy = PROG_TO_EDICT(ent->v.enemy); if (i == 0 && enemy != sv.edicts) { dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; if (dz > 40) neworg[2] -= 8; if (dz < 30) neworg[2] += 8; } trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); if (trace.fraction == 1) { if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) return false; // swim monster left water VectorCopy (trace.endpos, ent->v.origin); if (relink) SV_LinkEdict (ent, true); return true; } if (enemy == sv.edicts) break; } return false; } // push down from a step height above the wished position neworg[2] += STEPSIZE; VectorCopy (neworg, end); end[2] -= STEPSIZE*2; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) return false; if (trace.startsolid) { neworg[2] -= STEPSIZE; trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid || trace.startsolid) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ( (int)ent->v.flags & FL_PARTIALGROUND ) { VectorAdd (ent->v.origin, move, ent->v.origin); if (relink) SV_LinkEdict (ent, true); ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // Con_Printf ("fall down\n"); return true; } return false; // walked off an edge } // check point traces down for dangling corners VectorCopy (trace.endpos, ent->v.origin); if (!SV_CheckBottom (ent)) { if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) SV_LinkEdict (ent, true); return true; } VectorCopy (oldorg, ent->v.origin); return false; } if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // Con_Printf ("back on ground\n"); ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; } ent->v.groundentity = EDICT_TO_PROG(trace.ent); // the move is ok if (relink) SV_LinkEdict (ent, true); return true; } //============================================================================ /* ====================== SV_StepDirection Turns to the movement direction, and walks the current distance if facing it. ====================== */ void PF_changeyaw (void); bool SV_StepDirection (edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; ent->v.ideal_yaw = yaw; PF_changeyaw(); yaw = yaw*M_PI*2 / 360; move[0] = cosf(yaw)*dist; move[1] = sinf(yaw)*dist; move[2] = 0; VectorCopy (ent->v.origin, oldorigin); if (SV_movestep (ent, move, false)) { delta = ent->v.angles[YAW] - ent->v.ideal_yaw; if (delta > 45 && delta < 315) { // not turned far enough, so don't take the step VectorCopy (oldorigin, ent->v.origin); } SV_LinkEdict (ent, true); return true; } SV_LinkEdict (ent, true); return false; } /* ====================== SV_FixCheckBottom ====================== */ void SV_FixCheckBottom (edict_t *ent) { // Con_Printf ("SV_FixCheckBottom\n"); ent->v.flags = (int)ent->v.flags | FL_PARTIALGROUND; } /* ================ SV_NewChaseDir ================ */ #define DI_NODIR -1 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist) { float deltax,deltay; float d[3]; float tdir, olddir, turnaround; olddir = anglemod( (int)(actor->v.ideal_yaw/45)*45 ); turnaround = anglemod(olddir - 180); deltax = enemy->v.origin[0] - actor->v.origin[0]; deltay = enemy->v.origin[1] - actor->v.origin[1]; if (deltax>10) d[1]= 0; else if (deltax<-10) d[1]= 180; else d[1]= DI_NODIR; if (deltay<-10) d[2]= 270; else if (deltay>10) d[2]= 90; else d[2]= DI_NODIR; // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { if (d[1] == 0) tdir = d[2] == 90 ? 45 : 315; else tdir = d[2] == 90 ? 135 : 215; if (tdir != turnaround && SV_StepDirection(actor, tdir, dist)) return; } // try other directions if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax)) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } if (d[1]!=DI_NODIR && d[1]!=turnaround && SV_StepDirection(actor, d[1], dist)) return; if (d[2]!=DI_NODIR && d[2]!=turnaround && SV_StepDirection(actor, d[2], dist)) return; /* there is no direct path to the player, so pick another direction */ if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist)) return; if (rand()&1) /*randomly determine direction of search*/ { for (tdir=0 ; tdir<=315 ; tdir += 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } else { for (tdir=315 ; tdir >=0 ; tdir -= 45) if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) ) return; } if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) ) return; actor->v.ideal_yaw = olddir; // can't move // if a bridge was pulled out from underneath a monster, it may not have // a valid standing position at all if (!SV_CheckBottom (actor)) SV_FixCheckBottom (actor); } /* ====================== SV_CloseEnough ====================== */ bool SV_CloseEnough (edict_t *ent, edict_t *goal, float dist) { int i; for (i=0 ; i<3 ; i++) { if (goal->v.absmin[i] > ent->v.absmax[i] + dist) return false; if (goal->v.absmax[i] < ent->v.absmin[i] - dist) return false; } return true; } /* ====================== SV_MoveToGoal ====================== */ void SV_MoveToGoal (void) { edict_t *ent, *goal; float dist; #ifdef QUAKE2 edict_t *enemy; #endif ent = PROG_TO_EDICT(pr_global_struct->self); goal = PROG_TO_EDICT(ent->v.goalentity); dist = G_FLOAT(OFS_PARM0); if ( !( (int)ent->v.flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) ) { G_FLOAT(OFS_RETURN) = 0; return; } // if the next step hits the enemy, return immediately #ifdef QUAKE2 enemy = PROG_TO_EDICT(ent->v.enemy); if (enemy != sv.edicts && SV_CloseEnough (ent, enemy, dist) ) #else if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) #endif return; // bump around... if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) { SV_NewChaseDir (ent, goal, dist); } } ================================================ FILE: source/sv_phys.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_phys.c #include "quakedef.h" CVAR (sv_friction, 4, CVAR_SERVERINFO) CVAR(sv_gravity, 800, CVAR_SERVERINFO) CVAR(sv_stopspeed, 100, CVAR_NONE) CVAR(sv_maxvelocity, 2000, CVAR_NONE) CVAR(sv_nostep, 0, CVAR_NONE) //---------------------------------------------- /* pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move. onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS corpses are SOLID_NOT and MOVETYPE_TOSS crates are SOLID_BBOX and MOVETYPE_TOSS walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY solid_edge items only clip against bsp models. */ #ifdef QUAKE2 static vec3_t vec_origin = {0.0, 0.0, 0.0}; #endif #define MOVE_EPSILON 0.01 void SV_Physics_Toss (edict_t *ent); /* ================ SV_CheckAllEnts ================ */ void SV_CheckAllEnts (void) { int e; edict_t *check; // see if any solid entities are inside the final position check = NEXT_EDICT(sv.edicts); for (e=1 ; efree) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE #ifdef QUAKE2 || check->v.movetype == MOVETYPE_FOLLOW #endif || check->v.movetype == MOVETYPE_NOCLIP) continue; if (SV_TestEntityPosition (check)) Con_Printf ("entity in invalid position\n"); } } /* ================ SV_CheckVelocity ================ */ void SV_CheckVelocity (edict_t *ent) { int i; // // bound velocity // for (i=0 ; i<3 ; i++) { if (isnan(ent->v.velocity[i])) { Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname); ent->v.velocity[i] = 0; } if (isnan(ent->v.origin[i])) { Con_Printf ("Got a NaN origin on %s\n", pr_strings + ent->v.classname); ent->v.origin[i] = 0; } if (ent->v.velocity[i] > sv_maxvelocity.value) ent->v.velocity[i] = sv_maxvelocity.value; else if (ent->v.velocity[i] < -sv_maxvelocity.value) ent->v.velocity[i] = -sv_maxvelocity.value; } } /* ============= SV_RunThink Runs thinking code if time. There is some play in the exact time the think function will be called, because it is called before any movement is done in a frame. Not used for pushmove objects, because they must be exact. Returns false if the entity removed itself. ============= */ bool SV_RunThink (edict_t *ent) { float thinktime; thinktime = ent->v.nextthink; if (thinktime <= 0 || thinktime > sv.time + host_frametime) return true; if (thinktime < sv.time) thinktime = sv.time; // don't let things stay in the past. // it is possible to start that way // by a trigger with a local time. ent->v.nextthink = 0; pr_global_struct->time = thinktime; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); return !ent->free; } /* ================== SV_Impact Two entities have touched, so run their touch functions ================== */ void SV_Impact (edict_t *e1, edict_t *e2) { int old_self, old_other; old_self = pr_global_struct->self; old_other = pr_global_struct->other; pr_global_struct->time = sv.time; if (e1->v.touch && e1->v.solid != SOLID_NOT) { pr_global_struct->self = EDICT_TO_PROG(e1); pr_global_struct->other = EDICT_TO_PROG(e2); PR_ExecuteProgram (e1->v.touch); } if (e2->v.touch && e2->v.solid != SOLID_NOT) { pr_global_struct->self = EDICT_TO_PROG(e2); pr_global_struct->other = EDICT_TO_PROG(e1); PR_ExecuteProgram (e2->v.touch); } pr_global_struct->self = old_self; pr_global_struct->other = old_other; } /* ================== ClipVelocity Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i, blocked; blocked = 0; if (normal[2] > 0) blocked |= 1; // floor if (!normal[2]) blocked |= 2; // step backoff = DotProduct (in, normal) * overbounce; for (i=0 ; i<3 ; i++) { change = normal[i]*backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } return blocked; } /* ============ SV_FlyMove The basic solid body movement clip that slides along multiple planes Returns the clipflags if the velocity was modified (hit something solid) 1 = floor 2 = wall / step 4 = dead stop If steptrace is not NULL, the trace of any vertical wall hit will be stored ============ */ #define MAX_CLIP_PLANES 5 int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (ent->v.velocity, original_velocity); VectorCopy (ent->v.velocity, primal_velocity); numplanes = 0; time_left = time; for (bumpcount=0 ; bumpcountv.velocity[0] && !ent->v.velocity[1] && !ent->v.velocity[2]) break; for (i=0 ; i<3 ; i++) end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i]; trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) { // entity is trapped in another solid VectorCopy (vec3_origin, ent->v.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, ent->v.origin); VectorCopy (ent->v.velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance if (!trace.ent) Sys_Error ("SV_FlyMove: !trace.ent"); if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor if (trace.ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); } } if (!trace.plane.normal[2]) { blocked |= 2; // step if (steptrace) *steptrace = trace; // save for player extrafriction } // // run the impact function // SV_Impact (ent, trace.ent); if (ent->free) break; // removed by the impact function time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorCopy (vec3_origin, ent->v.velocity); return 3; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i=0 ; iv.velocity); } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy (vec3_origin, ent->v.velocity); return 7; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, ent->v.velocity); VectorScale (dir, d, ent->v.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (ent->v.velocity, primal_velocity) <= 0) { VectorCopy (vec3_origin, ent->v.velocity); return blocked; } } return blocked; } /* ============ SV_AddGravity ============ */ void SV_AddGravity (edict_t *ent) { float ent_gravity; #ifdef QUAKE2 if (ent->v.gravity) ent_gravity = ent->v.gravity; else ent_gravity = 1.0; #else eval_t *val; val = GetEdictFieldValue(ent, "gravity"); if (val && val->_float) ent_gravity = val->_float; else ent_gravity = 1.0; #endif ent->v.velocity[2] -= ent_gravity * sv_gravity.value * host_frametime; } /* =============================================================================== PUSHMOVE =============================================================================== */ /* ============ SV_PushEntity Does not change the entities velocity at all ============ */ trace_t SV_PushEntity (edict_t *ent, vec3_t push) { trace_t trace; vec3_t end; VectorAdd (ent->v.origin, push, end); if (ent->v.movetype == MOVETYPE_FLYMISSILE) trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) // only clip against bmodels trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); else trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); VectorCopy (trace.endpos, ent->v.origin); SV_LinkEdict (ent, true); if (trace.ent) SV_Impact (ent, trace.ent); return trace; } /* ============ SV_PushMove ============ */ void SV_PushMove (edict_t *pusher, float movetime) { int i, e; edict_t *check, *block; vec3_t mins, maxs, move; vec3_t entorig, pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2]) { pusher->v.ltime += movetime; return; } for (i=0 ; i<3 ; i++) { move[i] = pusher->v.velocity[i] * movetime; mins[i] = pusher->v.absmin[i] + move[i]; maxs[i] = pusher->v.absmax[i] + move[i]; } VectorCopy (pusher->v.origin, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.origin, move, pusher->v.origin); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e=1 ; efree) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE #ifdef QUAKE2 || check->v.movetype == MOVETYPE_FOLLOW #endif || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definately be moved if ( ! ( ((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher) ) { if ( check->v.absmin[0] >= maxs[0] || check->v.absmin[1] >= maxs[1] || check->v.absmin[2] >= maxs[2] || check->v.absmax[0] <= mins[0] || check->v.absmax[1] <= mins[1] || check->v.absmax[2] <= mins[2] ) continue; // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.origin); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { pr_global_struct->self = EDICT_TO_PROG(pusher); pr_global_struct->other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i=0 ; iv.origin); SV_LinkEdict (moved_edict[i], false); } return; } } } #ifdef QUAKE2 /* ============ SV_PushRotate ============ */ void SV_PushRotate (edict_t *pusher, float movetime) { int i, e; edict_t *check, *block; vec3_t move, a, amove; vec3_t entorig, pushorig; int num_moved; edict_t *moved_edict[MAX_EDICTS]; vec3_t moved_from[MAX_EDICTS]; vec3_t org, org2; vec3_t forward, right, up; if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2]) { pusher->v.ltime += movetime; return; } for (i=0 ; i<3 ; i++) amove[i] = pusher->v.avelocity[i] * movetime; VectorSubtract (vec3_origin, amove, a); AngleVectors (a, forward, right, up); VectorCopy (pusher->v.angles, pushorig); // move the pusher to it's final position VectorAdd (pusher->v.angles, amove, pusher->v.angles); pusher->v.ltime += movetime; SV_LinkEdict (pusher, false); // see if any solid entities are inside the final position num_moved = 0; check = NEXT_EDICT(sv.edicts); for (e=1 ; efree) continue; if (check->v.movetype == MOVETYPE_PUSH || check->v.movetype == MOVETYPE_NONE || check->v.movetype == MOVETYPE_FOLLOW || check->v.movetype == MOVETYPE_NOCLIP) continue; // if the entity is standing on the pusher, it will definately be moved if ( ! ( ((int)check->v.flags & FL_ONGROUND) && PROG_TO_EDICT(check->v.groundentity) == pusher) ) { if ( check->v.absmin[0] >= pusher->v.absmax[0] || check->v.absmin[1] >= pusher->v.absmax[1] || check->v.absmin[2] >= pusher->v.absmax[2] || check->v.absmax[0] <= pusher->v.absmin[0] || check->v.absmax[1] <= pusher->v.absmin[1] || check->v.absmax[2] <= pusher->v.absmin[2] ) continue; // see if the ent's bbox is inside the pusher's final position if (!SV_TestEntityPosition (check)) continue; } // remove the onground flag for non-players if (check->v.movetype != MOVETYPE_WALK) check->v.flags = (int)check->v.flags & ~FL_ONGROUND; VectorCopy (check->v.origin, entorig); VectorCopy (check->v.origin, moved_from[num_moved]); moved_edict[num_moved] = check; num_moved++; // calculate destination position VectorSubtract (check->v.origin, pusher->v.origin, org); org2[0] = DotProduct (org, forward); org2[1] = -DotProduct (org, right); org2[2] = DotProduct (org, up); VectorSubtract (org2, org, move); // try moving the contacted entity pusher->v.solid = SOLID_NOT; SV_PushEntity (check, move); pusher->v.solid = SOLID_BSP; // if it is still inside the pusher, block block = SV_TestEntityPosition (check); if (block) { // fail the move if (check->v.mins[0] == check->v.maxs[0]) continue; if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER) { // corpse check->v.mins[0] = check->v.mins[1] = 0; VectorCopy (check->v.mins, check->v.maxs); continue; } VectorCopy (entorig, check->v.origin); SV_LinkEdict (check, true); VectorCopy (pushorig, pusher->v.angles); SV_LinkEdict (pusher, false); pusher->v.ltime -= movetime; // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (pusher->v.blocked) { pr_global_struct->self = EDICT_TO_PROG(pusher); pr_global_struct->other = EDICT_TO_PROG(check); PR_ExecuteProgram (pusher->v.blocked); } // move back any entities we already moved for (i=0 ; iv.origin); VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles); SV_LinkEdict (moved_edict[i], false); } return; } else { VectorAdd (check->v.angles, amove, check->v.angles); } } } #endif /* ================ SV_Physics_Pusher ================ */ void SV_Physics_Pusher (edict_t *ent) { float thinktime; float oldltime; float movetime; oldltime = ent->v.ltime; thinktime = ent->v.nextthink; if (thinktime < ent->v.ltime + host_frametime) { movetime = thinktime - ent->v.ltime; if (movetime < 0) movetime = 0; } else movetime = host_frametime; if (movetime) { #ifdef QUAKE2 if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2]) SV_PushRotate (ent, movetime); else #endif SV_PushMove (ent, movetime); // advances ent->v.ltime if not blocked } if (thinktime > oldltime && thinktime <= ent->v.ltime) { ent->v.nextthink = 0; pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); PR_ExecuteProgram (ent->v.think); if (ent->free) return; } } /* =============================================================================== CLIENT MOVEMENT =============================================================================== */ /* ============= SV_CheckStuck This is a big hack to try and fix the rare case of getting stuck in the world clipping hull. ============= */ void SV_CheckStuck (edict_t *ent) { int i, j; int z; vec3_t org; if (!SV_TestEntityPosition(ent)) { VectorCopy (ent->v.origin, ent->v.oldorigin); return; } VectorCopy (ent->v.origin, org); VectorCopy (ent->v.oldorigin, ent->v.origin); if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("Unstuck.\n"); SV_LinkEdict (ent, true); return; } for (z=0 ; z< 18 ; z++) for (i=-1 ; i <= 1 ; i++) for (j=-1 ; j <= 1 ; j++) { ent->v.origin[0] = org[0] + i; ent->v.origin[1] = org[1] + j; ent->v.origin[2] = org[2] + z; if (!SV_TestEntityPosition(ent)) { Con_DPrintf ("Unstuck.\n"); SV_LinkEdict (ent, true); return; } } VectorCopy (org, ent->v.origin); Con_DPrintf ("player is stuck.\n"); } /* ============= SV_CheckWater ============= */ bool SV_CheckWater (edict_t *ent) { vec3_t point; int cont; #ifdef QUAKE2 int truecont; #endif point[0] = ent->v.origin[0]; point[1] = ent->v.origin[1]; point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; ent->v.waterlevel = 0; ent->v.watertype = CONTENTS_EMPTY; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { #ifdef QUAKE2 truecont = SV_TruePointContents (point); #endif ent->v.watertype = cont; ent->v.waterlevel = 1; point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2])*0.5; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) { ent->v.waterlevel = 2; point[2] = ent->v.origin[2] + ent->v.view_ofs[2]; cont = SV_PointContents (point); if (cont <= CONTENTS_WATER) ent->v.waterlevel = 3; } #ifdef QUAKE2 if (truecont <= CONTENTS_CURRENT_0 && truecont >= CONTENTS_CURRENT_DOWN) { static vec3_t current_table[] = { {1, 0, 0}, {0, 1, 0}, {-1, 0, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1} }; VectorMA (ent->v.basevelocity, 150.0*ent->v.waterlevel/3.0, current_table[CONTENTS_CURRENT_0 - truecont], ent->v.basevelocity); } #endif } return ent->v.waterlevel > 1; } /* ============ SV_WallFriction ============ */ void SV_WallFriction (edict_t *ent, trace_t *trace) { vec3_t forward, right, up; float d, i; vec3_t into, side; AngleVectors (ent->v.v_angle, forward, right, up); d = DotProduct (trace->plane.normal, forward); d += 0.5; if (d >= 0) return; // cut the tangential velocity i = DotProduct (trace->plane.normal, ent->v.velocity); VectorScale (trace->plane.normal, i, into); VectorSubtract (ent->v.velocity, into, side); ent->v.velocity[0] = side[0] * (1 + d); ent->v.velocity[1] = side[1] * (1 + d); } /* ===================== SV_TryUnstick Player has come to a dead stop, possibly due to the problem with limited float precision at some angle joins in the BSP hull. Try fixing by pushing one pixel in each direction. This is a hack, but in the interest of good gameplay... ====================== */ int SV_TryUnstick (edict_t *ent, vec3_t oldvel) { int i; vec3_t oldorg; vec3_t dir; int clip; trace_t steptrace; VectorCopy (ent->v.origin, oldorg); VectorCopy (vec3_origin, dir); for (i=0 ; i<8 ; i++) { // try pushing a little in an axial direction switch (i) { case 0: dir[0] = 2; dir[1] = 0; break; case 1: dir[0] = 0; dir[1] = 2; break; case 2: dir[0] = -2; dir[1] = 0; break; case 3: dir[0] = 0; dir[1] = -2; break; case 4: dir[0] = 2; dir[1] = 2; break; case 5: dir[0] = -2; dir[1] = 2; break; case 6: dir[0] = 2; dir[1] = -2; break; case 7: dir[0] = -2; dir[1] = -2; break; } SV_PushEntity (ent, dir); // retry the original move ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, 0.1, &steptrace); if ( fabs(oldorg[1] - ent->v.origin[1]) > 4 || fabs(oldorg[0] - ent->v.origin[0]) > 4 ) { //Con_DPrintf ("unstuck!\n"); return clip; } // go back to the original pos and try again VectorCopy (oldorg, ent->v.origin); } VectorCopy (vec3_origin, ent->v.velocity); return 7; // still not moving } /* ===================== SV_WalkMove Only used by players ====================== */ #define STEPSIZE 18 void SV_WalkMove (edict_t *ent) { vec3_t upmove, downmove; vec3_t oldorg, oldvel; vec3_t nosteporg, nostepvel; int clip; int oldonground; trace_t steptrace, downtrace; // // do a regular slide move unless it looks like you ran into a step // oldonground = (int)ent->v.flags & FL_ONGROUND; ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; VectorCopy (ent->v.origin, oldorg); VectorCopy (ent->v.velocity, oldvel); clip = SV_FlyMove (ent, host_frametime, &steptrace); if ( !(clip & 2) ) return; // move didn't block on a step if (!oldonground && ent->v.waterlevel == 0) return; // don't stair up while jumping if (ent->v.movetype != MOVETYPE_WALK) return; // gibbed by a trigger if (sv_nostep.value) return; if ( (int)sv_player->v.flags & FL_WATERJUMP ) return; VectorCopy (ent->v.origin, nosteporg); VectorCopy (ent->v.velocity, nostepvel); // // try moving up and forward to go up a step // VectorCopy (oldorg, ent->v.origin); // back to start pos VectorCopy (vec3_origin, upmove); VectorCopy (vec3_origin, downmove); upmove[2] = STEPSIZE; downmove[2] = -STEPSIZE + oldvel[2]*host_frametime; // move up SV_PushEntity (ent, upmove); // FIXME: don't link? // move forward ent->v.velocity[0] = oldvel[0]; ent->v. velocity[1] = oldvel[1]; ent->v. velocity[2] = 0; clip = SV_FlyMove (ent, host_frametime, &steptrace); // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls if (clip) { if ( fabs(oldorg[1] - ent->v.origin[1]) < 0.03125 && fabs(oldorg[0] - ent->v.origin[0]) < 0.03125 ) { // stepping up didn't make any progress clip = SV_TryUnstick (ent, oldvel); } } // extra friction based on view angle if ( clip & 2 ) SV_WallFriction (ent, &steptrace); // move down downtrace = SV_PushEntity (ent, downmove); // FIXME: don't link? if (downtrace.plane.normal[2] > 0.7) { if (ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(downtrace.ent); } } else { // if the push down didn't end up on good ground, use the move without // the step up. This happens near wall / slope combinations, and can // cause the player to hop up higher on a slope too steep to climb VectorCopy (nosteporg, ent->v.origin); VectorCopy (nostepvel, ent->v.velocity); } } /* ================ SV_Physics_Client Player character actions ================ */ void SV_Physics_Client (edict_t *ent, int num) { if ( ! svs.clients[num-1].active ) return; // unconnected slot // // call standard client pre-think // pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (pr_global_struct->PlayerPreThink); // // do a move // SV_CheckVelocity (ent); // // decide which move function to call // switch ((int)ent->v.movetype) { case MOVETYPE_NONE: if (!SV_RunThink (ent)) return; break; case MOVETYPE_WALK: if (!SV_RunThink (ent)) return; if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) SV_AddGravity (ent); SV_CheckStuck (ent); #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif SV_WalkMove (ent); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: SV_Physics_Toss (ent); break; case MOVETYPE_FLY: if (!SV_RunThink (ent)) return; SV_FlyMove (ent, host_frametime, NULL); break; case MOVETYPE_NOCLIP: if (!SV_RunThink (ent)) return; VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); break; default: Sys_Error ("SV_Physics_client: bad movetype %i", (int)ent->v.movetype); } // // call standard player post-think // SV_LinkEdict (ent, true); pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PR_ExecuteProgram (pr_global_struct->PlayerPostThink); } //============================================================================ /* ============= SV_Physics_None Non moving objects can only think ============= */ void SV_Physics_None (edict_t *ent) { // regular thinking SV_RunThink (ent); } #ifdef QUAKE2 /* ============= SV_Physics_Follow Entities that are "stuck" to another entity ============= */ void SV_Physics_Follow (edict_t *ent) { // regular thinking SV_RunThink (ent); VectorAdd (PROG_TO_EDICT(ent->v.aiment)->v.origin, ent->v.v_angle, ent->v.origin); SV_LinkEdict (ent, true); } #endif /* ============= SV_Physics_Noclip A moving object that doesn't obey physics ============= */ void SV_Physics_Noclip (edict_t *ent) { // regular thinking if (!SV_RunThink (ent)) return; VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin); SV_LinkEdict (ent, false); } /* ============================================================================== TOSS / BOUNCE ============================================================================== */ /* ============= SV_CheckWaterTransition ============= */ void SV_CheckWaterTransition (edict_t *ent) { int cont; #ifdef QUAKE2 vec3_t point; point[0] = ent->v.origin[0]; point[1] = ent->v.origin[1]; point[2] = ent->v.origin[2] + ent->v.mins[2] + 1; cont = SV_PointContents (point); #else cont = SV_PointContents (ent->v.origin); #endif if (!ent->v.watertype) { // just spawned here ent->v.watertype = cont; ent->v.waterlevel = 1; return; } if (cont <= CONTENTS_WATER) { if (ent->v.watertype == CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); } ent->v.watertype = cont; ent->v.waterlevel = 1; } else { if (ent->v.watertype != CONTENTS_EMPTY) { // just crossed into water SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1); } ent->v.watertype = CONTENTS_EMPTY; ent->v.waterlevel = cont; } } /* ============= SV_Physics_Toss Toss, bounce, and fly movement. When onground, do nothing. ============= */ void SV_Physics_Toss (edict_t *ent) { trace_t trace; vec3_t move; float backoff; #ifdef QUAKE2 edict_t *groundentity; groundentity = PROG_TO_EDICT(ent->v.groundentity); if ((int)groundentity->v.flags & FL_CONVEYOR) VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); else VectorCopy(vec_origin, ent->v.basevelocity); SV_CheckWater (ent); #endif // regular thinking if (!SV_RunThink (ent)) return; #ifdef QUAKE2 if (ent->v.velocity[2] > 0) ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; if ( ((int)ent->v.flags & FL_ONGROUND) ) //@@ if (VectorCompare(ent->v.basevelocity, vec_origin)) return; SV_CheckVelocity (ent); // add gravity if (! ((int)ent->v.flags & FL_ONGROUND) && ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_BOUNCEMISSILE && ent->v.movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent); #else // if onground, return without moving if ( ((int)ent->v.flags & FL_ONGROUND) ) return; SV_CheckVelocity (ent); // add gravity if (ent->v.movetype != MOVETYPE_FLY && ent->v.movetype != MOVETYPE_FLYMISSILE) SV_AddGravity (ent); #endif // move angles VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); // move origin #ifdef QUAKE2 VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif VectorScale (ent->v.velocity, host_frametime, move); trace = SV_PushEntity (ent, move); #ifdef QUAKE2 VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); #endif if (trace.fraction == 1) return; if (ent->free) return; if (ent->v.movetype == MOVETYPE_BOUNCE) backoff = 1.5; #ifdef QUAKE2 else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) backoff = 2.0; #endif else backoff = 1; ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff); // stop if on ground if (trace.plane.normal[2] > 0.7) { #ifdef QUAKE2 if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) #else if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE) #endif { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.ent); VectorCopy (vec3_origin, ent->v.velocity); VectorCopy (vec3_origin, ent->v.avelocity); } } // check for in water SV_CheckWaterTransition (ent); } /* =============================================================================== STEPPING MOVEMENT =============================================================================== */ /* ============= SV_Physics_Step Monsters freefall when they don't have a ground entity, otherwise all movement is done with discrete steps. This is also used for objects that have become still on the ground, but will fall if the floor is pulled out from under them. ============= */ #ifdef QUAKE2 void SV_Physics_Step (edict_t *ent) { bool wasonground; bool inwater; bool hitsound = false; float *vel; float speed, newspeed, control; float friction; edict_t *groundentity; groundentity = PROG_TO_EDICT(ent->v.groundentity); if ((int)groundentity->v.flags & FL_CONVEYOR) VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity); else VectorCopy(vec_origin, ent->v.basevelocity); //@@ pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(ent); PF_WaterMove(); SV_CheckVelocity (ent); wasonground = (int)ent->v.flags & FL_ONGROUND; // ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // add gravity except: // flying monsters // swimming monsters who are in the water inwater = SV_CheckWater(ent); if (! wasonground) if (!((int)ent->v.flags & FL_FLY)) if (!(((int)ent->v.flags & FL_SWIM) && (ent->v.waterlevel > 0))) { if (ent->v.velocity[2] < sv_gravity.value*-0.1) hitsound = true; if (!inwater) SV_AddGravity (ent); } if (!VectorCompare(ent->v.velocity, vec_origin) || !VectorCompare(ent->v.basevelocity, vec_origin)) { ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // apply friction // let dead monsters who aren't completely onground slide if (wasonground) if (!(ent->v.health <= 0.0 && !SV_CheckBottom(ent))) { vel = ent->v.velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (speed) { friction = sv_friction.value; control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; newspeed = speed - host_frametime*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; } } VectorAdd (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); SV_FlyMove (ent, host_frametime, NULL); VectorSubtract (ent->v.velocity, ent->v.basevelocity, ent->v.velocity); // determine if it's on solid ground at all { vec3_t mins, maxs, point; int x, y; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); point[2] = mins[2] - 1; for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { point[0] = x ? maxs[0] : mins[0]; point[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (point) == CONTENTS_SOLID) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; break; } } } SV_LinkEdict (ent, true); if ((int)ent->v.flags & FL_ONGROUND) if (!wasonground) if (hitsound) SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); } #else void SV_Physics_Step (edict_t *ent) { bool hitsound; // freefall if not onground if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) ) { if (ent->v.velocity[2] < sv_gravity.value*-0.1) hitsound = true; else hitsound = false; SV_AddGravity (ent); SV_CheckVelocity (ent); SV_FlyMove (ent, host_frametime, NULL); SV_LinkEdict (ent, true); if ( (int)ent->v.flags & FL_ONGROUND ) // just hit ground { if (hitsound) SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1); } } // regular thinking SV_RunThink (ent); SV_CheckWaterTransition (ent); } #endif //============================================================================ /* ================ SV_Physics ================ */ void SV_Physics (void) { int i; edict_t *ent; extern dfunction_t *EndFrameQC; // let the progs know that a new frame has started pr_global_struct->self = EDICT_TO_PROG(sv.edicts); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); pr_global_struct->time = sv.time; PR_ExecuteProgram (pr_global_struct->StartFrame); //SV_CheckAllEnts (); // // treat each object in turn // ent = sv.edicts; for (i=0 ; ifree) continue; if (pr_global_struct->force_retouch) { SV_LinkEdict (ent, true); // force retouch even for stationary } if (i > 0 && i <= svs.maxclients) SV_Physics_Client (ent, i); else if (ent->v.movetype == MOVETYPE_PUSH) SV_Physics_Pusher (ent); else if (ent->v.movetype == MOVETYPE_NONE) SV_Physics_None (ent); #ifdef QUAKE2 else if (ent->v.movetype == MOVETYPE_FOLLOW) SV_Physics_Follow (ent); #endif else if (ent->v.movetype == MOVETYPE_NOCLIP) SV_Physics_Noclip (ent); else if (ent->v.movetype == MOVETYPE_STEP) SV_Physics_Step (ent); else if (ent->v.movetype == MOVETYPE_TOSS || ent->v.movetype == MOVETYPE_BOUNCE #ifdef QUAKE2 || ent->v.movetype == MOVETYPE_BOUNCEMISSILE #endif || ent->v.movetype == MOVETYPE_FLY || ent->v.movetype == MOVETYPE_FLYMISSILE) SV_Physics_Toss (ent); else Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype); } if (pr_global_struct->force_retouch) pr_global_struct->force_retouch--; // LordHavoc: endframe support if (EndFrameQC) { pr_global_struct->self = EDICT_TO_PROG(sv.edicts); pr_global_struct->other = EDICT_TO_PROG(sv.edicts); pr_global_struct->time = sv.time; PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions)); } sv.time += host_frametime; } #ifdef QUAKE2 trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore) { edict_t tempent, *tent; trace_t trace; vec3_t move; vec3_t end; double save_frametime; // extern particle_t *active_particles, *free_particles; // particle_t *p; save_frametime = host_frametime; host_frametime = 0.05; memcpy(&tempent, ent, sizeof(edict_t)); tent = &tempent; while (1) { SV_CheckVelocity (tent); SV_AddGravity (tent); VectorMA (tent->v.angles, host_frametime, tent->v.avelocity, tent->v.angles); VectorScale (tent->v.velocity, host_frametime, move); VectorAdd (tent->v.origin, move, end); trace = SV_Move (tent->v.origin, tent->v.mins, tent->v.maxs, end, MOVE_NORMAL, tent); VectorCopy (trace.endpos, tent->v.origin); // p = free_particles; // if (p) // { // free_particles = p->next; // p->next = active_particles; // active_particles = p; // // p->die = 256; // p->color = 15; // p->type = pt_static; // VectorCopy (vec3_origin, p->vel); // VectorCopy (tent->v.origin, p->org); // } if (trace.ent) if (trace.ent != ignore) break; } // p->color = 224; host_frametime = save_frametime; return trace; } #endif ================================================ FILE: source/sv_user.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sv_user.c -- server code for moving users #include "quakedef.h" edict_t *sv_player; CVAR(pq_fullpitch, 0, CVAR_ARCHIVE) CVAR(sv_idealpitchscale, 0.8, CVAR_NONE) CVAR(sv_edgefriction, 2, CVAR_NONE) CVAR(sv_maxspeed, 320, CVAR_SERVERINFO) CVAR(sv_accelerate, 10, CVAR_NONE) extern cvar_t sv_friction; extern cvar_t sv_stopspeed; //---------------------------------------------- static vec3_t forward, right, up; vec3_t wishdir; float wishspeed; // world float *angles; float *origin; float *velocity; bool onground; usercmd_t cmd; /* =============== SV_SetIdealPitch =============== */ #define MAX_FORWARD 6 void SV_SetIdealPitch (void) { float angleval, sinval, cosval; trace_t tr; vec3_t top, bottom; float z[MAX_FORWARD]; int i, j; int step, dir, steps; if (!((int)sv_player->v.flags & FL_ONGROUND)) return; angleval = sv_player->v.angles[YAW] * M_PI*2 / 360; sinval = sinf(angleval); cosval = cosf(angleval); for (i=0 ; iv.origin[0] + cosval*(i+3)*12; top[1] = sv_player->v.origin[1] + sinval*(i+3)*12; top[2] = sv_player->v.origin[2] + sv_player->v.view_ofs[2]; bottom[0] = top[0]; bottom[1] = top[1]; bottom[2] = top[2] - 160; tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player); if (tr.allsolid) return; // looking at a wall, leave ideal the way is was if (tr.fraction == 1) return; // near a dropoff z[i] = top[2] + tr.fraction*(bottom[2]-top[2]); } dir = 0; steps = 0; for (j=1 ; j -ON_EPSILON && step < ON_EPSILON) continue; if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) ) return; // mixed changes steps++; dir = step; } if (!dir) { sv_player->v.idealpitch = 0; return; } if (steps < 2) return; sv_player->v.idealpitch = -dir * sv_idealpitchscale.value; } /* ================== SV_UserFriction ================== */ void SV_UserFriction (void) { float *vel; float speed, newspeed, control; vec3_t start, stop; float friction; trace_t trace; vel = velocity; speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); if (!speed) return; // if the leading edge is over a dropoff, increase friction start[0] = stop[0] = origin[0] + vel[0]/speed*16; start[1] = stop[1] = origin[1] + vel[1]/speed*16; start[2] = origin[2] + sv_player->v.mins[2]; stop[2] = start[2] - 34; trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player); if (trace.fraction == 1.0) friction = sv_friction.value*sv_edgefriction.value; else friction = sv_friction.value; // apply friction control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; newspeed = speed - host_frametime*control*friction; if (newspeed < 0) newspeed = 0; newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== SV_Accelerate ============== */ void SV_Accelerate (void) { int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct (velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = sv_accelerate.value*host_frametime*wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i=0 ; i<3 ; i++) velocity[i] += accelspeed*wishdir[i]; } void SV_AirAccelerate (vec3_t wishveloc) { int i; float addspeed, wishspd, accelspeed, currentspeed; wishspd = VectorNormalize (wishveloc); if (wishspd > 30) wishspd = 30; currentspeed = DotProduct (velocity, wishveloc); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; // accelspeed = sv_accelerate.value * host_frametime; accelspeed = sv_accelerate.value*wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i=0 ; i<3 ; i++) velocity[i] += accelspeed*wishveloc[i]; } void DropPunchAngle (void) { float len; len = VectorNormalize (sv_player->v.punchangle); len -= 10*host_frametime; if (len < 0) len = 0; VectorScale (sv_player->v.punchangle, len, sv_player->v.punchangle); } /* =================== SV_WaterMove =================== */ void SV_WaterMove (void) { int i; vec3_t wishvel; float speed, newspeed, wishspeed, addspeed, accelspeed; // // user intentions // AngleVectors (sv_player->v.v_angle, forward, right, up); for (i=0 ; i<3 ; i++) wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove; if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove) wishvel[2] -= 60; // drift towards bottom else wishvel[2] += cmd.upmove; wishspeed = Length(wishvel); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } wishspeed *= 0.7; // // water friction // speed = Length (velocity); if (speed) { newspeed = speed - host_frametime * speed * sv_friction.value; if (newspeed < 0) newspeed = 0; VectorScale (velocity, newspeed/speed, velocity); } else newspeed = 0; // // water acceleration // if (!wishspeed) return; addspeed = wishspeed - newspeed; if (addspeed <= 0) return; VectorNormalize (wishvel); accelspeed = sv_accelerate.value * wishspeed * host_frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i=0 ; i<3 ; i++) velocity[i] += accelspeed * wishvel[i]; } void SV_WaterJump (void) { if (sv.time > sv_player->v.teleport_time || !sv_player->v.waterlevel) { sv_player->v.flags = (int)sv_player->v.flags & ~FL_WATERJUMP; sv_player->v.teleport_time = 0; } sv_player->v.velocity[0] = sv_player->v.movedir[0]; sv_player->v.velocity[1] = sv_player->v.movedir[1]; } /* =================== SV_AirMove =================== */ void SV_AirMove (void) { int i; vec3_t wishvel; float fmove, smove; AngleVectors (sv_player->v.angles, forward, right, up); fmove = cmd.forwardmove; smove = cmd.sidemove; // hack to not let you back into teleporter if (sv.time < sv_player->v.teleport_time && fmove < 0) fmove = 0; for (i=0 ; i<3 ; i++) wishvel[i] = forward[i]*fmove + right[i]*smove; if ( (int)sv_player->v.movetype != MOVETYPE_WALK) wishvel[2] = cmd.upmove; else wishvel[2] = 0; VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > sv_maxspeed.value) { VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); wishspeed = sv_maxspeed.value; } if ( sv_player->v.movetype == MOVETYPE_NOCLIP) { // noclip VectorCopy (wishvel, velocity); } else if ( (int)sv_player->v.flags & FL_ONGROUND ) { SV_UserFriction (); SV_Accelerate (); } else { // not on ground, so little effect on velocity SV_AirAccelerate (wishvel); } } /* =================== SV_ClientThink the move fields specify an intended velocity in pix/sec the angle fields specify an exact angular motion in degrees =================== */ void SV_ClientThink (void) { vec3_t v_angle; if (sv_player->v.movetype == MOVETYPE_NONE) return; onground = (int)sv_player->v.flags & FL_ONGROUND; origin = sv_player->v.origin; velocity = sv_player->v.velocity; DropPunchAngle (); // // if dead, behave differently // if (sv_player->v.health <= 0) return; // // angles // show 1/3 the pitch angle and all the roll angle cmd = host_client->cmd; angles = sv_player->v.angles; VectorAdd (sv_player->v.v_angle, sv_player->v.punchangle, v_angle); angles[ROLL] = V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; if (!sv_player->v.fixangle) { angles[PITCH] = -v_angle[PITCH]/3; angles[YAW] = v_angle[YAW]; } if ( (int)sv_player->v.flags & FL_WATERJUMP ) { SV_WaterJump (); return; } // // walk // if ( (sv_player->v.waterlevel >= 2) && (sv_player->v.movetype != MOVETYPE_NOCLIP) ) { SV_WaterMove (); return; } SV_AirMove (); } /* =================== SV_ReadClientMove =================== */ void SV_ReadClientMove (usercmd_t *move) { int i; vec3_t angle; int bits; // read ping time host_client->ping_times[host_client->num_pings%NUM_PING_TIMES] = sv.time - MSG_ReadFloat (); host_client->num_pings++; // read current angles if (host_client->netconnection->proquake_connection == MOD_PROQUAKE) { for (i = 0; i<3; i++) angle[i] = MSG_ReadPreciseAngle(); } else { for (i = 0; i<3; i++) angle[i] = MSG_ReadAngle(); } // ProQuake - server-side fullpitch fix if (!pq_fullpitch.value) { if (angle[PITCH] > 80 || angle[PITCH] < -70) { angle[PITCH] = COM_Clamp(angle[PITCH], -70, 80); MSG_WriteByte (&host_client->message, svc_setangle); for (i=0 ; i < 3 ; i++) MSG_WriteAngle (&host_client->message, angle[i] ); } } VectorCopy (angle, host_client->edict->v.v_angle); // read movement move->forwardmove = MSG_ReadShort (); move->sidemove = MSG_ReadShort (); move->upmove = MSG_ReadShort (); // read buttons bits = MSG_ReadByte (); host_client->edict->v.button0 = bits & 1; host_client->edict->v.button2 = (bits & 2)>>1; i = MSG_ReadByte (); if (i) host_client->edict->v.impulse = i; #ifdef QUAKE2 // read light level host_client->edict->v.light_level = MSG_ReadByte (); #endif } /* =================== SV_ReadClientMessage Returns false if the client should be killed =================== */ bool SV_ReadClientMessage (void) { int ret; int cmd; char *s; do { nextmsg: ret = NET_GetMessage (host_client->netconnection); if (ret == -1) { Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n"); return false; } if (!ret) return true; MSG_BeginReading (); while (1) { if (!host_client->active) return false; // a command caused an error if (msg_badread) { Sys_Printf ("SV_ReadClientMessage: badread\n"); return false; } cmd = MSG_ReadChar (); switch (cmd) { case -1: goto nextmsg; // end of message default: Sys_Printf ("SV_ReadClientMessage: unknown command char\n"); return false; case clc_nop: // Sys_Printf ("clc_nop\n"); break; case clc_stringcmd: s = MSG_ReadString (); if (host_client->privileged) ret = 2; else ret = 0; if (strncasecmp(s, "status", 6) == 0) ret = 1; else if (strncasecmp(s, "god", 3) == 0) ret = 1; else if (strncasecmp(s, "notarget", 8) == 0) ret = 1; else if (strncasecmp(s, "fly", 3) == 0) ret = 1; else if (strncasecmp(s, "name", 4) == 0) ret = 1; else if (strncasecmp(s, "noclip", 6) == 0) ret = 1; else if (strncasecmp(s, "say", 3) == 0) ret = 1; else if (strncasecmp(s, "say_team", 8) == 0) ret = 1; else if (strncasecmp(s, "tell", 4) == 0) ret = 1; else if (strncasecmp(s, "color", 5) == 0) ret = 1; else if (strncasecmp(s, "kill", 4) == 0) ret = 1; else if (strncasecmp(s, "pause", 5) == 0) ret = 1; else if (strncasecmp(s, "spawn", 5) == 0) ret = 1; else if (strncasecmp(s, "begin", 5) == 0) ret = 1; else if (strncasecmp(s, "prespawn", 8) == 0) ret = 1; else if (strncasecmp(s, "kick", 4) == 0) ret = 1; else if (strncasecmp(s, "ping", 4) == 0) ret = 1; else if (strncasecmp(s, "give", 4) == 0) ret = 1; else if (strncasecmp(s, "ban", 3) == 0) ret = 1; if (ret == 2) Cbuf_InsertText (s); else if (ret == 1) Cmd_ExecuteString (s, src_client); else Con_DPrintf("%s tried to %s\n", host_client->name, s); break; case clc_disconnect: // Sys_Printf ("SV_ReadClientMessage: client disconnected\n"); return false; case clc_move: SV_ReadClientMove (&host_client->cmd); break; } } } while (ret == 1); return true; } /* ================== SV_RunClients ================== */ void SV_RunClients (void) { int i; for (i=0, host_client = svs.clients ; iactive) continue; sv_player = host_client->edict; if (!SV_ReadClientMessage ()) { SV_DropClient (false); // client misbehaved... continue; } if (!host_client->spawned) { // clear client movement until a new packet is received memset (&host_client->cmd, 0, sizeof(host_client->cmd)); continue; } // always pause in single player if in console or menus if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) SV_ClientThink (); } } ================================================ FILE: source/sys.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sys.h -- non-portable functions // // file IO // // returns the file size // return -1 if file is not present // the file should be in BINARY mode for stupid OSs that care int Sys_FileOpenRead (char *path, int *hndl); int Sys_FileOpenWrite (char *path); void Sys_FileClose (int handle); void Sys_FileSeek (int handle, int position); int Sys_FileRead (int handle, void *dest, int count); int Sys_FileWrite (int handle, void *data, int count); int Sys_FileTime (char *path); void Sys_mkdir (char *path); // // memory protection // void Sys_MakeCodeWriteable (unsigned long startaddr, unsigned long length); // // system IO // void Sys_DebugLog(char *file, char *fmt, ...); void Sys_Error (const char *error, ...); // an error will cause the entire program to exit void Sys_Printf (char *fmt, ...); // send text to the console void Sys_Quit (void); double Sys_FloatTime (void); char *Sys_ConsoleInput (void); void Sys_Sleep (void); // called to yield for a little bit so as // not to hog cpu when paused or debugging void Sys_SendKeyEvents (void); // Perform Key_Event () callbacks until the input que is empty void Sys_LowFPPrecision (void); void Sys_HighFPPrecision (void); void Sys_SetFPCW (void); // >>> FIX: For Nintendo Wii using devkitPPC / libogc // New functions for big stack handling: void* Sys_BigStackAlloc(int size, char* purpose); void Sys_BigStackFree(int size, char* purpose); void Sys_BigStackRewind(void); // <<< FIX ================================================ FILE: source/sys_psp2.c ================================================ /* Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2020 Asakura Reiko 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" #include "errno.h" #include "net_dgrm.h" #include #define BIGSTACK_SIZE 20 * 1024 * 1024 byte sys_bigstack[BIGSTACK_SIZE]; int sys_bigstack_cursize; uint8_t is_uma0 = 0; int tex_cache = 1; extern int antialiasing; extern uint8_t netcheck_dialog_running; extern uint8_t proto_idx; extern int gl_ssaa; // Mods support int max_mod_idx = -1; ModsList *mods = NULL; ModsList *addMod(char* name, ModsList* db){ ModsList* entry = (ModsList*)malloc(sizeof(ModsList)); strcpy(entry->name, name); entry->next = NULL; if (db == NULL) return entry; else { ModsList* ptr = db; while (ptr->next != NULL) { ptr = ptr->next; } ptr->next = entry; return db; } } int old_char; extern int setup_cursor; extern int lanConfig_cursor; int isKeyboard; extern uint32_t rumble_tick; extern cvar_t psvita_touchmode; extern cvar_t vid_vsync; extern int scr_width; extern int scr_height; extern int cfg_width; extern int cfg_height; bool isDedicated; uint64_t initialTime = 0; int hostInitialized = 0; SceCtrlData pad, oldpad; /* =============================================================================== FILE IO =============================================================================== */ #define PLATFORM_PSVITA 0x00010000 #define MAX_HANDLES 10 FILE *sys_handles[MAX_HANDLES]; int Sys_FindHandle(void) { int i; for (i=1;i= 0) { sys_bigstack_cursize = sys_bigstack_cursize - size; } else { Sys_Error("Sys_BigStackFree: %s - underflow on %i bytes", purpose, sys_bigstack_cursize - size); }; } // <<< FIX /* =============================================================================== SYSTEM IO =============================================================================== */ void Sys_MakeCodeWriteable(unsigned long startaddr, unsigned long length) { } void Sys_Quit(void) { Host_Shutdown(); sceKernelExitProcess(0); } void Sys_Error(const char *error, ...) { va_list argptr; char buf[256]; va_start(argptr, error); vsnprintf(buf, sizeof(buf), error, argptr); va_end(argptr); sprintf(buf, "%s\n", buf); FILE* f = NULL; if (is_uma0) f = fopen("uma0:/data/Quake/log.txt", "a+"); else f = fopen("ux0:/data/Quake/log.txt", "a+"); fwrite(buf, 1, strlen(buf), f); fclose(f); Sys_Quit(); } void Sys_Printf(char *fmt, ...) { #ifdef DEBUG if (hostInitialized) return; va_list argptr; char buf[256]; va_start(argptr, fmt); vsnprintf(buf, sizeof(buf), fmt, argptr); va_end(argptr); Log(buf); #endif } char *Sys_ConsoleInput(void) { return NULL; } void Sys_Sleep(void) { } double Sys_FloatTime(void) { SceRtcTick ticks; sceRtcGetCurrentTick(&ticks); return ticks.tick * 0.000001; } void Sys_HighFPPrecision(void) { } void Sys_LowFPPrecision(void) { } /* =============================================================================== KEYS & INPUTS =============================================================================== */ typedef struct { int button; int key; } psvita_buttons; #define MAX_PSVITA_KEYS 12 psvita_buttons KeyTable[MAX_PSVITA_KEYS] = { { SCE_CTRL_SELECT, K_SELECT }, { SCE_CTRL_START, K_START }, { SCE_CTRL_UP, K_UPARROW }, { SCE_CTRL_DOWN, K_DOWNARROW}, { SCE_CTRL_LEFT, K_LEFTARROW }, { SCE_CTRL_RIGHT, K_RIGHTARROW }, { SCE_CTRL_LTRIGGER, K_LEFTTRIGGER }, { SCE_CTRL_RTRIGGER, K_RIGHTTRIGGER }, { SCE_CTRL_SQUARE, K_SQUARE }, { SCE_CTRL_TRIANGLE, K_TRIANGLE }, { SCE_CTRL_CROSS, K_CROSS }, { SCE_CTRL_CIRCLE, K_CIRCLE } }; void PSP2_KeyDown(int keys) { int i; for (i = 0; i < MAX_PSVITA_KEYS; i++) { if (keys & KeyTable[i].button) { Key_Event(KeyTable[i].key, true); } } } void PSP2_KeyUp(int keys, int oldkeys) { int i; for (i = 0; i < MAX_PSVITA_KEYS; i++) { if ((!(keys & KeyTable[i].button)) && (oldkeys & KeyTable[i].button)) { Key_Event(KeyTable[i].key, false); } } } void Sys_SendKeyEvents(void) { sceCtrlPeekBufferPositive(0, &pad, 1); int kDown = pad.buttons; int kUp = oldpad.buttons; if (kDown) PSP2_KeyDown(kDown); if (kUp != kDown) PSP2_KeyUp(kDown, kUp); if (psvita_touchmode.value == 0) { // Touchscreen support for game status showing SceTouchData touch; sceTouchPeek(SCE_TOUCH_PORT_FRONT, &touch, 1); Key_Event(K_TOUCH, touch.reportNum > 0 ? true : false); } oldpad = pad; } //============================================================================= int _newlib_heap_size_user = 192 * 1024 * 1024; char* mod_path = NULL; char mp_path[32]; static uint16_t title[SCE_IME_DIALOG_MAX_TITLE_LENGTH]; static uint16_t initial_text[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; static uint16_t input_text[SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1]; char title_keyboard[256]; void ascii2utf(uint16_t* dst, char* src){ if(!src || !dst)return; while(*src)*(dst++)=(*src++); *dst=0x00; } void utf2ascii(char* dst, uint16_t* src){ if(!src || !dst)return; while(*src)*(dst++)=(*(src++))&0xFF; *dst=0x00; } void simulateKeyPress(char* text){ //We first delete the current text int i; for (i=0;i<100;i++){ Key_Event(K_BACKSPACE, true); Key_Event(K_BACKSPACE, false); } while (*text){ Key_Event(*text, true); Key_Event(*text, false); text++; } } #define MAXCMDLINE 256 // If changed, don't forget to change it in keys.c too!! extern char key_lines[32][MAXCMDLINE]; extern int edit_line; bool CheckForMod(char* dir) { int dd = sceIoDopen(dir); SceIoDirent entry; int res; bool ret = false; while ((res = sceIoDread(dd, &entry)) > 0 && (!ret)) if ( strstr(strtolower(entry.d_name),".pak") != NULL || !strcmp(strtolower(entry.d_name), "progs.dat") ) ret = true; // Enable checks for progs.dat only mods sceIoDclose(dd); return ret; } int quake_main (unsigned int argc, void* argv){ cl_entities = malloc(sizeof(entity_t) * MAX_EDICTS); cl_temp_entities = malloc(sizeof(entity_t) * MAX_TEMP_ENTITIES); cl_efrags = malloc(sizeof(efrag_t) * MAX_EFRAGS); // Checking for uma0 support FILE *f = fopen("uma0:/data/Quake/id1/pak0.pak", "rb"); if (f) { fclose(f); is_uma0 = 1; } // Initializing stuffs scePowerSetArmClockFrequency(444); scePowerSetBusClockFrequency(222); scePowerSetGpuClockFrequency(222); scePowerSetGpuXbarClockFrequency(166); sceSysmoduleLoadModule(SCE_SYSMODULE_NET); sceSysmoduleLoadModule(SCE_SYSMODULE_PSPNET_ADHOC); sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG_WIDE); sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, 1); sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK, 1); sceAppUtilInit(&(SceAppUtilInitParam){}, &(SceAppUtilBootParam){}); SceCommonDialogConfigParam cmnDlgCfgParam; sceCommonDialogConfigParamInit(&cmnDlgCfgParam); sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, (int *)&cmnDlgCfgParam.language); sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_ENTER_BUTTON, (int *)&cmnDlgCfgParam.enterButtonAssign); sceCommonDialogSetConfigParam(&cmnDlgCfgParam); const float tickRate = 1.0f / sceRtcGetTickResolution(); static quakeparms_t parms; // Loading resolution and MSAA mode from config files, those are not handled via Host cause Host_Init requires vitaGL to be working if (is_uma0) f = fopen("uma0:data/Quake/resolution.cfg", "rb"); else f = fopen("ux0:data/Quake/resolution.cfg", "rb"); if (f != NULL){ fscanf(f, "%dx%d", &scr_width, &scr_height); fclose(f); } if (is_uma0) f = fopen("uma0:data/Quake/antialiasing.cfg", "rb"); else f = fopen("ux0:data/Quake/antialiasing.cfg", "rb"); if (f != NULL){ fscanf(f, "%d", &antialiasing); fclose(f); } cfg_width = scr_width; cfg_height = scr_height; // Initializing vitaGL vglSetCircularPoolSize(64 * 1024 * 1024); GLboolean invalid_res = GL_FALSE; switch (antialiasing) { case 1: case 5: case 6: invalid_res = vglInitExtended(0x1400000, scr_width, scr_height, 0x1000000, SCE_GXM_MULTISAMPLE_2X); break; case 2: case 7: case 8: invalid_res = vglInitExtended(0x1400000, scr_width, scr_height, 0x1000000, SCE_GXM_MULTISAMPLE_4X); break; default: invalid_res = vglInitExtended(0x1400000, scr_width, scr_height, 0x1000000, SCE_GXM_MULTISAMPLE_NONE); break; } if (invalid_res) { cfg_width = scr_width = 960; cfg_height = scr_height = 544; } // Properly setting SSAA switch (antialiasing) { case 3: case 5: case 7: gl_ssaa = 2; break; case 4: case 6: case 8: gl_ssaa = 4; break; default: gl_ssaa = 1; break; } // Official mission packs support SceAppUtilAppEventParam eventParam; memset(&eventParam, 0, sizeof(SceAppUtilAppEventParam)); sceAppUtilReceiveAppEvent(&eventParam); if (eventParam.type == 0x05) { char *int_argv[16]; int_argv[0] = int_argv[2] = ""; char buffer[2048]; memset(buffer, 0, 2048); sceAppUtilAppEventParseLiveArea(&eventParam, buffer); if (strstr(buffer, "custom") != NULL) { memset(input_text, 0, (SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1) << 1); memset(initial_text, 0, (SCE_IME_DIALOG_MAX_TEXT_LENGTH) << 1); sprintf(title_keyboard, "Insert args list"); ascii2utf(title, title_keyboard); SceImeDialogParam param; sceImeDialogParamInit(¶m); param.supportedLanguages = 0x0001FFFF; param.languagesForced = SCE_TRUE; param.type = SCE_IME_TYPE_BASIC_LATIN; param.title = title; param.maxTextLength = SCE_IME_DIALOG_MAX_TEXT_LENGTH; param.initialText = initial_text; param.inputTextBuffer = input_text; sceImeDialogInit(¶m); while (sceImeDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) { vglSwapBuffers(GL_TRUE); } SceCommonDialogStatus status = sceImeDialogGetStatus(); SceImeDialogResult result; memset(&result, 0, sizeof(SceImeDialogResult)); sceImeDialogGetResult(&result); if (result.button == SCE_IME_DIALOG_BUTTON_ENTER) { utf2ascii(title_keyboard, input_text); if (strlen(title_keyboard) > 1) { int int_argc = 2; int_argv[1] = title_keyboard; char *ptr = title_keyboard; for (;;) { char *space = strstr(ptr, " "); if (space == NULL) break; *space = 0; int_argv[int_argc++] = ptr = space + 1; } int_argv[int_argc++] = ""; COM_InitArgv(int_argc, int_argv); } else COM_InitArgv(argc, argv); } else COM_InitArgv(argc, argv); sceImeDialogTerm(); } else { int_argv[1] = buffer; COM_InitArgv(3, int_argv); sprintf(mp_path, "%s", &buffer[1]); mod_path = mp_path; } } else COM_InitArgv(argc, argv); if (is_uma0) parms.basedir = "uma0:/data/Quake"; else parms.basedir = "ux0:/data/Quake"; // Initializing empty ModList mods = NULL; int i = 0; int max_idx = -1; // Scanning main folder in search of mods int dd = sceIoDopen(parms.basedir); SceIoDirent entry; int res; while (sceIoDread(dd, &entry) > 0) { if (SCE_S_ISDIR(entry.d_stat.st_mode)) { if (CheckForMod(va("%s/%s", parms.basedir, entry.d_name))) { mods = addMod(entry.d_name, mods); max_mod_idx++; } } } sceIoDclose(dd); // Bat files support if (COM_CheckParm("-bat")) { char *int_argv[16]; int_argv[0] = ""; int t = COM_CheckParm("-bat") + 1; if (t < com_argc) { char fname[128]; sprintf(fname, "%s/%s.bat", parms.basedir, com_argv[t]); SceUID fd = sceIoOpen(fname, SCE_O_RDONLY, 0777); if (fd >= 0) { int size = sceIoLseek32(fd, 0, SCE_SEEK_END); char bat_args[2048]; sceIoLseek32(fd, 0, SCE_SEEK_SET); sceIoRead(fd, bat_args, size); sceIoClose(fd); bat_args[size] = 0; int int_argc = 2; char *ptr = strstr(bat_args, "quake") + 6; int_argv[1] = ptr; for (;;) { char *space = strstr(ptr, " "); if (space == NULL) break; *space = 0; int_argv[int_argc++] = ptr = space + 1; } int_argv[int_argc++] = ""; COM_InitArgv(int_argc, int_argv); } } } // Texture cache if (COM_CheckParm("-no_tex_cache")) tex_cache = 0; if (COM_CheckParm("-mem")) { int t = COM_CheckParm("-mem") + 1; if (t < com_argc) parms.memsize = atoi(com_argv[t]) * 1024 * 1024; } else parms.memsize = 64 * 1024 * 1024; parms.membase = malloc(parms.memsize); parms.argc = com_argc; parms.argv = com_argv; Host_Init(&parms); hostInitialized = 1; // Setting PSN Account if it's his first time if (!strcmp(cl_name.string, "player")) { char nickname[32]; sceAppUtilSystemParamGetString(SCE_SYSTEM_PARAM_ID_USERNAME, nickname, SCE_SYSTEM_PARAM_USERNAME_MAXSIZE); static char cmd[256]; sprintf(cmd, "_cl_name \"%s\"\n", nickname); Cbuf_AddText(cmd); } IN_ResetInputs(); Cbuf_AddText("exec config.cfg\n"); vglWaitVblankStart(vid_vsync.value); int old_vsync = vid_vsync.value; SceRtcTick lastTick; sceRtcGetCurrentTick(&lastTick); // Disabling all FPU exceptions traps on main thread sceKernelChangeThreadVfpException(0x0800009FU, 0x0); while (1) { // Changing V-Sync setting in realtime if (old_vsync != vid_vsync.value) { vglWaitVblankStart(vid_vsync.value); old_vsync = vid_vsync.value; } // Prevent screen power-off sceKernelPowerTick(0); // Rumble effect managing (PSTV only) if (rumble_tick != 0) { if (sceKernelGetProcessTimeLow() - rumble_tick > 500000) IN_StopRumble(); // 0.5 sec } // AdHoc Message Dialog if (netcheck_dialog_running == 1) { SceCommonDialogStatus status = sceNetCheckDialogGetStatus(); if (status == 2) { SceNetCheckDialogResult result; memset(&result, 0, sizeof(SceNetCheckDialogResult)); sceNetCheckDialogGetResult(&result); if (result.result == SCE_COMMON_DIALOG_RESULT_OK) { proto_idx = 1; } else { proto_idx = 0; } sceNetCheckDialogTerm(); Datagram_Init(); } } // OSK manage for Console / Input if (key_dest == key_console || m_state == m_lanconfig || m_state == m_setup) { if (old_char != 0) Key_Event(old_char, false); SceCtrlData tmp_pad, oldpad; sceCtrlPeekBufferPositive(0, &tmp_pad, 1); if (isKeyboard) { SceCommonDialogStatus status = sceImeDialogGetStatus(); if (status == 2) { SceImeDialogResult result; memset(&result, 0, sizeof(SceImeDialogResult)); sceImeDialogGetResult(&result); if (result.button == SCE_IME_DIALOG_BUTTON_ENTER) { if (key_dest == key_console) { utf2ascii(title_keyboard, input_text); strcpy(key_lines[edit_line] + 1, title_keyboard); Key_SendText(key_lines[edit_line] + 1); } else { utf2ascii(title_keyboard, input_text); simulateKeyPress(title_keyboard); } } sceImeDialogTerm(); isKeyboard = false; } } else { if ((tmp_pad.buttons & SCE_CTRL_SELECT) && (!(oldpad.buttons & SCE_CTRL_SELECT))) { if ((m_state == m_setup && (setup_cursor == 0 || setup_cursor == 1)) || (key_dest == key_console) || (m_state == m_lanconfig && (lanConfig_cursor == 0 || lanConfig_cursor == 3))) { memset(input_text, 0, (SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1) << 1); memset(initial_text, 0, (SCE_IME_DIALOG_MAX_TEXT_LENGTH) << 1); if (key_dest == key_console) { sprintf(title_keyboard, "Insert Quake command"); } else if (m_state == m_setup) { (setup_cursor == 0) ? sprintf(title_keyboard, "Insert hostname") : sprintf(title_keyboard, "Insert player name"); } else if (m_state == m_lanconfig){ (lanConfig_cursor == 0) ? sprintf(title_keyboard, "Insert port number") : sprintf(title_keyboard, "Insert server address"); } ascii2utf(title, title_keyboard); isKeyboard = true; SceImeDialogParam param; sceImeDialogParamInit(¶m); param.supportedLanguages = 0x0001FFFF; param.languagesForced = SCE_TRUE; param.type = (m_state == m_lanconfig && lanConfig_cursor == 0) ? SCE_IME_TYPE_NUMBER : SCE_IME_TYPE_BASIC_LATIN; param.title = title; param.maxTextLength = (m_state == m_lanconfig && lanConfig_cursor == 0) ? 5 : SCE_IME_DIALOG_MAX_TEXT_LENGTH; if (key_dest == key_console) { ascii2utf(initial_text, key_lines[edit_line] + 1); ascii2utf(input_text, key_lines[edit_line] + 1); } param.initialText = initial_text; param.inputTextBuffer = input_text; sceImeDialogInit(¶m); } } } oldpad = tmp_pad; } // Get current frame SceRtcTick tick; sceRtcGetCurrentTick(&tick); const unsigned int deltaTick = tick.tick - lastTick.tick; const float deltaSecond = deltaTick * tickRate; // Show frame Host_Frame(deltaSecond); lastTick.tick = tick.tick; } return 0; } int main(int argc, char **argv) { // We need a bigger stack to run Quake, so we create a new thread with a proper stack size SceUID main_thread = sceKernelCreateThread("Quake", quake_main, 0x40, 0x800000, 0, 0, NULL); if (main_thread >= 0){ sceKernelStartThread(main_thread, 0, NULL); } return sceKernelExitDeleteThread(0); } ================================================ FILE: source/vid.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // vid.h -- video driver defs #define VID_CBITS 6 #define VID_GRADES (1 << VID_CBITS) // a pixel can be one, two, or four bytes typedef byte pixel_t; typedef struct vrect_s { int x,y,width,height; struct vrect_s *pnext; } vrect_t; typedef struct { pixel_t *buffer; // invisible buffer pixel_t *colormap; // 256 * VID_GRADES size unsigned short *colormap16; // 256 * VID_GRADES size int fullbright; // index of first fullbright color unsigned rowbytes; // may be > width if displayed in a window unsigned width; unsigned height; float aspect; // width / height -- < 0 is taller than wide int numpages; int recalc_refdef; // if true, recalc vid-based stuff pixel_t *conbuffer; int conrowbytes; unsigned conwidth; unsigned conheight; int maxwarpwidth; int maxwarpheight; pixel_t *direct; // direct drawing to framebuffer, if not // NULL } viddef_t; extern viddef_t vid; // global video state extern unsigned short d_8to16table[256]; extern unsigned d_8to24table[256]; extern void (*vid_menudrawfn)(void); extern void (*vid_menukeyfn)(int key); void VID_SetPalette (unsigned char *palette); // called at startup and after any gamma correction void VID_ShiftPalette (unsigned char *palette); // called for bonus and pain flashes, and for underwater color changes void VID_Init (unsigned char *palette); // Called at startup to set up translation tables, takes 256 8 bit RGB values // the palette data will go away after the call, so it must be copied off if // the video driver will need it again void VID_Shutdown (void); // Called at shutdown void VID_Update (vrect_t *rects); // flushes the given rectangles from the view buffer to the screen int VID_SetMode (int modenum, unsigned char *palette); // sets the mode; only used by the Quake engine for resetting to mode 0 (the // base mode) on memory allocation failures void VID_HandlePause (bool pause); // called only on Win32, when pause happens, so the mouse can be released void VID_ChangeRes(float scale); // called only on Vita build, changes rescaler properties ================================================ FILE: source/view.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // view.c -- player eye positioning #include "quakedef.h" #include "r_local.h" /* The view is allowed to move slightly from it's true position for bobbing, but if it exceeds 8 pixels linear distance (spherical, not box), the list of entities sent from the server may not include everything in the pvs, especially when crossing a water boudnary. */ CVAR (lcd_x, 0, CVAR_NONE) CVAR(lcd_yaw, 0, CVAR_NONE) CVAR(scr_ofsx, 0, CVAR_NONE) CVAR(scr_ofsy, 0, CVAR_NONE) CVAR(scr_ofsz, 0, CVAR_NONE) CVAR(cl_rollspeed, 200, CVAR_NONE) CVAR(cl_rollangle, 2.0, CVAR_NONE) CVAR(cl_bob, 0.02, CVAR_NONE) CVAR(cl_bobcycle, 0.6, CVAR_NONE) CVAR(cl_bobup, 0.5, CVAR_NONE) CVAR(v_kicktime, 0.5, CVAR_NONE) CVAR(v_kickroll, 0.6, CVAR_NONE) CVAR(v_kickpitch, 0.6, CVAR_NONE) CVAR(v_iyaw_cycle, 2, CVAR_NONE) CVAR(v_iroll_cycle, 0.5, CVAR_NONE) CVAR(v_ipitch_cycle, 1, CVAR_NONE) CVAR(v_iyaw_level, 0.3, CVAR_NONE) CVAR(v_iroll_level, 0.1, CVAR_NONE) CVAR(v_ipitch_level, 0.3, CVAR_NONE) CVAR(v_idlescale, 0, CVAR_NONE) CVAR(crosshair, 1, CVAR_ARCHIVE) CVAR(crosshaircolor_r, 255, CVAR_ARCHIVE) CVAR(crosshaircolor_g, 255, CVAR_ARCHIVE) CVAR(crosshaircolor_b, 0, CVAR_ARCHIVE) CVAR(cl_crossx, 0, CVAR_NONE) CVAR(cl_crossy, 0, CVAR_NONE) CVAR(gl_cshiftpercent, 100, CVAR_NONE) CVAR(v_centermove, 0.15, CVAR_NONE) CVAR(v_centerspeed, 500, CVAR_NONE) CVAR(r_viewmodel_quake, 1, CVAR_ARCHIVE) CVAR(r_viewmodeloffset, 0, CVAR_ARCHIVE) //---------------------------------------------- float v_dmg_time, v_dmg_roll, v_dmg_pitch; extern int in_forward, in_forward2, in_back; /* =============== V_CalcRoll Used by view and sv_user =============== */ vec3_t forward, right, up; float V_CalcRoll (vec3_t angles, vec3_t velocity) { float sign; float side; float value; AngleVectors (angles, forward, right, up); side = DotProduct (velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = cl_rollangle.value; // if (cl.inwater) // value *= 6; if (side < cl_rollspeed.value) side = side * value / cl_rollspeed.value; else side = value; return side*sign; } /* =============== V_CalcBob =============== */ float V_CalcBob (void) { float bob; float cycle; cycle = cl.time - (int)(cl.time/cl_bobcycle.value)*cl_bobcycle.value; cycle /= cl_bobcycle.value; if (cycle < cl_bobup.value) cycle = M_PI * cycle / cl_bobup.value; else cycle = M_PI + M_PI*(cycle-cl_bobup.value)/(1.0 - cl_bobup.value); // bob is proportional to velocity in the xy plane // (don't count Z, or jumping messes it up) bob = sqrt(cl.velocity[0]*cl.velocity[0] + cl.velocity[1]*cl.velocity[1]) * cl_bob.value; //Con_Printf ("speed: %5.1f\n", Length(cl.velocity)); bob = bob*0.3 + bob*0.7*sin(cycle); if (bob > 4) bob = 4; else if (bob < -7) bob = -7; return bob; } //============================================================================= void V_StartPitchDrift (void) { #if 1 if (cl.laststop == cl.time) { return; // something else is keeping it from drifting } #endif if (cl.nodrift || !cl.pitchvel) { cl.pitchvel = v_centerspeed.value; cl.nodrift = false; cl.driftmove = 0; } } void V_StopPitchDrift (void) { cl.laststop = cl.time; cl.nodrift = true; cl.pitchvel = 0; } /* =============== V_DriftPitch Moves the client pitch angle towards cl.idealpitch sent by the server. If the user is adjusting pitch manually, either with lookup/lookdown, mlook and mouse, or klook and keyboard, pitch drifting is constantly stopped. Drifting is enabled when the center view key is hit, mlook is released and lookspring is non 0, or when =============== */ void V_DriftPitch (void) { float delta, move; if (noclip_anglehack || !cl.onground || cls.demoplayback ) { cl.driftmove = 0; cl.pitchvel = 0; return; } // don't count small mouse motion if (cl.nodrift) { if ( fabs(cl.cmd.forwardmove) < cl_forwardspeed.value) cl.driftmove = 0; else cl.driftmove += host_frametime; if ( cl.driftmove > v_centermove.value) { V_StartPitchDrift (); } return; } delta = cl.idealpitch - cl.viewangles[PITCH]; if (!delta) { cl.pitchvel = 0; return; } move = host_frametime * cl.pitchvel; cl.pitchvel += host_frametime * v_centerspeed.value; //Con_Printf ("move: %f (%f)\n", move, host_frametime); if (delta > 0) { if (move > delta) { cl.pitchvel = 0; move = delta; } cl.viewangles[PITCH] += move; } else if (delta < 0) { if (move > -delta) { cl.pitchvel = 0; move = -delta; } cl.viewangles[PITCH] -= move; } } /* ============= V_CalcBlend ============= */ #ifdef GLQUAKE void V_CalcBlend (void) { float r, g, b, a, a2; int j; r = 0; g = 0; b = 0; a = 0; for (j=0 ; j 1) v_blend[3] = 1; if (v_blend[3] < 0) v_blend[3] = 0; } #endif /* ============================================================================== PALETTE FLASHES ============================================================================== */ cshift_t cshift_empty = { {0,0,0}, 0 }; cshift_t cshift_water = { {130,80,50}, 128 }; cshift_t cshift_slime = { {0,25,5}, 150 }; cshift_t cshift_lava = { {255,80,0}, 150 }; CVAR (v_gamma, 1, CVAR_ARCHIVE) byte gammatable[256]; // palette is sent through this #ifdef GLQUAKE byte ramps[3][256]; float v_blend[4]; // rgba 0.0 - 1.0 #endif // GLQUAKE void BuildGammaTable (float g) { int i, inf; if (g == 1.0) { for (i=0 ; i<256 ; i++) gammatable[i] = i; return; } for (i=0 ; i<256 ; i++) { inf = 255 * pow ( (i+0.5)/255.5 , g ) + 0.5; if (inf < 0) inf = 0; if (inf > 255) inf = 255; gammatable[i] = inf; } } /* ================= V_CheckGamma ================= */ bool V_CheckGamma (void) { /*static float oldgammavalue; if (v_gamma.value == oldgammavalue) return false; oldgammavalue = v_gamma.value;*/ BuildGammaTable (v_gamma.value); vid.recalc_refdef = 1; // force a surface cache flush return true; } /* =============== V_ParseDamage =============== */ void V_ParseDamage (void) { int armor, blood; vec3_t from; int i; vec3_t forward, right, up; entity_t *ent; float side; float count; armor = MSG_ReadByte (); blood = MSG_ReadByte (); for (i=0 ; i<3 ; i++) from[i] = MSG_ReadCoord (); count = blood*0.5 + armor*0.5; if (count < 10) count = 10; cl.faceanimtime = cl.time + 0.2; // but sbar face into pain frame cl.cshifts[CSHIFT_DAMAGE].percent += 3*count; if (cl.cshifts[CSHIFT_DAMAGE].percent < 0) cl.cshifts[CSHIFT_DAMAGE].percent = 0; if (cl.cshifts[CSHIFT_DAMAGE].percent > 150) cl.cshifts[CSHIFT_DAMAGE].percent = 150; if (armor > blood) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 200; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 100; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 100; } else if (armor) { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 220; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 50; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 50; } else { cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; } // // calculate view angle kicks // ent = &cl_entities[cl.viewentity]; VectorSubtract (from, ent->origin, from); VectorNormalize (from); AngleVectors (ent->angles, forward, right, up); side = DotProduct (from, right); v_dmg_roll = count*side*v_kickroll.value; side = DotProduct (from, forward); v_dmg_pitch = count*side*v_kickpitch.value; v_dmg_time = v_kicktime.value; } /* ================== V_cshift_f ================== */ void V_cshift_f (void) { cshift_empty.destcolor[0] = atoi(Cmd_Argv(1)); cshift_empty.destcolor[1] = atoi(Cmd_Argv(2)); cshift_empty.destcolor[2] = atoi(Cmd_Argv(3)); cshift_empty.percent = atoi(Cmd_Argv(4)); } /* ================== V_BonusFlash_f When you run over an item, the server sends this command ================== */ void V_BonusFlash_f (void) { cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; cl.cshifts[CSHIFT_BONUS].percent = 50; } /* ============= V_SetContentsColor Underwater, lava, etc each has a color shift ============= */ void V_SetContentsColor (int contents) { switch (contents) { case CONTENTS_EMPTY: case CONTENTS_SOLID: case CONTENTS_SKY: cl.cshifts[CSHIFT_CONTENTS] = cshift_empty; break; case CONTENTS_LAVA: cl.cshifts[CSHIFT_CONTENTS] = cshift_lava; break; case CONTENTS_SLIME: cl.cshifts[CSHIFT_CONTENTS] = cshift_slime; break; default: cl.cshifts[CSHIFT_CONTENTS] = cshift_water; } } /* ============= V_CalcPowerupCshift ============= */ void V_CalcPowerupCshift (void) { if (cl.items & IT_QUAD) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 0; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 255; cl.cshifts[CSHIFT_POWERUP].percent = 30; } else if (cl.items & IT_SUIT) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 0; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; cl.cshifts[CSHIFT_POWERUP].percent = 20; } else if (cl.items & IT_INVISIBILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 100; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 100; cl.cshifts[CSHIFT_POWERUP].percent = 100; } else if (cl.items & IT_INVULNERABILITY) { cl.cshifts[CSHIFT_POWERUP].destcolor[0] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[1] = 255; cl.cshifts[CSHIFT_POWERUP].destcolor[2] = 0; cl.cshifts[CSHIFT_POWERUP].percent = 30; } else cl.cshifts[CSHIFT_POWERUP].percent = 0; } /* ============= V_UpdatePalette ============= */ void V_UpdatePalette (void) { int i, j; bool new; byte *basepal, *newpal; byte pal[768]; int r,g,b; bool force; V_CalcPowerupCshift (); new = false; for (i=0 ; i>8; g += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[1]-g))>>8; b += (cl.cshifts[j].percent*(cl.cshifts[j].destcolor[2]-b))>>8; } newpal[0] = gammatable[r]; newpal[1] = gammatable[g]; newpal[2] = gammatable[b]; newpal += 3; } VID_ShiftPalette (pal); } /* ============================================================================== VIEW RENDERING ============================================================================== */ float angledelta (float a) { a = anglemod(a); if (a > 180) a -= 360; return a; } /* ================== CalcGunAngle ================== */ void CalcGunAngle (void) { float yaw, pitch, move; static float oldyaw = 0; static float oldpitch = 0; yaw = r_refdef.viewangles[YAW]; pitch = -r_refdef.viewangles[PITCH]; yaw = angledelta(yaw - r_refdef.viewangles[YAW]) * 0.4; if (yaw > 10) yaw = 10; if (yaw < -10) yaw = -10; pitch = angledelta(-pitch - r_refdef.viewangles[PITCH]) * 0.4; if (pitch > 10) pitch = 10; if (pitch < -10) pitch = -10; move = host_frametime*20; if (yaw > oldyaw) { if (oldyaw + move < yaw) yaw = oldyaw + move; } else { if (oldyaw - move > yaw) yaw = oldyaw - move; } if (pitch > oldpitch) { if (oldpitch + move < pitch) pitch = oldpitch + move; } else { if (oldpitch - move > pitch) pitch = oldpitch - move; } oldyaw = yaw; oldpitch = pitch; cl.viewent.angles[YAW] = r_refdef.viewangles[YAW] + yaw; cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); v4sf src = { cl.time*v_iroll_cycle.value, cl.time*v_ipitch_cycle.value, cl.time*v_iyaw_cycle.value, 0 }; v4sf dst = sin_ps(src); cl.viewent.angles[ROLL] -= v_idlescale.value * dst[0] * v_iroll_level.value; cl.viewent.angles[PITCH] -= v_idlescale.value * dst[1] * v_ipitch_level.value; cl.viewent.angles[YAW] -= v_idlescale.value * dst[2] * v_iyaw_level.value; } /* ============== V_BoundOffsets ============== */ void V_BoundOffsets (void) { entity_t *ent; ent = &cl_entities[cl.viewentity]; // absolutely bound refresh reletive to entity clipping hull // so the view can never be inside a solid wall if (r_refdef.vieworg[0] < ent->origin[0] - 14) r_refdef.vieworg[0] = ent->origin[0] - 14; else if (r_refdef.vieworg[0] > ent->origin[0] + 14) r_refdef.vieworg[0] = ent->origin[0] + 14; if (r_refdef.vieworg[1] < ent->origin[1] - 14) r_refdef.vieworg[1] = ent->origin[1] - 14; else if (r_refdef.vieworg[1] > ent->origin[1] + 14) r_refdef.vieworg[1] = ent->origin[1] + 14; if (r_refdef.vieworg[2] < ent->origin[2] - 22) r_refdef.vieworg[2] = ent->origin[2] - 22; else if (r_refdef.vieworg[2] > ent->origin[2] + 30) r_refdef.vieworg[2] = ent->origin[2] + 30; } /* ============== V_AddIdle Idle swaying ============== */ void V_AddIdle (void) { v4sf src = { cl.time*v_iroll_cycle.value, cl.time*v_ipitch_cycle.value, cl.time*v_iyaw_cycle.value, 0 }; v4sf dst = sin_ps(src); r_refdef.viewangles[ROLL] += v_idlescale.value * dst[0] * v_iroll_level.value; r_refdef.viewangles[PITCH] += v_idlescale.value * dst[1] * v_ipitch_level.value; r_refdef.viewangles[YAW] += v_idlescale.value * dst[2] * v_iyaw_level.value; } /* ============== V_CalcViewRoll Roll is induced by movement and damage ============== */ void V_CalcViewRoll (void) { float side; side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity); r_refdef.viewangles[ROLL] += side; if (v_dmg_time > 0) { r_refdef.viewangles[ROLL] += v_dmg_time/v_kicktime.value*v_dmg_roll; r_refdef.viewangles[PITCH] += v_dmg_time/v_kicktime.value*v_dmg_pitch; v_dmg_time -= host_frametime; } if (cl.stats[STAT_HEALTH] <= 0) { r_refdef.viewangles[ROLL] = 80; // dead view angle return; } } /* ================== V_CalcIntermissionRefdef ================== */ void V_CalcIntermissionRefdef (void) { entity_t *ent, *view; float old; // ent is the player model (visible when out of body) ent = &cl_entities[cl.viewentity]; // view is the weapon model (only visible from inside body) view = &cl.viewent; VectorCopy (ent->origin, r_refdef.vieworg); VectorCopy (ent->angles, r_refdef.viewangles); view->model = NULL; // always idle in intermission old = v_idlescale.value; v_idlescale.value = 1; V_AddIdle (); v_idlescale.value = old; } /* ================== V_CalcRefdef ================== */ void V_CalcRefdef (void) { entity_t *ent, *view; int i; vec3_t forward, right, up; vec3_t angles; float bob; static float oldz = 0; V_DriftPitch (); // ent is the player model (visible when out of body) ent = &cl_entities[cl.viewentity]; // view is the weapon model (only visible from inside body) view = &cl.viewent; // transform the view offset by the model's matrix to get the offset from // model origin for the view ent->angles[YAW] = cl.viewangles[YAW]; // the model should face // the view dir ent->angles[PITCH] = -cl.viewangles[PITCH]; // the model should face // the view dir bob = V_CalcBob (); // refresh position VectorCopy (ent->origin, r_refdef.vieworg); r_refdef.vieworg[2] += cl.viewheight + bob; // never let it sit exactly on a node line, because a water plane can // dissapear when viewed with the eye exactly on it. // the server protocol only specifies to 1/16 pixel, so add 1/32 in each axis r_refdef.vieworg[0] += 1.0/32; r_refdef.vieworg[1] += 1.0/32; r_refdef.vieworg[2] += 1.0/32; VectorCopy (cl.viewangles, r_refdef.viewangles); V_CalcViewRoll (); V_AddIdle (); // offsets angles[PITCH] = -ent->angles[PITCH]; // because entity pitches are // actually backward angles[YAW] = ent->angles[YAW]; angles[ROLL] = ent->angles[ROLL]; AngleVectors (angles, forward, right, up); for (i=0 ; i<3 ; i++) r_refdef.vieworg[i] += scr_ofsx.value*forward[i] + scr_ofsy.value*right[i] + scr_ofsz.value*up[i]; V_BoundOffsets (); // set up gun position VectorCopy (cl.viewangles, view->angles); CalcGunAngle (); VectorCopy (ent->origin, view->origin); view->origin[2] += cl.viewheight; VectorCopy (r_refdef.vieworg, view->origin); VectorMA (view->origin, bob * 0.4, forward, view->origin); if (r_viewmodeloffset.string[0]) { float offset[3]; int size = sizeof(offset)/sizeof(offset[0]); ParseFloats(r_viewmodeloffset.string, offset, &size); VectorMA (view->origin, offset[0], right, view->origin); VectorMA (view->origin, -offset[1], up, view->origin); VectorMA (view->origin, offset[2], forward, view->origin); } // fudge position around to keep amount of weapon visible // roughly equal with different FOV if (r_viewmodel_quake.value) { if (viewsize.value == 110) view->origin[2] += 1; else if (viewsize.value == 100) view->origin[2] += 2; else if (viewsize.value == 90) view->origin[2] += 1; else if (viewsize.value == 80) view->origin[2] += 0.5; } view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; view->frame = cl.stats[STAT_WEAPONFRAME]; view->colormap = vid.colormap; // set up the refresh position VectorAdd (r_refdef.viewangles, cl.punchangle, r_refdef.viewangles); // smooth out stair step ups if (cl.onground && ent->origin[2] - oldz > 0) { float steptime; steptime = cl.time - cl.oldtime; if (steptime < 0) //FIXME I_Error ("steptime < 0"); steptime = 0; oldz += steptime * 80; if (oldz > ent->origin[2]) oldz = ent->origin[2]; if (ent->origin[2] - oldz > 12) oldz = ent->origin[2] - 12; r_refdef.vieworg[2] += oldz - ent->origin[2]; view->origin[2] += oldz - ent->origin[2]; } else oldz = ent->origin[2]; if (chase_active.value) Chase_Update (); } /* ================== V_RenderView The player's clipping box goes from (-16 -16 -24) to (16 16 32) from the entity origin, so any view position inside that will be valid ================== */ extern vrect_t scr_vrect; void V_RenderView (void) { if (con_forcedup) return; // don't allow cheats in multiplayer if (cl.maxclients > 1) { Cvar_Set ("scr_ofsx", "0"); Cvar_Set ("scr_ofsy", "0"); Cvar_Set ("scr_ofsz", "0"); } if (cl.intermission) { // intermission / finale rendering V_CalcIntermissionRefdef (); } else { if (!cl.paused /* && (sv.maxclients > 1 || key_dest == key_game) */ ) V_CalcRefdef (); } R_PushDlights (); R_RenderView (); } //============================================================================ /* ============= V_Init ============= */ void V_Init (void) { Cmd_AddCommand ("v_cshift", V_cshift_f); Cmd_AddCommand ("bf", V_BonusFlash_f); Cmd_AddCommand ("centerview", V_StartPitchDrift); Cvar_RegisterVariable (&lcd_x); Cvar_RegisterVariable (&lcd_yaw); Cvar_RegisterVariable (&v_centermove); Cvar_RegisterVariable (&v_centerspeed); Cvar_RegisterVariable (&v_iyaw_cycle); Cvar_RegisterVariable (&v_iroll_cycle); Cvar_RegisterVariable (&v_ipitch_cycle); Cvar_RegisterVariable (&v_iyaw_level); Cvar_RegisterVariable (&v_iroll_level); Cvar_RegisterVariable (&v_ipitch_level); Cvar_RegisterVariable (&v_idlescale); Cvar_RegisterVariable (&crosshair); Cvar_RegisterVariable (&crosshaircolor_r); Cvar_RegisterVariable (&crosshaircolor_g); Cvar_RegisterVariable (&crosshaircolor_b); Cvar_RegisterVariable (&cl_crossx); Cvar_RegisterVariable (&cl_crossy); Cvar_RegisterVariable (&gl_cshiftpercent); Cvar_RegisterVariable (&scr_ofsx); Cvar_RegisterVariable (&scr_ofsy); Cvar_RegisterVariable (&scr_ofsz); Cvar_RegisterVariable (&cl_rollspeed); Cvar_RegisterVariable (&cl_rollangle); Cvar_RegisterVariable (&cl_bob); Cvar_RegisterVariable (&cl_bobcycle); Cvar_RegisterVariable (&cl_bobup); Cvar_RegisterVariable (&v_kicktime); Cvar_RegisterVariable (&v_kickroll); Cvar_RegisterVariable (&v_kickpitch); BuildGammaTable (1.0); // no gamma yet Cvar_RegisterVariable (&v_gamma); Cvar_RegisterVariable (&r_viewmodel_quake); Cvar_RegisterVariable (&r_viewmodeloffset); } ================================================ FILE: source/view.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // view.h extern cvar_t v_gamma; extern byte gammatable[256]; // palette is sent through this extern byte ramps[3][256]; extern float v_blend[4]; extern cvar_t lcd_x; void V_Init (void); void V_RenderView (void); float V_CalcRoll (vec3_t angles, vec3_t velocity); void V_UpdatePalette (void); ================================================ FILE: source/wad.c ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // wad.c #include "quakedef.h" int wad_numlumps; lumpinfo_t *wad_lumps; byte *wad_base; void SwapPic (qpic_t *pic); /* ================== W_CleanupName Lowercases name and pads with spaces and a terminating 0 to the length of lumpinfo_t->name. Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time Space padding is so names can be printed nicely in tables. Can safely be performed in place. ================== */ void W_CleanupName (const char *in, char *out) { int i; int c; for (i=0 ; i<16 ; i++ ) { c = in[i]; if (!c) break; if (c >= 'A' && c <= 'Z') c += ('a' - 'A'); out[i] = c; } for ( ; i< 16 ; i++ ) out[i] = 0; } /* ==================== W_LoadWadFile ==================== */ void W_LoadWadFile (char *filename) { lumpinfo_t *lump_p; wadinfo_t *header; unsigned i; int infotableofs; wad_base = COM_LoadHunkFile (filename, NULL); if (!wad_base) Sys_Error ("W_LoadWadFile: couldn't load %s", filename); header = (wadinfo_t *)wad_base; if (header->identification[0] != 'W' || header->identification[1] != 'A' || header->identification[2] != 'D' || header->identification[3] != '2') Sys_Error ("Wad file %s doesn't have WAD2 id\n",filename); wad_numlumps = LittleLong(header->numlumps); infotableofs = LittleLong(header->infotableofs); wad_lumps = (lumpinfo_t *)(wad_base + infotableofs); for (i=0, lump_p = wad_lumps ; ifilepos = LittleLong(lump_p->filepos); lump_p->size = LittleLong(lump_p->size); W_CleanupName (lump_p->name, lump_p->name); if (lump_p->type == TYP_QPIC) SwapPic ( (qpic_t *)(wad_base + lump_p->filepos)); } } /* ============= W_GetLumpinfo ============= */ lumpinfo_t *W_GetLumpinfo (const char *name) { int i; lumpinfo_t *lump_p; char clean[16]; W_CleanupName (name, clean); for (lump_p=wad_lumps, i=0 ; iname)) return lump_p; } Sys_Error ("W_GetLumpinfo: %s not found", name); return NULL; } void *W_GetLumpName (const char *name) { lumpinfo_t *lump; lump = W_GetLumpinfo (name); return (void *)(wad_base + lump->filepos); } void *W_GetLumpNum (int num) { lumpinfo_t *lump; if (num < 0 || num > wad_numlumps) Sys_Error ("W_GetLumpNum: bad number: %i", num); lump = wad_lumps + num; return (void *)(wad_base + lump->filepos); } /* ============================================================================= automatic byte swapping ============================================================================= */ void SwapPic (qpic_t *pic) { pic->width = LittleLong(pic->width); pic->height = LittleLong(pic->height); } /* ============================================================================= WAD3 Texture Loading for BSP 3.0 Support ============================================================================= */ #ifdef GLQUAKE #define TEXWAD_MAXIMAGES 16384 typedef struct { char name[MAX_QPATH]; FILE *file; int position; int size; } texwadlump_t; static texwadlump_t texwadlump[TEXWAD_MAXIMAGES]; void WAD3_LoadTextureWadFile (char *filename) { lumpinfo_t *lumps, *lump_p; wadinfo_t header; int i, j, infotableofs, numlumps, lowmark; FILE *file; if (COM_FOpenFile (va("textures/halflife/%s", filename), &file, NULL) != -1) goto loaded; if (COM_FOpenFile (va("textures/%s", filename), &file, NULL) != -1) goto loaded; if (COM_FOpenFile (filename, &file, NULL) != -1) goto loaded; Host_Error ("Couldn't load halflife wad \"%s\"\n", filename); loaded: if (fread(&header, 1, sizeof(wadinfo_t), file) != sizeof(wadinfo_t)) { Con_Printf ("WAD3_LoadTextureWadFile: unable to read wad header"); return; } if (memcmp(header.identification, "WAD3", 4)) { Con_Printf ("WAD3_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\n",filename); return; } numlumps = LittleLong(header.numlumps); if (numlumps < 1 || numlumps > TEXWAD_MAXIMAGES) { Con_Printf ("WAD3_LoadTextureWadFile: invalid number of lumps (%i)\n", numlumps); return; } infotableofs = LittleLong(header.infotableofs); if (fseek(file, infotableofs, SEEK_SET)) { Con_Printf ("WAD3_LoadTextureWadFile: unable to seek to lump table"); return; } lowmark = Hunk_LowMark(); if (!(lumps = Hunk_Alloc(sizeof(lumpinfo_t) * numlumps))) { Con_Printf ("WAD3_LoadTextureWadFile: unable to allocate temporary memory for lump table"); return; } if (fread(lumps, 1, sizeof(lumpinfo_t) * numlumps, file) != sizeof(lumpinfo_t) * numlumps) { Con_Printf ("WAD3_LoadTextureWadFile: unable to read lump table"); Hunk_FreeToLowMark(lowmark); return; } for (i = 0, lump_p = lumps; i < numlumps; i++,lump_p++) { W_CleanupName (lump_p->name, lump_p->name); for (j = 0; j < TEXWAD_MAXIMAGES; j++) { if (!texwadlump[j].name[0] || !strcmp(lump_p->name, texwadlump[j].name)) break; } if (j == TEXWAD_MAXIMAGES) break; // we are full, don't load any more if (!texwadlump[j].name[0]) strncpyz (texwadlump[j].name, lump_p->name, sizeof(texwadlump[j].name)); texwadlump[j].file = file; texwadlump[j].position = LittleLong(lump_p->filepos); texwadlump[j].size = LittleLong(lump_p->disksize); } Hunk_FreeToLowMark(lowmark); //leaves the file open } //converts paletted to rgba static byte *ConvertWad3ToRGBA(miptex_t *tex) { byte *in, *data, *pal; int i, p, image_size; if (!tex->offsets[0]) Sys_Error("ConvertWad3ToRGBA: tex->offsets[0] == 0"); image_size = tex->width * tex->height; in = (byte *) ((byte *) tex + tex->offsets[0]); data = malloc(image_size * 4); // Baker pal = in + ((image_size * 85) >> 6) + 2; for (i = 0; i < image_size; i++) { p = *in++; if (tex->name[0] == '{' && p == 255) { ((int *) data)[i] = 0; } else { p *= 3; data[i * 4 + 0] = pal[p]; data[i * 4 + 1] = pal[p + 1]; data[i * 4 + 2] = pal[p + 2]; data[i * 4 + 3] = 255; } } return data; } byte *WAD3_LoadTexture(miptex_t *mt) { char texname[MAX_QPATH]; int i, j, lowmark = 0; FILE *file; miptex_t *tex; byte *data; if (mt->offsets[0]) return ConvertWad3ToRGBA(mt); texname[sizeof(texname) - 1] = 0; W_CleanupName (mt->name, texname); for (i = 0; i < TEXWAD_MAXIMAGES; i++) { if (!texwadlump[i].name[0]) break; if (strcmp(texname, texwadlump[i].name)) continue; file = texwadlump[i].file; if (fseek(file, texwadlump[i].position, SEEK_SET)) { Con_Printf("WAD3_LoadTexture: corrupt WAD3 file"); return NULL; } lowmark = Hunk_LowMark(); tex = Hunk_Alloc(texwadlump[i].size); if (fread(tex, 1, texwadlump[i].size, file) < texwadlump[i].size) { Con_Printf("WAD3_LoadTexture: corrupt WAD3 file"); Hunk_FreeToLowMark(lowmark); return NULL; } tex->width = LittleLong(tex->width); tex->height = LittleLong(tex->height); if (tex->width != mt->width || tex->height != mt->height) { Hunk_FreeToLowMark(lowmark); return NULL; } for (j = 0;j < MIPLEVELS;j++) tex->offsets[j] = LittleLong(tex->offsets[j]); data = ConvertWad3ToRGBA(tex); Hunk_FreeToLowMark(lowmark); return data; } return NULL; } #endif ================================================ FILE: source/wad.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // wad.h //=============== // TYPES //=============== #define CMP_NONE 0 #define CMP_LZSS 1 #define TYP_NONE 0 #define TYP_LABEL 1 #define TYP_LUMPY 64 // 64 + grab command number #define TYP_PALETTE 64 #define TYP_QTEX 65 #define TYP_QPIC 66 #define TYP_SOUND 67 #define TYP_MIPTEX 68 typedef struct { int width, height; byte data[4]; // variably sized } qpic_t; typedef struct { char identification[4]; // should be WAD2 or 2DAW int numlumps; int infotableofs; } wadinfo_t; typedef struct { int filepos; int disksize; int size; // uncompressed char type; char compression; char pad1, pad2; char name[16]; // must be null terminated } lumpinfo_t; extern int wad_numlumps; extern lumpinfo_t *wad_lumps; extern byte *wad_base; void W_LoadWadFile (char *filename); void W_CleanupName (const char *in, char *out); lumpinfo_t *W_GetLumpinfo (const char *name); void *W_GetLumpName (const char *name); void *W_GetLumpNum (int num); void SwapPic (qpic_t *pic); ================================================ FILE: source/webdownload.c ================================================ /* Copyright (C) 2006 Pekka Lampila ("Medar"), Damien Deville ("Pb") and German Garcia Fernandez ("Jal") for Chasseur de bots association. 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "webdownload.h" #include "quakedef.h" #include #include static int Web_Init( void ); static void Web_Cleanup( void ); // to get the size and mime type before the download we can use that // http://curl.haxx.se/mail/lib-2002-05/0036.html // Baker: NO ... use #define APPLICATION "Qrack" static CURL *curl = NULL; static char curl_err[1024]; static int ( *progress )(double)= NULL; static size_t Write( void *ptr, size_t size, size_t nmemb, void *stream ) { FILE *f; //Con_Printf("name %s path %s\n", data->name, data->path); f = fopen( (char *)stream, "ab" ); if( f == NULL ) return size*nmemb; // weird //fwrite(ptr,size,nmemb,(FILE *) stream); fwrite( ptr, size, nmemb, f ); fclose( f ); return size*nmemb; } static int Progress( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow ) { // callback if( progress != NULL ) { if( progress( dlnow/dltotal ) != 0 ) return 1; // we abort } //Con_Printf("Progress: %2.2f\n",dlnow*100.0/dltotal); return 0; } static void Web_Cleanup( void ) { if( curl != NULL ) { /* always cleanup */ curl_easy_cleanup( curl ); curl = NULL; // reset callback progress = NULL; } } static int Web_Init( void ) { CURLcode code; char useragent[256]; if( curl != NULL ) { Web_Cleanup(); } // reinit curl = curl_easy_init(); // reset callback progress = NULL; // http://curl.haxx.se/libcurl/c/curl_easy_setopt.html /* init some options of curl */ code = curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, curl_err ); if( code != CURLE_OK ) { Con_Printf( "Failed to set error buffer\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_NOPROGRESS, 0 ); if( code != CURLE_OK ) { Con_Printf( "Failed to set NoProgress\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 ); if( code != CURLE_OK ) { Con_Printf( "Failed to set libcurl nosignal mode\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1 ); if( code != CURLE_OK ) { Con_Printf( "Failed to set FollowLocation\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_MAXREDIRS, 2 ); if( code != CURLE_OK ) { Con_Printf( "Failed to set Max Redirection\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, Write ); if( code != CURLE_OK ) { Con_Printf( "Failed to set writer callback function\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_PROGRESSFUNCTION, Progress ); if( code != CURLE_OK ) { Con_Printf( "Failed to set progress callback function\n" ); return 0; } strlcpy (useragent, "Linux GL vitaQuake", sizeof(useragent)); code = curl_easy_setopt( curl, CURLOPT_USERAGENT, useragent ); if( code != CURLE_OK ) { Con_Printf( "Failed to set UserAgent\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_FAILONERROR, 1 ); if( code != CURLE_OK ) { Con_Printf( "Failed to set fail on error\n" ); return 0; } return 1; } int Web_Get( const char *url, const char *referer, const char *name, int resume, int max_downloading_time, int timeout, int ( *_progress )(double) ) { CURLcode code; FILE *f; unsigned int fsize; // init/reinit curl Web_Init(); code = curl_easy_setopt( curl, CURLOPT_URL, url ); if( code != CURLE_OK ) { Con_Printf( "Failed to set url\n" ); return 0; } if( referer ) { code = curl_easy_setopt( curl, CURLOPT_REFERER, referer ); if( code != CURLE_OK ) { Con_Printf( "Failed to set Referer\n" ); return 0; } } // connection timeout code = curl_easy_setopt( curl, CURLOPT_CONNECTTIMEOUT, timeout ); if( code != CURLE_OK ) { Con_Printf( "Failed to set libcurl connection timeout\n" ); return 0; } code = curl_easy_setopt( curl, CURLOPT_TIMEOUT, max_downloading_time ); if( code != CURLE_OK ) { Con_Printf( "Failed to set libcurl global timeout\n" ); return 0; } if( resume == 1 ) { // test if file exist if( ( f = fopen( name, "r" ) ) == NULL ) { // file does not exist goto new_file; } // the file exist // get the size fsize = fseek( f, 0, SEEK_END ); fclose( f ); code = curl_easy_setopt( curl, CURLOPT_RESUME_FROM, fsize ); if( code != CURLE_OK ) { Con_Printf( "Failed to set file resume from length\n" ); return 0; } // file exist all good } else { // we will append to the file so if it already exist we will have twice the data // so delete the file if it exist remove( name ); } new_file: code = curl_easy_setopt( curl, CURLOPT_FILE, name ); //code=curl_easy_setopt(curl, CURLOPT_FILE, &f); if( code != CURLE_OK ) { Con_Printf( "Failed to set writer data\n" ); return 0; } // set callback progress = _progress; code = curl_easy_perform( curl ); if (progress != NULL) Con_Printf( "Downloading %s from %s\n", name, url ); if( code != CURLE_OK ) { Con_Printf( "Failed to download %s from %s\n", name, url ); Con_Printf( "Error: %s\n", curl_err ); Web_Cleanup(); return 0; } Web_Cleanup(); return 1; } ================================================ FILE: source/webdownload.h ================================================ /* Copyright (C) 2006 Pekka Lampila ("Medar"), Damien Deville ("Pb") and German Garcia Fernandez ("Jal") for Chasseur de bots association. 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __WEBDOWNLOAD__H__ #define __WEBDOWNLOAD__H__ int Web_Get( const char *url, const char *referer, const char *name, int resume, int max_downloading_time, int timeout, int ( *_progress )(double) ); #endif ================================================ FILE: source/world.c ================================================ /* * World query functions * * 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. */ #include "quakedef.h" /* entities never clip against themselves, or their owner line of sight checks trace->crosscontent, but bullets don't */ typedef struct { vec3_t boxmins, boxmaxs;// enclose the test object along entire move float *mins, *maxs; // size of the moving object vec3_t mins2, maxs2; // size when clipping against mosnters float *start, *end; trace_t trace; int type; edict_t *passedict; } moveclip_t; int SV_HullPointContents(hull_t *hull, int num, vec3_t p); /* =============================================================================== HULL BOXES =============================================================================== */ static hull_t box_hull; static mclipnode_t box_clipnodes[6]; static mplane_t box_planes[6]; /* =================== SV_InitBoxHull Set up the planes and clipnodes so that the six floats of a bounding box can just be stored out and get a proper hull_t structure. =================== */ void SV_InitBoxHull(void) { int i, side; box_hull.clipnodes = box_clipnodes; box_hull.planes = box_planes; box_hull.firstclipnode = 0; box_hull.lastclipnode = 5; for (i = 0; i<6; i++) { box_clipnodes[i].planenum = i; side = i & 1; box_clipnodes[i].children[side] = CONTENTS_EMPTY; if (i != 5) box_clipnodes[i].children[side ^ 1] = i + 1; else box_clipnodes[i].children[side ^ 1] = CONTENTS_SOLID; box_planes[i].type = i >> 1; box_planes[i].normal[i >> 1] = 1; } } /* =================== SV_HullForBox To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. =================== */ hull_t *SV_HullForBox(vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = mins[0]; box_planes[2].dist = maxs[1]; box_planes[3].dist = mins[1]; box_planes[4].dist = maxs[2]; box_planes[5].dist = mins[2]; return &box_hull; } /* ================ SV_HullForEntity Returns a hull that can be used for testing or clipping an object of mins/maxs size. Offset is filled in to contain the adjustment that must be added to the testing object's origin to get a point to use with the returned hull. ================ */ hull_t *SV_HullForEntity(edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset) { model_t *model; vec3_t size, hullmins, hullmaxs; hull_t *hull; // decide which clipping hull to use, based on the size if (ent->v.solid == SOLID_BSP) { // explicit hulls in the BSP model if (ent->v.movetype != MOVETYPE_PUSH) Sys_Error("SOLID_BSP without MOVETYPE_PUSH"); model = sv.models[(int)ent->v.modelindex]; if (!model || model->type != mod_brush) Sys_Error("MOVETYPE_PUSH with a non-bsp model"); VectorSubtract(maxs, mins, size); if (model->bspversion == HL_BSPVERSION) { if (size[0] < 3) { hull = &model->hulls[0]; // 0x0x0 } else if (size[0] <= 32) { if (size[2] < 54) // pick the nearest of 36 or 72 hull = &model->hulls[3]; // 32x32x36 else hull = &model->hulls[1]; // 32x32x72 } else { hull = &model->hulls[2]; // 64x64x64 } } else { if (size[0] < 3) hull = &model->hulls[0]; else if (size[0] <= 32) hull = &model->hulls[1]; else hull = &model->hulls[2]; } // calculate an offset value to center the origin VectorSubtract(hull->clip_mins, mins, offset); VectorAdd(offset, ent->v.origin, offset); } else { // create a temp hull from bounding box sizes VectorSubtract(ent->v.mins, maxs, hullmins); VectorSubtract(ent->v.maxs, mins, hullmaxs); hull = SV_HullForBox(hullmins, hullmaxs); VectorCopy(ent->v.origin, offset); } return hull; } /* =============================================================================== ENTITY AREA CHECKING =============================================================================== */ //============================================================================ static areanode_t sv_areanodes[AREA_NODES]; static int sv_numareanodes; areanode_t *SV_CreateAreaNode(int depth, vec3_t mins, vec3_t maxs) { areanode_t *anode; vec3_t size, mins1, maxs1, mins2, maxs2; anode = &sv_areanodes[sv_numareanodes]; sv_numareanodes++; ClearLink(&anode->trigger_edicts); ClearLink(&anode->solid_edicts); if (depth == AREA_DEPTH) { anode->axis = -1; anode->children[0] = anode->children[1] = NULL; return anode; } VectorSubtract(maxs, mins, size); anode->axis = (size[0] > size[1]) ? 0 : 1; anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); VectorCopy(mins, mins1); VectorCopy(mins, mins2); VectorCopy(maxs, maxs1); VectorCopy(maxs, maxs2); maxs1[anode->axis] = mins2[anode->axis] = anode->dist; anode->children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2); anode->children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1); return anode; } void SV_ClearWorld(void) { SV_InitBoxHull(); memset(sv_areanodes, 0, sizeof(sv_areanodes)); sv_numareanodes = 0; SV_CreateAreaNode(0, sv.worldmodel->mins, sv.worldmodel->maxs); } void SV_UnlinkEdict(edict_t *ent) { if (!ent->area.prev) return; // not linked in anywhere RemoveLink(&ent->area); ent->area.prev = ent->area.next = NULL; } void SV_TouchLinks(edict_t *ent, areanode_t *node) { link_t *l, *next; edict_t *touch; int old_self, old_other; // touch linked edicts for (l = node->trigger_edicts.next; l != &node->trigger_edicts; l = next) { //johnfitz -- fixes a crash when a touch function deletes an entity which comes later in the list if (!l) { Con_Printf("SV_TouchLinks: null link\n"); break; } //johnfitz next = l->next; touch = EDICT_FROM_AREA(l); if (touch == ent) continue; if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER) continue; if (ent->v.absmin[0] > touch->v.absmax[0] || ent->v.absmin[1] > touch->v.absmax[1] || ent->v.absmin[2] > touch->v.absmax[2] || ent->v.absmax[0] < touch->v.absmin[0] || ent->v.absmax[1] < touch->v.absmin[1] || ent->v.absmax[2] < touch->v.absmin[2]) continue; old_self = pr_global_struct->self; old_other = pr_global_struct->other; pr_global_struct->self = EDICT_TO_PROG(touch); pr_global_struct->other = EDICT_TO_PROG(ent); pr_global_struct->time = sv.time; PR_ExecuteProgram(touch->v.touch); pr_global_struct->self = old_self; pr_global_struct->other = old_other; } // recurse down both sides if (node->axis == -1) return; if (ent->v.absmax[node->axis] > node->dist) SV_TouchLinks(ent, node->children[0]); if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks(ent, node->children[1]); } /* =============== SV_FindTouchedLeafs =============== */ void SV_FindTouchedLeafs(edict_t *ent, mnode_t *node) { mplane_t *splitplane; mleaf_t *leaf; int sides, leafnum; if (node->contents == CONTENTS_SOLID) return; // add an efrag if the node is a leaf if (node->contents < 0) { if (ent->num_leafs == MAX_ENT_LEAFS) return; leaf = (mleaf_t *)node; leafnum = leaf - sv.worldmodel->leafs - 1; ent->leafnums[ent->num_leafs] = leafnum; ent->num_leafs++; return; } // NODE_MIXED splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(ent->v.absmin, ent->v.absmax, splitplane); // recurse down the contacted sides if (sides & 1) SV_FindTouchedLeafs(ent, node->children[0]); if (sides & 2) SV_FindTouchedLeafs(ent, node->children[1]); } void SV_LinkEdict(edict_t *ent, bool touch_triggers) { areanode_t *node; if (ent->area.prev) SV_UnlinkEdict(ent); // unlink from old position if (ent == sv.edicts) return; // don't add the world if (ent->free) return; // set the abs box // ROTATE START if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) && ent != sv.edicts) { // expand for rotation float max, v; int i; max = DotProduct(ent->v.mins, ent->v.mins); v = DotProduct(ent->v.maxs, ent->v.maxs); if (max < v) max = v; max = sqrt(max); for (i = 0; i<3; i++) { ent->v.absmin[i] = ent->v.origin[i] - max; ent->v.absmax[i] = ent->v.origin[i] + max; } } else // ROTATE END { VectorAdd(ent->v.origin, ent->v.mins, ent->v.absmin); VectorAdd(ent->v.origin, ent->v.maxs, ent->v.absmax); } // to make items easier to pick up and allow them to be grabbed off // of shelves, the abs sizes are expanded if ((int)ent->v.flags & FL_ITEM) { ent->v.absmin[0] -= 15; ent->v.absmin[1] -= 15; ent->v.absmax[0] += 15; ent->v.absmax[1] += 15; } else {// because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->v.absmin[0] -= 1; ent->v.absmin[1] -= 1; ent->v.absmin[2] -= 1; ent->v.absmax[0] += 1; ent->v.absmax[1] += 1; ent->v.absmax[2] += 1; } // link to PVS leafs ent->num_leafs = 0; if (ent->v.modelindex) SV_FindTouchedLeafs(ent, sv.worldmodel->nodes); if (ent->v.solid == SOLID_NOT) return; // find the first node that the ent's box crosses node = sv_areanodes; while (1) { if (node->axis == -1) break; if (ent->v.absmin[node->axis] > node->dist) node = node->children[0]; else if (ent->v.absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in if (ent->v.solid == SOLID_TRIGGER) InsertLinkBefore(&ent->area, &node->trigger_edicts); else InsertLinkBefore(&ent->area, &node->solid_edicts); // if touch_triggers, touch all entities at this node and decend for more if (touch_triggers) SV_TouchLinks(ent, sv_areanodes); } /* =============================================================================== POINT TESTING IN HULLS =============================================================================== */ /* ================== SV_HullPointContents ================== */ int SV_HullPointContents(hull_t *hull, int num, vec3_t p) { float d; mclipnode_t *node; mplane_t *plane; while (num >= 0) { if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error("SV_HullPointContents: bad node number"); node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProduct(plane->normal, p) - plane->dist; num = (d < 0) ? node->children[1] : node->children[0]; } return num; } /* ================== SV_PointContents ================== */ int SV_PointContents(vec3_t p) { int cont; cont = SV_HullPointContents(&sv.worldmodel->hulls[0], 0, p); if (cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN) cont = CONTENTS_WATER; return cont; } int SV_TruePointContents(vec3_t p) { return SV_HullPointContents(&sv.worldmodel->hulls[0], 0, p); } //=========================================================================== /* ============ SV_TestEntityPosition This could be a lot more efficient... ============ */ edict_t *SV_TestEntityPosition(edict_t *ent) { trace_t trace; trace = SV_Move(ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, 0, ent); if (trace.startsolid) return sv.edicts; return NULL; } /* =============================================================================== LINE TESTING IN HULLS =============================================================================== */ // 1/32 epsilon to keep floating point happy #define DIST_EPSILON (0.03125) /* ================== SV_RecursiveHullCheck ================== */ bool SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) { mclipnode_t *node; mplane_t *plane; float t1, t2, frac, midf; int i, side; vec3_t mid; // check for empty if (num < 0) { if (num != CONTENTS_SOLID) { trace->allsolid = false; if (num == CONTENTS_EMPTY) trace->inopen = true; else trace->inwater = true; } else { trace->startsolid = true; } return true; // empty } if (num < hull->firstclipnode || num > hull->lastclipnode) Sys_Error("SV_RecursiveHullCheck: bad node number"); // find the point distances node = hull->clipnodes + num; plane = hull->planes + node->planenum; if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; } else { t1 = DotProduct(plane->normal, p1) - plane->dist; t2 = DotProduct(plane->normal, p2) - plane->dist; } if (t1 >= 0 && t2 >= 0) return SV_RecursiveHullCheck(hull, node->children[0], p1f, p2f, p1, p2, trace); if (t1 < 0 && t2 < 0) return SV_RecursiveHullCheck(hull, node->children[1], p1f, p2f, p1, p2, trace); // put the crosspoint DIST_EPSILON pixels on the near side if (t1 < 0) frac = (t1 + DIST_EPSILON) / (t1 - t2); else frac = (t1 - DIST_EPSILON) / (t1 - t2); if (frac < 0) frac = 0; if (frac > 1) frac = 1; midf = p1f + (p2f - p1f)*frac; for (i = 0; i<3; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); side = (t1 < 0); // move up to the node if (!SV_RecursiveHullCheck(hull, node->children[side], p1f, midf, p1, mid, trace)) return false; #ifdef PARANOID if (SV_HullPointContents(sv_hullmodel, mid, node->children[side]) == CONTENTS_SOLID) { Con_Printf("mid PointInHullSolid\n"); return false; } #endif if (SV_HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID) // go past the node return SV_RecursiveHullCheck(hull, node->children[side ^ 1], midf, p2f, mid, p2, trace); if (trace->allsolid) return false; // never got out of the solid area // the other side of the node is solid, this is the impact point if (!side) { VectorCopy(plane->normal, trace->plane.normal); trace->plane.dist = plane->dist; } else { VectorSubtract(vec3_origin, plane->normal, trace->plane.normal); trace->plane.dist = -plane->dist; } while (SV_HullPointContents(hull, hull->firstclipnode, mid) == CONTENTS_SOLID) { // shouldn't really happen, but does occasionally frac -= 0.1; if (frac < 0) { trace->fraction = midf; VectorCopy(mid, trace->endpos); //Con_DPrintf ("backup past 0\n"); // JPG - removed this (it was spamming big time) return false; } midf = p1f + (p2f - p1f)*frac; for (i = 0; i<3; i++) mid[i] = p1[i] + frac*(p2[i] - p1[i]); } trace->fraction = midf; VectorCopy(mid, trace->endpos); return false; } //Handles selection or creation of a clipping hull, and offseting (and eventually rotation) of the end points trace_t SV_ClipMoveToEntity(edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { trace_t trace; vec3_t offset, start_l, end_l; hull_t *hull; // fill in a default trace memset(&trace, 0, sizeof(trace_t)); trace.fraction = 1; trace.allsolid = true; VectorCopy(end, trace.endpos); // get the clipping hull hull = SV_HullForEntity(ent, mins, maxs, offset); VectorSubtract(start, offset, start_l); VectorSubtract(end, offset, end_l); // ROTATE START // rotate start and end into the models frame of reference if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) && ent != sv.edicts) { // vec3_t a; vec3_t forward, right, up; vec3_t temp; AngleVectors(ent->v.angles, forward, right, up); VectorCopy(start_l, temp); start_l[0] = DotProduct(temp, forward); start_l[1] = -DotProduct(temp, right); start_l[2] = DotProduct(temp, up); VectorCopy(end_l, temp); end_l[0] = DotProduct(temp, forward); end_l[1] = -DotProduct(temp, right); end_l[2] = DotProduct(temp, up); } // ROTATE END // trace a line through the apropriate clipping hull SV_RecursiveHullCheck(hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); // ROTATE START // rotate endpos back to world frame of reference if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) && ent != sv.edicts) { vec3_t a; vec3_t forward, right, up; vec3_t temp; if (trace.fraction != 1) { VectorSubtract(vec3_origin, ent->v.angles, a); AngleVectors(a, forward, right, up); VectorCopy(trace.endpos, temp); trace.endpos[0] = DotProduct(temp, forward); trace.endpos[1] = -DotProduct(temp, right); trace.endpos[2] = DotProduct(temp, up); VectorCopy(trace.plane.normal, temp); trace.plane.normal[0] = DotProduct(temp, forward); trace.plane.normal[1] = -DotProduct(temp, right); trace.plane.normal[2] = DotProduct(temp, up); } // fix trace up by the offset VectorAdd(trace.endpos, offset, trace.endpos); } // other cases where not // Solid BSP else { if (trace.fraction != 1) VectorAdd(trace.endpos, offset, trace.endpos); } // ROTATE END // did we clip the move? if (trace.fraction < 1 || trace.startsolid) trace.ent = ent; return trace; } //=========================================================================== /* ==================== SV_ClipToLinks Mins and maxs enclose the entire area swept by the move ==================== */ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) { link_t *l, *next; edict_t *touch; trace_t trace; // touch linked edicts for (l = node->solid_edicts.next; l != &node->solid_edicts; l = next) { next = l->next; touch = EDICT_FROM_AREA(l); if (touch->v.solid == SOLID_NOT) continue; if (touch == clip->passedict) continue; if (touch->v.solid == SOLID_TRIGGER) Sys_Error("Trigger in clipping list"); if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP) continue; if (clip->boxmins[0] > touch->v.absmax[0] || clip->boxmins[1] > touch->v.absmax[1] || clip->boxmins[2] > touch->v.absmax[2] || clip->boxmaxs[0] < touch->v.absmin[0] || clip->boxmaxs[1] < touch->v.absmin[1] || clip->boxmaxs[2] < touch->v.absmin[2]) continue; if (clip->passedict && clip->passedict->v.size[0] && !touch->v.size[0]) continue; // points never interact // might intersect, so do an exact clip if (clip->trace.allsolid) return; if (clip->passedict) { if (PROG_TO_EDICT(touch->v.owner) == clip->passedict) continue; // don't clip against own missiles if (PROG_TO_EDICT(clip->passedict->v.owner) == touch) continue; // don't clip against owner } if ((int)touch->v.flags & FL_MONSTER) trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins2, clip->maxs2, clip->end); else trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins, clip->maxs, clip->end); if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction) { trace.ent = touch; if (clip->trace.startsolid) { clip->trace = trace; clip->trace.startsolid = true; } else { clip->trace = trace; } } else if (trace.startsolid) { clip->trace.startsolid = true; } } // recurse down both sides if (node->axis == -1) return; if (clip->boxmaxs[node->axis] > node->dist) SV_ClipToLinks(node->children[0], clip); if (clip->boxmins[node->axis] < node->dist) SV_ClipToLinks(node->children[1], clip); } void SV_MoveBounds(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) { int i; for (i = 0; i<3; i++) { if (end[i] > start[i]) { boxmins[i] = start[i] + mins[i] - 1; boxmaxs[i] = end[i] + maxs[i] + 1; } else { boxmins[i] = end[i] + mins[i] - 1; boxmaxs[i] = start[i] + maxs[i] + 1; } } } trace_t SV_Move(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict) { moveclip_t clip; int i; memset(&clip, 0, sizeof(moveclip_t)); // clip to world clip.trace = SV_ClipMoveToEntity(sv.edicts, start, mins, maxs, end); clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.type = type; clip.passedict = passedict; if (type == MOVE_MISSILE) { for (i = 0; i<3; i++) { clip.mins2[i] = -15; clip.maxs2[i] = 15; } } else { VectorCopy(mins, clip.mins2); VectorCopy(maxs, clip.maxs2); } // create the bounding box of the entire move SV_MoveBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs); // clip to entities SV_ClipToLinks(sv_areanodes, &clip); return clip.trace; } ================================================ FILE: source/world.h ================================================ /* * 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. */ #define MOVE_NORMAL 0 #define MOVE_NOMONSTERS 1 #define MOVE_MISSILE 2 typedef struct areanode_s { int axis; // -1 = leaf node float dist; struct areanode_s *children[2]; link_t trigger_edicts; link_t solid_edicts; } areanode_t; typedef struct { vec3_t normal; float dist; } plane_t; typedef struct { bool allsolid; // if true, plane is not valid bool startsolid; // if true, the initial point was in a solid area bool inopen, inwater; float fraction; // time completed, 1.0 = didn't hit anything vec3_t endpos; // final position plane_t plane; // surface normal at impact edict_t *ent; // entity the surface is on } trace_t; #define AREA_DEPTH 4 #define AREA_NODES 32 void SV_ClearWorld(void); // called after the world model has been loaded, before linking any entities void SV_UnlinkEdict(edict_t *ent); // call before removing an entity, and before trying to move one, // so it doesn't clip against itself // flags ent->v.modified void SV_LinkEdict(edict_t *ent, bool touch_triggers); // Needs to be called any time an entity changes origin, mins, maxs, or solid // flags ent->v.modified // sets ent->v.absmin and ent->v.absmax // if touchtriggers, calls prog functions for the intersected triggers int SV_TruePointContents(vec3_t p); int SV_PointContents(vec3_t p); // returns the CONTENTS_* value from the world at the given point. // does not check any entities at all // the non-true version remaps the water current contents to content_water bool SV_RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace); edict_t *SV_TestEntityPosition(edict_t *ent); trace_t SV_Move(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict); // mins and maxs are relative // if the entire move stays in a solid volume, trace.allsolid will be set // if the starting point is in a solid, it will be allowed to move out // to an open area // nomonsters is used for line of sight or edge testing, where mosnters // shouldn't be considered solid objects // passedict is explicitly excluded from clipping checks (normally NULL) ================================================ FILE: source/zone.c ================================================ /* * 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. */ #include "quakedef.h" #define ZONE_DEFAULT_SIZE 0x100000 // 1Mb #define DYNAMIC_SIZE 256*1024 //johnfitz: was 48k #define ZONEID 0x1d4a11 #define MINFRAGMENT 64 typedef struct memblock_s { int size; // including the header and possibly tiny fragments int tag; // a tag of 0 is a free block int id; // should be ZONEID struct memblock_s *next, *prev; int pad; // pad to 64 bit boundary } memblock_t; typedef struct { int size; // total bytes malloced, including header memblock_t blocklist; // start / end cap for linked list memblock_t *rover; } memzone_t; void Cache_FreeLow(int new_low_hunk); void Cache_FreeHigh(int new_high_hunk); /* ============================================================================== ZONE MEMORY ALLOCATION There is never any space between memblocks, and there will never be two contiguous free memblocks. The rover can be left pointing at a non-empty block The zone calls are pretty much only used for small strings and structures, all big things are allocated on the hunk. ============================================================================== */ memzone_t *mainzone; /* ======================== Z_ClearZone ======================== */ void Z_ClearZone(memzone_t *zone, int size) { memblock_t *block; // set the entire zone to one free block zone->blocklist.next = zone->blocklist.prev = block = (memblock_t *)((byte *)zone + sizeof(memzone_t)); zone->blocklist.tag = 1; // in use block zone->blocklist.id = 0; zone->blocklist.size = 0; zone->rover = block; block->prev = block->next = &zone->blocklist; block->tag = 0; // free block block->id = ZONEID; block->size = size - sizeof(memzone_t); } /* ======================== Z_Free ======================== */ void Z_Free(void *ptr) { memblock_t *block, *other; if (!ptr) Sys_Error("Z_Free: NULL pointer"); block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID){ Con_DPrintf("Z_Free: freed a pointer without ZONEID\n"); return; } if (block->tag == 0) Sys_Error("Z_Free: freed a freed pointer"); block->tag = 0; // mark as free other = block->prev; if (!other->tag) { // merge with previous free block other->size += block->size; other->next = block->next; other->next->prev = other; if (block == mainzone->rover) mainzone->rover = other; block = other; } other = block->next; if (!other->tag) { // merge the next free block onto the end block->size += other->size; block->next = other->next; block->next->prev = block; if (other == mainzone->rover) mainzone->rover = block; } } /* ======================== Z_Malloc ======================== */ void *Z_Malloc(int size) { void *buf; Z_CheckHeap(); // DEBUG if (!(buf = Z_TagMalloc(size, 1))) Sys_Error("Z_Malloc: failed on allocation of %i bytes", size); memset(buf, 0, size); return buf; } void *Z_TagMalloc(int size, int tag) { int extra; memblock_t *start, *rover, *new, *base; if (!tag) Sys_Error("Z_TagMalloc: tried to use a 0 tag"); // scan through the block list looking for the first free block // of sufficient size size += sizeof(memblock_t); // account for size of block header size += 4; // space for memory trash tester size = (size + 7) & ~7; // align to 8-byte boundary base = rover = mainzone->rover; start = base->prev; do { if (rover == start) // scaned all the way around the list return NULL; if (rover->tag) base = rover = rover->next; else rover = rover->next; } while (base->tag || base->size < size); // found a block big enough extra = base->size - size; if (extra > MINFRAGMENT) { // there will be a free fragment after the allocated block new = (memblock_t *)((byte *)base + size); new->size = extra; new->tag = 0; // free block new->prev = base; new->id = ZONEID; new->next = base->next; new->next->prev = new; base->next = new; base->size = size; } base->tag = tag; // no longer a free block mainzone->rover = base->next; // next allocation will start looking here base->id = ZONEID; // marker for memory trash testing *(int *)((byte *)base + base->size - 4) = ZONEID; return (void *)((byte *)base + sizeof(memblock_t)); } /* ======================== Z_Print ======================== */ void Z_Print(memzone_t *zone) { memblock_t *block; Con_Printf("zone size: %i location: %p\n", mainzone->size, mainzone); for (block = zone->blocklist.next;; block = block->next) { Con_Printf("block:%p size:%7i tag:%3i\n", block, block->size, block->tag); if (block->next == &zone->blocklist) break; // all blocks have been hit if ((byte *)block + block->size != (byte *)block->next) Con_Printf("ERROR: block size does not touch the next block\n"); if (block->next->prev != block) Con_Printf("ERROR: next block doesn't have proper back link\n"); if (!block->tag && !block->next->tag) Con_Printf("ERROR: two consecutive free blocks\n"); } } /* ======================== Z_CheckHeap ======================== */ void Z_CheckHeap (void) { memblock_t *block; for (block = mainzone->blocklist.next ; ; block = block->next) { if (block->next == &mainzone->blocklist) break; // all blocks have been hit if ( (byte *)block + block->size != (byte *)block->next) Sys_Error ("Z_CheckHeap: block size does not touch the next block\n"); if ( block->next->prev != block) Sys_Error ("Z_CheckHeap: next block doesn't have proper back link\n"); if (!block->tag && !block->next->tag) Sys_Error ("Z_CheckHeap: two consecutive free blocks\n"); } } /* ======================== Z_Realloc ======================== */ void *Z_Realloc(void *ptr, int size) { int old_size; void *old_ptr; memblock_t *block; if (!ptr) return Z_Malloc(size); block = (memblock_t *)((byte *)ptr - sizeof(memblock_t)); if (block->id != ZONEID) Sys_Error("Z_Realloc: realloced a pointer without ZONEID"); if (block->tag == 0) Sys_Error("Z_Realloc: realloced a freed pointer"); old_size = block->size; old_size -= (4 + (int) sizeof(memblock_t)); /* see Z_TagMalloc() */ old_ptr = ptr; Z_Free(ptr); ptr = Z_TagMalloc(size, 1); if (!ptr) Sys_Error("Z_Realloc: failed on allocation of %i bytes", size); if (ptr != old_ptr) memmove(ptr, old_ptr, min(old_size, size)); if (old_size < size) memset((byte *)ptr + old_size, 0, size - old_size); return ptr; } //============================================================================ #define HUNK_SENTINAL 0x1df001ed typedef struct { int sentinal; int size; // including sizeof(hunk_t), -1 = not allocated char name[8]; } hunk_t; byte *hunk_base; int hunk_size; int hunk_low_used; int hunk_high_used; bool hunk_tempactive; int hunk_tempmark; void R_FreeTextures(void); /* ============== Hunk_Check Run consistency and sentinal trashing checks ============== */ void Hunk_Check(void) { hunk_t *h; for (h = (hunk_t *)hunk_base; (byte *)h != hunk_base + hunk_low_used;) { if (h->sentinal != HUNK_SENTINAL) Sys_Error("Hunk_Check: trashed sentinal"); if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) Sys_Error("Hunk_Check: bad size"); h = (hunk_t *)((byte *)h + h->size); } } /* ============== Hunk_Print If "all" is specified, every single allocation is printed. Otherwise, allocations with the same name will be totaled up before printing. ============== */ void Hunk_Print(bool all) { hunk_t *h, *next, *endlow, *starthigh, *endhigh; int count, sum, totalblocks; char name[9]; name[8] = 0; count = 0; sum = 0; totalblocks = 0; h = (hunk_t *)hunk_base; endlow = (hunk_t *)(hunk_base + hunk_low_used); starthigh = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); endhigh = (hunk_t *)(hunk_base + hunk_size); Con_Printf(" :%8i total hunk size\n", hunk_size); Con_Printf("-------------------------\n"); while (1) { // skip to the high hunk if done with low hunk if (h == endlow) { Con_Printf("-------------------------\n"); Con_Printf(" :%8i REMAINING\n", hunk_size - hunk_low_used - hunk_high_used); Con_Printf("-------------------------\n"); h = starthigh; } // if totally done, break if (h == endhigh) break; // run consistency checks if (h->sentinal != HUNK_SENTINAL) Sys_Error("Hunk_Check: trashed sentinal"); if (h->size < 16 || h->size + (byte *)h - hunk_base > hunk_size) Sys_Error("Hunk_Check: bad size"); next = (hunk_t *)((byte *)h + h->size); count++; totalblocks++; sum += h->size; // print the single block memcpy(name, h->name, 8); if (all) Con_Printf("%8p :%8i %8s\n", h, h->size, name); // print the total if (next == endlow || next == endhigh || strncmp(h->name, next->name, 8)) { if (!all) Con_Printf(" :%8i %8s (TOTAL)\n", sum, name); count = 0; sum = 0; } h = next; } Con_Printf("-------------------------\n"); Con_Printf("%8i total blocks\n", totalblocks); } /* =================== Hunk_Print_f -- Baker 3.76 - Hunk Print from FitzQuake =================== */ void Hunk_Print_f(void) { Hunk_Print(false); } /* =================== Hunk_AllocName =================== */ void *Hunk_AllocName(int size, char *name) { hunk_t *h; #ifdef PARANOID Hunk_Check(); #endif if (size < 0) Sys_Error("Hunk_Alloc: bad size: %i", size); size = sizeof(hunk_t) + ((size + 15) & ~15); if (hunk_size - hunk_low_used - hunk_high_used < size) Sys_Error ("Hunk_AllocName: failed on %i bytes for %s",size, name); h = (hunk_t *)(hunk_base + hunk_low_used); hunk_low_used += size; Cache_FreeLow(hunk_low_used); memset(h, 0, size); h->size = size; h->sentinal = HUNK_SENTINAL; strncpy(h->name, name, 8); return (void *)(h + 1); } /* =================== Hunk_Alloc =================== */ void *Hunk_Alloc(int size) { return Hunk_AllocName(size, "unknown"); } int Hunk_LowMark(void) { return hunk_low_used; } void Hunk_FreeToLowMark(int mark) { if (mark < 0 || mark > hunk_low_used) Sys_Error("Hunk_FreeToLowMark: bad mark %i", mark); memset(hunk_base + mark, 0, hunk_low_used - mark); hunk_low_used = mark; } int Hunk_HighMark(void) { if (hunk_tempactive) { hunk_tempactive = false; Hunk_FreeToHighMark(hunk_tempmark); } return hunk_high_used; } void Hunk_FreeToHighMark(int mark) { if (hunk_tempactive) { hunk_tempactive = false; Hunk_FreeToHighMark(hunk_tempmark); } if (mark < 0 || mark > hunk_high_used) Sys_Error("Hunk_FreeToHighMark: bad mark %i", mark); memset(hunk_base + hunk_size - hunk_high_used, 0, hunk_high_used - mark); hunk_high_used = mark; } /* =================== Hunk_HighAllocName =================== */ void *Hunk_HighAllocName(int size, char *name) { hunk_t *h; if (size < 0) Sys_Error("Hunk_HighAllocName: bad size: %i", size); if (hunk_tempactive) { Hunk_FreeToHighMark(hunk_tempmark); hunk_tempactive = false; } #ifdef PARANOID Hunk_Check(); #endif size = sizeof(hunk_t) + ((size + 15) & ~15); if (hunk_size - hunk_low_used - hunk_high_used < size) Sys_Error("Hunk_HighAlloc: failed on %i bytes for %s", size, name); hunk_high_used += size; Cache_FreeHigh(hunk_high_used); h = (hunk_t *)(hunk_base + hunk_size - hunk_high_used); memset(h, 0, size); h->size = size; h->sentinal = HUNK_SENTINAL; strncpy(h->name, name, 8); return (void *)(h + 1); } /* ================= Hunk_TempAlloc Return space from the top of the hunk ================= */ void *Hunk_TempAlloc(int size) { void *buf; size = (size + 15) & ~15; if (hunk_tempactive) { Hunk_FreeToHighMark(hunk_tempmark); hunk_tempactive = false; } hunk_tempmark = Hunk_HighMark(); buf = Hunk_HighAllocName(size, "temp"); hunk_tempactive = true; return buf; } /* =============================================================================== CACHE MEMORY =============================================================================== */ typedef struct cache_system_s { int size; // including this header cache_user_t *user; char name[16]; struct cache_system_s *prev, *next; struct cache_system_s *lru_prev, *lru_next; // for LRU flushing } cache_system_t; cache_system_t *Cache_TryAlloc(int size, bool nobottom); cache_system_t cache_head; /* =========== Cache_Move =========== */ void Cache_Move(cache_system_t *c) { cache_system_t *new; // we are clearing up space at the bottom, so only allocate it late if ((new = Cache_TryAlloc(c->size, true))) { // Con_Printf ("cache_move ok\n"); memcpy(new + 1, c + 1, c->size - sizeof(cache_system_t)); new->user = c->user; memcpy(new->name, c->name, sizeof(new->name)); Cache_Free(c->user); new->user->data = (void *)(new + 1); } else { // Con_Printf ("cache_move failed\n"); Cache_Free(c->user); // tough luck... } } /* ============ Cache_FreeLow Throw things out until the hunk can be expanded to the given point ============ */ void Cache_FreeLow(int new_low_hunk) { cache_system_t *c; while (1) { c = cache_head.next; if (c == &cache_head) return; // nothing in cache at all if ((byte *)c >= hunk_base + new_low_hunk) return; // there is space to grow the hunk Cache_Move(c); // reclaim the space } } /* ============ Cache_FreeHigh Throw things out until the hunk can be expanded to the given point ============ */ void Cache_FreeHigh(int new_high_hunk) { cache_system_t *c, *prev; prev = NULL; while (1) { c = cache_head.prev; if (c == &cache_head) return; // nothing in cache at all if ((byte *)c + c->size <= hunk_base + hunk_size - new_high_hunk) return; // there is space to grow the hunk if (c == prev) Cache_Free(c->user); // didn't move out of the way else { Cache_Move(c); // try to move it prev = c; } } } void Cache_UnlinkLRU(cache_system_t *cs) { if (!cs->lru_next || !cs->lru_prev) Sys_Error("Cache_UnlinkLRU: NULL link"); cs->lru_next->lru_prev = cs->lru_prev; cs->lru_prev->lru_next = cs->lru_next; cs->lru_prev = cs->lru_next = NULL; } void Cache_MakeLRU(cache_system_t *cs) { if (cs->lru_next || cs->lru_prev) Sys_Error("Cache_MakeLRU: active link"); cache_head.lru_next->lru_prev = cs; cs->lru_next = cache_head.lru_next; cs->lru_prev = &cache_head; cache_head.lru_next = cs; } /* ============ Cache_TryAlloc Looks for a free block of memory between the high and low hunk marks Size should already include the header and padding ============ */ cache_system_t *Cache_TryAlloc(int size, bool nobottom) { cache_system_t *cs, *new; // is the cache completely empty? if (!nobottom && cache_head.prev == &cache_head) { if (hunk_size - hunk_high_used - hunk_low_used < size) Sys_Error("Cache_TryAlloc: %i is greater then free hunk", size); new = (cache_system_t *)(hunk_base + hunk_low_used); memset(new, 0, sizeof(*new)); new->size = size; cache_head.prev = cache_head.next = new; new->prev = new->next = &cache_head; Cache_MakeLRU(new); return new; } // search from the bottom up for space new = (cache_system_t *)(hunk_base + hunk_low_used); cs = cache_head.next; do { if (!nobottom || cs != cache_head.next) { if ((byte *)cs - (byte *) new >= size) { // found space memset(new, 0, sizeof(*new)); new->size = size; new->next = cs; new->prev = cs->prev; cs->prev->next = new; cs->prev = new; Cache_MakeLRU(new); return new; } } // continue looking new = (cache_system_t *)((byte *)cs + cs->size); cs = cs->next; } while (cs != &cache_head); // try to allocate one at the very end if (hunk_base + hunk_size - hunk_high_used - (byte *) new >= size) { memset(new, 0, sizeof(*new)); new->size = size; new->next = &cache_head; new->prev = cache_head.prev; cache_head.prev->next = new; cache_head.prev = new; Cache_MakeLRU(new); return new; } return NULL; // couldn't allocate } /* ============ Cache_Flush_f Throw everything out, so new data will be demand cached ============ */ void Cache_Flush(void) { while (cache_head.next != &cache_head) Cache_Free(cache_head.next->user); // reclaim the space } /* ============ Cache_Print ============ */ void Cache_Print(void) { cache_system_t *cd; for (cd = cache_head.next; cd != &cache_head; cd = cd->next) Con_Printf("%8i : %s\n", cd->size, cd->name); } /* ============ Cache_Report ============ */ void Cache_Report(void) { Con_DPrintf("%4.1f megabyte data cache\n", (hunk_size - hunk_high_used - hunk_low_used) / (float)(1024 * 1024)); } /* ============ Cache_Compact ============ */ void Cache_Compact(void) { } /* ============ Cache_Init ============ */ void Cache_Init(void) { cache_head.next = cache_head.prev = &cache_head; cache_head.lru_next = cache_head.lru_prev = &cache_head; Cmd_AddCommand("flush", Cache_Flush); } /* ============== Cache_Free Frees the memory and removes it from the LRU list ============== */ void Cache_Free(cache_user_t *c) { cache_system_t *cs; if (!c->data) Sys_Error("Cache_Free: not allocated"); cs = ((cache_system_t *)c->data) - 1; cs->prev->next = cs->next; cs->next->prev = cs->prev; cs->next = cs->prev = NULL; c->data = NULL; Cache_UnlinkLRU(cs); } /* ============== Cache_Check ============== */ void *Cache_Check(cache_user_t *c) { cache_system_t *cs; if (!c->data) return NULL; cs = ((cache_system_t *)c->data) - 1; // move to head of LRU Cache_UnlinkLRU(cs); Cache_MakeLRU(cs); return c->data; } /* ============== Cache_Alloc ============== */ void *Cache_Alloc(cache_user_t *c, int size, char *name) { cache_system_t *cs; if (c->data) Sys_Error("Cache_Alloc: already allocated"); if (size <= 0) Sys_Error("Cache_Alloc: size %i", size); size = (size + sizeof(cache_system_t) + 15) & ~15; // find memory for it while (1) { if ((cs = Cache_TryAlloc(size, false))) { strncpy(cs->name, name, sizeof(cs->name) - 1); c->data = (void *)(cs + 1); cs->user = c; break; } // free the least recently used cahedat if (cache_head.lru_prev == &cache_head) Sys_Error("Cache_Alloc: out of memory"); // not enough memory at all Cache_Free(cache_head.lru_prev->user); } return Cache_Check(c); } //============================================================================ /* ======================== Memory_Init ======================== */ void Memory_Init(void *buf, int size) { int p, zonesize = ZONE_DEFAULT_SIZE; hunk_base = buf; hunk_size = size; hunk_low_used = 0; hunk_high_used = 0; Cache_Init(); if ((p = COM_CheckParm("-zone")) && (p + 1) < com_argc) zonesize = atoi(com_argv[p + 1]) * 1024; mainzone = Hunk_AllocName(zonesize, "zone"); Z_ClearZone(mainzone, zonesize); Cmd_AddCommand("hunk_print", Hunk_Print_f); //johnfitz } ================================================ FILE: source/zone.h ================================================ /* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* memory allocation H_??? The hunk manages the entire memory block given to quake. It must be contiguous. Memory can be allocated from either the low or high end in a stack fashion. The only way memory is released is by resetting one of the pointers. Hunk allocations should be given a name, so the Hunk_Print () function can display usage. Hunk allocations are guaranteed to be 16 byte aligned. The video buffers are allocated high to avoid leaving a hole underneath server allocations when changing to a higher video mode. Z_??? Zone memory functions used for small, dynamic allocations like text strings from command input. There is only about 48K for it, allocated at the very bottom of the hunk. Cache_??? Cache memory is for objects that can be dynamically loaded and can usefully stay persistant between levels. The size of the cache fluctuates from level to level. To allocate a cachable object Temp_??? Temp memory is used for file loading and surface caching. The size of the cache memory is adjusted so that there is a minimum of 512k remaining for temp memory. ------ Top of Memory ------- high hunk allocations <--- high hunk reset point held by vid video buffer z buffer surface cache <--- high hunk used cachable memory <--- low hunk used client and server low hunk allocations <-- low hunk reset point held by host startup hunk allocations Zone block ----- Bottom of Memory ----- */ void Memory_Init (void *buf, int size); void Z_Free (void *ptr); void *Z_Malloc (int size); // returns 0 filled memory void *Z_TagMalloc (int size, int tag); void Z_DumpHeap (void); void Z_CheckHeap (void); int Z_FreeMemory (void); void *Hunk_Alloc (int size); // returns 0 filled memory void *Hunk_AllocName (int size, char *name); void *Hunk_HighAllocName (int size, char *name); int Hunk_LowMark (void); void Hunk_FreeToLowMark (int mark); int Hunk_HighMark (void); void Hunk_FreeToHighMark (int mark); void *Hunk_TempAlloc (int size); void Hunk_Check (void); typedef struct cache_user_s { void *data; } cache_user_t; void Cache_Flush (void); void *Cache_Check (cache_user_t *c); // returns the cached data, and moves to the head of the LRU list // if present, otherwise returns NULL void Cache_Free (cache_user_t *c); void *Cache_Alloc (cache_user_t *c, int size, char *name); // Returns NULL if all purgable data was tossed and there still // wasn't enough room. void Cache_Report (void);